如何从 PCIe 中的 BAR 地址计算 MMIO 映射区域的大小

How to calculate size of MMIO-mapped region from BAR address in PCIe

我一直在深入研究 PCIe 的一般工作原理,我被困在许多书籍和网站谈论 PCIe 配置的地方 space。
到目前为止我了解到的是,对于每个分配的设备及其 BDF(总线设备功能位),该设备对应一个 4KB 配置 space,其中包括如下 64B 区域:

我理解每个基地址寄存器(代表内存映射区域)被解码如下:

(两张图片均来自this site

我不明白区域的大小是如何确定的。例如,在一台服务器中,对于连接到 BDF 00:05.0 命令 lspci -x -v -s 05:00.0:

的 GPU,我得到以下信息
05:00.0 VGA compatible controller: NVIDIA Corporation GV100 [TITAN V] (rev a1) (prog-if 00 [VGA controller])
    Subsystem: NVIDIA Corporation GV100 [TITAN V]
    Flags: bus master, fast devsel, latency 0, IRQ 80, NUMA node 0
    Memory at f8000000 (32-bit, non-prefetchable) [size=16M]
    Memory at a0000000 (64-bit, prefetchable) [size=256M]
    Memory at b0000000 (64-bit, prefetchable) [size=32M]
    I/O ports at d000 [size=128]
    [virtual] Expansion ROM at 000c0000 [disabled] [size=128K]
    Capabilities: <access denied>
    Kernel driver in use: nvidia
    Kernel modules: nvidiafb, nouveau, nvidia_drm, nvidia
00: de 10 81 1d 07 05 10 00 a1 00 00 03 00 00 80 00
10: 00 00 00 f8 0c 00 00 a0 00 00 00 00 0c 00 00 b0
20: 00 00 00 00 01 d0 00 00 00 00 00 00 de 10 18 12
30: 00 00 00 00 60 00 00 00 00 00 00 00 0b 01 00 00

我们可以看到BAR0的值为0xf8000000。但是我们怎么知道从地址 0xf8000000 开始的区域的大小呢?从我检查过的一些网站 (, two, and three) 他们谈论:
(1) 找到应该是区域长度的地址的补值(这在某些方面对我来说没有意义)或
(2)因为0xf80000001111 1000 0000 0000 0000 0000 0000 0000,所以区域的大小是2^27=128MB,因为有27个连续的0,直到遇到第一个1。

但这两种方法都是错误的,因为lspci命令说特定区域映射了16MB,而不是128MB。

所以这是我真正的问题: 1.内存区域的大小到底应该怎么计算? 2. 还有,上面映射的显存加起来好像是16M+256M+32M+128(+128K),但是GPU显存实际大小是12GB多一点。不是所有的GPU内存都通过PCIe映射到MMIO吗?

提前致谢。

你提到的OSDev link定义了探测BAR的协议:

To determine the amount of address space needed by a PCI device, you must save the original value of the BAR, write a value of all 1's to the register, then read it back. The amount of memory can then be determined by masking the information bits, performing a bitwise NOT ('~' in C), and incrementing the value by 1. The original value of the BAR should then be restored. The BAR register is naturally aligned and as such you can only modify the bits that are set.