3.11.3. FIT Image 介绍

在 ZX 启动系统的开发过程中,使用过不同的 Kernel Image 格式:

  • uImage

  • zImage

  • Image.gz

  • Image

  • FIT Image(ITB Image)

由于未来需要改用 FIT Image(ITB Image),这里做一个简要的说明。

3.11.3.1. Kernel Image 格式

外网资源:

网上有一篇相关的文章可以参考 http://www.wowotech.net/u-boot/fit_image_overview.html

3.11.3.1.1. Image

这是 Linux kernel 编译时生成的未压缩 Image.

U-Boot 可以通过 booti 命令直接运行该 Image(ARMv7 是 ZX 自行添加的命令支持启动),启动命令格式为:

booti <kernel addr> - <dtb addr>

其中 <kernel addr> 是 Image 文件所在的内存地址。

Image 文件的格式,在不同芯片架构上是不一样的。对于 ARM64 和 RISCV64,会有一个格式头;但是对于 ARM32,则没有 头信息。

3.11.3.1.2. zImage

Image 经过压缩和打包后,生成的 zImage 文件。其做法是将压缩后的 Image 作为 Payload,与一段解压缩代码一起编译 链接,生成 zImage。因此 zImage 是一个可直接启动的自解压镜像。

启动命令为:

bootz <kernel addr> - <dtb addr>

其中 <kernel addr> 即 zImage 所在的内存地址。

ARM64, RISCV 架构的 Linux kernel 编译,不再生成 zImage 格式的文件。

3.11.3.1.3. uImage

uImage 是 U-Boot 定义的一种启动镜像格式,由 64 字节的 U-Boot Image header 加上镜像内容构成。

uImage 中的镜像内容,可以是未压缩的 Image,也可以是压缩的 Image。 Linux kernel 编译时生成(依赖 uboot-tools 包中的 mkimage 工具)。但是新版的 Linux kernel 不再生成 uImage 文件。

uImage 文件的启动命令:

bootm <kernel addr> - <dtb addr>

其中 <kernel addr> 即 uImage 所在的内存地址。

3.11.3.1.4. Image.gz

Image.gz 是 Linux kernel 编译时生成的压缩文件,使用 gzip 算法直接对 Image 文件进行压缩生成。除了 Image.gz,使用 其他压缩算法还可以生成:

  • Image.lzo

  • Image.lz4

  • Image.bz2

  • Image.lzma

这些压缩文件就是各种压缩工具生成的标准压缩文件。U-Boot 的启动命令为:

booti <kernel addr> - <dtb addr>

其中 <kernel addr> 即 Kernel 压缩文件所在的内存地址。

3.11.3.1.5. FIT Image

Flattened Image Tree。

原始参考文档:

source/uboot-2021.10/doc/usage/fit.rst
source/uboot-2021.10/doc/uImage.FIT/howto.txt
source/uboot-2021.10/doc/uImage.FIT/source_file_format.txt
source/uboot-2021.10/doc/uImage.FIT/command_syntax_extensions.txt

如 howto.txt 开篇所提,当前社区使用 FIT Image 的主要理由是:

  • 更灵活的处理各种类型的 image 类型(压缩、非压缩、各种格式,各种配置组合)

  • 可以处理安全启动过程中的安全校验(签名校验)

3.11.3.1.5.1. 配置和生成

Flattened Image Tree 顾名思义,是参考 Flattened Device Tree 命令而来,使用 DTS 的语法, 通过一些新增的节点,描述生成镜像文件所使用的 Image 文件和配置。

例子如: source/uboot-2021.10/doc/uImage.FIT/kernel_fdt.its

/dts-v1/;

/ {
    description = "Simple image with single Linux kernel and FDT blob";
    #address-cells = <1>;

    images {
            kernel {
                    description = "Vanilla Linux kernel";
                    data = /incbin/("./vmlinux.bin.gz");
                    type = "kernel";
                    arch = "ppc";
                    os = "linux";
                    compression = "gzip";
                    load = <00000000>;
                    entry = <00000000>;
                    hash-1 {
                            algo = "crc32";
                    };
                    hash-2 {
                            algo = "sha1";
                    };
            };
            fdt-1 {
                    description = "Flattened Device Tree blob";
                    data = /incbin/("./target.dtb");
                    type = "flat_dt";
                    arch = "ppc";
                    compression = "none";
                    hash-1 {
                            algo = "crc32";
                    };
                    hash-2 {
                            algo = "sha1";
                    };
            };
    };

    configurations {
            default = "conf-1";
            conf-1 {
                    description = "Boot Linux kernel with FDT blob";
                    kernel = "kernel";
                    fdt = "fdt-1";
            };
    };
};

