Linux PCI/PCIe子系统

PCI/PCIe子系统

PCI/PCIe子系统

1. 基础概念与拓扑结构

1.1 基本概念

  • PCI (Peripheral Component Interconnect):连接计算机主板与外设的并行总线标准
  • PCIe (PCI Express):PCI的串行演进,带宽更高、拓扑更灵活
  • 配置空间(Configuration Space):每个PCI设备都有256B基础配置空间,用于存储设备信息与控制寄存器
  • 桥(Bridge):连接不同总线域/segment的设备,负责地址域转换与请求/包转发

1.2 PCIe拓扑结构

PCIe系统结构按以下角色组织(树状层次结构):

  • Root Complex(RC):CPU与PCIe拓扑之间的接口
  • PCIe Bus / Link:数据传输通道
  • Endpoint(端点设备):拓扑末端的设备
  • Port / Bridge:连接不同总线域的设备
  • Switch(交换/扩展):提供多个下游端口的设备

1.3 设备类型

  • Root Complex(RC):位于倒立树拓扑的"根",通过Host Bridge向系统暴露PCI bus 0
  • Root Port:RC内部的端口,在PCI配置空间中表现为PCI-PCI bridge
  • Bridge:提供与其他总线(PCI/PCI-X,甚至另一段PCIe)互联的设备
  • Switch:PCIe-to-PCIe的桥/交换结构,提供扩展能力
  • Endpoint :拓扑末端设备,可作为请求发起者或完成者
    • Legacy PCIe Endpoint:兼容PCI的端点
    • Native PCIe Endpoint:标准PCIe端点,通常以MMIO为主进行访问
  • RCiEP(Root Complex Integrated Endpoint):直接挂到Root Complex上的集成设备

2. 设备表示与寻址

2.1 BDF(Bus:Device.Function)

系统启动/枚举后,每个PCI设备会被分配唯一地址BDF:

  • Bus Number:8 bits(最多256条总线)
  • Device Number:5 bits(每条总线最多32个设备)
  • Function Number:3 bits(每个设备最多8个功能)

一般写作BB:DD.F。在Linux上可用lspci查看拓扑与BDF:

shell 复制代码
~$ lspci -t -v
 # [Domain:Bus]
-[0000:00]-+-00.0  Intel Corporation 8th/9th Gen Core 8-core Desktop Processor Host Bridge/DRAM Registers [Coffee Lake S]
           +-02.0  Intel Corporation CoffeeLake-S GT2 [UHD Graphics 630]
           +-12.0  Intel Corporation Cannon Lake PCH Thermal Controller
           +-14.0  Intel Corporation Cannon Lake PCH USB 3.1 xHCI Host Controller
           +-14.2  Intel Corporation Cannon Lake PCH Shared SRAM
           +-15.0  Intel Corporation Cannon Lake PCH Serial IO I2C Controller #0
           +-16.0  Intel Corporation Cannon Lake PCH HECI Controller
           +-17.0  Intel Corporation SATA Controller [RAID mode]
           +-1c.0-[01-02]----00.0-[02]--
           +-1f.0  Intel Corporation Q370 Chipset LPC/eSPI Controller
           +-1f.3  Intel Corporation Cannon Lake PCH cAVS
           +-1f.4  Intel Corporation Cannon Lake PCH SMBus Controller
           +-1f.5  Intel Corporation Cannon Lake PCH SPI Controller
           \-1f.6  Intel Corporation Ethernet Connection (7) I219-LM

2.2 Function(多功能设备)

Function表示同一个PCI Device内部的不同逻辑功能单元。一个物理设备可包含多个独立功能模块,每个模块在系统看来像一个独立设备。

例如同一个1f号设备(Intel PCH)提供多个不同功能:

