Re: [PATCH] x86/PCI: Conditionally add a non-e820 mapped window to root bridge

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



[+cc Myron, just FYI]

On Thu, Jun 24, 2021 at 05:53:24PM +0800, Hui Wang wrote:
> The touchpad can't work on many Lenovo Ideapad S145/BS145 laptops.
> But it works well under Windows.
> 
> On those machines, the touchpad is an I2C device, while the I2C host
> controllers fail to initialize as below shown:
>  pci 0000:00:15.0: BAR 0: no space for [mem size 0x00001000 64bit]
>  pci 0000:00:15.0: BAR 0: failed to assign [mem size 0x00001000 64bit]
>  pci 0000:00:15.1: BAR 0: no space for [mem size 0x00001000 64bit]
>  pci 0000:00:15.1: BAR 0: failed to assign [mem size 0x00001000 64bit]
> 
> The BIOS assigns iomem space to host bridge and I2C host controller
> like this:
>  pci_bus 0000:00: root bus resource [mem 0x000a0000-0x000bffff window]
>  pci_bus 0000:00: root bus resource [mem 0x65400000-0xbfffffff window]
> 
>  pci 0000:00:15.0: [8086:34e8] type 00 class 0x0c8000
>  pci 0000:00:15.0: reg 0x10: [mem 0x00000000-0x00000fff 64bit]
>  pci 0000:00:15.1: [8086:34e9] type 00 class 0x0c8000
>  pci 0000:00:15.1: reg 0x10: [mem 0x00000000-0x00000fff 64bit]
> 
> The I2C host controllers need to allocate iomem space from root
> bridge, but all iomem window of the root bridge are overlapped with
> BIOS-e820 mapped region, that makes the allocate_resource() fail:
>  BIOS-e820: [mem 0x0000000000000000-0x000000000009efff] usable
>  BIOS-e820: [mem 0x000000000009f000-0x00000000000fffff] reserved
>  ...
>  BIOS-e820: [mem 0x000000004bc50000-0x00000000cfffffff] reserved
> 
> We could add "pci=nocrs" to make the touchpad work, but users expect
> the touchpad to work out-of-box under Linux distro as under Windows.
> 
> Here design a generic solution for x86 machines, if host bridge uses
> crs, we will check if all root bridge iomem windows are overlapped
> with BIOS-e820 mapped region, if yes, we try to build a non-e820
> mapped window according to the biggest gap in the e820 mapped region,
> and we need to clip this window with MMCONFIG region if this region
> is not mapped by BIOS-e820, then insert this window to the tail of
> the root bridge.
> 
> After this change, the I2C host controller could allocate the iomem
> region from host bridge successfully and the touchpad could work
> out-of-box.

I see from
https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1931715 that a
fix has been committed, I assume to the Ubuntu kernel, but I assume
this is still a problem for upstream and probably other distro
kernels.

The 0-day bot found some issue, which I haven't looked at:
https://lore.kernel.org/r/20210627143859.GD17986@xsang-OptiPlex-9020

I think this patch might be the wrong approach.  We learned from _CRS
that [mem 0x65400000-0xbfffffff window] is assigned to the PNP0A08:00
host bridge.

The e820 "[mem 0x000000004bc50000-0x00000000cfffffff] reserved" region
certainly includes all of that PCI aperture, and this entry should
prevent that space from being allocated to devices that haven't
already been assigned space.

But I don't think that e820 entry should prevent the PCI core from
managing that PCI aperture.  The platform told us that PNP0A08:00 owns
the [mem 0x65400000-0xbfffffff window], and the PNP0A08:00 driver
(pci_root.c) should be able to manage it as needed.

