ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

coreboot Payload 启动机制与 FIT(uImage.FIT)加载支持全解析

coreboot Payload 启动机制与 FIT(uImage.FIT)加载支持全解析 固件嵌入式操作系统驱动开发【免费下载链接】corebootRead-only mirror of https://review.coreboot.org/coreboot.git. Synced every hour. We dont handle Pull Requests.项目地址https://gitcode.com/gh_mirrors/co/coreboot点击查看免费下载coreboot 完成硬件初始化后需要把控制权移交给一个后续程序payload例如 Linux 内核、SeaBIOS 或 U-Boot。本文以 coreboot 官方文档 Documentation/lib/payloads/index.md 为骨架结合仓库内src/lib/fit.c、src/lib/fit_payload.c以及各架构的fit_payload.c源码系统讲解 coreboot 的 payload 体系并重点深入 uImage.FITFlattened Image Tree这一原生 Linux payload 格式包括其支持架构、FIT 分区与配置选择机制、compat 字符串匹配原理、压缩算法、以及从 ITS 源文件到mkimage生成 uImage 的完整构建流程。读完本文你将能独立为基于 aarch32 / aarch64 / RISC-V 的 coreboot 主板上配置并构建一个可引导 Linux 的 FIT payload。一、什么是 Payloadcoreboot 移交控制权的最后一棒coreboot 本身只负责硬件的早期初始化romstage、ramstage引导流程的最后一步是把控制权交给一个 payload。官方文档的描述是Payloads are run after coreboot has initialized the hardware. coreboot supports multiple payloads, depending on the architecture of the selected mainboard.也就是说payload 是运行在 coreboot 完成硬件初始化之后的程序coreboot 支持哪些 payload取决于所选主板的架构。coreboot 原生的 payload 二进制格式是 SimpleELFSELF它在 CBFS 文档 中作为CBFS_TYPE_SELF0x20详细记载。从 CBFS 的角度看payload 是 CBFS 中的一个文件条目。CBFS 是 coreboot 用于在系统 ROM 中管理独立数据块的扁平、追加式归档格式详见 Documentation/lib/cbfs.md一次典型的引导过程如下/------------------------\ - flash 起始 | FMAP | describes the region layout |------------------------| | COREBOOT | --\ | /-------------------\ | | | | fallback/payload | | |-- 一个 CBFS 实例 | | fallback/ramstage | | | | | fallback/romstage | | | | | bootblock | | | | \-------------------/ | | \------------------------/ - flash 末尾其中fallback/payload就是最终要执行的 payload 文件。CBFS 中与 payload 相关的文件类型见 cbfs.md 的 File types 章节名称值内容SELF0x20一个 SELF 格式的 payloadFIT_PAYLOAD0x21一个 FIT 格式的 payload需要特别留意的是FIT_PAYLOAD一种扁平镜像树与INTEL_FIT0x54由 Intel CPU 微码消费的指针表虽然缩写相同但完全是两回事。SELF 格式回顾cbfstool 如何把 ELF 变成 payload在深入 FIT 之前先回顾 SELF 这一原生格式。静态链接的 ELF 二进制在构建时由cbfstool的add-payload命令转换为 SELF二进制被拆分为多个段每个段拥有独立的加载地址和压缩方式LZMA、LZ4、ZSTD 或 none。SELF payload 是一串段头后跟段数据定义见 cbfs.mdstruct cbfs_payload_segment { uint32_t type; uint32_t compression; uint32_t offset; uint64_t load_addr; uint32_t len; uint32_t mem_len; } __packed;段类型包括PAYLOAD_SEGMENT_CODE可执行代码、PAYLOAD_SEGMENT_DATA数据、PAYLOAD_SEGMENT_BSS清零内存和PAYLOAD_SEGMENT_ENTRY入口点。与 FIT 不同SELF 的压缩是逐段进行的而不是通过文件级COMPRESSION属性。SELF 加载器实现在 src/lib/selfboot.c它在加载前会用 bootmem 映射检查每个段因此段必须落在可用 RAM 内——SELF payload从不重定位固定加载在指定的地址地址不可用则无法引导。二、FIT面向 Linux 的扁平镜像树格式uImage.FIT 是 U-Boot 社区为 uImage payload 开发的新格式目前也是 coreboot 在 aarch32、aarch64、RISC-V 架构上引导 Linux 的推荐方式之一。它的核心思想是把内核、initrd、设备树FDT和启动配置打包进一个统一的容器由 coreboot 在引导时解析、选择合适的配置并逐项解压、落位。支持的架构FIT 解析器需要架构级支持官方文档明确列出了三个支持架构其余架构如 x86不支持aarch32aarch64riscv对应源码架构架构相关源码内核格式要求aarch32src/arch/arm/fit_payload.cImage格式 FDTaarch64src/arch/arm64/fit_payload.cImage格式 FDTRISC-Vsrc/arch/riscv/fit_payload.cImage格式 FDT三个架构有一个共同点内核必须是Image格式即 ARM/RISC-V 内核的扁平镜像格式而非压缩的 zImage并且都必须携带设备树fdt才能引导。从源码看RISC-V 侧src/arch/riscv/fit_payload.c甚至会在缺失 FDT 时直接报错if (!config-fdt || !fdt) { printk(BIOS_CRIT, Providing a valid FDT is mandatory to boot a RISC-V kernel!\n); return false; }FIT 包含的分区sections一个 FIT 镜像可以包含多个分区每个分区持有唯一的内核、initrd 或配置。引导时根据匹配的配置从众多分区中挑出一个内核和一个 initrd。分区必须按以下名称命名FIT 解析器才能找到它们见 fit.mdkernelfdtramdisk在源码层面src/lib/fit.c的config_node()正是按属性名查找这些分区配置节点中的kernel、fdt、ramdisk属性分别通过find_image()关联到/images下的同名节点。此外fdt属性还支持通过空格分隔的多个名称指定FDT overlay 链find_image_with_overlays()coreboot 会把 overlay 依次应用到基础 FDT 上见 src/lib/fit_payload.c 的list_for_each(chain, config-overlays, ...)循环。三、配置选择与 Compat 字符串匹配机制FIT 的核心价值在于一份镜像、多种配置同一个 uImage 里可以存放面向多个板卡的 FDT/内核组合引导时按 compat 字符串自动选择。默认 compat 字符串FIT 配置条目中的compatible属性用于寻找匹配的配置。coreboot 会调用主板特有的函数获取 BOARDID 和 SKUIDuint32_t board_id(void); uint32_t sku_id(void);默认情况下src/lib/fit.c 的fit_add_default_compat_strings()会依次注册如下 compat 字符串优先级从低到高CONFIG_MAINBOARD_VENDOR,CONFIG_MAINBOARD_PART_NUMBERCONFIG_MAINBOARD_VENDOR,CONFIG_MAINBOARD_PART_NUMBER-revBOARDIDCONFIG_MAINBOARD_VENDOR,CONFIG_MAINBOARD_PART_NUMBER-revBOARDID-skuSKUIDCONFIG_MAINBOARD_VENDOR,CONFIG_MAINBOARD_PART_NUMBER-skuSKUID源码中同样会注册例如文档给出的示例cavium,cn8100_sff_evb如果board_id()或sku_id()返回无效值对应那一条 compat 字符串就不会被添加源码中通过UNDEFINED_STRAPPING_ID判断。需要指出的是先注册的字符串优先级更高见 src/include/fit.h 中fit_add_compat_string()的注释Strings that were added first have a higher priority on finding a match。默认字符串注册顺序为vendor,part-revX-skuY→vendor,part-revX→vendor,part-skuY→vendor,part因此最精确的revsku组合拥有最高优先级匹配到后即被选中。自定义 compat 字符串主板可以在板级代码中调用以下函数追加自定义 compat 字符串fit_add_compat_string(const char *str);该函数src/lib/fit.c会保存字符串的副本并通过clean_compat_string()将其转为小写、并把_和空格替换为-随后插入 compat 列表。这解释了为什么默认字符串中的CONFIG_MAINBOARD_VENDOR、CONFIG_MAINBOARD_PART_NUMBER配置值会被规范化成类似cavium,cn8100_sff_evb的小写连字符形式。匹配与回退逻辑fit_load()src/lib/fit.c是整套选择逻辑的入口用fdt_unflatten()解包整个 FIT 镜像遍历/images下的所有镜像节点、/configurations下的所有配置节点跳过缺少 kernel、缺少 fdt、或 ramdisk 压缩算法不支持的配置对每个配置计算其compatible属性与本地 compat 列表的匹配位置和优先级选优顺序有 compat 匹配的配置 匹配优先级最高者 显式指定的 default 配置若既无匹配也无 default 配置则报错放弃No compatible or default configs. Giving up.。文档特别说明如果没有找到任何匹配的 compat 字符串就选择 default 配置由configurations节点的default属性指定。fit_load()解析完成后fit_payload()src/lib/fit_payload.c负责真正装载解包 FDT、应用 overlay 与 fixup、把 coreboot 表/CBMEM 信息add_cb_fdt_data()写入设备树firmware/coreboot节点、根据CONFIG_LINUX_COMMAND_LINE写入chosen/bootargs、调用架构相关的fit_payload_arch()摆放内核/FDT/initrd最后通过extract()解压并跳转。四、支持的压缩算法与 CBFS 集成约束关于压缩官方文档有一项重要约束The FIT image has to be included uncompressed into the CBFS. The sections inside the FIT image can use different compression schemes.即FIT 镜像本身必须以未压缩形式放进 CBFS这也是 cbfs.md 所述 FIT payload does not support whole-file compression 的原因但 FIT内部的各个分区可以各自采用不同的压缩方案。支持三种LZMALZ4none源码验证src/lib/fit.c的image_node()只识别这三种compression属性值none、lzma、lz4其它值一律置为-1表示不支持src/lib/fit_payload.c的extract()也仅实现了CBFS_COMPRESS_NONEmemcpy 搬运、CBFS_COMPRESS_LZMAulzman与CBFS_COMPRESS_LZ4ulz4fn三分支其它压缩直接报 Unsupported compression。这与 CBFS 文件级支持的四种算法none/LZMA/LZ4/Zstandard不同——FIT 分区不支持 Zstandard。五、各架构的装载细节内核、FDT、initrd 如何落位fit_payload_arch()是架构相关代码的唯一入口声明于 src/include/fit.h负责把内核、FDT、initrd 摆放到内存中合适的可用 RAM 区域并设置入口点。各架构的算法均由bootmem_walk()遍历 bootmem 映射中的可用 RAM 完成摆放结果同时用bootmem_add_range(..., BM_MEM_PAYLOAD)标记为预留避免后续分配冲突。aarch64src/arch/arm64/fit_payload.c 的实现最精细先做部分解压读取内核 Image 头decompress_kernel_header()校验魔数0x644d5241ARM64_IMAGE_MAGIC并获取text_offset与image_size内核的摆放遵循 ARM64 规范放在距 2 MiB 对齐基址偏移text_offset处若内核头未设置image_size则按文档约定回退为text_offset 0x80000并在内核后预留最多MAX_KERNEL_SIZE64 MiB自由空间get_kernel_size()内核入口通过prog_set_entry(payload, (void *)kernel-offset, arg)设置arg是 FDT 地址——内核期望 FDT 作为首个参数initrd 与 FDT 的位置取决于内核头 flags 的 bit 3place_anywhere4.6 之前的内核无法使用 Image 物理偏移之下的内存此时 initrd/FDT 必须放在内核之后。aarch32src/arch/arm/fit_payload.c 相对简单内核放在_dram 32 MiB处避开解压时需重定位的区域FDT 放在_dram 128 MiB以上防止被内核解压器覆盖initrd 紧随 FDT 之后所有区域按 1 MiB 对齐Linux 4.15 doesnt like 4KiB alignment. Align to 1 MiB for now.。RISC-Vsrc/arch/riscv/fit_payload.c 实现的是兼容 Berkeley Boot LoaderBBL的加载方式内核放在按SECTION_ALIGN对齐的可用内存RV64 为 2 MiBRV32 为 4 MiBFDT 与 initrd 依次排在内核之后入口参数同样是 FDT 地址。结合 src/arch/riscv/boot.c 可以看到RISC-V 上 payload 与 coreboot 表的交接方式也与众不同coreboot 会把 coreboot 表指针以coreboot-table属性嵌入 FDT 的/chosen节点且调用约定在 HARTID、FDT 之后coreboot 表作为第三个参数传入这与 CBFS 文档中关于 SELF 调用约定的特例说明一致。六、构建 FIT 镜像从 ITS 到 uImageFIT 镜像必须通过 U-Boot 的mkimage工具构建。官方文档 fit.md 给出了完整的示例配置下面完整继承并补充注释/* * Simple U-Boot uImage source file containing a single kernel and FDT blob */ /dts-v1/; / { description Simple image with single Linux kernel and FDT blob; #address-cells 1; images { kernel { description Vanilla Linux kernel; data /incbin/(Image.lzma); /* 内核数据LZMA 压缩的 Image */ type kernel; arch arm64; /* 目标架构arm64 */ os linux; compression lzma; /* 该分区压缩算法lzma / lz4 / none */ load 0x80000; /* 建议加载地址aarch64 惯例 */ entry 0x80000; /* 入口地址 */ hash-1 { algo crc32; }; }; fdt-1 { description Flattened Device Tree blob; data /incbin/(target.dtb); /* 设备树数据 */ type flat_dt; arch arm64; compression none; /* FDT 通常不压缩 */ hash-1 { algo crc32; }; }; ramdisk-1 { description Compressed Initramfs; data /incbin/(initramfs.cpio.xz); /* 由 Linux 内核自行解压的 initrd */ type ramdisk; arch arm64; os linux; compression none; /* 此处不用 FIT 解压交给内核 */ load 00000000; entry 00000000; hash-1 { algo sha1; }; }; }; configurations { default conf-1; /* 无 compat 匹配时的回退配置 */ conf-1 { description Boot Linux kernel with FDT blob; kernel kernel; /* 引用 /images 下的分区名 */ fdt fdt-1; ramdisk ramdisk-1; /* 可在此添加 compatible vendor,board; 以便按 compat 精确匹配 */ }; }; };将这个文件保存为 ITS 源文件config.its并与以下文件放在同一目录target.dtb—— 设备树二进制initramfs.cpio.xz—— 压缩的 initramfsImage.lzma—— LZMA 压缩的 ARM64/ARM/RISC-V 内核 Image然后执行mkimage -f config.its uImage生成的uImage中initramfs.cpio.xz会在引导后由 Linux 内核负责解压Image.lzma由 coreboot 的 FIT 加载器extract()中的ulzman解压target.dtb以未压缩形式随镜像交付。最终把uImage作为 FIT payload 加入 CBFS 即可CBFS 文件类型为FIT_PAYLOAD0x21。七、接入 coreboot 构建系统Kconfig 配置要点从 payloads/Kconfig 可以看到 FIT payload 的配置入口PAYLOAD_FIT布尔选项 A FIT payload选中后自动select PAYLOAD_FIT_SUPPORTPAYLOAD_FILE指定 payload 文件路径当PAYLOAD_FIT时默认值为uImage压缩默认值FIT 支持时默认COMPRESSED_PAYLOAD_NONE与FIT 镜像必须以未压缩形式放入 CBFS的约束呼应非 FIT 时默认COMPRESSED_PAYLOAD_LZMA。构建时相关文件按架构纳入编译例如 src/arch/arm64/Makefile.mk 中的ramstage-$(CONFIG_PAYLOAD_FIT_SUPPORT) fit_payload.c即只有开启 FIT 支持时才把fit_payload.c编入 ramstage。RISC-V 侧src/arch/riscv/Makefile.mk则无条件编入。引导时各架构通过prog_cbfs_type(prog) CBFS_TYPE_FIT_PAYLOAD判断 payload 类型如 src/arch/arm/boot.c 在 ramstage 中调用boot_linux()直接跳转内核说明 FIT payload 只在 ramstage 阶段执行。八、调试与排查建议结合源码中的日志输出printk排查 FIT 引导问题时可关注以下节点无法选择配置fit_load()会打印每个配置的 compat 及匹配情况FIT: config xxx, kernel yyy, fdt zzz (match)最终若输出No compatible or default configs. Giving up.说明 ITS 中既无匹配的compatible也没有default配置找不到镜像Cannot find image node xxx!表示配置引用的分区名与/images下的节点名不一致注意区分kernel与kernel1这类命名不支持压缩Unsupported compression或Ramdisk is compressed with an unsupported algorithm提示分区压缩算法不在 LZMA/LZ4/none 之列ZSTD 不支持内存摆放失败Failed to find free memory region并伴随bootmem_dump_ranges()输出的内存映射说明内核/FDT/initrd 找不到足够的可用 RAM例如与 ramstage 重叠FDT 缺失RISC-V 下Providing a valid FDT is mandatory to boot a RISC-V kernel!表明配置未提供 fdt 分区。总结coreboot 的 payload 机制以 SELF 为原生格式同时为 aarch32、aarch64、RISC-V 架构提供了完善的 uImage.FIT 支持FIT 解析器src/lib/fit.c负责解包镜像并依据 compat 字符串或 default 配置选定引导配置架构相关的 fit_payload.c 负责按各架构规范把内核、FDT、initrd 摆放到安全的可用内存最终跳转内核。实践中只需按本文第六节的 ITS 模板准备Image、target.dtb、initramfs.cpio.xz用mkimage -f config.its uImage生成镜像并通过PAYLOAD_FIT配置接入构建即可完成一个由 coreboot 直接引导 Linux 的 FIT payload 方案。赞分享固件嵌入式操作系统驱动开发【免费下载链接】corebootRead-only mirror of https://review.coreboot.org/coreboot.git. Synced every hour. We dont handle Pull Requests.项目地址https://gitcode.com/gh_mirrors/co/coreboot点击查看免费下载相关推荐coreboot 的 Flattened uImage TreeFITPayload 引导机制与 uImage 构建实战指南coreboot 的 Flattened uImage TreeFITPayload 引导机制与 uImage 构建实战指南 本指南以仓库内 Documen固件嵌入式操作系统驱动开发coreboot 支持 vbootVerified Boot的设备清单与启用机制深度解析coreboot 支持 vbootVerified Boot的设备清单与启用机制深度解析 导读 本文面向 coreboot 开发者与固件安全研究人员系统梳固件嵌入式操作系统驱动开发Atmosphere重启机制全解析warmboot payload与冷启动流程Atmosphere重启机制全解析warmboot payload与冷启动流程 概述 Atmosphere作为Nintendo Switch的定制固件其重启固件操作系统嵌入式系统编程上一篇mtkclient项目Realme C3设备Bootloop问题分析与解决指南下一篇Web Audio API实战PitchDetect中的音频流处理与分析技术创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表