summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Wright <chrisw@sous-sol.org>2009-12-02 09:17:13 +0000
committerDavid Woodhouse <David.Woodhouse@intel.com>2009-12-08 10:02:15 +0000
commit2c99220810c1c79322034628b993573b088ff2da (patch)
tree0b5e69d0ad7a6da667b752ffd727781c875cccff
parentec208491936d6adb8a70c3dd4a517cdfe54e823d (diff)
intel-iommu: Detect DMAR in hyperspace at probe time.
Many BIOSes will lie to us about the existence of an IOMMU, and claim that there is one at an address which actually returns all 0xFF. We need to detect this early, so that we know we don't have a viable IOMMU and can set up swiotlb before it's too late. Cc: stable@kernel.org Signed-off-by: Chris Wright <chrisw@sous-sol.org> Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
-rw-r--r--drivers/pci/dmar.c34
1 files changed, 29 insertions, 5 deletions
diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c
index 525a32487ab..56883fc1c7b 100644
--- a/drivers/pci/dmar.c
+++ b/drivers/pci/dmar.c
@@ -632,6 +632,9 @@ int __init check_zero_address(void)
}
if (entry_header->type == ACPI_DMAR_TYPE_HARDWARE_UNIT) {
+ void __iomem *addr;
+ u64 cap, ecap;
+
drhd = (void *)entry_header;
if (!drhd->address) {
/* Promote an attitude of violence to a BIOS engineer today */
@@ -640,17 +643,38 @@ int __init check_zero_address(void)
dmi_get_system_info(DMI_BIOS_VENDOR),
dmi_get_system_info(DMI_BIOS_VERSION),
dmi_get_system_info(DMI_PRODUCT_VERSION));
-#ifdef CONFIG_DMAR
- dmar_disabled = 1;
-#endif
- return 0;
+ goto failed;
+ }
+
+ addr = early_ioremap(drhd->address, VTD_PAGE_SIZE);
+ if (!addr ) {
+ printk("IOMMU: can't validate: %llx\n", drhd->address);
+ goto failed;
+ }
+ cap = dmar_readq(addr + DMAR_CAP_REG);
+ ecap = dmar_readq(addr + DMAR_ECAP_REG);
+ early_iounmap(addr, VTD_PAGE_SIZE);
+ if (cap == (uint64_t)-1 && ecap == (uint64_t)-1) {
+ /* Promote an attitude of violence to a BIOS engineer today */
+ WARN(1, "Your BIOS is broken; DMAR reported at address %llx returns all ones!\n"
+ "BIOS vendor: %s; Ver: %s; Product Version: %s\n",
+ drhd->address,
+ dmi_get_system_info(DMI_BIOS_VENDOR),
+ dmi_get_system_info(DMI_BIOS_VERSION),
+ dmi_get_system_info(DMI_PRODUCT_VERSION));
+ goto failed;
}
- break;
}
entry_header = ((void *)entry_header + entry_header->length);
}
return 1;
+
+failed:
+#ifdef CONFIG_DMAR
+ dmar_disabled = 1;
+#endif
+ return 0;
}
void __init detect_intel_iommu(void)