EDK II模块开发与集成 - 第1节
1.1 UEFI 与 PI 规范简介
UEFI(统一可扩展固件接口)和 PI(平台初始化)规范是 EDK II 开发的两大基石。UEFI 定义了操作系统与固件之间的接口,而 PI 则规范了固件内部各模块的协作方式。
UEFI 规范核心概念
- 启动管理器:负责选择启动设备、管理启动顺序,是 UEFI 固件的入口点。
- 运行时服务:在操作系统接管后仍可调用的服务,如变量存储、时间管理。
- 启动服务:仅在启动阶段可用的服务,包括内存分配、协议安装、事件处理。
- 协议:UEFI 模块间通信的接口,每个协议由 GUID 唯一标识。
- 映像加载:支持 PE32+ 格式的驱动和应用加载。
PI 规范核心概念
PI 规范将固件初始化过程划分为七个阶段,每个阶段有明确的职责和接口:
| 阶段 | 全称 | 主要职责 |
|---|---|---|
| SEC | 安全验证 | CPU 初始复位、缓存作为临时 RAM、安全验证入口 |
| PEI | EFI 预初始化 | 检测内存、发现早期硬件、传递 HOB 列表 |
| DXE | 驱动执行环境 | 加载驱动、枚举设备、分配资源、产生 ACPI/SMBIOS 表 |
| BDS | 启动设备选择 | 显示启动菜单、执行启动策略、加载操作系统引导器 |
| TSL | 短暂系统加载 | 操作系统引导器运行阶段,准备退出启动服务 |
| RT | 运行时 | 操作系统已接管,仅保留运行时服务 |
| AL | 灾难恢复 | 系统崩溃或固件更新失败时的恢复处理 |
关键理解:UEFI 规范面向操作系统与固件的接口,PI 规范面向固件内部模块的协作。EDK II 同时实现了这两套规范,开发者编写的模块通常运行在 PEI 或 DXE 阶段。
1.2 EDK II 项目历史与社区
发展历程
| 时间 | 里程碑 | 说明 |
|---|---|---|
| 1999 | Intel 启动 EFI 1.0 | Intel 提出可扩展固件接口概念,替代传统 BIOS |
| 2005 | UEFI 论坛成立 | 统一 EFI 规范管理,发布 UEFI 2.0 |
| 2007 | EDK 开源 | Intel 开源 EFI 开发套件(EDK) |
| 2010 | EDK II 发布 | 重构为模块化架构,引入 .dsc/.fdf/.inf 构建系统 |
| 2017 | tianocore 成为核心 | EDK II 主仓库迁移至 tianocore/edk2 GitHub 组织 |
| 至今 | 活跃社区维护 | 每月稳定标签发布,支持 ARM/RISC-V 等新架构 |
社区生态
- tianocore 组织:GitHub 上的核心仓库,包含 edk2、edk2-platforms、edk2-non-osi。
- 邮件列表:edk2-devel 是主要技术讨论渠道,所有补丁和 RFC 在此审查。
- Bugzilla:缺陷跟踪和功能请求管理平台。
- 年度 UEFI 研讨会:社区成员分享固件开发经验和最新进展。
- 企业贡献者:Intel、AMD、ARM、Microsoft、Google 等持续贡献代码。
社区参与建议:新手可以从修复 Bugzilla 中的低优先级缺陷开始,或为现有模块添加单元测试。提交补丁前务必阅读
EDK II 补丁提交规范。
1.3 EDK II 源码获取与目录结构
获取源码
推荐使用 Git 克隆 tianocore 官方仓库:
# 克隆核心 EDK II 仓库
git clone https://github.com/tianocore/edk2.git
# 初始化子模块(包含加密库、编译工具等)
cd edk2
git submodule update --init --recursive
# 克隆平台代码(可选,用于特定硬件)
git clone https://github.com/tianocore/edk2-platforms.git
顶层目录结构
| 目录/文件 | 用途 |
|---|---|
| MdePkg/ | 核心协议定义、数据类型、基础库(UEFI/PI 规范头文件) |
| MdeModulePkg/ | 通用模块实现:内存管理、事件、协议数据库、启动管理器 |
| UefiCpuPkg/ | CPU 架构相关模块:MP 服务、异常处理、SMM |
| PcAtChipsetPkg/ | 传统 PC 芯片组支持:8259 PIC、8254 定时器 |
| NetworkPkg/ | 网络协议栈:TCP/IP、HTTP Boot、iSCSI |
| ShellPkg/ | UEFI Shell 实现及内置命令 |
| SecurityPkg/ | 安全功能:TPM、Secure Boot、Measured Boot |
| BaseTools/ | 编译工具链:GenFw、GenFds、Ecc 代码检查 |
| Conf/ | 构建配置文件:target.txt、tools_def.txt |
| edk2.nsh | Windows 环境变量设置脚本 |
| edk2.sh | Linux/macOS 环境变量设置脚本 |
模块包内部结构
每个 Package(如 MdeModulePkg)内部遵循统一布局:
MdeModulePkg/
├── MdeModulePkg.dsc # 包级构建描述文件
├── MdeModulePkg.dec # 包声明文件(GUID、PPIs、协议、PCD)
├── Include/ # 公共头文件
│ ├── Protocol/ # 协议定义
│ ├── Ppi/ # PEI PPIs 定义
│ ├── Guid/ # GUID 定义
│ └── Library/ # 库类头文件
├── Library/ # 库实例实现
├── Universal/ # 通用模块(PEIM/DXE Driver)
├── Bus/ # 总线驱动
├── Application/ # UEFI 应用
└── Test/ # 单元测试和 SCT 测试
注意:不要直接修改 MdePkg 和 MdeModulePkg 中的核心文件。自定义模块应放在独立的 Package 中,通过 .dsc 文件引用标准包。这能保证未来升级 EDK II 版本时不会产生冲突。
构建系统核心文件
EDK II 使用三文件构建模型,每个模块必须包含以下文件:
| 文件类型 | 扩展名 | 作用 | 示例 |
|---|---|---|---|
| 包声明文件 | .dec | 声明包内所有 GUID、协议、PPI、PCD 和库类 | MdePkg/MdePkg.dec |
| 包构建描述 | .dsc | 定义包内模块列表、库实例映射、PCD 默认值 | MdeModulePkg/MdeModulePkg.dsc |
| 模块信息文件 | .inf | 描述单个模块的源文件、依赖库、编译选项、入口点 | MdeModulePkg/Universal/DiskIoDxe/DiskIoDxe.inf |
| 固件卷描述 | .fdf | 定义固件映像的布局、FVs、FD 区域 | OvmfPkg/OvmfPkgX64.fdf |
快速上手:使用
edk2/BaseTools/Source/C/bin/Build 工具编译。首次编译建议从 OvmfPkg 开始,它支持 QEMU 模拟运行,无需真实硬件。