Skip to content

Commit 7fc986d

Browse files
Yinghai Lubjorn-helgaas
Yinghai Lu
authored andcommitted
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: d56dbf5 ("PCI: Allocate 64-bit BARs above 4G when possible") Link: https://bugzilla.kernel.org/show_bug.cgi?id=88131 Reported-by: Aaron Ma <[email protected]> Tested-by: Aaron Ma <[email protected]> Signed-off-by: Yinghai Lu <[email protected]> Signed-off-by: Bjorn Helgaas <[email protected]> CC: [email protected] # v3.14+
1 parent 7a1562d commit 7fc986d

File tree

1 file changed

+16
-12
lines changed

1 file changed

+16
-12
lines changed

drivers/pci/probe.c

+16-12
Original file line numberDiff line numberDiff line change
@@ -407,15 +407,16 @@ static void pci_read_bridge_mmio_pref(struct pci_bus *child)
407407
{
408408
struct pci_dev *dev = child->self;
409409
u16 mem_base_lo, mem_limit_lo;
410-
unsigned long base, limit;
410+
u64 base64, limit64;
411+
dma_addr_t base, limit;
411412
struct pci_bus_region region;
412413
struct resource *res;
413414

414415
res = child->resource[2];
415416
pci_read_config_word(dev, PCI_PREF_MEMORY_BASE, &mem_base_lo);
416417
pci_read_config_word(dev, PCI_PREF_MEMORY_LIMIT, &mem_limit_lo);
417-
base = ((unsigned long) mem_base_lo & PCI_PREF_RANGE_MASK) << 16;
418-
limit = ((unsigned long) mem_limit_lo & PCI_PREF_RANGE_MASK) << 16;
418+
base64 = (mem_base_lo & PCI_PREF_RANGE_MASK) << 16;
419+
limit64 = (mem_limit_lo & PCI_PREF_RANGE_MASK) << 16;
419420

420421
if ((mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) {
421422
u32 mem_base_hi, mem_limit_hi;
@@ -429,17 +430,20 @@ static void pci_read_bridge_mmio_pref(struct pci_bus *child)
429430
* this, just assume they are not being used.
430431
*/
431432
if (mem_base_hi <= mem_limit_hi) {
432-
#if BITS_PER_LONG == 64
433-
base |= ((unsigned long) mem_base_hi) << 32;
434-
limit |= ((unsigned long) mem_limit_hi) << 32;
435-
#else
436-
if (mem_base_hi || mem_limit_hi) {
437-
dev_err(&dev->dev, "can't handle 64-bit address space for bridge\n");
438-
return;
439-
}
440-
#endif
433+
base64 |= (u64) mem_base_hi << 32;
434+
limit64 |= (u64) mem_limit_hi << 32;
441435
}
442436
}
437+
438+
base = (dma_addr_t) base64;
439+
limit = (dma_addr_t) limit64;
440+
441+
if (base != base64) {
442+
dev_err(&dev->dev, "can't handle bridge window above 4GB (bus address %#010llx)\n",
443+
(unsigned long long) base64);
444+
return;
445+
}
446+
443447
if (base <= limit) {
444448
res->flags = (mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) |
445449
IORESOURCE_MEM | IORESOURCE_PREFETCH;

0 commit comments

Comments
 (0)