arduino 复制代码
           +-1f.0  Intel Corporation Q370 Chipset LPC/eSPI Controller
           +-1f.3  Intel Corporation Cannon Lake PCH cAVS
           +-1f.4  Intel Corporation Cannon Lake PCH SMBus Controller
           +-1f.5  Intel Corporation Cannon Lake PCH SPI Controller
           \-1f.6  Intel Corporation Ethernet Connection (7) I219-LM

2.3 Type 0 / Type 1(设备头类型)

从枚举与配置空间视角,常用两类"头"来区分角色:

  • Type 0:最终端设备(如显卡、声卡、网卡等)
  • Type 1:桥/端口类设备(如Root Port、PCIe Switch端口等)

3. 配置空间与访问机制

3.1 配置访问:Type 0 与 Type 1

  • Type 0 配置访问:访问同一条总线上的端点设备(直连设备)
  • Type 1 配置访问:访问桥设备或通过桥访问其他总线上的设备(跨桥/跨总线)

配置访问里常用的定位字段:

  • Bus:总线号
  • Device:设备号
  • Function:功能号
  • Register:寄存器偏移

3.2 ECAM(Enhanced Configuration Access Mechanism)

ECAM是现代平台常用的PCI配置空间访问机制:通过内存映射把"某个BDF + offset"的配置空间映射到CPU可访问的地址空间。

c 复制代码
void __iomem *pci_ecam_map_bus(struct pci_bus *bus, unsigned int devfn, int where)
{
    struct pci_config_window *cfg = bus->sysdata;
    void __iomem *base;

    // 计算配置空间地址:
    // bus号、devfn(设备+功能)、偏移地址 -> 映射到内存空间
}

4. 地址转换与包转发机制

4.1 地址转换原理

PCI/PCIe系统核心能力之一是地址转换:将CPU的系统地址转换为PCI设备可识别的地址,从而访问设备寄存器与MMIO空间。

关键点

  • 上电初始化:BIOS/固件/引导阶段会把地址映射信息写入桥设备的配置寄存器(资源窗口)
  • 桥设备:作为中间层,负责地址域转换与请求转发
  • 主机控制器驱动 :相关驱动常归在pci_host/host controller一类,用于管理PCI主机控制器与配置空间访问

4.2 包转发与转换

  • Type 0:用于访问直接连接的设备
  • Type 1:用于访问非直接连接的设备,在桥之间逐级转发
  • Type 1 到达目标总线对应的桥时,会在该桥处转换为Type 0,再由目标总线上的设备响应

端到端流程

  1. CPU发起访问(地址/配置访问)
  2. 主机桥/端口进行地址/路由判定
  3. 生成Type 0或Type 1
  4. Type 1在桥之间转发
  5. 到达目标bus的桥后Type 1 → Type 0
  6. 目标设备处理请求并完成返回

4.3 地址范围请求与分配

  • 桥设备会根据直接连接的下级设备需求,向上级总线控制器请求/索要地址范围(I/O、MMIO等资源窗口)
  • 这保证地址空间合理分配并避免冲突

分配流程

  1. 初始化:BIOS/引导程序提供基础资源规划
  2. 枚举:PCI子系统扫描总线设备
  3. 请求:桥根据下级需求向上级请求窗口
  4. 分配:上级分配合适范围
  5. 配置:写入桥设备配置寄存器,建立映射表

4.4 PCIe的"端口选通"

  • PCI(并行):可从"地址线选通"角度理解桥后设备选择
  • PCIe(点对点):每个端口后通常只连一个设备;拓扑上更像在"选通端口并沿树路由",而不是共享总线上的广播选通

5. PCIe Switch结构

当需要连接不止一个下游设备时,使用PCIe Switch。其内部可抽象为:

  • 一个Upstream Port + Bridge:连接上游(Root Port或上游Switch的Downstream Port)
  • 一组Downstream Port + Bridge:连接下游端点或下游Switch
  • 内部互联(虚拟总线/交换结构):把上/下游端口连接起来,使上游可访问下游设备