> BugLink: http://bugs.launchpad.net/bugs/1931715
> BugLink: http://bugs.launchpad.net/bugs/1932069
> BugLink: http://bugs.launchpad.net/bugs/1921649
> Cc: Bjorn Helgaas <bhelgaas@xxxxxxxxxx>
> Cc: Yinghai Lu <yinghai@xxxxxxxxxx>
> Cc: Ingo Molnar <mingo@xxxxxxxxxx>
> Cc: Rafael J. Wysocki <rafael.j.wysocki@xxxxxxxxx>
> Cc: linux-acpi@xxxxxxxxxxxxxxx
> Cc: linux-pci@xxxxxxxxxxxxxxx
> Signed-off-by: Hui Wang <hui.wang@xxxxxxxxxxxxx>
> ---
>  arch/x86/include/asm/e820/api.h |  1 +
>  arch/x86/include/asm/pci.h      |  2 ++
>  arch/x86/include/asm/pci_x86.h  |  1 +
>  arch/x86/kernel/e820.c          |  2 ++
>  arch/x86/pci/acpi.c             | 46 ++++++++++++++++++++++++++++++++-
>  arch/x86/pci/mmconfig-shared.c  | 28 ++++++++++++++++++++
>  6 files changed, 79 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/x86/include/asm/e820/api.h b/arch/x86/include/asm/e820/api.h
> index e8f58ddd06d9..46c76201dc28 100644
> --- a/arch/x86/include/asm/e820/api.h
> +++ b/arch/x86/include/asm/e820/api.h
> @@ -9,6 +9,7 @@ extern struct e820_table *e820_table_kexec;
>  extern struct e820_table *e820_table_firmware;
>  
>  extern unsigned long pci_mem_start;
> +extern unsigned long pci_mem_gap_size;
>  
>  extern bool e820__mapped_raw_any(u64 start, u64 end, enum e820_type type);
>  extern bool e820__mapped_any(u64 start, u64 end, enum e820_type type);
> diff --git a/arch/x86/include/asm/pci.h b/arch/x86/include/asm/pci.h
> index d2c76c8d8cfd..a940a792ef7b 100644
> --- a/arch/x86/include/asm/pci.h
> +++ b/arch/x86/include/asm/pci.h
> @@ -81,6 +81,8 @@ static inline int pcibios_assign_all_busses(void) { return 0; }
>  #endif
>  
>  extern unsigned long pci_mem_start;
> +extern unsigned long pci_mem_gap_size;
> +
>  #define PCIBIOS_MIN_IO		0x1000
>  #define PCIBIOS_MIN_MEM		(pci_mem_start)
>  
> diff --git a/arch/x86/include/asm/pci_x86.h b/arch/x86/include/asm/pci_x86.h
> index 490411dba438..3ea30aed9100 100644
> --- a/arch/x86/include/asm/pci_x86.h
> +++ b/arch/x86/include/asm/pci_x86.h
> @@ -171,6 +171,7 @@ extern int pci_mmconfig_delete(u16 seg, u8 start, u8 end);
>  extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus);
>  extern struct pci_mmcfg_region *__init pci_mmconfig_add(int segment, int start,
>  							int end, u64 addr);
> +extern void pci_mmconfig_clip_resource(struct resource *res);
>  
>  extern struct list_head pci_mmcfg_list;
>  
> diff --git a/arch/x86/kernel/e820.c b/arch/x86/kernel/e820.c
> index bc0657f0deed..2933745b19b7 100644
> --- a/arch/x86/kernel/e820.c
> +++ b/arch/x86/kernel/e820.c
> @@ -66,6 +66,7 @@ struct e820_table *e820_table_firmware __refdata	= &e820_table_firmware_init;
>  
>  /* For PCI or other memory-mapped resources */
>  unsigned long pci_mem_start = 0xaeedbabe;
> +unsigned long pci_mem_gap_size;
>  #ifdef CONFIG_PCI
>  EXPORT_SYMBOL(pci_mem_start);
>  #endif
> @@ -677,6 +678,7 @@ __init void e820__setup_pci_gap(void)
>  	 * e820__reserve_resources_late() protects stolen RAM already:
>  	 */
>  	pci_mem_start = gapstart;
> +	pci_mem_gap_size = gapsize;
>  
>  	pr_info("[mem %#010lx-%#010lx] available for PCI devices\n",
>  		gapstart, gapstart + gapsize - 1);
> diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c
> index 948656069cdd..e3a691024683 100644
> --- a/arch/x86/pci/acpi.c
> +++ b/arch/x86/pci/acpi.c
> @@ -231,6 +231,11 @@ static void teardown_mcfg_map(struct acpi_pci_root_info *ci)
>  		info->mcfg_added = false;
>  	}
>  }
> +
> +static void clip_resource_from_mmcfg(struct resource *res)
> +{
> +	return pci_mmconfig_clip_resource(res);
> +}
>  #else
>  static int setup_mcfg_map(struct acpi_pci_root_info *ci)
>  {
> @@ -240,6 +245,10 @@ static int setup_mcfg_map(struct acpi_pci_root_info *ci)
>  static void teardown_mcfg_map(struct acpi_pci_root_info *ci)
>  {
>  }
> +
> +static void clip_resource_from_mmcfg(struct resource *res)
> +{
> +}
>  #endif
>  
>  static int pci_acpi_root_get_node(struct acpi_pci_root *root)
> @@ -296,13 +305,48 @@ static int pci_acpi_root_prepare_resources(struct acpi_pci_root_info *ci)
>  	struct acpi_device *device = ci->bridge;
>  	int busnum = ci->root->secondary.start;
>  	struct resource_entry *entry, *tmp;
> +	bool has_non_e820_region = false;
>  	int status;
>  
>  	status = acpi_pci_probe_root_resources(ci);
>  	if (pci_use_crs) {
> -		resource_list_for_each_entry_safe(entry, tmp, &ci->resources)
> +		resource_list_for_each_entry_safe(entry, tmp, &ci->resources) {
> +			struct resource avail = *entry->res;
> +
>  			if (resource_is_pcicfg_ioport(entry->res))
>  				resource_list_destroy_entry(entry);
> +
> +			if (avail.flags & IORESOURCE_MEM) {
> +				arch_remove_reservations(&avail);
> +				if (avail.end > avail.start)
> +					has_non_e820_region = true;
> +			}
> +		}
> +
> +		/* all bridge windows are in the BIOS-e820 mapped region, this
> +		 * will make allocate_resource() fail when PCI devices request
> +		 * iomem address from bridge. To fix it, we try to build a non
> +		 * e820 mapped iomem resource and clip it with MMCONFIG region,
> +		 * then add it to the bridge window list.
> +		 */
> +		if (!has_non_e820_region) {
> +			struct resource_entry *rentry;
> +			struct resource avail;
> +
> +			avail.start = pci_mem_start;
> +			avail.end = pci_mem_start + pci_mem_gap_size - 1;
> +			avail.flags = IORESOURCE_MEM | IORESOURCE_WINDOW;
> +			avail.name = ci->name;
> +			clip_resource_from_mmcfg(&avail);
> +			if (avail.end > avail.start) {
> +				rentry = resource_list_create_entry(NULL, 0);
> +				if (rentry) {
> +					*rentry->res = avail;
> +					resource_list_add_tail(rentry, &ci->resources);
> +				}
> +			}
> +		}
> +
>  		return status;
>  	}
>  
> diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c
> index de6bf0e7e8f8..1f3ffb1bcdc8 100644
> --- a/arch/x86/pci/mmconfig-shared.c
> +++ b/arch/x86/pci/mmconfig-shared.c
> @@ -813,3 +813,31 @@ int pci_mmconfig_delete(u16 seg, u8 start, u8 end)
>  
>  	return -ENOENT;
>  }
> +
> +void pci_mmconfig_clip_resource(struct resource *res)
> +{
> +	struct pci_mmcfg_region *cfg;
> +	resource_size_t start, end;
> +	resource_size_t low = 0, high = 0;
> +
> +	/* Refers to the resource_clip() in the x86/kernel/resource.c */
> +	list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) {
> +		start = cfg->res.start;
> +		end = cfg->res.end;
> +
> +		if (res->end < start || res->start > end)
> +			continue;	/* no conflict */
> +
> +		if (res->start < start)
> +			low = start - res->start;
> +
> +		if (res->end > end)
> +			high = res->end - end;
> +
> +		/* Keep the area above or below the conflict, whichever is larger */
> +		if (low > high)
> +			res->end = start - 1;
> +		else
> +			res->start = end + 1;
> +	}
> +}
> -- 
> 2.25.1
> 



[Index of Archives]     [DMA Engine]     [Linux Coverity]     [Linux USB]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Greybus]

  Powered by Linux