PCI是 Peripheral Component Interconnect (外设部件互连标准)的缩写,它曾经是个人电脑中使用最为广泛的接口,几乎所有的主板产品上都带有这种插槽。目前该总线已经逐渐被PCI Express总线所取代。

PCI 总线

PCI 总线是一种树型结构,并且独立于 CPU 总线,可以和 CPU 总线并行操作。PCI总线上可以挂接 PCI 设备和 PCI 桥, PCI 总线上只允许有一个 PCI 主设备(同一时刻),其他的均为 PCI 从设备,而且读写操作只能在主从设备之间进行,从设备之间的数据交换需要通过主设备中转。

PCI device tree

PCI 配置空间

PCI 配置空间为256字节的固定空间,负责设备的信息提供和管理等。头部最开始的 deviceID 和 vendorID 寄存器由 pcisig 分配,只读,vendorID 代表 pci 设备的厂商,deviceID 代表厂商的具体设备。

Header Type

0xE偏移处是设备的 header type,决定了 PCI 配置头的布局和设备的类型。头类型有三种,在 PCIE 中只保留了前两种。

Type 0设备代表 PCI Endpoint,其配置空间如下:

type 0 pci configuration header layout

每个字段的详细含义可以参照 PCI - OSDev Wiki

Base Address Register - BAR寄存器

除了配置空间以外,PCI 还有空间去实现所需的功能,如硬件存储等,这就需要额外的可访问地址。BAR 寄存器存储了设备内部空间映射到对应地址空间的基址,它并不是一个单纯的地址,它拥有自己的结构。

bar layout

其中:

  • 最低位Bit 0:是一个标志位,用于描述地址空间的类型,0表示内存地址空间,1表示 IO 地址空间

  • Memory Space中的Bit [2:1] - Type:用于描述内存空间的类型,00表示32位地址空间,10表示64位地址空间

  • Memory Space中的Bit 3 - Prefetchable:用于描述内存空间是否支持预取,0表示不支持,1表示支持。如果一段内存空间支持预取,它意味着读取时不会产生任何副作用,所以CPU可以随时将其预取到DRAM中。而如果预取被启用,在读取数据时,内存控制器也会先去DRAM查看是否有缓存。当然,这是一把双刃剑,如果数据本身不支持预取,那么除了可能导致数据不一致,多一次DRAM的查询还会导致速度下降。

为得到所需映射空间的大小,在 BIOS 对 BAR 进行初始化时,对 BAR 寄存器写入全1,出去最低几位的 flag,对齐位会恢复为全0,高位能写入的位为全1,就可以求出所需的大小。比如0xFFFFF000,取反之后就是0x00000FFF,加1之后就是0x00001000,也就是4KB。另外,如何这个空间不可用,那么返回全0。BIOS接着进行真正的地址分配和映射,并将这个新的地址重新写入BAR。

对于BAR空间中保存的所有的地址,我们都可以通过lspci来查看到:

1
sudo lspci -s `BUS_NUM`:00.0 -nn -vv

地址空间

PCI 定义了三个地址空间,分别为 Memory Address Space, I/O Address Space, Configuration Address Space,即内存地址空间,IO 地址空间和配置地址空间,其中第三个应和配置空间区别开。

BAR 中储存的地址可以是内存地址空间中的,也可以是 IO 地址空间的,其访问就是和 x86 系统中一样的访问方式。配置地址空间的访问需要发送事务层的指令(Configuration Commands)。

BDF - Bus Number, Device Number, Function Number

PCI 上所有的设备,无论是 Type 0还是 Type 1,在系统启动的时候,都会被分配一个唯一的地址,它有三个部分组成:

  • Bus Number:8 bits,也就是最多256条总线
  • Device Number:5 bits,也就是最多32个设备
  • Function Number:3 bits,也就是最多8个功能

这就是我们常说的BDF,它类似于网络中的IP地址,一般写作 BB:DD.F 的格式。在 Linux 上,我们可以通过 lspci 命令来查看每个设备的 BDF 。比如,下面这个FCH SMBus Controller就是00:14.0:

1
2
3
4
5
$ lspci -t -v
# [Domain:Bus]
\-[0000:00]-+-00.0 Advanced Micro Devices, Inc. [AMD] Starship/Matisse Root Complex
# Device.Function
+-14.0 Advanced Micro Devices, Inc. [AMD] FCH SMBus Controller

知道了任何一个设备的BDF之后,我们就可以通过lspci -s `BDF` -vv查看到这个设备的详细信息。

可以算出 PCI 配置地址空间的总大小为 282523256 bytes=16MB2^8 \cdot 2^5 \cdot 2^3 \cdot 256 \ bytes = 16\mathbf{MB}

Linux 中的 PCI 子系统

参考文献

PCIe扫盲

PCIe(一) —— 基础概念与设备树 | Soul Orbit (r12f.com)

PCIe(二) —— 配置空间 | Soul Orbit (r12f.com)

PCI_SPEV_V3_0.pdf (lekensteyn.nl)