2. PCIe配置空间基础

PCIe 配置空间是软件与硬件交互的核心接口,驱动通过它发现设备功能、分配资源、配置中断。本章深入解析配置空间的布局结构、Capability 链表遍历机制以及中断相关寄存器的位定义,为后续 MSI-X 配置打下坚实基础。

2.1 配置空间布局概述

PCIe 兼容传统 PCI 配置空间规范,同时扩展了 4 KB 的增强配置空间。每个 PCIe 功能(Function)独立拥有 4 KB 配置空间,前 256 字节(0x00–0xFF)为 PCI 兼容区域,后 3840 字节(0x100–0xFFF)为 PCIe 扩展 Capability 空间。

关键概念: 配置空间通过 CONFIG_ADDR(0xCF8)和 CONFIG_DATA(0xCFC)I/O 端口访问(x86 传统方式),或通过内存映射配置访问(MMCFG,ECAM 机制)。在 Linux 内核中,pci_read_config_* / pci_write_config_* 系列函数封装了底层访问细节。
PCIe 配置空间布局(4 KB) PCI 兼容配置空间(0x00 – 0xFF) Vendor ID / Device ID / Command / Status / Revision ID / Class Code Header Type / BIST / Base Address Registers (BARs) / CardBus CIS Pointer Subsystem Vendor ID / Subsystem ID / Expansion ROM Base Address Capabilities Pointer (偏移 0x34) / Interrupt Line (偏移 0x3C) / Interrupt Pin (偏移 0x3D) PCIe 扩展 Capability 空间(0x100 – 0xFFF) PCI Express Capability (0x100) — 设备类型、链路能力、设备能力 MSI Capability (可选) — Message Signaled Interrupt 基础配置 MSI-X Capability (可选) — MSI-X 使能、表大小、Bir/Offset Power Management Capability / Advanced Error Reporting / AER Virtual Channel / Device Serial Number / SR-IOV / etc. 每个 Capability 结构以 Next Capability Offset 指针串联成链表 0x00 0xFF 0x100 0xFFF

2.2 Capability 结构链表与遍历

PCI/PCIe 使用 Capability 链表机制扩展功能。每个 Capability 结构以 Capability ID(1 字节)和 Next Capability Pointer(1 字节)开头,通过偏移 0x34 处的 Capabilities Pointer 寄存器找到链表头。

偏移 寄存器名称 位域 描述
0x34 Capabilities Pointer [7:0] 指向第一个 Capability 结构的字节偏移(仅当 Status 寄存器的 Capabilities List 位为 1 时有效)
Cap + 0x00 Capability ID [7:0] 标识 Capability 类型(0x05 = MSI, 0x11 = MSI-X, 0x10 = PCIe)
Cap + 0x01 Next Capability Pointer [7:0] 指向下一个 Capability 结构的偏移;0x00 表示链表结束
驱动遍历技巧: Linux 内核提供 pci_find_capability() 和 pci_find_ext_capability() 函数自动遍历链表。手动遍历时,从偏移 0x34 读取 Capabilities Pointer,然后循环读取 Capability ID 和 Next Pointer,直到 Next Pointer 为 0x00。
Capability 链表遍历流程 偏移 0x34 Capabilities Pointer Capability 1: PCIe (ID=0x10) Next Pointer → 0xE0 Capability 2: MSI (ID=0x05) Next Pointer → 0x100 Capability 3: MSI-X (ID=0x11) → Next = 0x00 (结束)

2.3 中断相关寄存器详解

配置空间中与中断直接相关的寄存器包括传统 INTx 中断寄存器、MSI Capability 寄存器以及 MSI-X Capability 寄存器。本节重点解析前两者,MSI-X 将在后续章节深入展开。

2.3.1 传统 INTx 中断寄存器(偏移 0x3C–0x3D)

偏移 寄存器 位宽 描述
0x3C Interrupt Line 8 位 系统分配的中断向量号(x86 上为 IRQ 号);驱动读取以注册中断处理函数
0x3D Interrupt Pin 8 位 设备使用的 INTx 引脚:0=无中断,1=INTA,2=INTB,3=INTC,4=INTD
注意: 对于支持 MSI/MSI-X 的设备,Interrupt Line 寄存器可能不会被系统固件正确填充。驱动应优先尝试配置 MSI/MSI-X,仅在回退时使用 INTx。读取 Interrupt Pin 为 0 表示设备不使用传统中断引脚。

2.3.2 MSI Capability 结构(Capability ID = 0x05)

MSI Capability 结构位于 PCI 兼容配置空间(通过 Capabilities Pointer 找到),其寄存器布局如下:

偏移(相对 Cap) 寄存器 位宽 关键位域
0x00 MSI Capability ID / Next Pointer 16 位 [7:0] Cap ID = 0x05; [15:8] Next Pointer
0x02 Message Control (MC) 16 位 [0] MSI Enable; [3:1] Multiple Message Capable; [6:4] Multiple Message Enable; [7] 64-bit Address Capable; [8] Per-vector Masking Capable
0x04 Message Address (低 32 位) 32 位 MSI 内存写目标地址(低 32 位)
0x08 Message Address (高 32 位) / Message Data 32 位 若 MC[7]=1,则为高 32 位地址;否则为 Message Data
0x0C Message Data (64 位模式) 16 位 MSI 数据负载,包含中断向量号等信息
0x0E Mask Bits / Pending Bits(可选) 32 位 Per-vector Masking 时使用,每位对应一个向量
驱动配置要点: 配置 MSI 时,驱动需先读取 Message Control 寄存器,检查 Multiple Message Capable 字段以确定设备支持的向量数量(编码为 0=1 向量,1=2 向量,2=4 向量,3=8 向量,4=16 向量,5=32 向量)。然后设置 Multiple Message Enable 字段(不能超过 Capable 值),写入 Message Address 和 Message Data,最后置位 MSI Enable 位。

