intel-iommu: Cope with broken HP DC7900 BIOS
authorDavid Woodhouse <David.Woodhouse@intel.com>
Tue, 4 Aug 2009 08:17:20 +0000 (09:17 +0100)
committerDavid Woodhouse <David.Woodhouse@intel.com>
Tue, 4 Aug 2009 08:17:24 +0000 (09:17 +0100)
Yet another reason why trusting this stuff to the BIOS was a bad idea.
The HP DC7900 BIOS reports an iommu at an address which just returns all
ones, when VT-d is disabled in the BIOS.

Fix up the missing iounmap in the error paths while we're at it.

Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
drivers/pci/dmar.c

index 7b287cb38b7ad76b562d1fd3684b1bca676e4bb7..380b60e677e038b51c781609cf3eb03f5d11bc0c 100644 (file)
@@ -632,20 +632,31 @@ int alloc_iommu(struct dmar_drhd_unit *drhd)
        iommu->cap = dmar_readq(iommu->reg + DMAR_CAP_REG);
        iommu->ecap = dmar_readq(iommu->reg + DMAR_ECAP_REG);
 
+       if (iommu->cap == (uint64_t)-1 && iommu->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->reg_base_addr,
+                    dmi_get_system_info(DMI_BIOS_VENDOR),
+                    dmi_get_system_info(DMI_BIOS_VERSION),
+                    dmi_get_system_info(DMI_PRODUCT_VERSION));
+               goto err_unmap;
+       }
+
 #ifdef CONFIG_DMAR
        agaw = iommu_calculate_agaw(iommu);
        if (agaw < 0) {
                printk(KERN_ERR
                       "Cannot get a valid agaw for iommu (seq_id = %d)\n",
                       iommu->seq_id);
-               goto error;
+               goto err_unmap;
        }
        msagaw = iommu_calculate_max_sagaw(iommu);
        if (msagaw < 0) {
                printk(KERN_ERR
                        "Cannot get a valid max agaw for iommu (seq_id = %d)\n",
                        iommu->seq_id);
-               goto error;
+               goto err_unmap;
        }
 #endif
        iommu->agaw = agaw;
@@ -665,7 +676,7 @@ int alloc_iommu(struct dmar_drhd_unit *drhd)
        }
 
        ver = readl(iommu->reg + DMAR_VER_REG);
-       pr_debug("IOMMU %llx: ver %d:%d cap %llx ecap %llx\n",
+       pr_info("IOMMU %llx: ver %d:%d cap %llx ecap %llx\n",
                (unsigned long long)drhd->reg_base_addr,
                DMAR_VER_MAJOR(ver), DMAR_VER_MINOR(ver),
                (unsigned long long)iommu->cap,
@@ -675,7 +686,10 @@ int alloc_iommu(struct dmar_drhd_unit *drhd)
 
        drhd->iommu = iommu;
        return 0;
-error:
+
+ err_unmap:
+       iounmap(iommu->reg);
+ error:
        kfree(iommu);
        return -1;
 }