U-Boot: Boot FIT Kernel Image

4 minute read

The way build ramdisk is not an option for me, load both uImage and ramdisk with U-Boot seems not a good way either, and as uImage is a legacy format, there should be a modern format, it is FIT image which is short for Flattened Image Trees, and it has been there for year, and have a handful of advantages over the legacy one, Joel A Fernandes made a presentation on this topic, you can find it on Embedded Linux Wiki.

Generate FIT Image

The components in FIT image are organized just like, device tree, to build it, a its (Image Source Tree) file is required, I took this example and made some changes to make kernel boot on mini2440, for more information regarding the its file, take a look at U-Boot new uImage source file format, this is the its I used for it:

/*
 * U-Boot uImage source file with multiple kernels and ramdisks
 */

/dts-v1/;

/ {
	description = "Various kernels and ramdisks";
	#address-cells = <1>;

	images {
		kernel-1 {
			description = "linux-5.9.11 debug version for mini2440";
			data = /incbin/("./zImage.dbg");
			type = "kernel";
			arch = "arm";
			os = "linux";
			compression = "none";
			load = <0x30008000>;
			entry = <0x30008000>;
			hash-1 {
				algo = "md5";
			};
			hash-2 {
				algo = "sha1";
			};
		};

		kernel-2 {
			description = "linux-5.9.11 mini2440";
			data = /incbin/("./zImage");
			type = "kernel";
			arch = "arm";
			os = "linux";
			compression = "none";
			load = <0x30008000>;
			entry = <0x30008000>;
			hash-1 {
				algo = "sha1";
			};
		};

		ramdisk-1 {
			description = "cpio ramdisk for mini2440";
			data = /incbin/("./ramdisk.cpio.img");
			type = "ramdisk";
			arch = "arm";
			os = "linux";
			compression = "none";
			load = <0x33000000>;
			entry = <0x33000000>;
			hash-1 {
				algo = "sha1";
			};
		};
	};

	configurations {
		default = "config-1";

		config-1 {
			description = "mini2440 5.9.11 debug configuration";
			kernel = "kernel-1";
			ramdisk = "ramdisk-1";
		};

		config-2 {
			description = "mini2440 5.9.11 configuration";
			kernel = "kernel-2";
			ramdisk = "ramdisk-1";
		};
	};
};

We have zImage, ramdisk, and its file, the FIT image can be generated with mkimage, put them in the same directory and create the final image with:

$ mkimage -f kernel.its kernel.itb
FIT description: Various kernels and ramdisks
Created:         Fri Jan  1 21:16:17 2021
 Image 0 (kernel-1)
  Description:  linux-5.9.11 debug version for mini2440
  Created:      Fri Jan  1 21:16:17 2021
  Type:         Kernel Image
  Compression:  uncompressed
  Data Size:    2353560 Bytes = 2298.40 KiB = 2.24 MiB
  Architecture: ARM
  OS:           Linux
  Load Address: 0x30008000
  Entry Point:  0x30008000
  Hash algo:    md5
  Hash value:   a15208f937b196988b77ca51eab8f42e
  Hash algo:    sha1
  Hash value:   de0b5302fee57763a95e1daf0c8aa52af1f41b96
 Image 1 (kernel-2)
  Description:  linux-5.9.11 mini2440
  Created:      Fri Jan  1 21:16:17 2021
  Type:         Kernel Image
  Compression:  uncompressed
  Data Size:    2254480 Bytes = 2201.64 KiB = 2.15 MiB
  Architecture: ARM
  OS:           Linux
  Load Address: 0x30008000
  Entry Point:  0x30008000
  Hash algo:    sha1
  Hash value:   f4cff2995aff1220848ba39c96d928ab2bdac55a
 Image 2 (ramdisk-1)
  Description:  cpio ramdisk for mini2440
  Created:      Fri Jan  1 21:16:17 2021
  Type:         RAMDisk Image
  Compression:  uncompressed
  Data Size:    2691561 Bytes = 2628.48 KiB = 2.57 MiB
  Architecture: ARM
  OS:           Linux
  Load Address: 0x33000000
  Entry Point:  0x33000000
  Hash algo:    sha1
  Hash value:   4ed9e3ae849eb9fcaeb59d219b74f9d82c64d780
 Default Configuration: 'config-1'
 Configuration 0 (config-1)
  Description:  mini2440 5.9.11 debug configuration
  Kernel:       kernel-1
  Init Ramdisk: ramdisk-1
 Configuration 1 (config-2)
  Description:  mini2440 5.9.11 configuration
  Kernel:       kernel-2
  Init Ramdisk: ramdisk-1

