ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

UefiPayloadPkg 通用 Payload 构建与集成指南:EDK II UEFI Universal Payload 的 ELF/FIT 双格式全解析

2026/10/4 11:09:02 拓冰建站 浏览量
UefiPayloadPkg 通用 Payload 构建与集成指南:EDK II UEFI Universal Payload 的 ELF/FIT 双格式全解析 固件操作系统驱动开发嵌入式【免费下载链接】edk2EDK II项目地址https://gitcode.com/gh_mirrors/ed/edk2点击查看免费下载本篇技术指南以 EDK II 仓库中 UefiPayloadPkg/Readme.md 为核心骨架系统讲解 UefiPayloadPkg 为不同 Bootloadercoreboot、Slim Bootloader 等生成 UEFI 环境所用的 Universal PayloadUPL二进制格式、构建环境与命令、二进制解析工具以及 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默认与FITFlattened Image Tree。二、UEFI UniversalPayload 格式概览原文档给出了 UPL 二进制格式与交接信息HandOffPayload - HOB的支持矩阵Binary FormatHandOffPayload - HOBELFVDefaultFITV要点解读HandOffPayload - HOB无论 ELF 还是 FIT 载体UPL 都通过 HOBHand-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。该函数接收BootloaderParameterHOB 列表起始地址后先ProcessLibraryConstructorList ()完成库构造器初始化再调用BuildHobs()基于 Bootloader 的 HOB 信息重建 UPL 自己的 HOB 列表随后通过UniversalLoadDxeCore()定位 DXE Core 并调用HandOffToDxeCore()完成交接详见第六节。.upld_infoUPL 信息头由构建脚本按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 二进制中对应数据块的位置。UniversalPayloadEntryPECOFF 格式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-startRelocate DataFIT 把入口 PECOFF 的重定位表独立提取出来紧随入口镜像之后存放。构建时由UniversalPayloadBuild.py调用GenFw -e SEC生成 PECOFF再用GenFw --rebase LoadAddrDataOffset重定位到指定加载地址随后用pefile解析DIRECTORY_ENTRY_BASERELOC基线重定位表按Offset Type的 8 字节记录格式拼出reloc-start数据详见 UniversalPayloadBuild.py。Multi Binaryuefi_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 linuxFIT 格式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通常由dtcDevice Tree Compiler项目提供对应 Python 绑定。六、如何构建 UEFI UniversalPayload6.1 初始化 EDK II 构建环境按平台执行Windowsedksetup RebuildLinuxmake -C BaseTools source edksetup.sh6.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.fitpython 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_PAYLOADTRUE]-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_fvxxx.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默认0x0070Revision默认0x0000010105Identifier固定为bPLDHImageId默认UEFI、ProducerId默认INTEL。--AddFv由ValidateAddFv校验必须符合xxx_fvxxx.fv格式左侧以_fv结尾、右侧以.fv结尾且文件存在否则报错。ELF 模式下SpecRevision等字段会写入.upld_infoSectionDEBUG目标下还会把Attribute的 bit0 置 1供加载端识别构建类型。6.5 构建脚本内部流程源码级BuildUniversalPayload()UniversalPayloadBuild.py的核心逻辑分流--Fit时为 FIT 分支入口模块为FitUniversalPayloadEntry追加宏UNIVERSAL_PAYLOAD_FORMATFIT否则为 ELF 分支强制使用CLANGDWARF工具链编译入口模块UniversalPayloadEntry追加宏UNIVERSAL_PAYLOAD_FORMATELF。整包构建用build -p UefiPayloadPkg/UefiPayloadPkg.dsc -b Target -a Arch -t ToolChain构建出DXEFV.Fv、BDSFV.Fv、SECFV.Fv、NETWORKFV.Fv等 FV 产物CI 构建场景下由 CI 公共流程完成此步脚本通过--CiBuild跳过。入口模块构建单独用-m UefiPayloadPkg/UefiPayloadEntry/UniversalPayloadEntry.inf或 FIT 对应 inf编译入口得到.dll实为可重定位镜像。装配ELF 模式写入upld_info后把入口镜像拷贝为UniversalPayload.elf再依次将uefi_fv、bds_fv、sec_fv、network_fv以及--AddFv指定的附加 FV 打补丁进.upld.*SectionFIT 模式则执行 PECOFF 转换、重定位、重定位表提取最后调用MkFitImage.MakeFitImage()生成 FIT。追加 FVUniversalPayloadFullBuild()中通过ReplaceFv()把MultiFvList内每个 FV 补丁进最终镜像ELF 用ElfFv.ReplaceFvFIT 用MkFitImage.ReplaceFv且 FIT 侧 Section 名中的_会被替换为-以匹配 FDT 节点命名。七、如何 Dump Payload 二进制数据7.1 查看 UniversalPayload.elf安装 ELF dump 工具llvm-objdumpllvmorg-10.0.1llvm-objdump -h Build/UefiPayloadPkgX64/UniversalPayload.elf-h输出各 Section 的偏移、大小、对齐信息可直接对照第三节的 ELF 结构图验证upld_info、upld.uefi_fv、upld.bds_fv等 Section 是否被正确补丁。7.2 查看 UniversalPayload.fit先安装fdtdump工具Windowschocolatey 安装dtc-msys2Set-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-msys2Linuxsudo 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.cUniversalPayloadEntry.c 的_ModuleEntryPointL463-L515执行如下关键流程将BootloaderParameter赋给全局mHobList调用ProcessLibraryConstructorList ()完成各库构造器InitializeFloatingPointUnits ()初始化浮点环境以符合 UEFI 规范BuildHobs (BootloaderParameter, DxeFv)基于 Bootloader 的 HOB 列表重建 UPL 自己的 HOB 列表。BuildHobsL305 起先寻找包含 PHIT 内存区间的 Resource Descriptor HOB若不存在或空间不足则调用FindAnotherHighestBelow4GResourceDescriptor()在 4G 以下找一个满足PcdSystemMemoryUefiRegionSize最小尺寸的高位内存区间用HobConstructor重建 PHIT HOB并通过IsHobNeed()过滤掉 PHIT 与模块内存分配 HOB 后把其余 HOB 逐个AddNewHob拷贝进新列表从gUniversalPayloadExtraDataGuidHOB 读取多 FV 信息uefi_fv被识别为 DXE FV其余 FV 依次BuildFvHob构造 Memory Type Information HOB若 Bootloader 未提供、执行FixUpPcdDatabase (DxeFv)修复 UPL 内嵌 PCD 数据库的 Token 编号使其可与 Bootloader 传入的 PEI PCD 数据库拼接UniversalLoadDxeCore (DxeFv, DxeCoreEntryPoint)从 DXE FV 定位并加载 DXE Core最后HandOffToDxeCore (DxeCoreEntryPoint, Hob)交出控制权。8.2 FIT 入口FitUniversalPayloadEntry.cFitUniversalPayloadEntry.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 侧加载PayloadLoaderPeimUPL 二进制如何被宿主固件发现并装载答案是 PayloadLoaderPeim它在 PEI 阶段安装EFI_PEI_LOAD_FILE_PPIgEfiPeiLoadFilePpiGuidDXE IPL 通过该 PPI 加载 UPL。PeiLoadFileLoadPayload()L78-L212的核心步骤定位 ELFUPL 以 RAW Section 形式存放在 FFS 文件中由于对齐可能产生多个 RAW Section代码用PeiServicesFfsFindSectionData3逐实例遍历直到ParseElfImage成功解析出合法 ELF构建 UniversalPayloadBase HOB把 ELF 文件基址写入gUniversalPayloadBaseGuidHOBFIT 场景由FitPayloadLoaderPeim.c对应逻辑处理供入口模块使用收集额外 Section遍历 ELF 所有 Section统计.upld_infoUNIVERSAL_PAYLOAD_INFO_SEC_NAME与所有以.upld.为前缀的额外 Section将其Identifier、Base、Size记录进gUniversalPayloadExtraDataGuidHOB——这正是入口模块读取多 FV 列表的数据来源加载镜像若需要重定位或实际地址与首选地址不符则AllocatePages分配内存后LoadElfImage装载输出入口点信号通知注册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由宿主固件的 EDK2SEC → PEI → DXE → DXE IPL完成DXE IPL 阶段经PayloadLoaderPeim加载UniversalPayload.elf平台信息以 HOB 形式跨过“Universal Loader Interface”交给 UPLUPL 继续 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、压缩数据→ 经FMMTFirmware Module Management Tool注入BIOS.rom的 RAW 数据区。10.2 FIT 行为——Edk2boot UniversalPayload.fit启动流程与 ELF 版本等价仅加载对象变为UniversalPayload.fitPayloadLoaderPeim的 FIT 变体位于 FitPayloadLoaderPeim.c。二进制装配链路源自原文档BIOS.rom ------------------- | Other Firmware | ------------------- | ... | FMMT UniversalPayloadBuild.py --Fit tianocore ->赞分享固件操作系统驱动开发嵌入式【免费下载链接】edk2EDK II项目地址https://gitcode.com/gh_mirrors/ed/edk2点击查看免费下载相关推荐EDK II EmulatorPkg使用教程在Windows/Linux上构建UEFI模拟器EDK II EmulatorPkg使用教程在Windows/Linux上构建UEFI模拟器 引言告别硬件依赖轻松进入UEFI开发 你是否还在为UEFI开固件操作系统驱动开发嵌入式UEFI设备路径Device Path完全指南EDK II实现与应用UEFI设备路径Device Path完全指南EDK II实现与应用 引言理解UEFI设备寻址的核心机制 你是否曾在调试UEFI驱动时因设备路径解析错误固件操作系统驱动开发嵌入式UEFI固件更新工具基于EDK II的CapsuleApp开发全指南UEFI固件更新工具基于EDK II的CapsuleApp开发全指南 引言固件更新的痛点与解决方案 你是否曾面临过UEFI固件更新过程中认证失败、依赖冲突或固件操作系统驱动开发嵌入式上一篇FactoryBluePrints戴森球计划3000精选蓝图库让你的太空工厂效率提升300%下一篇Slate移动端适配终极指南在Android和iOS上完美运行富文本编辑器创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考