From 86cf898e1d0fca245173980e3897580db38569a8 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 9 Nov 2009 22:15:15 +0000 Subject: 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 --- drivers/pci/dmar.c | 49 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 10 deletions(-) (limited to 'drivers/pci/dmar.c') diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index 22b02c6df85..e5f8fc164fd 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c @@ -175,15 +175,6 @@ dmar_parse_one_drhd(struct acpi_dmar_header *header) int ret = 0; drhd = (struct acpi_dmar_hardware_unit *)header; - if (!drhd->address) { - /* Promote an attitude of violence to a BIOS engineer today */ - WARN(1, "Your BIOS is broken; DMAR reported at address zero!\n" - "BIOS vendor: %s; Ver: %s; Product Version: %s\n", - dmi_get_system_info(DMI_BIOS_VENDOR), - dmi_get_system_info(DMI_BIOS_VERSION), - dmi_get_system_info(DMI_PRODUCT_VERSION)); - return -ENODEV; - } dmaru = kzalloc(sizeof(*dmaru), GFP_KERNEL); if (!dmaru) return -ENOMEM; @@ -591,12 +582,50 @@ int __init dmar_table_init(void) return 0; } +int __init check_zero_address(void) +{ + struct acpi_table_dmar *dmar; + struct acpi_dmar_header *entry_header; + struct acpi_dmar_hardware_unit *drhd; + + dmar = (struct acpi_table_dmar *)dmar_tbl; + entry_header = (struct acpi_dmar_header *)(dmar + 1); + + while (((unsigned long)entry_header) < + (((unsigned long)dmar) + dmar_tbl->length)) { + /* Avoid looping forever on bad ACPI tables */ + if (entry_header->length == 0) { + printk(KERN_WARNING PREFIX + "Invalid 0-length structure\n"); + return 0; + } + + if (entry_header->type == ACPI_DMAR_TYPE_HARDWARE_UNIT) { + drhd = (void *)entry_header; + if (!drhd->address) { + /* Promote an attitude of violence to a BIOS engineer today */ + WARN(1, "Your BIOS is broken; DMAR reported at address zero!\n" + "BIOS vendor: %s; Ver: %s; Product Version: %s\n", + dmi_get_system_info(DMI_BIOS_VENDOR), + dmi_get_system_info(DMI_BIOS_VERSION), + dmi_get_system_info(DMI_PRODUCT_VERSION)); + return 0; + } + break; + } + + entry_header = ((void *)entry_header + entry_header->length); + } + return 1; +} + void __init detect_intel_iommu(void) { int ret; ret = dmar_table_detect(); - + if (ret) + ret = check_zero_address(); { #ifdef CONFIG_INTR_REMAP struct acpi_table_dmar *dmar; -- cgit v1.2.3 From 9d5ce73a64be2be8112147a3e0b551ad9cd1247b Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Tue, 10 Nov 2009 19:46:16 +0900 Subject: x86: intel-iommu: Convert detect_intel_iommu to use iommu_init hook This changes detect_intel_iommu() to set intel_iommu_init() to iommu_init hook if detect_intel_iommu() finds the IOMMU. Signed-off-by: FUJITA Tomonori Cc: chrisw@sous-sol.org Cc: dwmw2@infradead.org Cc: joerg.roedel@amd.com Cc: muli@il.ibm.com LKML-Reference: <1257849980-22640-6-git-send-email-fujita.tomonori@lab.ntt.co.jp> [ -v2: build fix for the !CONFIG_DMAR case ] Signed-off-by: Ingo Molnar --- drivers/pci/dmar.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/pci/dmar.c') diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index 22b02c6df85..bce9cd7c755 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c @@ -616,6 +616,10 @@ void __init detect_intel_iommu(void) if (ret && !no_iommu && !iommu_detected && !swiotlb && !dmar_disabled) iommu_detected = 1; +#endif +#ifdef CONFIG_X86 + if (ret) + x86_init.iommu.iommu_init = intel_iommu_init; #endif } early_acpi_os_unmap_memory(dmar_tbl, dmar_tbl_size); -- cgit v1.2.3 From 75f1cdf1dda92cae037ec848ae63690d91913eac Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Tue, 10 Nov 2009 19:46:20 +0900 Subject: x86: Handle HW IOMMU initialization failure gracefully If HW IOMMU initialization fails (Intel VT-d often does this, typically due to BIOS bugs), we fall back to nommu. It doesn't work for the majority since nowadays we have more than 4GB memory so we must use swiotlb instead of nommu. The problem is that it's too late to initialize swiotlb when HW IOMMU initialization fails. We need to allocate swiotlb memory earlier from bootmem allocator. Chris explained the issue in detail: http://marc.info/?l=linux-kernel&m=125657444317079&w=2 The current x86 IOMMU initialization sequence is too complicated and handling the above issue makes it more hacky. This patch changes x86 IOMMU initialization sequence to handle the above issue cleanly. The new x86 IOMMU initialization sequence are: 1. we initialize the swiotlb (and setting swiotlb to 1) in the case of (max_pfn > MAX_DMA32_PFN && !no_iommu). dma_ops is set to swiotlb_dma_ops or nommu_dma_ops. if swiotlb usage is forced by the boot option, we finish here. 2. we call the detection functions of all the IOMMUs 3. the detection function sets x86_init.iommu.iommu_init to the IOMMU initialization function (so we can avoid calling the initialization functions of all the IOMMUs needlessly). 4. if the IOMMU initialization function doesn't need to swiotlb then sets swiotlb to zero (e.g. the initialization is sucessful). 5. if we find that swiotlb is set to zero, we free swiotlb resource. Signed-off-by: FUJITA Tomonori Cc: chrisw@sous-sol.org Cc: dwmw2@infradead.org Cc: joerg.roedel@amd.com Cc: muli@il.ibm.com LKML-Reference: <1257849980-22640-10-git-send-email-fujita.tomonori@lab.ntt.co.jp> Signed-off-by: Ingo Molnar --- drivers/pci/dmar.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/pci/dmar.c') diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index bce9cd7c755..437399667e5 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c @@ -613,8 +613,7 @@ void __init detect_intel_iommu(void) "x2apic and Intr-remapping.\n"); #endif #ifdef CONFIG_DMAR - if (ret && !no_iommu && !iommu_detected && !swiotlb && - !dmar_disabled) + if (ret && !no_iommu && !iommu_detected && !dmar_disabled) iommu_detected = 1; #endif #ifdef CONFIG_X86 -- cgit v1.2.3 From 5854d9c8d18359b1fc2f23c0ef2d51dd53281bd6 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 19 Nov 2009 02:18:44 +0000 Subject: Fix handling of the HP/Acer 'DMAR at zero' BIOS error for machines with <4GiB RAM. Commit 86cf898e1d0fca245173980e3897580db38569a8 ("intel-iommu: Check for 'DMAR at zero' BIOS error earlier.") was supposed to work by pretending not to detect an IOMMU if it was actually being reported by the BIOS at physical address zero. However, the intel_iommu_init() function is called unconditionally, as are the corresponding functions for other IOMMU hardware. So the patch only worked if you have RAM above the 4GiB boundary. It caused swiotlb to be initialised when no IOMMU was detected during early boot, and thus the later IOMMU init would refuse to run. But if you have less RAM than that, swiotlb wouldn't get set up and the IOMMU _would_ still end up being initialised, even though we never claimed to detect it. This patch also sets the dmar_disabled flag when the error is detected during the initial detection phase -- so that the later call to intel_iommu_init() will return without doing anything, regardless of whether swiotlb is used or not. Signed-off-by: David Woodhouse Signed-off-by: Linus Torvalds --- drivers/pci/dmar.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/pci/dmar.c') diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index e5f8fc164fd..b952ebc7a78 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c @@ -609,6 +609,9 @@ 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; } break; -- cgit v1.2.3