Flattened Image Tree (FIT) with Yocto
Long time ago, I wrota a post [1] that compared the legacy Image format against Flattened Image Tree Format (FIT) [2] and highlighted the benefits of using it. The benefits is still valid and FIT images is my preferred way to boot a Linux kernel. Dispite that, I almost never see that FIT images is used in examples nor Board Support Packages (BSPs).
So this post is mostly to give some more attention to the FIT images because I think it deserves it. The post will somehow overlap with the previous one, but my intension is to be a little more hands-on this time.
Even if creating a FIT image in Yocto is just two lines away, I think it is good to know what a FIT image really is - so we start from there.
Recap from the previous post
An embedded Linux system often needs multiple files in order to boot. Such files includes the kernel image, a Flattened Device Tree (FDT), Trusted Execution Environment (TEE) firmware, an Initrd image ... the list could be long.
FIT let you bundle all those components into one single image and create configurations compounded of a subset of these components. This way you may use the same kernel image with different FDT:s for different hardware configurations, all handled by a single FIT image.
What is a FIT image?
FIT image is essentially a Flattened Device Tree (FDT) with embedded binaries. You are probably already familiar with the Device Tree Source (DTS) format as it is heavily used in several big projects (e.g. Linux Kernel, U-Boot, Zephyr, ...).
The source file for FIT images is called Image Tree Source (ITS), compare to DTS for Device Tree Source, and use the same tools (libfdt library and Device Tree Compiler (dtc) ) as the other of the projects mention above.
What does a ITS file look like?
You will see the same tree structure with the same format you are used to when working with dts files:
/dts-v1/;
/ {
description = "iMX8 FIT image";
#address-cells = <1>;
images {
kernel {
description = "Kernel";
data = /incbin/("Image");
type = "kernel";
arch = "arm64";
os = "linux";
compression = "none";
load = <0x40480000>;
entry = <0x40480000>;
hash {
algo = "sha1";
};
};
fdt@1 {
description = "Devicetree for Board XXX";
data = /incbin/("imx8-board-XXX.dtb");
type = "flat_dt";
arch = "arm64";
compression = "none";
load = <0x43000000>;
entry = <0x43000000>;
hash {
algo = "sha1";
};
};
fdt@2 {
description = "Devicetree for Board YYY";
data = /incbin/("imx8-board-YYY.dtb");
type = "flat_dt";
arch = "arm64";
compression = "none";
load = <0x43000000>;
entry = <0x43000000>;
hash {
algo = "sha1";
};
};
initrd {
description = "Initrd";
data = /incbin/("core-image-minimal-custom-board.ext2.gz");
type = "ramdisk";
arch = "arm64";
os = "linux";
compression = "none";
hash {
algo = "sha1";
};
};
};
configurations {
default = "boardXXX";
boardXXX {
description = "Standard Boot for board XXX";
kernel = "kernel";
fdt = "fdt@1";
ramdisk = "initrd";
hash {
algo = "sha1";
};
};
boardYYY {
description = "Standard Boot for board YYY";
kernel = "kernel";
fdt = "fdt@2";
ramdisk = "initrd";
hash {
algo = "sha1";
};
};
};
};
All nodes is well described in the documentation [3], but we may take a closer look at the configurations node:
configurations {
default = "boardXXX";
boardXXX {
description = "Standard Boot for board XXX";
kernel = "kernel";
fdt = "fdt@1";
ramdisk = "initrd";
hash {
algo = "sha1";
};
};
boardYYY {
description = "Standard Boot for board YYY";
kernel = "kernel";
fdt = "fdt@2";
ramdisk = "initrd";
hash {
algo = "sha1";
};
};
};
A configuration is a group of related components that will be extracted during boot. In the example above, there are two configurations, boardXXX and boardYYY (I'm always suprised at how good I am at picking good names).
These two configurations share the same kernel and initrd, but uses different FDTs.
Supported image types
FIT supports many different types and U-boot does only make use of a few of them. That doesn't stop you from embedding the firmware for your coprocessor and writing the logic to flash it yourself. The advantage is that all firmware is collected in a single image and the versions are coherent with each other.
libfdt makes it convenient to handle the FIT image in your application.
Type is one of the mandatory attribute for images in the ITS file and specify the type of the image.
Supported image types are:
Sub-image type | Meaning |
---|---|
aisimage | Davinci AIS image |
atmelimage | ATMEL ROM-Boot Image |
copro | Coprocessor Image} |
fdt_legacy | legacy Image with Flat Device Tree |
filesystem | Filesystem Image |
firmware | Firmware |
firmware_ivt | Firmware with HABv4 IVT } |
flat_dt | Flat Device Tree |
fpga | FPGA Image } |
gpimage | TI Keystone SPL Image |
imx8image | NXP i.MX8 Boot Image |
imx8mimage | NXP i.MX8M Boot Image |
imximage | Freescale i.MX Boot Image |
kernel | Kernel Image |
kernel_noload | Kernel Image (no loading done) |
kwbimage | Kirkwood Boot Image |
lpc32xximage | LPC32XX Boot Image |
mtk_image | MediaTek BootROM loadable Image } |
multi | Multi-File Image |
mxsimage | Freescale MXS Boot Image |
omapimage | TI OMAP SPL With GP CH |
pblimage | Freescale PBL Boot Image |
pmmc | TI Power Management Micro-Controller Firmware |
ramdisk | RAMDisk Image |
rkimage | Rockchip Boot Image } |
rksd | Rockchip SD Boot Image } |
rkspi | Rockchip SPI Boot Image } |
script | Script |
socfpgaimage | Altera SoCFPGA CV/AV preloader |
socfpgaimage_v1 | Altera SoCFPGA A10 preloader |
spkgimage | Renesas SPKG Image } |
standalone | Standalone Program |
stm32image | STMicroelectronics STM32 Image } |
sunxi_egon | Allwinner eGON Boot Image } |
sunxi_toc0 | Allwinner TOC0 Boot Image } |
tee | Trusted Execution Environment Image |
ublimage | Davinci UBL image |
vybridimage | Vybrid Boot Image |
x86_setup | x86 setup.bin |
zynqimage | Xilinx Zynq Boot Image } |
zynqmpbif | Xilinx ZynqMP Boot Image (bif) } |
zynqmpimage | Xilinx ZynqMP Boot Image } |
Working with FIT images
Besides the Device Tree Compiler (dtc), all tools that you need to manipulate a FIT image are typically available in the u-boot-tools package you probably find in your Linux distribution.
mkimage is used to create the image and dumpimage is used to inspect and dump content of a FIT image.
Inspect a FIT image
Use dumpimage with the -l option to list the content of a FIT image.
See example below:
1$ dumpimage -l ./fitImage
2FIT description: Kernel fitImage for test Distro/5.15.87+gitAUTOINC+0eb4504bd3/test-imx8mp
3 Created: Wed Feb 8 16:32:47 2023
4Image 0 (kernel-1)
5 Description: Linux kernel
6 Created: Wed Feb 8 16:32:47 2023
7 Type: Kernel Image
8 Compression: gzip compressed
9 Data Size: 11880575 Bytes = 11602.12 KiB = 11.33 MiB
10 Architecture: AArch64
11 OS: Linux
12 Load Address: 0x40480000
13 Entry Point: 0x40480000
14 Hash algo: sha256
15 Hash value: 794491ba95d20ba1337d61b5b12d04a406168360072ec55c4c22ee3b4a24439c
16Image 1 (fdt-test-imx8mp.dtb)
17 Description: Flattened Device Tree blob
18 Created: Wed Feb 8 16:32:47 2023
19 Type: Flat Device Tree
20 Compression: uncompressed
21 Data Size: 54918 Bytes = 53.63 KiB = 0.05 MiB
22 Architecture: AArch64
23 Hash algo: sha256
24 Hash value: 4b37b6507a6e6f755bb701156f08274f7060062c03793a219de46ac4be8e2014
25Image 2 (ramdisk-1)
26 Description: core-image-readonly-rootfs-overlay-initramfs
27 Created: Wed Feb 8 16:32:47 2023
28 Type: RAMDisk Image
29 Compression: uncompressed
30 Data Size: 4305043 Bytes = 4204.14 KiB = 4.11 MiB
31 Architecture: AArch64
32 OS: Linux
33 Load Address: unavailable
34 Entry Point: unavailable
35 Hash algo: sha256
36 Hash value: 7fa455d2d5a9a6692e1aac67c3683ae795fb6dfe910ca06ee1607c797daa3b51
37 Default Configuration: 'conf-test-imx8mp.dtb'
38Configuration 0 (conf-test-imx8mp.dtb)
39 Description: 1 Linux kernel, FDT blob, ramdisk
40 Kernel: kernel-1
41 Init Ramdisk: ramdisk-1
42 FDT: fdt-test-imx8mp.dtb
43 Hash algo: sha256
44 Hash value: unavailable
This FIT image is generated by a Yocto build using the kernel-fitimage [4] class.
Extract a component from a FIT image
dumpimage is also used to extract any component from the FIT image. E.g.
1$ dumpimage -T flat_dt -p 1 -o devicetree.dtb ./fitImage
2Extracted:
3Image 1 (fdt-test-imx8mp.dtb)
4 Description: Flattened Device Tree blob
5 Created: Wed Feb 8 16:32:47 2023
6 Type: Flat Device Tree
7 Compression: uncompressed
8 Data Size: 54918 Bytes = 53.63 KiB = 0.05 MiB
9 Architecture: AArch64
10 Hash algo: sha256
11 Hash value: 4b37b6507a6e6f755bb701156f08274f7060062c03793a219de46ac4be8e2014
Create a FIT image
There is no magic in creating a FIT image. Just provide an ITS file to mkimage and get back a FIT.
1$ mkimage -f image.its image.fit
FIT support in Yocto
FIT images are supported in Yocto by the kernel-fitimage class [4].
The kernel-fitimage class will create and ITS structure and populate it with the components provided by the machine/local.conf files.
Unfortunately, the current implementation comes with some limitations though. It does only support one kernel image, an optional U-boot script, an optional initramfs, an optional RAM disk and any number of device trees. [5]. So it will not cover all the cases that I wrote about above, but at least multiple device trees are supported and that is the most essential.
Support for multiple initramfs files is on my wishlist though. It would make it possible to, for example, have the initramfs generated by meta-swupdate [6] to support an "update mode" in the same FIT image..
Anyway.
To create a FIT image, include kernel-fitimage to the KERNEL_CLASSES variable and include fitimage to either KERNEL_IMAGETYPE, KERNEL_ALT_IMAGETYPE or KERNEL_IMAGETYPES. In other words, put these lines into your local.conf:
KERNEL_CLASSES += "kernel-fitimage" KERNEL_IMAGETYPES += " fitImage "
The kernel-fitimage class will take care of the rest.
There are also a few other variables that may be set depending on what you want to generate. E.g.
INITRAMFS_IMAGE = "core-image-readonly-rootfs-overlay-initramfs" FIT_SUPPORTED_INITRAMFS_FSTYPES = " cpio.gz "
If you would like to have the FIT image contain an initramfs. The documentation [5] describes this very well.
References
[1] | https://www.marcusfolkesson.se/blog/fit-vs-legacy-image-format/ |
[2] | https://docs.u-boot.org/en/latest/usage/fit/source_file_format.html |
[3] | https://github.com/u-boot/u-boot/blob/master/doc/usage/fit/howto.rst |
[4] | (1, 2) https://git.yoctoproject.org/poky/tree/meta/classes/kernel-fitimage.bbclass?h=kirkstone |
[5] | (1, 2) https://docs.yoctoproject.org/singleindex.html#kernel-fitimage |
[6] | https://sbabic.github.io/swupdate/building-with-yocto.html |