Hi Daniel,

On 2026-08-23T19:12:43, Daniel Golle <[email protected]> wrote:
> boot: fit: support on-demand loading in fit_image_load()
>
> 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()
> [...]
>
> 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
> @@ -147,7 +148,22 @@ static int boot_get_kernel(const char *addr_fit, struct 
> bootm_headers *images,
> +             buf = imagemap_map(images->imagemap, 0, 64);

Where does 64 come from? A short comment or named constant would help
- genimg_get_format() has to distinguish a legacy header, a FIT FDT
magic and possibly an Android header, each with different minimum
sizes, so an unexplained 64 looks like a coincidence.

> diff --git a/boot/bootm.c b/boot/bootm.c
> @@ -1033,11 +1063,21 @@ int bootm_run_states(struct bootm_info *bmi, int 
> states)
> +     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;
> +     }

Restoring a field immediately after bootm_start() zeroes it is fragile
- any future state that also calls bootm_start() (BOOTM_STATE_RESTART
already runs bootm_restart(), similar in spirit) will silently drop
the pointer again. Please move this into bootm_start() itself, or
teach bootm_start() to preserve imagemap across the reset, so the
invariant is expressed in one place.

> diff --git a/boot/bootm.c b/boot/bootm.c
> @@ -1145,11 +1188,24 @@ int bootm_run_states(struct bootm_info *bmi, int 
> states)
> +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;
> +     }

Several paths in bootm_run_states() return directly rather than goto
err - 'if (images->no_os) return -ENOPKG;', 'if (ret) return ret;'
after the OS-func lookup, and the 'ERROR: booting os …' return 1 all
bypass this cleanup, so imagemap leaks on those exits. Please convert
those to goto err, or drive the cleanup from a single place that runs
on every exit.

> diff --git a/boot/image-fit.c b/boot/image-fit.c
> @@ -1140,6 +1141,16 @@ int fit_image_get_data(const void *fit, int noffset, 
> const void **data,
> +#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

Reaching into the global images from a low-level helper couples
fit_image_get_data() to bootm state - until now this function has been
usable purely from (fit, noffset). Please pass the imagemap in
explicitly (as a parameter or via a small wrapper), which also keeps
'ut fit' style tests free of hidden global dependencies.

> diff --git a/boot/image-fit.c b/boot/image-fit.c
> @@ -2140,6 +2151,84 @@ static const char *fit_get_image_type_property(int 
> ph_type)
> +     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) {

Please use FIT_VERITY_NODENAME from image.h rather than a literal
dm-verity, so there is one place to change the name.

> diff --git a/boot/image-fit.c b/boot/image-fit.c
> @@ -2140,6 +2151,84 @@ static const char *fit_get_image_type_property(int 
> ph_type)
> +     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;

This re-implements the data-position / data-offset / totalsize-align
dance that fit_image_get_data() already does a few hundred lines up,
and uses a slightly different expression (ALIGN(fdt_totalsize(fit), 4)
vs ((fdt_totalsize(fit) + 3) & ~3)). Please extract a shared helper -
otherwise a future fix in one path (e.g. the INT_MAX bounds check that
fit_image_get_data() has) will silently miss this one.

> diff --git a/boot/image-fit.c b/boot/image-fit.c
> @@ -2140,6 +2151,84 @@ static const char *fit_get_image_type_property(int 
> ph_type)
> +     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);

map_sysmem() should really be paired with unmap_sysmem() - a no-op on
most arches but not on sandbox, which is where the tests run.

> diff --git a/boot/image-fit.c b/boot/image-fit.c
> @@ -2349,8 +2453,9 @@ int fit_image_load(struct bootm_headers *images, ulong 
> addr,
> -             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);

This is a build-time gate, not a runtime one - any board with
CONFIG_IMAGEMAP=y silently loses the 'Loading … from … to …' message
for RAM-backed FITs whenever data happens to equal load, a
user-visible regression for boards that don't use imagemap at boot.
Please key the suppression on 'images->imagemap && data == load' so
the message only disappears on the genuine zero-copy path.

> diff --git a/include/image.h b/include/image.h
> @@ -412,6 +412,8 @@ struct bootm_headers {
> +     struct udevice *imagemap;       /* on-demand storage loader, or NULL */

It might be worth making this (and the equivalent field in struct
bootm_info) conditional on CONFIG_IMAGEMAP, in line with the
surrounding fields - otherwise every board pays the pointer with the
feature off.

Regards,
Simon

Reply via email to