天天看点

PCIE初始化时,下游设备还未分配到bus,如何访问它们?

学BIOS的都知道,访问PCI设备是通过bus:device:function来访问的。如果是CF8/CFC的方式,bus:device:function是向CF8里写,然后从CFC里读,或者向CFC里写数据;如果是memory map的方式,则直接去PCIEBase+bus:device:function的方式去读写。这里的访问是在设备已经初始化,分配了bus之后,那么,在整个系统刚启动时,是如何访问到整个系统所有的PCIE设备的呢?

这个问题在PCIE的规范里面有专门的章节来解释,称作“PCI Express Enumeration”,里面包括单个Root Complex和多个Root Complex的情况。

对于单个Root Complex的情况:

1 在reset之后,Host/PCI Bridge的bus是0,primary bus为0,secondary bus为0,subordinate bus也为0。

2 此时读取bus0上所有可能的32个device,如果读到device ID不为0,也不为全F,则表示该device存在。

3 接着进一步判断该device的类型。

4 如果该device是endpoint ,则看它是否是multi function。如果不是,则对该device的扫描结束,否则,读function 1-7.

5 如果该device是PCI-to-PCI bridge,则配置该device的primary bus, secondary bus, subordinate bus:

primary bus = current bus;

secondary bus = current bus + 1;

subordinate bus = current bus + 1;

同时,用它自己的subordinate bus去更新它所连接的primary bus上device的subordinate bus。

然后,current bus++; 这样,就能访问到该P2P bridge的下一层bus上的device了。

6 依次这样,进行深度优先的遍历,就能把整个PCIE系统枚举完了。

在多个Root Complex的情况下时,在第一步中需要区分开到底是那个Root Complex在bus0上。X86系统中,primary Root Complex在reset之后处于bus0,而secondary Root Complex在reset后处于bus 255,因此第一步不是问题。按照上面的方法把primary Root Complex中所有设备枚举完后,在primary Root Complex的subordinate bus上加1,就是secondary Root Complex的bus号,然后继续进行深度优先遍历。

总结下,bus在这里面的角色很巧妙,它既是访问设备时“索引”的一部分(通过bus:device:function来定位),又可以动态的指定。实际上,只要PCIE的request能够发到device上,device本身并不在乎bus的。一个device连在处于bus9的P2P bridge上,它所分配到的bus是10,只要能够保证PCIE request传到该device,哪怕把P2P bridge的secondary bus改成其他值,这个device还是一样会响应。巧妙的地方在于,在PCIE的规范中,如果bus不处于bridge的secondary bus和subordinate bus之间,该bridge就不会对PCIE request进行转发,因此,P2P bridge的secondary bus并不能随意更改。也即:bus值只用来找到该device所位于的层,在这层内,通过device和function来寻找设备。在UEFI的device path里,PCI path并不包括bus,就是因为bus是动态分配的,并不固定。

问题:primary Root Complex和secondary Root Complex是如何确定的?

继续阅读