簡介
PCI/PCIe設備有本身的獨立地址空間,這部分空間會映射到整個系統的地址空間。linux
映射地址在BIOS/UEFI下指定(若是有的話,對於使用非BIOS啓動的OS,不清楚),它有兩種類型,一種是MMIO,一種是IO。對於MMIO的訪問,跟訪問內存的方式同樣,它從稱爲PCIEXBAR的基地址開始,有很大的一段空間,這個PCIEXBAR的值根據不一樣的平臺可能不一樣,大體可能值有0xC0000000、0xE0000000等,關於這個值是怎麼使用的後面的章節會講到;對於IO,它是一種比較老的訪問PCI/PCIe設備的方式,並且佔有的空間相比MMIO很是小,好像只有64K的空間。函數
PCI/PCIe設備使用的空間也有兩個部分,一部分稱爲配置空間(經過MMIO);另外一部分經過配置空間的BAR寄存器指定,是設備實現功能所須要用到的地址空間(有MMIO也有IO, 不過IO用的比較少了)。佈局
PCI/PCIe配置空間的訪問方式
PCI/PCIe設備的配置空間經過PCIEXBAR加上設備的Bus、Device、Fun號的轉換來獲得,BDF到地址的轉換關係以下:spa
/** Macro that converts PCI Bus, PCI Device, PCI Function and PCI Register to an address that can be passed to the PCI Library functions. @param Bus PCI Bus number. Range 0..255. @param Device PCI Device number. Range 0..31. @param Function PCI Function number. Range 0..7. @param Register PCI Register number. Range 0..255 for PCI. Range 0..4095 for PCI Express. @return The encoded PCI address. **/ #define PCI_LIB_ADDRESS(Bus,Device,Function,Register) \ (((Register) & 0xfff) | (((Function) & 0x07) << 12) | (((Device) & 0x1f) << 15) | (((Bus) & 0xff) << 20))
其中的Register是具體要訪問的寄存器。3d
這是最經常使用的一種方式,經過將B/D/F轉換成MMIO的地址,以後就能夠經過MMIO的方式來訪問,下面是一個例子:code
UINT8 EFIAPI PciExpressRead8 ( IN UINTN Address ) { ASSERT_INVALID_PCI_ADDRESS (Address); return MmioRead8 ((UINTN) GetPciExpressBaseAddress () + Address); }
不過這裏有個問題,經過PCI_LIB_ADDRESS獲得的並非實際的系統地址空間,它算是一個偏移,還須要加上一個基地址(就是這個經過函數GetPciExpressBaseAddress()獲得的)。在UEFI中這個基地址被設置成一個PCD變量:PcdPciExpressBaseAddress。它的值根據不一樣平臺可能會不一樣。blog
不太重點不是它的值,重點是如何設置這個值,由於只有設置了這個值才能使用上面說的方式來讀寫PCI/PCIe配置空間。內存
而PCIEXBAR也是要寫到PCI設備的配置空間中的,它會被寫到B0/D0/F0/R060h這個寄存器(不一樣平臺可能不一樣)。ci
這裏就遇到了一個問題,該經過什麼方式來寫這個值呢?資源
實際上,須要注意幾點:
1. 上述提供的訪問PCI/PCIe配置空間的方式是PCIe的方式;
2. 早期在沒有PCIe的時候,要訪問配置空間時,使用的是兩個IO端口,CFCh和CF8h,經過往一個端口指定寄存器,另外一個端口寫值的方式爲指定寄存器賦值。
因此咱們要注意,有兩個配置空間的方式:
1. 傳統方式,寫IO端口CFCh和CF8h。只能訪問PCI/PCIe設備的開始256個字節(由於PCI設備的配置空間原本就只有256個字節);
2. PCIe的方式,就是上面提到的方式,它能夠方位4K個字節的配置空間。
PCI配置空間
因爲PCI/PCIe設備分爲Bridge和Agent兩種,因此配置空間也有兩種類型:
其中Agent的配置空間類型稱爲Type 00h:
簡單介紹其中的幾個寄存器的意義:
Vendor ID,Device ID:標記了一個設備的生產廠商和具體的設備,好比Intel的設備Vendor ID一般是0x8086,Device ID就須要廠家自定義了,總之可以識別到具體是哪一個設備就能夠了。
Status:設備狀態字,具體每一個BIT的意義見下圖:
Command:設備狀態字:
Base Address Registers:決定PCI/PCIe設備空間映射到系統空間具體位置的寄存器,映射方式有兩種,分別是IO和Memory映射:
處理器系統資源分爲IO資源和MMIO資源兩種,所以PCI/PCIe空間地址對應也有兩種。
下面是Bridge的配置空間,它的類型被稱爲Type 01h:
Type 01h中也有Vendor ID,Device ID,Status,Command等寄存器。
另外須要注意的是這裏的BAR計算獲得的系統空間是該橋下掛的全部設備的系統空間的總和。
另外Subordinate Bus Number、Secondary Bus Number和Primary Bus Number,這些寄存器共同肯定了該橋上行和下行的全部Bus號。
PCIe配置空間
PCIe是在PCI基礎上發展的協議,PCIe也有上述的PCI配置空間,而且在此基礎之上進行了擴展,其擴展形式是經過一種稱爲Capability的寄存器塊來完成的。
PCI配置空間的大小是256個字節,即0x00~0xFF,而PCIe的配置空間擴大到了0x00~0xFFF,下圖是具體的佈局。
在原來的配置空間中,有一個寄存器指定了第一個Capability的位置,而第一個Capability又指定下一個Capability,構成了一串Capability,具體以下圖所示:
各個不一樣的Capability的做用不一樣,且不一樣的設備有不一樣的Capability,在這裏不一一介紹了。
其它說明
處理器系統中會爲全部的PCI/PCIe設備留足足夠的空間,可是幾乎沒有系統會滿配,因此不少的配置空間其實是空的,此時若是去訪問,就會獲得全FF,表示設備不存在。
下面是linux下訪問PCI/PCIe配置空間的一個例子:
注:以上是一個Intel網卡的PCI/PCIe配置空間,這是虛擬機下的結果,真實機器上可能有所不一樣。