【每日一题】一文讲懂主机启动时是如何给每个PCIe外设分配BDF的
2025-12-08 14:39:23
我们经常在Linux里面使用lspci插卡某个pcie device 的b:d.f值,那么这个BDF (bus:device.function)值在系统化阶段是如何分配的呢?

下面的文章将简要分析一下我们常用的电脑在启动过程中是如何进行pcie device枚举,发现每个device,同时给每个device分配合理的b:d.f的,我们将通过举几个和pcie device交互的例子说明。有的朋友可能会问,该枚举是在link up进入L0,然后协商flow control等信息link logically up之后做的第一件事情吗?整个过程一般会持续多少时间,例如大概几百ms还是几秒钟?有的时候会卡住吗?会有timeout超时机制设置吗?如果路径中间经过pcie switch咋办?我们也会涉及一下上述内容。


1. PCIe 设备枚举是在 BIOS/UEFI 的 PCI Bus Driver 完成的

执行者:不是 PCIe 协议本身,而是 BIOS/UEFI 软件层(PCI Bus Driver)目的:发现所有端口下的设备,并给设备分配 Bus/Device/Function 编号与资源(BAR space)。

PCIe 协议只负责:链路训练(TS1/TS2)、进入 L0、FEC、Lane Negotiation、Scrambling、Symbol Lock、Flow Control Credits 初始化等。

设备枚举属于“configuration space access”,是软件行为,不是 PCIe 链路层功能。


2. 枚举使用的底层 TLP 类型:Config Read/Write TLP

很多工程师误以为 BIOS 枚举设备是通过 MemRd/MemWr TLP。

✔ 正确:使用 Config Type 0 / Type 1 TLP

  • Type 0 Configuration Read/Write TLP:访问同一个 bus上的设备 (Device/Function)

  • Type 1 Configuration Read/Write TLP:访问下层 bus(例如 PCIe Switch 下的子 Bus)

例如 BIOS 读 VendorID:

下图是使用SerialTek PCIe 5.0/6.0协议分析仪看到开机后CPU发了第一个MesD_local后大概过了400ms进开始进行pcie device枚举的过程。



3. 枚举触发条件:必须等到 PCIe Link 进入 L0 并完成 Flow Control 初始化

PCIe LTSSM:

  1. Detect

  2. Polling(TS1/TS2)

  3. Configuration(TS1/TS2 + Lane/Speed negotiation)

  4. Recovery

  5. L0  🔥

  6. Flow Control 初始化(InitFC1 / InitFC2)

  7. 链路逻辑 UP

✨ BIOS 只有在 Link Training 完全结束、进入 L0、FC credit 也 ready 后才可以开始发 PCIe TLP。

因此:

✔ 设备枚举是 Link Up 完成后的第一批软件操作之一。


4. B:D.F 分配规则(直连设备)

Boot 时,BIOS 从:

Bus 0 → Device 0–31 → Function 0–7

依次尝试访问:


若设备不存在: → 返回 0xFFFF_FFFF

若设备存在: → 返回 VendorID / DeviceID

示例:CPU Root Port 直连设备

假设 CPU Root Port 出现在:

BIOS 识别这是一个 PCIe Root Port,于是给这个 Root Port 下的新 Bus 指定一个编号:

然后 BIOS 开始扫描 Bus = 1 的设备。


5. 通过 PCIe Switch 的 B:D.F 分配流程

假设结构:

枚举过程:
  1. Root Port 被枚举(Bus 0, Dev x)

  2. BIOS 分配 Secondary Bus = 1

  3. BIOS 在 Bus 1 读 VendorID

  4. 发现这是 PCIe Switch(Device=0x10B5 for PLX/Avago/Broadcom)

  5. 配置 Switch 的 Primary/Secondary/Subordinate Bus

  6. 对每个 Downstream Port 再给一个新 Bus 号,例如:


  7. BIOS 再依次扫描 Bus 2、Bus 3、Bus 4、Bus 5…


