public inbox for u-boot@lists.denx.de
 help / color / mirror / Atom feed
* [PATCH v2 0/7] fit: dm-verity support
@ 2026-04-16  1:46 Daniel Golle
  2026-04-16  1:46 ` [PATCH v2 1/7] image: fit: add dm-verity property name constants Daniel Golle
                   ` (6 more replies)
  0 siblings, 7 replies; 19+ messages in thread
From: Daniel Golle @ 2026-04-16  1:46 UTC (permalink / raw)
  To: Tom Rini, Simon Glass, Quentin Schulz, Kory Maincent,
	Mattijs Korpershoek, Peng Fan, Heinrich Schuchardt, Martin Schwan,
	Daniel Golle, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
	Aristo Chen, Ludwig Nussel, Benjamin ROBIN, Marek Vasut,
	James Hilliard, Wolfgang Wallner, Kunihiko Hayashi, David Lechner,
	Neil Armstrong, Mayuresh Chitale, Jonas Karlman, Shiji Yang,
	Rasmus Villemoes, Francois Berder, u-boot

This series adds dm-verity support to U-Boot's FIT image infrastructure.
It is the first logical subset of the larger OpenWrt boot method series
posted as an RFC in February 2026 [1], extracted here for independent
review and merging.

OpenWrt's firmware model embeds a read-only squashfs or erofs root
filesystem directly inside a uImage.FIT container as a FILESYSTEM-type
loadable FIT image. At boot the kernel maps this sub-image directly from
the underlying block device via the fitblk driver (/dev/fit0, /dev/fit1,
...), the goal is that the bootloader never even copies it to RAM.

dm-verity enables the kernel to verify the integrity of those mapped
filesystems at read time, with a Merkle hash tree stored contiguously in
the same sub-image just after the data. Two kernel command-line
parameters are required:

  dm-mod.create=   -- the device-mapper target table for the verity device
  dm-mod.waitfor=  -- a comma-separated list of block devices to wait for
                      before dm-init sets up the targets (needed when fitblk
                      probes late, e.g. because it depends on NVMEM
                      calibration data)

The FIT dm-verity node schema was upstreamed into the flat-image-tree
specification [2], which this implementation tries to follow exactly.

The runtime feature is guarded behind CONFIG_FIT_VERITY. If not
enabled the resulting binary size remains unchanged. If enabled the
binary size increases by about 3kB.

[1] RFC/v2: https://www.mail-archive.com/u-boot@lists.denx.de/msg565945.html
[2] flat-image-tree dm-verity node spec:
    https://github.com/open-source-firmware/flat-image-tree/commit/795fd5fd7f0121d0cb03efb1900aafc61c704771

v2: address comments by Simon Glass
 * use is_power_of_2() for pre-boot sanity check
 * let fit_verity_build_cmdline() return 0 on success
 * add comment explaining why bootm_start() calls fit_verity_free()
 * use existing hex2bin() (and adapt it to be usable for host-tools)
 * fix stale comment still including superblock despite veritysetup
   being called with --no-superblock
 * add power-of-two check for data-block-size and hash-block-size to
   mkimage
 * don't ignore return value of fdt_delprop()
 * various documentation fixes, minimal example
 * add pytest for mkimage part
 * add run-time unit test for cmdline generation part

Daniel Golle (7):
  image: fit: add dm-verity property name constants
  boot: fit: support generating DM verity cmdline parameters
  include: hexdump: make hex2bin() usable from host tools
  tools: mkimage: add dm-verity Merkle-tree generation
  doc: fit: add dm-verity boot parameter documentation
  test: boot: add runtime unit test for fit_verity_build_cmdline()
  test: py: add mkimage dm-verity round-trip test

 boot/Kconfig                     |  20 ++
 boot/bootm.c                     |  13 ++
 boot/image-board.c               |   5 +
 boot/image-fit.c                 | 337 +++++++++++++++++++++++++++++
 doc/usage/fit/dm-verity.rst      | 282 +++++++++++++++++++++++++
 doc/usage/fit/index.rst          |   1 +
 include/hexdump.h                |   8 +-
 include/image.h                  |  97 ++++++++-
 test/boot/Makefile               |   1 +
 test/boot/fit_verity.c           | 298 ++++++++++++++++++++++++++
 test/cmd_ut.c                    |   2 +
 test/py/tests/test_fit_verity.py | 153 ++++++++++++++
 tools/fit_image.c                | 116 +++++++++-
 tools/image-host.c               | 349 ++++++++++++++++++++++++++++++-
 14 files changed, 1669 insertions(+), 13 deletions(-)
 create mode 100644 doc/usage/fit/dm-verity.rst
 create mode 100644 test/boot/fit_verity.c
 create mode 100644 test/py/tests/test_fit_verity.py

-- 
2.53.0

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v2 1/7] image: fit: add dm-verity property name constants
  2026-04-16  1:46 [PATCH v2 0/7] fit: dm-verity support Daniel Golle
@ 2026-04-16  1:46 ` Daniel Golle
  2026-04-16  1:46 ` [PATCH v2 2/7] boot: fit: support generating DM verity cmdline parameters Daniel Golle
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 19+ messages in thread
From: Daniel Golle @ 2026-04-16  1:46 UTC (permalink / raw)
  To: Tom Rini, Simon Glass, Quentin Schulz, Kory Maincent,
	Mattijs Korpershoek, Peng Fan, Heinrich Schuchardt, Martin Schwan,
	Daniel Golle, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
	Aristo Chen, Ludwig Nussel, Benjamin ROBIN, Marek Vasut,
	James Hilliard, Wolfgang Wallner, Kunihiko Hayashi, David Lechner,
	Neil Armstrong, Mayuresh Chitale, Jonas Karlman, Shiji Yang,
	Rasmus Villemoes, Francois Berder, u-boot

Add FIT_VERITY_NODENAME and the complete set of FIT_VERITY_*_PROP
constants for the dm-verity child node of filesystem-type images, plus
the five optional boolean error-handling property names aligned with the
flat-image-tree specification.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Reviewed-by: Simon Glass <sjg@chromium.org>
---
v2: no changes

 include/image.h | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/include/image.h b/include/image.h
index 34efac6056d..482446a8115 100644
--- a/include/image.h
+++ b/include/image.h
@@ -1079,6 +1079,23 @@ int booti_setup(ulong image, ulong *relocated_addr, ulong *size,
 #define FIT_CIPHER_NODENAME	"cipher"
 #define FIT_ALGO_PROP		"algo"
 
+/* dm-verity node */
+#define FIT_VERITY_NODENAME	"dm-verity"
+#define FIT_VERITY_ALGO_PROP	"algo"
+#define FIT_VERITY_DBS_PROP	"data-block-size"
+#define FIT_VERITY_HBS_PROP	"hash-block-size"
+#define FIT_VERITY_NBLK_PROP	"num-data-blocks"
+#define FIT_VERITY_HBLK_PROP	"hash-start-block"
+#define FIT_VERITY_DIGEST_PROP	"digest"
+#define FIT_VERITY_SALT_PROP	"salt"
+
+/* dm-verity error-handling modes (optional boolean property names) */
+#define FIT_VERITY_OPT_RESTART	"restart-on-corruption"
+#define FIT_VERITY_OPT_PANIC	"panic-on-corruption"
+#define FIT_VERITY_OPT_RERR	"restart-on-error"
+#define FIT_VERITY_OPT_PERR	"panic-on-error"
+#define FIT_VERITY_OPT_ONCE	"check-at-most-once"
+
 /* image node */
 #define FIT_DATA_PROP		"data"
 #define FIT_DATA_POSITION_PROP	"data-position"
-- 
2.53.0

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v2 2/7] boot: fit: support generating DM verity cmdline parameters
  2026-04-16  1:46 [PATCH v2 0/7] fit: dm-verity support Daniel Golle
  2026-04-16  1:46 ` [PATCH v2 1/7] image: fit: add dm-verity property name constants Daniel Golle
@ 2026-04-16  1:46 ` Daniel Golle
  2026-04-16 19:37   ` Simon Glass
  2026-04-16  1:46 ` [PATCH v2 3/7] include: hexdump: make hex2bin() usable from host tools Daniel Golle
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 19+ messages in thread
From: Daniel Golle @ 2026-04-16  1:46 UTC (permalink / raw)
  To: Tom Rini, Simon Glass, Quentin Schulz, Kory Maincent,
	Mattijs Korpershoek, Peng Fan, Heinrich Schuchardt, Martin Schwan,
	Daniel Golle, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
	Aristo Chen, Ludwig Nussel, Benjamin ROBIN, Marek Vasut,
	James Hilliard, Wolfgang Wallner, Kunihiko Hayashi, David Lechner,
	Neil Armstrong, Mayuresh Chitale, Jonas Karlman, Shiji Yang,
	Rasmus Villemoes, Francois Berder, u-boot

Add fit_verity_build_cmdline(): when a FILESYSTEM loadable carries a
dm-verity subnode, construct the dm-mod.create= kernel cmdline parameter
from the verity metadata (block-size, data-blocks, algo, root-hash,
salt) and append it to bootargs.

Also add dm-mod.waitfor=/dev/fit0[,/dev/fitN] for each dm-verity device
so the kernel waits for the underlying FIT block device to appear before
setting up device-mapper targets. This is needed when the block driver
probes late, e.g. because it depends on NVMEM calibration data.

The dm-verity target references /dev/fitN where N is the loadable's
index in the configuration -- matching the order Linux's FIT block
driver assigns block devices.  hash-start-block is read directly from
the FIT dm-verity node; mkimage ensures its value equals num-data-blocks
by invoking veritysetup with --no-superblock.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
v2:
 * use is_power_of_2() for pre-boot sanity check
 * let fit_verity_build_cmdline() return 0 on success
 * add comment explaining why bootm_start() calls fit_verity_free()

 boot/Kconfig       |  20 +++
 boot/bootm.c       |  13 ++
 boot/image-board.c |   5 +
 boot/image-fit.c   | 337 +++++++++++++++++++++++++++++++++++++++++++++
 include/image.h    |  80 ++++++++++-
 5 files changed, 454 insertions(+), 1 deletion(-)

diff --git a/boot/Kconfig b/boot/Kconfig
index be6bb6d4535..7c6e5a9fcf8 100644
--- a/boot/Kconfig
+++ b/boot/Kconfig
@@ -142,6 +142,26 @@ config FIT_CIPHER
 	  Enable the feature of data ciphering/unciphering in the tool mkimage
 	  and in the u-boot support of the FIT image.
 
+config FIT_VERITY
+	bool "dm-verity boot parameter generation from FIT metadata"
+	depends on FIT && OF_LIBFDT
+	help
+	  When a FIT configuration contains loadable sub-images of type
+	  IH_TYPE_FILESYSTEM with a dm-verity subnode, this option enables
+	  building the dm-mod.create= and dm-mod.waitfor= kernel
+	  command-line parameters from the verity metadata
+	  (data-block-size, hash-block-size, num-data-blocks,
+	  hash-start-block, algorithm, digest, salt) stored in the FIT.
+
+	  The generated parameters reference /dev/fitN block devices that
+	  Linux's uImage.FIT block driver assigns to loadable sub-images.
+
+	  During FIT parsing (BOOTM_STATE_FINDOTHER), verity cmdline
+	  fragments are stored in struct bootm_headers and automatically
+	  appended to the bootargs environment variable during
+	  BOOTM_STATE_OS_PREP. This works from both the bootm command
+	  and BOOTSTD bootmeths.
+
 config FIT_VERBOSE
 	bool "Show verbose messages when FIT images fail"
 	help
