iommu/amd: Work around wrong IOAPIC device-id in IVRS table
On some systems the BIOS puts the wrong device-id for the
IO-APIC into the IVRS table. The result is that interrupt
remapping is not working for the IO-APIC irqs. This usually
means a kernel panic at boot because the timer is not
working.
Fix this kernel panic by disabling interrupt remapping if
this problem is discovered in the IVRS table.
Reported-by: Andrew Oakley <andrew@ado.is-a-geek.net>
Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c
index 18b0d99..81837b0 100644
--- a/drivers/iommu/amd_iommu_init.c
+++ b/drivers/iommu/amd_iommu_init.c
@@ -1599,21 +1599,46 @@
#endif
}
+/* SB IOAPIC is always on this device in AMD systems */
+#define IOAPIC_SB_DEVID ((0x00 << 8) | PCI_DEVFN(0x14, 0))
+
static bool __init check_ioapic_information(void)
{
+ bool ret, has_sb_ioapic;
int idx;
- for (idx = 0; idx < nr_ioapics; idx++) {
- int id = mpc_ioapic_id(idx);
+ has_sb_ioapic = false;
+ ret = false;
- if (get_ioapic_devid(id) < 0) {
- pr_err(FW_BUG "AMD-Vi: IO-APIC[%d] not in IVRS table\n", id);
- pr_err("AMD-Vi: Disabling interrupt remapping due to BIOS Bug\n");
- return false;
+ for (idx = 0; idx < nr_ioapics; idx++) {
+ int devid, id = mpc_ioapic_id(idx);
+
+ devid = get_ioapic_devid(id);
+ if (devid < 0) {
+ pr_err(FW_BUG "AMD-Vi: IOAPIC[%d] not in IVRS table\n", id);
+ ret = false;
+ } else if (devid == IOAPIC_SB_DEVID) {
+ has_sb_ioapic = true;
+ ret = true;
}
}
- return true;
+ if (!has_sb_ioapic) {
+ /*
+ * We expect the SB IOAPIC to be listed in the IVRS
+ * table. The system timer is connected to the SB IOAPIC
+ * and if we don't have it in the list the system will
+ * panic at boot time. This situation usually happens
+ * when the BIOS is buggy and provides us the wrong
+ * device id for the IOAPIC in the system.
+ */
+ pr_err(FW_BUG "AMD-Vi: No southbridge IOAPIC found in IVRS table\n");
+ }
+
+ if (!ret)
+ pr_err("AMD-Vi: Disabling interrupt remapping due to BIOS Bug(s)\n");
+
+ return ret;
}
static void __init free_dma_resources(void)