PCIE配置和地址空間

yzxjd1314發表於2020-10-17

來源:https://www.jianshu.com/p/574e9a2cbc4e

 

每個PCIe裝置,有這麼一段空間,Host軟體可以讀取它獲得該裝置的一些資訊,也可以通過它來配置該裝置,這段空間就叫做PCIe的配置空間。不同於每個裝置的其它空間,PCIe裝置的配置空間是協議規定好的,哪個地方放什麼內容,都是有定義的。PCI或者PCI-X時代就有配置空間的概念,那時的配置空間如下:

圖6.1

整個配置空間就是一系列暫存器的集合,其中Type 0是Endpoint的配置,Type 1是Bridge(PCIe時代就是Switch)的配置,都由兩部分組成:64 Bytes的Header+192Bytes的Capability結構,後者是裝置告訴Host它有多牛逼,都會什麼絕活。

進入PCIe時代,PCIe能耐更大,192 Bytes不足以羅列它的絕活。為了保持後向相容,又要不把絕活落下,怎麼辦?很簡單,我擴充套件後者的空間,整個配置空間由256 Bytes擴充套件成4KB,前面256 Bytes保持不變:

圖6.2

PCIe有什麼能耐(Capability)我們不看,我們先挑軟柿子捏,先看看只佔64 Bytes的Configuration Header。

圖6.3

像Device ID,Vendor ID,Class Code和Revision ID,是隻讀暫存器,PCIe裝置通過這些暫存器告訴Host軟體,這是哪個廠家的裝置、裝置ID是多少、以及是什麼型別的(網路卡?顯示卡?橋?)裝置。

其它的我們暫時不看,我們看看重要的BAR(Base Address Register)。

對Endpoint Configuration(Type 0),提供了最多6個BAR,而對Switch(Type 1)來說,只有2個。BAR是幹什麼用的?

每個PCIe裝置,都有自己的內部空間,這部分空間如果開放給Host(軟體或者CPU)訪問,那麼Host怎樣才能往這部分空間寫入資料,或者讀資料呢?

我們知道,CPU只能直接訪問Host記憶體(Memory)空間(或者IO空間,我們不考慮),不對PCIe等外設直接操作。怎麼辦?記得皇帝身邊那個有根的太監嗎?Root Complex,RC。RC可以為CPU分憂。

解決辦法是:CPU如果想訪問某個裝置的空間,由於它不能(或者不屑)親自跟那些PCIe外設打交道,因此叫太監RC去辦。比如,如果CPU想讀PCIe外設的資料,先叫RC通過TLP把資料從PCIe外設讀到Host記憶體,然後CPU從Host記憶體讀資料;如果CPU要往外設寫資料,則先把資料在記憶體中準備好,然後叫RC通過TLP寫入到PCIe裝置。完美!

圖6.4

上圖例子中,最左邊虛線的表示CPU要讀Endpoint A的資料,RC則通過TLP(經歷Switch)資料互動獲得資料,並把它寫入到系統記憶體中,然後CPU從記憶體中讀取資料(紫色箭頭所示),從而CPU間接完成對PCIe裝置資料的讀取。

具體實現就是上電的時候,系統把PCIe裝置開放的空間(系統軟體可見)對映到記憶體空間,CPU要訪問該PCIe裝置空間,只需訪問對應的記憶體空間。RC檢查該記憶體地址,如果發現該記憶體空間地址是某個PCIe裝置空間的對映,就會觸發其產生TLP,去訪問對應的PCIe裝置,讀取或者寫入PCIe裝置。

一個PCIe裝置,可能有若干個內部空間(屬性可能不一樣,比如有些可預讀,有些不可預讀)需要對映到記憶體空間,裝置出廠時,這些空間的大小和屬性都寫在Configuration BAR暫存器裡面,然後上電後,系統軟體讀取這些BAR,分別為其分配對應的系統記憶體空間,並把相應的記憶體基地址寫回到BAR。(BAR的地址其實是PCI匯流排域的地址,CPU訪問的是儲存器域的地址,CPU訪問PCIe裝置時,需要把匯流排域地址轉換成儲存器域的地址。)

