UEFI 固件基础:规范、启动流程与架构

各位同学,欢迎来到第二章。上一章我们聊了安全启动为什么重要,这一章咱们得先把地基打牢——UEFI 到底是什么?它怎么启动的?内部长什么样?

说实话,我早年做 BIOS 开发时,UEFI 还是个新鲜玩意儿。那时候大家还在争论 Legacy BIOS 够不够用,结果 Intel 一推,整个行业就转过来了。现在回头看,UEFI 的设计确实比传统 BIOS 高明太多。

UEFI 规范简介

UEFI,全称 Unified Extensible Firmware Interface,统一可扩展固件接口。它不是一套代码,而是一份规范——定义了操作系统和固件之间的接口标准。

为什么要搞这么个东西?传统 BIOS 有几个硬伤:

  • 16 位实模式:只能访问 1MB 内存,启动慢得要命
  • MBR 分区:最大支持 2TB 磁盘,现在随便一个硬盘就超了
  • 没有标准驱动模型:每个厂商自己玩自己的,兼容性全靠运气
  • 安全机制为零:谁都能改启动项,病毒笑了

UEFI 把这些痛点全解决了。它运行在 64 位模式下,支持 GPT 分区表,有完整的驱动框架,还内置了安全启动(Secure Boot)——这也是咱们这门课的核心。

关键点:UEFI 规范由 UEFI 论坛维护,目前最新版本是 2.10。但别被版本号吓到,实际产品中 2.3.1 到 2.8 之间的版本最常见。我见过不少厂商还在用 2.4 的底子打补丁,稳定第一嘛。

UEFI 规范涵盖的内容很广,包括:

  • 启动管理器(Boot Manager)
  • 运行时服务(Runtime Services)
  • 启动服务(Boot Services)
  • 协议接口(Protocols)
  • 固件文件系统(Firmware File System)
  • 安全启动(Secure Boot)

嗯,这里要注意:安全启动只是 UEFI 规范里的一章,但这一章能决定整个系统的生死。

UEFI 启动流程

咱们来看看一台 UEFI 机器从按下电源键到进入操作系统,到底经历了什么。我画了一张流程图,方便你理解。

UEFI 启动流程 UEFI 启动流程 1. 上电 / SEC 阶段 2. PEI 阶段 3. DXE 阶段 4. BDS 阶段 5. TSL 阶段 6. RT 运行时阶段 安全启动检查主要在 PEI 和 DXE 阶段之间,以及 BDS 阶段加载启动项时执行

这张图里每个阶段都有明确的分工:

阶段 全称 主要工作 安全启动相关
SEC Security CPU 初始化,建立临时内存 验证固件卷的完整性
PEI Pre-EFI Initialization 检测内存、芯片组早期配置 验证 DXE 基础模块签名
DXE Driver Execution Environment 加载驱动、枚举设备 验证所有 DXE 驱动签名
BDS Boot Device Selection 选择启动设备,加载启动项 验证启动管理器签名
TSL Transient System Load 加载 OS Loader 验证 OS Loader 签名
RT Runtime 操作系统运行,UEFI 进入后台 运行时服务仍可调用

个人经验:我在调试某款国产服务器时,遇到过 PEI 阶段卡死的问题。查了两天才发现是内存 SPD 数据读取超时,导致 PEI 模块无法完成初始化。这种问题在 UEFI 开发中很常见——硬件时序问题往往比软件 bug 更难定位。

UEFI 固件架构

UEFI 固件不是一个大泥球,它是有层次结构的。我习惯把它分成三层来看:

  1. 硬件抽象层:封装 CPU、芯片组、主板的差异
  2. 核心框架层:提供启动服务、运行时服务、协议管理
  3. 应用/驱动层:UEFI 应用、Option ROM、OS Loader

说白了,UEFI 就是一个微型操作系统。它有内核(DXE Core)、有驱动模型(UEFI Drivers)、有文件系统(FAT12/16/32),甚至还有网络栈。

咱们重点看几个关键组件:

1. 启动服务(Boot Services)

启动服务在操作系统接管前可用,包括:

  • 内存分配与释放
  • 事件与定时器管理
  • 协议接口的安装与查找
  • 映像加载与启动

操作系统启动后,这些服务就失效了。ExitBootServices() 一调用,UEFI 就把控制权交出去了。

2. 运行时服务(Runtime Services)

运行时服务在操作系统运行期间仍然可用,包括:

  • 获取时间与设置时间
  • 重置系统
  • 变量存储(NVRAM)
  • 安全启动策略查询

这里有个坑:运行时服务必须用虚拟地址映射,操作系统会通过 SetVirtualAddressMap() 告诉 UEFI 新的地址映射关系。我见过不少固件在这个环节出问题,导致系统休眠唤醒后蓝屏。

3. 协议(Protocols)

UEFI 协议就是接口契约。你想访问某个硬件?先找到对应的协议。比如:

  • EFI_SIMPLE_FILE_SYSTEM_PROTOCOL — 文件系统访问
  • EFI_BLOCK_IO_PROTOCOL — 块设备读写
  • EFI_LOADED_IMAGE_PROTOCOL — 已加载映像的信息
  • EFI_SECURE_BOOT_PROTOCOL — 安全启动策略控制

避坑指南:我曾经在移植 UEFI 网络栈时,发现某个网卡驱动没有正确安装 EFI_SIMPLE_NETWORK_PROTOCOL,导致 PXE 启动失败。排查了半天,最后发现是驱动里少了一句 gBS->InstallProtocolInterface() 调用。这种低级错误在开发初期特别容易犯,建议每次写完驱动都检查协议是否安装成功。

4. 固件文件系统(Firmware File System, FFS)

UEFI 固件本身存储在 SPI Flash 中,文件系统格式是 FFS。每个文件有 GUID 标识,分为多种类型:

文件类型 GUID 示例 说明
PEIM PEI 模块 PEI 阶段执行的代码
DXE DXE 驱动 DXE 阶段加载的驱动
FV 固件卷 包含多个文件的容器
RAW 原始数据 Logo、证书、配置数据
SECURITY_CORE 安全核心 安全启动相关模块

嗯,这里要注意:安全启动的证书和签名数据库(db、dbx、KEK、PK)就存储在 FFS 的 RAW 文件中,或者放在 NVRAM 变量里。具体位置取决于厂商实现,但标准做法是两者都存——NVRAM 用于运行时查询,FFS 用于固件更新时的备份。

UEFI 与安全启动的关系

安全启动不是独立于 UEFI 的附加功能,它深深嵌入了 UEFI 的每个阶段。从 SEC 阶段验证固件卷的哈希,到 BDS 阶段验证启动管理器的签名,再到 TSL 阶段验证 OS Loader 的证书——安全启动贯穿了整个启动链。

我个人的理解是:UEFI 提供了舞台,安全启动是舞台上的保安。保安从演员入场就开始检查,直到演出结束。任何一个环节出问题,保安都会拉闸。

核心观点:没有 UEFI 的标准化接口,安全启动就无从谈起。反过来,没有安全启动,UEFI 的灵活性反而成了攻击面。两者相辅相成,缺一不可。