iT邦幫忙

2024 iThome 鐵人賽

DAY 24
0
自我挑戰組

Linux Kernel 網路巡禮系列 第 24

PCI Configuration Space

  • 分享至 

  • xImage
  •  

前兩天,我們介紹了作業系統如何透過 Memory-Mapped I/O (MMIO) 將外部設備的記憶體空間映射到物理記憶體空間,或者透過 Port-Mapped I/O (PMIO) 的方式,使用 I/O Port 與外部設備溝通。接下來,我們要來了解這些映射關係是如何設置的。

PCI Configuration Access Mechanism (CAM)

在 PCI 標準中,訪問 PCI 設備基本資料及設定的方式稱為 PCI Compatible Configuration Access Mechanism (CAM)。每個設備都有一些暫存器(Registers)來保存設備的基本資訊,例如 Device ID、Vendor ID 等,並提供設置暫存器來對該 PCI 設備進行設置。這些暫存器會排列成一個有序的結構,稱為 Configuration Space Header。

PCI 標準定義了這些暫存器的排列方式及結構外觀。Configuration Space Header 分為兩種類型:Type 0 和 Type 1。Type 0 用於一般外部設備,Type 1 則用於 Root Complex 等 PCI 架構元件。在此,我們僅探討 Type 0 的結構。

https://ithelp.ithome.com.tw/upload/images/20241008/20152703kcnL0Dl7fS.png
透過這個結構,作業系統可以讀取設備的 Device ID、Vendor ID 和 Class Code 等基本資訊。

https://ithelp.ithome.com.tw/upload/images/20241008/20152703WiYHo0V0rf.png
PCIe 提供的 Configuration Space Header 增加了更多暫存器。然而,為了保持與 PCI 架構的兼容性,PCIe 的 Configuration Space Header 前部的暫存器仍與 PCI 保持一致。PCIe 在此基礎上進行了額外擴展。

訪問 PCI Configuration Space 的方法

處理器並沒有直接存取 PCI 設備 Configuration Space 的指令。作業系統必須透過 I/O Space 或 Physical Memory Address 與 PCI 設備進行通訊。這裡我們引入另一個新的地址空間——Configuration Space。所有 PCI 設備的 Configuration Space Header 會映射到這個空間中。

https://ithelp.ithome.com.tw/upload/images/20241008/20152703ri24YQbtdw.png
在 I/O Space 中,有兩個特殊的 Port:0xCF8 和 0xCFC。當作業系統要讀寫某個設備的 Configuration Space Header 時,會透過修改 0xCF8(CONFIG_ADDRESS)指向 Configuration Space 中對應的地址,然後使用 0xCFC(CONFIG_DATA)進行讀寫。

Configuration Space Address

Configuration Address Space 並不是隨機排序的地址,而是使用 BDF(Bus, Device, Function)來定位設備。

https://ithelp.ithome.com.tw/upload/images/20241008/20152703mirPlfKgHi.png

根據 PCI Local Bus Specification,CONFIG_ADDRESS 的格式是透過 BDF 定位設備功能,並且位元 7:0 表示在 configuration space 中的 offset。bit 1 和 bit 0 總是為 0,每次讀取一個 DWORD(4 bytes)。

// arch/x86/pci/direct.c


static int pci_conf1_read(unsigned int seg, unsigned int bus,
			  unsigned int devfn, int reg, int len, u32 *value)
{
    ...
    outl(PCI_CONF1_ADDRESS(bus, devfn, reg), 0xCF8);

	switch (len) {
	case 1:
		*value = inb(0xCFC + (reg & 3));
		break;
	case 2:
		*value = inw(0xCFC + (reg & 2));
		break;
	case 4:
		*value = inl(0xCFC);
		break;
	...
}

static int pci_conf1_write(unsigned int seg, unsigned int bus,
			   unsigned int devfn, int reg, int len, u32 value)
{
	...
	outl(PCI_CONF1_ADDRESS(bus, devfn, reg), 0xCF8);

	switch (len) {
	case 1:
		outb((u8)value, 0xCFC + (reg & 3));
		break;
	case 2:
		outw((u16)value, 0xCFC + (reg & 2));
		break;
	case 4:
		outl((u32)value, 0xCFC);
		break;
	}
    ...
}

Linux Kerenl 利用 PCI_CONF1_ADDRESS 計算地址,並透過 CF8 和 CFC 暫存器進行讀寫。

#define PCI_CONF1_ADDRESS(bus, devfn, reg) \
	(0x80000000 | ((reg & 0xF00) << 16) | (bus << 16) \
	| (devfn << 8) | (reg & 0xFC))

PCI_CONF1_ADDRESS 依據 PCI 規範進行地址計算,將 reg & 0xFC 用來截斷末兩位,並把超過索引範圍的高位 (reg & 0xF00) << 16 寫到 30:24 這些 reserve 的高位,不過這個部分的功能不太清楚。

PCIe Enhanced Configuration Access Mechanism (ECAM)

由於 PCIe 架構擴展了 Configuration Space Header 的內容,額外的部分無法再透過 CAM 機制讀寫。因此,PCIe 額外提供了 PCI Express Enhanced Configuration Access Mechanism (ECAM)。ECAM 機制簡單來說,就是將 PCIe 設備的 Configuration Space Header 映射到 Physical Address Space 中,讓作業系統能夠像操作記憶體一樣,直接管理 PCIe 設備。

今天,我們介紹了 PCI 和 PCIe 設備的 Configuration Space 概念與存取方式。接下來,我們將進一步探討 MMIO 和 PMIO 的建立過程。


上一篇
Port-Mapped IO
下一篇
MMIO 與 PMIO 的建立與管理
系列文
Linux Kernel 網路巡禮30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言