diff --git a/boot/bootm.c b/boot/bootm.c
index 4836d6b2d41..d6a54a49fce 100644
--- a/boot/bootm.c
+++ b/boot/bootm.c
@@ -243,6 +243,13 @@ static int boot_get_kernel(const char *addr_fit, struct bootm_headers *images,
 
 static int bootm_start(void)
 {
+	/*
+	 * Free dm-verity allocations from a prior boot attempt before
+	 * zeroing the structure. The pointers are guaranteed to be valid
+	 * or NULL: .bss is zero-initialised, and memset() below zeroes
+	 * them again after every boot.
+	 */
+	fit_verity_free(&images);
 	memset((void *)&images, 0, sizeof(images));
 	images.verify = env_get_yesno("verify");
 
@@ -1071,6 +1078,12 @@ int bootm_run_states(struct bootm_info *bmi, int states)
 		/* For Linux OS do all substitutions at console processing */
 		if (images->os.os == IH_OS_LINUX)
 			flags = BOOTM_CL_ALL;
+		ret = fit_verity_apply_bootargs(images);
+		if (ret) {
+			printf("dm-verity bootargs failed (err=%d)\n", ret);
+			ret = CMD_RET_FAILURE;
+			goto err;
+		}
 		ret = bootm_process_cmdline_env(flags);
 		if (ret) {
 			printf("Cmdline setup failed (err=%d)\n", ret);
diff --git a/boot/image-board.c b/boot/image-board.c
index 005d60caf5c..265f29d44ff 100644
--- a/boot/image-board.c
+++ b/boot/image-board.c
@@ -810,6 +810,11 @@ int boot_get_loadable(struct bootm_headers *images)
 
 			fit_loadable_process(img_type, img_data, img_len);
 		}
+
+		fit_img_result = fit_verity_build_cmdline(buf, conf_noffset,
+							  images);
+		if (fit_img_result < 0)
+			return fit_img_result;
 		break;
 	default:
 		printf("The given image format is not supported (corrupt?)\n");
diff --git a/boot/image-fit.c b/boot/image-fit.c
index 067ad236081..0c28c220ba1 100644
--- a/boot/image-fit.c
+++ b/boot/image-fit.c
@@ -21,8 +21,11 @@
 extern void *aligned_alloc(size_t alignment, size_t size);
 #else
 #include <linux/compiler.h>
+#include <linux/log2.h>
 #include <linux/sizes.h>
+#include <env.h>
 #include <errno.h>
+#include <hexdump.h>
 #include <log.h>
 #include <mapmem.h>
 #include <asm/io.h>
@@ -243,6 +246,39 @@ static void fit_image_print_data(const void *fit, int noffset, const char *p,
 	}
 }
 
+static __maybe_unused void fit_image_print_dm_verity(const void *fit,
+						     int noffset,
+						     const char *p)
+{
+#if defined(USE_HOSTCC) || CONFIG_IS_ENABLED(FIT_VERITY)
+	const char *algo;
+	const u8 *bin;
+	int len, i;
+
+	algo = fdt_getprop(fit, noffset, FIT_VERITY_ALGO_PROP, NULL);
+	if (algo)
+		printf("%s  Verity algo:  %s\n", p, algo);
+
+	bin = fdt_getprop(fit, noffset, FIT_VERITY_DIGEST_PROP,
+			  &len);
+	if (bin && len > 0) {
+		printf("%s  Verity hash:  ", p);
+		for (i = 0; i < len; i++)
+			printf("%02x", bin[i]);
+		printf("\n");
+	}
+
+	bin = fdt_getprop(fit, noffset, FIT_VERITY_SALT_PROP,
+			  &len);
+	if (bin && len > 0) {
+		printf("%s  Verity salt:  ", p);
+		for (i = 0; i < len; i++)
+			printf("%02x", bin[i]);
+		printf("\n");
+	}
+#endif
+}
+
 /**
  * fit_image_print_verification_data() - prints out the hash/signature details
  * @fit: pointer to the FIT format image header
@@ -271,6 +307,11 @@ static void fit_image_print_verification_data(const void *fit, int noffset,
 				strlen(FIT_SIG_NODENAME))) {
 		fit_image_print_data(fit, noffset, p, "Sign");
 	}
+#if defined(USE_HOSTCC) || CONFIG_IS_ENABLED(FIT_VERITY)
+	else if (!strcmp(name, FIT_VERITY_NODENAME)) {
+		fit_image_print_dm_verity(fit, noffset, p);
+	}
+#endif
 }
 
 /**
@@ -2642,3 +2683,299 @@ out:
 	return fdt_noffset;
 }
 #endif
+
+#if !defined(USE_HOSTCC) && CONFIG_IS_ENABLED(FIT_VERITY)
+
+static const char *const verity_opt_props[] = {
+	FIT_VERITY_OPT_RESTART,
+	FIT_VERITY_OPT_PANIC,
+	FIT_VERITY_OPT_RERR,
+	FIT_VERITY_OPT_PERR,
+	FIT_VERITY_OPT_ONCE,
+};
+
+/**
+ * fit_verity_build_target() - build one dm-verity target specification
+ * @fit:	pointer to the FIT blob
+ * @img_noffset:	image node offset containing the dm-verity subnode
+ * @loadable_idx:	index of this loadable (for /dev/fitN)
+ * @uname:	unit name of the image
+ * @separator:	true if a ";" prefix is needed (not the first target)
+ * @buf:	output buffer, or NULL to measure only
+ * @bufsize:	size of @buf (ignored when @buf is NULL)
+ *
+ * Parses all dm-verity properties from the image's ``dm-verity`` child
+ * node and writes (or measures) a dm target specification string of the
+ * form used by the ``dm-mod.create`` kernel parameter.
+ *
+ * Return: number of characters that would be written (excluding '\0'),
+ *	   or -ve errno on error (e.g. missing mandatory property)
+ */
+static int fit_verity_build_target(const void *fit, int img_noffset,
+				   int loadable_idx, const char *uname,
+				   bool separator, char *buf, int bufsize)
+{
+	const char *algorithm;
+	const u8 *digest_raw, *salt_raw;
+	const fdt32_t *val;
+	char *digest_hex = NULL, *salt_hex = NULL, *opt_buf = NULL;
+	int verity_node;
+	int data_block_size, hash_block_size;
+	int num_data_blocks, hash_start_block;
+	u64 data_sectors;
+	int digest_len, salt_len;
+	int opt_count, opt_off, opt_buf_size;
+	int len;
+	int i;
+
+	verity_node = fdt_subnode_offset(fit, img_noffset, FIT_VERITY_NODENAME);
+	if (verity_node < 0)
+		return -ENOENT;
+
+	/* Mandatory u32 properties */
+	val = fdt_getprop(fit, verity_node, FIT_VERITY_DBS_PROP, NULL);
+	if (!val)
+		return -EINVAL;
+	data_block_size = fdt32_to_cpu(*val);
+
+	val = fdt_getprop(fit, verity_node, FIT_VERITY_HBS_PROP, NULL);
+	if (!val)
+		return -EINVAL;
+	hash_block_size = fdt32_to_cpu(*val);
+
+	val = fdt_getprop(fit, verity_node, FIT_VERITY_NBLK_PROP, NULL);
+	if (!val)
+		return -EINVAL;
+	num_data_blocks = fdt32_to_cpu(*val);
+
+	val = fdt_getprop(fit, verity_node, FIT_VERITY_HBLK_PROP, NULL);
+	if (!val)
+		return -EINVAL;
+	hash_start_block = fdt32_to_cpu(*val);
+
+	if (data_block_size < 512 || !is_power_of_2(data_block_size) ||
+	    hash_block_size < 512 || !is_power_of_2(hash_block_size) ||
+	    !num_data_blocks)
+		return -EINVAL;
+
+	/* Mandatory string */
+	algorithm = fdt_getprop(fit, verity_node, FIT_VERITY_ALGO_PROP, NULL);
+	if (!algorithm)
+		return -EINVAL;
+
+	/* Mandatory byte arrays */
+	digest_raw = fdt_getprop(fit, verity_node, FIT_VERITY_DIGEST_PROP,
+				 &digest_len);
+	if (!digest_raw || digest_len <= 0)
+		return -EINVAL;
+
+	salt_raw = fdt_getprop(fit, verity_node, FIT_VERITY_SALT_PROP,
+			       &salt_len);
+	if (!salt_raw || salt_len <= 0)
+		return -EINVAL;
+
+	/* Hex-encode digest and salt into dynamically sized buffers */
+	digest_hex = malloc(digest_len * 2 + 1);
+	salt_hex = malloc(salt_len * 2 + 1);
+	if (!digest_hex || !salt_hex) {
+		len = -ENOMEM;
+		goto out;
+	}
+	*bin2hex(digest_hex, digest_raw, digest_len) = '\0';
+	*bin2hex(salt_hex, salt_raw, salt_len) = '\0';
+
+	data_sectors = (u64)num_data_blocks * ((u64)data_block_size / 512);
+
+	/* Compute space needed for optional boolean properties */
+	opt_buf_size = 1; /* NUL terminator */
+	for (i = 0; i < ARRAY_SIZE(verity_opt_props); i++)
+		opt_buf_size += strlen(verity_opt_props[i]) + 1;
+	opt_buf = malloc(opt_buf_size);
+	if (!opt_buf) {
+		len = -ENOMEM;
+		goto out;
+	}
+
+	/* Collect optional boolean properties */
+	opt_count = 0;
+	opt_off = 0;
+	opt_buf[0] = '\0';
+	for (i = 0; i < ARRAY_SIZE(verity_opt_props); i++) {
+		if (fdt_getprop(fit, verity_node,
+				verity_opt_props[i], NULL)) {
+			const char *s = verity_opt_props[i];
+			int slen = strlen(s);
+
+			if (opt_off)
+				opt_buf[opt_off++] = ' ';
+			/* Copy with hyphen-to-underscore conversion */
+			while (slen-- > 0) {
+				opt_buf[opt_off++] =
+					(*s == '-') ? '_' : *s;
+				s++;
+			}
+			opt_buf[opt_off] = '\0';
+			opt_count++;
+		}
+	}
+
+	/* Emit (or measure) the target spec */
+	len = snprintf(buf, buf ? bufsize : 0,
+		       "%s%s,,, ro,0 %llu verity 1 /dev/fit%d /dev/fit%d %d %d %d %d %s %s %s",
+		       separator ? ";" : "", uname,
+		       (unsigned long long)data_sectors, loadable_idx, loadable_idx,
+		       data_block_size, hash_block_size,
+		       num_data_blocks, hash_start_block,
+		       algorithm, digest_hex, salt_hex);
+	if (opt_count) {
+		int extra = snprintf(buf ? buf + len : NULL,
+				     buf ? bufsize - len : 0,
+				     " %d %s", opt_count, opt_buf);
+		len += extra;
+	}
+
+out:
+	free(digest_hex);
+	free(salt_hex);
+	free(opt_buf);
+	return len;
+}
+
+int fit_verity_build_cmdline(const void *fit, int conf_noffset,
+			     struct bootm_headers *images)
+{
+	int images_noffset;
+	int dm_create_len = 0, dm_waitfor_len = 0;
+	char *dm_create = NULL, *dm_waitfor = NULL;
+	const char *uname;
+	int loadable_idx;
+	int found = 0;
+	int ret = 0;
+
+	images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH);
+	if (images_noffset < 0)
+		return 0;
+
+	for (loadable_idx = 0;
+	     (uname = fdt_stringlist_get(fit, conf_noffset,
+					 FIT_LOADABLE_PROP,
+					 loadable_idx, NULL));
+	     loadable_idx++) {
+		int img_noffset, need;
+		u8 img_type;
+		char *tmp;
+
+		img_noffset = fdt_subnode_offset(fit, images_noffset, uname);
+		if (img_noffset < 0)
+			continue;
+
+		if (fit_image_get_type(fit, img_noffset, &img_type) ||
+		    img_type != IH_TYPE_FILESYSTEM)
+			continue;
+
+		/* Measure first, then allocate and write */
+		need = fit_verity_build_target(fit, img_noffset,
+					       loadable_idx, uname,
+					       found > 0, NULL, 0);
+		if (need == -ENOENT)
+			continue;	/* no dm-verity subnode -- fine */
+		if (need < 0) {
+			printf("FIT: broken dm-verity metadata in '%s'\n",
+			       uname);
+			ret = need;
+			goto err;
+		}
+
+		tmp = realloc(dm_create, dm_create_len + need + 1);
+		if (!tmp) {
+			ret = -ENOMEM;
+			goto err;
+		}
+		dm_create = tmp;
+		fit_verity_build_target(fit, img_noffset, loadable_idx,
+					uname, found > 0,
+					dm_create + dm_create_len,
+					need + 1);
+		dm_create_len += need;
+
+		/* Grow dm_waitfor buffer */
+		need = snprintf(NULL, 0, "%s/dev/fit%d",
+				dm_waitfor_len ? "," : "",
+				loadable_idx);
+		tmp = realloc(dm_waitfor, dm_waitfor_len + need + 1);
+		if (!tmp) {
+			ret = -ENOMEM;
+			goto err;
+		}
+		dm_waitfor = tmp;
+		sprintf(dm_waitfor + dm_waitfor_len, "%s/dev/fit%d",
+			dm_waitfor_len ? "," : "",
+			loadable_idx);
+		dm_waitfor_len += need;
+
+		found++;
+	}
+
+	if (found) {
+		/* Transfer ownership to the bootm_headers */
+		images->dm_mod_create = dm_create;
+		images->dm_mod_waitfor = dm_waitfor;
+	} else {
+		free(dm_create);
+		free(dm_waitfor);
+	}
+
+	return 0;
+
+err:
+	free(dm_create);
+	free(dm_waitfor);
+	return ret;
+}
+
+/**
+ * fmt used by both the measurement and the actual write of bootargs.
+ * Shared to guarantee they stay in sync.
+ */
+#define VERITY_BOOTARGS_FMT	"%s%sdm-mod.create=\"%s\" dm-mod.waitfor=\"%s\""
+
+int fit_verity_apply_bootargs(const struct bootm_headers *images)
+{
+	const char *existing;
+	char *newargs;
+	int len;
+
+	if (!images->dm_mod_create)
+		return 0;
+
+	existing = env_get("bootargs");
+	if (!existing)
+		existing = "";
+
+	/* Measure */
+	len = snprintf(NULL, 0, VERITY_BOOTARGS_FMT,
+		       existing, existing[0] ? " " : "",
+		       images->dm_mod_create, images->dm_mod_waitfor);
+
+	newargs = malloc(len + 1);
+	if (!newargs)
+		return -ENOMEM;
+
+	snprintf(newargs, len + 1, VERITY_BOOTARGS_FMT,
+		 existing, existing[0] ? " " : "",
+		 images->dm_mod_create, images->dm_mod_waitfor);
+
+	env_set("bootargs", newargs);
+	free(newargs);
+
+	return 0;
+}
+
+void fit_verity_free(struct bootm_headers *images)
+{
+	free(images->dm_mod_create);
+	free(images->dm_mod_waitfor);
+	images->dm_mod_create = NULL;
+	images->dm_mod_waitfor = NULL;
+}
+#endif /* FIT_VERITY */
diff --git a/include/image.h b/include/image.h
index 482446a8115..fe2361a667e 100644
--- a/include/image.h
+++ b/include/image.h
@@ -396,7 +396,19 @@ struct bootm_headers {
 	ulong		cmdline_start;
 	ulong		cmdline_end;
 	struct bd_info		*kbd;
-#endif
+
+#if CONFIG_IS_ENABLED(FIT_VERITY)
+	/*
+	 * dm-verity kernel command-line fragments, populated during FIT
+	 * parsing by fit_verity_build_cmdline().  Bootmeths can check
+	 * fit_verity_active() between bootm states, and
+	 * fit_verity_apply_bootargs() appends these to the "bootargs"
+	 * env var during BOOTM_STATE_OS_PREP.
+	 */
+	char *dm_mod_create;
+	char *dm_mod_waitfor;
+#endif /* FIT_VERITY */
+#endif /* !USE_HOSTCC */
 
 	int		verify;		/* env_get("verify")[0] != 'n' */
 
@@ -756,6 +768,72 @@ int fit_image_load(struct bootm_headers *images, ulong addr,
 		   int arch, int image_ph_type, int bootstage_id,
 		   enum fit_load_op load_op, ulong *datap, ulong *lenp);
 
+#if !defined(USE_HOSTCC) && CONFIG_IS_ENABLED(FIT_VERITY)
+/**
+ * fit_verity_build_cmdline() - build dm-verity cmdline from FIT metadata
+ * @fit:		pointer to the FIT blob
+ * @conf_noffset:	configuration node offset in @fit
+ * @images:		bootm headers; dm_mod_create / dm_mod_waitfor are
+ *			populated on success
+ *
+ * Called automatically from boot_get_loadable() during FIT parsing.
+ * For each IH_TYPE_FILESYSTEM loadable with a dm-verity subnode,
+ * builds the corresponding dm target specification.
+ *
+ * Return: 0 on success, -ve errno on error
+ */
+int fit_verity_build_cmdline(const void *fit, int conf_noffset,
+			     struct bootm_headers *images);
+
+/**
+ * fit_verity_apply_bootargs() - append dm-verity params to bootargs env
+ * @images:	bootm headers with dm-verity cmdline fragments
+ *
+ * Called from BOOTM_STATE_OS_PREP before bootm_process_cmdline_env().
+ *
+ * Return: 0 on success, -ve errno on error
+ */
+int fit_verity_apply_bootargs(const struct bootm_headers *images);
+
+/**
+ * fit_verity_active() - check whether dm-verity targets were found
+ * @images:	bootm headers
+ *
+ * Return: true if at least one dm-verity target was built
+ */
+static inline bool fit_verity_active(const struct bootm_headers *images)
+{
+	return !!images->dm_mod_create;
+}
+
+/**
+ * fit_verity_free() - free dm-verity cmdline allocations
+ * @images:	bootm headers
+ */
+void fit_verity_free(struct bootm_headers *images);
+
+#else /* !FIT_VERITY */
+
+static inline int fit_verity_build_cmdline(const void *fit, int conf_noffset,
+					   struct bootm_headers *images)
+{
+	return 0;
+}
+
+static inline int fit_verity_apply_bootargs(const struct bootm_headers *images)
+{
+	return 0;
+}
+
+static inline bool fit_verity_active(const struct bootm_headers *images)
+{
+	return false;
+}
+
+static inline void fit_verity_free(struct bootm_headers *images) {}
+
+#endif /* FIT_VERITY */
+
 /**
  * image_locate_script() - Locate the raw script in an image
  *
-- 
2.53.0

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v2 3/7] include: hexdump: make hex2bin() usable from host tools
  2026-04-16  1:46 [PATCH v2 0/7] fit: dm-verity support Daniel Golle
  2026-04-16  1:46 ` [PATCH v2 1/7] image: fit: add dm-verity property name constants Daniel Golle
  2026-04-16  1:46 ` [PATCH v2 2/7] boot: fit: support generating DM verity cmdline parameters Daniel Golle
@ 2026-04-16  1:46 ` Daniel Golle
  2026-04-16 11:00   ` Heinrich Schuchardt
  2026-04-16 19:37   ` Simon Glass
  2026-04-16  1:46 ` [PATCH v2 4/7] tools: mkimage: add dm-verity Merkle-tree generation Daniel Golle
                   ` (3 subsequent siblings)
  6 siblings, 2 replies; 19+ messages in thread
From: Daniel Golle @ 2026-04-16  1:46 UTC (permalink / raw)
  To: Tom Rini, Simon Glass, Quentin Schulz, Kory Maincent,
	Mattijs Korpershoek, Peng Fan, Heinrich Schuchardt, Martin Schwan,
	Daniel Golle, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
	Aristo Chen, Ludwig Nussel, Benjamin ROBIN, Marek Vasut,
	James Hilliard, Wolfgang Wallner, Kunihiko Hayashi, David Lechner,
	Neil Armstrong, Mayuresh Chitale, Jonas Karlman, Shiji Yang,
	Rasmus Villemoes, Francois Berder, u-boot

Make hexdump.h work in host-tool builds by using 'uint8_t' instead
of 'u8', and including either user-space libc <ctype.h> for host-tools
or <linux/ctype.h> when building U-Boot itself.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
v2: new patch

 include/hexdump.h | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/include/hexdump.h b/include/hexdump.h
index f2ca4793d69..5cb48d79efe 100644
--- a/include/hexdump.h
+++ b/include/hexdump.h
@@ -7,7 +7,11 @@
 #ifndef HEXDUMP_H
 #define HEXDUMP_H
 
+#ifdef USE_HOSTCC
+#include <ctype.h>
+#else
 #include <linux/ctype.h>
+#endif
 #include <linux/types.h>
 
 enum dump_prefix_t {
@@ -20,7 +24,7 @@ extern const char hex_asc[];
 #define hex_asc_lo(x)	hex_asc[((x) & 0x0f)]
 #define hex_asc_hi(x)	hex_asc[((x) & 0xf0) >> 4]
 
-static inline char *hex_byte_pack(char *buf, u8 byte)
+static inline char *hex_byte_pack(char *buf, uint8_t byte)
 {
 	*buf++ = hex_asc_hi(byte);
 	*buf++ = hex_asc_lo(byte);
@@ -52,7 +56,7 @@ static inline int hex_to_bin(char ch)
  *
  * Return 0 on success, -1 in case of bad input.
  */
-static inline int hex2bin(u8 *dst, const char *src, size_t count)
+static inline int hex2bin(uint8_t *dst, const char *src, size_t count)
 {
 	while (count--) {
 		int hi = hex_to_bin(*src++);
-- 
2.53.0

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v2 4/7] tools: mkimage: add dm-verity Merkle-tree generation
  2026-04-16  1:46 [PATCH v2 0/7] fit: dm-verity support Daniel Golle
                   ` (2 preceding siblings ...)
  2026-04-16  1:46 ` [PATCH v2 3/7] include: hexdump: make hex2bin() usable from host tools Daniel Golle
@ 2026-04-16  1:46 ` Daniel Golle
  2026-04-16 19:36   ` Simon Glass
  2026-04-16  1:46 ` [PATCH v2 5/7] doc: fit: add dm-verity boot parameter documentation Daniel Golle
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 19+ messages in thread
From: Daniel Golle @ 2026-04-16  1:46 UTC (permalink / raw)
  To: Tom Rini, Simon Glass, Quentin Schulz, Kory Maincent,
	Mattijs Korpershoek, Peng Fan, Heinrich Schuchardt, Martin Schwan,
	Daniel Golle, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
	Aristo Chen, Ludwig Nussel, Benjamin ROBIN, Marek Vasut,
	James Hilliard, Wolfgang Wallner, Kunihiko Hayashi, David Lechner,
	Neil Armstrong, Mayuresh Chitale, Jonas Karlman, Shiji Yang,
	Rasmus Villemoes, Francois Berder, u-boot

When mkimage encounters a dm-verity subnode inside a component image
node it now automatically invokes veritysetup(8) with --no-superblock
to generate the Merkle hash tree, screen-scrapes the Root hash and Salt
from the tool output, and writes the computed properties back into the
FIT blob.

The user only needs to specify algorithm, data-block-size, and
hash-block-size in the ITS; mkimage fills in digest, salt,
num-data-blocks, and hash-start-block.  Because --no-superblock is
used, hash-start-block equals num-data-blocks with no off-by-one.

The image data property is replaced with the expanded content (original
data followed directly by the hash tree) so that subsequent hash and
signature subnodes operate on the complete image.

fit_image_add_verification_data() is restructured into two passes:
dm-verity first (may grow data), then hashes and signatures.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
v2:
 * use existing hex2bin() (and adapt it to be usable)
 * fix stale comment still including superblock despite veritysetup
   being called with --no-superblock
 * add power-of-two check for data-block-size and hash-block-size to
   mkimage
 * don't ignore return value of fdt_delprop()

 tools/fit_image.c  | 116 ++++++++++++++-
 tools/image-host.c | 349 ++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 455 insertions(+), 10 deletions(-)

diff --git a/tools/fit_image.c b/tools/fit_image.c
index 1dbc14c63e4..95942336820 100644
--- a/tools/fit_image.c
+++ b/tools/fit_image.c
@@ -40,10 +40,10 @@ static int fit_estimate_hash_sig_size(struct image_tool_params *params, const ch
 		return -EIO;
 
 	/*
-	 * Walk the FIT image, looking for nodes named hash* and
-	 * signature*. Since the interesting nodes are subnodes of an
-	 * image or configuration node, we are only interested in
-	 * those at depth exactly 3.
+	 * Walk the FIT image, looking for nodes named hash*,
+	 * signature*, and dm-verity.  Since the interesting nodes are
+	 * subnodes of an image or configuration node, we are only
+	 * interested in those at depth exactly 3.
 	 *
 	 * The estimate for a hash node is based on a sha512 digest
 	 * being 64 bytes, with another 64 bytes added to account for
@@ -55,6 +55,10 @@ static int fit_estimate_hash_sig_size(struct image_tool_params *params, const ch
 	 * account for fdt overhead and the various other properties
 	 * (hashed-nodes etc.) that will also be filled in.
 	 *
+	 * For a dm-verity node the small metadata properties (digest,
+	 * salt, two u32s and a temp-file path) are written into the
+	 * FDT by fit_image_process_verity().
+	 *
 	 * One could try to be more precise in the estimates by
 	 * looking at the "algo" property and, in the case of
 	 * configuration signatures, the sign-images property. Also,
@@ -76,6 +80,18 @@ static int fit_estimate_hash_sig_size(struct image_tool_params *params, const ch
 
 		if (signing && !strncmp(name, FIT_SIG_NODENAME, strlen(FIT_SIG_NODENAME)))
 			estimate += 1024;
+
+		if (!strcmp(name, FIT_VERITY_NODENAME)) {
+			if (!params->external_data) {
+				fprintf(stderr,
+					"%s: dm-verity requires external data (-E)\n",
+					params->cmdname);
+				munmap(fdt, sbuf.st_size);
+				close(fd);
+				return -EINVAL;
+			}
+			estimate += 256;
+		}
 	}
 
 	munmap(fdt, sbuf.st_size);
@@ -470,6 +486,67 @@ static int fit_write_images(struct image_tool_params *params, char *fdt)
 	return 0;
 }
 
+/**
+ * fit_copy_image_data() - copy image data, using verity temp file if present
+ * @fdt:		FIT blob
+ * @node:		image node offset
+ * @buf:		destination buffer
+ * @buf_ptr:	write offset within @buf
+ * @data:		embedded image data (used when no verity temp file exists)
+ * @lenp:		in/out: on entry, length of @data; on exit, bytes written
+ *
+ * When fit_image_process_verity() has run, a temp-file path is stored in
+ * the dm-verity subnode.  Read that file (original data + hash tree) into
+ * @buf instead of copying the embedded data property.
+ *
+ * Return: 0 on success, -EIO on error
+ */
+static int fit_copy_image_data(void *fdt, int node, void *buf,
+			       int buf_ptr, const void *data, int *lenp)
+{
+	const char *vfile;
+	char vfile_buf[256];
+	struct stat vst;
+	int vfd, ret;
+	int vn;
+
+	vn = fdt_subnode_offset(fdt, node, FIT_VERITY_NODENAME);
+	vfile = NULL;
+	if (vn >= 0)
+		vfile = fdt_getprop(fdt, vn, "verity-data-file", NULL);
+	if (!vfile) {
+		memcpy(buf + buf_ptr, data, *lenp);
+		return 0;
+	}
+
+	/* Copy path -- FDT shifts after delprop */
+	snprintf(vfile_buf, sizeof(vfile_buf), "%s", vfile);
+	ret = fdt_delprop(fdt, vn, "verity-data-file");
+	if (ret) {
+		fprintf(stderr, "Can't remove verity-data-file property: %s\n",
+			fdt_strerror(ret));
+		return -EPERM;
+	}
+
+	if (stat(vfile_buf, &vst)) {
+		fprintf(stderr, "Can't stat verity data: %s\n",
+			strerror(errno));
+		return -EIO;
+	}
+	vfd = open(vfile_buf, O_RDONLY);
+	if (vfd < 0 || read(vfd, buf + buf_ptr, vst.st_size) != vst.st_size) {
+		fprintf(stderr, "Can't read verity data: %s\n",
+			strerror(errno));
+		if (vfd >= 0)
+			close(vfd);
+		return -EIO;
+	}
+	close(vfd);
+	*lenp = vst.st_size;
+	unlink(vfile_buf);
+	return 0;
+}
+
 /**
  * fit_write_configs() - Write out a list of configurations to the FIT
  *
@@ -653,6 +730,11 @@ static int fit_extract_data(struct image_tool_params *params, const char *fname)
 	int node;
 	int align_size = 0;
 	int len = 0;
+	int verity_extra = 0;
+	int orig_len;
+	int vn;
+	const char *vf;
+	struct stat vst;
 
 	fd = mmap_fdt(params->cmdname, fname, 0, &fdt, &sbuf, false, false);
 	if (fd < 0)
@@ -686,11 +768,30 @@ static int fit_extract_data(struct image_tool_params *params, const char *fname)
 		align_size += 4;
 	}
 
+	/*
+	 * When dm-verity is active the external data for an image is
+	 * larger than the embedded data property (original + hash tree).
+	 * Walk images once more to account for the difference.
+	 */
+	fdt_for_each_subnode(node, fdt, images) {
+		vn = fdt_subnode_offset(fdt, node, FIT_VERITY_NODENAME);
+		orig_len = 0;
+
+		if (vn < 0)
+			continue;
+		vf = fdt_getprop(fdt, vn, "verity-data-file", NULL);
+		if (!vf)
+			continue;
+		fdt_getprop(fdt, node, FIT_DATA_PROP, &orig_len);
+		if (!stat(vf, &vst) && vst.st_size > orig_len)
+			verity_extra += vst.st_size - orig_len;
+	}
+
 	/*
 	 * Allocate space to hold the image data we will extract,
 	 * extral space allocate for image alignment to prevent overflow.
 	 */
-	buf = calloc(1, fit_size + align_size);
+	buf = calloc(1, fit_size + align_size + verity_extra);
 	if (!buf) {
 		ret = -ENOMEM;
 		goto err_munmap;
@@ -721,7 +822,10 @@ static int fit_extract_data(struct image_tool_params *params, const char *fname)
 		data = fdt_getprop(fdt, node, FIT_DATA_PROP, &len);
 		if (!data)
 			continue;
-		memcpy(buf + buf_ptr, data, len);
+
+		ret = fit_copy_image_data(fdt, node, buf, buf_ptr, data, &len);
+		if (ret)
+			goto err_munmap;
 		debug("Extracting data size %x\n", len);
 
 		ret = fdt_delprop(fdt, node, FIT_DATA_PROP);
diff --git a/tools/image-host.c b/tools/image-host.c
index 8b550af0dc1..9b0640cb18e 100644
--- a/tools/image-host.c
+++ b/tools/image-host.c
@@ -12,8 +12,11 @@
 #include <bootm.h>
 #include <fdt_region.h>
 #include <image.h>
+#include <hexdump.h>
 #include <version.h>
 
+#include <sys/stat.h>
+
 #if CONFIG_IS_ENABLED(FIT_SIGNATURE)
 #include <openssl/pem.h>
 #include <openssl/evp.h>
@@ -626,6 +628,309 @@ int fit_image_cipher_data(const char *keydir, void *keydest,
 		image_noffset, cipher_node_offset, data, size, cmdname);
 }
 
+/**
+ * fit_image_process_verity() - Run veritysetup and fill dm-verity properties
+ *
+ * Extracts the embedded image data to a temporary file, runs
+ * ``veritysetup format`` to generate the Merkle hash tree (appended to the
+ * same file), parses Root hash / Salt from its stdout, and writes the
+ * computed properties (digest, salt, num-data-blocks, hash-start-block)
+ * back into the FIT dm-verity subnode.
+ *
+ * The expanded data (original data + hash tree) is returned
+ * through @expanded_data / @expanded_size so that hash and signature
+ * subnodes can be computed over the complete image.  The FIT ``data``
+ * property is intentionally NOT replaced -- the expanded content is kept in
+ * a temporary file whose path is stored in the ``verity-data-file``
+ * property; ``fit_extract_data()`` picks it up later.
+ *
+ * @fit:		FIT blob (read-write)
+ * @image_name:		image unit name (for diagnostics)
+ * @image_noffset:	image node offset (parent of dm-verity)
+ * @verity_noffset:	dm-verity subnode offset
+ * @data:		embedded image data
+ * @data_size:		size of @data in bytes
+ * @expanded_data:	output -- malloc'd buffer with expanded content
+ * @expanded_size:	output -- size of @expanded_data
+ * Return: 0 on success, -ve on error (-ENOSPC when the FIT blob is full)
+ */
+static int fit_image_process_verity(void *fit, const char *image_name,
+				    int image_noffset, int verity_noffset,
+				    const void *data, size_t data_size,
+				    void **expanded_data, size_t *expanded_size)
+{
+	const char *algo_prop;
+	char algo[64];
+	const fdt32_t *val;
+	int data_block_size, hash_block_size;
+	uint32_t num_data_blocks;
+	size_t hash_offset;
+	uint32_t hash_start_block;
+	char tmpfile[] = "/tmp/mkimage-verity-XXXXXX";
+	char cmd[512];
+	FILE *fp;
+	char line[256];
+	char *colon, *value, *end;
+	char root_hash_hex[256] = {0};
+	char salt_hex[256] = {0};
+	uint8_t digest_bin[FIT_MAX_HASH_LEN];
+	uint8_t salt_bin[FIT_MAX_HASH_LEN];
+	int digest_len = 0, salt_len = 0;
+	void *expanded = NULL;
+	struct stat st;
+	int fd, ret;
+
+	*expanded_data = NULL;
+	*expanded_size = 0;
+
+	algo_prop = fdt_getprop(fit, verity_noffset, FIT_VERITY_ALGO_PROP,
+				NULL);
+	if (!algo_prop) {
+		fprintf(stderr,
+			"Missing '%s' in dm-verity node of '%s'\n",
+			FIT_VERITY_ALGO_PROP, image_name);
+		return -EINVAL;
+	}
+	/* Local copy -- the FDT pointer goes stale after fdt_setprop(). */
+	snprintf(algo, sizeof(algo), "%s", algo_prop);
+
+	/*
+	 * Validate algo against a known-good list before interpolating it
+	 * into the shell command passed to popen().  An unchecked value
+	 * could allow command injection when mkimage processes a crafted
+	 * .its file (e.g. algo = "sha256; rm -rf /").
+	 *
+	 * List derived from the veritysetup(8) man page (cryptsetup 2.x).
+	 */
+	{
+		static const char * const valid_algos[] = {
+			"sha1", "sha224", "sha256", "sha384", "sha512",
+			"ripemd160", "whirlpool",
+			"sha3-224", "sha3-256", "sha3-384", "sha3-512",
+			"stribog256", "stribog512",
+			"sm3",
+			/* blake2b/blake2s with explicit digest sizes */
+			"blake2b-160", "blake2b-256", "blake2b-384",
+			"blake2b-512",
+			"blake2s-128", "blake2s-160", "blake2s-224",
+			"blake2s-256",
+			NULL
+		};
+		int i;
+
+		for (i = 0; valid_algos[i]; i++)
+			if (!strcmp(algo, valid_algos[i]))
+				break;
+		if (!valid_algos[i]) {
+			fprintf(stderr,
+				"Unsupported dm-verity hash algorithm '%s' in '%s'\n",
+				algo, image_name);
+			return -EINVAL;
+		}
+	}
+
+	val = fdt_getprop(fit, verity_noffset, FIT_VERITY_DBS_PROP, NULL);
+	if (!val) {
+		fprintf(stderr,
+			"Missing '%s' in dm-verity node of '%s'\n",
+			FIT_VERITY_DBS_PROP, image_name);
+		return -EINVAL;
+	}
+	data_block_size = fdt32_to_cpu(*val);
+
+	val = fdt_getprop(fit, verity_noffset, FIT_VERITY_HBS_PROP, NULL);
+	if (!val) {
+		fprintf(stderr,
+			"Missing '%s' in dm-verity node of '%s'\n",
+			FIT_VERITY_HBS_PROP, image_name);
+		return -EINVAL;
+	}
+	hash_block_size = fdt32_to_cpu(*val);
+
+	if (data_block_size < 512 || (data_block_size & (data_block_size - 1)) ||
+	    hash_block_size < 512 || (hash_block_size & (hash_block_size - 1))) {
+		fprintf(stderr,
+			"Block sizes must be >= 512 and a power of two in dm-verity node of '%s'\n",
+			image_name);
+		return -EINVAL;
+	}
+
+	if (data_size % data_block_size) {
+		fprintf(stderr,
+			"Image '%s' size %zu not a multiple of data-block-size %d\n",
+			image_name, data_size, data_block_size);
+		return -EINVAL;
+	}
+
+	if (data_size / data_block_size > UINT32_MAX) {
+		fprintf(stderr,
+			"Image '%s' too large for dm-verity (> 2^32 data blocks)\n",
+			image_name);
+		return -EINVAL;
+	}
+	num_data_blocks = data_size / data_block_size;
+	hash_offset = data_size;
+
+	fd = mkstemp(tmpfile);
+	if (fd < 0) {
+		fprintf(stderr, "Can't create temp file: %s\n",
+			strerror(errno));
+		return -EIO;
+	}
+
+	if (write(fd, data, data_size) != (ssize_t)data_size) {
+		fprintf(stderr, "Can't write temp file: %s\n",
+			strerror(errno));
+		ret = -EIO;
+		goto err_unlink;
+	}
+	close(fd);
+	fd = -1;
+
+	snprintf(cmd, sizeof(cmd),
+		 "veritysetup format \"%s\" \"%s\" --no-superblock --hash=%s --data-block-size=%d --hash-block-size=%d --hash-offset=%zu 2>&1",
+		 tmpfile, tmpfile, algo, data_block_size, hash_block_size,
+		 hash_offset);
+
+	fp = popen(cmd, "r");
+	if (!fp) {
+		fprintf(stderr, "Can't run veritysetup: %s\n",
+			strerror(errno));
+		ret = -EIO;
+		goto err_unlink;
+	}
+
+	/* parse key: value lines from veritysetup stdout */
+	while (fgets(line, sizeof(line), fp)) {
+		colon = strchr(line, ':');
+		if (!colon)
+			continue;
+		value = colon + 1;
+		while (*value == ' ' || *value == '\t')
+			value++;
+		end = value + strlen(value) - 1;
+		while (end > value && (*end == '\n' || *end == '\r' ||
+				       *end == ' '))
+			*end-- = '\0';
+
+		if (!strncmp(line, "Root hash:", 10))
+			snprintf(root_hash_hex, sizeof(root_hash_hex),
+				 "%s", value);
+		else if (!strncmp(line, "Salt:", 5))
+			snprintf(salt_hex, sizeof(salt_hex), "%s", value);
+	}
+
+	ret = pclose(fp);
+	if (ret) {
+		fprintf(stderr, "veritysetup failed (exit %d) for '%s'\n",
+			WEXITSTATUS(ret), image_name);
+		ret = -EIO;
+		goto err_unlink;
+	}
+
+	if (!root_hash_hex[0] || !salt_hex[0]) {
+		fprintf(stderr, "Failed to parse veritysetup output for '%s'\n",
+			image_name);
+		ret = -EIO;
+		goto err_unlink;
+	}
+
+	digest_len = strlen(root_hash_hex) / 2;
+	salt_len   = strlen(salt_hex) / 2;
+
+	if (digest_len > (int)sizeof(digest_bin) ||
+	    salt_len > (int)sizeof(salt_bin)) {
+		fprintf(stderr, "Hash/salt too long for '%s'\n", image_name);
+		ret = -EINVAL;
+		goto err_unlink;
+	}
+
+	if (hex2bin(digest_bin, root_hash_hex, digest_len) ||
+	    hex2bin(salt_bin, salt_hex, salt_len)) {
+		fprintf(stderr, "Invalid hex in veritysetup output for '%s'\n",
+			image_name);
+		ret = -EINVAL;
+		goto err_unlink;
+	}
+
+	if (stat(tmpfile, &st)) {
+		fprintf(stderr, "Can't stat temp file: %s\n",
+			strerror(errno));
+		ret = -EIO;
+		goto err_unlink;
+	}
+
+	expanded = malloc(st.st_size);
+	if (!expanded) {
+		ret = -ENOMEM;
+		goto err_unlink;
+	}
+
+	fd = open(tmpfile, O_RDONLY);
+	if (fd < 0 || read(fd, expanded, st.st_size) != st.st_size) {
+		fprintf(stderr, "Can't read back temp file: %s\n",
+			strerror(errno));
+		ret = -EIO;
+		goto err_free;
+	}
+	close(fd);
+	fd = -1;
+
+	/* hash tree starts immediately after data (no superblock) */
+	hash_start_block = hash_offset / hash_block_size;
+
+	ret = fdt_setprop(fit, verity_noffset, FIT_VERITY_DIGEST_PROP,
+			  digest_bin, digest_len);
+	if (ret) {
+		ret = (ret == -FDT_ERR_NOSPACE) ? -ENOSPC : -EIO;
+		goto err_free;
+	}
+
+	ret = fdt_setprop(fit, verity_noffset, FIT_VERITY_SALT_PROP,
+			  salt_bin, salt_len);
+	if (ret) {
+		ret = (ret == -FDT_ERR_NOSPACE) ? -ENOSPC : -EIO;
+		goto err_free;
+	}
+
+	ret = fdt_setprop_u32(fit, verity_noffset, FIT_VERITY_NBLK_PROP,
+			      num_data_blocks);
+	if (ret) {
+		ret = (ret == -FDT_ERR_NOSPACE) ? -ENOSPC : -EIO;
+		goto err_free;
+	}
+
+	ret = fdt_setprop_u32(fit, verity_noffset, FIT_VERITY_HBLK_PROP,
+			      hash_start_block);
+	if (ret) {
+		ret = (ret == -FDT_ERR_NOSPACE) ? -ENOSPC : -EIO;
+		goto err_free;
+	}
+
+	/*
+	 * Stash the temp-file path so that fit_extract_data() can use
+	 * the expanded content for the external-data section.
+	 */
+	ret = fdt_setprop_string(fit, verity_noffset,
+				 "verity-data-file", tmpfile);
+	if (ret) {
+		ret = (ret == -FDT_ERR_NOSPACE) ? -ENOSPC : -EIO;
+		goto err_free;
+	}
+
+	*expanded_data = expanded;
+	*expanded_size = st.st_size;
+	return 0;
+
+err_free:
+	free(expanded);
+err_unlink:
+	if (fd >= 0)
+		close(fd);
+	unlink(tmpfile);
+	return ret;
+}
+
 /**
  * fit_image_add_verification_data() - calculate/set verig. data for image node
  *
@@ -652,6 +958,8 @@ int fit_image_cipher_data(const char *keydir, void *keydest,
  *
  * For signature details, please see doc/usage/fit/signature.rst
  *
+ * For dm-verity details, please see doc/usage/fit/dm-verity.rst
+ *
  * @keydir	Directory containing *.key and *.crt files (or NULL)
  * @keydest	FDT Blob to write public keys into (NULL if none)
  * @fit:	Pointer to the FIT format image header
@@ -667,9 +975,12 @@ int fit_image_add_verification_data(const char *keydir, const char *keyfile,
 		const char *cmdname, const char* algo_name)
 {
 	const char *image_name;
+	const char *node_name;
 	const void *data;
 	size_t size;
+	void *verity_data = NULL;
 	int noffset;
+	int ret;
 
 	/* Get image data and data length */
 	if (fit_image_get_emb_data(fit, image_noffset, &data, &size)) {
@@ -679,13 +990,39 @@ int fit_image_add_verification_data(const char *keydir, const char *keyfile,
 
 	image_name = fit_get_name(fit, image_noffset, NULL);
 
-	/* Process all hash subnodes of the component image node */
+	/*
+	 * Pass 1 -- dm-verity: run veritysetup to produce the Merkle
+	 * hash tree and fill in computed metadata.  The expanded
+	 * content (original data + hash tree) is returned in
+	 * verity_data so that pass 2 hashes the complete image.
+	 */
 	for (noffset = fdt_first_subnode(fit, image_noffset);
 	     noffset >= 0;
 	     noffset = fdt_next_subnode(fit, noffset)) {
-		const char *node_name;
-		int ret = 0;
+		if (!strcmp(fit_get_name(fit, noffset, NULL),
+			    FIT_VERITY_NODENAME)) {
+			ret = fit_image_process_verity(fit, image_name,
+						       image_noffset,
+						       noffset,
+						       data, size,
+						       &verity_data,
+						       &size);
+			if (ret)
+				return ret;
+			if (verity_data)
+				data = verity_data;
+			break;
+		}
+	}
 
+	/*
+	 * Pass 2 -- hashes and signatures: compute over the (possibly
+	 * expanded) image data.
+	 */
+	for (noffset = fdt_first_subnode(fit, image_noffset);
+	     noffset >= 0;
+	     noffset = fdt_next_subnode(fit, noffset)) {
+		ret = 0;
 		/*
 		 * Check subnode name, must be equal to "hash" or "signature".
 		 * Multiple hash nodes require unique unit node
@@ -704,10 +1041,13 @@ int fit_image_add_verification_data(const char *keydir, const char *keyfile,
 				comment, require_keys, engine_id, cmdname,
 				algo_name);
 		}
-		if (ret < 0)
+		if (ret < 0) {
+			free(verity_data);
 			return ret;
+		}
 	}
 
+	free(verity_data);
 	return 0;
 }
 
-- 
2.53.0

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v2 5/7] doc: fit: add dm-verity boot parameter documentation
  2026-04-16  1:46 [PATCH v2 0/7] fit: dm-verity support Daniel Golle
                   ` (3 preceding siblings ...)
  2026-04-16  1:46 ` [PATCH v2 4/7] tools: mkimage: add dm-verity Merkle-tree generation Daniel Golle
@ 2026-04-16  1:46 ` Daniel Golle
  2026-04-16 19:38   ` Simon Glass
  2026-04-16  1:47 ` [PATCH v2 6/7] test: boot: add runtime unit test for fit_verity_build_cmdline() Daniel Golle
  2026-04-16  1:47 ` [PATCH v2 7/7] test: py: add mkimage dm-verity round-trip test Daniel Golle
  6 siblings, 1 reply; 19+ messages in thread
From: Daniel Golle @ 2026-04-16  1:46 UTC (permalink / raw)
  To: Tom Rini, Simon Glass, Quentin Schulz, Kory Maincent,
	Mattijs Korpershoek, Peng Fan, Heinrich Schuchardt, Martin Schwan,
	Daniel Golle, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
	Aristo Chen, Ludwig Nussel, Benjamin ROBIN, Marek Vasut,
	James Hilliard, Wolfgang Wallner, Kunihiko Hayashi, David Lechner,
	Neil Armstrong, Mayuresh Chitale, Jonas Karlman, Shiji Yang,
	Rasmus Villemoes, Francois Berder, u-boot

Add documentation for CONFIG_FIT_VERITY which allows U-Boot to
construct dm-mod.create= and dm-mod.waitfor= kernel command-line
parameters from dm-verity metadata embedded in FIT filesystem
sub-images.

The new document covers the relationship between FIT loadable indices
and the /dev/fitN block devices that the Linux uImage.FIT block driver
creates, provides a complete .its example with a dm-verity-protected
SquashFS root filesystem, describes all required and optional dm-verity
subnode properties and explains how mkimage generates the verity
metadata automatically.

dm-verity is only supported for external-data FIT images (mkimage -E);
mkimage aborts with an error if the flag is omitted.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
v2:
 * strip example (only user-provided properties in .its)
 * remove stale off-by-one reflecting --no-superblock usage

 doc/usage/fit/dm-verity.rst | 282 ++++++++++++++++++++++++++++++++++++
 doc/usage/fit/index.rst     |   1 +
 2 files changed, 283 insertions(+)
 create mode 100644 doc/usage/fit/dm-verity.rst

diff --git a/doc/usage/fit/dm-verity.rst b/doc/usage/fit/dm-verity.rst
new file mode 100644
index 00000000000..247b655818f
--- /dev/null
+++ b/doc/usage/fit/dm-verity.rst
@@ -0,0 +1,282 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+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_VERITY`` must be
+  enabled so the kernel can process the ``dm-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()``)
+
+1. After all loadable sub-images have been loaded,
+   ``fit_verity_build_cmdline()`` iterates the configuration's
+   ``loadables`` list.
+
+2. For each loadable that is an ``IH_TYPE_FILESYSTEM`` image **and**
+   contains a ``dm-verity`` child node, a dm-verity target specification is
+   built by the helper ``fit_verity_build_target()``.
+
+3. 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.
+
+4. The resulting fragments are stored in ``struct bootm_headers``:
+
+   ``images->dm_mod_create``
+     The full dm-verity target table. Multiple targets are separated by ``;``.
+
+   ``images->dm_mod_waitfor``
+     Comma-separated list of ``/dev/fitN`` devices so the kernel waits for
+     the underlying FIT block devices to appear before activating
+     device-mapper.
+
+**Apply phase** (``BOOTM_STATE_OS_PREP``)
+
+5. Just before ``bootm_process_cmdline_env()`` processes the ``bootargs``
+   environment variable, ``fit_verity_apply_bootargs()`` appends the
+   ``dm-mod.create=`` and ``dm-mod.waitfor=`` parameters.
+
+**Bootmeth integration**
+
+  Because the fragments are stored in ``struct bootm_headers``, a boot
+  method can check ``fit_verity_active(images)`` between bootm state
+  invocations. A typical pattern splits ``bootm_run_states()`` into two
+  calls -- one for ``START|FINDOS|FINDOTHER|LOADOS`` and one for
+  ``OS_PREP|OS_GO`` -- and inspects ``fit_verity_active()`` in
+  between to decide whether to add a ``root=`` 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``):
+
+.. list-table::
+   :header-rows: 1
+   :widths: 20 15 65
+
+   * - Property
+     - Type
+     - Description
+   * - ``algo``
+     - string
+     - Hash algorithm name, e.g. ``"sha256"``.
+   * - ``data-block-size``
+     - u32
+     - Data block size in bytes (>= 512, typically 4096).
+   * - ``hash-block-size``
+     - u32
+     - Hash block size in bytes (>= 512, typically 4096).
+
+Computed properties (filled in by ``mkimage``):
+
+.. list-table::
+   :header-rows: 1
+   :widths: 20 15 65
+
+   * - Property
+     - Type
+     - Description
+   * - ``num-data-blocks``
+     - u32
+     - Number of data blocks in the filesystem image (computed from the
+       image size and ``data-block-size``).
+   * - ``hash-start-block``
+     - u32
+     - Offset in ``hash-block-size``-sized blocks from the start of the
+       sub-image to the root block of the hash tree.
+   * - ``digest``
+     - byte array
+     - Root hash of the Merkle tree, stored as raw bytes. Length must match
+       the output size of ``algo``.
+   * - ``salt``
+     - 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):
+
+.. list-table::
+   :header-rows: 1
+   :widths: 30 70
+
+   * - Property
+     - Description
+   * - ``restart-on-corruption``
+     - Restart the system on data corruption.
+   * - ``panic-on-corruption``
+     - Panic the system on data corruption.
+   * - ``restart-on-error``
+     - Restart the system on I/O error.
+   * - ``panic-on-error``
+     - Panic the system on I/O error.
+   * - ``check-at-most-once``
+     - Verify data blocks only on first read.
+
+
+Generating verity metadata
+--------------------------
+
+``mkimage`` automates the entire process. When it encounters a
+``dm-verity`` subnode, it:
+
+1. Writes the embedded image data to a temporary file.
+2. Runs ``veritysetup format`` with the user-supplied algorithm and
+   block sizes.
+3. Parses ``Root hash`` and ``Salt`` from ``veritysetup`` stdout.
+4. Reads back the expanded file (original data + Merkle hash tree) and
+   replaces the image's ``data`` property.
+5. Writes the computed ``digest``, ``salt``, ``num-data-blocks``, and
+   ``hash-start-block`` properties into the ``dm-verity`` subnode.
+
+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)
+    mkimage -E -f image.its image.itb
+
+``veritysetup`` (from the cryptsetup_ package) must be installed on
+the build host.
+
+.. _cryptsetup: https://gitlab.com/cryptsetup/cryptsetup
+
+.. 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. Consequently ``hash-start-block``
+   equals ``num-data-blocks``.
+
+Kconfig
+-------
+
+``CONFIG_FIT_VERITY``
+  Depends on ``CONFIG_FIT`` and ``CONFIG_OF_LIBFDT``.
+  When enabled, ``fit_verity_build_cmdline()`` and
+  ``fit_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 the ``bootm`` command and
+  BOOTSTD boot methods.
diff --git a/doc/usage/fit/index.rst b/doc/usage/fit/index.rst
index 6c78d8584ed..d17582b1d64 100644
--- a/doc/usage/fit/index.rst
+++ b/doc/usage/fit/index.rst
@@ -11,6 +11,7 @@ images that it reads and boots. Documentation about FIT is available in
     :maxdepth: 1
 
     beaglebone_vboot
+    dm-verity
     howto
     kernel_fdt
     kernel_fdts_compressed
-- 
2.53.0

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v2 6/7] test: boot: add runtime unit test for fit_verity_build_cmdline()
  2026-04-16  1:46 [PATCH v2 0/7] fit: dm-verity support Daniel Golle
                   ` (4 preceding siblings ...)
  2026-04-16  1:46 ` [PATCH v2 5/7] doc: fit: add dm-verity boot parameter documentation Daniel Golle
@ 2026-04-16  1:47 ` Daniel Golle
  2026-04-16 19:36   ` Simon Glass
  2026-04-16  1:47 ` [PATCH v2 7/7] test: py: add mkimage dm-verity round-trip test Daniel Golle
  6 siblings, 1 reply; 19+ messages in thread
From: Daniel Golle @ 2026-04-16  1:47 UTC (permalink / raw)
  To: Tom Rini, Simon Glass, Quentin Schulz, Kory Maincent,
	Mattijs Korpershoek, Peng Fan, Heinrich Schuchardt, Martin Schwan,
	Daniel Golle, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
	Aristo Chen, Ludwig Nussel, Benjamin ROBIN, Marek Vasut,
	James Hilliard, Wolfgang Wallner, Kunihiko Hayashi, David Lechner,
	Neil Armstrong, Mayuresh Chitale, Jonas Karlman, Shiji Yang,
	Rasmus Villemoes, Francois Berder, u-boot

Add test/boot/fit_verity.c with four tests that construct FIT blobs
in memory and exercise fit_verity_build_cmdline().

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
v2: new patch

 test/boot/Makefile     |   1 +
 test/boot/fit_verity.c | 298 +++++++++++++++++++++++++++++++++++++++++
 test/cmd_ut.c          |   2 +
 3 files changed, 301 insertions(+)
 create mode 100644 test/boot/fit_verity.c

diff --git a/test/boot/Makefile b/test/boot/Makefile
index 89538d4f0a6..d98f212b243 100644
--- a/test/boot/Makefile
+++ b/test/boot/Makefile
@@ -15,6 +15,7 @@ endif
 ifdef CONFIG_SANDBOX
 obj-$(CONFIG_$(PHASE_)CMDLINE) += bootm.o
 endif
+obj-$(CONFIG_$(PHASE_)FIT_VERITY) += fit_verity.o
 obj-$(CONFIG_MEASURED_BOOT) += measurement.o
 
 ifdef CONFIG_OF_LIVE
diff --git a/test/boot/fit_verity.c b/test/boot/fit_verity.c
new file mode 100644
index 00000000000..40c54998387
--- /dev/null
+++ b/test/boot/fit_verity.c
@@ -0,0 +7,302 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for FIT dm-verity cmdline generation
+ *
+ * Copyright 2026 Daniel Golle <daniel@makrotopia.org>
+ */
+
+#include <image.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+#define FIT_VERITY_TEST(_name, _flags)	UNIT_TEST(_name, _flags, fit_verity)
+
+/* FIT blob buffer size — generous to avoid FDT_ERR_NOSPACE */
+#define FIT_BUF_SIZE	4096
+
+/* Test digest (32 bytes = sha256) */
+static const u8 test_digest[32] = {
+	0x8e, 0x67, 0x91, 0x63, 0x7f, 0x93, 0xcb, 0xb8,
+	0x1f, 0xc4, 0x52, 0x99, 0xe2, 0x03, 0xcb, 0xe8,
+	0x5c, 0xa2, 0xe4, 0x7a, 0x38, 0xf5, 0x05, 0x1b,
+	0xdd, 0xee, 0xce, 0x92, 0xd7, 0xb1, 0xc9, 0xf9,
+};
+
+/* Test salt (32 bytes) */
+static const u8 test_salt[32] = {
+	0xaa, 0x7b, 0x11, 0xf8, 0xdb, 0x8f, 0xe2, 0xe5,
+	0xbf, 0xd4, 0xec, 0xa1, 0xd1, 0x8a, 0x22, 0xb5,
+	0xde, 0x7e, 0xa3, 0x9d, 0x2e, 0x1b, 0x93, 0xbb,
+	0x72, 0x72, 0xce, 0x0c, 0x6c, 0xa3, 0xcc, 0x8e,
+};
+
+/**
+ * build_verity_fit() - construct a minimal FIT blob with dm-verity metadata
+ * @buf:		output buffer (at least FIT_BUF_SIZE bytes)
+ * @num_loadables:	number of filesystem loadables to create (1 or 2)
+ *
+ * Builds a FIT blob containing:
+ *  - /images/rootfsN  with type="filesystem" and a dm-verity subnode
+ *  - /configurations/conf-1  referencing the loadable(s)
+ *
+ * Return: configuration node offset, or -ve on error
+ */
+static int build_verity_fit(void *buf, int num_loadables)
+{
+	int images_node, conf_node, confs_node, img_node, verity_node;
+	fdt32_t val;
+	int ret, i;
+	char name[32];
+	/*
+	 * Build the loadables string list.  FDT stringlists are concatenated
+	 * NUL-terminated strings.  E.g. "rootfs0\0rootfs1\0"
+	 */
+	char loadables[128];
+	int loadables_len = 0;
+
+	ret = fdt_create_empty_tree(buf, FIT_BUF_SIZE);
+	if (ret)
+		return ret;
+
+	/* /images */
+	images_node = fdt_add_subnode(buf, 0, "images");
+	if (images_node < 0)
+		return images_node;
+
+	for (i = 0; i < num_loadables; i++) {
+		snprintf(name, sizeof(name), "rootfs%d", i);
+
+		img_node = fdt_add_subnode(buf, images_node, name);
+		if (img_node < 0)
+			return img_node;
+
+		ret = fdt_setprop_string(buf, img_node, "type", "filesystem");
+		if (ret)
+			return ret;
+
+		verity_node = fdt_add_subnode(buf, img_node, "dm-verity");
+		if (verity_node < 0)
+			return verity_node;
+
+		ret = fdt_setprop_string(buf, verity_node, "algo", "sha256");
+		if (ret)
+			return ret;
+
+		val = cpu_to_fdt32(4096);
+		ret = fdt_setprop(buf, verity_node, "data-block-size",
+				  &val, sizeof(val));
+		if (ret)
+			return ret;
+
+		ret = fdt_setprop(buf, verity_node, "hash-block-size",
+				  &val, sizeof(val));
+		if (ret)
+			return ret;
+
+		val = cpu_to_fdt32(100);
+		ret = fdt_setprop(buf, verity_node, "num-data-blocks",
+				  &val, sizeof(val));
+		if (ret)
+			return ret;
+
+		val = cpu_to_fdt32(100);
+		ret = fdt_setprop(buf, verity_node, "hash-start-block",
+				  &val, sizeof(val));
+		if (ret)
+			return ret;
+
+		ret = fdt_setprop(buf, verity_node, "digest",
+				  test_digest, sizeof(test_digest));
+		if (ret)
+			return ret;
+
+		ret = fdt_setprop(buf, verity_node, "salt",
+				  test_salt, sizeof(test_salt));
+		if (ret)
+			return ret;
+
+		/* Append to loadables stringlist */
+		loadables_len += snprintf(loadables + loadables_len,
+					  sizeof(loadables) - loadables_len,
+					  "%s", name) + 1;
+	}
+
+	/* /configurations/conf-1 */
+	confs_node = fdt_add_subnode(buf, 0, "configurations");
+	if (confs_node < 0)
+		return confs_node;
+
+	conf_node = fdt_add_subnode(buf, confs_node, "conf-1");
+	if (conf_node < 0)
+		return conf_node;
+
+	ret = fdt_setprop(buf, conf_node, "loadables",
+			  loadables, loadables_len);
+	if (ret)
+		return ret;
+
+	return conf_node;
+}
+
+/* Test: single dm-verity loadable produces correct cmdline fragments */
+static int fit_verity_test_single(struct unit_test_state *uts)
+{
+	char buf[FIT_BUF_SIZE];
+	struct bootm_headers images;
+	int conf_noffset;
+
+	conf_noffset = build_verity_fit(buf, 1);
+	ut_assert(conf_noffset >= 0);
+
+	memset(&images, 0, sizeof(images));
+	ut_assertok(fit_verity_build_cmdline(buf, conf_noffset, &images));
+
+	/* dm_mod_create should contain the target spec for rootfs0 */
+	ut_assertnonnull(images.dm_mod_create);
+	ut_assert(strstr(images.dm_mod_create, "rootfs0,,,"));
+	ut_assert(strstr(images.dm_mod_create, "verity 1"));
+	ut_assert(strstr(images.dm_mod_create, "/dev/fit0"));
+	ut_assert(strstr(images.dm_mod_create, "4096 4096 100 100"));
+	ut_assert(strstr(images.dm_mod_create, "sha256"));
+	/* Check hex-encoded digest prefix */
+	ut_assert(strstr(images.dm_mod_create, "8e6791637f93cbb8"));
+	/* Check hex-encoded salt prefix */
+	ut_assert(strstr(images.dm_mod_create, "aa7b11f8db8fe2e5"));
+
+	/* dm_mod_waitfor should reference /dev/fit0 */
+	ut_assertnonnull(images.dm_mod_waitfor);
+	ut_asserteq_str("/dev/fit0", images.dm_mod_waitfor);
+
+	fit_verity_free(&images);
+	ut_assertnull(images.dm_mod_create);
+	ut_assertnull(images.dm_mod_waitfor);
+
+	return 0;
+}
+
+FIT_VERITY_TEST(fit_verity_test_single, 0);
+
+/* Test: FIT with no dm-verity subnode returns 0, pointers stay NULL */
+static int fit_verity_test_no_verity(struct unit_test_state *uts)
+{
+	char buf[FIT_BUF_SIZE];
+	struct bootm_headers images;
+	int conf_node, images_node, img_node, confs_node;
+	int ret;
+
+	ret = fdt_create_empty_tree(buf, FIT_BUF_SIZE);
+	ut_assertok(ret);
+
+	images_node = fdt_add_subnode(buf, 0, "images");
+	ut_assert(images_node >= 0);
+
+	img_node = fdt_add_subnode(buf, images_node, "rootfs");
+	ut_assert(img_node >= 0);
+	ut_assertok(fdt_setprop_string(buf, img_node, "type", "filesystem"));
+	/* No dm-verity subnode */
+
+	confs_node = fdt_add_subnode(buf, 0, "configurations");
+	ut_assert(confs_node >= 0);
+	conf_node = fdt_add_subnode(buf, confs_node, "conf-1");
+	ut_assert(conf_node >= 0);
+	ut_assertok(fdt_setprop_string(buf, conf_node, "loadables", "rootfs"));
+
+	memset(&images, 0, sizeof(images));
+	ut_asserteq(0, fit_verity_build_cmdline(buf, conf_node, &images));
+	ut_assertnull(images.dm_mod_create);
+	ut_assertnull(images.dm_mod_waitfor);
+
+	return 0;
+}
+
+FIT_VERITY_TEST(fit_verity_test_no_verity, 0);
+
+/* Test: two dm-verity loadables produce combined cmdline */
+static int fit_verity_test_two_loadables(struct unit_test_state *uts)
+{
+	char buf[FIT_BUF_SIZE];
+	struct bootm_headers images;
+	int conf_noffset;
+
+	conf_noffset = build_verity_fit(buf, 2);
+	ut_assert(conf_noffset >= 0);
+
+	memset(&images, 0, sizeof(images));
+	ut_assertok(fit_verity_build_cmdline(buf, conf_noffset, &images));
+
+	/* Both targets should appear, separated by ";" */
+	ut_assertnonnull(images.dm_mod_create);
+	ut_assert(strstr(images.dm_mod_create, "rootfs0,,,"));
+	ut_assert(strstr(images.dm_mod_create, ";rootfs1,,,"));
+	ut_assert(strstr(images.dm_mod_create, "/dev/fit0"));
+	ut_assert(strstr(images.dm_mod_create, "/dev/fit1"));
+
+	/* dm_mod_waitfor should list both devices */
+	ut_assertnonnull(images.dm_mod_waitfor);
+	ut_assert(strstr(images.dm_mod_waitfor, "/dev/fit0"));
+	ut_assert(strstr(images.dm_mod_waitfor, "/dev/fit1"));
+
+	fit_verity_free(&images);
+	return 0;
+}
+
+FIT_VERITY_TEST(fit_verity_test_two_loadables, 0);
+
+/* Test: invalid block size (not power of two) returns -EINVAL */
+static int fit_verity_test_bad_blocksize(struct unit_test_state *uts)
+{
+	char buf[FIT_BUF_SIZE];
+	struct bootm_headers images;
+	int images_node, conf_node, confs_node, img_node, verity_node;
+	fdt32_t val;
+	int ret;
+
+	ret = fdt_create_empty_tree(buf, FIT_BUF_SIZE);
+	ut_assertok(ret);
+
+	images_node = fdt_add_subnode(buf, 0, "images");
+	ut_assert(images_node >= 0);
+
+	img_node = fdt_add_subnode(buf, images_node, "rootfs");
+	ut_assert(img_node >= 0);
+	ut_assertok(fdt_setprop_string(buf, img_node, "type", "filesystem"));
+
+	verity_node = fdt_add_subnode(buf, img_node, "dm-verity");
+	ut_assert(verity_node >= 0);
+
+	ut_assertok(fdt_setprop_string(buf, verity_node, "algo", "sha256"));
+
+	/* 3000 is not a power of two */
+	val = cpu_to_fdt32(3000);
+	ut_assertok(fdt_setprop(buf, verity_node, "data-block-size",
+				&val, sizeof(val)));
+	val = cpu_to_fdt32(4096);
+	ut_assertok(fdt_setprop(buf, verity_node, "hash-block-size",
+				&val, sizeof(val)));
+
+	val = cpu_to_fdt32(100);
+	ut_assertok(fdt_setprop(buf, verity_node, "num-data-blocks",
+				&val, sizeof(val)));
+	ut_assertok(fdt_setprop(buf, verity_node, "hash-start-block",
+				&val, sizeof(val)));
+
+	ut_assertok(fdt_setprop(buf, verity_node, "digest",
+				test_digest, sizeof(test_digest)));
+	ut_assertok(fdt_setprop(buf, verity_node, "salt",
+				test_salt, sizeof(test_salt)));
+
+	confs_node = fdt_add_subnode(buf, 0, "configurations");
+	ut_assert(confs_node >= 0);
+	conf_node = fdt_add_subnode(buf, confs_node, "conf-1");
+	ut_assert(conf_node >= 0);
+	ut_assertok(fdt_setprop_string(buf, conf_node, "loadables", "rootfs"));
+
+	memset(&images, 0, sizeof(images));
+	ut_asserteq(-EINVAL, fit_verity_build_cmdline(buf, conf_node, &images));
+	ut_assertnull(images.dm_mod_create);
+	ut_assertnull(images.dm_mod_waitfor);
+
+	return 0;
+}
+
+FIT_VERITY_TEST(fit_verity_test_bad_blocksize, 0);
diff --git a/test/cmd_ut.c b/test/cmd_ut.c
index 44e5fdfdaa6..d1b376f617c 100644
--- a/test/cmd_ut.c
+++ b/test/cmd_ut.c
@@ -59,6 +59,7 @@ SUITE_DECL(env);
 SUITE_DECL(exit);
 SUITE_DECL(fdt);
 SUITE_DECL(fdt_overlay);
+SUITE_DECL(fit_verity);
 SUITE_DECL(font);
 SUITE_DECL(hush);
 SUITE_DECL(lib);
@@ -86,6 +87,7 @@ static struct suite suites[] = {
 	SUITE(exit, "shell exit and variables"),
 	SUITE(fdt, "fdt command"),
 	SUITE(fdt_overlay, "device tree overlays"),
+	SUITE(fit_verity, "FIT dm-verity cmdline generation"),
 	SUITE(font, "font command"),
 	SUITE(hush, "hush behaviour"),
 	SUITE(lib, "library functions"),
-- 
2.53.0

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v2 7/7] test: py: add mkimage dm-verity round-trip test
  2026-04-16  1:46 [PATCH v2 0/7] fit: dm-verity support Daniel Golle
                   ` (5 preceding siblings ...)
  2026-04-16  1:47 ` [PATCH v2 6/7] test: boot: add runtime unit test for fit_verity_build_cmdline() Daniel Golle
@ 2026-04-16  1:47 ` Daniel Golle
  2026-04-16 19:36   ` Simon Glass
  6 siblings, 1 reply; 19+ messages in thread
From: Daniel Golle @ 2026-04-16  1:47 UTC (permalink / raw)
  To: Tom Rini, Simon Glass, Quentin Schulz, Kory Maincent,
	Mattijs Korpershoek, Peng Fan, Heinrich Schuchardt, Martin Schwan,
	Daniel Golle, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
	Aristo Chen, Ludwig Nussel, Benjamin ROBIN, Marek Vasut,
	James Hilliard, Wolfgang Wallner, Kunihiko Hayashi, David Lechner,
	Neil Armstrong, Mayuresh Chitale, Jonas Karlman, Shiji Yang,
	Rasmus Villemoes, Francois Berder, u-boot

Add test/py/tests/test_fit_verity.py with two tests.

Both tests are skipped if veritysetup is not installed on the host.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
v2: new patch

 test/py/tests/test_fit_verity.py | 153 +++++++++++++++++++++++++++++++
 1 file changed, 153 insertions(+)
 create mode 100644 test/py/tests/test_fit_verity.py

diff --git a/test/py/tests/test_fit_verity.py b/test/py/tests/test_fit_verity.py
new file mode 100644
index 00000000000..670232995cc
--- /dev/null
+++ b/test/py/tests/test_fit_verity.py
@@ -0,0 +1,153 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright 2026 Daniel Golle <daniel@makrotopia.org>
+
+"""
+Test mkimage dm-verity Merkle-tree generation
+
+Build a minimal .its with a dm-verity subnode (user-provided properties only),
+run mkimage -E, and verify that the computed properties (digest, salt,
+num-data-blocks, hash-start-block) are written into the resulting FIT.
+
+This test does not run the sandbox.  It only exercises the host tool 'mkimage'.
+Requires 'veritysetup' from the cryptsetup package on the build host.
+"""
+
+import os
+import shutil
+import pytest
+import struct
+import utils
+
+ITS_TEMPLATE = """\
+/dts-v1/;
+
+/ {
+    description = "dm-verity test";
+    #address-cells = <1>;
+
+    images {
+        rootfs {
+            description = "test filesystem";
+            data = /incbin/("./rootfs.bin");
+            type = "filesystem";
+            arch = "sandbox";
+            compression = "none";
+
+            dm-verity {
+                algo = "sha256";
+                data-block-size = <4096>;
+                hash-block-size = <4096>;
+            };
+        };
+    };
+
+    configurations {
+        default = "conf-1";
+        conf-1 {
+            description = "test config";
+            loadables = "rootfs";
+        };
+    };
+};
+"""
+
+def have_veritysetup():
+    return shutil.which('veritysetup') is not None
+
+
+@pytest.mark.requiredtool('dtc')
+@pytest.mark.requiredtool('fdtget')
+@pytest.mark.skipif(not have_veritysetup(),
+                    reason='veritysetup not installed')
+def test_mkimage_verity(ubman):
+    """Test that mkimage computes dm-verity properties correctly."""
+
+    mkimage = ubman.config.build_dir + '/tools/mkimage'
+    tempdir = os.path.join(ubman.config.result_dir, 'verity')
+    os.makedirs(tempdir, exist_ok=True)
+
+    rootfs_file = os.path.join(tempdir, 'rootfs.bin')
+    its_file = os.path.join(tempdir, 'image.its')
+    fit_file = os.path.join(tempdir, 'image.itb')
+
+    # Create a dummy filesystem image: 64 x 4096-byte blocks of 0xa5
+    block_size = 4096
+    num_blocks = 64
+    with open(rootfs_file, 'wb') as f:
+        f.write(bytes([0xa5]) * block_size * num_blocks)
+
+    with open(its_file, 'w') as f:
+        f.write(ITS_TEMPLATE)
+
+    # Build the FIT with external data (required for dm-verity)
+    dtc_args = f'-I dts -O dtb -i {tempdir}'
+    utils.run_and_log(ubman,
+                      [mkimage, '-E', '-D', dtc_args, '-f', its_file, fit_file])
+
+    # Helper to read FIT properties via fdtget
+    def fdt_get(node, prop):
+        val = utils.run_and_log(
+            ubman, f'fdtget {fit_file} {node} {prop}')
+        return val.strip()
+
+    def fdt_get_hex(node, prop):
+        """Read a byte-array property as hex string."""
+        val = utils.run_and_log(
+            ubman, f'fdtget -tbx {fit_file} {node} {prop}')
+        return ''.join(b.zfill(2) for b in val.strip().split())
+
+    verity_path = '/images/rootfs/dm-verity'
+
+    # Verify mkimage filled in the computed properties
+    algo = fdt_get(verity_path, 'algo')
+    assert algo == 'sha256', f'algo mismatch: {algo}'
+
+    dbs = int(fdt_get(verity_path, 'data-block-size'))
+    assert dbs == block_size, f'data-block-size mismatch: {dbs}'
+
+    hbs = int(fdt_get(verity_path, 'hash-block-size'))
+    assert hbs == block_size, f'hash-block-size mismatch: {hbs}'
+
+    nblk = int(fdt_get(verity_path, 'num-data-blocks'))
+    assert nblk == num_blocks, f'num-data-blocks mismatch: {nblk} != {num_blocks}'
+
+    hblk = int(fdt_get(verity_path, 'hash-start-block'))
+    # With --no-superblock, hash-start-block == num-data-blocks
+    assert hblk == num_blocks, f'hash-start-block mismatch: {hblk} != {num_blocks}'
+
+    # digest and salt should be non-empty hex strings (sha256 = 32 bytes = 64 hex chars)
+    digest = fdt_get_hex(verity_path, 'digest')
+    assert len(digest) == 64, f'digest length mismatch: {len(digest)} (expected 64)'
+    # Should not be all zeros
+    assert digest != '0' * 64, 'digest is all zeros'
+
+    salt = fdt_get_hex(verity_path, 'salt')
+    assert len(salt) == 64, f'salt length mismatch: {len(salt)} (expected 64)'
+
+
+@pytest.mark.requiredtool('dtc')
+@pytest.mark.skipif(not have_veritysetup(),
+                    reason='veritysetup not installed')
+def test_mkimage_verity_requires_external(ubman):
+    """Test that mkimage rejects dm-verity without -E flag."""
+
+    mkimage = ubman.config.build_dir + '/tools/mkimage'
+    tempdir = os.path.join(ubman.config.result_dir, 'verity_no_ext')
+    os.makedirs(tempdir, exist_ok=True)
+
+    rootfs_file = os.path.join(tempdir, 'rootfs.bin')
+    its_file = os.path.join(tempdir, 'image.its')
+    fit_file = os.path.join(tempdir, 'image.itb')
+
+    with open(rootfs_file, 'wb') as f:
+        f.write(bytes([0xa5]) * 4096 * 8)
+
+    with open(its_file, 'w') as f:
+        f.write(ITS_TEMPLATE)
+
+    # Without -E, mkimage should fail for dm-verity images
+    dtc_args = f'-I dts -O dtb -i {tempdir}'
+    with pytest.raises(Exception):
+        utils.run_and_log(ubman,
+                          [mkimage, '-D', dtc_args, '-f', its_file, fit_file])
-- 
2.53.0

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 3/7] include: hexdump: make hex2bin() usable from host tools
  2026-04-16  1:46 ` [PATCH v2 3/7] include: hexdump: make hex2bin() usable from host tools Daniel Golle
@ 2026-04-16 11:00   ` Heinrich Schuchardt
  2026-04-16 19:37   ` Simon Glass
  1 sibling, 0 replies; 19+ messages in thread
From: Heinrich Schuchardt @ 2026-04-16 11:00 UTC (permalink / raw)
  To: Daniel Golle, Tom Rini, Simon Glass, Quentin Schulz,
	Kory Maincent, Mattijs Korpershoek, Peng Fan, Martin Schwan,
	Anshul Dalal, Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
	Ludwig Nussel, Benjamin ROBIN, Marek Vasut, James Hilliard,
	Wolfgang Wallner, Kunihiko Hayashi, David Lechner, Neil Armstrong,
	Mayuresh Chitale, Jonas Karlman, Shiji Yang, Rasmus Villemoes,
	Francois Berder, u-boot

Am 16. April 2026 03:46:42 MESZ schrieb Daniel Golle <daniel@makrotopia.org>:
>Make hexdump.h work in host-tool builds by using 'uint8_t' instead
>of 'u8', and including either user-space libc <ctype.h> for host-tools
>or <linux/ctype.h> when building U-Boot itself.
>
>Signed-off-by: Daniel Golle <daniel@makrotopia.org>
>---
>v2: new patch
>
> include/hexdump.h | 8 ++++++--
> 1 file changed, 6 insertions(+), 2 deletions(-)
>
>diff --git a/include/hexdump.h b/include/hexdump.h
>index f2ca4793d69..5cb48d79efe 100644
>--- a/include/hexdump.h
>+++ b/include/hexdump.h
>@@ -7,7 +7,11 @@
> #ifndef HEXDUMP_H
> #define HEXDUMP_H
> 
>+#ifdef USE_HOSTCC
>+#include <ctype.h>
>+#else
> #include <linux/ctype.h>

uin8_t is defined in stdint.h.
Don't we need it in the HOSTCC case?

Best regards 

Heinrich


>+#endif
> #include <linux/types.h>
> 
> enum dump_prefix_t {
>@@ -20,7 +24,7 @@ extern const char hex_asc[];
> #define hex_asc_lo(x)	hex_asc[((x) & 0x0f)]
> #define hex_asc_hi(x)	hex_asc[((x) & 0xf0) >> 4]
> 
>-static inline char *hex_byte_pack(char *buf, u8 byte)
>+static inline char *hex_byte_pack(char *buf, uint8_t byte)
> {
> 	*buf++ = hex_asc_hi(byte);
> 	*buf++ = hex_asc_lo(byte);
>@@ -52,7 +56,7 @@ static inline int hex_to_bin(char ch)
>  *
>  * Return 0 on success, -1 in case of bad input.
>  */
>-static inline int hex2bin(u8 *dst, const char *src, size_t count)
>+static inline int hex2bin(uint8_t *dst, const char *src, size_t count)
> {
> 	while (count--) {
> 		int hi = hex_to_bin(*src++);


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 7/7] test: py: add mkimage dm-verity round-trip test
  2026-04-16  1:47 ` [PATCH v2 7/7] test: py: add mkimage dm-verity round-trip test Daniel Golle
@ 2026-04-16 19:36   ` Simon Glass
  2026-04-16 22:58     ` Daniel Golle
  0 siblings, 1 reply; 19+ messages in thread
From: Simon Glass @ 2026-04-16 19:36 UTC (permalink / raw)
  To: daniel
  Cc: Tom Rini, Simon Glass, Quentin Schulz, Kory Maincent,
	Mattijs Korpershoek, Peng Fan, Heinrich Schuchardt, Martin Schwan,
	Anshul Dalal, Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
	Ludwig Nussel, Benjamin ROBIN, Marek Vasut, James Hilliard,
	Wolfgang Wallner, Kunihiko Hayashi, David Lechner, Neil Armstrong,
	Mayuresh Chitale, Jonas Karlman, Shiji Yang, Rasmus Villemoes,
	Francois Berder, u-boot

Hi Daniel,

On 2026-04-16T01:46:15, Daniel Golle <daniel@makrotopia.org> wrote:
> test: py: add mkimage dm-verity round-trip test
>
> Add test/py/tests/test_fit_verity.py with two tests.
>
> Both tests are skipped if veritysetup is not installed on the host.
>
> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
>
> test/py/tests/test_fit_verity.py | 153 +++++++++++++++++++++++++++++++++++++++
>  1 file changed, 153 insertions(+)

> diff --git a/test/py/tests/test_fit_verity.py b/test/py/tests/test_fit_verity.py
> @@ -0,0 +1,153 @@
> +import struct

The struct import seems to be unused.

> diff --git a/test/py/tests/test_fit_verity.py b/test/py/tests/test_fit_verity.py
> @@ -0,0 +1,153 @@
> +def have_veritysetup():
> +    return shutil.which('veritysetup') is not None
> +
> +
> +@pytest.mark.requiredtool('dtc')
> +@pytest.mark.requiredtool('fdtget')
> +@pytest.mark.skipif(not have_veritysetup(),
> +                    reason='veritysetup not installed')
> +def test_mkimage_verity(ubman):

The test infrastructure supports
@pytest.mark.requiredtool('veritysetup') which handles the skip
message consistently. That also means you can drop the
have_veritysetup() helper.

Regards,
Simon

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 4/7] tools: mkimage: add dm-verity Merkle-tree generation
  2026-04-16  1:46 ` [PATCH v2 4/7] tools: mkimage: add dm-verity Merkle-tree generation Daniel Golle
@ 2026-04-16 19:36   ` Simon Glass
  0 siblings, 0 replies; 19+ messages in thread
