diff options
author | David Woodhouse <David.Woodhouse@intel.com> | 2009-08-04 04:17:20 -0400 |
---|---|---|
committer | David Woodhouse <David.Woodhouse@intel.com> | 2009-08-04 04:17:24 -0400 |
commit | 0815565adfe3f4c369110c57d8ffe83caefeed68 (patch) | |
tree | c1a2436a7f8c7b61a09ab1b809f65bd5b1325ccc /drivers/pci | |
parent | cfc65dd57967f2e0c7b3a8b73e6d12470b1cf1c1 (diff) |
intel-iommu: Cope with broken HP DC7900 BIOS
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>
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/dmar.c | 22 |
1 files changed, 18 insertions, 4 deletions
diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index 7b287cb38b7a..380b60e677e0 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c | |||
@@ -632,20 +632,31 @@ int alloc_iommu(struct dmar_drhd_unit *drhd) | |||
632 | iommu->cap = dmar_readq(iommu->reg + DMAR_CAP_REG); | 632 | iommu->cap = dmar_readq(iommu->reg + DMAR_CAP_REG); |
633 | iommu->ecap = dmar_readq(iommu->reg + DMAR_ECAP_REG); | 633 | iommu->ecap = dmar_readq(iommu->reg + DMAR_ECAP_REG); |
634 | 634 | ||
635 | if (iommu->cap == (uint64_t)-1 && iommu->ecap == (uint64_t)-1) { | ||
636 | /* Promote an attitude of violence to a BIOS engineer today */ | ||
637 | WARN(1, "Your BIOS is broken; DMAR reported at address %llx returns all ones!\n" | ||
638 | "BIOS vendor: %s; Ver: %s; Product Version: %s\n", | ||
639 | drhd->reg_base_addr, | ||
640 | dmi_get_system_info(DMI_BIOS_VENDOR), | ||
641 | dmi_get_system_info(DMI_BIOS_VERSION), | ||
642 | dmi_get_system_info(DMI_PRODUCT_VERSION)); | ||
643 | goto err_unmap; | ||
644 | } | ||
645 | |||
635 | #ifdef CONFIG_DMAR | 646 | #ifdef CONFIG_DMAR |
636 | agaw = iommu_calculate_agaw(iommu); | 647 | agaw = iommu_calculate_agaw(iommu); |
637 | if (agaw < 0) { | 648 | if (agaw < 0) { |
638 | printk(KERN_ERR | 649 | printk(KERN_ERR |
639 | "Cannot get a valid agaw for iommu (seq_id = %d)\n", | 650 | "Cannot get a valid agaw for iommu (seq_id = %d)\n", |
640 | iommu->seq_id); | 651 | iommu->seq_id); |
641 | goto error; | 652 | goto err_unmap; |
642 | } | 653 | } |
643 | msagaw = iommu_calculate_max_sagaw(iommu); | 654 | msagaw = iommu_calculate_max_sagaw(iommu); |
644 | if (msagaw < 0) { | 655 | if (msagaw < 0) { |
645 | printk(KERN_ERR | 656 | printk(KERN_ERR |
646 | "Cannot get a valid max agaw for iommu (seq_id = %d)\n", | 657 | "Cannot get a valid max agaw for iommu (seq_id = %d)\n", |
647 | iommu->seq_id); | 658 | iommu->seq_id); |
648 | goto error; | 659 | goto err_unmap; |
649 | } | 660 | } |
650 | #endif | 661 | #endif |
651 | iommu->agaw = agaw; | 662 | iommu->agaw = agaw; |
@@ -665,7 +676,7 @@ int alloc_iommu(struct dmar_drhd_unit *drhd) | |||
665 | } | 676 | } |
666 | 677 | ||
667 | ver = readl(iommu->reg + DMAR_VER_REG); | 678 | ver = readl(iommu->reg + DMAR_VER_REG); |
668 | pr_debug("IOMMU %llx: ver %d:%d cap %llx ecap %llx\n", | 679 | pr_info("IOMMU %llx: ver %d:%d cap %llx ecap %llx\n", |
669 | (unsigned long long)drhd->reg_base_addr, | 680 | (unsigned long long)drhd->reg_base_addr, |
670 | DMAR_VER_MAJOR(ver), DMAR_VER_MINOR(ver), | 681 | DMAR_VER_MAJOR(ver), DMAR_VER_MINOR(ver), |
671 | (unsigned long long)iommu->cap, | 682 | (unsigned long long)iommu->cap, |
@@ -675,7 +686,10 @@ int alloc_iommu(struct dmar_drhd_unit *drhd) | |||
675 | 686 | ||
676 | drhd->iommu = iommu; | 687 | drhd->iommu = iommu; |
677 | return 0; | 688 | return 0; |
678 | error: | 689 | |
690 | err_unmap: | ||
691 | iounmap(iommu->reg); | ||
692 | error: | ||
679 | kfree(iommu); | 693 | kfree(iommu); |
680 | return -1; | 694 | return -1; |
681 | } | 695 | } |