☰
UefiPayloadPkg 通用 Payload 构建与集成指南:EDK II UEFI Universal Payload 的 ELF/FIT 双格式全解析
2026/10/4 11:08:54 网站建设 项目流程
  • 固件
  • 操作系统
  • 驱动开发
  • 嵌入式

【免费下载链接】edk2

EDK II

项目地址:https://gitcode.com/gh_mirrors/ed/edk2
点击查看免费下载

本篇技术指南以 EDK II 仓库中 UefiPayloadPkg/Readme.md 为核心骨架,系统讲解 UefiPayloadPkg 为不同 Bootloader(coreboot、Slim Bootloader 等)生成 UEFI 环境所用的 Universal Payload(UPL)二进制格式、构建环境与命令、二进制解析工具,以及 Edk2boot 集成 UPL 时的压缩与签名流程。读者读完本文后,将能够独立完成UniversalPayload.elf与UniversalPayload.fit的构建、参数定制、数据 dump,并理解 UPL 从 Bootloader 参数(HOB 列表)到 DXE Core 交接的完整底层机制。

一、Universal Payload 与 UefiPayloadPkg 概述

UefiPayloadPkg 的目标是:为不同的 Bootloader 提供一个 UEFI Universal Payload,使其能够生成完整的 EFI 环境。这意味着 UPL 不是一套完整的固件(不需要自己的 SEC/PEI 阶段),而是由宿主 Bootloader(如 coreboot、Slim Bootloader、Edk2boot)在完成平台初始化后,将系统控制权交给的一个独立二进制——它自带 DXE 阶段与 DXE 驱动,接管后继续完成 UEFI 启动流程直至操作系统加载。

该设计遵循 Universal Payload 开放规范(通用规范定义了 UPL 二进制应包含的upld_info信息头、多 Firmware Volume 打包方式等约定),其二进制载体支持两种格式:ELF(默认)与FIT(Flattened Image Tree)。

二、UEFI UniversalPayload 格式概览

原文档给出了 UPL 二进制格式与交接信息(HandOffPayload - HOB)的支持矩阵:

Binary FormatHandOffPayload - HOB
ELFV(Default)
FITV

要点解读:

  • HandOffPayload - HOB:无论 ELF 还是 FIT 载体,UPL 都通过 HOB(Hand-Off Block)列表接收 Bootloader 传递的内存布局、启动模式等平台信息,这是 UPL 入口模块的_ModuleEntryPoint (IN UINTN BootloaderParameter)参数,即 Bootloader 参数块的起始内存地址。
  • ELF 为默认格式:仅需 EDK II 常规工具链即可构建;FIT 格式则额外依赖 Python 的pefile与pylibfdt库来生成 FIT 头与重定位数据。

三、ELF 二进制格式详解

ELF 格式的 UPL 镜像布局如下(源自原文档):

+ +-----------------------+ | | UniversalPayloadEntry | <----------- UefiPayloadPkg\UefiPayloadEntry\UniversalPayloadEntry.c:_ModuleEntryPoint (HOB) | +-----------------------+ | | .upld_info | patch it directly ELF Format | +-----------------------+ | | .upld.uefi_fv | patch it directly | +-----------------------+ | | .upld.bds_fv | patch it directly | +-----------------------+ | | .upld.<afpx>_fv | patch it directly + +-----------------------+

各组成部分说明:

  • UniversalPayloadEntry 段:存放 UPL 入口模块,即 UefiPayloadEntry/UniversalPayloadEntry.c 中的_ModuleEntryPoint。该函数接收BootloaderParameter(HOB 列表起始地址)后,先ProcessLibraryConstructorList ()完成库构造器初始化,再调用BuildHobs()基于 Bootloader 的 HOB 信息重建 UPL 自己的 HOB 列表,随后通过UniversalLoadDxeCore()定位 DXE Core 并调用HandOffToDxeCore()完成交接(详见第六节)。
  • .upld_info:UPL 信息头,由构建脚本按UPLD_INFO_HEADER结构生成后“直接打补丁”(patch it directly)写入 ELF 的该 Section。
  • .upld.uefi_fv / .upld.bds_fv / .upld. _fv:分别存放 DXE FV、BDS FV 以及各附加功能包 FV(如sec_fv、network_fv等),构建完成后同样被“直接打补丁”进对应 Section。