From: Simon Glass @ 2026-04-16 19:36 UTC (permalink / raw)
  To: daniel
  Cc: Tom Rini, Simon Glass, Quentin Schulz, Kory Maincent,
	Mattijs Korpershoek, Peng Fan, Heinrich Schuchardt, Martin Schwan,
	Anshul Dalal, Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
	Ludwig Nussel, Benjamin ROBIN, Marek Vasut, James Hilliard,
	Wolfgang Wallner, Kunihiko Hayashi, David Lechner, Neil Armstrong,
	Mayuresh Chitale, Jonas Karlman, Shiji Yang, Rasmus Villemoes,
	Francois Berder, u-boot

Hi Daniel,

On 2026-04-16T01:46:15, Daniel Golle <daniel@makrotopia.org> wrote:
> tools: mkimage: add dm-verity Merkle-tree generation
>
> When mkimage encounters a dm-verity subnode inside a component image
> node it now automatically invokes veritysetup(8) with --no-superblock
> to generate the Merkle hash tree, screen-scrapes the Root hash and Salt
> from the tool output, and writes the computed properties back into the
> FIT blob.
>
> The user only needs to specify algorithm, data-block-size, and
> hash-block-size in the ITS; mkimage fills in digest, salt,
> num-data-blocks, and hash-start-block.  Because --no-superblock is
> used, hash-start-block equals num-data-blocks with no off-by-one.
>
> The image data property is replaced with the expanded content (original
> data followed directly by the hash tree) so that subsequent hash and
> signature subnodes operate on the complete image.
>
> fit_image_add_verification_data() is restructured into two passes:
> dm-verity first (may grow data), then hashes and signatures.
>
> [...]