圖6.5

如上圖例子,一個Native PCIe Endpoint,只支援Memory Map,它有兩個不同屬性的內部空間要開放給系統軟體,因此,它可以分別對映到系統記憶體空間的兩個地方;還有一個Legacy Endpoint,它既支援Memory Map,還支援IO Map,它也有兩個不同屬性的內部空間,分別對映到系統記憶體空間和IO空間。

來個例子,看一下一個PCIe裝置,系統軟體是如何為其分配對映空間的。

圖6.6

上電時,系統軟體首先會讀取PCIe裝置的BAR0,得到資料:

圖6.7

然後系統軟體往該BAR0寫入全1,得到:

圖6.8

BAR暫存器有些bit是隻讀的,是PCIe裝置在出廠前就固定好的bit,寫全1進去,如果值保持不變,就說明這些bit是廠家固化好的,這些固化好的bit提供了這塊內部空間的一些資訊:

怎麼解讀?低12沒變,表明該裝置空間大小是4KB(2的12次方),然後低4位表明了該儲存空間的一些屬性(IO對映還是記憶體對映,32bit地址還是64bit地址,能否預取?做過微控制器的人可能知道,有些暫存器只要一讀,資料就會清掉,因此,對這樣的空間,是不能預讀的,因為預讀會改變原來的值),這些都是PCIe裝置在出廠前都設定好的,提供給系統軟體的資訊。

然後系統軟體根據這些資訊,在系統記憶體空間找到這樣一塊地方來對映這4KB的空間,把分配的基地址寫入到BAR0:

圖6.9

從而最終完成了該PCIe空間的對映。一個PCIe裝置可能有若干個內部空間需要開放出來,系統軟體依次讀取BAR1,BAR2。。。,直到BAR5,完成所有內部空間的對映。

上面主要講了Endpoint的BAR,Switch也有兩個BAR,今天不打算講,下節講TLP路由,再回過頭來講。繼續說配置空間。

前面說每個PCIe裝置都有一個配置空間,其實這樣說是不準確的,而是每個PCIe裝置至少有一個配置空間。一個PCIe裝置,它可能具有多個功能(function),比如既能當硬碟,還能當網路卡。每個功能對應一個配置空間。

在一個PCIe拓撲結構裡,一條匯流排下面可以掛幾個裝置,而每個裝置可以具有幾個功能,如下所示:

圖6.10

因此,在整個PCIe系統中,只要知道了Bus+Device+Function,就能找到對應的Function。定址基本單元是功能(function),它的ID就由Bus+Device+Function組成 (BDF)。一個PCIe系統,可以最多有256條Bus,每條Bus上可以掛最多32個Device,而每個Device最多又能實現8個Function,而每個Function對應著4KB的配置空間。上電的時候,這些配置空間都是需要對映到Host的記憶體空間,因此,需要佔用記憶體空間是:256*32*8*4KB =256MB。在這個動輒4GB、8GB記憶體的時代,256MB算不了什麼。

系統軟體是如何讀取Configuration空間呢?不能通過BAR中的地址,為什麼?別忘了BAR是在Configuration中的,你首先要讀取Configuration,才能得到BAR。前面不是系統為所有可能的Configuration預留了256MB記憶體空間嗎?系統軟體想訪問哪個Configuration,只需指定相應Function對應的記憶體空間地址,RC發現這個地址是Configuration對映空間,就會產生相應的Configuration Read TLP去獲得相應Function的Configuration。

再回想一下前面介紹的Configuration Read TLP的Header格式:

圖6.11

Bus Number + Device + Function就唯一決定了目標裝置; Ext Reg Number + Register Number相當於配置空間的偏移。找到了裝置,然後指定了配置空間的偏移,就能找到具體想訪問的配置空間的某個位置。

結束前,強調一下,只有RC才能發起Configuration的訪問請求,其他裝置是不允許對別的裝置進行Configuration讀寫的。



作者:idorax
連結:https://www.jianshu.com/p/574e9a2cbc4e
來源:簡書
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。

相關文章