Commit 7fc986d8 authored by Yinghai Lu's avatar Yinghai Lu Committed by Bjorn Helgaas

PCI: Support 64-bit bridge windows if we have 64-bit dma_addr_t

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: d56dbf5b ("PCI: Allocate 64-bit BARs above 4G when possible")
Link: https://bugzilla.kernel.org/show_bug.cgi?id=88131Reported-by: default avatarAaron Ma <mapengyu@gmail.com>
Tested-by: default avatarAaron Ma <mapengyu@gmail.com>
Signed-off-by: default avatarYinghai Lu <yinghai@kernel.org>
Signed-off-by: default avatarBjorn Helgaas <bhelgaas@google.com>
CC: stable@vger.kernel.org	# v3.14+
parent 7a1562d4
......@@ -407,15 +407,16 @@ static void pci_read_bridge_mmio_pref(struct pci_bus *child)
{
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;
......@@ -429,17 +430,20 @@ static void pci_read_bridge_mmio_pref(struct pci_bus *child)
* 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;
base64 |= (u64) mem_base_hi << 32;
limit64 |= (u64) mem_limit_hi << 32;
}
#endif
}
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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment