FIT dm-verity Boot Parameters
Introduction
Linux’s dm-verity device-mapper target provides transparent integrity checking of block devices using a Merkle tree. It is commonly used to protect read-only root filesystems such as SquashFS images.
When a FIT image packages the root filesystem as a loadable sub-image of
type filesystem (IH_TYPE_FILESYSTEM), the verity metadata can be
stored alongside the image data in a dm-verity subnode. U-Boot reads
this metadata at boot time and generates the kernel command-line parameters
that Linux needs to activate the verity target, eliminating the need for
an initramfs or userspace helper to set up dm-verity.
This feature is enabled by CONFIG_FIT_VERITY (see boot/Kconfig).
Prerequisites
Linux uImage.FIT block driver – the kernel must include the FIT block driver that exposes loadable sub-images as
/dev/fit0,/dev/fit1, etc. The driver assigns device numbers in the order loadables appear in the FIT configuration.dm-verity support in the kernel –
CONFIG_DM_VERITYmust be enabled so the kernel can process thedm-mod.create=parameter.CONFIG_FIT_VERITY enabled in U-Boot.
How it works
The implementation is split into a build phase and an apply phase,
both of which run automatically within the bootm state machine. No boot
method needs to call verity functions explicitly.
Build phase (BOOTM_STATE_FINDOTHER → boot_get_loadable())
After all loadable sub-images have been loaded,
fit_verity_build_cmdline()iterates the configuration’sloadableslist.For each loadable that is an
IH_TYPE_FILESYSTEMimage and contains adm-veritychild node, a dm-verity target specification is built by the helperfit_verity_build_target().The dm-verity target references
/dev/fitN, where N is the zero-based index of the loadable in the configuration. This matches the numbering used by the Linux FIT block driver.The resulting fragments are stored in
struct bootm_headers:images->dm_mod_createThe full dm-verity target table. Multiple targets are separated by
;.images->dm_mod_waitforComma-separated list of
/dev/fitNdevices so the kernel waits for the underlying FIT block devices to appear before activating device-mapper.
Apply phase (BOOTM_STATE_OS_PREP)
Just before
bootm_process_cmdline_env()processes thebootargsenvironment variable,fit_verity_apply_bootargs()appends thedm-mod.create=anddm-mod.waitfor=parameters.
Bootmeth integration
Because the fragments are stored in
struct bootm_headers, a boot method can checkfit_verity_active(images)between bootm state invocations. A typical pattern splitsbootm_run_states()into two calls – one forSTART|FINDOS|FINDOTHER|LOADOSand one forOS_PREP|OS_GO– and inspectsfit_verity_active()in between to decide whether to add aroot=parameter pointing at the dm-verity device.
FIT image source (.its) example
Below is a minimal .its file showing a kernel and a dm-verity-protected
root filesystem packaged as a FIT. Only the three user-provided properties
(algo, data-block-size, hash-block-size) are included; mkimage
computes and fills in digest, salt, num-data-blocks, and
hash-start-block automatically (see Generating verity metadata below):
/dts-v1/;
/ {
description = "Kernel + dm-verity rootfs";
#address-cells = <1>;
images {
kernel {
description = "Linux kernel";
data = /incbin/("./Image.gz");
type = "kernel";
arch = "arm64";
os = "linux";
compression = "gzip";
load = <0x44000000>;
entry = <0x44000000>;
hash-1 {
algo = "sha256";
};
};
fdt {
description = "Device tree blob";
data = /incbin/("./board.dtb");
type = "flat_dt";
arch = "arm64";
compression = "none";
hash-1 {
algo = "sha256";
};
};
rootfs {
description = "SquashFS root filesystem";
data = /incbin/("./rootfs.squashfs");
type = "filesystem";
arch = "arm64";
compression = "none";
hash-1 {
algo = "sha256";
};
dm-verity {
algo = "sha256";
data-block-size = <4096>;
hash-block-size = <4096>;
};
};
};
configurations {
default = "config-1";
config-1 {
description = "Boot with dm-verity rootfs";
kernel = "kernel";
fdt = "fdt";
loadables = "rootfs";
};
};
};
With this configuration U-Boot produces a kernel command line similar to:
dm-mod.create="rootfs,,, ro,0 <data_sectors> verity 1 \
/dev/fit0 /dev/fit0 4096 4096 3762 3762 sha256 \
8e6791637f93cbb81fc45299e203cbe85ca2e47a38f5051bddeece92d7b1c9f9 \
aa7b11f8db8fe2e5bfd4eca1d18a22b5de7ea39d2e1b93bb7272ce0c6ca3cc8e" \
dm-mod.waitfor=/dev/fit0
dm-verity subnode properties
User-provided properties (required in the .its):
Property |
Type |
Description |
|---|---|---|
|
string |
Hash algorithm name, e.g. |
|
u32 |
Data block size in bytes (>= 512, typically 4096). |
|
u32 |
Hash block size in bytes (>= 512, typically 4096). |
Computed properties (filled in by mkimage):
Property |
Type |
Description |
|---|---|---|
|
u32 |
Number of data blocks in the filesystem image (computed from the
image size and |
|
u32 |
Offset in |
|
byte array |
Root hash of the Merkle tree, stored as raw bytes. Length must match
the output size of |
|
byte array |
Salt used when computing the Merkle tree, stored as raw bytes. |
These values are the same ones produced by veritysetup format and can
typically be obtained from its output.
The digest and salt byte arrays correspond to the hex-encoded
Root hash and Salt printed by veritysetup format.
Optional boolean properties (when present, they are collected and appended as dm-verity optional parameters with hyphens converted to underscores):
Property |
Description |
|---|---|
|
Restart the system on data corruption. |
|
Panic the system on data corruption. |
|
Restart the system on I/O error. |
|
Panic the system on I/O error. |
|
Verify data blocks only on first read. |
Generating verity metadata
mkimage automates the entire process. When it encounters a
dm-verity subnode, it:
Writes the embedded image data to a temporary file.
Runs
veritysetup formatwith the user-supplied algorithm and block sizes.Parses
Root hashandSaltfromveritysetupstdout.Reads the expanded content (original data + Merkle hash tree) back into an in-memory buffer and removes the temporary file. The external-data section written to the .itb file uses this buffer in place of the original
dataproperty.Writes the computed
digest,salt,num-data-blocks, andhash-start-blockproperties into thedm-veritysubnode.
Images with dm-verity subnodes must use external data layout
(mkimage -E). mkimage will abort with an error if -E is
not specified.
Usage:
# Create the filesystem image
mksquashfs rootfs/ rootfs.squashfs -comp xz
# Build the FIT (dm-verity is computed automatically); align each
# external-data section to the block size of the underlying storage
# (see the alignment note below).
mkimage -E -B 0x1000 -f image.its image.itb
veritysetup (from the cryptsetup package) must be installed on
the build host.
Note
veritysetup format is invoked with --no-superblock, so no
on-disk superblock is written between the data and hash regions.
The Merkle hash tree is appended directly to the image data within
the FIT external data section. hash-start-block is therefore
computed as data_size / hash-block-size (the offset of the hash
region in units of hash-block-size). When data-block-size
equals hash-block-size this happens to equal num-data-blocks.
Note
The Linux fitblk driver currently requires each filesystem
sub-image to start and end on block boundaries of the underlying
block device (typically 512 bytes, sometimes 4 KiB for eMMC or NVMe
with 4 KiB native sectors). Use mkimage -B <align> to pad
external-data sections to that boundary; -B 0x1000 is a safe
default for the storage in common use.
This alignment requirement comes from the kernel-side fitblk
driver to avoid unaligned-access fix-up overhead in block I/O, and
is independent of the dm-verity data-block-size and
hash-block-size properties – those describe the block sizes
used by the device-mapper verity target itself, not storage
alignment.
Kconfig
CONFIG_FIT_VERITYDepends on
CONFIG_FITandCONFIG_OF_LIBFDT. When enabled,fit_verity_build_cmdline()andfit_verity_apply_bootargs()are compiled into the boot path. When disabled, the functions are static inlines returning 0, so there is no code-size impact. Works with both thebootmcommand and BOOTSTD boot methods.