From 15bc3dc111d7f1444ebce32e1a610a1ad473fc75 Mon Sep 17 00:00:00 2001 From: Hans de Goede <hdegoede@redhat.com> Date: Thu, 14 Oct 2021 20:39:42 +0200 Subject: x86/PCI: Ignore E820 reservations for bridge windows on newer systems MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some firmware reports invalid PCI host bridge windows via the ACPI _CRS method; see commit 4dc2287c1805 ("x86: avoid E820 regions when allocating address space"). To work around this bug Linux, has excluded E820 reserved addresses from the PCI host bridge window since 2010. But some systems have E820 reservations that cover the entire PCI bridge memory window, so all attempts to assign memory to PCI BARs fail. For example, from a Lenovo IdeaPad 3 15IIL 81WE: [mem 0x000000004bc50000-0x00000000cfffffff] reserved pci_bus 0000:00: root bus resource [mem 0x65400000-0xbfffffff window] pci 0000:00:15.0: BAR 0: failed to assign [mem size 0x00001000 64bit] pci 0000:00:15.1: BAR 0: failed to assign [mem size 0x00001000 64bit] pci 0000:00:1f.5: BAR 0: failed to assign [mem size 0x00001000] This appears to be legal because ACPI v6.3, sec 15, table 15-374, says AddressRangeReserved means: This range of addresses is in use or reserved by the system and is not to be included in the allocatable memory pool of the operating system's memory manager. ... The address range is in use by a memory-mapped system device. Furthermore, sec 15.2 says: Address ranges defined for baseboard memory-mapped I/O devices, such as APICs, are returned as reserved. A PCI host bridge qualifies as a baseboard memory-mapped I/O device, and its apertures are in use and certainly should not be included in the general allocatable pool, so the fact that the PCI aperture is "reserved" in E820 doesn't seem like a BIOS bug. So it seems that the excluding of E820 reserved addresses is a mistake. Ideally Linux would fully stop excluding E820 reserved addresses, but then the old systems that needed 4dc2287c1805 would regress. Keep the existing behavior for old systems (BIOS year < 2018), while ignoring the E820 reservations for newer ones. Also add "pci=no_e820" and "pci=use_e820" kernel parameters to allow overriding the BIOS year heuristic. Old systems are defined here as BIOS year < 2018. This was chosen to make sure that pci_use_e820 will not be set on the currently affected systems, while at the same time also taking into account that the systems for which the E820 checking was originally added may have received BIOS updates for quite a while (esp. CVE related ones), giving them a more recent BIOS year than 2010. [bhelgaas: commit log] BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=206459 BugLink: https://bugzilla.redhat.com/show_bug.cgi?id=1868899 BugLink: https://bugzilla.redhat.com/show_bug.cgi?id=1871793 BugLink: https://bugs.launchpad.net/bugs/1878279 BugLink: https://bugs.launchpad.net/bugs/1931715 BugLink: https://bugs.launchpad.net/bugs/1932069 BugLink: https://bugs.launchpad.net/bugs/1921649 Link: https://lore.kernel.org/r/20211014183943.27717-2-hdegoede@redhat.com Signed-off-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com> Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Cc: stable@vger.kernel.org Cc: Benoit Grégoire <benoitg@coeus.ca> Cc: Hui Wang <hui.wang@canonical.com> --- Documentation/admin-guide/kernel-parameters.txt | 10 ++++++++++ arch/x86/include/asm/pci_x86.h | 13 +++++++++++++ arch/x86/kernel/resource.c | 4 ++++ arch/x86/pci/acpi.c | 24 ++++++++++++++++++++++++ arch/x86/pci/common.c | 6 ++++++ 5 files changed, 57 insertions(+) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 91ba391f9b328..2366949cc8b47 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -3949,6 +3949,16 @@ please report a bug. nocrs [X86] Ignore PCI host bridge windows from ACPI. If you need to use this, please report a bug. + use_e820 [X86] Use E820 reservations to exclude parts of + PCI host bridge windows. This is a workaround + for BIOS defects in host bridge _CRS methods. + If you need to use this, please report a bug to + <linux-pci@vger.kernel.org>. + no_e820 [X86] Ignore E820 reservations for PCI host + bridge windows. This is the default on + systems from 2018 and later. If you need + to use this, please report a bug to + <linux-pci@vger.kernel.org>. routeirq Do IRQ routing for all PCI devices. This is normally done in pci_enable_device(), so this option is a temporary workaround diff --git a/arch/x86/include/asm/pci_x86.h b/arch/x86/include/asm/pci_x86.h index 490411dba438d..c5413d8007edd 100644 --- a/arch/x86/include/asm/pci_x86.h +++ b/arch/x86/include/asm/pci_x86.h @@ -5,7 +5,10 @@ * (c) 1999 Martin Mares <mj@ucw.cz> */ +#include <linux/errno.h> +#include <linux/init.h> #include <linux/ioport.h> +#include <linux/spinlock.h> #undef DEBUG @@ -39,6 +42,8 @@ do { \ #define PCI_ROOT_NO_CRS 0x100000 #define PCI_NOASSIGN_BARS 0x200000 #define PCI_BIG_ROOT_WINDOW 0x400000 +#define PCI_USE_E820 0x800000 +#define PCI_NO_E820 0x1000000 extern unsigned int pci_probe; extern unsigned long pirq_table_addr; @@ -64,6 +69,8 @@ void pcibios_scan_specific_bus(int busn); /* pci-irq.c */ +struct pci_dev; + struct irq_info { u8 bus, devfn; /* Bus, device and function */ struct { @@ -232,3 +239,9 @@ static inline void mmio_config_writel(void __iomem *pos, u32 val) # define x86_default_pci_init_irq NULL # define x86_default_pci_fixup_irqs NULL #endif + +#if defined(CONFIG_PCI) && defined(CONFIG_ACPI) +extern bool pci_use_e820; +#else +#define pci_use_e820 false +#endif diff --git a/arch/x86/kernel/resource.c b/arch/x86/kernel/resource.c index 9b9fb7882c206..e8dc9bc327bd7 100644 --- a/arch/x86/kernel/resource.c +++ b/arch/x86/kernel/resource.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include <linux/ioport.h> #include <asm/e820/api.h> +#include <asm/pci_x86.h> static void resource_clip(struct resource *res, resource_size_t start, resource_size_t end) @@ -28,6 +29,9 @@ static void remove_e820_regions(struct resource *avail) int i; struct e820_entry *entry; + if (!pci_use_e820) + return; + for (i = 0; i < e820_table->nr_entries; i++) { entry = &e820_table->entries[i]; diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index 948656069cddd..c884fafed1b5d 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c @@ -21,6 +21,8 @@ struct pci_root_info { static bool pci_use_crs = true; static bool pci_ignore_seg = false; +/* Consumed in arch/x86/kernel/resource.c */ +bool pci_use_e820 = false; static int __init set_use_crs(const struct dmi_system_id *id) { @@ -160,6 +162,28 @@ void __init pci_acpi_crs_quirks(void) "if necessary, use \"pci=%s\" and report a bug\n", pci_use_crs ? "Using" : "Ignoring", pci_use_crs ? "nocrs" : "use_crs"); + + /* + * Some firmware reports PCI host bridge windows via ACPI _CRS that + * contain addresses that don't work (see 4dc2287c1805 ("x86: avoid + * E820 regions when allocating address space")). + * + * To work around those bugs, we excluded areas reserved in the + * E820 map. But that was a poor workaround, since firmware is + * allowed to report *all* host bridge windows as reserved in E820. + * Exclude those E820 areas for older systems and ignore E820 for + * newer ones. + */ + if (year >= 0 && year < 2018) + pci_use_e820 = true; + + if (pci_probe & PCI_NO_E820) + pci_use_e820 = false; + else if (pci_probe & PCI_USE_E820) + pci_use_e820 = true; + + printk(KERN_INFO "PCI: %s E820 reservations for host bridge windows\n", + pci_use_e820 ? "Using" : "Ignoring"); } #ifdef CONFIG_PCI_MMCONFIG diff --git a/arch/x86/pci/common.c b/arch/x86/pci/common.c index 3507f456fcd09..091ec7e94fcbe 100644 --- a/arch/x86/pci/common.c +++ b/arch/x86/pci/common.c @@ -595,6 +595,12 @@ char *__init pcibios_setup(char *str) } else if (!strcmp(str, "nocrs")) { pci_probe |= PCI_ROOT_NO_CRS; return NULL; + } else if (!strcmp(str, "use_e820")) { + pci_probe |= PCI_USE_E820; + return NULL; + } else if (!strcmp(str, "no_e820")) { + pci_probe |= PCI_NO_E820; + return NULL; #ifdef CONFIG_PHYS_ADDR_T_64BIT } else if (!strcmp(str, "big_root_window")) { pci_probe |= PCI_BIG_ROOT_WINDOW; -- cgit 1.2.3-1.el7