[PATCH v2 6/7] test: boot: add runtime unit test for fit_verity_build_cmdline()

Daniel Golle daniel at makrotopia.org
Thu Apr 16 03:47:03 CEST 2026


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 at 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 at 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


More information about the U-Boot mailing list