> diff --git a/tools/image-host.c b/tools/image-host.c
> @@ -626,6 +629,309 @@ int fit_image_cipher_data(const char *keydir, void *keydest,
> +     uint32_t hash_start_block;
> ...
> +     /* hash tree starts immediately after data (no superblock) */
> +     hash_start_block = hash_offset / hash_block_size;

The check for num_data_blocks overflowdoesn't protect against
hash_start_block overflow when hash-block-size < data-block-size. For
example, if data_block_size is 4096 and hash_block_size is 512,
hash_start_block can be 8x larger than num_data_blocks. Please can you
add a similar overflow check for hash_start_block?

Regards,
Simon

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 6/7] test: boot: add runtime unit test for fit_verity_build_cmdline()
  2026-04-16  1:47 ` [PATCH v2 6/7] test: boot: add runtime unit test for fit_verity_build_cmdline() Daniel Golle
@ 2026-04-16 19:36   ` Simon Glass
  2026-04-16 23:25     ` Daniel Golle
  0 siblings, 1 reply; 19+ messages in thread
From: Simon Glass @ 2026-04-16 19:36 UTC (permalink / raw)
  To: daniel
  Cc: Tom Rini, Simon Glass, Quentin Schulz, Kory Maincent,
	Mattijs Korpershoek, Peng Fan, Heinrich Schuchardt, Martin Schwan,
	Anshul Dalal, Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
	Ludwig Nussel, Benjamin ROBIN, Marek Vasut, James Hilliard,
	Wolfgang Wallner, Kunihiko Hayashi, David Lechner, Neil Armstrong,
	Mayuresh Chitale, Jonas Karlman, Shiji Yang, Rasmus Villemoes,
	Francois Berder, u-boot

Hi Daniel,

On 2026-04-16T01:46:15, Daniel Golle <daniel@makrotopia.org> wrote:
> test: boot: add runtime unit test for fit_verity_build_cmdline()
>
> Add test/boot/fit_verity.c with four tests that construct FIT blobs
> in memory and exercise fit_verity_build_cmdline().
>
> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
>
> test/boot/Makefile     |   1 +
>  test/boot/fit_verity.c | 302 +++++++++++++++++++++++++++++++++++++++++++++++++
>  test/cmd_ut.c          |   2 +
>  3 files changed, 305 insertions(+)

> diff --git a/test/boot/fit_verity.c b/test/boot/fit_verity.c
> @@ -0,0 +1,302 @@
> +             ret = fdt_setprop_string(buf, verity_node, "algo", "sha256");
> +             val = cpu_to_fdt32(4096);
> +             ret = fdt_setprop(buf, verity_node, "data-block-size",

Please can you use the FIT_VERITY_*_PROP constants from
include/image.h instead of literal strings? See test/image/spl_load.c
for an example.

Regards,
Simon

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 2/7] boot: fit: support generating DM verity cmdline parameters
  2026-04-16  1:46 ` [PATCH v2 2/7] boot: fit: support generating DM verity cmdline parameters Daniel Golle