2.3.3 MSI-X Capability 结构(Capability ID = 0x11)

MSI-X 是 PCIe 推荐的现代中断机制,支持最多 2048 个独立中断向量,每个向量拥有独立的地址和数据。其 Capability 结构位于 PCI 兼容配置空间:

偏移(相对 Cap) 寄存器 位宽 关键位域
0x00 MSI-X Capability ID / Next Pointer 16 位 [7:0] Cap ID = 0x11; [15:8] Next Pointer
0x02 Message Control (MC) 16 位 [0] MSI-X Enable; [10:2] Table Size (N-1 编码,N 为向量总数); [14] Function Mask; [15] 保留
0x04 Table Offset / BIR 32 位 [2:0] BAR Indicator Register (BIR); [31:3] Table Offset(相对于 BAR 的偏移)
0x08 Pending Bit Array Offset / BIR 32 位 [2:0] BIR; [31:3] PBA Offset(相对于 BAR 的偏移)
MSI-X 表结构: MSI-X Table 位于设备 BAR 空间,每个表项 16 字节:
偏移 +0x00: Message Address (64 位)
偏移 +0x08: Message Data (32 位)
偏移 +0x0C: Vector Control (32 位,[0] = Mask 位)
驱动通过 pci_msix_vec_count() 获取向量总数,通过 pci_enable_msix_range() 申请并配置向量。
MSI-X Capability 结构与 MSI-X Table 关系 MSI-X Capability (配置空间) Cap ID (0x11) | Next Ptr Message Control: [0] Enable, [10:2] Table Size Table Offset / BIR: [2:0] BIR, [31:3] Offset PBA Offset / BIR: [2:0] BIR, [31:3] Offset BIR 指向 BAR 编号 (0-5) Offset 是相对于 BAR 基址的字节偏移 MSI-X Table (BAR 空间) Entry 0: Address (64b) | Data (32b) | Vector Control Entry 1: Address (64b) | Data (32b) | Vector Control Entry 2: Address (64b) | Data (32b) | Vector Control ... Entry N-1: Address (64b) | Data (32b) | Vector Control 每个 Entry 16 字节,N = Table Size + 1 Pending Bit Array (PBA) 每位对应一个 MSI-X 向量,表示是否有挂起中断

2.4 配置空间访问示例代码

以下代码展示如何在 Linux 内核驱动中遍历 Capability 链表并读取 MSI-X 相关寄存器:

#include <linux/pci.h>

static int probe_msix_capability(struct pci_dev *pdev)
{
    int pos;
    u16 msgctl;
    u32 table_offset;
    u32 pba_offset;
    int bir_table, bir_pba;
    int table_size;

    // 查找 MSI-X Capability (ID = 0x11)
    pos = pci_find_capability(pdev, PCI_CAP_ID_MSIX);
    if (!pos) {
        dev_err(&pdev->dev, "MSI-X capability not found\n");
        return -ENODEV;
    }

    // 读取 Message Control 寄存器
    pci_read_config_word(pdev, pos + PCI_MSIX_FLAGS, &msgctl);
    table_size = (msgctl & PCI_MSIX_FLAGS_QSIZE) + 1;
    dev_info(&pdev->dev, "MSI-X table size: %d entries\n", table_size);

    // 读取 Table Offset / BIR
    pci_read_config_dword(pdev, pos + PCI_MSIX_TABLE, &table_offset);
    bir_table = table_offset & PCI_MSIX_BIR;
    table_offset &= ~PCI_MSIX_BIR;

    // 读取 PBA Offset / BIR
    pci_read_config_dword(pdev, pos + PCI_MSIX_PBA, &pba_offset);
    bir_pba = pba_offset & PCI_MSIX_BIR;
    pba_offset &= ~PCI_MSIX_BIR;

    dev_info(&pdev->dev, "MSI-X table: BAR%d + 0x%x\n", bir_table, table_offset);
    dev_info(&pdev->dev, "MSI-X PBA:   BAR%d + 0x%x\n", bir_pba, pba_offset);

    // 后续可通过 pci_enable_msix_range() 配置向量
    return 0;
}
关键 API 总结:
pci_find_capability(pdev, PCI_CAP_ID_MSIX) — 查找 MSI-X Capability 偏移
pci_read_config_word/pci_read_config_dword — 读取配置空间寄存器
pci_enable_msix_range() — 使能 MSI-X 并分配指定范围的向量
pci_disable_msix() — 禁用 MSI-X 并释放资源

2.5 本章小结

  • PCIe 配置空间分为 256 字节 PCI 兼容区域和 3840 字节 PCIe 扩展 Capability 空间。
  • Capability 结构通过链表形式组织,由偏移 0x34 的 Capabilities Pointer 指向链表头。
  • 传统 INTx 中断使用 Interrupt Line (0x3C) 和 Interrupt Pin (0x3D) 寄存器。
  • MSI Capability 支持最多 32 个向量,通过 Message Address 和 Message Data 触发中断。
  • MSI-X Capability 支持最多 2048 个独立向量,每个向量拥有独立的地址/数据寄存器,存储在 BAR 空间的 MSI-X Table 中。
  • 驱动应优先使用 MSI-X,其次 MSI,最后回退到 INTx。