从源码看,“patch it directly”的落地实现位于 Tools/ElfFv.py:该脚本自行解析 ELF 文件头、程序头表与节头表(ElfHeader64/ElfHeader32、ElfSectionHeader64/ElfSectionHeader32等类),然后通过ReplaceFv()将指定二进制内容替换进以.upld.<名字>命名的 Section——这正是UniversalPayloadBuild.py中ElfFv.ReplaceFv (EntryOutputDir, UpldInfoFile, '.upld_info', Alignment = 4)与后续逐 FV 打补丁的底层支撑。

四、FIT 二进制格式详解

FIT 格式的 UPL 镜像布局如下(源自原文档):

+ +-----------------------+ FIT Data | | FIT Header | <----------- Generate by pylibfdt + +-----------------------+ PECOFF Format | | UniversalPayloadEntry | <----------- UefiPayloadPkg\UefiPayloadEntry\FitUniversalPayloadEntry.c:_ModuleEntryPoint (HOB) + +-----------------------+ Relocate Data | | reloc-start | + +-----------------------+ | | uefi_fv | patch it directly + +-----------------------+ Multi Binary | | bds_fv | patch it directly + +-----------------------+ | | afp_xxx_fv | patch it directly + +-----------------------+ | | afp_xxx_fv | patch it directly + +-----------------------+

各部分说明:

  • FIT Header:由pylibfdt(即 Python 绑定后的 libfdt)生成,实现位于 Tools/MkFitImage.py。脚本通过libfdt.fdt_create_empty_tree()创建空设备树,然后构建images { tianocore {...}; uefi-fv {...}; bds-fv {...}; afp1-fv {...}; ... }与configurations { conf-1 {...} }节点;其中tianocore节点携带load、entry-start、reloc-start、data-offset、data-size等属性,各 FV 节点携带data-offset/data-size指向 FIT 二进制中对应数据块的位置。
  • UniversalPayloadEntry(PECOFF 格式):FIT 中的 UPL 入口不再是 ELF,而是 PECOFF 格式的入口镜像,对应 UefiPayloadEntry/FitUniversalPayloadEntry.c 的_ModuleEntryPoint。它通过BuildFitLoadablesFvHob()解析 FIT 内的 FDT:先定位configurations/conf-1与images/tianocore节点,再遍历所有以fv结尾的子节点,读取data-offset/data-size属性,把uefi-fv识别为 DXE FV,其余 FV 依次通过BuildFvHob()注册(见 FitUniversalPayloadEntry.c)。
  • reloc-start(Relocate Data):FIT 把入口 PECOFF 的重定位表独立提取出来,紧随入口镜像之后存放。构建时由UniversalPayloadBuild.py调用GenFw -e SEC生成 PECOFF,再用GenFw --rebase <LoadAddr+DataOffset>重定位到指定加载地址,随后用pefile解析DIRECTORY_ENTRY_BASERELOC基线重定位表,按Offset + Type的 8 字节记录格式拼出reloc-start数据(详见 UniversalPayloadBuild.py)。
  • Multi Binary(uefi_fv / bds_fv / afp_xxx_fv):多个 FV 以“多二进制”形式顺序拼接在 FIT 尾部,与 FDT 中各节点data-offset一一对应。

五、构建环境准备

原文档对两类格式的构建环境要求如下:

ELF 格式:

Install GCC compiler on linux and MSVC compiler on windows Install CLANG compiler (llvmorg-10.0.1) on windows and linux

FIT 格式:

Install GCC compiler on linux and MSVC compiler on windows pip3 install pefile pip3 install pylibfdt

