[PATCH 6/9] boot: fit: support on-demand loading in fit_image_load()

Daniel Golle daniel at makrotopia.org
Sun Aug 23 12:14:10 PDT 2026


Integrate storage-backed image loading transparently into the existing
FIT flow so that all verification, decompression, and copy-to-load-
address code runs unchanged for both RAM-backed and storage-backed
images.

Hook imagemap into fit_image_get_data(): when gd->imagemap is set and
the sub-image uses external data, call imagemap_map() to bring the
payload into RAM instead of returning the unmapped fit + offset
pointer. Every existing caller, including fit_image_verify() called
from fit_image_select(), transparently gets valid data without any
changes to the authentication path.

For uncompressed sub-images with a known load address, pre-populate
the translation table via imagemap_map_to() before verification runs.
This loads data directly to the final RAM destination; when
fit_image_get_data() is later called, the translation table hit
returns the same pointer, achieving zero-copy. Compressed images or
those without a load address fall through to the lazy imagemap_map()
path.

Filesystem sub-images protected by dm-verity are returned early and
stay on storage, as the kernel verifies them at block level.

Signed-off-by: Daniel Golle <daniel at makrotopia.org>
---
 boot/bootm.c     |  70 +++++++++++++++++++++++++++---
 boot/image-fit.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++-
 include/bootm.h  |   2 +
 include/image.h  |   2 +
 4 files changed, 174 insertions(+), 9 deletions(-)

diff --git a/boot/bootm.c b/boot/bootm.c
index 3bce8586834..6422bdc16f6 100644
--- a/boot/bootm.c
+++ b/boot/bootm.c
@@ -14,6 +14,7 @@
 #include <env.h>
 #include <errno.h>
 #include <fdt_support.h>
+#include <imagemap.h>
 #include <irq_func.h>
 #include <lmb.h>
 #include <log.h>