6. 与 PCIe 设备交互的典型发现例子

例 1:读 VendorID(识别设备类型)

BIOS 发:

Config Read Type 0 (0:1:0 offset 0x00)

返回:

→ 说明这是 Intel 网卡。


例 2:读 Class Code(判断是否是 Switch、NVMe、GPU、NIC)

值示例:
Class Code设备类型
0x010802NVMe
0x030000VGA / GPU
0x060400PCIe-to-PCIe Bridge (Switch)
0x0C0330USB 3.0 controller

BIOS 根据 Class Code 决定如何深入扫描。


例 3:读 BAR(请求资源)

BIOS 写 BAR = 0xFFFF_FFFF,然后读回来。

如果设备返回:

→ 表示需要 4KB MMIO BAR space。

BIOS 用此信息分配地址。


7. 整个枚举过程一般耗时多久?

典型耗时(服务器或 PC)

项目耗时
PCIe Link Training (TS1/TS2 + FEC lock)5–50 ms PER PORT
枚举、高级扫描、BAR 分配10–200 ms
PCIe Switch 多层拓扑最多 300–800 ms

总计通常:100ms~1 秒之间。

如有大量 Switch + 多层拓扑(大服务器系统)可能达到 2–3 秒。


8. 会卡住吗?是否有 timeout?

✔ BIOS 有严格 timeout 机制:

常见超时:

  • 配置读超时(无响应设备 → 返回 0xFFFF_FFFF)

  • Link Training timeout(链路一直在 Recovery 状态 → UEFI 有 100ms~500ms 的重试)

  • BAR 分配失败(MMIO 资源不足)

  • 设备用 DPC / LTSSM 死循环导致 Root Port 死等

典型值:

  • Link Training Timeout:100ms~500ms

  • Config TLP Timeout:20ms~50ms

  • Switch 热插拔端口 Recovery 超时:≈100 ms

因此:

✔ 枚举大概率不会“无限卡死”,都会降级、跳过设备、或者报错继续。


9. 为什么偶尔枚举失败?

工程师常碰到:

  • Retimer/Switch training 不稳定 → link 一直在 Polling/Recovery 循环

  • BIOS 没有足够的地址空间分配给 BAR(尤其 GPU 或 SmartNIC)

  • PCIe device 还没上电 or VSEC 读不到

  • FLR resetting 需要时间

这些都会导致“有时能枚举、有时不能枚举”。


10. 流程总结图


11. 关键问题回答总结

你的问题回答
枚举是通过 TLP MemRd 实现的吗?❌ 不是。使用 Config Read/Write TLP
枚举何时发生?Link Up → Flow Control Ready 之后立刻进行
枚举耗时多少?100ms ~ 1 秒(大型系统可达 3 秒)
会卡住吗?有 timeout,会跳过问题设备
如何为 switch 分派 B:D.F?BIOS 为每个 downstream port 分配一个新的 Bus number,递归扫描
和直连设备的差异?增加 Type1 Config TLP,并分层 bus 扫描结构
更多关于PCIe 6.0/CXL的测试工具和技术,请下载Saniffer公司2025.6.16最新更新的白皮书12.3版本 - 《PCIe5&6.0, CXL, NVMeNVMoF, SSD, NAND, DDR5, 800GE测试技术和工具白皮书_ver12.3》。
白皮书下载链接 (或者点击下面的二维码直接下载):

https://pan.baidu.com/s/18_c11aeFhSBe2qa-jUFs_Q?pwd=mm9y 提取码: mm9y

图片

如果你有其任何关于PCIe5&6.0, CXL, NVMe/NVMoF, NAND, DDR5/LPDDR5以及UFS测试方面的我问题想咨询,请访问:访问www.saniffer.cn / www.saniffer.com 访问我们的相关测试工具和产品;或者添加点击左下角“阅读原文”留言,或者saniffer公众号留言,致电021-50807071 / 13127856862,sales@saniffer.com。

图片