@ 2026-04-16 19:37   ` Simon Glass
  0 siblings, 0 replies; 19+ messages in thread
From: Simon Glass @ 2026-04-16 19:37 UTC (permalink / raw)
  To: daniel
  Cc: Tom Rini, Simon Glass, Quentin Schulz, Kory Maincent,
	Mattijs Korpershoek, Peng Fan, Heinrich Schuchardt, Martin Schwan,
	Anshul Dalal, Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
	Ludwig Nussel, Benjamin ROBIN, Marek Vasut, James Hilliard,
	Wolfgang Wallner, Kunihiko Hayashi, David Lechner, Neil Armstrong,
	Mayuresh Chitale, Jonas Karlman, Shiji Yang, Rasmus Villemoes,
	Francois Berder, u-boot

Hi Daniel,

On 2026-04-16T01:46:15, Daniel Golle <daniel@makrotopia.org> wrote:
> boot: fit: support generating DM verity cmdline parameters
>
> Add fit_verity_build_cmdline(): when a FILESYSTEM loadable carries a
> dm-verity subnode, construct the dm-mod.create= kernel cmdline parameter
> from the verity metadata (block-size, data-blocks, algo, root-hash,
> salt) and append it to bootargs.
>
> Also add dm-mod.waitfor=/dev/fit0[,/dev/fitN] for each dm-verity device
> so the kernel waits for the underlying FIT block device to appear before
> setting up device-mapper targets. This is needed when the block driver
> probes late, e.g. because it depends on NVMEM calibration data.
>
> The dm-verity target references /dev/fitN where N is the loadable's
> index in the configuration -- matching the order Linux's FIT block
> driver assigns block devices.  hash-start-block is read directly from
> the FIT dm-verity node; mkimage ensures its value equals num-data-blocks
> by invoking veritysetup with --no-superblock.
>
> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
>
> boot/Kconfig       |  20 ++++
>  boot/bootm.c       |  13 +++
>  boot/image-board.c |   5 +
>  boot/image-fit.c   | 337 +++++++++++++++++++++++++++++++++++++++++++++++++++++
>  include/image.h    |  80 ++++++++++++-
>  5 files changed, 454 insertions(+), 1 deletion(-)

> diff --git a/boot/image-fit.c b/boot/image-fit.c
> @@ -2642,3 +2683,299 @@ out:
> +     /* Mandatory u32 properties */
> +     val = fdt_getprop(fit, verity_node, FIT_VERITY_DBS_PROP, NULL);
> +     if (!val)
> +             return -EINVAL;
> +     data_block_size = fdt32_to_cpu(*val);

This reads an fdt32_t into a signed int

data_block_size and hash_block_size are never negative, so could you
use uint instead?

> diff --git a/boot/image-fit.c b/boot/image-fit.c
> @@ -2642,3 +2683,299 @@ out:
> +             printf("FIT: broken dm-verity metadata in '%s'\n",
> +                    uname);

This file defines LOG_CATEGORY as LOGC_BOOT and includes log.h - how
about log_err() instead of printf() ?

Reviewed-by: Simon Glass <sjg@chromium.org>

Regards,
Simon

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 3/7] include: hexdump: make hex2bin() usable from host tools
  2026-04-16  1:46 ` [PATCH v2 3/7] include: hexdump: make hex2bin() usable from host tools Daniel Golle
  2026-04-16 11:00   ` Heinrich Schuchardt
@ 2026-04-16 19:37   ` Simon Glass
  1 sibling, 0 replies; 19+ messages in thread
