Linux下PCIe子系統(二)——PCIe子系統框架詳解
1. 概述
PCIe(PCI Express)子系統是Linux內核中負責管理PCI/PCIe設備的核心組件。它提供了一套完整的框架來發現、配置和管理PCI設備,實現了設備的即插即用和熱插拔功能。
2. 核心數據結構
PCIe子系統使用以下核心數據結構來組織和管理設備:
2.1 主要數據結構
結構體 | 描述 | 作用 |
---|---|---|
struct pci_host_bridge | Host Bridge表示 | PCI域的根管理器,連接CPU和PCI總線 |
struct pci_bus | PCI總線表示 | 代表一條PCI總線,可以掛載多個設備 |
struct pci_dev | PCI設備表示 | 代表具體的PCI設備實例 |
2.2 數據結構關系
┌─────────────────────┐
│ pci_host_bridge │ ← Host Bridge (根管理器)
└──────────┬──────────┘│▼
┌─────────────────────┐
│ pci_bus │ ← PCI總線 (Bus 0)
│ (Root Bus) │
└──────────┬──────────┘│▼
┌─────────────────────┐
│ pci_dev │ ← PCI設備
│ (PCI設備/橋設備) │
└─────────────────────┘
3. 驅動框架基礎
3.1 設備分類
Linux設備驅動將設備分為三大類:
- 字符設備 - 順序訪問的設備(如串口、鍵盤)
- 塊設備 - 隨機訪問的設備(如硬盤、U盤)
- 網絡設備 - 網絡通信設備(如網卡)
3.2 字符設備注冊流程
PCI設備通常以字符設備的形式加載到內核中,其注冊流程包括:
- 注冊設備號 - 獲取主設備號和次設備號
- 創建并初始化cdev結構體 - 綁定file_operations
- 添加字符設備 - 將設備添加到內核
- 創建設備文件 - 在/dev下創建設備節點
- 實現file_operations - 實現設備操作函數集
注意: 學習PCIe子系統的目的是為專門的PCI設備開發對應的驅動程序。
4. PCI子系統實現流程
4.1 整體流程概覽
4.2 時間線
啟動時間線:
0ms 內核啟動開始
2ms pci_driver_init() - PCI總線類型注冊
5ms pcibios_init() - 架構相關初始化及設備枚舉
8ms pci_subsys_init() - PCI子系統完善
15ms device_initcall() - 設備驅動匹配
20ms 用戶空間啟動
5. PCI子系統實現細節
5.1 PCI子系統初始化
5.1.1 pci_driver_init()函數
PCI子系統初始化通過pci_driver_init()
函數實現,在內核中創建PCI總線(軟件抽象):
static int __init pci_driver_init(void)
{int ret;// 注冊PCI總線類型到設備模型ret = bus_register(&pci_bus_type);if(ret)return ret;// 添加DMA調試支持dma_debug_add_bus(&pci_bus_type);return 0;
}
5.1.2 關鍵功能
- 總線注冊: 調用
bus_register(&pci_bus_type)
(所有總線都通過此函數注冊) - 設備模型集成: 將全局
pci_bus_type
結構體注冊到內核設備模型 - sysfs接口: 在
/sys/bus/
下創建pci
目錄,包含devices
和drivers
子目錄
5.2 設備枚舉機制
5.2.1 枚舉 vs 設備樹
傳統設備樹方式:
- 在設備樹中描述硬件信息
- 通過
compatible
屬性與驅動的of_match_table
匹配 - 靜態配置,啟動時解析一次
PCIe枚舉方式:
- 動態發現設備
- 支持即插即用和熱插拔
- 運行時設備管理
枚舉定義: 在計算機硬件領域是指系統性地發現、識別和配置硬件設備的過程。
5.2.2 為什么PCIe使用枚舉
使用設備樹會破壞PCIe設備的核心優勢:
- ? 即插即用 - 新設備無法自動識別
- ? 熱插拔 - 運行時插拔設備無法處理
- ? 標準化 - 失去PCI協議的標準化優勢
5.2.3 PCIe設備枚舉詳細流程
階段一:基礎設施建設
pci_driver_init() → 注冊PCI總線類型
pcibios_init() → 平臺特定PCI配置
pci_subsys_init() → 啟動完整枚舉流程
階段二:Host Bridge注冊和Root Bus創建
pci_scan_root_bus_bridge()
├── pci_register_host_bridge() // 注冊Host Bridge
│ ├── 創建Root Bus (Bus 0)
│ ├── 分配地址空間資源
│ │ ├── 內存窗口
│ │ └── I/O端口窗口
│ └── 注冊到Linux設備模型
└── pci_scan_child_bus() // 開始設備掃描
階段三:遞歸設備掃描
核心掃描工作由pci_scan_child_bus_extend()
完成,采用深度優先遞歸算法:
pci_scan_child_bus_extend(bus, available_buses)
├── 第一階段:設備掃描
│ └── for (devfn = 0; devfn < 0x100; devfn += 8) { // 32設備×8功能
│ ├── pci_scan_slot(bus, devfn)
│ ├── pci_scan_single_device()
│ ├── pci_scan_device() // 讀取配置空間
│ └── pci_device_add() // 添加到系統
│ }
├── 第二階段:橋設備處理
│ └── for_each_pci_bridge(dev, bus) {
│ ├── pci_add_new_bus() // 創建子總線
│ ├── 分配下級總線號
│ └── pci_scan_child_bus_extend(child_bus) // 遞歸!
│ }
└── 第三階段:資源分配├── pci_bus_size_bridges() // 計算橋設備資源需求└── pci_bus_assign_resources() // 分配實際資源
5.2.4 設備發現機制
設備存在檢測:
// 讀取vendor ID判斷設備是否存在
pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &vendor_id);if (vendor_id == 0xffffffff || vendor_id == 0x00000000) {// 設備不存在return NULL;
}
配置空間信息讀取:
struct pci_dev *dev = pci_alloc_dev(bus);
dev->vendor = vendor_id & 0xffff;
dev->device = (vendor_id >> 16) & 0xffff;// 讀取其他重要信息
pci_read_config_dword(dev, PCI_CLASS_REVISION, &class);
pci_read_config_byte(dev, PCI_HEADER_TYPE, &hdr_type);
// 讀取BAR寄存器等...
5.2.5 遞歸掃描示例
掃描Root Bus 0:├── 00:00.0 - Host Bridge├── 00:01.0 - PCIe Root Port (橋設備)│ └── 創建Bus 1 → 遞歸掃描│ └── 01:00.0 - 顯卡├── 00:02.0 - 集成顯卡├── 00:1c.0 - PCIe Root Port (橋設備) │ └── 創建Bus 2 → 遞歸掃描│ └── 02:00.0 - 網卡└── 00:1f.0 - ISA橋└── 創建Bus 3 → 遞歸掃描
6. 設備驅動匹配機制
6.1 關鍵函數
PCIe設備與驅動的匹配依賴兩個核心函數:
6.1.1 pci_bus_match()
功能:設備驅動匹配函數
static int pci_bus_match(struct device *dev, struct device_driver *drv)
{struct pci_dev *pci_dev = to_pci_dev(dev);struct pci_driver *pci_drv = to_pci_driver(drv);const struct pci_device_id *found_id;// 遍歷驅動的設備ID表進行匹配found_id = pci_match_device(pci_drv, pci_dev);if (found_id)return 1; // 匹配成功return 0; // 匹配失敗
}
匹配條件:
- Vendor ID: 廠商標識(由PCI-SIG分配)
- Device ID: 設備標識(廠商自定義)
- Subsystem Vendor ID: 子系統廠商ID(可選)
- Subsystem Device ID: 子系統設備ID(可選)
- Class Code: 設備類別(可選)
特殊值:
- 驅動中設置
PCI_ANY_ID
可匹配所有對應字段
6.1.2 pci_bus_probe()
功能:設備探測函數
static int pci_device_probe(struct device *dev)
{struct pci_dev *pci_dev = to_pci_dev(dev);struct pci_driver *drv = to_pci_driver(dev->driver);const struct pci_device_id *id;id = pci_match_device(drv, pci_dev);if (!id)return -ENODEV;// 調用驅動的probe函數return drv->probe(pci_dev, id);
}
6.2 匹配流程
6.3 vendor和device信息
存儲位置:
- PCI配置空間: 偏移0x00(Vendor ID)和0x02(Device ID)
- 硬件固化: 設備制造時寫入,無法修改
獲取方式:
# 使用lspci查看設備信息
lspci -nn
# 輸出示例:
# 00:02.0 VGA compatible controller [0300]: Intel Corporation Device [8086:9bc4]
# ^^^^ ^^^^
# vendor device
驅動中的使用:
static const struct pci_device_id my_pci_ids[] = {{ PCI_DEVICE(0x8086, 0x1234) }, // Intel的某個設備{ PCI_DEVICE(PCI_ANY_ID, PCI_ANY_ID) }, // 支持所有設備 { 0, } // 結束標記
};
MODULE_DEVICE_TABLE(pci, my_pci_ids);
7. PCIe Host設備創建(設備樹方式)
7.1 PCIe Host Controller的特殊性
雖然PCIe設備通過枚舉發現,但PCIe Host Controller本身需要通過設備樹描述:
- Host Controller: Platform設備,通過設備樹描述
- PCI設備: 通過Host Controller枚舉發現
7.2 設備樹到Platform設備流程
7.2.1 設備樹編譯和加載
DTS源文件 → DTC編譯器 → DTB二進制文件 → Bootloader加載到內存
7.2.2 DTS文件示例
pcie: pcie@f0000000 {compatible = "samsung,exynos5440-pcie";reg = <0xf0000000 0x1000>, <0xf0001000 0x1000>;interrupts = <0 5 0>, <0 4 0>;ranges = <0x81000000 0 0x60000000 0x60000000 0 0x00010000 /* I/O */0x82000000 0 0x60010000 0x60010000 0 0x0FFF0000>; /* Memory */#address-cells = <3>;#size-cells = <2>;device_type = "pci";
};
7.2.3 內核解析流程
start_kernel()└── setup_arch()└── unflatten_device_tree() // 展開設備樹└── __unflatten_device_tree()└── populate_node() // 創建device_node結構
7.2.4 Platform設備創建
// 在core_initcall階段
of_platform_default_populate_init()└── of_platform_default_populate()└── of_platform_bus_create()└── of_platform_device_create_pdata() // 創建platform_device
7.3 時間線對比
啟動時間線:
0ms bootloader加載DTB到內存
2ms unflatten_device_tree() - 解析成device_node
5ms pci_driver_init() - PCI子系統初始化
10ms of_platform_default_populate() - 創建platform_device
12ms PCIe Host Controller驅動匹配和probe
15ms pci_scan_root_bus_bridge() - 開始PCI設備枚舉
20ms PCI設備驅動匹配
8. 枚舉過程關鍵特點
8.1 技術特性
- 自動發現性: 利用PCI協議的自描述特性,無需外部配置
- 層次化管理: 通過總線-橋-設備的樹狀結構組織
- 資源協調: 統一管理地址空間分配,避免資源沖突
- 熱插拔支持: 為運行時設備插拔提供基礎架構
8.2 掃描算法特點
- 廣度優先: 同級設備并行掃描
- 深度遞歸: 發現橋設備時遞歸掃描下級總線
- 資源預留: 為橋設備預留足夠的地址空間
- 錯誤處理: 優雅處理配置空間讀取失敗等異常
8.3 與其他總線對比
特性 | PCIe設備 | 設備樹設備 |
---|---|---|
設備發現 | 硬件自動枚舉 | 軟件解析設備樹 |
設備信息 | 配置空間自描述 | 設備樹人工描述 |
匹配依據 | vendor/device ID | compatible字符串 |
熱插拔 | 天然支持 | 通常不支持 |
標準化 | 完全標準化 | 需要自定義 |
9. 總結
9.1 PCIe子系統優勢
PCIe子系統通過精心設計的層次化架構,實現了從硬件發現到驅動匹配的完整流程:
- 自動化設備發現: 通過枚舉機制自動發現設備
- 標準化管理: 統一的數據結構和接口
- 即插即用支持: 支持熱插拔和動態配置
- 層次化設計: 清晰的Host Bridge → Bus → Device架構
- 資源管理: 智能的地址空間分配和管理
9.2 關鍵技術點
- 遞歸掃描算法: 深度優先遍歷整個PCI設備樹
- 配置空間標準: 標準化的設備信息獲取方式
- 雙重機制: Host Controller用設備樹,PCI設備用枚舉
- 設備驅動分離: 清晰的設備發現和驅動匹配分離