此处描述的一个配置,其中包含了 fdt + kernel,以及校验方式。

生成 itb image的命令:

mkimage -f kernel_fdt.its kernel_fdt.itb

3.11.3.1.5.2. 启动命令

使用默认的配置进行启动:

bootm <kernel itb addr>

如果 its 中有多个配置,可以指定启动的配置组合:

bootm <kernel itb addr>#<conf-name>

如:bootm 0x81000000#conf-2

更多说明请参考:source/uboot-2021.10/doc/uImage.FIT/command_syntax_extensions.txt

3.11.3.2. 使用 FIT Image

ZX 项目中,将 Kernel Image 改为使用 FIT Image 的原因:

  • 快速启动的需要

  • 减少分区的需要

  • 安全启动的需要

  • 支持 RISCV 的需要

3.11.3.2.1. 快速启动

M4 的快速启动,eMMC 的方案使用未压缩的 Image 速度最快,SPINOR/SPINAND 使用压缩的 zImage 比较合适。

同时 U-Boot 加载 Kernel 最好只读取 Kernel Image 实际大小的数据,避免过多读取无关数据, 才能节省启动时间。

U-Boot 加载 Kernel 时,无论 Image 还是 zImage,都没有头信息,因此即读取 Kernel Image 的大小需要根据实际编译生成的镜像大小进行修改。

如果采用 FIT Image 则可以避免上述问题。

生成的 FIT Image 有一个信息头,U-Boot 可以先读取信息头的数据,得到 Image 大小,然后按照实际大小读取 剩下的 Kernel Image 数据:

  1. 从存储介质读取的数据可以做到尽可能的少

  2. 开发者不需要手动修改读取的数据大小

当然是用了 FIT Image 之后,无论是使用压缩的 zImage/Image.gz 还是使用未压缩的 Image,对启动流程/启动命令 都没有影响,开发者仅需修改 ITS 文件配置即可。

3.11.3.2.2. 减少分区

在使用 FIT Image 之前,Kernel 启动所需的 DTB 保存在一个独立的分区中。

DTB 使用一个独立的分区保存对于 SPINOR/SPINAND 的方案而言是一种比较浪费空间的方式。

对于 SPINOR 而言,一般存储空间都比较小,但是分区必须按照一个擦写单元进行,一般是 64KB, 而 DTB 绝大多数在 32KB 以内。

对于 SPINAND 而言,分区同样必须按照一个擦写块进行,一般是 128KB 或者 256KB, 同时要考虑坏块的情况,需要多分配几个块进行备份。

使用 FIT Image,Kernel 所使用的 DTB 与 Kernel 一起进行打包,存放到同一个分区, 有利于提高存储空间的利用效率,同时分区划分更简单。

3.11.3.2.3. 安全启动

在安全启动方案中,安全信任链的校验过程如下:

BROM -> SPL -> U-Boot -> Kernel -> RootFS

按照上述顺序逐级进行安全校验。

在 SPL 校验 U-Boot 和 U-Boot 校验 Kernel 的阶段,如果使用 FIT Image,则已经有成熟的安全校验方案, 并且启动处理流程与非安全方案基本一致。不采用 FIT Image,则安全方案和非安全方案所采用的启动流程 差异性比较大,不利于方案的开发和维护。

3.11.3.2.4. 支持 RISCV

RISCV 需要 OpenSBI 协助进行启动,OpenSBI 运行在 U-Boot 之前。

启动流程:

BROM -> SPL -> OpenSBI -> U-Boot -> Kernel

RISCV 版本的 SPL 在加载运行 OpenSBI 和 U-Boot 时,仅支持使用 FIT Image, 即需要将下列几个数据打包为一个 ITB 文件(uboot.itb):

  • OpenSBI

  • U-Boot

  • DTB

SPL 加载 uboot.itb,读取 DTB 和 U-Boot 到对应的位置,然后运行 OpenSBI,通过 OpenSBI 跳转到 U-Boot。启动的 DTB 是 OpenSBI 和 U-Boot 共用。

Kernel 也使用 FIT Image,可以简化 Image 的种类。