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 常用操作
HobListHOB 列表链表头指针通过 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 传递数据的完整流程:

PEI 阶段 → ABL 数据传递流程 PEI Core 初始化服务表 & PPI PEI Services Table HobList, LocatePpi … PPI 实例集 BootMode, Memory, Store HOB 链表 资源描述、FV、DXE入口 ABL 入口 解析 HOB + 调用 PPI ABL 入口通过 PEI Services 表指针间接访问 HOB 和 PPI;数据流均为只读传递,ABL 不可修改 HOB 链表

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 入口初始化时间