2. PCI配置空间:配置空间的布局、配置空间寄存器的功能、配置空间访问机制
PCI配置空间是PCI总线体系结构的核心概念,它是每个PCI功能(Function)都拥有的一个独立地址空间,用于存放设备类型、资源需求、状态和控制信息。操作系统和驱动程序通过读写配置空间来枚举总线、分配资源、使能总线主控以及配置DMA传输。本章将深入剖析配置空间的布局、关键寄存器的功能以及主机CPU如何访问这些寄存器。
2.1 配置空间的布局结构
根据PCI规范,每个PCI功能拥有 256字节 的配置空间。前64字节(偏移0x00–0x3F)是预定义头区(Header Region),其布局由设备类型决定;后192字节(偏移0x40–0xFF)是设备特定区(Device-Specific Region),由厂商自定义。预定义头区又分为两种类型:Type 0(用于非桥设备)和 Type 1(用于PCI-PCI桥)。
2.1.1 Type 0 配置空间布局(非桥设备)
下表列出了Type 0配置空间前64字节的布局,这是驱动开发中最常接触的部分:
| 偏移(字节) | 寄存器名称 | 宽度 | 功能描述 |
|---|---|---|---|
| 0x00 | Vendor ID | 16位 | 厂商识别码(如Intel为0x8086) |
| 0x02 | Device ID | 16位 | 设备识别码 |
| 0x04 | Command | 16位 | 控制设备响应(I/O、内存、总线主控使能等) |
| 0x06 | Status | 16位 | 设备状态信息(中断状态、能力列表等) |
| 0x08 | Revision ID | 8位 | 修订版本号 |
| 0x09 | Class Code | 24位 | 设备分类(基类、子类、编程接口) |
| 0x0C | Cache Line Size | 8位 | 缓存行大小(单位:DWORD) |
| 0x0D | Latency Timer | 8位 | PCI总线延迟定时器 |
| 0x0E | Header Type | 8位 | 头类型(0x00=Type 0,0x01=Type 1) |
| 0x0F | BIST | 8位 | 内建自测试控制/状态 |
| 0x10–0x27 | Base Address Registers (BAR0–BAR5) | 32位×6 | 设备地址空间基址寄存器(内存/I/O) |
| 0x28 | Cardbus CIS Pointer | 32位 | Cardbus CIS指针(可选) |
| 0x2C | Subsystem Vendor ID | 16位 | 子系统厂商识别码 |
| 0x2E | Subsystem ID | 16位 | 子系统识别码 |
| 0x30 | Expansion ROM Base Address | 32位 | 扩展ROM基址寄存器 |
| 0x34 | Capabilities Pointer | 8位 | 能力链表指针(若Status寄存器Cap位=1) |
| 0x35–0x3B | Reserved | — | 保留 |
| 0x3C | Interrupt Line | 8位 | 中断线路由(由固件/OS分配) |
| 0x3D | Interrupt Pin | 8位 | 中断引脚(INTA#=1, INTB#=2, INTC#=3, INTD#=4) |
| 0x3E | Min_Gnt | 8位 | 设备期望的突发周期长度(单位:1/4微秒) |
| 0x3F | Max_Lat | 8位 | 设备期望的最大延迟(单位:1/4微秒) |
struct pci_dev 结构体封装了上述寄存器的读取结果。驱动通常使用 pci_read_config_byte/word/dword() 系列函数访问配置空间,而不是直接操作I/O端口。
2.1.2 配置空间布局SVG结构图
下图展示了Type 0配置空间前64字节的布局,以及后192字节的设备特定区:
2.2 关键配置空间寄存器功能详解
在DMA传输和总线主控使能的上下文中,以下几个寄存器至关重要:
2.2.1 Command寄存器(偏移0x04)
Command寄存器是控制PCI设备行为的核心寄存器。其各位定义如下:
| 位 | 名称 | 功能描述 |
|---|---|---|
| 0 | I/O Space Enable | 置1使能设备响应I/O空间访问 |
| 1 | Memory Space Enable | 置1使能设备响应内存空间访问 |
| 2 | Bus Master Enable | 置1使能设备作为总线主控发起DMA传输 |
| 3 | Special Cycles Enable | 置1使能设备监控特殊周期 |
| 4 | Memory Write and Invalidate Enable | 置1使能内存写并无效化缓存行 |
| 5 | VGA Palette Snoop Enable | VGA调色板侦听使能 |
| 6 | Parity Error Response Enable | 置1使能奇偶校验错误响应 |
| 7 | Wait Cycle Enable | 地址/数据步进等待周期使能(已废弃) |
| 8 | SERR# Enable | 系统错误驱动使能 |
| 9 | Fast Back-to-Back Enable | 快速背靠背传输使能 |
| 10 | Interrupt Disable | 置1禁止设备断言INTx#中断 |
pci_set_master() 函数正是通过设置此位来使能总线主控能力。
2.2.2 Base Address Registers (BARs)
BAR寄存器用于声明设备需要的地址空间大小和类型。每个BAR的bit 0指示空间类型:
- bit 0 = 0: 映射到内存空间(Memory Space)
- bit 0 = 1: 映射到I/O空间(I/O Space)
对于内存空间BAR,bit 1–2编码地址类型:
- 00: 32位地址,可位于任何32位地址
- 01: 保留
- 10: 64位地址(占用连续两个BAR槽位)
BAR的大小检测机制是PCI枚举的关键步骤:软件先向BAR写入全1(0xFFFFFFFF),然后读回,根据返回的低位0的个数确定地址空间大小。
2.2.3 Status寄存器(偏移0x06)
Status寄存器反映设备状态,驱动开发中常用以下位:
- bit 4 – Capabilities List: 置1表示设备实现了能力链表,Capabilities Pointer寄存器有效
- bit 5 – 66 MHz Capable: 设备是否支持66 MHz PCI时钟
- bit 7 – Fast Back-to-Back Capable: 设备是否支持快速背靠背传输
- bit 8 – Master Data Parity Error: 主设备数据奇偶校验错误
- bit 11 – Signaled Target Abort: 设备作为目标时发起了目标终止
- bit 12 – Received Target Abort: 设备作为主控时收到了目标终止
- bit 13 – Received Master Abort: 设备作为主控时收到了主控终止
- bit 14 – Signaled System Error: 设备断言了SERR#
- bit 15 – Detected Parity Error: 设备检测到奇偶校验错误
2.3 配置空间访问机制
CPU访问PCI配置空间有两种主要机制:传统配置访问机制(CF8/CFC) 和 内存映射配置访问(MMCONF,PCIe专用)。
2.3.1 传统配置访问机制(I/O端口方法)
在x86平台上,通过两个I/O端口访问配置空间:
- 0xCF8 – 配置地址端口(CONFIG_ADDRESS): 32位寄存器,用于指定目标设备
- 0xCFC – 配置数据端口(CONFIG_DATA): 32位寄存器,用于读写配置数据
配置地址端口的格式如下:
| 位 | 功能 |
|---|---|
| 31 | Enable位(必须置1才能访问配置空间) |
| 30–24 | 保留(必须为0) |
| 23–16 | 总线号(Bus Number) |
| 15–11 | 设备号(Device Number) |
| 10–8 | 功能号(Function Number) |
| 7–2 | 寄存器偏移(Register Offset,按DWORD对齐) |
| 1–0 | 必须为0(DWORD对齐) |
访问流程如下:
- 构造配置地址:
addr = (1 << 31) | (bus << 16) | (device << 11) | (function << 8) | (offset & 0xFC) - 将地址写入端口
0xCF8 - 从端口
0xCFC读取或写入数据
pci_read_config_dword() 最终调用 pci_conf1_read(),该函数通过 outl() 和 inl() 操作CF8/CFC端口。对于PCIe设备,内核优先使用MMCONF机制。
2.3.2 内存映射配置访问(MMCONF)
PCIe规范引入了内存映射配置访问机制,将配置空间映射到一段物理内存地址区域。每个PCIe功能映射4KB配置空间(包括256字节传统配置空间和扩展配置空间)。
MMCONF基地址由BIOS在系统启动时通过MCFG(Memory Mapped Configuration)ACPI表报告。访问公式为:
phys_addr = mcfg_base + (bus << 20) | (device << 15) | (function << 12) | offset
其中 mcfg_base 是MMCONF区域的物理基地址,每个总线占用1MB空间,每个设备占用32KB,每个功能占用4KB。
2.3.3 配置访问机制对比
| 特性 | 传统CF8/CFC | MMCONF(PCIe) |
|---|---|---|
| 访问方式 | I/O端口(0xCF8/0xCFC) | 内存映射读写 |
| 配置空间大小 | 256字节(传统) | 4KB(含扩展配置空间) |
| 访问延迟 | 较高(I/O端口序列化) | 较低(直接内存访问) |
| 适用总线 | PCI、PCI-X | PCIe(向后兼容PCI) |
| 多线程安全 | 需要自旋锁保护CF8/CFC | 每个功能独立地址,天然安全 |
2.3.4 配置访问机制SVG流程图
下图展示了操作系统枚举PCI设备时,通过两种机制访问配置空间的流程:
2.4 配置空间访问的代码示例
以下是在Linux内核中通过传统CF8/CFC机制读取配置空间的简化实现:
/* 传统PCI配置空间读取(x86 CF8/CFC机制) */
static u32 pci_conf1_read(struct pci_bus *bus, unsigned int devfn, int where, int size)
{
u32 addr;
u32 data = 0xFFFFFFFF;
/* 构造配置地址寄存器值 */
addr = (bus->number << 16) | (devfn << 8) | (where & 0xFC) | (1U << 31);
/* 写入配置地址端口 */
outl(addr, 0xCF8);
/* 从配置数据端口读取 */
switch (size) {
case 1:
data = inb(0xCFC + (where & 3));
break;
case 2:
data = inw(0xCFC + (where & 2));
break;
case 4:
data = inl(0xCFC);
break;
}
return data;
}
/* 使用示例:读取设备的总线主控使能状态 */
int pci_is_bus_master(struct pci_dev *dev)
{
u16 cmd;
pci_read_config_word(dev, PCI_COMMAND, &cmd);
return cmd & PCI_COMMAND_MASTER;
}
pci_read_config_* 和 pci_write_config_* 系列函数,而不是直接操作I/O端口。这些函数已经处理了总线锁定、字节序转换和MMCONF/CF8自动选择等细节。
2.5 配置空间与DMA传输的关系
配置空间中的以下寄存器直接或间接影响DMA传输:
- Command寄存器 bit 2 (Bus Master Enable): DMA传输的“总开关”,必须置1
- BAR寄存器: 定义了DMA引擎访问内存的地址映射(如DMA描述符环的基地址)
- Latency Timer: 控制设备在总线上保持主控权的最大时间,影响DMA突发长度
- Cache Line Size: 影响内存写并无效化操作,与DMA缓存一致性相关
- Capabilities Pointer: 指向MSI/MSI-X能力结构,用于DMA完成中断
pci_set_master() 如何通过设置Command寄存器的Bus Master Enable位来激活DMA引擎。