6. Linux PCI驱动架构

6.1 PCI核心功能

PCI核心驱动位于drivers/pci/pci.c,提供核心能力:

  • 设备枚举与初始化:扫描总线上的设备并初始化
  • 配置空间访问:读写设备配置寄存器
  • 资源管理:分配和管理设备资源
  • 电源管理:控制设备电源状态
  • 中断管理:管理设备中断

6.2 主机控制器驱动

通用PCI主机控制器驱动示例:

c 复制代码
static int gen_pci_probe(struct platform_device *pdev)
{
    const struct of_device_id *of_id;
    struct pci_ecam_ops *ops;

    of_id = of_match_node(gen_pci_of_match, pdev->dev.of_node);
    ops = (struct pci_ecam_ops *)of_id->data;

    return pci_host_common_probe(pdev, ops);
}

配置空间访问操作示例:

c 复制代码
static struct pci_ecam_ops gen_pci_cfg_cam_bus_ops = {
    .bus_shift    = 16,
    .pci_ops    = {
        .map_bus    = pci_ecam_map_bus,
        .read        = pci_generic_config_read,
        .write        = pci_generic_config_write,
    }
};

7. 核心代码分析

7.1 设备启用流程

c 复制代码
int pci_enable_device(struct pci_dev *dev)
{
    return pci_enable_device_flags(dev, IORESOURCE_MEM | IORESOURCE_IO);
}

static int do_pci_enable_device(struct pci_dev *dev, int bars)
{
    int err;
    struct pci_dev *bridge;
    u16 cmd;
    u8 pin;

    err = pci_set_power_state(dev, PCI_D0);
    if (err < 0 && err != -EIO)
        return err;

    bridge = pci_upstream_bridge(dev);
    if (bridge)
        pcie_aspm_powersave_config_link(bridge);

    err = pcibios_enable_device(dev, bars);
    if (err < 0)
        return err;
    pci_fixup_device(pci_fixup_enable, dev);

    // 启用中断
    if (dev->msi_enabled || dev->msix_enabled)
        return 0;

    pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
    if (pin) {
        pci_read_config_word(dev, PCI_COMMAND, &cmd);
        if (cmd & PCI_COMMAND_INTX_DISABLE)
            pci_write_config_word(dev, PCI_COMMAND,
                                  cmd & ~PCI_COMMAND_INTX_DISABLE);
    }

    return 0;
}

7.2 设备枚举

c 复制代码
struct pci_dev *pci_scan_single_device(struct pci_bus *bus, int devfn)
{
    struct pci_dev *dev;
    u32 l;

    if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l) != PCIBIOS_SUCCESSFUL)
        return NULL;

    if (l == 0xffffffff)
        return NULL;

    dev = pci_scan_device(bus, devfn);
    if (!dev)
        return NULL;

    return dev;
}

7.3 电源管理

c 复制代码
int pci_set_power_state(struct pci_dev *dev, pci_power_t state)
{
    int error;

    /* 状态验证 */
    if (state > PCI_D3cold)
        state = PCI_D3cold;
    else if (state < PCI_D0)
        state = PCI_D0;
    else if ((state == PCI_D1 || state == PCI_D2) && pci_no_d1d2(dev))
        return 0;

    /* 检查当前状态 */
    if (dev->current_state == state)
        return 0;

    __pci_start_power_transition(dev, state);

    /* 处理特殊情况 */
    if (state >= PCI_D3hot && (dev->dev_flags & PCI_DEV_FLAGS_NO_D3))
        return 0;

    /* 设置电源状态 */
    error = pci_raw_set_power_state(dev, state > PCI_D3hot ?
                                  PCI_D3hot : state);

    if (!__pci_complete_power_transition(dev, state))
        error = 0;

    return error;
}

7.4 配置空间访问

