PCI Express配置空间
PCI-E是用来互联如计算和通信平台应用中外围设备的第三代高性能I/O总线。PCI-E采用了与PCI相同的使用模型和读写(load-store)通信模型,支持各种常见的事务,如存储器读/写、IO读/写和配置读/写事务。其存储器、IO和配置地址空间与PCI的地址空间相同。PCI Express与PCI系统是软件向后兼容的。
PCI-E的配置空间大小为4096字节,如下图所示。其中前256字节是与PCI兼容的配置寄存器,该区域可以用以下两种机制访问:
- PCI配置访问机制。
- PCI Express增强型配置机制。
Memory-mapped I/O (MMIO)与port I/O
MMIO和port I/O(也称为port-mapped I/O或PMIO)是两种CPU与外设之间进行I/O操作的方式。
Port I/O是通过特殊的CPU指令来进行I/O操作,在x86架构上,可以通过指令in和out在特定的端口上进行I/O读写。I/O设备拥有与内存不同的地址空间,实现的方式是通过在CPU上额外的I/O pin或者将整个总线赋予端口。
MMIO即内存映射I/O,它是PCI规范一部分,I/O设备被放置在内存空间而不是I/O空。从处理器角度看,内存映射I/O后系统设备访问起来和内存一样。这样访问AGP/PCI-E显卡上的帧缓存,BIOS,PCI设备就可以使用读写内存一样的汇编指令完成,简化了程序设计的难度和接口的复杂性。
对软件人员来说,MMIO比Port I/O更方便使用。
PCI Express扩展配置空间访问
《PCI Express Base Specification Revision 1.1》规范中规定了PCI-E扩展配置空间(257~4096字节)的访问方式,即通过MMIO方式访问。
PCI-E规范规定物理内存地址如下表:
规范中只规定了物理地址中A[(20+n-1):20]遵从的规则。因为PCI Express规范最大支持256个总线,所以A[(20+n-1):20]中n的最大值为8。
PCI Express设备配置空间的物理内存地址基址(Base Address)对齐。
最大支持32设备,Device Number A[19:15]占5bit;
最大8个功能号,Function Number A[14:12]占3bit;
PCI Express配置空间大小4096字节,因此占用12bit, A[11:8], A[7:2],A[1:0]。
注意:上面的n为CPU架构中支持的最大总线数。
如下面设备Broadcom NetXtreme II 5709网卡
01:00.0 Ethernet controller: Broadcom Corporation NetXtreme II BCM5709 Gigabit Ethernet (rev 20)
Bus Number:01
Device Number: 00
Function Number: 00
则对应物理地址低21bit为
A[20]为1
A[19:15]为00000
A[14:12]为000
即01:00.0设备PCI Express配置空间的MMIO物理地址低21bit起始地址为0x100000
x86/x86_64 CPU中PCI Express扩展配置空间访问
在上一节中,我们从PCI Express规范来了解扩展配置空间和物理内存地址对应关系,但只提到A[(20+n-1): 0],那么在CPU中的地址高bit(64位CPU为A[63: (20+n)])是由CPU桥片和Firmware来确定。
PCI Express设备配置空间的物理内存地址基址(Base Address)对齐。
- x86/x86_64 CPU中若设置支持的最大总线数为256,则n=8,PCI-E设备配置空间的MMIO内存地址是对齐,即PCI Express配置空间占用256MB内存地址空间。
- x86/x86_64 CPU中若设置支持的最大总线数为64,则n=6,PCI-E设备配置空间的MMIO内存地址是对齐,即PCI Express配置空间占用64MB内存地址空间。
在x86/x86_64 CPU默认的是支持最大总线为256,PCI Express设备配置空间所占的内存物理地址范围为0xe0000000-0xefffffff。
下面是Linux内核启动部分打印信息,可以看到BIOS e820图中,将0xe0000000-0xf0000000作为保留区域,给PCI Express配置空间使用,
[ 0.000000] BIOS-e820: 0000000000000000 – 000000000009f800 (usable)
[ 0.000000] BIOS-e820: 000000000009f800 – 00000000000a0000 (reserved)
[ 0.000000] BIOS-e820: 00000000000ca000 – 00000000000cc000 (reserved)
[ 0.000000] BIOS-e820: 00000000000dc000 – 00000000000e4000 (reserved)
[ 0.000000] BIOS-e820: 00000000000e8000 – 0000000000100000 (reserved)
[ 0.000000] BIOS-e820: 0000000000100000 – 000000001fef0000 (usable)
[ 0.000000] BIOS-e820: 000000001fef0000 – 000000001feff000 (ACPI data)
[ 0.000000] BIOS-e820: 000000001feff000 – 000000001ff00000 (ACPI NVS)
[ 0.000000] BIOS-e820: 000000001ff00000 – 0000000020000000 (usable)
[ 0.000000] BIOS-e820: 00000000e0000000 – 00000000f0000000 (reserved)
[ 0.000000] BIOS-e820: 00000000fec00000 – 00000000fec10000 (reserved)
[ 0.000000] BIOS-e820: 00000000fee00000 – 00000000fee01000 (reserved)
[ 0.000000] BIOS-e820: 00000000fffe0000 – 0000000100000000 (reserved)
… …
[ 0.834563] PCI: MMCONFIG for domain 0000 [bus 00-ff] at [mem 0xe0000000-0xefffffff] (base 0xe0000000)
[ 0.834729] PCI: MMCONFIG at [mem 0xe0000000-0xefffffff] reserved in E820
Linux中如何读写PCI Express配置空间
通过上面介绍,我们可以计算出某个具体PCI Express设备配置空间的起始物理内存地址,但Linux内核并不能直接读写这些物理地址,需要将这些物理内存地址映射到内核中的逻辑地址后,才可以读写PCI Express配置空间。具体原因,请参考x86/x86_64 CPU中逻辑地址、线性地址与物理地址。
我会在另外一篇文章中单独介绍Linux PCI-E设备配置空间读取与修改内核详细实现。
Leave a Reply