补充说明:

  • ELF 构建要求安装CLANG 编译器,因为构建脚本对 ELF 载体强制使用CLANGDWARF工具链编译 UPL 入口模块(见下节源码分析);Windows 上同时需要 MSVC 编译器、Linux 上需要 GCC 作为整体固件/其余模块的编译工具。
  • FIT 构建额外要求 Python 包pefile(用于读取 PECOFF 重定位表)与pylibfdt(用于生成 FIT 头/FDT 节点);Linux 下pylibfdt通常由dtc(Device Tree Compiler)项目提供对应 Python 绑定。

六、如何构建 UEFI UniversalPayload

6.1 初始化 EDK II 构建环境

按平台执行:

  • Windows:edksetup Rebuild
  • Linux:
    make -C BaseTools source edksetup.sh

6.2 构建 UniversalPayload.elf(默认)

python UefiPayloadPkg/UniversalPayloadBuild.py -t <TOOL_CHAIN_TAG> llvm-objdump -h Build/UefiPayloadPkgX64/UniversalPayload.elf

-t <TOOL_CHAIN_TAG>是必填的工具链标识(如GCC5、CLANGDWARF等,需与宿主机已安装编译器对应)。构建成功后,产物位于Build/UefiPayloadPkgX64/UniversalPayload.elf,第二条命令用于查看其 Section 布局(即上一节的 ELF 结构图)。

6.3 构建 UniversalPayload.fit

python UefiPayloadPkg/UniversalPayloadBuild.py -t <TOOL_CHAIN_TAG> --Fit

加--Fit(等价于脚本参数-f/--Fit)后,产物为Build/UefiPayloadPkgX64/UniversalPayload.fit。

6.4 构建脚本参数全景

UniversalPayloadBuild.py 通过 argparse 暴露了丰富的定制参数(见 InitArgumentParser),原文档未逐一列出的关键参数如下:

参数说明默认值
-t, --ToolChain工具链标签(必填)无
-b, --Target构建目标DEBUG(可选RELEASE/NOOPT)
-a, --Arch入口模块架构X64(可选IA32/RISCV64/AARCH64)
-D, --Macro额外宏定义,可多次指定["UNIVERSAL_PAYLOAD=TRUE"]
-i, --ImageIdPayload ID(最长 16 字节)UEFI
-s, --SpecRevision规范版本号,BCD 格式Major.Minor0.7
-r, --RevisionPayload 二进制修订号Major.Minor.Revision.Build0x0000010105
-o, --ProducerId生产者标识(最长 16 字节,可含 OEM 字符串)INTEL
-p, --pcd追加 PCD 定义,可多次指定无
-e, --BuildEntryOnly只构建 UniversalPayload Entry 模块关闭
-pb, --PreBuildUplBinary指定已构建的 UPL 二进制(跳过入口构建)无
-sk, --SkipBuild跳过 UPL 构建(仅打补丁)关闭
-af, --AddFv添加/替换 FV,格式xxx_fv=xxx.fv,可多次指定无
-f, --Fit构建为UniversalPayload.fit关闭
-l, --LoadAddressPayload 加载地址(FIT 重定位基址)0x000800000
-c, --DscPathDSC 文件路径UefiPayloadPkg/UefiPayloadPkg.dsc
-ac, --add_cc_flags追加 CC 编译标志无
-ci, --CiBuild是否由 edk2 CI 构建流程调用关闭

源码中的字段校验规则值得注意:

  • --SpecRevision由ValidateSpecRevision校验,格式必须为Major.Minor(各 8 位,支持十六进制),最终编码为0x{Major}{Minor};UPLD_INFO_HEADER 结构中SpecRevision默认0x0070,Revision默认0x0000010105,Identifier固定为b'PLDH',ImageId默认UEFI、ProducerId默认INTEL。
  • --AddFv由ValidateAddFv校验,必须符合xxx_fv=xxx.fv格式(左侧以_fv结尾、右侧以.fv结尾且文件存在),否则报错。
  • ELF 模式下SpecRevision等字段会写入.upld_infoSection;DEBUG目标下还会把Attribute的 bit0 置 1,供加载端识别构建类型。

