intel-iommu: Detect DMAR in hyperspace at probe time.
[safe/jmp/linux-2.6] / drivers / pci / dmar.c
index 525a324..56883fc 100644 (file)
@@ -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)