From: Simon Glass @ 2026-04-16 19:37 UTC (permalink / raw)
  To: daniel
  Cc: Tom Rini, Simon Glass, Quentin Schulz, Kory Maincent,
	Mattijs Korpershoek, Peng Fan, Heinrich Schuchardt, Martin Schwan,
	Anshul Dalal, Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
	Ludwig Nussel, Benjamin ROBIN, Marek Vasut, James Hilliard,
	Wolfgang Wallner, Kunihiko Hayashi, David Lechner, Neil Armstrong,
	Mayuresh Chitale, Jonas Karlman, Shiji Yang, Rasmus Villemoes,
	Francois Berder, u-boot

Hi Daniel,

On 2026-04-16T01:46:15, Daniel Golle <daniel@makrotopia.org> wrote:
> include: hexdump: make hex2bin() usable from host tools
>
> Make hexdump.h work in host-tool builds by using 'uint8_t' instead
> of 'u8', and including either user-space libc <ctype.h> for host-tools
> or <linux/ctype.h> when building U-Boot itself.
>
> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
>
> include/hexdump.h | 8 ++++++--
>  1 file changed, 6 insertions(+), 2 deletions(-)

> diff --git a/include/hexdump.h b/include/hexdump.h
> @@ -7,7 +7,11 @@
> +#ifdef USE_HOSTCC
> +#include <ctype.h>
> +#else
>  #include <linux/ctype.h>
> +#endif