6.5 构建脚本内部流程(源码级)

BuildUniversalPayload()(UniversalPayloadBuild.py)的核心逻辑:

  1. 分流:--Fit时为 FIT 分支,入口模块为FitUniversalPayloadEntry,追加宏UNIVERSAL_PAYLOAD_FORMAT=FIT;否则为 ELF 分支,强制使用CLANGDWARF工具链编译入口模块UniversalPayloadEntry,追加宏UNIVERSAL_PAYLOAD_FORMAT=ELF。
  2. 整包构建:用build -p UefiPayloadPkg/UefiPayloadPkg.dsc -b <Target> -a <Arch> -t <ToolChain>构建出DXEFV.Fv、BDSFV.Fv、SECFV.Fv、NETWORKFV.Fv等 FV 产物(CI 构建场景下由 CI 公共流程完成此步,脚本通过--CiBuild跳过)。
  3. 入口模块构建:单独用-m UefiPayloadPkg/UefiPayloadEntry/UniversalPayloadEntry.inf(或 FIT 对应 inf)编译入口,得到.dll(实为可重定位镜像)。
  4. 装配:ELF 模式写入upld_info后,把入口镜像拷贝为UniversalPayload.elf,再依次将uefi_fv、bds_fv、sec_fv、network_fv以及--AddFv指定的附加 FV 打补丁进.upld.*Section;FIT 模式则执行 PECOFF 转换、重定位、重定位表提取,最后调用MkFitImage.MakeFitImage()生成 FIT。
  5. 追加 FV:UniversalPayloadFullBuild()中通过ReplaceFv()把MultiFvList内每个 FV 补丁进最终镜像(ELF 用ElfFv.ReplaceFv,FIT 用MkFitImage.ReplaceFv,且 FIT 侧 Section 名中的_会被替换为-以匹配 FDT 节点命名)。

七、如何 Dump Payload 二进制数据

7.1 查看 UniversalPayload.elf

安装 ELF dump 工具(llvm-objdump,llvmorg-10.0.1):

llvm-objdump -h Build/UefiPayloadPkgX64/UniversalPayload.elf

-h输出各 Section 的偏移、大小、对齐信息,可直接对照第三节的 ELF 结构图验证upld_info、upld.uefi_fv、upld.bds_fv等 Section 是否被正确补丁。

7.2 查看 UniversalPayload.fit

先安装fdtdump工具:

  • Windows(chocolatey 安装dtc-msys2):
    Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1')) choco install dtc-msys2
  • Linux:
    sudo apt install -y u-boot-tools

然后执行:

fdtdump Build/UefiPayloadPkgX64/UniversalPayload.fit

输出为 FIT 内的完整 FDT 树,可看到images/tianocore(含load、entry-start、reloc-start)、images/uefi-fv、images/bds-fv、images/afp1-fv等节点及其data-offset/data-size,与第四节结构图一一对应。

八、UPL 入口模块:从 HOB 到 DXE Core 的交接

8.1 ELF 入口:UniversalPayloadEntry.c