The components packed into the FIT image can be extracted with dumpimage, for example to extract the first file:

$ dumpimage -T flat_dt -p 0  kernel.itb -o zImage.dbg

Use the -p option to specify the index of the image you wanna extract, the index in the FIT image can be examined with -l option:

$ dumpimage -l kernel.itb

Boot FIT Kernel Image with bootm

To boot FIT kernel image with U-Boot, FIT supported is mandatory, build U-Boot with below configs:

+CONFIG_FIT=y
+CONFIG_FIT_VERBOSE=y

Download and boot kernel with:

MINI2440 # tftp 30800000 kernel.itb
MINI2440 # bootm 30800000

This will boot with the default configuration, which is config-1.

Boot other kernel image is also supported by bootm:

MINI2440 # bootm 30800000#config-1
MINI2440 # bootm :kernel-2 :ramdisk-1
MINI2440 # bootm 30800000:kernel-1 30800000:ramdisk-1
MINI2440 # bootm 30800000:kernel-1 :ramdisk-1

More combinations can be found in Command syntax extensions for the new uImage format.

The image info can be inspected with iminfo, for example:

MINI2440 # iminfo 0x30800000

## Checking Image at 30800000 ...
   FIT image found
   FIT description: Various kernels and ramdisks
    Image 0 (kernel-1)
     Description:  linux-5.9.11 debug version for mini2440
     Type:         Kernel Image
     Compression:  uncompressed
     Data Start:   0x308000e8
     Data Size:    2353560 Bytes = 2.2 MiB
     Architecture: ARM
     OS:           Linux
     Load Address: 0x30008000
     Entry Point:  0x30008000
     Hash algo:    md5
     Hash value:   a15208f937b196988b77ca51eab8f42e
     Hash algo:    sha1
     Hash value:   de0b5302fee57763a95e1daf0c8aa52af1f41b96
[...]
## Checking hash(es) for FIT Image at 30800000 ...
   Hash(es) for Image 0 (kernel-1): md5+ sha1+
   Hash(es) for Image 1 (kernel-2): sha1+
   Hash(es) for Image 2 (ramdisk-1): sha1+

Troubleshooting

Could not find configuration node

MINI2440 # tftp 30800000 kernel.itb
dm9000 i/o: 0x20000000, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 0c:96:e6:15:09:12
could not establish link
Using dm9000 device
TFTP from server 192.168.0.3; our IP address is 192.168.0.89
Filename 'kernel.itb'.
## Current stack ends at 0x33bbcc40 Load address: 0x30800000
Loading: #T ################################################################
[...]
	 ##############################################################
	 790 KiB/s
done
Bytes transferred = 7301836 (6f6acc hex)
MINI2440 # bootm 30800000
## Current stack ends at 0x33bbccf0 ## Loading kernel from FIT Image at 30800000 ...
Could not find configuration node
ERROR: can't get kernel image!

Fixed by removing FIT_BEST_MATCH in default config file.

Wrong Ramdisk Image Format

MINI2440 # bootm 30800000:kernel-1 33000000:ramdisk-1
## Current stack ends at 0x33bbccf0 ## Loading kernel from FIT Image at 30800000 ...
   Trying 'kernel-1' kernel subimage
     Description:  linux-5.9.11 debug version for mini2440
     Type:         Kernel Image
     Compression:  uncompressed
     Data Start:   0x308000e8
     Data Size:    2353560 Bytes = 2.2 MiB
     Architecture: ARM
     OS:           Linux
     Load Address: 0x30008000
     Entry Point:  0x30008000
     Hash algo:    md5
     Hash value:   a15208f937b196988b77ca51eab8f42e
     Hash algo:    sha1
     Hash value:   de0b5302fee57763a95e1daf0c8aa52af1f41b96
   Verifying Hash Integrity ... md5+ sha1+ OK
Wrong Ramdisk Image Format
Ramdisk image is corrupt or invalid

This is because the ramdisk does not exist at that address:

MINI2440 # iminfo 33000000

## Checking Image at 33000000 ...
Unknown image format!