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: d56dbf5bab
("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>
CC: stable@vger.kernel.org # v3.14+
This commit is contained in:
parent
7a1562d4f2
commit
7fc986d8a9
@ -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;
|
||||
}
|
||||
#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;
|
||||
|
Loading…
Reference in New Issue
Block a user