Flattened Image Tree Specification v0.8-30-g795fd5f¶
1. Copyright¶
THIS SPECIFICATION PROVIDED “AS IS” AND WITHOUT ANY WARRANTY OF ANY KIND, INCLUDING, WITHOUT LIMITATION, ANY EXPRESS OR IMPLIED WARRANTY OF NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL LINARO OR ANY MEMBER OF LINARO BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL DAMAGES, INCLUDING, WITHOUT LIMITATION, LOST PROFITS, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
Questions pertaining to this document, or the terms or conditions of its provision, should be addressed to:
1.1. License Information¶
Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
2. Acknowledgements¶
FIT (Flattened Image Tree) was developed in 2008 by Marian Balakowicz and Bartlomiej Sieka of Semihalf, under the guidance of Wolfgang Denk, founder of Denx Software Engineering and creator of U-Boot.
Since then, FIT has been maintained and extended by the U-Boot community to deal with the developing needs of Open Source firmware.
This specification builds on this previous work.
FIT has stood the test of time due to its simplicity and extensibility. This specification aims to build on this work and provide a means for further improvement with a wider group of collaborators.
3. Revision History¶
Revision |
Date |
Description |
|---|---|---|
0.8 |
2023-AUG-9 |
Initial prerelease version. Imported text from U-Boot source tree. |
4. Introduction¶
4.1. Purpose and Scope¶
The number of elements playing a role in the kernel booting process has increased over time and now typically includes the devicetree, kernel image and possibly a ramdisk image. Generally, all must be placed in the system memory and booted together.
For firmware images a similar process has taken place, with various binaries loaded at different addresses, such as ARM’s ATF, OpenSBI, FPGA and U-Boot itself.
FIT provides a flexible and extensible format to deal with this complexity. It provides support for multiple components. It also supports multiple configurations, so that the same FIT can be used to boot multiple boards, with some components in common (e.g. kernel) and some specific to that board (e.g. devicetree).
This specification, the Flattened Image Tree Specification (FITSpec), provides a suitable format which can be used to describe any set of files along with grouping and selection mechanisms.
Chapter 4 introduces the purpose and background of FITSpec.
Chapter 5 introduces the FIT concept and describes its logical structure and standard properties. certain classes of devices and specific device types.
Chapter 6 describes how FIT is used in bootloaders to handle booting Operating Systems as well as firmware.
Conventions Used in this Document
The word shall is used to indicate mandatory requirements strictly to be followed in order to conform to the standard and from which no deviation is permitted (shall equals is required to).
The word should is used to indicate that among several possibilities one is recommended as particularly suitable, without mentioning or excluding others; or that a certain course of action is preferred but not necessarily required; or that (in the negative form) a certain course of action is deprecated but not prohibited (should equals is recommended that).
The word may is used to indicate a course of action permissible within the limits of the standard (may equals is permitted).
Examples of devicetree constructs are frequently shown in Devicetree Syntax form. See [dtspec] for a description of this.
4.2. Relationship to Devicetree Specification¶
FITSpec is based on the Devicetree Specification, in that it uses the same structure and shares some concepts.
4.3. 32-bit and 64-bit Support¶
The FITSpec supports CPUs with both 32-bit and 64-bit addressing capabilities. Where applicable, sections of the FITSpec describe any requirements or considerations for 32-bit and 64-bit addressing.
4.4. Definition of Terms¶
5. Flattened Image Tree (FIT) Format¶
5.1. Introduction¶
FIT consists of a devicetree blob with nodes and properties following a certain schema. Therefore this document defines FIT by providing FDT (Flat Device Tree) bindings. These describe the final form of the FIT at the moment when it is used. The user perspective may be simpler, as some of the properties (like timestamps and hashes) are filled in automatically by available tooling, such as mkimage.
To avoid confusion with the kernel FDT the following naming convention is used:
- FIT
Flattened Image Tree
FIT is formally a flattened devicetree (in the libfdt meaning), which conforms to bindings defined in this document.
- .its
image tree source
- .fit
flattened image tree blob
This was previously known as .itb but has been renamed to .fit.
5.1.1. Image-building procedure¶
The following picture shows how the FIT is prepared. Input consists of image source file (.its) and a set of data files. Image is created with the help of standard U-Boot mkimage tool which in turn uses dtc (device tree compiler) to produce image tree blob (.fit). The resulting .fit file is the actual binary of a new FIT:
tqm5200.its
+
vmlinux.bin.gz mkimage + dtc xfer to target
eldk-4.2-ramdisk --------------> tqm5200.fit --------------> boot
tqm5200.dtb /|\
|
'new FIT'
Steps:
Create .its file, automatically filled-in properties are omitted
Call mkimage tool on .its file
mkimage calls dtc to create .fit image and assures that missing properties are added
.fit (new FIT) is uploaded onto the target and used therein
5.1.2. Unique identifiers¶
To identify FIT sub-nodes representing images, hashes, configurations (which are defined in the following sections), the “unit name” of the given sub-node is used as its identifier as it assures uniqueness without additional checking required.
5.1.3. External data¶
FIT is normally built initially with image data in the ‘data’ property of each image node. It is also possible for this data to reside outside the FIT itself. This allows the ‘FDT’ part of the FIT to be quite small, so that it can be loaded and scanned without loading a large amount of data. Then when an image is needed it can be loaded from an external source.
External FITs use ‘data-offset’ or ‘data-position’ instead of ‘data’.
The mkimage tool can convert a FIT to use external data using the -E argument, optionally using -p to specific a fixed position.
It is often desirable to align each image to a block size or cache-line size (e.g. 512 bytes), so that there is no need to copy it to an aligned address when reading the image data. The mkimage tool provides a -B argument to support this.
5.2. Root-node properties¶
The root node of the FIT should have the following layout:
/ o image-tree
|- description = "image description"
|- timestamp = <12399321>
|- #address-cells = <1>
|
o images
| |
| o image-1 {...}
| o image-2 {...}
| ...
|
o configurations
|- default = "conf-1"
|
o conf-1 {...}
o conf-2 {...}
...
5.2.1. Optional property¶
- description
Textual description of the FIT
5.2.2. Mandatory property¶
- timestamp
Last image modification time being counted in seconds since 1970-01-01 00:00:00 - to be automatically calculated by mkimage tool.
5.2.3. Conditionally mandatory property¶
- #address-cells
Number of 32bit cells required to represent entry and load addresses supplied within sub-image nodes. May be omitted when no entry or load addresses are used.
5.2.4. Mandatory nodes¶
- images
This node contains a set of sub-nodes, each of them representing single component sub-image (like kernel, ramdisk, etc.). At least one sub-image is required.
- configurations
Contains a set of available configuration nodes and defines a default configuration.
5.3. ‘/images’ node¶
This node is a container node for component sub-image nodes. Each sub-node of the ‘/images’ node should have the following layout:
o image-1
|- description = "component sub-image description"
|- data = /incbin/("path/to/data/file.bin")
|- type = "sub-image type name"
|- arch = "ARCH name"
|- os = "OS name"
|- compression = "compression name"
|- load = <00000000>
|- entry = <00000000>
|
o hash-1 {...}
o hash-2 {...}
o dm-verity {...}
...
5.3.1. Mandatory properties¶
- description
Textual description of the component sub-image
- type
Name of component sub-image type. Supported types are:
Sub-image type
Meaning
invalid
Invalid Image
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 Device Image (bitstream file, vendor specific)
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
tfa-bl31
Trusted Firmware-A BL31 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
- compression
Compression used by included data. If no compression is used, the compression property should be set to “none”. If the data is compressed but it should not be uncompressed by the loader (e.g. compressed ramdisk <pair: ramdisk; compressed), this should also be set to “none”.
Supported compression types are:
Compression type
Meaning
none
uncompressed
bzip2
bzip2 compressed
gzip
gzip compressed
lz4
lz4 compressed
lzma
lzma compressed
lzo
lzo compressed
zstd
zstd compressed
5.3.2. Conditionally mandatory properties¶
- data
Path to the external file which contains this node’s binary data. Within the FIT this is the contents of the file. This is mandatory unless external data is used.
- data-size
Size of the data in bytes. This is mandatory if external data is used.
- data-offset
Offset of the data in a separate image store. The image store is placed immediately after the last byte of the device tree binary, aligned to a 4-byte boundary. This is mandatory if external data is used, with an offset.
- data-position
Machine address at which the data is to be found. This is a fixed address not relative to the loading of the FIT. This is mandatory if external data is used with a fixed address.
- os
OS name, mandatory for types “kernel”. Valid OS names are:
OS name
Meaning
invalid
Invalid OS
4_4bsd
4_4BSD
arm-trusted-firmware
ARM Trusted Firmware
dell
Dell
efi
EFI Firmware
esix
Esix
freebsd
FreeBSD
integrity
INTEGRITY
irix
Irix
linux
Linux
ncr
NCR
netbsd
NetBSD
openbsd
OpenBSD
openrtos
OpenRTOS
opensbi
RISC-V OpenSBI
ose
Enea OSE
plan9
Plan 9
psos
pSOS
qnx
QNX
rtems
RTEMS
sco
SCO
solaris
Solaris
svr4
SVR4
tee
Trusted Execution Environment
u-boot
U-Boot
vxworks
VxWorks
- arch
Architecture name, mandatory for types: “standalone”, “kernel”, “firmware”, “ramdisk” and “fdt”. Valid architecture names are:
Architecture type
Meaning
invalid
Invalid ARCH
alpha
Alpha
arc
ARC
arm64
AArch64
arm
ARM
avr32
AVR32
blackfin
Blackfin
ia64
IA64
m68k
M68K
microblaze
MicroBlaze
mips64
MIPS 64 Bit
mips
MIPS
nds32
NDS32
nios2
NIOS II
or1k
OpenRISC 1000
powerpc
PowerPC
ppc
PowerPC
riscv
RISC-V
s390
IBM S390
sandbox
Sandbox
sh
SuperH
sparc64
SPARC 64 Bit
sparc
SPARC
x86_64
AMD x86_64
x86
Intel x86
xtensa
Xtensa
- entry
Entry point address, address size is determined by ‘#address-cells’ property of the root node. Mandatory for types: “firmware”, and “kernel”.
- load
Load address, address size is determined by ‘#address-cells’ property of the root node. Mandatory for types: “firmware”, and “kernel”.
- compatible
Compatible method for loading image. Mandatory for types: “fpga”, and images that do not specify a load address. Supported compatible methods:
Compatible string
Meaning
u-boot,fpga-legacy
Generic fpga loading routine.
u-boot,zynqmp-fpga-ddrauth
Signed non-encrypted FPGA bitstream for Xilinx Zynq UltraScale+ (ZymqMP) device.
u-boot,zynqmp-fpga-enc
Encrypted FPGA bitstream for Xilinx Zynq UltraScale+ (ZynqMP) device.
Note
For fdt images, the node should not have a compatible for the model. The compatible here is not derived from the fdt, nor is it used to identify the fdt. Such usage belongs in the configuration node.
- phase
U-Boot phase for which the image is intended.
- “spl”
image is an SPL image
- “u-boot”
image is a U-Boot image
5.3.3. Optional nodes¶
- hash-1
Each hash sub-node represents a separate hash or checksum calculated for node’s data according to specified algorithm.
- signature-1
Each signature sub-node represents a separate signature calculated for node’s data according to specified algorithm.
- dm-verity
For images of type
filesystem, this sub-node carries dm-verity Merkle-tree metadata so that the bootloader can construct kernel command-line parameters for integrity-verified boot. See dm-verity nodes.
5.4. Hash nodes¶
o hash-1
|- algo = "hash or checksum algorithm name"
|- value = [hash or checksum value]
5.4.1. Mandatory properties¶
- algo
Algorithm name. Supported algorithms and their value sizes are:
Sub-image type
Size (bytes)
Meaning
crc16-ccitt
2
Cyclic Redundancy Check 16-bit (Consultative Committee for International Telegraphy and Telephony)
crc32
4
Cyclic Redundancy Check 32-bit
md5
16
Message Digest 5 (MD5)
sha1
20
Secure Hash Algorithm 1 (SHA1)
sha256
32
Secure Hash Algorithm 2 (SHA256)
sha384
48
Secure Hash Algorithm 2 (SHA384)
sha512
64
Secure Hash Algorithm 2 (SHA512)
- value
Actual checksum or hash value.
5.5. Image-signature nodes¶
o signature-1
|- algo = "algorithm name"
|- key-name-hint = "key name"
|- value = [hash or checksum value]
5.5.1. Mandatory properties¶
FIT Algorithm:
- algo
Algorithm name. Supported algorithms and their value sizes are shown below. Note that the hash is specified separately from the signing algorithm, so it is possible to mix and match any SHA algorithm with any signing algorithm. The size of the signature relates to the signing algorithm, not the hash, since it is the hash that is signed.
Sub-image type
Size (bytes)
Meaning
sha1,rsa2048
256
SHA1 hash signed with 2048-bit Rivest–Shamir–Adleman algorithm
sha1,rsa3072
384
SHA1 hash signed with 2048-bit RSA
sha1,rsa4096
512
SHA1 hash signed with 2048-bit RSA
sha1,ecdsa256
32
SHA1 hash signed with 256-bit Elliptic Curve Digital Signature Algorithm
sha256,…
sha384,…
sha512,…
- key-name-hint
Name of key to use for signing. The keys will normally be in a single directory (parameter -k to mkimage). For a given key <name>, its private key is stored in <name>.key and the certificate is stored in <name>.crt.
- sign-images
An unsorted list of images to sign, each being a property of the conf node that contains them. The default is “kernel,fdt” which means that these two images will be looked up in the config and signed if present. This is used by mkimage to determine which images to sign.
The following properties are added as part of signing, and are mandatory:
- value
Actual signature value. This is added by mkimage.
- hashed-nodes
An unsorted list of nodes which were hashed by the signer. Each is a string - the full path to node. Since this property is not itself protected by a hash, it serves only as a hint for the signer and must not be relied upon by the loader for validation purposes. A typical value might be:
hashed-nodes = "/", "/configurations/conf-1", "/images/kernel", "/images/kernel/hash-1", "/images/fdt-1", "/images/fdt-1/hash-1";- hashed-strings
The start and size of the string region of the FIT that was hashed. The start is normally 0, indicating the first byte of the string table. The size indicates the number of bytes hashed as part of signing.
The following properties are added as part of signing, and are optional:
- timestamp
Time when image was signed (standard Unix time_t format)
- signer-name
Name of the signer (e.g. “mkimage”)
- signer-version
Version string of the signer (e.g. “2013.01”)
- comment
Additional information about the signer or image
- padding
The padding algorithm, it may be pkcs-1.5 or pss, if no value is provided we assume pkcs-1.5
5.6. dm-verity nodes¶
Image nodes whose type is filesystem may contain an optional dm-verity
child node. The bootloader uses this metadata to construct dm-mod.create
and dm-mod.waitfor kernel command-line parameters so that the kernel can set
up a dm-verity integrity
target over the corresponding block device at boot.
See dm-verity for filesystem images for details on how a bootloader should translate these properties into kernel command-line parameters.
o dm-verity
|- data-block-size = <data block size in bytes>
|- hash-block-size = <hash block size in bytes>
|- num-data-blocks = <number of data blocks>
|- hash-start-block = <hash tree start block>
|- algo = "hash algorithm name"
|- digest = [root hash bytes]
|- salt = [salt bytes]
The property names are intentionally aligned with the dm-verity construction
parameters
defined by the Linux kernel. The <version> parameter is always 1, and
<dev> / <hash_dev> both implicitly refer to the /dev/fitN block
device that the Linux uImage.FIT block driver creates for this sub-image.
5.6.1. Mandatory properties¶
- data-block-size
The block size on the data device in bytes. Each block corresponds to one digest in the hash tree. Must be a power of two and at least 512; typically 4096.
- hash-block-size
The size of a hash block in bytes. Must be a power of two and at least 512; typically 4096.
- num-data-blocks
The number of data blocks on the data device. This corresponds to the
<num_data_blocks>dm-verity parameter and the value reported byveritysetup format.- hash-start-block
Offset in
hash-block-size-sized blocks from the start of the sub-image to the root block of the hash tree. Corresponds to the<hash_start_block>dm-verity parameter.- algo
The cryptographic hash algorithm used for dm-verity, e.g.
"sha256".- digest
The root hash of the dm-verity Merkle tree, stored as a raw byte array. The length must match the output size of
algo.- salt
Salt value, stored as a raw byte array.
5.6.2. Optional properties¶
- restart-on-corruption
Boolean. Restart the system when a corrupted block is discovered. Corresponds to
restart_on_corruption.- panic-on-corruption
Boolean. Panic the system when a corrupted block is discovered. Not compatible with
restart-on-corruption. Corresponds topanic_on_corruption.- restart-on-error
Boolean. Restart the system when an I/O error is detected. Corresponds to
restart_on_error.- panic-on-error
Boolean. Panic the system when an I/O error is detected. Not compatible with
restart-on-error. Corresponds topanic_on_error.- check-at-most-once
Boolean. Verify data blocks only the first time they are read, reducing overhead on constrained systems at the cost of not detecting online tampering. Corresponds to
check_at_most_once.
When none of the error-handling properties are present, the kernel default behaviour (return I/O error) applies.
Additional optional parameters defined by the kernel’s dm-verity target may be added as properties in future revisions of this specification.
These values correspond to the parameters passed to dm-mod.create on the
kernel command line (see dm-verity for filesystem images).
Both the filesystem payload data and the dm-verity Merkle-tree hash data are expected to reside inside the same sub-image.
5.7. ‘/configurations’ node¶
The ‘configurations’ node creates convenient, labeled boot configurations, which combine together kernel images with their ramdisks and fdt blobs.
The ‘configurations’ node has the following structure:
o configurations
|- default = "default configuration sub-node unit name"
|
o config-1 {...}
o config-2 {...}
...
5.7.1. Optional property¶
- default
Selects one of the configuration sub-nodes as a default configuration.
5.7.2. Mandatory nodes¶
- configuration-sub-node-unit-name
At least one configuration sub-node is required.
5.7.3. Optional nodes¶
- signature-1
Each signature sub-node represents a separate signature calculated for the configuration according to specified algorithm.
5.8. Configuration nodes¶
Each configuration has the following structure:
o config-1
|- description = "configuration description";
|- kernel = "kernel sub-node unit name";
|- cmdline = "command line for next boot stage";
|- fdt = "fdt sub-node unit-name" [, "fdt overlay sub-node unit-name", ...];
|- ramdisk = "ramdisk sub-node unit-name";
|- loadables = "loadables sub-node unit-name" [, ...];
|- script = "script sub-node unit-name";
|- compatible = "vendor,board-style device tree compatible string";
o signature-1 {...}
5.8.1. Mandatory properties¶
- description
Textual configuration description.
5.8.2. Conditionally mandatory property¶
- kernel or firmware
Unit name of the corresponding kernel or firmware (u-boot, op-tee, etc) image. If both “kernel” and “firmware” are specified, control is passed to the firmware image. For load_only images, these two properties are optional.
5.8.3. Optional properties¶
- cmdline
Command line passed to the next boot stage, e.g. the operating system kernel. The value is an UTF-8 encoded string.
- fdt
Unit name of the corresponding fdt blob (component image node of a “fdt type”). Additional fdt overlay nodes can be supplied which signify that the resulting device tree blob is generated by the first base fdt blob with all subsequent overlays applied.
- fpga
Unit name of the corresponding fpga bitstream blob (component image node of a “fpga type”).
- loadables
Unit name containing a list of additional binaries to be loaded at their given locations. “loadables” is a comma-separated list of strings. U-Boot will load each binary at its given load address (see load) and may optionally invoke additional post-processing steps on this binary based on its component image node type.
- ramdisk
Unit name of the corresponding ramdisk to be loaded at the given location.
- script
The image to use when loading a U-Boot script (for use with the source command).
- compatible
The root compatible string of the bootloader device tree that this configuration shall automatically match. If this property is not provided, the compatible string will be extracted from the fdt blob instead. This is only possible if the fdt is not compressed, so images with compressed fdts that want to use compatible string matching must always provide this property.
Note that U-Boot requires the CONFIG_FIT_BEST_MATCH option to be enabled for this matching to work.
- load-only
Indicates that this configuration does not necessarily contain an executable image, i.e. kernel or firmware. The configuration’s images may be loaded into memory for use by the executable image, which comes from another configuration or FIT. See see Multi-step loading.
The FDT blob is required to properly boot FDT-based kernel, so the minimal configuration for 2.6 FDT kernel is (kernel, fdt) pair.
Older, 2.4 kernel and 2.6 non-FDT kernel do not use FDT blob, in such cases ‘struct bd_info’ must be passed instead of FDT blob, thus fdt property must not be specified in a configuration node.
5.9. Configuration-signature nodes¶
o signature-1
|- algo = "algorithm name"
|- key-name-hint = "key name"
|- sign-images = "path1", "path2";
|- value = [hash or checksum value]
|- hashed-strings = <0 len>
5.9.1. Mandatory properties¶
- algo
See FIT Algorithm.
- key-name-hint
Name of key to use for signing. The keys will normally be in a single directory (parameter -k to mkimage). For a given key <name>, its private key is stored in <name>.key and the certificate is stored in <name>.crt.
The following properties are added as part of signing, and are mandatory:
- value
Actual signature value. This is added by mkimage.
The following properties are added as part of signing, and are optional:
- timestamp
Time when image was signed (standard Unix time_t format)
- signer-name
Name of the signer (e.g. “mkimage”)
- signer-version
Version string of the signer (e.g. “2013.01”)
- comment
Additional information about the signer or image
- padding
The padding algorithm, it may be pkcs-1.5 or pss, if no value is provided we assume pkcs-1.5
6. Flattened Image Tree (FIT) Usage¶
6.1. Introduction¶
This section describes how FIT is typically used. This is not necessarily proscriptive but may be useful for those implementing this specification.
6.2. Boot process¶
At some point in the boot process, the bootloader select and boot an Operating System. To do this, it follows these steps:
Load a FIT into memory
Select a configuration to boot
Load the images from the selected configuration
Fix up the devicetree
Jump to the OS
Each of these is now dealt with in turn.
6.2.1. Load a FIT into memory¶
The bootloader provides a way to select a FIT to load into memory. This is typically on boot media available to the bootloader, such as eMMC or UFS.
There may be multiple FITs available. The mechanism for locating and selecting a FIT is not defined by this specification. See for example [VBE].
The bootloader may load the entire FIT into memory at once, before processing it. For simple applications where there are just a few images, this is the easiest approach.
Where there are many configuration and several images, such that only a subset of the available images will actually be used on any one boot, it is inefficient to load the entire FIT, since most of the loaded data will not be used. In this case, an external-data FIT can be used. See External data.
In this case, the bootloader reads the FDT header (say 64 bytes), checks that
it is valid, then reads enough more bytes to bring in totalsize bytes
(totalsize is the second 32-bit word in the header). Typically this will be
a few KB of data, consisting just of the FIT metadata. Later, the bootloader can
read more data from the FIT as it needs to load each image.
Another case that sometimes comes up is loading images from a FIT into internal
SRAM, which may be very limited. In that case it may be useful to align images
on a storage-device’s block boundary (see -B flag in External data).
The bootloader can then avoid needing bounce buffers and other complications.
6.2.2. Select a configuration to boot¶
The FIT typically contains more than one configuration. It is common to use a
separate configuration for each supported model. The configuration contains
a compatible stringlist which indicates which models the configuration is
compatible with.
The bootloader itself typically has a compatible stringlist, indicating the model that it is running on. For U-Boot this is in the root node of the devicetree used by U-Boot, typically exactly the same devicetree as is used by Linux for that model. For other bootloaders, the stringlist may be hard-coded, or obtained by some other means.
The bootloader should loop through each configuration to find the best match to its own compatible string. The best match is the configuration which matches earliest string in the bootloader’s compatible stringlist.
For example, imagine the bootloader has compatible = "foo,bar", "bim,bam"
and the FIT has two configurations:
config-1 {
compatible = "foo,bar";
fdt = "fdt-1";
...
};
config-2 {
compatible = "bim,bam", "baz,biz";
fdt = "fdt-2";
...
};
Here, the bootloader chooses config-1 since it is a better match. The first
string in the bootloader’s compatible list, "foo,bar", matches a compatible
string in the root of fdt1. Although "bim,bam" in fdt2 matches the
second string, this isn’t as good a match as fdt1.
In U-Boot this algorithm is handled by fit_conf_find_compat() and enabled
by the CONFIG_FIT_BEST_MATCH option.
Sometime models have multiple PCB revisions or different minor variants, often referred to as SKUs. For this reason, bootloaders may want to select configurations in a finer-grained way. In this case, rather than using the compatible stringlist in its devicetree, if any, it constructs a single string using the base name along with any available suffixes, each beginning with a hyphen. The best match algorithm is then run using that string.
The following compatible-string suffixes may be used to this end. They must be provided in this order (<n> is an integer >= 0):
-rev<n>Board revision number, typically referring to a revision of the PCB to fix a problem or adjust component selection. The intention is that the board is the same design, just with some minor fixes or improvements. The first revision is typically
rev0.-sku<n>Board variant, called a SKU (Stock-Keeping Unit) which is a unique code that identifies a model variant. This may encode differences in the display, WiFi and the like, but where the same PCB design (and revision) is used. The base SKU is typically
sku0.
Examples:
compatible = "google,kevin-rev15";
compatible = "google,kevin-rev15-sku2";
When matching, the bootloader should build the most specific string it can using
any available revision / SKU information, then try to match that. If the most
specific string fails (e.g. "google,kevin-rev15-sku2"), it should fall back
to just "google,kevin-rev15" and then "google,kevin-sku2". If nothing
matches, then it should try without any additions, i.e. "google,kevin".
This multi-stage process uses the same ‘best match’ approach as above. Each attempt finds the best match given the compatible string being searched. Where a stage does not find any match, the next stage begins. As soon as a match is found, searching stops, using the best match found in the stage.
Other suffixes may be added in future.
6.2.3. Load the images from the selected configuration¶
The configuration contains a number of images. One of these is the OS itself. Another is typically a devicetree blob, which provides information about available devices, useful for the OS as it boots and runs. Another image may be a ramdisk (or initrd) which provides an initial root disk for the OS to use, before it is able to access the real root disk.
The bootloader reads each image from the FIT and ‘loads’ it to the correct
address. This address may be provided by the image’s load property
(see load), but if not provided, the bootloader can load it to any
suitable address. In some cases it may be possible to avoid loading the image
and just refer to the image data within the FIT itself.
6.2.4. Fix up the devicetree¶
Many Operating Systems use devicetree blobs for configuration. As a result, most bootloaders provide a way to update the devicetree in the FIT before passing it to the OS. This may be used to pass command-line parameters to Linux, to select the console device to use, or to pass the ramdisk to the OS. It is also common to enable or disable certain devicetree nodes based on the hardware in use.
The fixups required depend on the OS and its expectations. The result is a devicetree slightly modified from the FIT version.
6.2.5. Jump to the OS¶
Once everything is ready, the bootloader jumps to the OS. At this point the FIT is no longer in use. The OS typically does not see the FIT itself and only cares about the images that were loaded. At this point, the FIT has served its purpose.
6.3. Firmware usage¶
As firmware has become more complex, with multiple binaries loaded at each phase of the boot, it has become common to use FIT to load firmware.
In this case, there is the concept of a boot phase (see phase), indicating which phase each image is for.
In this case the bootloader itself is likely split into multiple phases. For U-Boot, a common approach is for SPL (Secondary Program Loader) to load U-Boot proper, along with ATF and any other images required by U-Boot proper.
FIT processing for firmware images is no different from the approach described
above, except that any image with a phase property is only loaded if the
phase matches the phase being loaded. So, for example, SPL loads U-Boot proper
so will only load images with a phase of “u-boot”. If TPL is in use (the phase
before SPL), then TPL will only load images with a phase of “spl”. This allows
all images to be provided in a single FIT, with each phase pulling out what is
needed as the boot proceeds.
6.4. Multi-step loading¶
The most common use of a FIT is where each configuration contains everything needed to boot. For example, on ARM systems a configuration contains a kernel, devicetree(s) and a ramdisk if needed. This approach is widely used on embedded systems.
This approach is not always desirable, however, particularly when the firmware and the OS are supplied by different parties. In that case, the devicetree may be provided by the firmware with the other pieces coming from the OS. This means that FIT may omit the devicetree images.
With devicetree in particular, it is common for the OS to provide its own version, or perhaps a devicetree overlay to add some new nodes and properties.
Obviously if the OS has to provide a devicetree for every device, the OS files would become very large. A middle path could be that the hardware vendor provides a FIT on a boot partition, containing devicetrees for hardware supported by that vendor. Then the bootloader can load that FIT to get just the devicetree, followed by the main FIT to load the OS.
To enable this last case, add a load-only property to the configuration. This signals to the bootloader that it should not require an executable (i.e. kernel or firmware), nor should it try to boot with this configuration. Booting then becomes a two-step process: load one FIT to obtain the devicetree, then another to obtain the OS. Only the second FIT is booted.
Specifically, the ‘load-only’ property adjusts the meaning of loading a FIT, so that implementors should follow the following behaviour:
‘load-only’ present |
Executable present |
Behaviour |
|---|---|---|
no |
no |
Raise an error |
yes |
no |
Only load the images |
no |
yes |
Execute binary |
yes |
yes |
Execute binary |
6.5. dm-verity for filesystem images¶
A FIT may contain filesystem-type sub-images that carry a dm-verity
child node (see dm-verity nodes). Such images bundle both the
filesystem payload and the dm-verity Merkle-tree hash data inside the same
sub-image. A Linux block driver exposes each loadable sub-image as
/dev/fit0, /dev/fit1, etc.
When a dm-verity node is present, the bootloader should translate its
properties into kernel command-line parameters so that the kernel can activate
a dm-verity integrity target at boot, before mounting the root filesystem.
Two parameters are needed:
dm-mod.waitforTells
dm-initto wait for the block device to appear before creating mapped devices. The bootloader should list the/dev/fitNdevice that corresponds to the sub-image:dm-mod.waitfor=/dev/fit0
dm-mod.createDefines a device-mapper table using the
--conciseformat accepted bydmsetup. The general form for a verity target is:<name>,<uuid>,<minor>,ro, 0 <num_sectors> verity <version> <dev> <hash_dev> <data_block_size> <hash_block_size> <num_data_blocks> <hash_start_block> <algorithm> <digest> <salt> [<#opt_params> <opt_params>]
Because both the filesystem data and the hash tree reside inside the same
/dev/fitNdevice,<dev>and<hash_dev>are identical and shall be set by the bootloader. The<version>is always1.
6.5.1. Field mapping¶
The following table shows how each dm-verity construction parameter
is derived from the dm-verity node properties.
dm-verity parameter |
Source |
|---|---|
|
The unit name of the |
|
May be left empty ( |
|
May be left empty ( |
|
|
|
Always |
|
Both set to the |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Constructed from the boolean option properties present in the
|
6.5.2. Example¶
Given a filesystem sub-image node named rootfs-1,
exposed as /dev/fit0, whose dm-verity node contains:
dm-verity {
data-block-size = <4096>;
hash-block-size = <4096>;
num-data-blocks = <204800>;
hash-start-block = <204800>;
algo = "sha256";
digest = [ac 87 db 56 30 3c 9c 1d a4 33 d7 20 9b 5a 6e f3
e4 77 9d f1 41 20 0c bd 7c 15 7d cb 8d d8 9c 42];
salt = [5e bf e8 7f 7d f3 23 5b 80 a1 17 eb c4 07 8e 44
f5 50 45 48 7a d4 a9 65 81 d1 ad b5 64 61 5b 51];
panic-on-corruption;
panic-on-error;
};
The bootloader constructs:
dm-mod.waitfor=/dev/fit0
dm-mod.create="rootfs-1,,, ro,
0 1638400 verity 1
/dev/fit0 /dev/fit0
4096 4096 204800 204800 sha256
ac87db56303c9c1da433d7209b5a6ef3e4779df141200cbd7c157dcb8dd89c42
5ebfe87f7df3235b80a117ebc4078e44f55045487ad4a96581d1adb564615b51
2 panic_on_corruption panic_on_error"
Note
The newlines inside the dm-mod.create value above are
for readability only. The actual kernel command-line
parameter must be a single line.
Here num_sectors = 204800 × (4096 / 512) = 1638400. <name> is the image
unit name rootfs-1; <uuid> and <minor> are left empty; ro
indicates a read-only target. <dev> and <hash_dev> are both
/dev/fit0 because the filesystem data and the Merkle tree reside in the same
sub-image. The two boolean properties panic-on-corruption and
panic-on-error become the optional-parameter suffix 2 panic_on_corruption
panic_on_error (count followed by underscore-separated names). The remaining
fields map directly from the dm-verity node properties.
Note
When preparing the sub-image with veritysetup format, pass
--no-superblock so that the hash tree starts directly after
the data blocks. By default veritysetup writes a one-block
on-disk superblock between data and hash tree, which would shift
hash-start-block to num-data-blocks + 1. The kernel
dm-verity target never reads this superblock — all parameters
are supplied via dm-mod.create — so it is unnecessary when
the metadata is already stored in the FIT dm-verity node.
7. Security¶
7.1. Introduction¶
FIT has robust security features. When enabled, each FIT configuration has one or more signatures. These protect the configuration and the images it refers to. The bootloader must check the signatures against a public key which it has stored elsewhere.
If any configuration fails its signature check, then it must be ignored. Images
must each include a suitable hash node so that they are protected against
modification. Once each image is loaded, its hash must be computed and checked
against the hash in the FIT. The exception is filesystem-type images that
carry a dm-verity node: their integrity is delegated to the kernel’s
dm-verity target rather than verified by the bootloader.
For more information on FIT security, see U-Boot’s documentation. The mechanism is also widely covered in conference talks, some of which are listed at elinux.org.
7.2. Architecture¶
FIT security uses a two-level scheme: image hashing and configuration signing.
7.2.1. Image hashing¶
Each image node contains one or more hash sub-nodes. Each hash sub-node holds
the algorithm name (e.g. sha256) and the resulting digest of the image
data. The hash covers the image content only, so the loader can verify that the
image data has not been modified after the hash was computed.
Hashing alone does not provide authentication, since an attacker who can modify the image data can also replace the hash. Authentication comes from the configuration signature, described next.
7.2.2. Configuration signing¶
Each configuration node may contain one or more signature sub-nodes. A configuration signature covers:
the configuration node itself (including its references to images),
each image node referenced by the configuration,
the hash sub-nodes of those images,
the
dm-veritysub-nodes of those images (where present), andthe root (
/) node of the FIT.
Because the signature covers the hash sub-nodes, the image data is transitively protected: any change to the image data invalidates the hash, and any change to the hash invalidates the configuration signature.
This design means that image data is protected without being directly included
in the configuration signature. The data property (and related properties
data-size, data-position and data-offset) of image nodes are
explicitly excluded from the signed region, since image-data integrity is already
guaranteed by the image hash.
This two-level design has an important consequence: the same image can appear in multiple configurations, each with its own signature, without duplicating the image data or requiring it to be signed multiple times.
7.2.3. Configuration signing compared to image signing¶
Signing each image independently is vulnerable to a mix-and-match attack, where an attacker combines legitimately signed images into a configuration that was never intended. For example, an attacker could pair a signed kernel with a different signed devicetree to change the system’s behaviour, even though both images carry valid signatures.
Configuration signing prevents this, because the signature binds a specific set of images together. A loader that verifies the configuration signature knows that this exact combination of images was approved by the signer.
7.2.4. Verification procedure¶
The bootloader verifies a configuration as follows:
Locate the configuration’s signature node and verify the signature against a trusted public key.
The signature covers certain FDT nodes and a region of the string table (see Hash contents below). Rebuild the list of nodes that should have been signed (the root node, the configuration node, each referenced image node, its hash sub-nodes, and any
dm-veritysub-node) and verify that the hash of those nodes matches the signature.For each image referenced by the configuration, compute the hash of the image data and compare it against the
valuein the image’s hash sub-node. This step may be deferred until the image is actually loaded, which can be some time after the configuration is selected.For
filesystem-type images that carry adm-veritychild node and are being used to launch Linux, the bootloader may omit loading the image into RAM and skip this hash check entirely. Instead, the bootloader shall derive kernel command-line parameters from thedm-veritynode as described in dm-verity for filesystem images, delegating integrity verification to the kernel’s dm-verity target at mount time.
If any step fails, the configuration must be rejected.
7.3. Hash contents¶
This section defines exactly which bytes are included when computing the hash for a signature. A FIT is a flattened devicetree (FDT), so the hash operates on the FDT binary structure as defined in the Devicetree Specification [dtspec].
The input to the hash is the concatenation of two regions: a set of nodes from the FDT structure block, followed by a region of the FDT strings block.
7.3.1. Structure block¶
The signer and verifier each construct a node list: the set of FDT nodes whose content is included in the hash. For a configuration signature this list contains:
the root (
/) node,the configuration node (e.g.
/configurations/conf-1),each image node referenced by the configuration (e.g.
/images/kernel,/images/fdt-1),the hash sub-nodes of those image nodes (e.g.
/images/kernel/hash-1,/images/fdt-1/hash-1),any cipher sub-nodes of those image nodes (e.g.
/images/kernel/cipher-1), andthe
dm-veritysub-node of anyfilesystem-type image node that carries one (e.g./images/rootfs-1/dm-verity).
The signer walks the FDT structure block sequentially and includes or excludes each token according to the following rules:
FDT_BEGIN_NODEThe token and the node’s unit name are included if the node or its parent is in the node list.
FDT_END_NODEIncluded under the same condition as
FDT_BEGIN_NODE.FDT_PROPThe token, the property length word, the string-table offset word, and the property data are all included if the containing node is in the node list and the property name is not one of the excluded properties:
data,data-size,data-positionanddata-offset. These are excluded because image-data integrity is covered by image hashes instead.FDT_NOPIncluded if the containing node is in the node list.
FDT_ENDAlways included.
Note that the “or its parent” condition in the FDT_BEGIN_NODE and
FDT_END_NODE rules means that sub-nodes of listed nodes contribute their
structural tokens to the hash, even though they are not themselves in the node
list. For example, the signature sub-nodes of the configuration node have their
FDT_BEGIN_NODE and FDT_END_NODE tokens included, but their properties
are excluded (since FDT_PROP requires the node itself to be in the list).
All included bytes are fed into the hash in the order they appear in the structure block. Padding bytes that are part of the FDT token alignment are included as they appear.
7.3.2. Strings block¶
The hashed-strings property in the signature node records the start offset
and size of the region of the FDT strings block that is hashed. The start is
normally 0 (the beginning of the strings block). Only property names that
are referenced by the signed nodes need to appear in this region; the signer
must ensure that the region is large enough to cover them.
After hashing the structure-block regions, the hash algorithm continues with the strings-block region to produce the final digest.
7.3.3. Image hashing¶
For image hash nodes (/images/image-name/hash-1), the hash is computed
over the image’s data property value only (i.e. the raw image content,
not any FDT metadata). The algorithm is given by the hash node’s algo
property and the resulting digest is stored in its value property.
7.3.4. Worked example¶
This section walks through a concrete FIT to show exactly which bytes are included in a configuration signature hash.
7.3.4.1. Source¶
Consider the following minimal FIT source:
/ {
description = "Example FIT";
#address-cells = <1>;
images {
kernel {
data = /incbin/("vmlinuz");
type = "kernel";
arch = "arm64";
os = "linux";
compression = "none";
load = <0x40000000>;
entry = <0x40000000>;
hash-1 {
algo = "sha256";
};
};
fdt-1 {
data = /incbin/("board.dtb");
type = "flat_dt";
arch = "arm64";
compression = "none";
hash-1 {
algo = "sha256";
};
};
};
configurations {
default = "conf-1";
conf-1 {
description = "Boot Linux";
compatible = "vendor,board";
kernel = "kernel";
fdt = "fdt-1";
signature-1 {
algo = "sha256,rsa2048";
key-name-hint = "dev";
sign-images = "kernel", "fdt";
};
};
};
};
7.3.4.2. After signing¶
During signing, the signer adds a value property to each hash node
containing the image digest, and adds value, hashed-nodes,
hashed-strings and other properties to the signature node. The
resulting FIT looks like this:
/ {
description = "Example FIT";
timestamp = <0x67d96bac>;
#address-cells = <1>;
images {
kernel {
data = <...kernel data...>;
type = "kernel";
arch = "arm64";
os = "linux";
compression = "none";
load = <0x40000000>;
entry = <0x40000000>;
hash-1 {
algo = "sha256";
value = <...32-byte SHA-256 digest of kernel data...>;
};
};
fdt-1 {
data = <...devicetree data...>;
type = "flat_dt";
arch = "arm64";
compression = "none";
hash-1 {
algo = "sha256";
value = <...32-byte SHA-256 digest of devicetree data...>;
};
};
};
configurations {
default = "conf-1";
conf-1 {
description = "Boot Linux";
compatible = "vendor,board";
kernel = "kernel";
fdt = "fdt-1";
signature-1 {
algo = "sha256,rsa2048";
key-name-hint = "dev";
sign-images = "kernel", "fdt";
value = <...256-byte RSA-2048 signature...>;
hashed-nodes = "/", "/configurations/conf-1",
"/images/kernel", "/images/kernel/hash-1",
"/images/fdt-1", "/images/fdt-1/hash-1";
hashed-strings = <0x00000000 0x000000d4>;
timestamp = <0x67d96bac>;
signer-name = "mkimage";
signer-version = "2025.04-rc3";
};
};
};
};
7.3.4.3. Node list¶
For the configuration signature /configurations/conf-1/signature-1, the
node list is:
//configurations/conf-1/images/kernel/images/kernel/hash-1/images/fdt-1/images/fdt-1/hash-1
7.3.4.4. What is hashed¶
The following shows the signed FIT with bold indicating the parts that are
included in the configuration signature hash. Lines in normal weight are not
hashed. Note that node braces ({ and }) represent FDT_BEGIN_NODE
and FDT_END_NODE tokens respectively; these are included whenever the node
or its parent is in the node list.
/ {
description = "Example FIT";
timestamp = <0x67d96bac>;
#address-cells = <1>;
images {
kernel {
data = <...kernel data...>;
type = "kernel";
arch = "arm64";
os = "linux";
compression = "none";
load = <0x40000000>;
entry = <0x40000000>;
hash-1 {
algo = "sha256";
value = <...32-byte SHA-256 digest...>;
};
};
fdt-1 {
data = <...devicetree data...>;
type = "flat_dt";
arch = "arm64";
compression = "none";
hash-1 {
algo = "sha256";
value = <...32-byte SHA-256 digest...>;
};
};
};
configurations {
default = "conf-1";
conf-1 {
description = "Boot Linux";
compatible = "vendor,board";
kernel = "kernel";
fdt = "fdt-1";
signature-1 {
algo = "sha256,rsa2048";
key-name-hint = "dev";
sign-images = "kernel", "fdt";
value = <...256-byte RSA-2048 signature...>;
hashed-nodes = "/", "/configurations/conf-1", ...;
hashed-strings = <0x00000000 0x000000d4>;
timestamp = <0x67d96bac>;
signer-name = "mkimage";
signer-version = "2025.04-rc3";
};
};
};
};
Strings block:
description\0
timestamp\0
#address-cells\0
type\0
arch\0
os\0
compression\0
load\0
entry\0
algo\0
value\0
compatible\0
kernel\0
fdt\0
default\0
padding\0
Key points to note:
The
dataproperties of both image nodes are excluded since image-data integrity is verified separately through the hash nodes.The
defaultproperty of theconfigurationsnode is not hashed because that node is not in the node list (only its parent/is). This is safe because the bootloader selects a configuration by its own logic, not by trusting the default.All properties of
signature-1are excluded because that node is not in the node list. Its braces are included because its parentconf-1is. This is safe because the signature itself is verified against a trusted public key, not by hashing.The
imagesandconfigurationsnodes have no properties of their own, but their braces are included because their parent/is in the node list. This serves as a structural sanity check, ensuring that an attacker cannot inject unexpected nodes into the tree without detection.The strings-block region contains the property name strings referenced by the hashed nodes. Although the string-table offset in each
FDT_PROPtoken is hashed, the string at that offset must also be protected; otherwise an attacker could rename a property (e.g. changingalgoto something unrecognised) to trick the bootloader into skipping verification. The hashed region should therefore always start at offset 0.
The complete byte sequence (structure-block regions plus strings-block region)
is hashed with SHA-256. The resulting digest is then signed with the RSA-2048
private key to produce the signature value.
8. References¶
Devicetree Specification https://www.devicetree.org/specifications
Verified Boot for Embedded (VBE) https://docs.u-boot.org/en/latest/develop/vbe.html