diff options
author | David Woodhouse <David.Woodhouse@intel.com> | 2009-11-09 17:15:15 -0500 |
---|---|---|
committer | David Woodhouse <David.Woodhouse@intel.com> | 2009-11-09 17:15:15 -0500 |
commit | 86cf898e1d0fca245173980e3897580db38569a8 (patch) | |
tree | fe9ba4ed67ef8e5ae430f0d7d69fba68f70869d7 /drivers/pci | |
parent | 799dd75b1a8380a967c929a4551895788c374b31 (diff) |
intel-iommu: Check for 'DMAR at zero' BIOS error earlier.
Chris Wright has some patches which let us fall back to swiotlb nicely
if IOMMU initialisation fails. But those are a bit much for 2.6.32.
Instead, let's shift the check for the biggest problem, the HP and Acer
BIOS bug which reports a DMAR at physical address zero. That one can
actually be checked much earlier -- before we even admit to having
detected an IOMMU in the first place. So the swiotlb init goes ahead as
we want.
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/dmar.c | 49 |
1 files changed, 39 insertions, 10 deletions
diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index 22b02c6df854..e5f8fc164fd3 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c | |||
@@ -175,15 +175,6 @@ dmar_parse_one_drhd(struct acpi_dmar_header *header) | |||
175 | int ret = 0; | 175 | int ret = 0; |
176 | 176 | ||
177 | drhd = (struct acpi_dmar_hardware_unit *)header; | 177 | drhd = (struct acpi_dmar_hardware_unit *)header; |
178 | if (!drhd->address) { | ||
179 | /* Promote an attitude of violence to a BIOS engineer today */ | ||
180 | WARN(1, "Your BIOS is broken; DMAR reported at address zero!\n" | ||
181 | "BIOS vendor: %s; Ver: %s; Product Version: %s\n", | ||
182 | dmi_get_system_info(DMI_BIOS_VENDOR), | ||
183 | dmi_get_system_info(DMI_BIOS_VERSION), | ||
184 | dmi_get_system_info(DMI_PRODUCT_VERSION)); | ||
185 | return -ENODEV; | ||
186 | } | ||
187 | dmaru = kzalloc(sizeof(*dmaru), GFP_KERNEL); | 178 | dmaru = kzalloc(sizeof(*dmaru), GFP_KERNEL); |
188 | if (!dmaru) | 179 | if (!dmaru) |
189 | return -ENOMEM; | 180 | return -ENOMEM; |
@@ -591,12 +582,50 @@ int __init dmar_table_init(void) | |||
591 | return 0; | 582 | return 0; |
592 | } | 583 | } |
593 | 584 | ||
585 | int __init check_zero_address(void) | ||
586 | { | ||
587 | struct acpi_table_dmar *dmar; | ||
588 | struct acpi_dmar_header *entry_header; | ||
589 | struct acpi_dmar_hardware_unit *drhd; | ||
590 | |||
591 | dmar = (struct acpi_table_dmar *)dmar_tbl; | ||
592 | entry_header = (struct acpi_dmar_header *)(dmar + 1); | ||
593 | |||
594 | while (((unsigned long)entry_header) < | ||
595 | (((unsigned long)dmar) + dmar_tbl->length)) { | ||
596 | /* Avoid looping forever on bad ACPI tables */ | ||
597 | if (entry_header->length == 0) { | ||
598 | printk(KERN_WARNING PREFIX | ||
599 | "Invalid 0-length structure\n"); | ||
600 | return 0; | ||
601 | } | ||
602 | |||
603 | if (entry_header->type == ACPI_DMAR_TYPE_HARDWARE_UNIT) { | ||
604 | drhd = (void *)entry_header; | ||
605 | if (!drhd->address) { | ||
606 | /* Promote an attitude of violence to a BIOS engineer today */ | ||
607 | WARN(1, "Your BIOS is broken; DMAR reported at address zero!\n" | ||
608 | "BIOS vendor: %s; Ver: %s; Product Version: %s\n", | ||
609 | dmi_get_system_info(DMI_BIOS_VENDOR), | ||
610 | dmi_get_system_info(DMI_BIOS_VERSION), | ||
611 | dmi_get_system_info(DMI_PRODUCT_VERSION)); | ||
612 | return 0; | ||
613 | } | ||
614 | break; | ||
615 | } | ||
616 | |||
617 | entry_header = ((void *)entry_header + entry_header->length); | ||
618 | } | ||
619 | return 1; | ||
620 | } | ||
621 | |||
594 | void __init detect_intel_iommu(void) | 622 | void __init detect_intel_iommu(void) |
595 | { | 623 | { |
596 | int ret; | 624 | int ret; |
597 | 625 | ||
598 | ret = dmar_table_detect(); | 626 | ret = dmar_table_detect(); |
599 | 627 | if (ret) | |
628 | ret = check_zero_address(); | ||
600 | { | 629 | { |
601 | #ifdef CONFIG_INTR_REMAP | 630 | #ifdef CONFIG_INTR_REMAP |
602 | struct acpi_table_dmar *dmar; | 631 | struct acpi_table_dmar *dmar; |