Appearance
PCIe 主题索引
PCIe 是现代计算机的外设高速总线。NVMe、GPU、FPGA、网卡——全都挂在 PCIe 上。这个主题按时间线组织:从按下电源键到驱动 probe 成功,逐阶段拆解。
四阶段全景时间线
术语速查
| 缩写/术语 | 全称 | 一句话解释 |
|---|---|---|
| LTSSM | Link Training and Status State Machine | PCIe 物理层内的硬件状态机,自动完成链路协商,软件零参与 |
| L0 | Link Active (L0) | LTSSM 的最终就绪状态,在此状态下可正常收发 TLP |
| TLP | Transaction Layer Packet | PCIe 事务层的数据包,分 Config/Memory/Message/Completion 四种 |
| ECAM | Enhanced Configuration Access Mechanism | 将每个设备的 4KB 配置空间平铺映射到一段连续的 MMIO 地址中 |
| MCFG | Memory-mapped ConFiGuration (ACPI 表) | ACPI 表,记录 ECAM 窗口的物理基址和覆盖的 Bus 范围 |
| RC | Root Complex | CPU 进入 PCIe 世界的门户,负责把 CPU 的 MMIO 访问翻译成 PCIe TLP |
| BDF | Bus : Device . Function | PCIe 设备的三级寻址标识,枚举时以此遍历所有设备 |
| BAR | Base Address Register | 设备配置空间中的基址寄存器,存放分配给该设备的 MMIO/Memory 物理地址区间 |
| VID/DID | Vendor ID / Device ID | 配置空间偏移 0x00 的 32-bit 寄存器,0xFFFF 表示空槽位 |
| MMIO | Memory-Mapped I/O | 把设备寄存器映射到物理地址空间,CPU 用 mov 指令读写,硬件自动翻译成 TLP |
| 固件 | UEFI/BIOS Firmware | 存储在 SPI Flash 中的一段程序,CPU 上电后执行的第一条指令就跳到这里,负责初始化硬件、发现引导设备、准备好 ACPI 表后把控制权交给内核 |
按下电源键后,PCIe 总线依次经历四个阶段,从纯硬件到驱动上线:
为什么链路训练之后是固件? CPU 上电后会从固定的硬件地址(复位向量)取出第一条指令执行——这条指令跳转到 SPI Flash 中存储的固件(UEFI/BIOS)。此时还没有操作系统,内核还躺在硬盘里。固件的核心使命是当好"开机管家"。
- 初始化内存控制器让 DRAM 可用
- 让显卡能输出画面
- 识别 NVMe/硬盘并从中读出内核文件
- 把硬件拓扑记录到 ACPI 表
- 最后跳转到内核入口(
start_kernel)控制权交接完成。 固件对 PCIe 的处理是"够用就好"——只扫描 Bus 0、只初始化引导必需的设备,完整枚举留给内核来做。
PCIe 拓扑结构
按下电源键之前,先看清 PCIe 的硬件拓扑——后面四阶段都在这张图上发生:

