每一个PCIe设备可以只有一个功能(Function),即Fun0。也可以拥有最多8个功能,即多功能设备(Multi-Fun)。 不管这个PCIe设备拥有多少个功能,其每一个功能都有一个唯一独立的配置空间(Configuration Space)与之对应。 一个例子,一个设备具有两个功能,一个作为显卡功能,一个作为网卡功能。 需要注意的是,每个设备必须要有功能0(Fun0),其他的7个功能(Fun1~Fun7)都是可选的。
和PCI总线一样,PCIe总线中的每一个功能(Function)都有一个唯一的标识符与之对应。 这个标识符就是BDF(Bus,Device,Function),PCIe的配置软件(即Root的应用层,一般是PC)应当有能力识别整个PCIe总线系统的拓扑逻辑, 以及其中的每一条总线(Bus),每一个设备(Device)和每一项功能(Function)。
在BDF中,Bus Number占用8位,Device Number占用5位,Function Number占用3位。 显然,PCIe总线最多支持256个子总线,每个子总线最多支持32个设备,每个设备最多支持8个功能。
PCIe总线采用的是一种深度优先(Depth First Search)的拓扑算法,且Bus0总是分配给Root Complex。 Root中包含有集成的Endpoint和多个端口(Port),每个端口内部都有一个虚拟的PCI-to-PCI桥(P2P),并且这个桥也应有设备号和功能号。
连接到上游端口的BUS称为Primary Bus,连接到下游端口的BUS称为Secondary Bus。
系统首次通电时,配置软件尚未扫描 PCI Express 结构以发现机器拓扑结构以及结构的填充方式。 配置软件只知道根联合体中主机/PCI 桥的存在,并且总线号0 直接连接到桥的下游(即次级)侧。 它尚未扫描总线0,因此尚不知道在 Root Complex 上实现了多少 PCI Express 端口。 扫描 PCI Express 结构以发现其拓扑的过程称为枚举过程。
example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@xtpc-09 tests]# lspci
00:00.0 Host bridge: Intel Corporation Device 3ec2 (rev 07)
00:01.0 PCI bridge: Intel Corporation Skylake PCIe Controller (x16) (rev 07)
00:01.1 PCI bridge: Intel Corporation Skylake PCIe Controller (x8) (rev 07)
00:02.0 VGA compatible controller: Intel Corporation Device 3e92
00:14.0 USB controller: Intel Corporation Device a36d (rev 10)
00:14.2 RAM memory: Intel Corporation Device a36f (rev 10)
00:16.0 Communication controller: Intel Corporation Device a360 (rev 10)
00:17.0 SATA controller: Intel Corporation Device a352 (rev 10)
00:1b.0 PCI bridge: Intel Corporation Device a340 (rev f0)
00:1c.0 PCI bridge: Intel Corporation Device a338 (rev f0)
00:1d.0 PCI bridge: Intel Corporation Device a330 (rev f0)
00:1f.0 ISA bridge: Intel Corporation Device a305 (rev 10)
00:1f.3 Audio device: Intel Corporation Device a348 (rev 10)
00:1f.4 SMBus: Intel Corporation Device a323 (rev 10)
00:1f.5 Serial bus controller [0c80]: Intel Corporation Device a324 (rev 10)
00:1f.6 Ethernet controller: Intel Corporation Ethernet Connection (7) I219-V (rev 10)
02:00.0 Serial controller: Xilinx Corporation Device 9038
补:02[23:16]总线号, 00[15:11] 设备号,0[10:8] 功能号。PCI Spec规定了两种类型的Header: Type1 和Type0。其中,Type1 Header表示该PCI设备功能为桥(Bridge),而Type0 Header则表示该PCI设备功能不是桥。
补:1f的设备,可以看到,1f是设备号,对应具体的设备,该设备有5个功能。如果该设备为Endpoint,则其最多可拥有8项功能(Function), 且每项功能都有一个对应的配置空间(Configuration Space)。如果该设备为Switch,则应用层需要实现包路由(Packet Routing)等相关逻辑。 如果该设备为Root,则应用层需要实现虚拟的PCIe总线0(Virtual PCIe Bus 0),并代表整个PCIe总线系统与CPU通信。 另外,一个配置空间对应一个功能号。一个设备可以有最多8个配置空间,就是最多八个功能,在一张卡上。 有时显示4个值,前面多划分一个16位的域, (域,总线,设备,功能,如0000:00:09.1)
补:设备号和功能号实际构成的是设备号,正好5+3=8位,就是PCI驱动中的那个设备号。因为功能号0-7是连续的,所以设备号也是连续的, 这样就可以实现所谓的不能PCI功能号加载不同的PCI驱动,其实就是设备号不同,功能号属于设备号的子集。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
pjw@hw-cpr:~$ sudo lspci -nn
00:00.0 Host bridge [0600]: Intel Corporation Device [8086:4668] (rev 02)
00:01.0 PCI bridge [0604]: Intel Corporation Device [8086:460d] (rev 02)
00:02.0 VGA compatible controller [0300]: Intel Corporation Device [8086:4680] (rev 0c)
00:06.0 PCI bridge [0604]: Intel Corporation Device [8086:464d] (rev 02)
00:0a.0 Signal processing controller [1180]: Intel Corporation Device [8086:467d] (rev 01)
00:14.0 USB controller [0c03]: Intel Corporation Device [8086:7ae0] (rev 11)
00:14.2 RAM memory [0500]: Intel Corporation Device [8086:7aa7] (rev 11)
00:15.0 Serial bus controller [0c80]: Intel Corporation Device [8086:7acc] (rev 11)
00:15.1 Serial bus controller [0c80]: Intel Corporation Device [8086:7acd] (rev 11)
00:15.2 Serial bus controller [0c80]: Intel Corporation Device [8086:7ace] (rev 11)
00:15.3 Serial bus controller [0c80]: Intel Corporation Device [8086:7acf] (rev 11)
00:16.0 Communication controller [0780]: Intel Corporation Device [8086:7ae8] (rev 11)
00:17.0 SATA controller [0106]: Intel Corporation Device [8086:7ae2] (rev 11)
00:19.0 Serial bus controller [0c80]: Intel Corporation Device [8086:7afc] (rev 11)
00:19.1 Serial bus controller [0c80]: Intel Corporation Device [8086:7afd] (rev 11)
00:1c.0 PCI bridge [0604]: Intel Corporation Device [8086:7ab8] (rev 11)
00:1c.2 PCI bridge [0604]: Intel Corporation Device [8086:7aba] (rev 11)
00:1d.0 PCI bridge [0604]: Intel Corporation Device [8086:7ab0] (rev 11)
00:1f.0 ISA bridge [0601]: Intel Corporation Device [8086:7a84] (rev 11)
00:1f.3 Audio device [0403]: Intel Corporation Device [8086:7ad0] (rev 11)
00:1f.4 SMBus [0c05]: Intel Corporation Device [8086:7aa3] (rev 11)
00:1f.5 Serial bus controller [0c80]: Intel Corporation Device [8086:7aa4] (rev 11)
01:00.0 Memory controller [0580]: Xilinx Corporation Device [10ee:5048]
01:00.1 Memory controller [0580]: Xilinx Corporation Device [10ee:5049]
02:00.0 Non-Volatile memory controller [0108]: Sandisk Corp Device [15b7:501e]
04:00.0 Ethernet controller [0200]: Realtek Semiconductor Co., Ltd. RTL8125 2.5GbE Controller [10ec:8125] (rev 05)
05:00.0 Non-Volatile memory controller [0108]: Intel Corporation NVMe Datacenter SSD [3DNAND, Beta Rock Controller] [8086:0a54]
配置空间
为了兼容PCI软件,PCIe保留了256Byte的配置空间,并将配置空间扩展到4KB, 用于支持一些PCIe总线中新的功能,如PCI Express Capability、Power Management和MSI/MSI-X中断等。如下图:
扩展后的区域将使用MMIO的方式进行访问; PCIE 扩展的空间包括:
- Advanced Error Reporting Capability register set.
- Virtual Channel Capability register set.
- Device Serial Number Capability register set.
- Power Budgeting Capability register set.
配置空间读写(重要)
PCI EP设备的配置空间大小为64B或64+192=256B,前面的64字节是必须的,是基本配置空间,后面的64-256字节是可选的,一般也都有, 它是用来存放MSI/MSIX以及电源管理相关的Capability结构。而到了PCIe,则将256B的配置空间进一步扩展到了4KB,从256字节后面开始, 存放了PCIe总线独有的新特性,如AER, Virtual Channel,Device Serial Number, Power Budgeting等。
linux kernel 启动过程中,会先配置好pcie rc,然后rc去枚举设备,枚举设备会需要读写pcie总线上ep设备的配置空间,进行配置。
传统的,在x86上,当时是还是pci总线,会通过IO port去读写pci ep设备的配置空间,这也是最古老的方法,需要cpu支持io空间才行, 现在的arm处理器就不能使用这种方式。PCI设备使用IO空间的CF8(Configuration Address Port)/CFC(Configuration Data Port)地址来访问配置空间。 往CF8写地址,CFC用来读写数据,如下图,
总线号,设备号,功能号,再加配置空间内部偏移,可以看到,留给寄存器的地址空间就剩8位,刚好256字节,完美符合PCI 设备。 但是对PCIe而言,就不够行了,PCIe配置空间需要4KB。
到目前,这种IO Port读写PCI配置空间的方式比较少了,因为PCIe引入了所谓的增强配置空间访问机制Enhanced Configuration Access Mechanism, 它通过将配置空间映射到MMIO空间,使得对配置空间的访问就像对内存一样,也因此可以访问完整的4KB配置空间。同时一般保留的IO Port的方式,可以访问前面256B的配置空间。
使用MMIO访问PCIe配置空间这种方式,操作系统在系统的存储器空间分配出容量为256MB的地址空间,一一对应所有的PCIe总线上的各个设备的配置空间。 (8位总线号(00-ff)*5位设备号(00-20)*3位功能号(00-08)*4KB配置空间 == 0x100*0x20*0x8*4096=0x10000000B=256MB)
访问方式即为方位iomem,按上图地址即可。(PcieConfigHeadBaseAdd + Bus«20 + Dev«15 + Fun«12 + offset)
PCIe在操作系统中映射的地址可以这样查看:
1
2
$ sudo cat /proc/iomem | grep "MMCONFIG"
80000000-8fffffff : PCI MMCONFIG 0000 [bus 00-ff]
物理机应该都可以这样查看,部分虚拟机中没有,可能和PCIe虚拟化有关。
注意这里是PCIe设备的配置空间不是BAR空间,不一样的。example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
80000000-8fffffff : PCI MMCONFIG 0000 [bus 00-ff]
90000000-9b7fffff : PCI Bus 0000:00
90000000-901fffff : PCI Bus 0000:01
9a000000-9b0fffff : PCI Bus 0000:03
9a000000-9b0fffff : PCI Bus 0000:04
9a000000-9affffff : 0000:04:00.0
9b000000-9b03ffff : 0000:04:00.0
9b100000-9b17ffff : 0000:00:17.0
9b100000-9b17ffff : ahci
......
=================================================
40400000-bfffffff : PCI Bus 0000:00
40400000-405fffff : PCI Bus 0000:02
40600000-407fffff : PCI Bus 0000:02
40800000-40800fff : 0000:00:15.0
40800000-408001ff : lpss_dev
40800000-408001ff : i2c_designware.0 lpss_dev
40800200-408002ff : lpss_priv
40800800-40800fff : idma64.0
40800800-40800fff : idma64.0 idma64.0
......
c0000000-cfffffff : PCI MMCONFIG 0000 [bus 00-ff]