2022年9月4日 星期日

初學 PCIe System (二) - 如何訪問PCIe設備的配置空間

初學 PCIe System (一) - PCIe介紹及其配置空間中有提到可以從PCIe設備的配置空間(Configuration Space)獲取許多重要的資訊,那在CPU中,訪問配置空間常用的兩種方式如下:

Programmed Input/Output (PIO)

PIO 是由CPU主動向Device 獲取資料 ,CPU 會等待Device資料傳輸完後才去做其他事情。使用PIO會占住CPU資源,會讓系統變得低效,因此此技術已幾乎被直接記憶體訪問(DMA)所取代。

這邊簡單介紹一下PIO的作法,首先每個PCI Function的配置空間中和PCI相容的前256 Bytes會映射到一個第三個空間:配置地址空間(configuration address space)。 因為PCI Function最多會有(2^8) * (2^5) * (2^3) = 65536個,每個Function都會映射256Byte的配置空間,因此配置地址空間的大小為 65536 * 256 Bytes = 16 MB。

CPU 通過主橋(Host Bridge)中的 IO 映射地址端口(Address Port )和資料端口(Data Port)進行索引來間接訪問 PCI 配置空間。

地址端口(Address Port )位於 IO 地址 CF8h-CFBh,是一個大小為32 bits的暫存器,需要填入CONFIG_ADDRESS,其格式如下

Bit 31Bits 30-24Bits 23-16Bits 15-11Bits 10-8Bits 7-0
Enable BitReservedBus NumberDevice NumberFunction NumberRegister Offset

Bit 31表示Enable bit,如果不設起來就不會有作用。(1<<31) = 0x80000000,所以通常會直接設起來,直接做 | 0x80000000的動作

Bit 8-23 是上一章節提到的PCIe Device Address是由 Bus Number+Device Number+Function Number所組成的

Bit0-7這邊就填想到訪問的PCIe 配置空間的Offset,最多就是0x00-0xff: 256 Bytes

所以CONFIG_ADDRESS 的格式公式如下:

0x80000000 | bus << 16 | device << 11 | function <<  8 | offset

CONFIG_ADDRESS 指定完需要訪問的配置地址,會對資料端口映射的位置 CFCh-CFFhCONFIG_DATA 暫存器的訪問生成配置訪問,將資料傳入或傳出 CONFIG_DATA 暫存器。

簡單來說,例如我們想要讀取"bus0 dev0 fun0, offset 0x00"的位置,那他的CONFIG_ADDRESS 就是0x80000000, 我們把它寫入地址端口0xCF8,那該位置的資料就可以從資料端口0xCFC讀到資料



這邊用Linux的程式碼early.c « pci « x86 « arch - kernel/git/stable/linux.git - Linux kernel stable tree為範例,讀取的話動作如下


寫入的話,動作差不多,就是將想要寫入的值填到CONIG_DATA而已


*另外這邊可以發現,透過PIO只能讀寫配置空間中的前256Bytes,所以其餘的部分的讀取需要透過DMA。


Direct Memory Access (DMA) 

DMA 是通知Device要做什麼,Device會自己去寫記憶體。所以DMA 可以一次獲取大量資料,而且不會占住CPU資源。

所以我們可以直接透過Memory去訪問PCI Configuration,地址公式如下:

BaseAddress + bus << 20 | device << 15 | function <<  12 | offset

這邊給offset的位置大小為3 Bytes

另外BaseAddress 每個platform都不一樣,需要查Spec

有了地址之後,可以直接用指標的方式取的資料,例如地址為Addr:

unsigned int data = *Addr;



沒有留言:

張貼留言

注意:只有此網誌的成員可以留言。