@@ -147,7 +148,22 @@ static int boot_get_kernel(const char *addr_fit, struct bootm_headers *images,
 
 	/* check image type, for FIT images get FIT kernel node */
 	*os_data = *os_len = 0;
-	buf = map_sysmem(img_addr, 0);
+	if (IS_ENABLED(CONFIG_IMAGEMAP) && images->imagemap) {
+		/*
+		 * Storage path: read enough bytes to detect the image
+		 * format. genimg_get_kernel_addr_fit() above still
+		 * parsed any #config / :subimage suffix so the FIT
+		 * selection variables are populated.
+		 */
+		buf = imagemap_map(images->imagemap, 0, 64);
+		if (IS_ERR(buf)) {
+			puts("Cannot read image header from storage\n");
+			return PTR_ERR(buf);
+		}
+		img_addr = map_to_sysmem(buf);
+	} else {
+		buf = map_sysmem(img_addr, 0);
+	}
 	switch (genimg_get_format(buf)) {
 #if CONFIG_IS_ENABLED(LEGACY_IMAGE_FORMAT)
 	case IMAGE_FORMAT_LEGACY:
@@ -193,6 +209,20 @@ static int boot_get_kernel(const char *addr_fit, struct bootm_headers *images,
 #endif
 #if CONFIG_IS_ENABLED(FIT)
 	case IMAGE_FORMAT_FIT:
+		if (IS_ENABLED(CONFIG_IMAGEMAP) && images->imagemap) {
+			/*
+			 * Extend the mapping to cover the full FIT
+			 * FDT structure so all metadata is accessible.
+			 */
+			size_t fdt_sz = fdt_totalsize(buf);
+
+			buf = imagemap_map(images->imagemap, 0, fdt_sz);
+			if (IS_ERR(buf)) {
+				puts("Cannot read FIT header from storage\n");
+				return PTR_ERR(buf);
+			}
+			img_addr = map_to_sysmem(buf);
+		}
 		os_noffset = fit_image_load(images, img_addr,
 				&fit_uname_kernel, &fit_uname_config,
 				IH_ARCH_DEFAULT, IH_TYPE_KERNEL,
@@ -1033,11 +1063,21 @@ int bootm_run_states(struct bootm_info *bmi, int states)
 	 * Work through the states and see how far we get. We stop on
 	 * any error.
 	 */
-	if (states & BOOTM_STATE_START)
+	if (states & BOOTM_STATE_START) {
 		ret = bootm_start();
+		/*
+		 * bootm_start() zeroes the global images struct. Restore
+		 * the loader pointer so the storage-backed path works.
+		 */
+		if (IS_ENABLED(CONFIG_IMAGEMAP) && bmi->imagemap)
+			images->imagemap = bmi->imagemap;
+	}
 
-	if (!ret && (states & BOOTM_STATE_PRE_LOAD))
-		ret = bootm_pre_load(bmi->addr_img);
+	if (!ret && (states & BOOTM_STATE_PRE_LOAD)) {
+		/* Pre-load verification is not applicable to storage boot */
+		if (!IS_ENABLED(CONFIG_IMAGEMAP) || !images->imagemap)
+			ret = bootm_pre_load(bmi->addr_img);
+	}
 
 	if (!ret && (states & BOOTM_STATE_FINDOS))
 		ret = bootm_find_os(bmi->cmd_name, bmi->addr_img);
@@ -1045,8 +1085,11 @@ int bootm_run_states(struct bootm_info *bmi, int states)
 	if (!ret && (states & BOOTM_STATE_FINDOTHER)) {
 		ulong img_addr;
 
-		img_addr = bmi->addr_img ? hextoul(bmi->addr_img, NULL)
-			: image_load_addr;
+		if (IS_ENABLED(CONFIG_IMAGEMAP) && images->imagemap)
+			img_addr = images->os.start;
+		else
+			img_addr = bmi->addr_img ? hextoul(bmi->addr_img, NULL)
+				: image_load_addr;
 		ret = bootm_find_other(img_addr, bmi->conf_ramdisk,
 				       bmi->conf_fdt);
 	}
@@ -1145,11 +1188,24 @@ int bootm_run_states(struct bootm_info *bmi, int states)
 	}
 
 	/* Now run the OS! We hope this doesn't return */
-	if (!ret && (states & BOOTM_STATE_OS_GO))
+	if (!ret && (states & BOOTM_STATE_OS_GO)) {
+		/* Release storage backend before jumping — no return expected */
+		if (IS_ENABLED(CONFIG_IMAGEMAP) && images->imagemap) {
+			imagemap_cleanup(images->imagemap);
+			images->imagemap = NULL;
+		}
+
 		ret = boot_selected_os(BOOTM_STATE_OS_GO, bmi, boot_fn);
+	}
 
 	/* Deal with any fallout */
 err:
+	/* Clean up imagemap on error (not reached on successful boot) */
+	if (IS_ENABLED(CONFIG_IMAGEMAP) && images->imagemap) {
+		imagemap_cleanup(images->imagemap);
+		images->imagemap = NULL;
+	}
+
 	if (iflag)
 		enable_interrupts();
 
diff --git a/boot/image-fit.c b/boot/image-fit.c
index ef90c5abd18..9b05273d14a 100644
--- a/boot/image-fit.c
+++ b/boot/image-fit.c
@@ -26,6 +26,7 @@ extern void *aligned_alloc(size_t alignment, size_t size);
 #include <env.h>
 #include <errno.h>
 #include <hexdump.h>
+#include <imagemap.h>
 #include <log.h>
 #include <mapmem.h>
 #include <asm/io.h>
@@ -1140,6 +1141,16 @@ int fit_image_get_data(const void *fit, int noffset, const void **data,
 				return -EINVAL;
 			}
 			*data = fit + offset;
+#if !defined(USE_HOSTCC) && CONFIG_IS_ENABLED(IMAGEMAP)
+			if (images.imagemap) {
+				void *mapped;
+
+				mapped = imagemap_lookup(images.imagemap,
+							    offset, len);
+				if (mapped)
+					*data = mapped;
+			}
+#endif
 			*size = len;
 		}
 	} else {
@@ -2140,6 +2151,84 @@ static const char *fit_get_image_type_property(int ph_type)
 	return "unknown";
 }
 
+#if !defined(USE_HOSTCC) && CONFIG_IS_ENABLED(IMAGEMAP)
+/**
+ * fit_image_load_storage() - Pre-load a sub-image from on-demand storage
+ *
+ * Brings an external-data sub-image into RAM through the imagemap loader before
+ * fit_image_select() runs verification, so the existing verify/copy path sees a
+ * valid RAM pointer.  Uncompressed sub-images with a load address are read
+ * straight to their destination (zero-copy); everything else goes to scratch
+ * RAM.  Filesystem sub-images protected by dm-verity are left on storage and
+ * verified by the kernel at block level; @early is set for those so the caller
+ * returns the node without loading the payload.
+ *
+ * @early is set to true when the caller should stop and return @noffset with an
+ * empty payload.
+ *
+ * Return: 0 to continue, negative errno on failure
+ */
+static int fit_image_load_storage(struct bootm_headers *images, const void *fit,
+				  int noffset, enum fit_load_op load_op,
+				  ulong *datap, ulong *lenp, bool *early)
+{
+	int data_off = 0, data_sz = 0;
+	bool external = false;
+	ulong img_load;
+	u8 img_comp = IH_COMP_NONE;
+	void *mapped;
+
+	if (CONFIG_IS_ENABLED(FIT_VERITY)) {
+		u8 img_type;
+
+		if (!fit_image_get_type(fit, noffset, &img_type) &&
+		    img_type == IH_TYPE_FILESYSTEM &&
+		    fdt_subnode_offset(fit, noffset, "dm-verity") >= 0) {
+			fit_image_print(fit, noffset, "   ");
+			*datap = 0;
+			*lenp = 0;
+			*early = true;
+			return 0;
+		}
+	}
+
+	if (!fit_image_get_data_position(fit, noffset, &data_off)) {
+		external = true;
+	} else if (!fit_image_get_data_offset(fit, noffset, &data_off)) {
+		external = true;
+		data_off += ALIGN(fdt_totalsize(fit), 4);
+	}
+
+	if (!external || fit_image_get_data_size(fit, noffset, &data_sz))
+		return 0;
+
+	if (data_off < 0 || data_sz < 0)
+		return -EINVAL;
+
+	fit_image_get_comp(fit, noffset, &img_comp);
+
+	if (img_comp == IH_COMP_NONE && load_op != FIT_LOAD_IGNORED &&
+	    !fit_image_get_load(fit, noffset, &img_load)) {
+		void *dst = map_sysmem(img_load, data_sz);
+
+		mapped = imagemap_map_to(images->imagemap, data_off, data_sz,
+					 dst);
+	} else {
+		mapped = imagemap_map(images->imagemap, data_off, data_sz);
+	}
+
+	return IS_ERR(mapped) ? PTR_ERR(mapped) : 0;
+}
+#else
+static inline int fit_image_load_storage(struct bootm_headers *images,
+					 const void *fit, int noffset,
+					 enum fit_load_op load_op, ulong *datap,
+					 ulong *lenp, bool *early)
+{
+	return 0;
+}
+#endif
+
 int fit_image_load(struct bootm_headers *images, ulong addr,
 		   const char **fit_unamep, const char **fit_uname_configp,
 		   int arch, int ph_type, int bootstage_id,
@@ -2236,6 +2325,21 @@ int fit_image_load(struct bootm_headers *images, ulong addr,
 
 	printf("   Trying '%s' %s subimage\n", fit_uname, prop_name);
 
+	/*
+	 * On-demand storage: pre-load external-data payloads into RAM (or leave
+	 * dm-verity filesystems on storage) before fit_image_select() verifies.
+	 */
+	if (CONFIG_IS_ENABLED(IMAGEMAP) && !tools_build() && images->imagemap) {
+		bool early = false;
+
+		ret = fit_image_load_storage(images, fit, noffset, load_op,
+					     datap, lenp, &early);
+		if (ret)
+			return ret;
+		if (early)
+			return noffset;
+	}
+
 	ret = fit_image_select(fit, noffset, images->verify);
 	if (ret) {
 		bootstage_error(bootstage_id + BOOTSTAGE_SUB_HASH);
@@ -2349,8 +2453,9 @@ int fit_image_load(struct bootm_headers *images, ulong addr,
 			return -EXDEV;
 		}
 
-		printf("   Loading %s from 0x%08lx to 0x%08lx\n",
-		       prop_name, data, load);
+		if (!CONFIG_IS_ENABLED(IMAGEMAP) || data != load)
+			printf("   Loading %s from 0x%08lx to 0x%08lx\n",
+			       prop_name, data, load);
 	} else {
 		load = data;	/* load address specified but set to 0 */
 	}
diff --git a/include/bootm.h b/include/bootm.h
index f6958be751a..d1aac3d44df 100644
--- a/include/bootm.h
+++ b/include/bootm.h
@@ -40,6 +40,7 @@ struct cmd_tbl;
  *	boot_get_fdt() for processing, or NULL for none
  * @boot_progress: true to show boot progress
  * @images: images information
+ * @imagemap: imagemap device for storage-backed boot (NULL for in-memory)
  * @cmd_name: command which invoked this operation, e.g. "bootm"
  * @argc: Number of arguments to the command (excluding the actual command).
  *	This is 0 if there are no arguments
@@ -51,6 +52,7 @@ struct bootm_info {
 	const char *conf_fdt;
 	bool boot_progress;
 	struct bootm_headers *images;
+	struct udevice *imagemap;
 	const char *cmd_name;
 	int argc;
 	char *const *argv;
diff --git a/include/image.h b/include/image.h
index 6edcb1995bf..f5baec9a516 100644
--- a/include/image.h
+++ b/include/image.h
@@ -412,6 +412,8 @@ struct bootm_headers {
 
 	int		verify;		/* env_get("verify")[0] != 'n' */
 
+	struct udevice *imagemap;	/* on-demand storage loader, or NULL */
+
 #define BOOTM_STATE_START	0x00000001
 #define BOOTM_STATE_FINDOS	0x00000002
 #define BOOTM_STATE_FINDOTHER	0x00000004
-- 
2.55.0



More information about the openwrt-devel mailing list