UniversalPayloadEntry.c 的_ModuleEntryPoint(L463-L515)执行如下关键流程:

  1. 将BootloaderParameter赋给全局mHobList,调用ProcessLibraryConstructorList ()完成各库构造器;
  2. InitializeFloatingPointUnits ()初始化浮点环境以符合 UEFI 规范;
  3. BuildHobs (BootloaderParameter, &DxeFv)基于 Bootloader 的 HOB 列表重建 UPL 自己的 HOB 列表。BuildHobs(L305 起)先寻找包含 PHIT 内存区间的 Resource Descriptor HOB,若不存在或空间不足,则调用FindAnotherHighestBelow4GResourceDescriptor()在 4G 以下找一个满足PcdSystemMemoryUefiRegionSize最小尺寸的高位内存区间,用HobConstructor重建 PHIT HOB,并通过IsHobNeed()过滤掉 PHIT 与模块内存分配 HOB 后,把其余 HOB 逐个AddNewHob拷贝进新列表;
  4. 从gUniversalPayloadExtraDataGuidHOB 读取多 FV 信息,uefi_fv被识别为 DXE FV,其余 FV 依次BuildFvHob;
  5. 构造 Memory Type Information HOB(若 Bootloader 未提供)、执行FixUpPcdDatabase (DxeFv)修复 UPL 内嵌 PCD 数据库的 Token 编号(使其可与 Bootloader 传入的 PEI PCD 数据库拼接);
  6. UniversalLoadDxeCore (DxeFv, &DxeCoreEntryPoint)从 DXE FV 定位并加载 DXE Core,最后HandOffToDxeCore (DxeCoreEntryPoint, Hob)交出控制权。

8.2 FIT 入口:FitUniversalPayloadEntry.c

FitUniversalPayloadEntry.c 的入口流程与 ELF 版本一致,区别在于 DXE FV 的定位方式:通过BuildFitLoadablesFvHob()(L220-L310)从gUniversalPayloadBaseGuidHOB 中取出PayloadBase->Entry(即 FIT 在内存中的基址),再解析 FIT 的 FDT 树:

  • 定位configurations/conf-1与images/tianocore节点;
  • 遍历tianocore之后所有以fv结尾的兄弟节点,读取data-offset与data-size(注意大小端转换SwapBytes32);
  • 名字为uefi-fv的节点对应 DXE FV(*DxeFv = Entry + DataOffset),其余节点依次BuildFvHob (Entry + DataOffset, DataSize)。

两个入口模块都声明为MODULE_TYPE = SEC(见 UefiPayloadEntry.inf 与 FitUniversalPayloadEntry.inf),这也是 FIT 构建时用GenFw -e SEC生成 PECOFF 的原因。

九、Bootloader 侧加载:PayloadLoaderPeim

UPL 二进制如何被宿主固件发现并装载?答案是 PayloadLoaderPeim:它在 PEI 阶段安装EFI_PEI_LOAD_FILE_PPI(gEfiPeiLoadFilePpiGuid),DXE IPL 通过该 PPI 加载 UPL。PeiLoadFileLoadPayload()(L78-L212)的核心步骤:

  1. 定位 ELF:UPL 以 RAW Section 形式存放在 FFS 文件中,由于对齐可能产生多个 RAW Section,代码用PeiServicesFfsFindSectionData3逐实例遍历,直到ParseElfImage成功解析出合法 ELF;
  2. 构建 UniversalPayloadBase HOB:把 ELF 文件基址写入gUniversalPayloadBaseGuidHOB(FIT 场景由FitPayloadLoaderPeim.c对应逻辑处理),供入口模块使用;
  3. 收集额外 Section:遍历 ELF 所有 Section,统计.upld_info(UNIVERSAL_PAYLOAD_INFO_SEC_NAME)与所有以.upld.为前缀的额外 Section,将其Identifier、Base、Size记录进gUniversalPayloadExtraDataGuidHOB——这正是入口模块读取多 FV 列表的数据来源;
  4. 加载镜像:若需要重定位或实际地址与首选地址不符,则AllocatePages分配内存后LoadElfImage装载,输出入口点;
  5. 信号通知:注册gEfiEndOfPeiSignalPpiGuid回调,在 End of PEI 时安装gUplReadyToPayloadPpiGuidPPI,向后续阶段通告“已就绪可进入 Payload”。

十、Edk2boot + UefiUniversalPayload:压缩与签名集成

原文档指出:ELF 与 FIT 两种 Edk2boot 集成方式,均通过下述工具链实现 UPL 的压缩与签名。

