摘要:本文主要是对 Single-Root System 的枚举过程进行分析。
启动阶段,处理器上的系统软件执行的枚举步骤如下:
- 从 Device 0 开始,依次尝试读取 Bus 0 上可能存在的 32 个 Device 的 Function 0 的 Vendor ID。如果 Device 0 返回一个合法的 Vendor ID,则证明 Device 0 存在并且包含至少一个 Function。如果返回的 Vendor ID 不合法,则开始尝试 Bus 0 Device 1 Function 0。
一个 Bus 上最多包含 32 个 Device,所以每个 Bus 都需要遍历 Device 0 ~ Device 31。
- 在图片事例中,Bus 0 Device 0 Function 0 的 Header Type 寄存器的 Bit[6;0] 位是 0x01,表明该设备是 PCI-to-PCI 桥。Header Type 寄存器的 Bit[7] 是 0,表明该 Device 只包含 1 个 Function。
-
系统软件识别到 Bus 0 Device 0 Function 0 是 PCI-to-PCI 桥后,对它的配置空间寄存器做如下修改:
- Primary Bus Number Register = 0
- Secondary Bus Number Register = 1
- Subordinate Bus Number Register = 255
配置完成后,这个 PCI-to-PCI 桥就知道它的上游端口直接相连的是 Bus 0,下游端口直接相连的是 Bus 1,并且它的下游端口可以访问的最大的总线号是 255。
- PCIe 总线枚举采用的是深度优先(depth-first)算法,因此,完成第 4 步的配置之后,需要先去搜索 Bus 1 上的设备,而不是去搜索 Bus 0 Device 1 Function 0。
- 系统软件尝试去读取 Bus 1 Device 0 Function 0 的 Vendor ID。在本例中,Bus 1 Device 0 Function 0 是 C 桥(Bridge C),所以能返回合法的 Vendor ID,表明 Bus 1 Device 0 Function 0 设备存在。
- 系统软件读取 Bus 1 Device 0 Function 0 的 Header Type 寄存器,发现 Bit[6:0] = 0x1,表明这是个 PCI-to-PCI 桥;Bit[7] = 0,表明该 Device 只包含 1 个 Function。
-
系统软件对 C 桥(Bridge C)的配置空间寄存器进行如下配置:
- Primary Bus Number Register = 1
- Secondary Bus Number Register = 2
- Subordinate Bus Number Register = 255
配置完成后,这个 PCI-to-PCI 桥就知道它的上游端口直接相连的是 Bus 1,下游端口直接相连的是 Bus 2,并且它的下游端口可以访问的最大的总线号是 255。
- 根据深度优先(depth-first)算法,继续读取 Bus 2 Device 0 Function 0 的 Vendor ID。在本例中,Bus 2 Device 0 Function 0 对应的是 D 桥(Bridge D)。
- 系统软件会从 Bus 2 Device 0 Function 0 得到合法的 Vendor ID,表明 Bus 2 Device 0 Function 0 存在。
- 读取 Bus 2 Device 0 Function 0 的 Header Type 寄存器,发现 Bit[6:0] = 0x1,表明这是个 PCI-to-PCI 桥;Bit[7] = 0,表明该 Device 只包含 1 个 Function。
- 系统软件对 D 桥(Bridge D)的配置空间寄存器进行如下配置:
- Primary Bus Number Register = 2
- Secondary Bus Number Register = 3
- Subordinate Bus Number Register = 255
- 根据深度优先(depth-first)算法,继续读取 Bus 3 Device 0 Function 0 的 Vendor ID。
- 从 Bus 3 Device 0 Function 0 中获取合法的 Vendor ID,表明 Bus 3 Device 0 Function 0 存在。
- 读取 Bus 3 Device 0 Function 0 的 Header Type,发现 Bit[6:0] = 0x0,表明 Bus 3 Device 0 Function 0 是个 Endpoint;Bit[7] = 1,表明 Bus 3 Device 0 上包含多个 Function。
- 每个 Device 最多允许包含 8 个 Function,所以系统软件会依次尝试读取 Bus 3 Device 0 Function 1 ~ Bus 3 Device 0 Function 7。在本例中,只有 Bus 3 Device 0 Function 1 会返回合法的 Vendor ID,表明 Bus 3 Device 0 Function 1 存在。与此同时,Bus 3 Device 0 Function 1 的 Header Type Bit[6:0] = 0x1,表明这是个 Endpoint。
- 系统软件继续尝试读取 Bus 3 Device 1 Function 0 ~ Bus 3 Device 31 Function 的 Vendor ID。在本例中,由于这些设备都不存在,所以得到的 Vendor ID 都是非法的。
- 此时,系统软件已经枚举了 D 桥(Bridge D)下的所有设备,能够确认 D 桥(Bridge D)的下游端口(downstream)连接的最大的总线号就是 3。因此,系统软件需要将 D 桥(Bridge D)的 Subordinate Bus Number Register 设置为 3。既然 Bus 3 已经遍历完了,那就需要回退的 Bus 2,开始接着遍历 Bus 2 上的其他 Device 。在本例中,Bus 2 Device 1 Function 0 对应的是 E 桥(Bridge E)。
- 从 Bus 2 Device 1 Function 0 中获取合法的 Vendor ID,表明 Bus 2 Device 1 Function 0 存在。
- 读取 Bus 2 Device 1 Function 0 的 Header Type,发现 Bit[6:0] = 0x1,表明 Bus 2 Device 1 Function 0 是个 PCI-to-PCI 桥;Bit[7] = 0,表明 Bus 2 Device 1 上只包含 1 个 Function。
- 系统软件对 E 桥(Bridge E)的配置空间寄存器进行如下配置:
- Primary Bus Number Register = 2
- Secondary Bus Number Register = 4 (E 桥引入了一条新的 Bus)
- Subordinate Bus Number Register = 255
- 根据深度优先算法,尝试读取 Bus 4 Device 0 Function 0 的 Vendor ID。
- 从 Bus 4 Device 0 Function 0 中获取合法的 Vendor ID,表明 Bus 4 Device 0 Function 0 存在。
- 读取 Bus 4 Device 0 Function 0 的 Header Type,发现 Bit[6:0] = 0x0,表明 Bus 4 Device 0 Function 0 是个 Endpoint;Bit[7] = 0,表明 Bus 4 Device 0 上只包含 1 个 Function。
- 继续尝试枚举 Bus 4 上的其他 Device 1 ~ 31,发现都不存在。
- 此时,E 桥(Bridge E)下游端口(downstream)Bus 4 上的所有 Device & Function 都枚举完成了,能够确认 E 桥下游端口连接的最大总线号就是 4。因此,系统软件将 E 桥的 Subordinate Bus Number Register 设置为 4。Bus 4 枚举完成之后,再返回 Bus 2 进行枚举,读取 Bus 2 Device 2 Function 的 Vendor ID。在本例中,Bus 2 Device 2~31 Function 0 都不存在,所以返回的都是非法 Vendor ID。
- Bus 2 的枚举到此结束,能够确认 C 桥(Bridge C)下游端口(downstream)连接的最大的总线号是 4。因此,系统软件需要将 C 桥(Bridge C)的 Subordinate Bus Number Register 设置为 4。然后,返回 Bus 1,继续枚举下一个 Device(Bus 1 Device 1 Function 0)。在本例中,Bus 1 Device 1~31 Function 0 都不存在,所以返回的都是非法 Vendor ID。
- Bus 1 的枚举到此结束,能够确认 A 桥(Bridge A)下游端口(downstream)连接的最大的总线号是 4。因此,系统软件需要将 A 桥(Bridge A)的 Subordinate Bus Number Register 设置为 4。Bus 1 枚举结束后,返回 Bus 0 继续枚举下一个 Device (Device 1)。在本例中,Bus 0 Device 1 Function 0 是一个 PCI-to-PCI 桥。
- 像之前的步骤一样,系统软件检测到 Bus 0 Device 1 Function 0 是一个 PCI-to-PCI 桥,于是修改它的配置空间寄存器:
- Primary Bus Number Register = 0
- Secondary Bus Number Register = 5
- Subordinate Bus Number Register = 255
- 根据深度优先算法,开始枚举 Bus 5,于是发现了 F 桥。系统软件修改 F 桥的配置空间寄存器:
- Primary Bus Number Register = 5
- Secondary Bus Number Register = 6
- Subordinate Bus Number Register = 255
- 根据深度优先算法,开始枚举 Bus 6,于是发现了 G 桥。修改 G 桥的配置空间寄存器:
- Primary Bus Number Register = 6
- Secondary Bus Number Register = 7
- Subordinate Bus Number Register = 255
- 根据深度优先算法,开始枚举 Bus 7, 于是发现了 Bus 7 Device 0 Function 0 是一个 Endpoint,并且该 Device 只包含 1 个 Function。到此,Bus 7 枚举结束,需要将 G 桥的 Subordinate Bus Number Register 修改为 7。
- Bus 7 枚举完成,返回 Bus 6 继续枚举下一个 Device(Bus 6 Device 1 Function 0),于是发现了 H 桥。修改 H 桥的配置如下:
- Primary Bus Number Register = 6
- Secondary Bus Number Register = 8
- Subordinate Bus Number Register = 255
- 根据深度优先算法,开始枚举 Bus 8,于是发现了 J 桥。修改其配置空间寄存器:
- Primary Bus Number Register = 8
- Secondary Bus Number Register = 9
- Subordinate Bus Number Register = 255
- 根据深度优先算法,枚举 Bus 9 上的所有 Device & Function,发现没有 Bridge,全是 Endpoint。此时能够确定 J 桥下游端口(downstream)连接的最大总线号是 9。修改 J 桥的 Subordinate Bus Number Register 为 9。返回 Bus 8 继续枚举,发现没有其它 Device & Function 了,所以修改 H 桥的 Subordinate Bus Number Register 为 9 。
- Bus 8 枚举完成后,返回 Bus 6 继续枚举下一个 Device(Bus 6 Device 2 Function 0),于是发现了 I 桥。修改 I 桥的配置空间寄存器:
- Primary Bus Number Register = 6
- Secondary Bus Number Register = 10
- Subordinate Bus Number Register = 255
- 根据深度优先算法,开始枚举 Bus 10,发现只有一个 Endpoint(Bus 10 Device 0 Function 0)。
- Bus 10 枚举完成,修改 I 桥的 Subordinate Bus Number Register 为 10。
- 返回 Bus 6,发现没有其它的 Device & Function 了,于是修改 F 桥的 Subordinate Bus Number Register 为 10。
- 返回 Bus 5,发现没有其它的 Device & Function 了,于是修改 B 桥的 Subordinate Bus Number Register 为 10。
- 返回 Bus 0,发现没有其它的 Device & Function 了,于是修改 Host/PCI 桥的 Subordinate Bus Number Register 为 10。
到此为止,整个枚举的过程就完成了!!!
声明:本文中的图片来自MindShare, Inc 经典书籍《PCI Express Technology》。