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桥)。

关键概念: 配置空间是PCI设备与软件交互的“寄存器窗口”,驱动程序通过它获知设备能力、设置地址、控制中断和DMA。

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微秒)
驱动开发提示: 在Linux内核中,struct pci_dev 结构体封装了上述寄存器的读取结果。驱动通常使用 pci_read_config_byte/word/dword() 系列函数访问配置空间,而不是直接操作I/O端口。

2.1.2 配置空间布局SVG结构图

下图展示了Type 0配置空间前64字节的布局,以及后192字节的设备特定区:

PCI Type 0 配置空间布局(256字节) 预定义头区(64字节) Vendor ID + Device ID Command + Status Revision + Class Code Cache Line + Latency Header + BIST Base Address Registers (BAR0 – BAR5) [0x10 – 0x27] 每个BAR 32位,共6个,用于声明设备需要的内存/I/O地址空间 bit[0] = 0 表示内存空间,bit[0] = 1 表示I/O空间 Cardbus CIS (0x28) Subsystem VID/DID Expansion ROM (0x30) Capabilities Ptr (0x34) Int Line + Int Pin Min_Gnt + Max_Lat 设备特定区(192字节)[0x40 – 0xFF] • 能力结构(Capability Structures):如PCIe能力、MSI/MSI-X、电源管理 • 扩展配置空间(Extended Configuration Space,PCIe支持到4096字节) • 厂商自定义寄存器 • 通过 Capabilities Pointer (0x34) 链接到第一个能力结构 指向能力链表

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#中断
总线主控使能(Bus Master Enable)是DMA传输的前提条件: 如果Command寄存器的bit 2未被置1,设备将无法作为总线主控发起任何内存读写操作,DMA引擎将无法工作。Linux内核的 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对齐)

访问流程如下:

  1. 构造配置地址:addr = (1 << 31) | (bus << 16) | (device << 11) | (function << 8) | (offset & 0xFC)
  2. 将地址写入端口 0xCF8
  3. 从端口 0xCFC 读取或写入数据
Linux内核实现: 在x86上,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设备时,通过两种机制访问配置空间的流程:

PCI配置空间访问机制流程 PCI设备枚举开始 PCIe 设备? 是 MMCONF 访问 内存映射直接读写 否 传统 CF8/CFC I/O端口序列化访问 读取 Vendor ID / Device ID 判断设备是否存在(Vendor ID ≠ 0xFFFF) 读取 BAR → 分配地址空间 设置 Command 寄存器使能位 设备初始化完成

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驱动时,应使用内核提供的 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设备的“身份证明”和“控制面板”。驱动程序通过配置空间了解设备能力、分配资源、使能总线主控,从而为DMA传输铺平道路。在后续章节中,我们将看到 pci_set_master() 如何通过设置Command寄存器的Bus Master Enable位来激活DMA引擎。