Skip to content

Commit

Permalink
iommu/vt-d: Report more information about invalidation errors
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Lu Baolu authored and Joerg Roedel committed Mar 18, 2021
1 parent dec991e commit 6ca69e5
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 4 deletions.
68 changes: 64 additions & 4 deletions drivers/iommu/intel/dmar.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand All @@ -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;
}
}
Expand All @@ -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)
Expand All @@ -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;
}
Expand Down
6 changes: 6 additions & 0 deletions include/linux/intel-iommu.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit 6ca69e5

Please sign in to comment.