Linux 64 位 BAR 编程

Linux 64 bit BAR programming

一个 PCI 桥支持 32/64 pref/no pref:

pci_bus 0000:00: root bus resource [bus 00-ff]
pci_bus 0000:00: root bus resource [io  0x0000-0xffff] 
pci_bus 0000:00: root bus resource [mem 0x1000000000-0x10ffffffff] (bus address [0x00000000-0xffffffff])
pci_bus 0000:00: root bus resource [mem 0x1100000000-0x11ffffffff pref] (bus address [0x00000000-0xffffffff])
pci_bus 0000:00: root bus resource [mem 0x2000000000-0x2fffffffff] (bus address [0x00000000-0xfffffffff])
pci_bus 0000:00: root bus resource [mem 0x3000000000-0x3fffffffff pref] (bus  address [0x00000000-0xfffffffff])

扫描具有 2 个 BAR 的端点:

pci 0000:01:00.0: BAR 2: assigned [mem 0x2100000000-0x211fffffff 64bit pref]
pci 0000:01:00.0: BAR 0: assigned [mem 0x1000000000-0x100007ffff]

桥显示 windows:

pci 0000:00:00.0: PCI bridge to [bus 01]
pci 0000:00:00.0:   bridge window [mem 0x1000000000-0x10000fffff]
pci 0000:00:00.0:   bridge window [mem 0x2100000000-0x211fffffff 64bit pref]

端点总结了 BAR:

endpoint: BAR 0 addr = 0x1000000000, size = 524287
endpoint: BAR 1 addr = 0x2100000000, size = 536870911

BAR1 的高 32 位将被编程为什么?我期望 0x21,但找到 0x1。

这是因为 pcibios_bus_to_resource/pcibios_resource_to_bus。 它标识了 3 window 个偏移量:

window->res->start = 1000000000 window->offset = 1000000000 window->res->end = 10FFFFFFFF
window->res->start = 1100000000 window->offset = 1100000000 window->res->end = 11FFFFFFFF
window->res->start = 2000000000 window->offset = 2000000000 window->res->end = 2FFFFFFFFF

显示上面地址时加上偏移量,编程BAR时减去偏移量 在端点注册。所以 0x2100000000 变成 0x100000000.

这对于 64 位地址和 32 位 BAR 可能有意义,但为什么对于 64 位 BAR 就正确呢?

据此(取自问题):

    pci_bus 0000:00: root bus resource [mem 0x1000000000-0x10ffffffff] (bus address [0x00000000-0xffffffff])
    pci_bus 0000:00: root bus resource [mem 0x1100000000-0x11ffffffff pref] (bus address [0x00000000-0xffffffff])
    pci_bus 0000:00: root bus resource [mem 0x2000000000-0x2fffffffff] (bus address [0x00000000-0xfffffffff])
    pci_bus 0000:00: root bus resource [mem 0x3000000000-0x3fffffffff pref] (bus  address [0x00000000-0xfffffffff])

内存地址0x20'0000'0000对应总线地址0.

因此,如果要使用内存地址 0x21'0000'0000 访问设备中的 BAR,则需要将其编程为 0x1'0000'0000。

同理,内存地址0x10'0000'0000对应总线地址0。所以设备中的BAR被编程为0,使用内存地址0x10'0000'0000访问。