Bios实习 学习篇- 6.PCIe学习(3)
配置空间深度解析(硬件寄存器级)
在PCIe里,每个设备都有一块配置空间(4KB),里面存: 这个设备是谁(厂商ID) 它是干嘛的(class code) 它用哪些地址(BAR) 怎么控制它(command寄存器)
00: 86 80 0e 10 07 04 10 00 00 00 00 02 00 00 00 00 10: 00 00 00 00 00 00 00 00 00 00 00 00 81 01 00 00 20: 00 00 00 00 00 00 00 00 00 00 00 00 86 80 00 00 30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40: 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00: 86 80 0e 10 07 04 10 00 00 00 00 02 00 00 00 00
↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
0 1 2 3 4 5 6 7 8 9 A B C D E F
| 偏移 | 字节数 | 十六进制值 | 实际含义 | |------|--------|-----------|---------| | 0x00 | 2字节 | 86 80 | Vendor ID = 0x8086 (Intel) | | 0x02 | 2字节 | 0e 10 | Device ID = 0x100e (Intel 82540EM网卡) | | 0x04 | 2字节 | 07 04 | Command寄存器 = 0x0407 (启用I/O+内存+总线主控) | | 0x06 | 2字节 | 10 00 | Status寄存器 = 0x0010 | | 0x08 | 1字节 | 00 | Revision ID = 0 | | 0x09 | 3字节 | 00 00 02 | Class Code = 0x020000 (网络控制器) |
而这块配置空间的“前64字节”叫:Header头部
这个Header不是统一格式,而是分两种:
1.Type 0 Header(端设备)
用于:真正干活的设备(Endpoint)
比如:显卡 、网卡、NVMe SSD ,Type 0 = “我是设备,请通过这些地址访问我”
2.Type 1 Header(桥设备)
用于:PCIe桥(Bridge)
比如:Root Complex(CPU侧)、PCIe Switch、PCIe-to-PCI桥,Type 1 = “我不是设备,我是中转站,我负责转发数据”
// Offset 0x00-0x3F (64B)
struct Type0_Header {
uint16_t vendor_id; // 0x00: 0xFFFF = 无设备
uint16_t device_id; // 0x02: 厂商分配
uint16_t command; // 0x04: 关键控制寄存器
uint16_t status; // 0x06: 状态寄存器
uint8_t revision_id; // 0x08: 修订版本
uint8_t class_code[3]; // 0x09-0x0B: 0x030000 = VGA
uint8_t cache_line_sz; // 0x0C: 缓存行大小
uint8_t latency_timer; // 0x0D: PCI遗留
uint8_t header_type; // 0x0E: [7]=多功能位, [6:0]=0x00
uint8_t bist; // 0x0F: 内建自检
// BAR寄存器 (关键!)
uint32_t bar[6]; // 0x10-0x24: 基地址寄存器
uint32_t cardbus_cis; // 0x28: CardBus CIS指针
uint16_t subsystem_vendor_id; // 0x2C
uint16_t subsystem_id; // 0x2E
uint32_t rom_base; // 0x30: ROM基地址
uint8_t cap_ptr; // 0x34: 能力列表指针
uint8_t reserved[7]; // 0x35-0x3B
uint8_t interrupt_line; // 0x3C: IRQ线路
uint8_t interrupt_pin; // 0x3D: 中断引脚 (1-4)
uint8_t min_grant; // 0x3E: 最小延迟
uint8_t max_latency; // 0x3F: 最大延迟
};
// 与Type 0前16字节相同
struct Type1_Header {
// 0x00-0x0F: 同Type 0
// 0x10-0x17: Primary/Secondary Bus配置
uint8_t primary_bus; // 0x18: 上游总线号
uint8_t secondary_bus; // 0x19: 下游总线号
uint8_t subordinate_bus; // 0x1A: 从属总线号
uint8_t secondary_latency; // 0x1B
// 0x1C-0x1F: I/O基地址/限地址
uint8_t io_base; // 0x1C: I/O空间基地址
uint8_t io_limit; // 0x1D: I/O空间限地址
// 0x20-0x27: Secondary Status/IO寄存器
uint16_t secondary_status; // 0x1E
uint16_t memory_base; // 0x20: 内存空间基地址
uint16_t memory_limit; // 0x22: 内存空间限地址
uint16_t prefetchable_memory_base; // 0x24
uint16_t prefetchable_memory_limit; // 0x26
uint32_t prefetchable_base_upper; // 0x28
uint32_t prefetchable_limit_upper; // 0x2C
uint16_t io_base_upper; // 0x30
uint16_t io_limit_upper; // 0x32
uint8_t cap_ptr; // 0x34: 能力指针
uint8_t reserved[3]; // 0x35-0x37
uint32_t expansion_rom; // 0x38: 扩展ROM
uint8_t interrupt_line; // 0x3C
uint8_t interrupt_pin; // 0x3D
uint16_t bridge_control; // 0x3E: 桥控制寄存器
};
3.Capability结构链(硬件自动构建)
Capability链 = PCIe设备用“链表”方式挂载的一组“高级功能描述块”,这些结构是设备硬件自己提供的,不是CPU/软件创建的
为什么需要 Capability?前面那64B Header(你刚看的 struct)是固定格式
但问题是:设备功能越来越复杂,比如:MSI中断、PCIe链路信息、电源管理、热插拔、SR-IOV(虚拟化)
⚠️ 如果全塞进固定Header: 不够用、不灵活
所以PCIe设计了:🧠 扩展机制:Capability(能力结构)
Capability“链”的本质
它其实是一个单向链表(linked list):
配置空间(4KB): [Header] ↓ (cap_ptr = 0x40) [Capability A] ↓ (next_ptr) [Capability B] ↓ [Capability C] ↓ NULL
起点:Header Type 寄存器(偏移 0x0E)+ Status 寄存器(偏移 0x06,bit 4 = “Capabilities List” 置 1)+ Capabilities Pointer(偏移 0x34,8 位指针,指向第一个 Capability 的起始偏移,通常 dword 对齐)。
查看26道真题和解析