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 | |
| 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>
| -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 | } |