10.1 ELF 行为——Edk2boot + UniversalPayload.elf

启动流程与二进制装配如下(源自原文档):

Boot Flow +-------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------+-------------------+ | Platform Init | Universal Loader Interface | OS | +-------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------+-------------------+ HOBs SEC -> PEI -> DXE -> DXE IPL -> UefiPayloadPkg\PayloadLoaderPeim\PayloadLoaderPeim.c ------------------------------------------------------------------------------------> Load UniversalPayload.elf -> Operation System

其中Platform Init由宿主固件的 EDK2(SEC → PEI → DXE → DXE IPL)完成,DXE IPL 阶段经PayloadLoaderPeim加载UniversalPayload.elf;平台信息以 HOB 形式跨过“Universal Loader Interface”交给 UPL,UPL 继续 DXE 阶段直至启动 OS。

二进制装配链路(右侧为 UPL 侧、左侧为固件侧):

BIOS.rom +-------------------+ | Other Firmware | +-------------------+ | ... | FMMT UniversalPayloadBuild.py +-------------------+<----------------+-----------------------+ GenFfs +-----------------------+ Rsa2048Sha256 Sign +-----------------------+ LzmaCompress +----------------------+ GenSec +--------------------------------+ | | | EDK2 FFS Header |<-----------| Rsa2048Sha256 Hash |<--------------------| UniversalPayload.lzma |<--------------| EDK2 SEC Header |<--------| UniversalPayload.elf | | RAW Data | +-----------------------+ +-----------------------+ +-----------------------+ +----------------------+ +--------------------------------+ | | | Rsa2048Sha256 Hash | | UniversalPayload.lzma | | UniversalPayload.elf | | upld_info | | | +-----------------------+ +-----------------------+ +----------------------+ +--------------------------------+ | | | UniversalPayload.lzma | | upld_info | | upld.uefi_fv | +-------------------+<----------------+-----------------------+ +----------------------+ +--------------------------------+ | ... | | upld.uefi_fv | | upld.bds_fv | +-------------------+ +----------------------+ +--------------------------------+ | Other Firmware | | upld.bds_fv | | upld.AFP1 | +-------------------+ +----------------------+ +--------------------------------+ | upld.AFP1 | | upld.AFP2 | +----------------------+ +--------------------------------+ | upld.AFP2 | | ... | +----------------------+ +--------------------------------+ | ... | | upld.AFPn | +----------------------+ +--------------------------------+ | upld.AFPn | +----------------------+

处理链条自左向右:UniversalPayloadBuild.py产出含upld_info、upld.uefi_fv、upld.bds_fv、upld.AFP1..n各 Section 的UniversalPayload.elf→GenSec包上 EDK2 SEC Header →LzmaCompress压缩为UniversalPayload.lzma→Rsa2048Sha256 Sign签名并生成Rsa2048Sha256 Hash→GenFfs封装成 EDK2 FFS 文件(含 FFS Header、Hash、压缩数据)→ 经FMMT(Firmware Module Management Tool)注入BIOS.rom的 RAW 数据区。

10.2 FIT 行为——Edk2boot + UniversalPayload.fit

启动流程与 ELF 版本等价,仅加载对象变为UniversalPayload.fit(PayloadLoaderPeim的 FIT 变体位于 FitPayloadLoaderPeim.c)。二进制装配链路(源自原文档):

BIOS.rom +-------------------+ | Other Firmware | +-------------------+ | ... | FMMT UniversalPayloadBuild.py --Fit tianocore ->>
  • 固件
  • 操作系统
  • 驱动开发
  • 嵌入式

【免费下载链接】edk2

EDK II

项目地址:https://gitcode.com/gh_mirrors/ed/edk2
点击查看免费下载

相关推荐

上一篇:FactoryBluePrints:戴森球计划3000+精选蓝图库,让你的太空工厂效率提升300%
下一篇:Slate移动端适配终极指南:在Android和iOS上完美运行富文本编辑器

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询