1. 课程导入

在传统 BIOS 时代,固件工程师面对的是 16 位实模式、1MB 寻址空间、汇编主导的封闭开发环境。硬件初始化代码与操作系统引导逻辑高度耦合,每更换一款芯片组或外设,往往需要重写大量底层代码。UEFI(Unified Extensible Firmware Interface)的出现,正是为了把固件从“一次性启动代码”升级为“可扩展、可复用、可工程化的平台服务框架”。本课程围绕 UEFI 协议与平台服务开发工程展开,目标是让学习者既能理解规范背后的设计思想,又能动手完成可运行的固件模块。

一、从 BIOS 到 UEFI:一次架构范式的迁移

UEFI 的起源可以追溯到 Intel 在 1998 年提出的 EFI 1.0 规范。2005 年,Intel 将 EFI 规范交由 UEFI Forum 管理,并更名为 UEFI。其核心变化不是“换了一种启动方式”,而是把固件重新定义为运行在 32/64 位保护模式下的引导服务与运行时服务集合。传统 BIOS 通过中断向量提供有限服务,而 UEFI 通过协议(Protocol)和句柄(Handle)构建了一个面向对象的服务总线。

理解 UEFI 演进,需要抓住三条主线:第一,执行环境从实模式走向保护模式,固件获得更大寻址空间和更安全的执行环境;第二,服务模型从“中断调用”走向“协议接口”,驱动与硬件之间通过标准接口解耦;第三,开发方式从“汇编硬编码”走向“C 语言 + 模块化 + 工程构建”,使固件具备类似应用软件的工程化能力。

UEFI 演进与平台服务开发工程学习地图 传统 BIOS EFI 1.0 UEFI 2.x UEFI + PI 16位实模式 保护模式服务 协议化驱动 平台初始化规范 应用层 OS Loader / UEFI Shell / 诊断工具 / Capsule 更新程序 协议与服务层 Boot Services / Runtime Services / Protocol Handler / Driver Binding 硬件与平台层 CPU / 芯片组 / PCIe / 存储 / 网络 / ACPI / SMBIOS 阶段一:规范与概念 协议、句柄、服务 阶段二:驱动开发 DXE、Protocol 实现 阶段三:平台服务 Runtime、ACPI、SMI 阶段四:工程实战 调试、优化、发布

二、核心概念:协议、句柄与服务

UEFI 中最关键的概念是“协议”。协议本质上是一组函数指针和数据结构组成的接口,它规定了“能做什么”,而不关心“谁来实现”。例如,一个存储驱动可以实现 Block I/O 协议,上层文件系统只依赖该协议,不依赖具体硬件。句柄则是协议实例的载体,一个句柄可以挂载多个协议,形成设备对象。Boot Services 和 Runtime Services 是固件向操作系统和驱动提供的两类基础服务:前者在 ExitBootServices 后失效,后者在操作系统运行期间仍然可用。

核心关系:句柄(Handle)挂载协议(Protocol),协议提供函数表(Protocol Interface),驱动通过 LocateProtocol / OpenProtocol 获取服务,通过 InstallProtocolInterface 发布服务。

三、平台服务开发工程的方法流程

平台服务开发不是“写一个 main 函数”,而是遵循固件工程的标准流程。一个典型的 UEFI 驱动或服务模块,通常经历以下阶段:

  1. 需求与接口定义:明确要暴露的协议 GUID、函数签名、输入输出参数和错误码。
  2. 模块骨架搭建:编写 INF 文件、入口函数和协议结构体,确定依赖的包和库。
  3. 协议安装与消费:在入口点安装协议,或通过 gBS->LocateProtocol 获取已有服务。
  4. 硬件访问与抽象:通过 PCI I/O、MMIO 或 SMM 通信访问寄存器,避免直接硬编码地址。
  5. 构建与集成:使用 EDK II 构建系统编译,生成 .efi 或 FV 镜像,集成到平台固件。
  6. 调试验证:通过串口日志、UEFI Shell、调试器和硬件跟踪定位问题。
# EDK II 典型构建命令
build -p OvmfPkg/OvmfPkgX64.dsc -a X64 -t GCC5 -b DEBUG

# 在 UEFI Shell 中加载驱动
load fs0:\MyPlatformService.efi
drivers

四、案例场景:一个平台服务模块的诞生

假设我们需要为某平台增加一个“平台信息查询服务”,让操作系统加载器能够读取主板温度、固件版本和启动模式。工程上可以这样落地:定义一个 PlatformInfoProtocol,包含 GetTemperature、GetFirmwareVersion、GetBootMode 三个函数;在 DXE 阶段编写驱动,通过 SMBus 或 ACPI 方法读取温度,通过 PCD 或变量读取固件版本;在入口点安装协议;最后在 UEFI Shell 中编写测试程序调用该协议。这个案例贯穿了协议定义、驱动绑定、硬件访问、构建集成和调试验证,是理解平台服务开发工程的典型缩影。

学习阶段 关键对象 产出物 常见误区
规范与概念 协议、句柄、服务表 概念图、接口清单 把协议当成普通函数库
驱动开发 Driver Binding、DXE 可加载 .efi 驱动 忽略 Supported/Start/Stop 生命周期
平台服务 Runtime Services、ACPI、SMI 运行时服务与平台接口 在 Runtime 中调用 Boot Services
工程实战 EDK II、调试器、日志 集成镜像与测试报告 只调一次就认为稳定

五、常见误区与复盘建议

误区一:认为 UEFI 只是“更高级的 BIOS”,忽略其协议化、模块化的工程本质。
误区二:在 Runtime Services 中调用 Boot Services,导致操作系统阶段崩溃。
误区三:直接操作硬件地址而不通过协议抽象,导致平台移植困难。
误区四:只关注功能实现,不关注启动顺序、依赖关系和错误恢复。

复盘时建议建立“协议—驱动—平台—验证”四层检查表:协议 GUID 是否唯一且符合规范;驱动入口和卸载路径是否完整;平台资源是否通过 PCD 或 ACPI 描述;验证是否覆盖正常、异常和边界场景。只有把固件当作长期维护的软件工程来对待,才能真正掌握 UEFI 平台服务开发。

学习建议:先通读 UEFI 规范第 4、6、7、8 章,建立协议与服务的基本认知;再基于 EDK II 的 OvmfPkg 或 MinPlatformPkg 跑通一个 HelloWorld 驱动;最后选择一个真实平台服务需求,完成从接口定义到调试验证的完整闭环。

本课程后续将围绕协议实现、驱动模型、运行时服务、ACPI 集成、SMM 通信、固件更新与安全启动等主题逐步展开。掌握本章的学习地图,意味着你已经从“写启动代码”转向“设计平台服务”,这是 UEFI 工程能力跃迁的起点。