iommu/vt-d: Report more information about invalidation errors
When the invalidation queue errors are encountered, dump the information logged by the VT-d hardware together with the pending queue invalidation descriptors. Signed-off-by: Ashok Raj <ashok.raj@intel.com> Tested-by: Guo Kaijie <Kaijie.Guo@intel.com> Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com> Link: https://lore.kernel.org/r/20210318005340.187311-1-baolu.lu@linux.intel.com Signed-off-by: Joerg Roedel <jroedel@suse.de>
This commit is contained in:
parent
dec991e472
commit
6ca69e5841
@ -1205,6 +1205,63 @@ static inline void reclaim_free_desc(struct q_inval *qi)
|
||||
}
|
||||
}
|
||||
|
||||
static const char *qi_type_string(u8 type)
|
||||
{
|
||||
switch (type) {
|
||||
case QI_CC_TYPE:
|
||||
return "Context-cache Invalidation";
|
||||
case QI_IOTLB_TYPE:
|
||||
return "IOTLB Invalidation";
|
||||
case QI_DIOTLB_TYPE:
|
||||
return "Device-TLB Invalidation";
|
||||
case QI_IEC_TYPE:
|
||||
return "Interrupt Entry Cache Invalidation";
|
||||
case QI_IWD_TYPE:
|
||||
return "Invalidation Wait";
|
||||
case QI_EIOTLB_TYPE:
|
||||
return "PASID-based IOTLB Invalidation";
|
||||
case QI_PC_TYPE:
|
||||
return "PASID-cache Invalidation";
|
||||
case QI_DEIOTLB_TYPE:
|
||||
return "PASID-based Device-TLB Invalidation";
|
||||
case QI_PGRP_RESP_TYPE:
|
||||
return "Page Group Response";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
static void qi_dump_fault(struct intel_iommu *iommu, u32 fault)
|
||||
{
|
||||
unsigned int head = dmar_readl(iommu->reg + DMAR_IQH_REG);
|
||||
u64 iqe_err = dmar_readq(iommu->reg + DMAR_IQER_REG);
|
||||
struct qi_desc *desc = iommu->qi->desc + head;
|
||||
|
||||
if (fault & DMA_FSTS_IQE)
|
||||
pr_err("VT-d detected Invalidation Queue Error: Reason %llx",
|
||||
DMAR_IQER_REG_IQEI(iqe_err));
|
||||
if (fault & DMA_FSTS_ITE)
|
||||
pr_err("VT-d detected Invalidation Time-out Error: SID %llx",
|
||||
DMAR_IQER_REG_ITESID(iqe_err));
|
||||
if (fault & DMA_FSTS_ICE)
|
||||
pr_err("VT-d detected Invalidation Completion Error: SID %llx",
|
||||
DMAR_IQER_REG_ICESID(iqe_err));
|
||||
|
||||
pr_err("QI HEAD: %s qw0 = 0x%llx, qw1 = 0x%llx\n",
|
||||
qi_type_string(desc->qw0 & 0xf),
|
||||
(unsigned long long)desc->qw0,
|
||||
(unsigned long long)desc->qw1);
|
||||
|
||||
head = ((head >> qi_shift(iommu)) + QI_LENGTH - 1) % QI_LENGTH;
|
||||
head <<= qi_shift(iommu);
|
||||
desc = iommu->qi->desc + head;
|
||||
|
||||
pr_err("QI PRIOR: %s qw0 = 0x%llx, qw1 = 0x%llx\n",
|
||||
qi_type_string(desc->qw0 & 0xf),
|
||||
(unsigned long long)desc->qw0,
|
||||
(unsigned long long)desc->qw1);
|
||||
}
|
||||
|
||||
static int qi_check_fault(struct intel_iommu *iommu, int index, int wait_index)
|
||||
{
|
||||
u32 fault;
|
||||
@ -1216,6 +1273,8 @@ static int qi_check_fault(struct intel_iommu *iommu, int index, int wait_index)
|
||||
return -EAGAIN;
|
||||
|
||||
fault = readl(iommu->reg + DMAR_FSTS_REG);
|
||||
if (fault & (DMA_FSTS_IQE | DMA_FSTS_ITE | DMA_FSTS_ICE))
|
||||
qi_dump_fault(iommu, fault);
|
||||
|
||||
/*
|
||||
* If IQE happens, the head points to the descriptor associated
|
||||
@ -1232,12 +1291,10 @@ static int qi_check_fault(struct intel_iommu *iommu, int index, int wait_index)
|
||||
* used by software as private data. We won't print
|
||||
* out these two qw's for security consideration.
|
||||
*/
|
||||
pr_err("VT-d detected invalid descriptor: qw0 = %llx, qw1 = %llx\n",
|
||||
(unsigned long long)desc->qw0,
|
||||
(unsigned long long)desc->qw1);
|
||||
memcpy(desc, qi->desc + (wait_index << shift),
|
||||
1 << shift);
|
||||
writel(DMA_FSTS_IQE, iommu->reg + DMAR_FSTS_REG);
|
||||
pr_info("Invalidation Queue Error (IQE) cleared\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
@ -1254,6 +1311,7 @@ static int qi_check_fault(struct intel_iommu *iommu, int index, int wait_index)
|
||||
tail = ((tail >> shift) - 1 + QI_LENGTH) % QI_LENGTH;
|
||||
|
||||
writel(DMA_FSTS_ITE, iommu->reg + DMAR_FSTS_REG);
|
||||
pr_info("Invalidation Time-out Error (ITE) cleared\n");
|
||||
|
||||
do {
|
||||
if (qi->desc_status[head] == QI_IN_USE)
|
||||
@ -1265,8 +1323,10 @@ static int qi_check_fault(struct intel_iommu *iommu, int index, int wait_index)
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (fault & DMA_FSTS_ICE)
|
||||
if (fault & DMA_FSTS_ICE) {
|
||||
writel(DMA_FSTS_ICE, iommu->reg + DMAR_FSTS_REG);
|
||||
pr_info("Invalidation Completion Error (ICE) cleared\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <linux/io-64-nonatomic-lo-hi.h>
|
||||
#include <linux/dmar.h>
|
||||
#include <linux/ioasid.h>
|
||||
#include <linux/bitfield.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/iommu.h>
|
||||
@ -80,6 +81,7 @@
|
||||
#define DMAR_IQ_SHIFT 4 /* Invalidation queue head/tail shift */
|
||||
#define DMAR_IQA_REG 0x90 /* Invalidation queue addr register */
|
||||
#define DMAR_ICS_REG 0x9c /* Invalidation complete status register */
|
||||
#define DMAR_IQER_REG 0xb0 /* Invalidation queue error record register */
|
||||
#define DMAR_IRTA_REG 0xb8 /* Interrupt remapping table addr register */
|
||||
#define DMAR_PQH_REG 0xc0 /* Page request queue head register */
|
||||
#define DMAR_PQT_REG 0xc8 /* Page request queue tail register */
|
||||
@ -126,6 +128,10 @@
|
||||
#define DMAR_VCMD_REG 0xe10 /* Virtual command register */
|
||||
#define DMAR_VCRSP_REG 0xe20 /* Virtual command response register */
|
||||
|
||||
#define DMAR_IQER_REG_IQEI(reg) FIELD_GET(GENMASK_ULL(3, 0), reg)
|
||||
#define DMAR_IQER_REG_ITESID(reg) FIELD_GET(GENMASK_ULL(47, 32), reg)
|
||||
#define DMAR_IQER_REG_ICESID(reg) FIELD_GET(GENMASK_ULL(63, 48), reg)
|
||||
|
||||
#define OFFSET_STRIDE (9)
|
||||
|
||||
#define dmar_readq(a) readq(a)
|
||||
|
Loading…
Reference in New Issue
Block a user