Reviewed-by: Simon Glass <sjg@chromium.org>

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 5/7] doc: fit: add dm-verity boot parameter documentation
  2026-04-16  1:46 ` [PATCH v2 5/7] doc: fit: add dm-verity boot parameter documentation Daniel Golle
@ 2026-04-16 19:38   ` Simon Glass
  0 siblings, 0 replies; 19+ messages in thread
From: Simon Glass @ 2026-04-16 19:38 UTC (permalink / raw)
  To: daniel
  Cc: Tom Rini, Simon Glass, Quentin Schulz, Kory Maincent,
	Mattijs Korpershoek, Peng Fan, Heinrich Schuchardt, Martin Schwan,
	Anshul Dalal, Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
	Ludwig Nussel, Benjamin ROBIN, Marek Vasut, James Hilliard,
	Wolfgang Wallner, Kunihiko Hayashi, David Lechner, Neil Armstrong,
	Mayuresh Chitale, Jonas Karlman, Shiji Yang, Rasmus Villemoes,
	Francois Berder, u-boot

Hi Daniel,

On 2026-04-16T01:46:15, Daniel Golle <daniel@makrotopia.org> wrote:
> doc: fit: add dm-verity boot parameter documentation
>
> Add documentation for CONFIG_FIT_VERITY which allows U-Boot to
> construct dm-mod.create= and dm-mod.waitfor= kernel command-line
> parameters from dm-verity metadata embedded in FIT filesystem
> sub-images.
>
> The new document covers the relationship between FIT loadable indices
> and the /dev/fitN block devices that the Linux uImage.FIT block driver
> creates, provides a complete .its example with a dm-verity-protected
> SquashFS root filesystem, describes all required and optional dm-verity
> subnode properties and explains how mkimage generates the verity
> metadata automatically.
>
> dm-verity is only supported for external-data FIT images (mkimage -E);
> mkimage aborts with an error if the flag is omitted.
>
> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
>
> doc/usage/fit/dm-verity.rst | 282 ++++++++++++++++++++++++++++++++++++++++++++
>  doc/usage/fit/index.rst     |   1 +
>  2 files changed, 283 insertions(+)

