| From 7fc986d8a9727e5d40da3c2c1c343da6142e82a9 Mon Sep 17 00:00:00 2001 |
| From: Yinghai Lu <yinghai@kernel.org> |
| Date: Wed, 19 Nov 2014 14:30:32 -0700 |
| Subject: PCI: Support 64-bit bridge windows if we have 64-bit dma_addr_t |
| |
| From: Yinghai Lu <yinghai@kernel.org> |
| |
| commit 7fc986d8a9727e5d40da3c2c1c343da6142e82a9 upstream. |
| |
| Aaron reported that a 32-bit x86 kernel with Physical Address Extension |
| (PAE) support complains about bridge prefetchable memory windows above 4GB: |
| |
| pci_bus 0000:00: root bus resource [mem 0x380000000000-0x383fffffffff] |
| ... |
| pci 0000:03:00.0: reg 0x10: [mem 0x383fffc00000-0x383fffdfffff 64bit pref] |
| pci 0000:03:00.0: reg 0x20: [mem 0x383fffe04000-0x383fffe07fff 64bit pref] |
| pci 0000:03:00.1: reg 0x10: [mem 0x383fffa00000-0x383fffbfffff 64bit pref] |
| pci 0000:03:00.1: reg 0x20: [mem 0x383fffe00000-0x383fffe03fff 64bit pref] |
| pci 0000:00:02.2: PCI bridge to [bus 03-04] |
| pci 0000:00:02.2: bridge window [io 0x1000-0x1fff] |
| pci 0000:00:02.2: bridge window [mem 0x91900000-0x91cfffff] |
| pci 0000:00:02.2: can't handle 64-bit address space for bridge |
| |
| In this kernel, unsigned long is 32 bits and dma_addr_t is 64 bits. |
| Previously we used "unsigned long" to hold the bridge window address. But |
| this is a bus address, so we should use dma_addr_t instead. |
| |
| Use dma_addr_t to hold the bridge window base and limit. |
| |
| The question of whether the CPU can actually *address* the window is |
| separate and depends on what the physical address space of the CPU is and |
| whether the host bridge does any address translation. |
| |
| [bhelgaas: fix "shift count > width of type", changelog, stable tag] |
| Fixes: d56dbf5bab8c ("PCI: Allocate 64-bit BARs above 4G when possible") |
| Link: https://bugzilla.kernel.org/show_bug.cgi?id=88131 |
| Reported-by: Aaron Ma <mapengyu@gmail.com> |
| Tested-by: Aaron Ma <mapengyu@gmail.com> |
| Signed-off-by: Yinghai Lu <yinghai@kernel.org> |
| Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/pci/probe.c | 29 ++++++++++++++++------------- |
| 1 file changed, 16 insertions(+), 13 deletions(-) |
| |
| --- a/drivers/pci/probe.c |
| +++ b/drivers/pci/probe.c |
| @@ -395,15 +395,16 @@ static void pci_read_bridge_mmio_pref(st |
| { |
| struct pci_dev *dev = child->self; |
| u16 mem_base_lo, mem_limit_lo; |
| - unsigned long base, limit; |
| + u64 base64, limit64; |
| + dma_addr_t base, limit; |
| struct pci_bus_region region; |
| struct resource *res; |
| |
| res = child->resource[2]; |
| pci_read_config_word(dev, PCI_PREF_MEMORY_BASE, &mem_base_lo); |
| pci_read_config_word(dev, PCI_PREF_MEMORY_LIMIT, &mem_limit_lo); |
| - base = ((unsigned long) mem_base_lo & PCI_PREF_RANGE_MASK) << 16; |
| - limit = ((unsigned long) mem_limit_lo & PCI_PREF_RANGE_MASK) << 16; |
| + base64 = (mem_base_lo & PCI_PREF_RANGE_MASK) << 16; |
| + limit64 = (mem_limit_lo & PCI_PREF_RANGE_MASK) << 16; |
| |
| if ((mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) { |
| u32 mem_base_hi, mem_limit_hi; |
| @@ -417,18 +418,20 @@ static void pci_read_bridge_mmio_pref(st |
| * this, just assume they are not being used. |
| */ |
| if (mem_base_hi <= mem_limit_hi) { |
| -#if BITS_PER_LONG == 64 |
| - base |= ((unsigned long) mem_base_hi) << 32; |
| - limit |= ((unsigned long) mem_limit_hi) << 32; |
| -#else |
| - if (mem_base_hi || mem_limit_hi) { |
| - dev_err(&dev->dev, "can't handle 64-bit " |
| - "address space for bridge\n"); |
| - return; |
| - } |
| -#endif |
| + base64 |= (u64) mem_base_hi << 32; |
| + limit64 |= (u64) mem_limit_hi << 32; |
| } |
| } |
| + |
| + base = (dma_addr_t) base64; |
| + limit = (dma_addr_t) limit64; |
| + |
| + if (base != base64) { |
| + dev_err(&dev->dev, "can't handle bridge window above 4GB (bus address %#010llx)\n", |
| + (unsigned long long) base64); |
| + return; |
| + } |
| + |
| if (base <= limit) { |
| res->flags = (mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) | |
| IORESOURCE_MEM | IORESOURCE_PREFETCH; |