为什么驱动工程师要懂固件阶段?

Linux 枚举设备时,很多参数不是内核决定的:BAR 是否分配、分在 32 位还是 64 位空间、总线号范围、ASPM 策略、甚至"设备压根不出现在 lspci 里"——都可能是固件(UEFI/BIOS 或设备树)先做了决定。本页梳理固件与内核的交接面,覆盖四块最常打交道的部分:描述表(MCFG/设备树)→ 资源声明(_CRS)→ 设备引导代码(Option ROM)→ 可调整 BAR(ReBAR)

上电
 └─ UEFI/BIOS:枚举 PCIe、分配 BAR、记录描述(MCFG/_CRS)
      │              ├─ 加载 Option ROM(显卡初始化、iSCSI Boot)
      │              └─ 决定 Above 4G / ASPM / ReBAR 策略
      ▼
内核启动
 └─ ACPI/设备树解析(MCFG → PCI 根域;_CRS → 资源窗口)
      │
      ▼
pci_host_probe / pci_scan_root_bus_bridge
 ├─ 按固件记录复用重分配资源(pci=realloc 等参数)
 └─ 设备驱动 probe(此时设备已可访问)
                    

描述表:ACPI MCFG 与设备树

内核要知道"配置空间在哪里",靠平台描述:

平台描述机制关键字段
x86 / ACPI MCFG 表(PCI Segment Group 表) 每项描述一个 PCI 段:段的基地址(ECAM 基址)、起始总线号、结束总线号、保留字段。内核 acpi_pci_root_get_mcfg_addr() 据此定位 ECAM。一个段对应一个 PCI 域(domain:bus:dev.fn 中的 domain)
ARM/ARM64 设备树pcie@ 节点) reg/reg-names="config" 给 ECAM;ranges 给 MEM/IO 窗口;bus-range 给总线号;interrupt-map 给 INTx 映射;msi-parent 指向 ITS/GICv3
# 查看 MCFG 表(需 root,acpica-tools)
$ acpidump -n MCFG -b
# 或
$ cat /sys/firmware/acpi/tables/MCFG | xxd | head

# 内核视角:每个 PCI 域的 ECAM 与总线范围
$ dmesg | grep -i "ECAM\|PCI host bridge"
$ ls /sys/devices/pci0000:00/ | head    # 域 0000 下的总线

# 设备树平台:
$ ls /proc/device-tree/ | grep pcie
$ cat /proc/device-tree/pcie@40000000/ranges | xxd | head

要点:MCFG 只描述 ECAM 位置与总线范围,不描述 MEM/IO 窗口——后者由固件在桥/RC 的 Base/Limit 寄存器里配好,内核通过 _CRS(见下)或直接读寄存器拿到。

资源声明:ACPI _CRS 与 _DSM

ACPI 命名空间在 PCI 根桥(\_SB.PCI0 之类)下提供方法,把固件掌握的资源与策略告诉内核:

方法含义内核消费点
_CRS(Current Resource Settings) 当前资源设置:根桥的总线号范围(Bus Number Range)、内存窗口(Memory32/64)、IO 窗口。内核 pci_acpi_root_prepare_resources() 解析它并建立 RC 的资源窗口 drivers/pci/pci-acpi.c;解析失败会看到 "no IO and memory resources present in _CRS"
_DSM(Device Specific Method) 厂商/规范定义的策略位。PCI 相关:功能 5"PCI Boot Configuration"(是否允许内核重分配资源)、功能 8(根桥设备属性)、D3cold 延迟等 pci_acpi_optimize_delay()pci_acpi_preserve_config()
_DSD(Device Specific Data) 设备属性(键值对)。PCI 用途如根端口的 HotPlugSupportInD3 属性 按属性名匹配消费
_HPX / _HPP 热插拔/热启动参数:设备挂载时的 MPS/MRRS、总线参数等(_HPP 已废弃于 _HPX) 热插拔编程路径

"固件留的资源不够"是第一大坑

_CRS 声明的窗口小于实际需求时,内核要么无法容纳设备 BAR(表现为 BAR 12: no space for ... 或部分设备不出现),要么被 bootloader 的 pci=realloc 救回——pci=realloc 强制内核重新分配总线资源,绕过固件的分配结果;配合 pci=assign-busses(重指派总线号)、pci=hpiosize= 等参数使用。服务器上 BIOS 升级后设备"消失"、加装第二块 GPU 后第一块设备 BAR 变小,都是这类问题的典型现场。

Option ROM:设备自带的引导代码