> diff --git a/doc/usage/fit/dm-verity.rst b/doc/usage/fit/dm-verity.rst
> @@ -0,0 +1,282 @@
> +.. 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. Consequently hash-start-block
> +   equals num-data-blocks`.

This is only true when data-block-size equals hash-block-size -- in
the general case hash-start-block = data_size / hash_block_size versus
num-data-blocks = data_size / data_block_size

So can you update the comment?

Reviewed-by: Simon Glass <sjg@chromium.org>

Regards,
Simon

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 7/7] test: py: add mkimage dm-verity round-trip test
  2026-04-16 19:36   ` Simon Glass
@ 2026-04-16 22:58     ` Daniel Golle
  2026-04-16 23:59       ` Simon Glass
  0 siblings, 1 reply; 19+ messages in thread
From: Daniel Golle @ 2026-04-16 22:58 UTC (permalink / raw)
  To: Simon Glass
  Cc: Tom Rini, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
	Peng Fan, Heinrich Schuchardt, Martin Schwan, Anshul Dalal,
	Ilias Apalodimas, Sughosh Ganu, Aristo Chen, Ludwig Nussel,
	Benjamin ROBIN, Marek Vasut, James Hilliard, Wolfgang Wallner,
	Kunihiko Hayashi, David Lechner, Neil Armstrong, Mayuresh Chitale,
	Jonas Karlman, Shiji Yang, Rasmus Villemoes, Francois Berder,
	u-boot

On Fri, Apr 17, 2026 at 07:36:17AM +1200, Simon Glass wrote:
> Hi Daniel,
> 
> On 2026-04-16T01:46:15, Daniel Golle <daniel@makrotopia.org> wrote:
> > test: py: add mkimage dm-verity round-trip test
> >
> > Add test/py/tests/test_fit_verity.py with two tests.
> >
> > Both tests are skipped if veritysetup is not installed on the host.
> >
> > Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> >
> > test/py/tests/test_fit_verity.py | 153 +++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 153 insertions(+)
> 
> > diff --git a/test/py/tests/test_fit_verity.py b/test/py/tests/test_fit_verity.py
> > @@ -0,0 +1,153 @@
> > +import struct
> 
> The struct import seems to be unused.
> 
> > diff --git a/test/py/tests/test_fit_verity.py b/test/py/tests/test_fit_verity.py
> > @@ -0,0 +1,153 @@
> > +def have_veritysetup():
> > +    return shutil.which('veritysetup') is not None
> > +
> > +
> > +@pytest.mark.requiredtool('dtc')
> > +@pytest.mark.requiredtool('fdtget')
> > +@pytest.mark.skipif(not have_veritysetup(),
> > +                    reason='veritysetup not installed')
> > +def test_mkimage_verity(ubman):
> 
> The test infrastructure supports
> @pytest.mark.requiredtool('veritysetup') which handles the skip
> message consistently. That also means you can drop the
> have_veritysetup() helper.

I have to admit that Python is not one of my strengths...
I'll try to figure it out and address this in v3.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 6/7] test: boot: add runtime unit test for fit_verity_build_cmdline()
  2026-04-16 19:36   ` Simon Glass
@ 2026-04-16 23:25     ` Daniel Golle
  2026-04-16 23:59       ` Simon Glass
  0 siblings, 1 reply; 19+ messages in thread
From: Daniel Golle @ 2026-04-16 23:25 UTC (permalink / raw)
  To: Simon Glass
  Cc: Tom Rini, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
	Peng Fan, Heinrich Schuchardt, Martin Schwan, Anshul Dalal,
	Ilias Apalodimas, Sughosh Ganu, Aristo Chen, Ludwig Nussel,
	Benjamin ROBIN, Marek Vasut, James Hilliard, Wolfgang Wallner,
	Kunihiko Hayashi, David Lechner, Neil Armstrong, Mayuresh Chitale,
	Jonas Karlman, Shiji Yang, Rasmus Villemoes, Francois Berder,
	u-boot

On Fri, Apr 17, 2026 at 07:36:37AM +1200, Simon Glass wrote:
> Hi Daniel,
> 
> On 2026-04-16T01:46:15, Daniel Golle <daniel@makrotopia.org> wrote:
> > test: boot: add runtime unit test for fit_verity_build_cmdline()
> >
> > Add test/boot/fit_verity.c with four tests that construct FIT blobs
> > in memory and exercise fit_verity_build_cmdline().
> >
> > Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> >
> > test/boot/Makefile     |   1 +
> >  test/boot/fit_verity.c | 302 +++++++++++++++++++++++++++++++++++++++++++++++++
> >  test/cmd_ut.c          |   2 +
> >  3 files changed, 305 insertions(+)
> 
> > diff --git a/test/boot/fit_verity.c b/test/boot/fit_verity.c
> > @@ -0,0 +1,302 @@
> > +             ret = fdt_setprop_string(buf, verity_node, "algo", "sha256");
> > +             val = cpu_to_fdt32(4096);
> > +             ret = fdt_setprop(buf, verity_node, "data-block-size",
> 
> Please can you use the FIT_VERITY_*_PROP constants from
> include/image.h instead of literal strings? See test/image/spl_load.c
> for an example.

Sure, I'll change that.

I was deliberately not doing it so the test code is as self-contained
as possible.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 6/7] test: boot: add runtime unit test for fit_verity_build_cmdline()
  2026-04-16 23:25     ` Daniel Golle
@ 2026-04-16 23:59       ` Simon Glass
  0 siblings, 0 replies; 19+ messages in thread
From: Simon Glass @ 2026-04-16 23:59 UTC (permalink / raw)
  To: Daniel Golle
  Cc: Tom Rini, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
	Peng Fan, Heinrich Schuchardt, Martin Schwan, Anshul Dalal,
	Ilias Apalodimas, Sughosh Ganu, Aristo Chen, Ludwig Nussel,
	Benjamin ROBIN, Marek Vasut, James Hilliard, Wolfgang Wallner,
	Kunihiko Hayashi, David Lechner, Neil Armstrong, Mayuresh Chitale,
	Jonas Karlman, Shiji Yang, Rasmus Villemoes, Francois Berder,
	u-boot

Hi Daniel,

On Fri, 17 Apr 2026 at 11:25, Daniel Golle <daniel@makrotopia.org> wrote:
>
> On Fri, Apr 17, 2026 at 07:36:37AM +1200, Simon Glass wrote:
> > Hi Daniel,
> >
> > On 2026-04-16T01:46:15, Daniel Golle <daniel@makrotopia.org> wrote:
> > > test: boot: add runtime unit test for fit_verity_build_cmdline()
> > >
> > > Add test/boot/fit_verity.c with four tests that construct FIT blobs
> > > in memory and exercise fit_verity_build_cmdline().
> > >
> > > Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> > >
> > > test/boot/Makefile     |   1 +
> > >  test/boot/fit_verity.c | 302 +++++++++++++++++++++++++++++++++++++++++++++++++
> > >  test/cmd_ut.c          |   2 +
> > >  3 files changed, 305 insertions(+)
> >
> > > diff --git a/test/boot/fit_verity.c b/test/boot/fit_verity.c
> > > @@ -0,0 +1,302 @@
> > > +             ret = fdt_setprop_string(buf, verity_node, "algo", "sha256");
> > > +             val = cpu_to_fdt32(4096);
> > > +             ret = fdt_setprop(buf, verity_node, "data-block-size",
> >
> > Please can you use the FIT_VERITY_*_PROP constants from
> > include/image.h instead of literal strings? See test/image/spl_load.c
> > for an example.
>
> Sure, I'll change that.
>
> I was deliberately not doing it so the test code is as self-contained
> as possible.

That is a good goal in general, yes, but I would hope that no one
would be silly enough to change the constant :-)

Regards,
Simon

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 7/7] test: py: add mkimage dm-verity round-trip test
  2026-04-16 22:58     ` Daniel Golle
@ 2026-04-16 23:59       ` Simon Glass
  0 siblings, 0 replies; 19+ messages in thread
From: Simon Glass @ 2026-04-16 23:59 UTC (permalink / raw)
  To: Daniel Golle
  Cc: Tom Rini, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
	Peng Fan, Heinrich Schuchardt, Martin Schwan, Anshul Dalal,
	Ilias Apalodimas, Sughosh Ganu, Aristo Chen, Ludwig Nussel,
	Benjamin ROBIN, Marek Vasut, James Hilliard, Wolfgang Wallner,
	Kunihiko Hayashi, David Lechner, Neil Armstrong, Mayuresh Chitale,
	Jonas Karlman, Shiji Yang, Rasmus Villemoes, Francois Berder,
	u-boot

Hi Daniel,

On Fri, 17 Apr 2026 at 10:58, Daniel Golle <daniel@makrotopia.org> wrote:
>
> On Fri, Apr 17, 2026 at 07:36:17AM +1200, Simon Glass wrote:
> > Hi Daniel,
> >
> > On 2026-04-16T01:46:15, Daniel Golle <daniel@makrotopia.org> wrote:
> > > test: py: add mkimage dm-verity round-trip test
> > >
> > > Add test/py/tests/test_fit_verity.py with two tests.
> > >
> > > Both tests are skipped if veritysetup is not installed on the host.
> > >
> > > Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> > >
> > > test/py/tests/test_fit_verity.py | 153 +++++++++++++++++++++++++++++++++++++++
> > >  1 file changed, 153 insertions(+)
> >
> > > diff --git a/test/py/tests/test_fit_verity.py b/test/py/tests/test_fit_verity.py
> > > @@ -0,0 +1,153 @@
> > > +import struct
> >
> > The struct import seems to be unused.
> >
> > > diff --git a/test/py/tests/test_fit_verity.py b/test/py/tests/test_fit_verity.py
> > > @@ -0,0 +1,153 @@
> > > +def have_veritysetup():
> > > +    return shutil.which('veritysetup') is not None
> > > +
> > > +
> > > +@pytest.mark.requiredtool('dtc')
> > > +@pytest.mark.requiredtool('fdtget')
> > > +@pytest.mark.skipif(not have_veritysetup(),
> > > +                    reason='veritysetup not installed')
> > > +def test_mkimage_verity(ubman):
> >
> > The test infrastructure supports
> > @pytest.mark.requiredtool('veritysetup') which handles the skip
> > message consistently. That also means you can drop the
> > have_veritysetup() helper.
>
> I have to admit that Python is not one of my strengths...
> I'll try to figure it out and address this in v3.

Me neither, despite having written quite a bit of it. Let me know if
you need help with any of it.

Regards,
Simon

^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2026-04-17  0:00 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-16  1:46 [PATCH v2 0/7] fit: dm-verity support Daniel Golle
2026-04-16  1:46 ` [PATCH v2 1/7] image: fit: add dm-verity property name constants Daniel Golle
2026-04-16  1:46 ` [PATCH v2 2/7] boot: fit: support generating DM verity cmdline parameters Daniel Golle
2026-04-16 19:37   ` Simon Glass
2026-04-16  1:46 ` [PATCH v2 3/7] include: hexdump: make hex2bin() usable from host tools Daniel Golle
2026-04-16 11:00   ` Heinrich Schuchardt
2026-04-16 19:37   ` Simon Glass
2026-04-16  1:46 ` [PATCH v2 4/7] tools: mkimage: add dm-verity Merkle-tree generation Daniel Golle
2026-04-16 19:36   ` Simon Glass
2026-04-16  1:46 ` [PATCH v2 5/7] doc: fit: add dm-verity boot parameter documentation Daniel Golle
2026-04-16 19:38   ` Simon Glass
2026-04-16  1:47 ` [PATCH v2 6/7] test: boot: add runtime unit test for fit_verity_build_cmdline() Daniel Golle
2026-04-16 19:36   ` Simon Glass
2026-04-16 23:25     ` Daniel Golle
2026-04-16 23:59       ` Simon Glass
2026-04-16  1:47 ` [PATCH v2 7/7] test: py: add mkimage dm-verity round-trip test Daniel Golle
2026-04-16 19:36   ` Simon Glass
2026-04-16 22:58     ` Daniel Golle
2026-04-16 23:59       ` Simon Glass

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox