Преглед на файлове

x86/amd-iommu: Workaround for erratum 63

There is an erratum for IOMMU hardware which documents
undefined behavior when forwarding SMI requests from
peripherals and the DTE of that peripheral has a sysmgt
value of 01b. This problem caused weird IO_PAGE_FAULTS in my
case.
This patch implements the suggested workaround for that
erratum into the AMD IOMMU driver.  The erratum is
documented with number 63.

Cc: stable@kernel.org
Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
Joerg Roedel преди 15 години
родител
ревизия
c5cca146aa
променени са 3 файла, в които са добавени 25 реда и са изтрити 0 реда
  1. 1 0
      arch/x86/include/asm/amd_iommu.h
  2. 2 0
      arch/x86/kernel/amd_iommu.c
  3. 22 0
      arch/x86/kernel/amd_iommu_init.c

+ 1 - 0
arch/x86/include/asm/amd_iommu.h

@@ -30,6 +30,7 @@ extern irqreturn_t amd_iommu_int_handler(int irq, void *data);
 extern void amd_iommu_flush_all_domains(void);
 extern void amd_iommu_flush_all_domains(void);
 extern void amd_iommu_flush_all_devices(void);
 extern void amd_iommu_flush_all_devices(void);
 extern void amd_iommu_shutdown(void);
 extern void amd_iommu_shutdown(void);
+extern void amd_iommu_apply_erratum_63(u16 devid);
 #else
 #else
 static inline int amd_iommu_init(void) { return -ENODEV; }
 static inline int amd_iommu_init(void) { return -ENODEV; }
 static inline void amd_iommu_detect(void) { }
 static inline void amd_iommu_detect(void) { }

+ 2 - 0
arch/x86/kernel/amd_iommu.c

@@ -1114,6 +1114,8 @@ static void __detach_device(struct protection_domain *domain, u16 devid)
 	amd_iommu_dev_table[devid].data[1] = 0;
 	amd_iommu_dev_table[devid].data[1] = 0;
 	amd_iommu_dev_table[devid].data[2] = 0;
 	amd_iommu_dev_table[devid].data[2] = 0;
 
 
+	amd_iommu_apply_erratum_63(devid);
+
 	/* decrease reference counter */
 	/* decrease reference counter */
 	domain->dev_cnt -= 1;
 	domain->dev_cnt -= 1;
 
 

+ 22 - 0
arch/x86/kernel/amd_iommu_init.c

@@ -509,6 +509,26 @@ static void set_dev_entry_bit(u16 devid, u8 bit)
 	amd_iommu_dev_table[devid].data[i] |= (1 << _bit);
 	amd_iommu_dev_table[devid].data[i] |= (1 << _bit);
 }
 }
 
 
+static int get_dev_entry_bit(u16 devid, u8 bit)
+{
+	int i = (bit >> 5) & 0x07;
+	int _bit = bit & 0x1f;
+
+	return (amd_iommu_dev_table[devid].data[i] & (1 << _bit)) >> _bit;
+}
+
+
+void amd_iommu_apply_erratum_63(u16 devid)
+{
+	int sysmgt;
+
+	sysmgt = get_dev_entry_bit(devid, DEV_ENTRY_SYSMGT1) |
+		 (get_dev_entry_bit(devid, DEV_ENTRY_SYSMGT2) << 1);
+
+	if (sysmgt == 0x01)
+		set_dev_entry_bit(devid, DEV_ENTRY_IW);
+}
+
 /* Writes the specific IOMMU for a device into the rlookup table */
 /* Writes the specific IOMMU for a device into the rlookup table */
 static void __init set_iommu_for_device(struct amd_iommu *iommu, u16 devid)
 static void __init set_iommu_for_device(struct amd_iommu *iommu, u16 devid)
 {
 {
@@ -537,6 +557,8 @@ static void __init set_dev_entry_from_acpi(struct amd_iommu *iommu,
 	if (flags & ACPI_DEVFLAG_LINT1)
 	if (flags & ACPI_DEVFLAG_LINT1)
 		set_dev_entry_bit(devid, DEV_ENTRY_LINT1_PASS);
 		set_dev_entry_bit(devid, DEV_ENTRY_LINT1_PASS);
 
 
+	amd_iommu_apply_erratum_63(devid);
+
 	set_iommu_for_device(iommu, devid);
 	set_iommu_for_device(iommu, devid);
 }
 }