x86/PCI: Clip only host bridge windows for E820 regions
authorBjorn Helgaas <bhelgaas@google.com>
Thu, 3 Mar 2022 22:00:39 +0000 (16:00 -0600)
committerBjorn Helgaas <bhelgaas@google.com>
Fri, 8 Apr 2022 16:35:01 +0000 (11:35 -0500)
ACPI firmware advertises PCI host bridge resources via PNP0A03 _CRS
methods.  Some BIOSes include non-window address space in _CRS, and if we
allocate that non-window space for PCI devices, they don't work.

4dc2287c1805 ("x86: avoid E820 regions when allocating address space")
works around this issue by clipping out any regions mentioned in the E820
table in the allocate_resource() path, but the implementation has a couple
issues:

  - The clipping is done for *all* allocations, not just those for PCI
    address space, and

  - The clipping is done at each allocation instead of being done once when
    setting up the host bridge windows.

Rework the implementation so we only clip PCI host bridge windows, and we
do it once when setting them up.

Example output changes:

    BIOS-e820: [mem 0x00000000b0000000-0x00000000c00fffff] reserved
  + acpi PNP0A08:00: clipped [mem 0xc0000000-0xfebfffff window] to [mem 0xc0100000-0xfebfffff window] for e820 entry [mem 0xb0000000-0xc00fffff]
  - pci_bus 0000:00: root bus resource [mem 0xc0000000-0xfebfffff window]
  + pci_bus 0000:00: root bus resource [mem 0xc0100000-0xfebfffff window]

Link: https://lore.kernel.org/r/20220304035110.988712-3-helgaas@kernel.org
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
arch/x86/include/asm/e820/api.h
arch/x86/kernel/resource.c
arch/x86/pci/acpi.c

index e8f58ddd06d97fbce6ef1f701f9f91766ef2847d..5a39ed59b6db7d1effbdc46bd2c0ba1798f709b3 100644 (file)
@@ -4,6 +4,9 @@
 
 #include <asm/e820/types.h>
 
+struct device;
+struct resource;
+
 extern struct e820_table *e820_table;
 extern struct e820_table *e820_table_kexec;
 extern struct e820_table *e820_table_firmware;
@@ -43,6 +46,8 @@ extern void e820__register_nosave_regions(unsigned long limit_pfn);
 
 extern int  e820__get_entry_type(u64 start, u64 end);
 
+extern void remove_e820_regions(struct device *dev, struct resource *avail);
+
 /*
  * Returns true iff the specified range [start,end) is completely contained inside
  * the ISA region.
index 30d524adb012d592a74dd95eeccf011de8ff015c..db2b350a37b774c051f37129860c9a4fe73daa3b 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
+#include <linux/dev_printk.h>
 #include <linux/ioport.h>
-#include <linux/printk.h>
 #include <asm/e820/api.h>
 
 static void resource_clip(struct resource *res, resource_size_t start,
@@ -24,13 +24,16 @@ static void resource_clip(struct resource *res, resource_size_t start,
                res->start = end + 1;
 }
 
-static void remove_e820_regions(struct resource *avail)
+void remove_e820_regions(struct device *dev, struct resource *avail)
 {
        int i;
        struct e820_entry *entry;
        u64 e820_start, e820_end;
        struct resource orig = *avail;
 
+       if (!(avail->flags & IORESOURCE_MEM))
+               return;
+
        for (i = 0; i < e820_table->nr_entries; i++) {
                entry = &e820_table->entries[i];
                e820_start = entry->addr;
@@ -38,7 +41,7 @@ static void remove_e820_regions(struct resource *avail)
 
                resource_clip(avail, e820_start, e820_end);
                if (orig.start != avail->start || orig.end != avail->end) {
-                       pr_info("clipped %pR to %pR for e820 entry [mem %#010Lx-%#010Lx]\n",
+                       dev_info(dev, "clipped %pR to %pR for e820 entry [mem %#010Lx-%#010Lx]\n",
                                 &orig, avail, e820_start, e820_end);
                        orig = *avail;
                }
@@ -52,9 +55,6 @@ void arch_remove_reservations(struct resource *avail)
         * the low 1MB unconditionally, as this area is needed for some ISA
         * cards requiring a memory range, e.g. the i82365 PCMCIA controller.
         */
-       if (avail->flags & IORESOURCE_MEM) {
+       if (avail->flags & IORESOURCE_MEM)
                resource_clip(avail, BIOS_ROM_BASE, BIOS_ROM_END);
-
-               remove_e820_regions(avail);
-       }
 }
index 052f1d78a5620a22458ce99eeff6258dc2f026e0..562c81a51ea02022718831e52bc581fe4b384d05 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/pci-acpi.h>
 #include <asm/numa.h>
 #include <asm/pci_x86.h>
+#include <asm/e820/api.h>
 
 struct pci_root_info {
        struct acpi_pci_root_info common;
@@ -299,6 +300,10 @@ static int pci_acpi_root_prepare_resources(struct acpi_pci_root_info *ci)
        int status;
 
        status = acpi_pci_probe_root_resources(ci);
+
+       resource_list_for_each_entry(entry, &ci->resources)
+               remove_e820_regions(&device->dev, entry->res);
+
        if (pci_use_crs) {
                resource_list_for_each_entry_safe(entry, tmp, &ci->resources)
                        if (resource_is_pcicfg_ioport(entry->res))