c 复制代码
int pci_find_capability(struct pci_dev *dev, int cap)
{
    int pos;

    pos = __pci_bus_find_cap_start(dev->bus, dev->devfn, dev->hdr_type);
    if (pos)
        pos = __pci_find_next_cap(dev->bus, dev->devfn, pos, cap);

    return pos;
}

8. PCIe特有机制

8.1 端点设备特性

  • 端口单一设备:每个PCIe端口后只连接一个设备
  • 端口地址:PCIe桥内部端口有独立地址
  • 端口选通:选通的实际上是端口,而非设备本身

8.2 串行传输优势

  • 串行差分信号:提供更高的带宽
  • 多通道支持:x1, x2, x4, x8, x16等
  • 点对点连接:每个设备直接连接到上游端口

9. 整体架构总结

markdown 复制代码
CPU → 根复合体 (Root Complex) → PCIe交换机 → 端点设备
      ↑
    主桥
      ↑
    PCI总线
      ↑
    PCI桥
      ↑
    次级PCI总线

10. 技术要点

  • Type 0/Type 1包:区分直接访问和跨桥访问;Type 1在桥间转发,到目标桥处转为Type 0
  • ECAM:现代配置空间访问机制(内存映射方式)
  • 地址/资源窗口:上电初始化与枚举过程中,通过桥设备配置寄存器建立映射与窗口
  • 总线枚举:自动发现设备、分配bus number、设置桥窗口与设备BAR等资源
  • BDF寻址:Bus:Device.Function的三层寻址方式
  • 多功能设备:一个物理设备可包含多个逻辑功能

11. 学习建议

11.1 代码阅读顺序

  1. 先了解基本概念:PCI架构、配置空间、BDF编号
  2. 查看核心数据结构struct pci_devstruct pci_bus
  3. 分析核心函数:设备枚举、配置访问、资源管理
  4. 研究主机控制器drivers/pci/host/目录下的代码
  5. 跟踪地址转换:分析Type 0/Type 1包的处理流程

11.2 重点关注

  • 配置空间访问pci_read_config_*pci_write_config_*函数
  • 设备枚举pci_scan_bus系列函数
  • 资源分配pci_enable_resources函数
  • 桥设备管理:地址范围请求和分配机制
  • PCIe特有功能:AER、ASPM等高级特性

12. 总结

PCI/PCIe子系统通过层次化拓扑(RC/Port/Switch/Endpoint)配置空间地址转换与资源窗口 、以及Type 0/Type 1转发/转换机制,为CPU与外设之间提供高效可靠的通信路径。

理解这些机制有助于驱动开发、系统优化与故障排查;结合Linux内核drivers/pci/drivers/pci/host/的实现,可以把"规范里的抽象"与"实际平台行为"对应起来。

PCI总线架构的设计思想和实现机制,为现代计算机系统的外设连接提供了灵活、高效的解决方案,是计算机体系结构中的重要组成部分。

相关推荐
虚拟世界AI2 小时前
Linux运维实战:从部署到高可用全指南
linux·运维
闫记康2 小时前
scp工具
linux·运维·服务器·学习·ssh·github
虾..2 小时前
Linux 套接字编程---基于UDP协议实现简易的聊天室
linux·运维·udp
我爱学习好爱好爱2 小时前
ELK日志分析平台(三):Logstash 7.17.10 独立节点部署与基础测试(基于Rocky Linux 9.6)
linux·python·elk
默|笙2 小时前
【Linux】库制作与原理(2)_ELF格式
linux·运维·服务器
青桔柠薯片2 小时前
Linux I/O多路复用:深入浅出poll与epoll
linux·运维·服务器·算法
雾岛听蓝2 小时前
Linux文件系统:从硬件到软硬链接
linux·经验分享·笔记
HalvmånEver2 小时前
Linux:初始网络(上)
linux·网络·学习·通信
REDcker2 小时前
Linux C++ 内存泄漏排查分析手册
java·linux·c++