3. UEFI规范体系

很多初学者会把“UEFI”当成一个单独的规范来读,结果越读越乱:一会儿是启动服务,一会儿是PEI/DXE,一会儿又冒出ACPI表、SMBIOS结构、设备树。真正的原因在于,UEFI固件并不是由单一规范定义的,而是由一组分工明确、彼此衔接的标准共同构成的体系。理解这些标准“谁管什么、谁引用谁、谁在什么阶段生效”,比死记某个接口更有价值。

一、核心原理:分层定义,各司其职

可以把整个固件生态理解为四层:接口层、阶段层、描述层、操作系统层。UEFI规范定义的是“固件对外提供什么接口”,PI规范定义的是“固件内部如何分阶段实现”,ACPI/SMBIOS/设备树定义的是“如何把硬件信息交给操作系统”,而操作系统则消费这些信息完成驱动加载与设备管理。

关键点在于:UEFI规范与PI规范是“实现侧”标准,ACPI、SMBIOS、设备树是“描述侧”标准。前者回答“固件怎么写”,后者回答“硬件怎么告诉系统”。二者通过启动服务、配置表和句柄数据库连接起来。

UEFI 规范体系分层关系图 接口层 · UEFI 规范 启动服务 / 运行时服务 · 协议(Protocol)· 句柄数据库 · 配置表 · 事件与任务优先级 作用:定义固件对 OS Loader 与驱动暴露的统一调用接口 阶段层 · PI 规范 SEC → PEI → DXE → BDS → TSL → RT → AL 作用:规定固件内部启动阶段划分、阶段间交接(HOB/PPI/Protocol)与调度顺序 描述层 · 硬件信息标准 ACPI 电源/热插拔/中断/设备枚举 SMBIOS 主板/内存/CPU/资产信息 设备树 (DT) 嵌入式/ARM 硬件拓扑描述 操作系统层 Linux / Windows / 其他 OS:解析 ACPI 表、SMBIOS 结构、设备树,加载对应驱动 对外接口 内部实现 信息传递 消费方

二、各标准的作用与边界

UEFI 规范是固件与操作系统之间的“合同”。它规定了启动服务(Boot Services)和运行时服务(Runtime Services)的函数原型、协议(Protocol)的 GUID 与接口、配置表(Configuration Table)的注册方式。例如,OS Loader 通过 gBS->LocateProtocol() 找到 GOP 协议来画图,通过 gRT->GetVariable() 读取启动项。

PI 规范是固件内部的“施工图”。它把启动过程拆成 SEC、PEI、DXE、BDS 等阶段,并定义了阶段之间传递信息的方式:PEI 阶段用 PPI 和 HOB,DXE 阶段用 Protocol 和句柄数据库。没有 PI 规范,不同芯片厂商的固件就无法在统一框架下协作。

ACPI 主要解决电源管理、设备枚举和中断路由。操作系统通过 ACPI 表(如 DSDT、MADT、FADT)识别 CPU 核心、PCI 中断、电源状态。UEFI 固件负责在启动时生成或传递这些表。

SMBIOS 提供的是“资产与结构信息”,比如主板型号、内存条位置、CPU 版本。它不负责驱动加载,但操作系统和运维工具依赖它做硬件识别与资产管理。

设备树(Device Tree) 在 ARM/嵌入式领域替代了部分 ACPI 的功能,用文本或二进制 DTB 描述硬件拓扑。x86 平台以 ACPI 为主,ARM 平台常以设备树为主,二者在 UEFI 固件中通过配置表传递给内核。

三、方法流程:从固件到操作系统的信息流

以一次典型启动为例,信息流动顺序如下:

  1. SEC/PEI 阶段完成最早期硬件初始化,构建 HOB 列表。
  2. DXE 阶段加载驱动,安装 Protocol,并准备 ACPI、SMBIOS、设备树等配置表。
  3. BDS 阶段选择启动项,调用 OS Loader。
  4. OS Loader 通过 UEFI 启动服务获取配置表地址,把 ACPI/SMBIOS/DTB 传给内核。
  5. 内核解析这些表,完成设备枚举与驱动加载,随后调用 ExitBootServices() 接管系统。
// 典型:OS Loader 获取 ACPI 表
EFI_STATUS Status;
EFI_CONFIGURATION_TABLE *ConfigTable;
Status = gBS->GetConfigurationTable(&gEfiAcpiTableGuid, (VOID **)&AcpiTable);
if (EFI_ERROR(Status)) {
    DEBUG((EFI_D_ERROR, "ACPI table not found\n"));
}

四、对比清单:各标准定位速查

标准 核心作用 生效阶段 主要消费方
UEFI 规范 定义固件对外接口、协议、配置表 DXE 之后至 OS 接管 OS Loader、UEFI 驱动
PI 规范 定义固件内部阶段划分与交接机制 SEC 至 BDS 固件开发者、芯片厂商
ACPI 电源管理、设备枚举、中断路由 OS 运行时 操作系统内核
SMBIOS 硬件资产与结构信息 OS 运行时 OS、运维工具
设备树 嵌入式硬件拓扑描述 OS 启动早期 Linux/ARM 内核

五、案例与误区复盘

案例:某 ARM 服务器启动后网卡不识别。排查发现固件把设备树 DTB 地址放在配置表中,但内核启动参数未正确传递 dtb= 地址,导致内核使用了内置默认 DTB,网卡节点缺失。修复方式是在 BDS 阶段正确注册 gFdtTableGuid 配置表,并确保启动参数一致。
常见误区:
  • 把 UEFI 规范当成固件实现规范,忽略 PI 规范对阶段和交接的约束。
  • 认为 ACPI 和 SMBIOS 由操作系统自己生成,实际上它们通常由固件准备并传递。
  • 在 x86 平台强行使用设备树,或在 ARM 平台强行套用 ACPI,导致驱动模型不匹配。
  • 混淆“配置表”与“协议”:配置表是数据指针,协议是函数接口,二者用途不同。

总结来看,UEFI 规范体系不是一堆孤立文档,而是一条从固件内部实现到操作系统消费的完整链路。PI 管内部阶段,UEFI 管对外接口,ACPI/SMBIOS/设备树管硬件描述。掌握这条链路,后续阅读启动流程、编写 UEFI 驱动、排查 OS 识别问题时,才能快速定位到正确的标准与阶段。