阶段① 链路训练(~100ms,纯硬件):pcie-link-training.md
- LTSSM 硬件状态机自动运行:Detect → Polling → Configuration → L0 就绪,协商 Width/Speed
- 训练完成后物理层通了,但 RC 地址解码器未配置,软件仍看不到任何设备
阶段② 固件枚举(~几百ms,UEFI/BIOS):pcie-firmware-enum.md
- 固件配置 RC 地址解码器打通 ECAM 窗口,然后用 Config TLP (ID 路由) 扫描 Bus 0
- 回到拓扑图——4 个 EP 都在 Switch 下游,Bus 0 只能看到 Switch 本身(Type 1 桥)
- 固件"够用就好":只为 NVMe(系统盘)和 GPU(显示)穿透 Switch 分配临时 BAR,FPGA/网卡留给内核
- 构建 ACPI 表,跳转
start_kernel交接
阶段③ 内核建树(~几ms/设备,Linux PCI 子系统):pcie-enumeration.md
pci_scan_root_bus()深度优先递归扫全树,探测 BAR size、分配 MMIO 基址、配置 MSI/MSI-X- 完成后全树 BDF 拓扑可见,BAR 寄存器已填入地址
阶段④ BAR 激活 + 驱动探针(~几ms/驱动):pcie-bar-activation.md
- BAR 写入后设备地址解码器激活,MMIO 读写路径打通
pci_match_id()→probe()→ioremap/request_irq→/dev/节点出现
核心机制层横向贯穿:pcie.md(拓扑/Lane/协议层/MMIO)和 pcie-pre-enumeration.md(RC/BDF/BAR 硬件原理)。
六篇文档的定位
| # | 文档 | 类型 | 核心问题 |
|---|---|---|---|
| — | pcie.md | 物理基础 | PCIe 是什么?拓扑、Lane、三层协议、MMIO/BAR 基础 |
| ① | pcie-link-training.md | 阶段 | 上电后硬件自己做了什么?LTSSM 11个状态、Width/Speed协商、失败排查 |
| — | pcie-pre-enumeration.md | 横向机制 | 树构建前凭什么能通信?RC/BDF/BAR 的硬件原理(跨阶段②③的底层机制) |
| ② | pcie-firmware-enum.md | 阶段 | 固件怎么发现设备?MCFG表/ECAM窗口、Bus 0扫描、第一笔Config TLP、ACPI交接 |
| ③ | pcie-enumeration.md | 阶段 | 内核怎么建完整拓扑?深度优先递归、BAR探测trick、Type 0/1配置空间、ID路由与地址路由 |
| ④ | pcie-bar-activation.md | 阶段 | 枚举完了设备怎么用?BAR激活→地址解码器→MMIO路径→驱动probe()→ioremap |
推荐阅读路径
初学者路径(从头理解 PCIe)
| 顺序 | 文档 | 解决什么问题 |
|---|---|---|
| 0 | pcie.md | PCIe 是什么?树形拓扑、Lane 带宽、三层协议、MMIO/BAR 基础(物理常识) |
| 1 | pcie-link-training.md | 按下电源键后,硬件自动做了什么?链路训练全过程 |
| 2 | pcie-pre-enumeration.md | 链路就绪后,RC/BDF/BAR 这些"零件"怎么工作?(理解底层机制再进入软件阶段) |
| 3 | pcie-firmware-enum.md | 固件怎么"摸黑"发现 Bus 0 上的设备?ECAM 窗口谁设的? |
| 4 | pcie-enumeration.md | 内核接管后怎么把整棵树扫出来?BAR 怎么分配?Switch 怎么路由? |
| 5 | pcie-bar-activation.md | 树建完了,设备怎么真正变得可用?MMIO 路径、驱动 probe |
进阶路径(按角色查阅)
| 角色 | 先看 | 再看 |
|---|---|---|
| 驱动开发者 | pcie-bar-activation.md(probe/ioremap/MMIO路径) | pcie-enumeration.md §四(BAR探测与地址分配) |
| FPGA/ASIC 工程师 | pcie-pre-enumeration.md(BAR硬件设计) | pcie-link-training.md(PHY训练) |
| 系统架构师 | pcie-enumeration.md(路由机制) | pcie-firmware-enum.md(固件交接) |
| 性能调优 | pcie-bar-activation.md §三(MMIO读vs写延迟) | pcie-link-training.md §三(降速降宽排查) |
按场景查阅
| 场景 | 直接看 |
|---|---|
| 我想知道 x16 / Gen3 / Gen4 带宽怎么算 | pcie.md §一 |
lspci 看不到设备,从哪排查 | pcie-link-training.md §五 |
LnkSta: Speed ... (downgraded) 是什么意思 | pcie-link-training.md §三 |
| 链路训练的 LTSSM 有哪些状态、各自干什么 | pcie-link-training.md §二 |
CPU 执行 mov [BAR], val 之后发生了什么 | pcie-bar-activation.md §三 |
| MMIO Write 和 MMIO Read 的延迟为什么差几倍 | pcie-bar-activation.md §三.3 |
| 固件怎么拿到 ECAM 基址、怎么发第一笔 Config TLP | pcie-firmware-enum.md §二~三 |
| 固件和内核怎么交接 PCIe 设备信息 | pcie-firmware-enum.md §四 |
| BAR 大小探测的"写全 1 读回"到底怎么工作的 | pcie-pre-enumeration.md §三 |
| Root Complex 是软件还是硬件?需不需要驱动? | pcie-pre-enumeration.md §一 |
| Switch 收到一个 TLP 后怎么决定向哪个端口转发 | pcie-enumeration.md §三.5 |
| Config TLP 和 Memory TLP 的寻址方式有什么区别 | pcie-enumeration.md §三.5 |
| 驱动 probe() 里做了什么、为什么枚举和驱动匹配要分开 | pcie-bar-activation.md §四 |
| 设备从被发现到用户态可读写经历了哪些状态变化 | pcie-bar-activation.md §五 |
跨主题关联
| 相关主题 | 关联点 |
|---|---|
| DMA | DMA 通过 PCIe 总线实现 Bus Mastering,发 Memory Read/Write TLP |
| DMA 与 CPU 交互 | CPU/DMA 通过 BAR 寄存器交互,BAR 分配在枚举阶段完成 |
| I/O 编址 | PMIO vs MMIO,mov [BAR] 如何穿过 Store Buffer、RC 变成 TLP |
| FPGA 通信 | FPGA 作为 PCIe Endpoint 的六种通信方式 |
| 中断控制器进化 §二 | PCIe MSI/MSI-X 中断如何投递到 LAPIC |
| CPU 总线架构 | PCIe 在整体总线拓扑中的位置(Ring/Mesh/IFOP/UPI/CXL) |
| CXL | CXL 基于 PCIe 物理层,共享内存语义 |
四阶段连贯记忆:按下电源键 → ① 硬件链路训练(纯硬件,几十ms)→ ② 固件扫 Bus 0 发现 Boot 设备(读 MCFG→ECAM→发 Config TLP)→ ③ 内核深度优先递归建完整树(分配 BAR/Bus 号/MSI-X)→ ④ BAR 激活 + 驱动 probe()(地址解码器生效、ioremap、设备可用)。四阶段串起来就是 PCIe 从物理层到应用层的完整认知链。