PCIe包括物理层,链路层和事务层共3层协议。
链路层通过Nack/Ack seq num保证传输质量。
其中seqence number是用于链路层,每次经过PCIe节点转发,sequence number都会变化,表示的是点对点的序号,不是端对端的序号。
其中事务层报文TLP(PCIe Transaction Layer Packets)格式如下图所示:
其中TLP又分为很多子类型,如下图所示:
事务层报文分为Post和non-posted,
常见的类型的具体格式如下:
第1种TLP: Config request(配置空间读写请求):
报文样例如下:
cfg read type 0:
cfg write type 0:
在扫描之前,软件唯一能感知到的设备就是 Host/PCI 桥,同时还知道 Host/PCI 桥下面的总线号是 0。
枚举过程中,系统软件会遍历所有可能的 Bus & Device & Function 的组合,尝试去读取(表现为cfg read TLP和CPL iwth data)每个 Bus & Device & Function 位置的 Vendor ID 寄存器。根据读取到的结果,就能判断某个 Bus & Device & Function 所定位的 Function 是否真实存在。按照深度优先算法遍历PCIe总线的树行结构并将总线号配置到各个PCIe的bridage端口(表现为cfg write TLP和CPL no data)。
对于每一级bus的下一层级的枚举, linux kernel会试图遍历寻找出所有的叶子节点,(deivce id从0到31)代码如下所示:
linux kernel枚举log和对应的设备树如下:
+-[0000:16]-+-00.0-[17-21]----00.0-[18-21]--+-00.0-[19]----00.0 Device 7992:0004
| | +-01.0-[1a]----00.0 Device 7992:0003
| | +-02.0-[1b]----00.0 Device 7992:0003
| | +-03.0-[1c]----00.0 Device 7992:0003
| | +-04.0-[1d]----00.0 Device 7992:0003
| | +-05.0-[1e]----00.0 Device 7992:0003
| | +-06.0-[1f]----00.0 Device 7992:0003
| | +-07.0-[20]----00.0 Device 7992:0003
| | \-08.0-[21]----00.0 Device 7992:0003
如下是读写PCIe配置空间报文交互样例:
如下是枚举后形成的PCIe设备树样例: