天天看點

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是如何确定的?

繼續閱讀