PEI阶段与ABL的交互:PEI服务、PPI、HOB传递机制
UEFI 的 PEI(Pre-EFI Initialization)阶段是系统初始化早期的最小环境。ABL 作为 DXE 阶段的加载器,必须从 PEI 阶段获取系统关键信息才能正确初始化硬件、定位 Firmware Volume 并启动 DXE 核心。PEI 阶段通过三组抽象暴露给后续阶段:PEI 服务(PEI Services Table)、PPI(PEIM-to-PEIM Interface)以及 HOB(Hand-Off Block)。本章详细分析这三类数据对象的定义、生成机制以及 ABL 入口代码如何定位并解析它们。
4.1 PEI 服务表结构
PEI 服务表是 PEI 基础环境的核心调度接口,由 PEI Core 初始化后填充,并通过 ABL 的入口参数传递。结构定义如下:
typedef struct {
UINT64 Signature; // "PEIS"
EFI_PEI_SERVICES_REVISION Revision;
UINTN PeiRevision;
UINTN HobList; // 指向 HOB 列表首地址
// Boot Mode、内存信息等
EFI_PEI_SERVICES_BOOT_MODE BootMode;
EFI_PEI_SERVICES_MEMORY_TYPE MemoryType;
// 服务函数指针表
PEI_INSTALL_PPI InstallPpi;
PEI_REINSTALL_PPI ReInstallPpi;
PEI_LOCATE_PPI LocatePpi;
PEI_NOTIFY_PPI NotifyPpi;
// 其他服务和保留字段
} EFI_PEI_SERVICES;
| 字段 | 说明 | ABL 常用操作 |
|---|---|---|
| HobList | HOB 列表链表头指针 | 通过 HobList 启动 HOB 遍历 |
| BootMode | 引导模式(如 S5、S3、Normal) | 判断是否需要跳过某些初始化 |
| LocatePpi | 查找指定 GUID 的 PPI 实例 | 获取平台芯片组 PPI、存储 PPI 等 |
| InstallPpi | 安装新的 PPI(ABL 也可安装) | 向后续 PEIM 或 DXE 暴露 ABL 功能 |
4.2 PPI:PEIM 之间的接口协议
PPI 是 PEI 阶段组件间通信的唯一标准。每个 PPI 由一个 GUID 唯一标识,包含函数指针和数据结构。ABL 通过 PEI 服务中的 LocatePpi 或 NotifyPpi 获取所需的接口,例如:
EFI_STATUS EFIAPI LocatePpi(
IN EFI_GUID *Guid,
IN UINTN Instance,
OUT EFI_PEI_PPI_DESCRIPTOR **PpiDescriptor,
OUT VOID **Ppi
);
典型 PPI:
gEfiPeiMasterBootModePpiGuid(引导模式)、gEfiPeiMemoryDiscoverPpiGuid(内存发现)、gEfiPeiVariableStorePpiGuid(变量访问)。ABL 在初始化阶段会依次定位至少 3~5 个关键 PPI 以获取平台配置。4.3 HOB:跨阶段数据传递主干
HOB 是 PEI 阶段产出的唯一数据对象,以单向链表形式存在。每个 HOB 头部统一格式:
typedef struct {
UINT16 HobType; // 0x0001~0xFFFF,如 Resource Descriptor、Memory Allocation、Module Entry
UINT16 HobLength; // 包含头部的总长度(8 字节对齐)
UINT32 Reserved;
} EFI_HOB_HEADER;
ABL 通过 PEI 服务中的 HobList 字段,使用封装宏遍历:
EFI_PEI_HOB_POINTERS Hob;
Hob.Raw = PeiServices->HobList;
while (END_OF_HOB_LIST(Hob) == FALSE) {
if (Hob.Header->HobType == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) {
EFI_HOB_RESOURCE_DESCRIPTOR *ResHob = Hob.ResourceDescriptor;
// 记录物理内存范围、属性
} else if (Hob.Header->HobType == EFI_HOB_TYPE_MEMORY_ALLOCATION) {
EFI_HOB_MEMORY_ALLOCATION *MemHob = Hob.MemoryAllocation;
// 记录系统内存、ACPI NVS 区域
}
// ... 处理其他类型
GET_NEXT_HOB(Hob);
}
关键点:ABL 必须在代码中完整遍历 HOB 列表,收集以下信息:
- 系统物理内存布局(
EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) - 固件卷(FV)所在地址(
EFI_HOB_TYPE_FV) - DXE 核心入口指针(
EFI_HOB_TYPE_GUID_EXTENSION+ 特定 GUID)
4.4 交互全景图
下图展示了 PEI Core 初始化后,通过 PEI 服务表、PPI 接口和 HOB 列表向 ABL 传递数据的完整流程:
4.5 常见错误与调试方法
- HobList 为 NULL:PEI Core 未正确初始化,检查 PEI 阶段启动代码;ABL 立即返回错误。
- PPI Locate 返回 EFI_NOT_FOUND:可能是 GUID 拼写错误或 PPI 未被安装;使用
NotifyPpi设置回调等待。 - HOB 遍历死循环:HOB 节点长度未对齐(必须 8 字节对齐),或
HobLength被破坏;添加长度校验。 - 内存资源 HOB 重叠:多个
EFI_HOB_TYPE_RESOURCE_DESCRIPTOR描述相同地址范围;ABL 应合并或取后者的属性。
安全提示:在 UEFI Secure Boot 环境下,HOB 中的 DXE 核心入口必须经过签名验证。ABL 在跳转前应计算哈希并与
gEfiSecurityPkgTokenSpaceGuid 等 PPI 交互确认。4.6 性能优化建议
| 优化项 | 实施方法 | 预期收益 |
|---|---|---|
| HOB 预扫描 | 一次性遍历 HOB,将常用信息存入局部结构体 | 减少后续多次 LocatePpi 调用 |
| PPI 缓存 | 使用静态数组记录已查找成功的 PPI 指针 | 避免重复 Locate,提升约 30% 查询速度 |
| 按需延迟定位 | 将非必须 PPI 在真正使用时才查找 | 减少 ABL 入口初始化时间 |