很多设备把一小段固件烧在板载 ROM 里(显卡 BIOS、网卡 PXE/iSCSI Boot ROM),固件阶段会把它映射到内存并执行——这就是"显卡在 BIOS 界面就能显示"的原因。

  • 物理形态:ROM 通过 PCI 扩展 ROM 机制暴露(ROM BAR,标准配置空间偏移 0x30);容量经 ROM BAR 的 sizing 得到。
  • 执行环境:传统 BIOS 走 16 位实模式 ROM;UEFI 走 EFI Option ROM(FFS 格式),由固件的 PCI Bus Driver 加载签名与协议。
  • 内核阶段:Linux 默认不执行 Option ROM(避免执行未签名代码),只有少数例外(如 vgacon 需要时用 video=... 或通过 sysfs rom 文件手动触发校验后执行,见 Documentation/PCI/sysfs-pci.rst)。
  • 现场价值:机器在 BIOS 下有显示但进内核黑屏——说明 Option ROM 完成了初始化,问题在内核驱动而非硬件;反之则先怀疑 ROM/固件。
# 查看设备的 ROM 信息(lspci 会显示 Expansion ROM 基址)
$ lspci -vvv -s 01:00.0 | grep -i "expansion rom"

# 通过 sysfs 使能 ROM 并 dump(需确认平台允许)
$ echo 1 > /sys/bus/pci/devices/0000:01:00.0/rom
$ cat /sys/bus/pci/devices/0000:01:00.0/rom > gpu.rom
$ echo 0 > /sys/bus/pci/devices/0000:01:00.0/rom

Above 4G 解码与 Resizable BAR

这两个概念经常一起出现,因为它们解决同一类问题:32 位地址空间装不下大 BAR

Above 4G Decoding

固件选项(BIOS 里通常叫 "Above 4G Decoding"/"64-bit BAR"):启用后,固件可以把设备的 64 位 BAR 分配到 4GB 以上的地址空间。对大显存 GPU(16GB+)几乎是必需——否则显存只能映射到 256MB 的 32 位窗口。调试含义:BIOS 里关掉这项,或平台不支持时,大 BAR 设备会分配失败或降级为窗口映射。

Resizable BAR(ReBAR)

ReBAR 是 PCIe Extended Capability(Cap ID 0x0015,见本站 Capability ID 速查):允许设备的某个 BAR 在硬件支持的尺寸集合里运行时调整大小——GPU 显存 BAR 从 256MB 扩到 16GB,让 CPU 可以直接读写整块显存,游戏/推理/HPC 场景收益明显。

层面动作
设备通过 ReBAR Capability 声明每个 BAR 支持的尺寸列表(Capability Register 的 "Sizes Supported" 位图)
固件UEFI 按 ReBAR Capability 选择目标尺寸并配置;部分平台需要 BIOS 选项显式开启
Linux 内核内核可在运行时重设:sysfs 属性 resourceN_resize 读写目标尺寸(底层 pci_rebar_get_possible_sizes() / pci_resize_resource(),需重新分配资源)
驱动使用 resize 后的 BAR(pci_iomap 等按最终尺寸重新映射);有的驱动在 probe 时主动请求特定尺寸
# 查看 BAR0 支持哪些尺寸(返回值是尺寸的位图/集合)
$ cat /sys/bus/pci/devices/0000:01:00.0/resource0_resize

# 请求把 BAR0 调整为 8GB(写入尺寸;需平台允许重分配)
$ echo 8G > /sys/bus/pci/devices/0000:01:00.0/resource0_resize

# 查看结果
$ lspci -vvv -s 01:00.0 | grep -i "region 0\|resizable"

与本站其他页面的连线

  • BAR sizing 的底层规则见 RC 控制器驱动的枚举深挖一节;
  • ReBAR 属于扩展能力,用 配置空间解析器粘贴 lspci dump 可直接看到 0x0015 能力的解码;
  • 固件分配的 BAR 地址是否落在 ECAM/iATU 窗口内,见 iATU 地址翻译
  • 枚举与资源分配的内核实现详见 RC 驱动页的"枚举流程深挖"。
理解检测
1. ACPI 的 MCFG 表描述的是什么?
MCFG 只描述"配置空间在哪里 + 段范围"(每个 PCI 段一行:ECAM 基址、起始/结束总线号)。BAR 分配结果来自 _CRS 与桥寄存器,电源与 MSI 都在别处。
2. 设备在 BIOS 下能显示画面、进入 Linux 后黑屏,最可能的原因是?
BIOS 画面能显示,说明 Option ROM 已正常执行、硬件与链路基本没问题;黑屏发生在内核接管之后,排查方向应转向 Linux 驱动(如 nouveau/amdgpu)与 framebuffer 配置。