> From: Jan Beulich <jbeulich@suse.com>
> Sent: Wednesday, June 9, 2021 5:29 PM
>
> If there is any way for one fault to be left set in the recording
> registers, there's no reason there couldn't also be multiple ones. If
> PPF set set (being the OR or all F fields), simply loop over the entire
> range of fault recording registers, clearing F everywhere.
>
> Since PPF is a r/o bit, also remove it from DMA_FSTS_FAULTS (arguably
> the constant's name is ambiguous as well).
>
> Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Kevin Tian <kevin.tian@intel.com>
>
> --- a/xen/drivers/passthrough/vtd/iommu.c
> +++ b/xen/drivers/passthrough/vtd/iommu.c
> @@ -2094,13 +2094,23 @@ static int __hwdom_init setup_hwdom_devi
>
> void clear_fault_bits(struct vtd_iommu *iommu)
> {
> - u64 val;
> unsigned long flags;
>
> spin_lock_irqsave(&iommu->register_lock, flags);
> - val = dmar_readq(iommu->reg, cap_fault_reg_offset(iommu->cap) + 8);
> - dmar_writeq(iommu->reg, cap_fault_reg_offset(iommu->cap) + 8, val);
> +
> + if ( dmar_readl(iommu->reg, DMAR_FSTS_REG) & DMA_FSTS_PPF )
> + {
> + unsigned int reg = cap_fault_reg_offset(iommu->cap);
> + unsigned int end = reg + cap_num_fault_regs(iommu->cap);
> +
> + do {
> + dmar_writel(iommu->reg, reg + 12, DMA_FRCD_F);
> + reg += PRIMARY_FAULT_REG_LEN;
> + } while ( reg < end );
> + }
> +
> dmar_writel(iommu->reg, DMAR_FSTS_REG, DMA_FSTS_FAULTS);
> +
> spin_unlock_irqrestore(&iommu->register_lock, flags);
> }
>
> --- a/xen/drivers/passthrough/vtd/iommu.h
> +++ b/xen/drivers/passthrough/vtd/iommu.h
> @@ -174,9 +174,8 @@
> #define DMA_FSTS_IQE (1u << 4)
> #define DMA_FSTS_ICE (1u << 5)
> #define DMA_FSTS_ITE (1u << 6)
> -#define DMA_FSTS_FAULTS (DMA_FSTS_PFO | DMA_FSTS_PPF |
> DMA_FSTS_AFO | \
> - DMA_FSTS_APF | DMA_FSTS_IQE | DMA_FSTS_ICE | \
> - DMA_FSTS_ITE)
> +#define DMA_FSTS_FAULTS (DMA_FSTS_PFO | DMA_FSTS_AFO |
> DMA_FSTS_APF | \
> + DMA_FSTS_IQE | DMA_FSTS_ICE | DMA_FSTS_ITE)
> #define dma_fsts_fault_record_index(s) (((s) >> 8) & 0xff)
>
> /* FRCD_REG, 32 bits access */