機(jī)制與 FIT(uImage.FIT)加載支持全解析)
固件嵌入式操作系統(tǒng)驅(qū)動(dòng)開發(fā)【免費(fèi)下載鏈接】corebootRead-only mirror of https://review.coreboot.org/coreboot.git. Synced every hour. We dont handle Pull Requests.項(xiàng)目地址https://gitcode.com/gh_mirrors/co/coreboot點(diǎn)擊查看免費(fèi)下載coreboot 完成硬件初始化后需要把控制權(quán)移交給一個(gè)后續(xù)程序payload例如 Linux 內(nèi)核、SeaBIOS 或 U-Boot。本文以 coreboot 官方文檔 Documentation/lib/payloads/index.md 為骨架結(jié)合倉庫內(nèi)src/lib/fit.c、src/lib/fit_payload.c以及各架構(gòu)的fit_payload.c源碼系統(tǒng)講解 coreboot 的 payload 體系并重點(diǎn)深入 uImage.FITFlattened Image Tree這一原生 Linux payload 格式包括其支持架構(gòu)、FIT 分區(qū)與配置選擇機(jī)制、compat 字符串匹配原理、壓縮算法、以及從 ITS 源文件到mkimage生成 uImage 的完整構(gòu)建流程。讀完本文你將能獨(dú)立為基于 aarch32 / aarch64 / RISC-V 的 coreboot 主板上配置并構(gòu)建一個(gè)可引導(dǎo) Linux 的 FIT payload。一、什么是 Payloadcoreboot 移交控制權(quán)的最后一棒coreboot 本身只負(fù)責(zé)硬件的早期初始化romstage、ramstage引導(dǎo)流程的最后一步是把控制權(quán)交給一個(gè) payload。官方文檔的描述是Payloads are run after coreboot has initialized the hardware. coreboot supports multiple payloads, depending on the architecture of the selected mainboard.也就是說payload 是運(yùn)行在 coreboot 完成硬件初始化之后的程序coreboot 支持哪些 payload取決于所選主板的架構(gòu)。coreboot 原生的 payload 二進(jìn)制格式是 SimpleELFSELF它在 CBFS 文檔 中作為CBFS_TYPE_SELF0x20詳細(xì)記載。從 CBFS 的角度看payload 是 CBFS 中的一個(gè)文件條目。CBFS 是 coreboot 用于在系統(tǒng) ROM 中管理獨(dú)立數(shù)據(jù)塊的扁平、追加式歸檔格式詳見 Documentation/lib/cbfs.md一次典型的引導(dǎo)過程如下/------------------------\ - flash 起始 | FMAP | describes the region layout |------------------------| | COREBOOT | --\ | /-------------------\ | | | | fallback/payload | | |-- 一個(gè) CBFS 實(shí)例 | | fallback/ramstage | | | | | fallback/romstage | | | | | bootblock | | | | \-------------------/ | | \------------------------/ - flash 末尾其中fallback/payload就是最終要執(zhí)行的 payload 文件。CBFS 中與 payload 相關(guān)的文件類型見 cbfs.md 的 File types 章節(jié)名稱值內(nèi)容SELF0x20一個(gè) SELF 格式的 payloadFIT_PAYLOAD0x21一個(gè) FIT 格式的 payload需要特別留意的是FIT_PAYLOAD一種扁平鏡像樹與INTEL_FIT0x54由 Intel CPU 微碼消費(fèi)的指針表雖然縮寫相同但完全是兩回事。SELF 格式回顧cbfstool 如何把 ELF 變成 payload在深入 FIT 之前先回顧 SELF 這一原生格式。靜態(tài)鏈接的 ELF 二進(jìn)制在構(gòu)建時(shí)由cbfstool的add-payload命令轉(zhuǎn)換為 SELF二進(jìn)制被拆分為多個(gè)段每個(gè)段擁有獨(dú)立的加載地址和壓縮方式LZMA、LZ4、ZSTD 或 none。SELF payload 是一串段頭后跟段數(shù)據(jù)定義見 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可執(zhí)行代碼、PAYLOAD_SEGMENT_DATA數(shù)據(jù)、PAYLOAD_SEGMENT_BSS清零內(nèi)存和PAYLOAD_SEGMENT_ENTRY入口點(diǎn)。與 FIT 不同SELF 的壓縮是逐段進(jìn)行的而不是通過文件級(jí)COMPRESSION屬性。SELF 加載器實(shí)現(xiàn)在 src/lib/selfboot.c它在加載前會(huì)用 bootmem 映射檢查每個(gè)段因此段必須落在可用 RAM 內(nèi)——SELF payload從不重定位固定加載在指定的地址地址不可用則無法引導(dǎo)。二、FIT面向 Linux 的扁平鏡像樹格式uImage.FIT 是 U-Boot 社區(qū)為 uImage payload 開發(fā)的新格式目前也是 coreboot 在 aarch32、aarch64、RISC-V 架構(gòu)上引導(dǎo) Linux 的推薦方式之一。它的核心思想是把內(nèi)核、initrd、設(shè)備樹FDT和啟動(dòng)配置打包進(jìn)一個(gè)統(tǒng)一的容器由 coreboot 在引導(dǎo)時(shí)解析、選擇合適的配置并逐項(xiàng)解壓、落位。支持的架構(gòu)FIT 解析器需要架構(gòu)級(jí)支持官方文檔明確列出了三個(gè)支持架構(gòu)其余架構(gòu)如 x86不支持aarch32aarch64riscv對(duì)應(yīng)源碼架構(gòu)架構(gòu)相關(guān)源碼內(nèi)核格式要求aarch32src/arch/arm/fit_payload.cImage格式 FDTaarch64src/arch/arm64/fit_payload.cImage格式 FDTRISC-Vsrc/arch/riscv/fit_payload.cImage格式 FDT三個(gè)架構(gòu)有一個(gè)共同點(diǎn)內(nèi)核必須是Image格式即 ARM/RISC-V 內(nèi)核的扁平鏡像格式而非壓縮的 zImage并且都必須攜帶設(shè)備樹fdt才能引導(dǎo)。從源碼看RISC-V 側(cè)src/arch/riscv/fit_payload.c甚至?xí)谌笔?FDT 時(shí)直接報(bào)錯(cuò)if (!config-fdt || !fdt) { printk(BIOS_CRIT, Providing a valid FDT is mandatory to boot a RISC-V kernel!\n); return false; }FIT 包含的分區(qū)sections一個(gè) FIT 鏡像可以包含多個(gè)分區(qū)每個(gè)分區(qū)持有唯一的內(nèi)核、initrd 或配置。引導(dǎo)時(shí)根據(jù)匹配的配置從眾多分區(qū)中挑出一個(gè)內(nèi)核和一個(gè) initrd。分區(qū)必須按以下名稱命名FIT 解析器才能找到它們見 fit.mdkernelfdtramdisk在源碼層面src/lib/fit.c的config_node()正是按屬性名查找這些分區(qū)配置節(jié)點(diǎn)中的kernel、fdt、ramdisk屬性分別通過find_image()關(guān)聯(lián)到/images下的同名節(jié)點(diǎn)。此外fdt屬性還支持通過空格分隔的多個(gè)名稱指定FDT overlay 鏈find_image_with_overlays()coreboot 會(huì)把 overlay 依次應(yīng)用到基礎(chǔ) FDT 上見 src/lib/fit_payload.c 的list_for_each(chain, config-overlays, ...)循環(huán)。三、配置選擇與 Compat 字符串匹配機(jī)制FIT 的核心價(jià)值在于一份鏡像、多種配置同一個(gè) uImage 里可以存放面向多個(gè)板卡的 FDT/內(nèi)核組合引導(dǎo)時(shí)按 compat 字符串自動(dòng)選擇。默認(rèn) compat 字符串FIT 配置條目中的compatible屬性用于尋找匹配的配置。coreboot 會(huì)調(diào)用主板特有的函數(shù)獲取 BOARDID 和 SKUIDuint32_t board_id(void); uint32_t sku_id(void);默認(rèn)情況下src/lib/fit.c 的fit_add_default_compat_strings()會(huì)依次注冊(cè)如下 compat 字符串優(yōu)先級(jí)從低到高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源碼中同樣會(huì)注冊(cè)例如文檔給出的示例cavium,cn8100_sff_evb如果board_id()或sku_id()返回?zé)o效值對(duì)應(yīng)那一條 compat 字符串就不會(huì)被添加源碼中通過UNDEFINED_STRAPPING_ID判斷。需要指出的是先注冊(cè)的字符串優(yōu)先級(jí)更高見 src/include/fit.h 中fit_add_compat_string()的注釋Strings that were added first have a higher priority on finding a match。默認(rèn)字符串注冊(cè)順序?yàn)関endor,part-revX-skuY→vendor,part-revX→vendor,part-skuY→vendor,part因此最精確的revsku組合擁有最高優(yōu)先級(jí)匹配到后即被選中。自定義 compat 字符串主板可以在板級(jí)代碼中調(diào)用以下函數(shù)追加自定義 compat 字符串fit_add_compat_string(const char *str);該函數(shù)src/lib/fit.c會(huì)保存字符串的副本并通過clean_compat_string()將其轉(zhuǎn)為小寫、并把_和空格替換為-隨后插入 compat 列表。這解釋了為什么默認(rèn)字符串中的CONFIG_MAINBOARD_VENDOR、CONFIG_MAINBOARD_PART_NUMBER配置值會(huì)被規(guī)范化成類似cavium,cn8100_sff_evb的小寫連字符形式。匹配與回退邏輯fit_load()src/lib/fit.c是整套選擇邏輯的入口用fdt_unflatten()解包整個(gè) FIT 鏡像遍歷/images下的所有鏡像節(jié)點(diǎn)、/configurations下的所有配置節(jié)點(diǎn)跳過缺少 kernel、缺少 fdt、或 ramdisk 壓縮算法不支持的配置對(duì)每個(gè)配置計(jì)算其compatible屬性與本地 compat 列表的匹配位置和優(yōu)先級(jí)選優(yōu)順序有 compat 匹配的配置 匹配優(yōu)先級(jí)最高者 顯式指定的 default 配置若既無匹配也無 default 配置則報(bào)錯(cuò)放棄No compatible or default configs. Giving up.。文檔特別說明如果沒有找到任何匹配的 compat 字符串就選擇 default 配置由configurations節(jié)點(diǎn)的default屬性指定。fit_load()解析完成后fit_payload()src/lib/fit_payload.c負(fù)責(zé)真正裝載解包 FDT、應(yīng)用 overlay 與 fixup、把 coreboot 表/CBMEM 信息add_cb_fdt_data()寫入設(shè)備樹firmware/coreboot節(jié)點(diǎn)、根據(jù)CONFIG_LINUX_COMMAND_LINE寫入chosen/bootargs、調(diào)用架構(gòu)相關(guān)的fit_payload_arch()擺放內(nèi)核/FDT/initrd最后通過extract()解壓并跳轉(zhuǎn)。四、支持的壓縮算法與 CBFS 集成約束關(guān)于壓縮官方文檔有一項(xiàng)重要約束The FIT image has to be included uncompressed into the CBFS. The sections inside the FIT image can use different compression schemes.即FIT 鏡像本身必須以未壓縮形式放進(jìn) CBFS這也是 cbfs.md 所述 FIT payload does not support whole-file compression 的原因但 FIT內(nèi)部的各個(gè)分區(qū)可以各自采用不同的壓縮方案。支持三種LZMALZ4none源碼驗(yàn)證src/lib/fit.c的image_node()只識(shí)別這三種compression屬性值none、lzma、lz4其它值一律置為-1表示不支持src/lib/fit_payload.c的extract()也僅實(shí)現(xiàn)了CBFS_COMPRESS_NONEmemcpy 搬運(yùn)、CBFS_COMPRESS_LZMAulzman與CBFS_COMPRESS_LZ4ulz4fn三分支其它壓縮直接報(bào) Unsupported compression。這與 CBFS 文件級(jí)支持的四種算法none/LZMA/LZ4/Zstandard不同——FIT 分區(qū)不支持 Zstandard。五、各架構(gòu)的裝載細(xì)節(jié)內(nèi)核、FDT、initrd 如何落位fit_payload_arch()是架構(gòu)相關(guān)代碼的唯一入口聲明于 src/include/fit.h負(fù)責(zé)把內(nèi)核、FDT、initrd 擺放到內(nèi)存中合適的可用 RAM 區(qū)域并設(shè)置入口點(diǎn)。各架構(gòu)的算法均由bootmem_walk()遍歷 bootmem 映射中的可用 RAM 完成擺放結(jié)果同時(shí)用bootmem_add_range(..., BM_MEM_PAYLOAD)標(biāo)記為預(yù)留避免后續(xù)分配沖突。aarch64src/arch/arm64/fit_payload.c 的實(shí)現(xiàn)最精細(xì)先做部分解壓讀取內(nèi)核 Image 頭decompress_kernel_header()校驗(yàn)?zāi)?shù)0x644d5241ARM64_IMAGE_MAGIC并獲取text_offset與image_size內(nèi)核的擺放遵循 ARM64 規(guī)范放在距 2 MiB 對(duì)齊基址偏移text_offset處若內(nèi)核頭未設(shè)置image_size則按文檔約定回退為text_offset 0x80000并在內(nèi)核后預(yù)留最多MAX_KERNEL_SIZE64 MiB自由空間get_kernel_size()內(nèi)核入口通過prog_set_entry(payload, (void *)kernel-offset, arg)設(shè)置arg是 FDT 地址——內(nèi)核期望 FDT 作為首個(gè)參數(shù)initrd 與 FDT 的位置取決于內(nèi)核頭 flags 的 bit 3place_anywhere4.6 之前的內(nèi)核無法使用 Image 物理偏移之下的內(nèi)存此時(shí) initrd/FDT 必須放在內(nèi)核之后。aarch32src/arch/arm/fit_payload.c 相對(duì)簡(jiǎn)單內(nèi)核放在_dram 32 MiB處避開解壓時(shí)需重定位的區(qū)域FDT 放在_dram 128 MiB以上防止被內(nèi)核解壓器覆蓋initrd 緊隨 FDT 之后所有區(qū)域按 1 MiB 對(duì)齊Linux 4.15 doesnt like 4KiB alignment. Align to 1 MiB for now.。RISC-Vsrc/arch/riscv/fit_payload.c 實(shí)現(xiàn)的是兼容 Berkeley Boot LoaderBBL的加載方式內(nèi)核放在按SECTION_ALIGN對(duì)齊的可用內(nèi)存RV64 為 2 MiBRV32 為 4 MiBFDT 與 initrd 依次排在內(nèi)核之后入口參數(shù)同樣是 FDT 地址。結(jié)合 src/arch/riscv/boot.c 可以看到RISC-V 上 payload 與 coreboot 表的交接方式也與眾不同coreboot 會(huì)把 coreboot 表指針以coreboot-table屬性嵌入 FDT 的/chosen節(jié)點(diǎn)且調(diào)用約定在 HARTID、FDT 之后coreboot 表作為第三個(gè)參數(shù)傳入這與 CBFS 文檔中關(guān)于 SELF 調(diào)用約定的特例說明一致。六、構(gòu)建 FIT 鏡像從 ITS 到 uImageFIT 鏡像必須通過 U-Boot 的mkimage工具構(gòu)建。官方文檔 fit.md 給出了完整的示例配置下面完整繼承并補(bǔ)充注釋/* * 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); /* 內(nèi)核數(shù)據(jù)LZMA 壓縮的 Image */ type kernel; arch arm64; /* 目標(biāo)架構(gòu)arm64 */ os linux; compression lzma; /* 該分區(qū)壓縮算法lzma / lz4 / none */ load 0x80000; /* 建議加載地址aarch64 慣例 */ entry 0x80000; /* 入口地址 */ hash-1 { algo crc32; }; }; fdt-1 { description Flattened Device Tree blob; data /incbin/(target.dtb); /* 設(shè)備樹數(shù)據(jù) */ type flat_dt; arch arm64; compression none; /* FDT 通常不壓縮 */ hash-1 { algo crc32; }; }; ramdisk-1 { description Compressed Initramfs; data /incbin/(initramfs.cpio.xz); /* 由 Linux 內(nèi)核自行解壓的 initrd */ type ramdisk; arch arm64; os linux; compression none; /* 此處不用 FIT 解壓交給內(nèi)核 */ load 00000000; entry 00000000; hash-1 { algo sha1; }; }; }; configurations { default conf-1; /* 無 compat 匹配時(shí)的回退配置 */ conf-1 { description Boot Linux kernel with FDT blob; kernel kernel; /* 引用 /images 下的分區(qū)名 */ fdt fdt-1; ramdisk ramdisk-1; /* 可在此添加 compatible vendor,board; 以便按 compat 精確匹配 */ }; }; };將這個(gè)文件保存為 ITS 源文件config.its并與以下文件放在同一目錄target.dtb—— 設(shè)備樹二進(jìn)制initramfs.cpio.xz—— 壓縮的 initramfsImage.lzma—— LZMA 壓縮的 ARM64/ARM/RISC-V 內(nèi)核 Image然后執(zhí)行mkimage -f config.its uImage生成的uImage中initramfs.cpio.xz會(huì)在引導(dǎo)后由 Linux 內(nèi)核負(fù)責(zé)解壓Image.lzma由 coreboot 的 FIT 加載器extract()中的ulzman解壓target.dtb以未壓縮形式隨鏡像交付。最終把uImage作為 FIT payload 加入 CBFS 即可CBFS 文件類型為FIT_PAYLOAD0x21。七、接入 coreboot 構(gòu)建系統(tǒng)Kconfig 配置要點(diǎn)從 payloads/Kconfig 可以看到 FIT payload 的配置入口PAYLOAD_FIT布爾選項(xiàng) A FIT payload選中后自動(dòng)select PAYLOAD_FIT_SUPPORTPAYLOAD_FILE指定 payload 文件路徑當(dāng)PAYLOAD_FIT時(shí)默認(rèn)值為uImage壓縮默認(rèn)值FIT 支持時(shí)默認(rèn)COMPRESSED_PAYLOAD_NONE與FIT 鏡像必須以未壓縮形式放入 CBFS的約束呼應(yīng)非 FIT 時(shí)默認(rèn)COMPRESSED_PAYLOAD_LZMA。構(gòu)建時(shí)相關(guān)文件按架構(gòu)納入編譯例如 src/arch/arm64/Makefile.mk 中的ramstage-$(CONFIG_PAYLOAD_FIT_SUPPORT) fit_payload.c即只有開啟 FIT 支持時(shí)才把fit_payload.c編入 ramstage。RISC-V 側(cè)src/arch/riscv/Makefile.mk則無條件編入。引導(dǎo)時(shí)各架構(gòu)通過prog_cbfs_type(prog) CBFS_TYPE_FIT_PAYLOAD判斷 payload 類型如 src/arch/arm/boot.c 在 ramstage 中調(diào)用boot_linux()直接跳轉(zhuǎn)內(nèi)核說明 FIT payload 只在 ramstage 階段執(zhí)行。八、調(diào)試與排查建議結(jié)合源碼中的日志輸出printk排查 FIT 引導(dǎo)問題時(shí)可關(guān)注以下節(jié)點(diǎn)無法選擇配置fit_load()會(huì)打印每個(gè)配置的 compat 及匹配情況FIT: config xxx, kernel yyy, fdt zzz (match)最終若輸出No compatible or default configs. Giving up.說明 ITS 中既無匹配的compatible也沒有default配置找不到鏡像Cannot find image node xxx!表示配置引用的分區(qū)名與/images下的節(jié)點(diǎn)名不一致注意區(qū)分kernel與kernel1這類命名不支持壓縮Unsupported compression或Ramdisk is compressed with an unsupported algorithm提示分區(qū)壓縮算法不在 LZMA/LZ4/none 之列ZSTD 不支持內(nèi)存擺放失敗Failed to find free memory region并伴隨bootmem_dump_ranges()輸出的內(nèi)存映射說明內(nèi)核/FDT/initrd 找不到足夠的可用 RAM例如與 ramstage 重疊FDT 缺失RISC-V 下Providing a valid FDT is mandatory to boot a RISC-V kernel!表明配置未提供 fdt 分區(qū)??偨Y(jié)coreboot 的 payload 機(jī)制以 SELF 為原生格式同時(shí)為 aarch32、aarch64、RISC-V 架構(gòu)提供了完善的 uImage.FIT 支持FIT 解析器src/lib/fit.c負(fù)責(zé)解包鏡像并依據(jù) compat 字符串或 default 配置選定引導(dǎo)配置架構(gòu)相關(guān)的 fit_payload.c 負(fù)責(zé)按各架構(gòu)規(guī)范把內(nèi)核、FDT、initrd 擺放到安全的可用內(nèi)存最終跳轉(zhuǎn)內(nèi)核。實(shí)踐中只需按本文第六節(jié)的 ITS 模板準(zhǔn)備Image、target.dtb、initramfs.cpio.xz用mkimage -f config.its uImage生成鏡像并通過PAYLOAD_FIT配置接入構(gòu)建即可完成一個(gè)由 coreboot 直接引導(dǎo) Linux 的 FIT payload 方案。贊分享固件嵌入式操作系統(tǒng)驅(qū)動(dòng)開發(fā)【免費(fèi)下載鏈接】corebootRead-only mirror of https://review.coreboot.org/coreboot.git. Synced every hour. We dont handle Pull Requests.項(xiàng)目地址https://gitcode.com/gh_mirrors/co/coreboot點(diǎn)擊查看免費(fèi)下載相關(guān)推薦coreboot 的 Flattened uImage TreeFITPayload 引導(dǎo)機(jī)制與 uImage 構(gòu)建實(shí)戰(zhàn)指南coreboot 的 Flattened uImage TreeFITPayload 引導(dǎo)機(jī)制與 uImage 構(gòu)建實(shí)戰(zhàn)指南 本指南以倉庫內(nèi) Documen固件嵌入式操作系統(tǒng)驅(qū)動(dòng)開發(fā)coreboot 支持 vbootVerified Boot的設(shè)備清單與啟用機(jī)制深度解析coreboot 支持 vbootVerified Boot的設(shè)備清單與啟用機(jī)制深度解析 導(dǎo)讀 本文面向 coreboot 開發(fā)者與固件安全研究人員系統(tǒng)梳固件嵌入式操作系統(tǒng)驅(qū)動(dòng)開發(fā)Atmosphere重啟機(jī)制全解析warmboot payload與冷啟動(dòng)流程Atmosphere重啟機(jī)制全解析warmboot payload與冷啟動(dòng)流程 概述 Atmosphere作為Nintendo Switch的定制固件其重啟固件操作系統(tǒng)嵌入式系統(tǒng)編程上一篇mtkclient項(xiàng)目Realme C3設(shè)備Bootloop問題分析與解決指南下一篇Web Audio API實(shí)戰(zhàn)PitchDetect中的音頻流處理與分析技術(shù)創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考