U-Boot 的 FIT 镜像格式比老方法好用多了~
内存泄漏
2026年6月26日 07:40
辽宁
在小说阅读器读本章
去阅读
以下文章来源于嵌入式情报局
,作者情报小哥
嵌入式情报局
.
集单片机与linux于一身的技术号~
目录
- FIT 是什么、为什么需要它
- 传统 uImage 的痛点
- FIT 的文件结构:.its 和 .itb
- 动手写一个 FIT 镜像
- FIT 镜像的结构图解
- 配置(configuration):一套组合拳
- 完整性校验:Hash 守护数据安全
- 压缩支持:给镜像瘦身
- 签名验证:安全启动的基石
- 总结
大家好,我是情报小哥~
前段时间,一个小伙伴在群里问:
小哥,我们项目要从老版本 U-Boot 切到新版本,发现原来的 uImage 启动方式要改成 FIT 格式了,这 FIT 到底是个啥玩意?为啥要搞这么复杂?
FIT 这几年在嵌入式 Linux 圈子里越来越常见,但很多人只是照着教程敲命令,并没有真正理解它做了什么.今天,我们就聊清楚了~
FIT 是什么
FIT,全称叫 Flattened Image Tree。
翻译过来就是\"扁平化镜像树\"。
传统的 uImage 就是这样:内核镜像打一个包,设备树是另一个文件,根文件系统又是一个文件,initramfs 还要单独管理。
更别提有些项目还有 FPGA 比特流、M4 协处理器固件、安全 OS 镜像……
FIT 做的事情很简单:它把所有这些二进制文件,按照一个统一的格式打包在一起,并且附上了描述信息、校验信息、加载地址等元数据。
每个成员都在自己该在的位置,并且身上贴好了标签。
传统 uImage 的痛点
先回顾一下老式的 uImage 是怎么用的。
用 mkimage 给内核镜像包个头:
mkimage -A arm -O linux -T kernel -C none -a 0x80008000 -e 0x80008000 \\
-n \"Linux Kernel\" -d zImage uImage
然后启动的时候:
bootm 0x10000000
看起来很简单对吧?
但是问题来了:
第一,信息全挤在一个 64 字节的头部里。
镜像类型、加载地址、入口地址、压缩方式、名字……全部塞进去。
想加个校验?没地方了。
想放两个设备树?想都别想。
第二,多个组件要分开管理。
内核放一个分区,设备树放一个分区,根文件系统放一个分区。
生产烧录的时候,每个分区都得单独处理,烧错一个就起不来。
第三,没有完整性校验。
uImage 头部有个 CRC32,但那只校验头部,不校验数据部分。
内核镜像在存储介质上被意外篡改了一个 bit,bootm 发现不了,启动时直接崩溃给你看。
FIT 的文件结构:.its 和 .itb
FIT 有两个核心文件类型:
| 文件 | 后缀 | 性质 | 用途 |
|---|---|---|---|
| Image Tree Source | .its |
文本文件 | 人类可读的镜像描述 |
| Image Tree Blob | .itb |
二进制文件 | 烧录到设备上的镜像 |
工作流程是这样的:
.its 文件 ──mkimage──> .itb 文件 ──bootm──> 启动内核
.its 文件用 Device Tree 语法来写(没错,就是和 .dts 一样的语法)。
.itb 文件就是编译后的 FIT 镜像,是一个大的二进制 blob。
动手写一个 FIT 镜像
先看一个最简的 .its 文件,它包含:一个内核镜像 + 一个设备树。
1. 准备原材料
假设我们已经有编译好的内核和设备树:
work/
├── zImage # Linux 内核
├── myboard.dtb # 设备树
└── fit_demo.its # 我们要写的 ITS 文件(还未创建)
2. 编写 .its 文件
/dts-v1/;
/ {
description = \"FIT Demo - 内核 + 设备树\";
#address-cells = <1>;
images {
kernel@1 {
description = \"Linux Kernel\";
data = /incbin/(\"./zImage\");
type = \"kernel\";
arch = \"arm\";
os = \"linux\";
compression = \"none\";
load = <0x80008000>;
entry = <0x80008000>;
hash@1 {
algo = \"sha256\";
};
};
fdt@1 {
description = \"MyBoard Device Tree\";
data = /incbin/(\"./myboard.dtb\");
type = \"flat_dt\";
arch = \"arm\";
compression = \"none\";
hash@1 {
algo = \"sha256\";
};
};
};
configurations {
default = \"conf@1\";
conf@1 {
description = \"MyBoard Default Config\";
kernel = \"kernel@1\";
fdt = \"fdt@1\";
};
};
};
3. 编译生成 .itb
mkimage -f fit_demo.its fit_demo.itb
mkimage 会帮我们:
- 读取
.its文件 - 把
zImage和myboard.dtb嵌入进去 - 计算每个镜像的 SHA256 哈希值,写入到镜像树中
- 生成最终的
.itb文件
4. 在 U-Boot 中使用
tftp 0x82000000 fit_demo.itb
bootm 0x82000000
U-Boot 会自动解析 FIT 结构,把内核和设备树加载到正确的地址,然后启动。
FIT 镜像的结构图解
下面我们看一张图,把 FIT 的内部结构看透:
从上图可以看到:
- 最外层是一个
/根节点(和 Device Tree 一样) images子节点下,每个镜像是一个独立节点,按名字索引configurations子节点下,每个配置指定 \"用哪个内核 + 用哪个设备树\"- 每个镜像节点内部可以挂
hash子节点来做校验 - 加载地址(
load)、入口地址(entry)直接写在镜像属性里
这种树状结构的好处是:可扩展性极强。
想加一个 initramfs?在 images 下加个 ramdisk@1 节点就行。
想加两个设备树,通过不同配置切换?再加一个 fdt@2 节点,新建一个 conf@2 引用它。
配置(configuration)
单内核 + 单设备树是最简单的情况。
但在实际项目中,同一个硬件可能运行不同的内核配置,或者需要根据产品型号选择不同的设备树。
比如你有一个车载项目,硬件平台相同,但:
- 标准版用 Linux 主线内核
- RT 版用打过 PREEMPT\_RT 补丁的内核
- 高端版多了个 FPGA,需要加载 FPGA 比特流
用 FIT 的配置机制,一个 .itb 全部搞定:
configurations {
default = \"standard\";
standard {
description = \"标准版配置\";
kernel = \"kernel_standard\";
fdt = \"fdt_standard\";
};
rt {
description = \"实时版配置\";
kernel = \"kernel_rt\";
fdt = \"fdt_rt\";
};
high_end {
description = \"高端版配置(含FPGA)\";
kernel = \"kernel_high_end\";
fdt = \"fdt_high_end\";
loadables = \"fpga_bitstream\";
};
};
在 U-Boot 中可以通过环境变量选择配置:
setenv fit_conf high_end
bootm 0x82000000
或者直接指定:
bootm 0x82000000#high_end
完整性校验
还记得前面说传统 uImage 只校验头部吗?
FIT 的 hash 机制直接解决了这个痛点。
每个镜像节点下可以挂一个或多个 hash 子节点:
kernel@1 {
...
hash@1 {
algo = \"sha256\";
};
};
mkimage 在打包时会自动计算镜像数据的哈希值,写入 FIT 结构中。
U-Boot 在启动前会重新计算一遍,和存储的值做比对。
如果对不上,直接报错拒绝启动:
## Loading kernel from FIT Image at 82000000 ...
Using 'conf@1' configuration
Verifying Hash Integrity ... sha256 error!
Bad hash value for 'kernel@1' hash node in 'kernel@1' image node
这个报错信息,排查过闪存位翻转问题的小伙伴一定不陌生。
可选的算法有:sha1、sha256、md5、crc32。
推荐用 sha256,安全性和性能的平衡点最好。
压缩支持
嵌入式设备的存储空间寸土寸金。
FIT 支持对镜像子节点做压缩:
kernel@1 {
...
compression = \"gzip\";
};
支持的压缩算法:
none:不压缩gzip:通用压缩bzip2:压缩率更高,但解压慢lzma:高压缩率lzo:解压速度极快,适合内核
要注意:压缩后 U-Boot 需要在启动时解压,会消耗 CPU 时间和内存。
一般推荐内核用 lzo(启动快),根文件系统用 lzma(体积小)。
签名验证
Hash 校验保证了数据完整性,但无法防止镜像被恶意替换。
假如攻击者把你 Flash 上的 FIT 镜像整个替换掉,重新算一遍 hash 写入,U-Boot 验 hash 的时候是验不出来的。
这时候就需要 FIT 的签名机制:
kernel@1 {
...
signature@1 {
algo = \"sha256,rsa2048\";
key-name-hint = \"dev_key\";
sign-images = \"kernel\", \"fdt\";
};
};
配置签名后,需要先生成密钥对,再用 mkimage 签名:
# 生成 RSA 密钥对
openssl genpkey -algorithm RSA -out dev_key.key -pkeyopt rsa_keygen_bits:2048
# 编译并签名 FIT 镜像
mkimage -f fit_demo.its -K u-boot.dtb -k . -r fit_signed.itb
参数说明:
-K u-boot.dtb:把公钥写入 U-Boot 的设备树,这样 U-Boot 启动时就能验签-k .:密钥所在目录-r:要求签名必须存在
签名后的 FIT 镜像,U-Boot 会先验签再验 hash,双重保障。
这也是各大芯片厂商(NXP、TI、ST)安全启动方案的底层基础。
最后完整版 .its
把上面提到的所有特性汇总,一个典型的完整 .its 文件长这样:
/dts-v1/;
/ {
description = \"Production FIT Image for MyBoard\";
#address-cells = <1>;
images {
kernel@1 {
description = \"Linux Kernel 5.10\";
data = /incbin/(\"./zImage\");
type = \"kernel\";
arch = \"arm\";
os = \"linux\";
compression = \"lzo\";
load = <0x80008000>;
entry = <0x80008000>;
hash@1 {
algo = \"sha256\";
};
signature@1 {
algo = \"sha256,rsa2048\";
key-name-hint = \"prod_key\";
};
};
fdt@1 {
description = \"MyBoard Rev B Device Tree\";
data = /incbin/(\"./myboard-revb.dtb\");
type = \"flat_dt\";
arch = \"arm\";
compression = \"none\";
hash@1 {
algo = \"sha256\";
};
};
ramdisk@1 {
description = \"Initial RAM Disk\";
data = /incbin/(\"./initramfs.cpio.gz\");
type = \"ramdisk\";
arch = \"arm\";
os = \"linux\";
compression = \"gzip\";
hash@1 {
algo = \"sha256\";
};
};
};
configurations {
default = \"production\";
production {
description = \"生产环境配置\";
kernel = \"kernel@1\";
fdt = \"fdt@1\";
ramdisk = \"ramdisk@1\";
};
};
};
编译命令还是那一条:
mkimage -f fit_demo.its -K u-boot.dtb -k . -r fit_production.itb
如果你正在做新项目,或者从老 U-Boot 迁移,强烈推荐用 FIT。
毕竟,一个 .itb 文件代替过去三四个分散的二进制文件,不管是版本管理、生产烧录、还是现场升级,都清爽太多了。
完美!
星标公众号,第一时间看文章!
最 后
小哥搜集了一些嵌入式学习资料,公众号内回复【1024】即可找到下载链接!
推荐好文 点击蓝色字体即可跳转
☞ 专辑|Linux应用程序编程大全
☞ 专辑|学点网络知识
☞ 专辑|手撕C语言
☞ 专辑|手撕C++语言
☞ 专辑|经验分享
☞ 专辑|从单片机到Linux
☞ 专辑|电能控制技术
☞ 专辑|嵌入式必备数学知识 
☞ MCU进阶专辑
☞ 嵌入式C语言进阶专辑
☞ 经验分享
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。









评论