* [RFC PATCH 01/20] boot: add image_loader on-demand loading abstraction
2026-02-16 21:21 [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading Daniel Golle
@ 2026-02-16 21:21 ` Daniel Golle
2026-02-19 13:09 ` Simon Glass
2026-02-16 21:21 ` [RFC PATCH 02/20] boot: image-loader: add block device backend Daniel Golle
` (24 subsequent siblings)
25 siblings, 1 reply; 88+ messages in thread
From: Daniel Golle @ 2026-02-16 21:21 UTC (permalink / raw)
To: Tom Rini, Simon Glass, Quentin Schulz, Daniel Golle,
Kory Maincent, Mattijs Korpershoek, Martin Schwan, Anshul Dalal,
Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
牛 志宏, Marek Vasut, Heinrich Schuchardt,
Wolfgang Wallner, Frank Wunderlich, David Lechner,
Osama Abdelkader, Mikhail Kshevetskiy, Michael Trimarchi,
Miquel Raynal, Andrew Goodbody, Yegor Yefremov, Mike Looijmans,
Weijie Gao, Alexander Stein, Neil Armstrong, Mayuresh Chitale,
Paul HENRYS, u-boot
Cc: John Crispin, Paul Spooren
Introduce struct image_loader, a small generic layer that lets callers
read arbitrary byte ranges from a storage device and keeps a translation
table of the regions that have already been loaded into RAM.
The API consists of four functions:
image_loader_lookup() - check whether a range is already mapped
image_loader_map() - return a mapped pointer, reading on demand
image_loader_map_to() - read into a caller-supplied address
image_loader_cleanup() - release all backend resources
A read-callback (image_loader_read_fn) plus an opaque *priv pointer
abstract the actual I/O so that block, MTD, and UBI back-ends can be
added in follow-up patches without touching the core.
Each backend provides a .cleanup callback which is invoked by
image_loader_cleanup() to release held device references and free
allocated memory. This ensures safe resource teardown between
consecutive boot attempts.
The translation table (struct image_loader_region[]) avoids redundant
reads when the same region is requested more than once, and allows
extending an existing mapping when a larger size is needed at the same
offset. The table size is controlled by CONFIG_IMAGE_LOADER_MAX_REGIONS
(default 16).
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
boot/Kconfig | 20 +++++
boot/Makefile | 2 +
boot/image-loader.c | 163 +++++++++++++++++++++++++++++++++++++++++
include/image-loader.h | 141 +++++++++++++++++++++++++++++++++++
4 files changed, 326 insertions(+)
create mode 100644 boot/image-loader.c
create mode 100644 include/image-loader.h
diff --git a/boot/Kconfig b/boot/Kconfig
index e5db165424a..f6908e04a51 100644
--- a/boot/Kconfig
+++ b/boot/Kconfig
@@ -1167,6 +1167,26 @@ config SYS_BOOT_RAMDISK_HIGH
endmenu # Boot images
+config IMAGE_LOADER
+ bool "On-demand image loading from storage"
+ help
+ Provides a generic abstraction for reading image data from
+ storage on demand. A translation table maps already-loaded
+ regions to their RAM addresses, avoiding redundant reads.
+
+ Used by bootm when a storage device is specified instead of a
+ RAM address.
+
+config IMAGE_LOADER_MAX_REGIONS
+ int "Maximum number of mapped regions in image loader"
+ default 16 if IMAGE_LOADER
+ default 0
+ help
+ Maximum number of distinct image regions that can be mapped
+ into RAM simultaneously. 16 is sufficient for typical FIT
+ images (FDT structure + kernel + device tree + ramdisk +
+ a few loadable sub-images).
+
config DISTRO_DEFAULTS
bool "(deprecated) Script-based booting of Linux distributions"
select CMDLINE
diff --git a/boot/Makefile b/boot/Makefile
index 7fb56e7ef37..1dbc285dad8 100644
--- a/boot/Makefile
+++ b/boot/Makefile
@@ -73,6 +73,8 @@ obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_SIMPLE_OS) += vbe_simple_os.o
obj-$(CONFIG_$(PHASE_)BOOTMETH_ANDROID) += bootmeth_android.o
+obj-$(CONFIG_IMAGE_LOADER) += image-loader.o
+
obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_ABREC) += vbe_abrec.o vbe_common.o
obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_ABREC_FW) += vbe_abrec_fw.o
obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_ABREC_OS) += vbe_abrec_os.o
diff --git a/boot/image-loader.c b/boot/image-loader.c
new file mode 100644
index 00000000000..77f5b8c69a1
--- /dev/null
+++ b/boot/image-loader.c
@@ -0,0 +1,163 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * On-demand image loading from storage
+ *
+ * Copyright (C) 2026 Daniel Golle <daniel@makrotopia.org>
+ */
+
+#include <image-loader.h>
+#include <mapmem.h>
+#include <asm/cache.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <log.h>
+
+void *image_loader_lookup(struct image_loader *ldr, ulong img_offset,
+ ulong size)
+{
+ int i;
+
+ for (i = 0; i < ldr->nr_regions; i++) {
+ struct image_loader_region *r = &ldr->regions[i];
+
+ if (img_offset >= r->img_offset &&
+ img_offset + size <= r->img_offset + r->size)
+ return (char *)r->ram + (img_offset - r->img_offset);
+ }
+
+ return NULL;
+}
+
+void image_loader_cleanup(struct image_loader *ldr)
+{
+ if (ldr->cleanup)
+ ldr->cleanup(ldr);
+
+ ldr->read = NULL;
+ ldr->cleanup = NULL;
+ ldr->priv = NULL;
+ ldr->nr_regions = 0;
+}
+
+/**
+ * image_loader_record() - Record a region in the translation table
+ *
+ * If an entry with the same img_offset already exists and the new size
+ * is larger, update the existing entry. Otherwise add a new entry.
+ *
+ * @ldr: The image loader
+ * @img_offset: Byte offset within the source image
+ * @size: Region size
+ * @ram: RAM pointer where the region was loaded
+ * Return: pointer to the region entry, or NULL if the table is full
+ */
+static struct image_loader_region *
+image_loader_record(struct image_loader *ldr, ulong img_offset, ulong size,
+ void *ram)
+{
+ struct image_loader_region *r;
+ int i;
+
+ /* Check for an existing entry at the same base that we can extend */
+ for (i = 0; i < ldr->nr_regions; i++) {
+ r = &ldr->regions[i];
+ if (r->img_offset == img_offset) {
+ r->size = size;
+ r->ram = ram;
+ return r;
+ }
+ }
+
+ if (ldr->nr_regions >= CONFIG_IMAGE_LOADER_MAX_REGIONS) {
+ log_err("image_loader: translation table full (%d regions)\n",
+ ldr->nr_regions);
+ return NULL;
+ }
+
+ r = &ldr->regions[ldr->nr_regions++];
+ r->img_offset = img_offset;
+ r->size = size;
+ r->ram = ram;
+
+ return r;
+}
+
+void *image_loader_map(struct image_loader *ldr, ulong img_offset, ulong size)
+{
+ struct image_loader_region *r;
+ void *p;
+ int ret;
+
+ /* Return existing mapping if the range is already covered */
+ p = image_loader_lookup(ldr, img_offset, size);
+ if (p)
+ return p;
+
+ /*
+ * Check if we have an entry at the same base offset but smaller.
+ * If so, re-read the full range to the same RAM address.
+ */
+ for (int i = 0; i < ldr->nr_regions; i++) {
+ r = &ldr->regions[i];
+ if (r->img_offset == img_offset && r->size < size) {
+ ulong region_end;
+
+ ret = ldr->read(ldr, img_offset, size, r->ram);
+ if (ret) {
+ log_err("image_loader: read failed at offset 0x%lx (size 0x%lx): %d\n",
+ img_offset, size, ret);
+ return NULL;
+ }
+ r->size = size;
+
+ /* Keep alloc_ptr past the extended region */
+ region_end = ALIGN(map_to_sysmem(r->ram) + size,
+ ARCH_DMA_MINALIGN);
+ if (region_end > ldr->alloc_ptr)
+ ldr->alloc_ptr = region_end;
+
+ return r->ram;
+ }
+ }
+
+ /* New region — allocate from scratch area */
+ p = map_sysmem(ldr->alloc_ptr, size);
+
+ ret = ldr->read(ldr, img_offset, size, p);
+ if (ret) {
+ log_err("image_loader: read failed at offset 0x%lx (size 0x%lx): %d\n",
+ img_offset, size, ret);
+ return NULL;
+ }
+
+ if (!image_loader_record(ldr, img_offset, size, p))
+ return NULL;
+
+ ldr->alloc_ptr = ALIGN(ldr->alloc_ptr + size, ARCH_DMA_MINALIGN);
+
+ return p;
+}
+
+void *image_loader_map_to(struct image_loader *ldr, ulong img_offset,
+ ulong size, void *dst)
+{
+ int ret;
+
+ /* If already mapped to this exact destination, return it */
+ void *p = image_loader_lookup(ldr, img_offset, size);
+
+ if (p && p == dst)
+ return p;
+
+ ret = ldr->read(ldr, img_offset, size, dst);
+ if (ret) {
+ log_err("image_loader: read failed at offset 0x%lx (size 0x%lx): %d\n",
+ img_offset, size, ret);
+ return NULL;
+ }
+
+ if (!image_loader_record(ldr, img_offset, size, dst))
+ return NULL;
+
+ return dst;
+}
diff --git a/include/image-loader.h b/include/image-loader.h
new file mode 100644
index 00000000000..e273b1ca50f
--- /dev/null
+++ b/include/image-loader.h
@@ -0,0 +1,141 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * On-demand image loading from storage
+ *
+ * Copyright (C) 2026 Daniel Golle <daniel@makrotopia.org>
+ */
+
+#ifndef __IMAGE_LOADER_H
+#define __IMAGE_LOADER_H
+
+#include <linux/types.h>
+
+/**
+ * struct image_loader_region - One mapped region of the image
+ *
+ * Records the fact that image bytes [img_offset, img_offset + size)
+ * have been loaded into RAM at address @ram.
+ *
+ * @img_offset: Start offset within the source image (bytes)
+ * @size: Region size (bytes)
+ * @ram: RAM pointer where this region was loaded
+ */
+struct image_loader_region {
+ ulong img_offset;
+ ulong size;
+ void *ram;
+};
+
+struct image_loader;
+
+/**
+ * image_loader_read_fn - Read data from a storage device
+ *
+ * @ldr: The image loader instance
+ * @src: Byte offset within the source image
+ * @size: Number of bytes to read
+ * @dst: Destination buffer in RAM
+ * Return: 0 on success, negative errno on failure
+ */
+typedef int (*image_loader_read_fn)(struct image_loader *ldr, ulong src,
+ ulong size, void *dst);
+
+/**
+ * image_loader_cleanup_fn - Release backend resources
+ *
+ * Called by image_loader_cleanup() to free any backend-specific state
+ * such as allocated priv structs or held device references.
+ *
+ * @ldr: The image loader instance
+ */
+typedef void (*image_loader_cleanup_fn)(struct image_loader *ldr);
+
+/**
+ * struct image_loader - On-demand image loading from storage
+ *
+ * Provides a generic abstraction for reading image data from a storage
+ * device on demand. A translation table maps regions of the source
+ * image that have already been loaded to their RAM addresses, avoiding
+ * redundant reads.
+ *
+ * @read: Backend read callback
+ * @cleanup: Optional backend cleanup callback
+ * @priv: Opaque backend-specific context
+ * @regions: Translation table of loaded regions
+ * @nr_regions: Number of entries currently used in @regions
+ * @alloc_ptr: Next free RAM address for scratch allocations
+ */
+struct image_loader {
+ image_loader_read_fn read;
+ image_loader_cleanup_fn cleanup;
+ void *priv;
+ struct image_loader_region regions[CONFIG_IMAGE_LOADER_MAX_REGIONS];
+ int nr_regions;
+ ulong alloc_ptr;
+};
+
+/**
+ * image_loader_lookup() - Look up an already-mapped region
+ *
+ * Checks the translation table to see if the requested range
+ * [img_offset, img_offset + size) is fully contained within a
+ * previously loaded region.
+ *
+ * @ldr: The image loader
+ * @img_offset: Byte offset within the source image
+ * @size: Number of bytes needed
+ * Return: RAM pointer on hit, NULL on miss (does not trigger a read)
+ */
+void *image_loader_lookup(struct image_loader *ldr, ulong img_offset,
+ ulong size);
+
+/**
+ * image_loader_cleanup() - Release all backend resources
+ *
+ * Calls the backend cleanup callback (if set) and resets the loader
+ * state so it can be safely re-initialised or discarded. Should be
+ * called when the boot attempt is finished, whether it succeeded or
+ * not.
+ *
+ * @ldr: The image loader to clean up
+ */
+void image_loader_cleanup(struct image_loader *ldr);
+
+/**
+ * image_loader_map() - Ensure an image region is accessible in RAM
+ *
+ * If the region is already in the translation table, returns the
+ * existing RAM pointer. Otherwise allocates RAM at @ldr->alloc_ptr,
+ * reads the data from storage, records the mapping, advances the
+ * allocation pointer (aligned to ARCH_DMA_MINALIGN), and returns the
+ * new pointer.
+ *
+ * If the requested range starts at the same offset as an existing
+ * region but is larger, the existing region is extended in place
+ * (re-read to the same RAM base, size updated).
+ *
+ * @ldr: The image loader
+ * @img_offset: Byte offset within the source image
+ * @size: Number of bytes needed
+ * Return: RAM pointer on success, NULL on failure
+ */
+void *image_loader_map(struct image_loader *ldr, ulong img_offset,
+ ulong size);
+
+/**
+ * image_loader_map_to() - Load an image region to a specific RAM address
+ *
+ * Like image_loader_map() but reads into a caller-specified address
+ * instead of allocating from the scratch area. Used when the sub-image
+ * has a known load address for a zero-copy path.
+ *
+ * @ldr: The image loader
+ * @img_offset: Byte offset within the source image
+ * @size: Number of bytes to load
+ * @dst: Destination address in RAM
+ * Return: @dst on success, NULL on failure
+ */
+void *image_loader_map_to(struct image_loader *ldr, ulong img_offset,
+ ulong size, void *dst);
+
+#endif /* __IMAGE_LOADER_H */
--
2.53.0
^ permalink raw reply related [flat|nested] 88+ messages in thread* Re: [RFC PATCH 01/20] boot: add image_loader on-demand loading abstraction
2026-02-16 21:21 ` [RFC PATCH 01/20] boot: add image_loader on-demand loading abstraction Daniel Golle
@ 2026-02-19 13:09 ` Simon Glass
0 siblings, 0 replies; 88+ messages in thread
From: Simon Glass @ 2026-02-19 13:09 UTC (permalink / raw)
To: Daniel Golle
Cc: Tom Rini, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
Hi Daniel,
On Mon, 16 Feb 2026 at 14:21, Daniel Golle <daniel@makrotopia.org> wrote:
>
> Introduce struct image_loader, a small generic layer that lets callers
> read arbitrary byte ranges from a storage device and keeps a translation
> table of the regions that have already been loaded into RAM.
>
> The API consists of four functions:
>
> image_loader_lookup() - check whether a range is already mapped
> image_loader_map() - return a mapped pointer, reading on demand
> image_loader_map_to() - read into a caller-supplied address
> image_loader_cleanup() - release all backend resources
This is somewhat similar to blkmap, but in this case the block is 1.
How about calling it imagemap instead of image_loader? We have lots of
load_image and image_load stuff in the code base so image_loader would
be confusing.
>
> A read-callback (image_loader_read_fn) plus an opaque *priv pointer
> abstract the actual I/O so that block, MTD, and UBI back-ends can be
> added in follow-up patches without touching the core.
>
> Each backend provides a .cleanup callback which is invoked by
> image_loader_cleanup() to release held device references and free
> allocated memory. This ensures safe resource teardown between
> consecutive boot attempts.
>
> The translation table (struct image_loader_region[]) avoids redundant
> reads when the same region is requested more than once, and allows
> extending an existing mapping when a larger size is needed at the same
> offset. The table size is controlled by CONFIG_IMAGE_LOADER_MAX_REGIONS
> (default 16).
>
> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> ---
> boot/Kconfig | 20 +++++
> boot/Makefile | 2 +
> boot/image-loader.c | 163 +++++++++++++++++++++++++++++++++++++++++
> include/image-loader.h | 141 +++++++++++++++++++++++++++++++++++
> 4 files changed, 326 insertions(+)
> create mode 100644 boot/image-loader.c
> create mode 100644 include/image-loader.h
>
Having looked through some of the patches, this should be a new
uclass, with a driver for each of the parent devices (block, mtd) that
you support.
In other words it should be a child of the media device, similar to
how UCLASS_BOOTDEV works.
See my notes in patch 2 as well.
I wonder if your tests could be added here or perhaps in the next
patch after this one?
> diff --git a/boot/Kconfig b/boot/Kconfig
> index e5db165424a..f6908e04a51 100644
> --- a/boot/Kconfig
> +++ b/boot/Kconfig
> @@ -1167,6 +1167,26 @@ config SYS_BOOT_RAMDISK_HIGH
>
> endmenu # Boot images
>
> +config IMAGE_LOADER
> + bool "On-demand image loading from storage"
> + help
> + Provides a generic abstraction for reading image data from
> + storage on demand. A translation table maps already-loaded
> + regions to their RAM addresses, avoiding redundant reads.
> +
> + Used by bootm when a storage device is specified instead of a
> + RAM address.
> +
> +config IMAGE_LOADER_MAX_REGIONS
> + int "Maximum number of mapped regions in image loader"
> + default 16 if IMAGE_LOADER
> + default 0
> + help
> + Maximum number of distinct image regions that can be mapped
> + into RAM simultaneously. 16 is sufficient for typical FIT
> + images (FDT structure + kernel + device tree + ramdisk +
> + a few loadable sub-images).
It doesn't seem worth having a limit and this would render some images
unloadable. How about using an alist so you can handle any number?
Regards,
Simon
^ permalink raw reply [flat|nested] 88+ messages in thread
* [RFC PATCH 02/20] boot: image-loader: add block device backend
2026-02-16 21:21 [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading Daniel Golle
2026-02-16 21:21 ` [RFC PATCH 01/20] boot: add image_loader on-demand loading abstraction Daniel Golle
@ 2026-02-16 21:21 ` Daniel Golle
2026-02-19 13:09 ` Simon Glass
2026-02-16 21:21 ` [RFC PATCH 03/20] mtd: add mtd_read_skip_bad() helper Daniel Golle
` (23 subsequent siblings)
25 siblings, 1 reply; 88+ messages in thread
From: Daniel Golle @ 2026-02-16 21:21 UTC (permalink / raw)
To: Tom Rini, Simon Glass, Quentin Schulz, Daniel Golle,
Kory Maincent, Mattijs Korpershoek, Martin Schwan, Anshul Dalal,
Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
牛 志宏, Marek Vasut, Heinrich Schuchardt,
Wolfgang Wallner, Frank Wunderlich, David Lechner,
Osama Abdelkader, Mikhail Kshevetskiy, Michael Trimarchi,
Miquel Raynal, Andrew Goodbody, Yegor Yefremov, Mike Looijmans,
Weijie Gao, Alexander Stein, Neil Armstrong, Mayuresh Chitale,
Paul HENRYS, u-boot
Cc: John Crispin, Paul Spooren
Add a block device storage backend for the image_loader framework.
image_loader_init_blk() takes a device and partition specification
string, resolves the partition, and installs a .read() callback that
translates byte-offset reads into blk_dread() calls with proper
sector alignment.
Partitions can be identified by number or by name, following the
syntax of part_get_info_by_dev_and_name_or_num():
"0:4" partition 4 on device 0
"0#kernel" partition named "kernel" on device 0
This is important for systems where partition numbers are not stable
across firmware updates.
Sub-sector reads (offset or size not aligned to blk_desc->blksz) use
a single-sector bounce buffer to avoid overreading into adjacent RAM.
The .cleanup callback frees the allocated private context.
Gated by CONFIG_IMAGE_LOADER_BLK (depends on BLK && PARTITIONS &&
IMAGE_LOADER).
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
boot/Kconfig | 8 +++
boot/Makefile | 1 +
boot/image-loader-blk.c | 133 ++++++++++++++++++++++++++++++++++++++++
include/image-loader.h | 20 ++++++
4 files changed, 162 insertions(+)
create mode 100644 boot/image-loader-blk.c
diff --git a/boot/Kconfig b/boot/Kconfig
index f6908e04a51..e94b52288a3 100644
--- a/boot/Kconfig
+++ b/boot/Kconfig
@@ -1187,6 +1187,14 @@ config IMAGE_LOADER_MAX_REGIONS
images (FDT structure + kernel + device tree + ramdisk +
a few loadable sub-images).
+config IMAGE_LOADER_BLK
+ bool "Block device backend for image loader"
+ depends on IMAGE_LOADER && BLK && PARTITIONS
+ help
+ Allows loading images from block device partitions (MMC, SATA,
+ USB, etc.) using the image_loader framework. Partitions can
+ be identified by number or name.
+
config DISTRO_DEFAULTS
bool "(deprecated) Script-based booting of Linux distributions"
select CMDLINE
diff --git a/boot/Makefile b/boot/Makefile
index 1dbc285dad8..ac006bbaa82 100644
--- a/boot/Makefile
+++ b/boot/Makefile
@@ -74,6 +74,7 @@ obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_SIMPLE_OS) += vbe_simple_os.o
obj-$(CONFIG_$(PHASE_)BOOTMETH_ANDROID) += bootmeth_android.o
obj-$(CONFIG_IMAGE_LOADER) += image-loader.o
+obj-$(CONFIG_IMAGE_LOADER_BLK) += image-loader-blk.o
obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_ABREC) += vbe_abrec.o vbe_common.o
obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_ABREC_FW) += vbe_abrec_fw.o
diff --git a/boot/image-loader-blk.c b/boot/image-loader-blk.c
new file mode 100644
index 00000000000..3f9a309a60c
--- /dev/null
+++ b/boot/image-loader-blk.c
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Block device backend for image_loader
+ *
+ * Copyright (C) 2026 Daniel Golle <daniel@makrotopia.org>
+ */
+
+#include <blk.h>
+#include <image-loader.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <part.h>
+#include <log.h>
+
+struct image_loader_blk_priv {
+ struct blk_desc *desc;
+ lbaint_t part_start;
+ lbaint_t part_size;
+};
+
+/**
+ * blk_read_partial() - Read a partial sector via bounce buffer
+ *
+ * Reads one full sector into a stack-allocated bounce buffer, then
+ * copies @len bytes starting at byte offset @skip within that sector
+ * into @dst.
+ *
+ * @desc: Block device descriptor
+ * @lba: Absolute LBA of the sector to read
+ * @skip: Byte offset within the sector
+ * @len: Number of bytes to copy
+ * @dst: Destination buffer
+ * Return: 0 on success, -EIO on read failure
+ */
+static int blk_read_partial(struct blk_desc *desc, lbaint_t lba,
+ ulong skip, ulong len, void *dst)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(u8, sec, desc->blksz);
+
+ if (blk_dread(desc, lba, 1, sec) != 1)
+ return -EIO;
+
+ memcpy(dst, sec + skip, len);
+
+ return 0;
+}
+
+static int image_loader_blk_read(struct image_loader *ldr, ulong src,
+ ulong size, void *dst)
+{
+ struct image_loader_blk_priv *priv = ldr->priv;
+ struct blk_desc *desc = priv->desc;
+ unsigned long blksz = desc->blksz;
+ lbaint_t lba = priv->part_start + src / blksz;
+ ulong head = src % blksz;
+ u8 *out = dst;
+ lbaint_t n;
+ int ret;
+
+ /* Bounds check */
+ if (src + size > (ulong)priv->part_size * blksz) {
+ log_err("image_loader_blk: read at 0x%lx+0x%lx exceeds partition size\n",
+ src, size);
+ return -EINVAL;
+ }
+
+ /* Handle unaligned head */
+ if (head) {
+ ulong chunk = min(size, blksz - head);
+
+ ret = blk_read_partial(desc, lba, head, chunk, out);
+ if (ret)
+ return ret;
+
+ out += chunk;
+ size -= chunk;
+ lba++;
+ }
+
+ /* Aligned middle — read whole sectors directly into dst */
+ if (size >= blksz) {
+ n = size / blksz;
+
+ if (blk_dread(desc, lba, n, out) != n)
+ return -EIO;
+
+ out += n * blksz;
+ size -= n * blksz;
+ lba += n;
+ }
+
+ /* Handle unaligned tail */
+ if (size) {
+ ret = blk_read_partial(desc, lba, 0, size, out);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void image_loader_blk_cleanup(struct image_loader *ldr)
+{
+ free(ldr->priv);
+}
+
+int image_loader_init_blk(struct image_loader *ldr, const char *ifname,
+ const char *dev_part_str)
+{
+ struct image_loader_blk_priv *priv;
+ struct blk_desc *desc;
+ struct disk_partition info;
+ int ret;
+
+ ret = part_get_info_by_dev_and_name_or_num(ifname, dev_part_str,
+ &desc, &info, 0);
+ if (ret < 0)
+ return ret;
+
+ priv = malloc(sizeof(*priv));
+ if (!priv)
+ return -ENOMEM;
+
+ priv->desc = desc;
+ priv->part_start = info.start;
+ priv->part_size = info.size;
+
+ ldr->read = image_loader_blk_read;
+ ldr->cleanup = image_loader_blk_cleanup;
+ ldr->priv = priv;
+
+ return 0;
+}
diff --git a/include/image-loader.h b/include/image-loader.h
index e273b1ca50f..1a9048ba482 100644
--- a/include/image-loader.h
+++ b/include/image-loader.h
@@ -138,4 +138,24 @@ void *image_loader_map(struct image_loader *ldr, ulong img_offset,
void *image_loader_map_to(struct image_loader *ldr, ulong img_offset,
ulong size, void *dst);
+/**
+ * image_loader_init_blk() - Initialise loader for a block device partition
+ *
+ * Resolves the partition using @ifname and @dev_part_str, then installs
+ * a .read() callback that translates byte-offset reads into blk_dread()
+ * calls. The dev_part_str accepts the same formats as
+ * part_get_info_by_dev_and_name_or_num():
+ *
+ * "0:4" partition 4 on device 0
+ * "0#kernel" partition named "kernel" on device 0
+ * "0:1" partition 1 on device 0
+ *
+ * @ldr: The image loader to initialise
+ * @ifname: Block interface name (e.g. "mmc", "scsi")
+ * @dev_part_str: Device and partition specification
+ * Return: 0 on success, negative errno on failure
+ */
+int image_loader_init_blk(struct image_loader *ldr, const char *ifname,
+ const char *dev_part_str);
+
#endif /* __IMAGE_LOADER_H */
--
2.53.0
^ permalink raw reply related [flat|nested] 88+ messages in thread* Re: [RFC PATCH 02/20] boot: image-loader: add block device backend
2026-02-16 21:21 ` [RFC PATCH 02/20] boot: image-loader: add block device backend Daniel Golle
@ 2026-02-19 13:09 ` Simon Glass
0 siblings, 0 replies; 88+ messages in thread
From: Simon Glass @ 2026-02-19 13:09 UTC (permalink / raw)
To: Daniel Golle
Cc: Tom Rini, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
Hi Daniel,
On Mon, 16 Feb 2026 at 14:21, Daniel Golle <daniel@makrotopia.org> wrote:
>
> Add a block device storage backend for the image_loader framework.
>
> image_loader_init_blk() takes a device and partition specification
> string, resolves the partition, and installs a .read() callback that
> translates byte-offset reads into blk_dread() calls with proper
> sector alignment.
>
> Partitions can be identified by number or by name, following the
> syntax of part_get_info_by_dev_and_name_or_num():
> "0:4" partition 4 on device 0
> "0#kernel" partition named "kernel" on device 0
>
> This is important for systems where partition numbers are not stable
> across firmware updates.
>
> Sub-sector reads (offset or size not aligned to blk_desc->blksz) use
> a single-sector bounce buffer to avoid overreading into adjacent RAM.
>
> The .cleanup callback frees the allocated private context.
>
> Gated by CONFIG_IMAGE_LOADER_BLK (depends on BLK && PARTITIONS &&
> IMAGE_LOADER).
>
> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> ---
> boot/Kconfig | 8 +++
> boot/Makefile | 1 +
> boot/image-loader-blk.c | 133 ++++++++++++++++++++++++++++++++++++++++
> include/image-loader.h | 20 ++++++
> 4 files changed, 162 insertions(+)
> create mode 100644 boot/image-loader-blk.c
>
Yes, this should be a driver, a child of the device that provides its
media, in this case a UCLASS_BLK. You can see a similar pattern with
UCLASS_BOOTDEV
You can use probe() and remove() to set up and clean up.
> diff --git a/boot/Kconfig b/boot/Kconfig
> index f6908e04a51..e94b52288a3 100644
> --- a/boot/Kconfig
> +++ b/boot/Kconfig
> @@ -1187,6 +1187,14 @@ config IMAGE_LOADER_MAX_REGIONS
> images (FDT structure + kernel + device tree + ramdisk +
> a few loadable sub-images).
>
> +config IMAGE_LOADER_BLK
> + bool "Block device backend for image loader"
> + depends on IMAGE_LOADER && BLK && PARTITIONS
> + help
> + Allows loading images from block device partitions (MMC, SATA,
> + USB, etc.) using the image_loader framework. Partitions can
> + be identified by number or name.
> +
> config DISTRO_DEFAULTS
> bool "(deprecated) Script-based booting of Linux distributions"
> select CMDLINE
> diff --git a/boot/Makefile b/boot/Makefile
> index 1dbc285dad8..ac006bbaa82 100644
> --- a/boot/Makefile
> +++ b/boot/Makefile
> @@ -74,6 +74,7 @@ obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_SIMPLE_OS) += vbe_simple_os.o
> obj-$(CONFIG_$(PHASE_)BOOTMETH_ANDROID) += bootmeth_android.o
>
> obj-$(CONFIG_IMAGE_LOADER) += image-loader.o
> +obj-$(CONFIG_IMAGE_LOADER_BLK) += image-loader-blk.o
>
> obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_ABREC) += vbe_abrec.o vbe_common.o
> obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_ABREC_FW) += vbe_abrec_fw.o
> diff --git a/boot/image-loader-blk.c b/boot/image-loader-blk.c
> new file mode 100644
> index 00000000000..3f9a309a60c
> --- /dev/null
> +++ b/boot/image-loader-blk.c
> @@ -0,0 +1,133 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Block device backend for image_loader
> + *
> + * Copyright (C) 2026 Daniel Golle <daniel@makrotopia.org>
> + */
> +
> +#include <blk.h>
> +#include <image-loader.h>
> +#include <malloc.h>
> +#include <memalign.h>
> +#include <part.h>
> +#include <log.h>
> +
> +struct image_loader_blk_priv {
> + struct blk_desc *desc;
> + lbaint_t part_start;
> + lbaint_t part_size;
> +};
Needs a comment. The 'desc' won't be needed when this is a driver
since you can use dev_get_parent(your_dev) which will be a block
device.
> +
> +/**
> + * blk_read_partial() - Read a partial sector via bounce buffer
> + *
> + * Reads one full sector into a stack-allocated bounce buffer, then
> + * copies @len bytes starting at byte offset @skip within that sector
> + * into @dst.
> + *
> + * @desc: Block device descriptor
> + * @lba: Absolute LBA of the sector to read
> + * @skip: Byte offset within the sector
So maybe call it 'offset' ?
> + * @len: Number of bytes to copy
> + * @dst: Destination buffer
> + * Return: 0 on success, -EIO on read failure
> + */
> +static int blk_read_partial(struct blk_desc *desc, lbaint_t lba,
> + ulong skip, ulong len, void *dst)
> +{
> + ALLOC_CACHE_ALIGN_BUFFER(u8, sec, desc->blksz);
> +
> + if (blk_dread(desc, lba, 1, sec) != 1)
> + return -EIO;
> +
> + memcpy(dst, sec + skip, len);
> +
> + return 0;
> +}
> +
> +static int image_loader_blk_read(struct image_loader *ldr, ulong src,
> + ulong size, void *dst)
> +{
> + struct image_loader_blk_priv *priv = ldr->priv;
> + struct blk_desc *desc = priv->desc;
> + unsigned long blksz = desc->blksz;
ulong
> + lbaint_t lba = priv->part_start + src / blksz;
> + ulong head = src % blksz;
> + u8 *out = dst;
> + lbaint_t n;
> + int ret;
> +
> + /* Bounds check */
> + if (src + size > (ulong)priv->part_size * blksz) {
> + log_err("image_loader_blk: read at 0x%lx+0x%lx exceeds partition size\n",
> + src, size);
> + return -EINVAL;
> + }
> +
> + /* Handle unaligned head */
> + if (head) {
> + ulong chunk = min(size, blksz - head);
> +
> + ret = blk_read_partial(desc, lba, head, chunk, out);
> + if (ret)
> + return ret;
> +
> + out += chunk;
> + size -= chunk;
> + lba++;
> + }
> +
> + /* Aligned middle — read whole sectors directly into dst */
> + if (size >= blksz) {
> + n = size / blksz;
> +
> + if (blk_dread(desc, lba, n, out) != n)
> + return -EIO;
> +
> + out += n * blksz;
> + size -= n * blksz;
> + lba += n;
> + }
> +
> + /* Handle unaligned tail */
> + if (size) {
> + ret = blk_read_partial(desc, lba, 0, size, out);
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static void image_loader_blk_cleanup(struct image_loader *ldr)
> +{
> + free(ldr->priv);
This is handled automatically by driver model.
> +}
> +
> +int image_loader_init_blk(struct image_loader *ldr, const char *ifname,
> + const char *dev_part_str)
> +{
> + struct image_loader_blk_priv *priv;
> + struct blk_desc *desc;
> + struct disk_partition info;
> + int ret;
> +
> + ret = part_get_info_by_dev_and_name_or_num(ifname, dev_part_str,
> + &desc, &info, 0);
> + if (ret < 0)
> + return ret;
> +
> + priv = malloc(sizeof(*priv));
> + if (!priv)
> + return -ENOMEM;
and this
> +
> + priv->desc = desc;
> + priv->part_start = info.start;
> + priv->part_size = info.size;
> +
> + ldr->read = image_loader_blk_read;
> + ldr->cleanup = image_loader_blk_cleanup;
> + ldr->priv = priv;
> +
> + return 0;
> +}
> diff --git a/include/image-loader.h b/include/image-loader.h
> index e273b1ca50f..1a9048ba482 100644
> --- a/include/image-loader.h
> +++ b/include/image-loader.h
> @@ -138,4 +138,24 @@ void *image_loader_map(struct image_loader *ldr, ulong img_offset,
> void *image_loader_map_to(struct image_loader *ldr, ulong img_offset,
> ulong size, void *dst);
>
> +/**
> + * image_loader_init_blk() - Initialise loader for a block device partition
> + *
> + * Resolves the partition using @ifname and @dev_part_str, then installs
> + * a .read() callback that translates byte-offset reads into blk_dread()
> + * calls. The dev_part_str accepts the same formats as
> + * part_get_info_by_dev_and_name_or_num():
> + *
> + * "0:4" partition 4 on device 0
> + * "0#kernel" partition named "kernel" on device 0
> + * "0:1" partition 1 on device 0
> + *
> + * @ldr: The image loader to initialise
> + * @ifname: Block interface name (e.g. "mmc", "scsi")
> + * @dev_part_str: Device and partition specification
> + * Return: 0 on success, negative errno on failure
> + */
> +int image_loader_init_blk(struct image_loader *ldr, const char *ifname,
> + const char *dev_part_str);
> +
> #endif /* __IMAGE_LOADER_H */
> --
> 2.53.0
Regards,
Simon
^ permalink raw reply [flat|nested] 88+ messages in thread
* [RFC PATCH 03/20] mtd: add mtd_read_skip_bad() helper
2026-02-16 21:21 [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading Daniel Golle
2026-02-16 21:21 ` [RFC PATCH 01/20] boot: add image_loader on-demand loading abstraction Daniel Golle
2026-02-16 21:21 ` [RFC PATCH 02/20] boot: image-loader: add block device backend Daniel Golle
@ 2026-02-16 21:21 ` Daniel Golle
2026-02-16 21:21 ` [RFC PATCH 04/20] boot: image-loader: add MTD backend Daniel Golle
` (22 subsequent siblings)
25 siblings, 0 replies; 88+ messages in thread
From: Daniel Golle @ 2026-02-16 21:21 UTC (permalink / raw)
To: Tom Rini, Simon Glass, Quentin Schulz, Daniel Golle,
Kory Maincent, Mattijs Korpershoek, Martin Schwan, Anshul Dalal,
Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
牛 志宏, Marek Vasut, Heinrich Schuchardt,
Wolfgang Wallner, Frank Wunderlich, David Lechner,
Osama Abdelkader, Mikhail Kshevetskiy, Michael Trimarchi,
Miquel Raynal, Andrew Goodbody, Yegor Yefremov, Mike Looijmans,
Weijie Gao, Alexander Stein, Neil Armstrong, Mayuresh Chitale,
Paul HENRYS, u-boot
Cc: John Crispin, Paul Spooren
Add a generic mtd_read_skip_bad() helper to the MTD core that reads
data from an MTD device while transparently skipping bad erase blocks
on NAND.
The function takes a physical byte offset, reads in erase-block-sized
chunks, and automatically skips any block where mtd_block_isbad()
returns true. Non-block-aligned start offsets are handled correctly.
For NOR and other device types without bad-block support, it falls
through to a plain mtd_read().
Refactor the plain-data read path in cmd/mtd.c ("mtd read", without
.raw or .oob suffixes) to use the new helper instead of open-coding
the bad-block skip loop. The write path and OOB/raw read paths
retain their existing page-at-a-time mtd_read_oob() loop.
This helper will also be used by the image_loader MTD backend in a
subsequent patch. It could also serve as a replacement for
nand_read_skip_bad() in drivers/mtd/nand/raw/nand_util.c in the
future.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
cmd/mtd.c | 65 +++++++++++++++++++++++------------------
drivers/mtd/mtdcore.c | 45 ++++++++++++++++++++++++++++
include/linux/mtd/mtd.h | 24 +++++++++++++++
3 files changed, 106 insertions(+), 28 deletions(-)
diff --git a/cmd/mtd.c b/cmd/mtd.c
index 7f25144098b..30e4845b26d 100644
--- a/cmd/mtd.c
+++ b/cmd/mtd.c
@@ -551,12 +551,6 @@ static int do_mtd_io(struct cmd_tbl *cmdtp, int flag, int argc,
printf("%s %lld byte(s) at offset 0x%08llx\n",
read ? "Reading" : "Writing", len, start_off);
- io_op.mode = raw ? MTD_OPS_RAW : MTD_OPS_AUTO_OOB;
- io_op.len = has_pages ? mtd->writesize : len;
- io_op.ooblen = woob ? mtd->oobsize : 0;
- io_op.datbuf = buf;
- io_op.oobbuf = woob ? &buf[len] : NULL;
-
/* Search for the first good block after the given offset */
off = start_off;
while (mtd_block_isbad(mtd, off))
@@ -567,31 +561,46 @@ static int do_mtd_io(struct cmd_tbl *cmdtp, int flag, int argc,
if (benchmark)
bench_start = timer_get_us();
- /* Loop over the pages to do the actual read/write */
- while (remaining) {
- /* Skip the block if it is bad */
- if (mtd_is_aligned_with_block_size(mtd, off) &&
- mtd_block_isbad(mtd, off)) {
- off += mtd->erasesize;
- continue;
- }
+ if (read && !raw && !woob) {
+ /* Plain data read — use the skip-bad-block helper */
+ size_t rdlen;
- if (read)
- ret = mtd_read_oob(mtd, off, &io_op);
- else
- ret = mtd_special_write_oob(mtd, off, &io_op,
- write_empty_pages, woob);
+ ret = mtd_read_skip_bad(mtd, off, remaining, &rdlen, buf);
+ remaining -= rdlen;
+ } else {
+ io_op.mode = raw ? MTD_OPS_RAW : MTD_OPS_AUTO_OOB;
+ io_op.len = has_pages ? mtd->writesize : len;
+ io_op.ooblen = woob ? mtd->oobsize : 0;
+ io_op.datbuf = buf;
+ io_op.oobbuf = woob ? &buf[len] : NULL;
+
+ /* Loop over the pages to do the actual read/write */
+ while (remaining) {
+ /* Skip the block if it is bad */
+ if (mtd_is_aligned_with_block_size(mtd, off) &&
+ mtd_block_isbad(mtd, off)) {
+ off += mtd->erasesize;
+ continue;
+ }
- if (ret) {
- printf("Failure while %s at offset 0x%llx\n",
- read ? "reading" : "writing", off);
- break;
- }
+ if (read)
+ ret = mtd_read_oob(mtd, off, &io_op);
+ else
+ ret = mtd_special_write_oob(mtd, off, &io_op,
+ write_empty_pages,
+ woob);
+
+ if (ret) {
+ printf("Failure while %s at offset 0x%llx\n",
+ read ? "reading" : "writing", off);
+ break;
+ }
- off += io_op.retlen;
- remaining -= io_op.retlen;
- io_op.datbuf += io_op.retlen;
- io_op.oobbuf += io_op.oobretlen;
+ off += io_op.retlen;
+ remaining -= io_op.retlen;
+ io_op.datbuf += io_op.retlen;
+ io_op.oobbuf += io_op.oobretlen;
+ }
}
if (benchmark && bench_start) {
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index 3bfa5aebbc6..eb36743af1f 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -1684,6 +1684,51 @@ int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs)
}
EXPORT_SYMBOL_GPL(mtd_block_markbad);
+int mtd_read_skip_bad(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ size_t remaining = len;
+ u_char *p = buf;
+ int ret;
+
+ *retlen = 0;
+
+ if (!mtd_can_have_bb(mtd)) {
+ ret = mtd_read(mtd, from, len, retlen, buf);
+ if (ret == -EUCLEAN)
+ ret = 0;
+ return ret;
+ }
+
+ while (remaining) {
+ loff_t block_start = from & ~(loff_t)(mtd->erasesize - 1);
+ size_t block_off = from - block_start;
+ size_t chunk, rdlen;
+
+ if (from >= mtd->size)
+ return -EINVAL;
+
+ if (mtd_block_isbad(mtd, block_start)) {
+ /* Skip to start of next erase block */
+ from = block_start + mtd->erasesize;
+ continue;
+ }
+
+ chunk = min(remaining, (size_t)mtd->erasesize - block_off);
+
+ ret = mtd_read(mtd, from, chunk, &rdlen, p);
+ if (ret && ret != -EUCLEAN)
+ return ret;
+
+ p += rdlen;
+ from += rdlen;
+ remaining -= rdlen;
+ *retlen += rdlen;
+ }
+
+ return 0;
+}
+
#ifndef __UBOOT__
/*
* default_mtd_writev - the default writev method
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
index 1f97bc4fe11..ea3fa513c58 100644
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -471,6 +471,30 @@ int mtd_block_isreserved(struct mtd_info *mtd, loff_t ofs);
int mtd_block_isbad(struct mtd_info *mtd, loff_t ofs);
int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs);
+/**
+ * mtd_read_skip_bad() - Read from an MTD device, skipping bad blocks
+ *
+ * Read @len bytes starting at physical offset @from into @buf. On NAND
+ * devices, erase blocks that are marked bad are transparently skipped
+ * so the caller always receives a contiguous stream of good data.
+ *
+ * If @from is not erase-block-aligned, reading starts at the correct
+ * position within the first good block.
+ *
+ * For NOR and other device types without bad-block support, this is
+ * equivalent to a plain mtd_read().
+ *
+ * @mtd: MTD device
+ * @from: Start offset (physical, byte address)
+ * @len: Number of bytes to read
+ * @retlen: Actual number of bytes read (output)
+ * @buf: Destination buffer
+ * Return: 0 on success, negative errno on failure. -EUCLEAN is
+ * treated as success (ECC corrected).
+ */
+int mtd_read_skip_bad(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf);
+
#ifndef __UBOOT__
static inline int mtd_suspend(struct mtd_info *mtd)
{
--
2.53.0
^ permalink raw reply related [flat|nested] 88+ messages in thread* [RFC PATCH 04/20] boot: image-loader: add MTD backend
2026-02-16 21:21 [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading Daniel Golle
` (2 preceding siblings ...)
2026-02-16 21:21 ` [RFC PATCH 03/20] mtd: add mtd_read_skip_bad() helper Daniel Golle
@ 2026-02-16 21:21 ` Daniel Golle
2026-02-16 21:21 ` [RFC PATCH 05/20] cmd: ubi: export ubi_find_volume() Daniel Golle
` (21 subsequent siblings)
25 siblings, 0 replies; 88+ messages in thread
From: Daniel Golle @ 2026-02-16 21:21 UTC (permalink / raw)
To: Tom Rini, Simon Glass, Quentin Schulz, Daniel Golle,
Kory Maincent, Mattijs Korpershoek, Martin Schwan, Anshul Dalal,
Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
牛 志宏, Marek Vasut, Heinrich Schuchardt,
Wolfgang Wallner, Frank Wunderlich, David Lechner,
Osama Abdelkader, Mikhail Kshevetskiy, Michael Trimarchi,
Miquel Raynal, Andrew Goodbody, Yegor Yefremov, Mike Looijmans,
Weijie Gao, Alexander Stein, Neil Armstrong, Mayuresh Chitale,
Paul HENRYS, u-boot
Cc: John Crispin, Paul Spooren
Add an MTD storage backend for the image_loader framework.
image_loader_init_mtd() takes the name of an MTD partition, resolves
it via get_mtd_device_nm(), and installs a .read() callback that
translates byte-offset reads into mtd_read() calls.
On NAND devices, the read callback implements bad-block skipping:
when a read straddles a bad block, the block is silently skipped and
reading continues at the next good block. This matches the convention
used by the mtd command and OpenWrt's mtd utilities.
For NOR devices, reads are passed straight through to mtd_read()
without bad-block logic.
The .cleanup callback releases the MTD device reference (via
put_mtd_device()) and frees the private context, ensuring safe
teardown between consecutive boot attempts.
Gated by CONFIG_IMAGE_LOADER_MTD (depends on DM_MTD && IMAGE_LOADER).
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
boot/Kconfig | 8 ++++
boot/Makefile | 1 +
boot/image-loader-mtd.c | 103 ++++++++++++++++++++++++++++++++++++++++
include/image-loader.h | 14 ++++++
4 files changed, 126 insertions(+)
create mode 100644 boot/image-loader-mtd.c
diff --git a/boot/Kconfig b/boot/Kconfig
index e94b52288a3..23848a0f57e 100644
--- a/boot/Kconfig
+++ b/boot/Kconfig
@@ -1195,6 +1195,14 @@ config IMAGE_LOADER_BLK
USB, etc.) using the image_loader framework. Partitions can
be identified by number or name.
+config IMAGE_LOADER_MTD
+ bool "MTD backend for image loader"
+ depends on IMAGE_LOADER && DM_MTD
+ help
+ Allows loading images from MTD partitions (SPI-NOR, SPI-NAND,
+ parallel NAND, etc.) using the image_loader framework.
+ NAND bad blocks are skipped transparently.
+
config DISTRO_DEFAULTS
bool "(deprecated) Script-based booting of Linux distributions"
select CMDLINE
diff --git a/boot/Makefile b/boot/Makefile
index ac006bbaa82..1dde16db694 100644
--- a/boot/Makefile
+++ b/boot/Makefile
@@ -75,6 +75,7 @@ obj-$(CONFIG_$(PHASE_)BOOTMETH_ANDROID) += bootmeth_android.o
obj-$(CONFIG_IMAGE_LOADER) += image-loader.o
obj-$(CONFIG_IMAGE_LOADER_BLK) += image-loader-blk.o
+obj-$(CONFIG_IMAGE_LOADER_MTD) += image-loader-mtd.o
obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_ABREC) += vbe_abrec.o vbe_common.o
obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_ABREC_FW) += vbe_abrec_fw.o
diff --git a/boot/image-loader-mtd.c b/boot/image-loader-mtd.c
new file mode 100644
index 00000000000..0aa1163e2db
--- /dev/null
+++ b/boot/image-loader-mtd.c
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * MTD backend for image_loader
+ *
+ * Copyright (C) 2026 Daniel Golle <daniel@makrotopia.org>
+ */
+
+#include <image-loader.h>
+#include <malloc.h>
+#include <mtd.h>
+#include <log.h>
+#include <linux/err.h>
+
+struct image_loader_mtd_priv {
+ struct mtd_info *mtd;
+};
+
+static int image_loader_mtd_read(struct image_loader *ldr, ulong src,
+ ulong size, void *dst)
+{
+ struct image_loader_mtd_priv *priv = ldr->priv;
+ struct mtd_info *mtd = priv->mtd;
+ loff_t phys = src;
+ size_t retlen;
+ int ret;
+
+ if (mtd_can_have_bb(mtd)) {
+ /*
+ * NAND — walk from the beginning of the device, skipping
+ * bad blocks, to translate the logical byte offset @src
+ * into a physical byte offset. The actual read with
+ * further bad-block skipping is handled by
+ * mtd_read_skip_bad().
+ */
+ loff_t off = 0;
+ ulong logical = 0;
+
+ while (logical < src) {
+ loff_t block_start = off & ~(loff_t)(mtd->erasesize - 1);
+ ulong block_remain, chunk;
+
+ if (off >= mtd->size) {
+ log_err("image_loader_mtd: offset past end of device\n");
+ return -EINVAL;
+ }
+
+ if (mtd_block_isbad(mtd, block_start)) {
+ off = block_start + mtd->erasesize;
+ continue;
+ }
+
+ block_remain = mtd->erasesize - (ulong)(off - block_start);
+ chunk = min(block_remain, (ulong)(src - logical));
+ off += chunk;
+ logical += chunk;
+ }
+
+ phys = off;
+ }
+
+ ret = mtd_read_skip_bad(mtd, phys, size, &retlen, dst);
+ if (ret)
+ return ret;
+
+ return (retlen == size) ? 0 : -EIO;
+}
+
+static void image_loader_mtd_cleanup(struct image_loader *ldr)
+{
+ struct image_loader_mtd_priv *priv = ldr->priv;
+
+ put_mtd_device(priv->mtd);
+ free(priv);
+}
+
+int image_loader_init_mtd(struct image_loader *ldr, const char *name)
+{
+ struct image_loader_mtd_priv *priv;
+ struct mtd_info *mtd;
+
+ mtd_probe_devices();
+
+ mtd = get_mtd_device_nm(name);
+ if (IS_ERR_OR_NULL(mtd)) {
+ log_err("image_loader_mtd: MTD device \"%s\" not found\n",
+ name);
+ return -ENODEV;
+ }
+
+ priv = malloc(sizeof(*priv));
+ if (!priv) {
+ put_mtd_device(mtd);
+ return -ENOMEM;
+ }
+
+ priv->mtd = mtd;
+
+ ldr->read = image_loader_mtd_read;
+ ldr->cleanup = image_loader_mtd_cleanup;
+ ldr->priv = priv;
+
+ return 0;
+}
diff --git a/include/image-loader.h b/include/image-loader.h
index 1a9048ba482..7ccf901d37d 100644
--- a/include/image-loader.h
+++ b/include/image-loader.h
@@ -158,4 +158,18 @@ void *image_loader_map_to(struct image_loader *ldr, ulong img_offset,
int image_loader_init_blk(struct image_loader *ldr, const char *ifname,
const char *dev_part_str);
+/**
+ * image_loader_init_mtd() - Initialise loader for an MTD partition
+ *
+ * Resolves the MTD device by @name via get_mtd_device_nm(), then
+ * installs a .read() callback that translates byte-offset reads into
+ * mtd_read() calls. On NAND devices, bad blocks are transparently
+ * skipped.
+ *
+ * @ldr: The image loader to initialise
+ * @name: MTD device/partition name
+ * Return: 0 on success, negative errno on failure
+ */
+int image_loader_init_mtd(struct image_loader *ldr, const char *name);
+
#endif /* __IMAGE_LOADER_H */
--
2.53.0
^ permalink raw reply related [flat|nested] 88+ messages in thread* [RFC PATCH 05/20] cmd: ubi: export ubi_find_volume()
2026-02-16 21:21 [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading Daniel Golle
` (3 preceding siblings ...)
2026-02-16 21:21 ` [RFC PATCH 04/20] boot: image-loader: add MTD backend Daniel Golle
@ 2026-02-16 21:21 ` Daniel Golle
2026-02-19 13:09 ` Simon Glass
2026-02-16 21:22 ` [RFC PATCH 06/20] mtd: set flash_node on DT-created partitions Daniel Golle
` (20 subsequent siblings)
25 siblings, 1 reply; 88+ messages in thread
From: Daniel Golle @ 2026-02-16 21:21 UTC (permalink / raw)
To: Tom Rini, Simon Glass, Quentin Schulz, Daniel Golle,
Kory Maincent, Mattijs Korpershoek, Martin Schwan, Anshul Dalal,
Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
牛 志宏, Marek Vasut, Heinrich Schuchardt,
Wolfgang Wallner, Frank Wunderlich, David Lechner,
Osama Abdelkader, Mikhail Kshevetskiy, Michael Trimarchi,
Miquel Raynal, Andrew Goodbody, Yegor Yefremov, Mike Looijmans,
Weijie Gao, Alexander Stein, Neil Armstrong, Mayuresh Chitale,
Paul HENRYS, u-boot
Cc: John Crispin, Paul Spooren
Remove the static qualifier from ubi_find_volume() and add a
declaration to include/ubi_uboot.h so that other translation units
can look up a UBI volume by name.
The function now refers to ubi_devices[0] directly instead of the
file-local 'ubi' pointer, and takes a const char * for the volume
name.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
cmd/ubi.c | 10 +++++++---
include/ubi_uboot.h | 1 +
2 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/cmd/ubi.c b/cmd/ubi.c
index 93de6f3aea2..ca7d657aa07 100644
--- a/cmd/ubi.c
+++ b/cmd/ubi.c
@@ -247,13 +247,17 @@ static int ubi_create_vol(char *volume, int64_t size, int dynamic, int vol_id,
return ubi_create_volume(ubi, &req);
}
-static struct ubi_volume *ubi_find_volume(char *volume)
+struct ubi_volume *ubi_find_volume(const char *volume)
{
+ struct ubi_device *dev = ubi_devices[0];
struct ubi_volume *vol;
int i;
- for (i = 0; i < ubi->vtbl_slots; i++) {
- vol = ubi->volumes[i];
+ if (!dev)
+ return NULL;
+
+ for (i = 0; i < dev->vtbl_slots; i++) {
+ vol = dev->volumes[i];
if (vol && !strcmp(vol->name, volume))
return vol;
}
diff --git a/include/ubi_uboot.h b/include/ubi_uboot.h
index ea0db69c72a..92892704d0a 100644
--- a/include/ubi_uboot.h
+++ b/include/ubi_uboot.h
@@ -50,6 +50,7 @@ extern void ubi_exit(void);
extern int ubi_part(char *part_name, const char *vid_header_offset);
extern int ubi_volume_write(char *volume, void *buf, loff_t offset, size_t size);
extern int ubi_volume_read(char *volume, char *buf, loff_t offset, size_t size);
+struct ubi_volume *ubi_find_volume(const char *volume);
extern struct ubi_device *ubi_devices[];
int cmd_ubifs_mount(char *vol_name);
--
2.53.0
^ permalink raw reply related [flat|nested] 88+ messages in thread* Re: [RFC PATCH 05/20] cmd: ubi: export ubi_find_volume()
2026-02-16 21:21 ` [RFC PATCH 05/20] cmd: ubi: export ubi_find_volume() Daniel Golle
@ 2026-02-19 13:09 ` Simon Glass
0 siblings, 0 replies; 88+ messages in thread
From: Simon Glass @ 2026-02-19 13:09 UTC (permalink / raw)
To: Daniel Golle
Cc: Tom Rini, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
Hi Daniel,
On Mon, 16 Feb 2026 at 14:22, Daniel Golle <daniel@makrotopia.org> wrote:
>
> Remove the static qualifier from ubi_find_volume() and add a
> declaration to include/ubi_uboot.h so that other translation units
> can look up a UBI volume by name.
>
> The function now refers to ubi_devices[0] directly instead of the
> file-local 'ubi' pointer, and takes a const char * for the volume
> name.
>
> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> ---
> cmd/ubi.c | 10 +++++++---
> include/ubi_uboot.h | 1 +
> 2 files changed, 8 insertions(+), 3 deletions(-)
>
> diff --git a/cmd/ubi.c b/cmd/ubi.c
> index 93de6f3aea2..ca7d657aa07 100644
> --- a/cmd/ubi.c
> +++ b/cmd/ubi.c
> @@ -247,13 +247,17 @@ static int ubi_create_vol(char *volume, int64_t size, int dynamic, int vol_id,
> return ubi_create_volume(ubi, &req);
> }
>
> -static struct ubi_volume *ubi_find_volume(char *volume)
> +struct ubi_volume *ubi_find_volume(const char *volume)
> {
> + struct ubi_device *dev = ubi_devices[0];
> struct ubi_volume *vol;
> int i;
>
> - for (i = 0; i < ubi->vtbl_slots; i++) {
> - vol = ubi->volumes[i];
> + if (!dev)
> + return NULL;
> +
> + for (i = 0; i < dev->vtbl_slots; i++) {
> + vol = dev->volumes[i];
> if (vol && !strcmp(vol->name, volume))
> return vol;
> }
> diff --git a/include/ubi_uboot.h b/include/ubi_uboot.h
> index ea0db69c72a..92892704d0a 100644
> --- a/include/ubi_uboot.h
> +++ b/include/ubi_uboot.h
> @@ -50,6 +50,7 @@ extern void ubi_exit(void);
> extern int ubi_part(char *part_name, const char *vid_header_offset);
> extern int ubi_volume_write(char *volume, void *buf, loff_t offset, size_t size);
> extern int ubi_volume_read(char *volume, char *buf, loff_t offset, size_t size);
> +struct ubi_volume *ubi_find_volume(const char *volume);
Needs a full comment.
>
> extern struct ubi_device *ubi_devices[];
> int cmd_ubifs_mount(char *vol_name);
> --
> 2.53.0
Regards,
Simon
^ permalink raw reply [flat|nested] 88+ messages in thread
* [RFC PATCH 06/20] mtd: set flash_node on DT-created partitions
2026-02-16 21:21 [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading Daniel Golle
` (4 preceding siblings ...)
2026-02-16 21:21 ` [RFC PATCH 05/20] cmd: ubi: export ubi_find_volume() Daniel Golle
@ 2026-02-16 21:22 ` Daniel Golle
2026-02-16 21:22 ` [RFC PATCH 07/20] cmd: ubi: add ubi_part_from_mtd() Daniel Golle
` (19 subsequent siblings)
25 siblings, 0 replies; 88+ messages in thread
From: Daniel Golle @ 2026-02-16 21:22 UTC (permalink / raw)
To: Tom Rini, Simon Glass, Quentin Schulz, Daniel Golle,
Kory Maincent, Mattijs Korpershoek, Martin Schwan, Anshul Dalal,
Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
牛 志宏, Marek Vasut, Heinrich Schuchardt,
Wolfgang Wallner, Frank Wunderlich, David Lechner,
Osama Abdelkader, Mikhail Kshevetskiy, Michael Trimarchi,
Miquel Raynal, Andrew Goodbody, Yegor Yefremov, Mike Looijmans,
Weijie Gao, Alexander Stein, Neil Armstrong, Mayuresh Chitale,
Paul HENRYS, u-boot
Cc: John Crispin, Paul Spooren
add_mtd_partitions_of() creates MTD partition slaves from device-tree
sub-nodes but never stores the originating ofnode on the partition.
This means there is no way to match a registered MTD partition back
to its DT node.
Set slave->flash_node = child right after allocate_partition() so
that every DT-created partition carries a reference to its DT node.
This will be used by the image_loader UBI backend to find the MTD
partition that corresponds to a 'compatible = "linux,ubi"' node.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
drivers/mtd/mtdpart.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
index f3b7f1ee13c..eac7215907b 100644
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -940,6 +940,8 @@ int add_mtd_partitions_of(struct mtd_info *master)
if (IS_ERR(slave))
return PTR_ERR(slave);
+ slave->flash_node = child;
+
mutex_lock(&mtd_partitions_mutex);
list_add_tail(&slave->node, &master->partitions);
mutex_unlock(&mtd_partitions_mutex);
--
2.53.0
^ permalink raw reply related [flat|nested] 88+ messages in thread* [RFC PATCH 07/20] cmd: ubi: add ubi_part_from_mtd()
2026-02-16 21:21 [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading Daniel Golle
` (5 preceding siblings ...)
2026-02-16 21:22 ` [RFC PATCH 06/20] mtd: set flash_node on DT-created partitions Daniel Golle
@ 2026-02-16 21:22 ` Daniel Golle
2026-02-16 21:22 ` [RFC PATCH 08/20] boot: image-loader: add UBI volume backend Daniel Golle
` (18 subsequent siblings)
25 siblings, 0 replies; 88+ messages in thread
From: Daniel Golle @ 2026-02-16 21:22 UTC (permalink / raw)
To: Tom Rini, Simon Glass, Quentin Schulz, Daniel Golle,
Kory Maincent, Mattijs Korpershoek, Martin Schwan, Anshul Dalal,
Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
牛 志宏, Marek Vasut, Heinrich Schuchardt,
Wolfgang Wallner, Frank Wunderlich, David Lechner,
Osama Abdelkader, Mikhail Kshevetskiy, Michael Trimarchi,
Miquel Raynal, Andrew Goodbody, Yegor Yefremov, Mike Looijmans,
Weijie Gao, Alexander Stein, Neil Armstrong, Mayuresh Chitale,
Paul HENRYS, u-boot
Cc: John Crispin, Paul Spooren
Add ubi_part_from_mtd() which attaches UBI to a given struct mtd_info
directly, without going through the name-based lookup that ubi_part()
uses. This is useful for callers that already hold a reference to the
MTD device, e.g. after finding it via its device-tree node.
If the same MTD device is already attached, the function returns
immediately.
Declared in include/ubi_uboot.h alongside ubi_part().
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
cmd/ubi.c | 23 +++++++++++++++++++++++
include/ubi_uboot.h | 1 +
2 files changed, 24 insertions(+)
diff --git a/cmd/ubi.c b/cmd/ubi.c
index ca7d657aa07..9f73c36aaa9 100644
--- a/cmd/ubi.c
+++ b/cmd/ubi.c
@@ -662,6 +662,29 @@ static int ubi_detach(void)
return 0;
}
+int ubi_part_from_mtd(struct mtd_info *mtd)
+{
+ int err;
+
+ if (ubi && ubi->mtd == mtd) {
+ printf("UBI partition '%s' already selected\n", mtd->name);
+ return 0;
+ }
+
+ ubi_detach();
+
+ err = ubi_dev_scan(mtd, NULL);
+ if (err) {
+ printf("UBI init error %d\n", err);
+ printf("Please check, if the correct MTD partition is used (size big enough?)\n");
+ return err;
+ }
+
+ ubi = ubi_devices[0];
+
+ return 0;
+}
+
int ubi_part(char *part_name, const char *vid_header_offset)
{
struct mtd_info *mtd;
diff --git a/include/ubi_uboot.h b/include/ubi_uboot.h
index 92892704d0a..8f3fb007b0e 100644
--- a/include/ubi_uboot.h
+++ b/include/ubi_uboot.h
@@ -48,6 +48,7 @@ extern int ubi_mtd_param_parse(const char *val, struct kernel_param *kp);
extern int ubi_init(void);
extern void ubi_exit(void);
extern int ubi_part(char *part_name, const char *vid_header_offset);
+int ubi_part_from_mtd(struct mtd_info *mtd);
extern int ubi_volume_write(char *volume, void *buf, loff_t offset, size_t size);
extern int ubi_volume_read(char *volume, char *buf, loff_t offset, size_t size);
struct ubi_volume *ubi_find_volume(const char *volume);
--
2.53.0
^ permalink raw reply related [flat|nested] 88+ messages in thread* [RFC PATCH 08/20] boot: image-loader: add UBI volume backend
2026-02-16 21:21 [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading Daniel Golle
` (6 preceding siblings ...)
2026-02-16 21:22 ` [RFC PATCH 07/20] cmd: ubi: add ubi_part_from_mtd() Daniel Golle
@ 2026-02-16 21:22 ` Daniel Golle
2026-02-19 13:09 ` Simon Glass
2026-02-16 21:22 ` [RFC PATCH 09/20] boot: fit: support on-demand loading in fit_image_load() Daniel Golle
` (17 subsequent siblings)
25 siblings, 1 reply; 88+ messages in thread
From: Daniel Golle @ 2026-02-16 21:22 UTC (permalink / raw)
To: Tom Rini, Simon Glass, Quentin Schulz, Daniel Golle,
Kory Maincent, Mattijs Korpershoek, Martin Schwan, Anshul Dalal,
Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
牛 志宏, Marek Vasut, Heinrich Schuchardt,
Wolfgang Wallner, Frank Wunderlich, David Lechner,
Osama Abdelkader, Mikhail Kshevetskiy, Michael Trimarchi,
Miquel Raynal, Andrew Goodbody, Yegor Yefremov, Mike Looijmans,
Weijie Gao, Alexander Stein, Neil Armstrong, Mayuresh Chitale,
Paul HENRYS, u-boot
Cc: John Crispin, Paul Spooren
Add a UBI volume storage backend for the image_loader framework.
image_loader_init_ubi() takes a volume name, ensures the UBI device
is attached (auto-attaching if needed), and installs a .read() callback
wrapping ubi_volume_read().
Auto-attach works by scanning the device tree for the first MTD
partition with compatible = "linux,ubi", then calling ubi_part() on
that partition. The partition name is resolved using the same
precedence as the MTD partition parser: the "label" property first,
then "name", then the node name. Since U-Boot only supports a single
attached UBI device at a time, only the first matching partition is
used. If a UBI device is already attached, the auto-attach step is
skipped.
UBI handles bad-block management and wear leveling internally, so the
read callback is a straightforward passthrough. Note that
ubi_volume_read() returns positive errno values; the wrapper negates
them for the image_loader convention.
Gated by CONFIG_IMAGE_LOADER_UBI (depends on CMD_UBI && IMAGE_LOADER).
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
boot/Kconfig | 8 +++
boot/Makefile | 1 +
boot/image-loader-ubi.c | 112 ++++++++++++++++++++++++++++++++++++++++
include/image-loader.h | 13 +++++
4 files changed, 134 insertions(+)
create mode 100644 boot/image-loader-ubi.c
diff --git a/boot/Kconfig b/boot/Kconfig
index 23848a0f57e..89832014af6 100644
--- a/boot/Kconfig
+++ b/boot/Kconfig
@@ -1203,6 +1203,14 @@ config IMAGE_LOADER_MTD
parallel NAND, etc.) using the image_loader framework.
NAND bad blocks are skipped transparently.
+config IMAGE_LOADER_UBI
+ bool "UBI volume backend for image loader"
+ depends on IMAGE_LOADER && CMD_UBI
+ help
+ Allows loading images from UBI volumes using the image_loader
+ framework. Auto-attaches the UBI device from the device tree
+ if not already attached.
+
config DISTRO_DEFAULTS
bool "(deprecated) Script-based booting of Linux distributions"
select CMDLINE
diff --git a/boot/Makefile b/boot/Makefile
index 1dde16db694..7d1d4a28106 100644
--- a/boot/Makefile
+++ b/boot/Makefile
@@ -76,6 +76,7 @@ obj-$(CONFIG_$(PHASE_)BOOTMETH_ANDROID) += bootmeth_android.o
obj-$(CONFIG_IMAGE_LOADER) += image-loader.o
obj-$(CONFIG_IMAGE_LOADER_BLK) += image-loader-blk.o
obj-$(CONFIG_IMAGE_LOADER_MTD) += image-loader-mtd.o
+obj-$(CONFIG_IMAGE_LOADER_UBI) += image-loader-ubi.o
obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_ABREC) += vbe_abrec.o vbe_common.o
obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_ABREC_FW) += vbe_abrec_fw.o
diff --git a/boot/image-loader-ubi.c b/boot/image-loader-ubi.c
new file mode 100644
index 00000000000..64901a13378
--- /dev/null
+++ b/boot/image-loader-ubi.c
@@ -0,0 +1,112 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * UBI volume backend for image_loader
+ *
+ * Copyright (C) 2026 Daniel Golle <daniel@makrotopia.org>
+ */
+
+#include <dm/ofnode.h>
+#include <image-loader.h>
+#include <log.h>
+#include <malloc.h>
+#include <mtd.h>
+#include <ubi_uboot.h>
+
+struct image_loader_ubi_priv {
+ char *vol_name;
+};
+
+/**
+ * ubi_auto_attach() - attach UBI if not already attached
+ *
+ * If no UBI device is currently attached, walk the device tree for the
+ * first MTD partition node with compatible = "linux,ubi", find the
+ * corresponding MTD device by matching flash_node, and attach UBI to
+ * it via ubi_part_from_mtd().
+ *
+ * Since U-Boot only supports a single attached UBI device at a time,
+ * only the first matching partition is used.
+ *
+ * Return: 0 on success or if already attached, negative errno on failure
+ */
+static int ubi_auto_attach(void)
+{
+ struct mtd_info *mtd;
+ ofnode node;
+
+ /* Already attached? */
+ if (ubi_devices[0])
+ return 0;
+
+ mtd_probe_devices();
+
+ ofnode_for_each_compatible_node(node, "linux,ubi") {
+ mtd_for_each_device(mtd) {
+ if (ofnode_equal(mtd->flash_node, node))
+ goto found;
+ }
+ }
+
+ log_err("image_loader_ubi: no MTD device for \"linux,ubi\" node\n");
+ return -ENODEV;
+
+found:
+ if (ubi_part_from_mtd(mtd)) {
+ log_err("image_loader_ubi: failed to attach UBI on \"%s\"\n",
+ mtd->name);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int image_loader_ubi_read(struct image_loader *ldr, ulong src,
+ ulong size, void *dst)
+{
+ struct image_loader_ubi_priv *priv = ldr->priv;
+ int ret;
+
+ ret = ubi_volume_read(priv->vol_name, dst, src, size);
+ /* ubi_volume_read() returns positive errno values on error */
+ return ret ? -ret : 0;
+}
+
+static void image_loader_ubi_cleanup(struct image_loader *ldr)
+{
+ struct image_loader_ubi_priv *priv = ldr->priv;
+
+ free(priv->vol_name);
+ free(priv);
+}
+
+int image_loader_init_ubi(struct image_loader *ldr, const char *vol_name)
+{
+ struct image_loader_ubi_priv *priv;
+ int ret;
+
+ ret = ubi_auto_attach();
+ if (ret)
+ return ret;
+
+ if (!ubi_find_volume(vol_name)) {
+ log_err("image_loader_ubi: volume \"%s\" not found\n",
+ vol_name);
+ return -ENODEV;
+ }
+
+ priv = malloc(sizeof(*priv));
+ if (!priv)
+ return -ENOMEM;
+
+ priv->vol_name = strdup(vol_name);
+ if (!priv->vol_name) {
+ free(priv);
+ return -ENOMEM;
+ }
+
+ ldr->read = image_loader_ubi_read;
+ ldr->cleanup = image_loader_ubi_cleanup;
+ ldr->priv = priv;
+
+ return 0;
+}
diff --git a/include/image-loader.h b/include/image-loader.h
index 7ccf901d37d..a036e98982f 100644
--- a/include/image-loader.h
+++ b/include/image-loader.h
@@ -172,4 +172,17 @@ int image_loader_init_blk(struct image_loader *ldr, const char *ifname,
*/
int image_loader_init_mtd(struct image_loader *ldr, const char *name);
+/**
+ * image_loader_init_ubi() - Initialise loader for a UBI volume
+ *
+ * Ensures a UBI device is attached (auto-attaching from the device
+ * tree if needed), verifies the named volume exists, then installs a
+ * .read() callback wrapping ubi_volume_read().
+ *
+ * @ldr: The image loader to initialise
+ * @vol_name: UBI volume name
+ * Return: 0 on success, negative errno on failure
+ */
+int image_loader_init_ubi(struct image_loader *ldr, const char *vol_name);
+
#endif /* __IMAGE_LOADER_H */
--
2.53.0
^ permalink raw reply related [flat|nested] 88+ messages in thread* Re: [RFC PATCH 08/20] boot: image-loader: add UBI volume backend
2026-02-16 21:22 ` [RFC PATCH 08/20] boot: image-loader: add UBI volume backend Daniel Golle
@ 2026-02-19 13:09 ` Simon Glass
2026-02-19 16:51 ` Daniel Golle
0 siblings, 1 reply; 88+ messages in thread
From: Simon Glass @ 2026-02-19 13:09 UTC (permalink / raw)
To: Daniel Golle
Cc: Tom Rini, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
Hi Daniel,
On Mon, 16 Feb 2026 at 14:22, Daniel Golle <daniel@makrotopia.org> wrote:
>
> Add a UBI volume storage backend for the image_loader framework.
>
> image_loader_init_ubi() takes a volume name, ensures the UBI device
> is attached (auto-attaching if needed), and installs a .read() callback
> wrapping ubi_volume_read().
>
> Auto-attach works by scanning the device tree for the first MTD
> partition with compatible = "linux,ubi", then calling ubi_part() on
> that partition. The partition name is resolved using the same
> precedence as the MTD partition parser: the "label" property first,
> then "name", then the node name. Since U-Boot only supports a single
> attached UBI device at a time, only the first matching partition is
> used. If a UBI device is already attached, the auto-attach step is
> skipped.
>
> UBI handles bad-block management and wear leveling internally, so the
> read callback is a straightforward passthrough. Note that
> ubi_volume_read() returns positive errno values; the wrapper negates
> them for the image_loader convention.
>
> Gated by CONFIG_IMAGE_LOADER_UBI (depends on CMD_UBI && IMAGE_LOADER).
>
> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> ---
> boot/Kconfig | 8 +++
> boot/Makefile | 1 +
> boot/image-loader-ubi.c | 112 ++++++++++++++++++++++++++++++++++++++++
> include/image-loader.h | 13 +++++
> 4 files changed, 134 insertions(+)
> create mode 100644 boot/image-loader-ubi.c
>
> diff --git a/boot/Kconfig b/boot/Kconfig
> index 23848a0f57e..89832014af6 100644
> --- a/boot/Kconfig
> +++ b/boot/Kconfig
> @@ -1203,6 +1203,14 @@ config IMAGE_LOADER_MTD
> parallel NAND, etc.) using the image_loader framework.
> NAND bad blocks are skipped transparently.
>
> +config IMAGE_LOADER_UBI
> + bool "UBI volume backend for image loader"
> + depends on IMAGE_LOADER && CMD_UBI
> + help
> + Allows loading images from UBI volumes using the image_loader
> + framework. Auto-attaches the UBI device from the device tree
> + if not already attached.
> +
> config DISTRO_DEFAULTS
> bool "(deprecated) Script-based booting of Linux distributions"
> select CMDLINE
> diff --git a/boot/Makefile b/boot/Makefile
> index 1dde16db694..7d1d4a28106 100644
> --- a/boot/Makefile
> +++ b/boot/Makefile
> @@ -76,6 +76,7 @@ obj-$(CONFIG_$(PHASE_)BOOTMETH_ANDROID) += bootmeth_android.o
> obj-$(CONFIG_IMAGE_LOADER) += image-loader.o
> obj-$(CONFIG_IMAGE_LOADER_BLK) += image-loader-blk.o
> obj-$(CONFIG_IMAGE_LOADER_MTD) += image-loader-mtd.o
> +obj-$(CONFIG_IMAGE_LOADER_UBI) += image-loader-ubi.o
>
> obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_ABREC) += vbe_abrec.o vbe_common.o
> obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_ABREC_FW) += vbe_abrec_fw.o
> diff --git a/boot/image-loader-ubi.c b/boot/image-loader-ubi.c
> new file mode 100644
> index 00000000000..64901a13378
> --- /dev/null
> +++ b/boot/image-loader-ubi.c
> @@ -0,0 +1,112 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * UBI volume backend for image_loader
> + *
> + * Copyright (C) 2026 Daniel Golle <daniel@makrotopia.org>
> + */
> +
> +#include <dm/ofnode.h>
> +#include <image-loader.h>
> +#include <log.h>
> +#include <malloc.h>
> +#include <mtd.h>
> +#include <ubi_uboot.h>
> +
> +struct image_loader_ubi_priv {
> + char *vol_name;
> +};
> +
> +/**
> + * ubi_auto_attach() - attach UBI if not already attached
> + *
> + * If no UBI device is currently attached, walk the device tree for the
> + * first MTD partition node with compatible = "linux,ubi", find the
> + * corresponding MTD device by matching flash_node, and attach UBI to
> + * it via ubi_part_from_mtd().
> + *
> + * Since U-Boot only supports a single attached UBI device at a time,
> + * only the first matching partition is used.
> + *
> + * Return: 0 on success or if already attached, negative errno on failure
> + */
> +static int ubi_auto_attach(void)
> +{
> + struct mtd_info *mtd;
> + ofnode node;
> +
> + /* Already attached? */
> + if (ubi_devices[0])
> + return 0;
> +
> + mtd_probe_devices();
This should be handled by your new ubi driver for your uclass.
> +
> + ofnode_for_each_compatible_node(node, "linux,ubi") {
> + mtd_for_each_device(mtd) {
> + if (ofnode_equal(mtd->flash_node, node))
> + goto found;
> + }
> + }
Eek this is really strange. There should be a device so you can use
uclass_first_device(UCLASS_...).
> +
> + log_err("image_loader_ubi: no MTD device for \"linux,ubi\" node\n");
> + return -ENODEV;
> +
> +found:
> + if (ubi_part_from_mtd(mtd)) {
> + log_err("image_loader_ubi: failed to attach UBI on \"%s\"\n",
> + mtd->name);
> + return -EIO;
> + }
> +
> + return 0;
> +}
> +
> +static int image_loader_ubi_read(struct image_loader *ldr, ulong src,
> + ulong size, void *dst)
> +{
> + struct image_loader_ubi_priv *priv = ldr->priv;
> + int ret;
> +
> + ret = ubi_volume_read(priv->vol_name, dst, src, size);
> + /* ubi_volume_read() returns positive errno values on error */
blank line before final return
> + return ret ? -ret : 0;
> +}
> +
> +static void image_loader_ubi_cleanup(struct image_loader *ldr)
> +{
> + struct image_loader_ubi_priv *priv = ldr->priv;
> +
> + free(priv->vol_name);
> + free(priv);
> +}
> +
> +int image_loader_init_ubi(struct image_loader *ldr, const char *vol_name)
> +{
> + struct image_loader_ubi_priv *priv;
> + int ret;
> +
> + ret = ubi_auto_attach();
> + if (ret)
> + return ret;
> +
> + if (!ubi_find_volume(vol_name)) {
> + log_err("image_loader_ubi: volume \"%s\" not found\n",
> + vol_name);
> + return -ENODEV;
> + }
> +
> + priv = malloc(sizeof(*priv));
> + if (!priv)
> + return -ENOMEM;
> +
> + priv->vol_name = strdup(vol_name);
> + if (!priv->vol_name) {
> + free(priv);
> + return -ENOMEM;
> + }
> +
> + ldr->read = image_loader_ubi_read;
> + ldr->cleanup = image_loader_ubi_cleanup;
> + ldr->priv = priv;
> +
> + return 0;
> +}
> diff --git a/include/image-loader.h b/include/image-loader.h
> index 7ccf901d37d..a036e98982f 100644
> --- a/include/image-loader.h
> +++ b/include/image-loader.h
> @@ -172,4 +172,17 @@ int image_loader_init_blk(struct image_loader *ldr, const char *ifname,
> */
> int image_loader_init_mtd(struct image_loader *ldr, const char *name);
>
> +/**
> + * image_loader_init_ubi() - Initialise loader for a UBI volume
> + *
> + * Ensures a UBI device is attached (auto-attaching from the device
> + * tree if needed), verifies the named volume exists, then installs a
> + * .read() callback wrapping ubi_volume_read().
> + *
> + * @ldr: The image loader to initialise
> + * @vol_name: UBI volume name
> + * Return: 0 on success, negative errno on failure
> + */
> +int image_loader_init_ubi(struct image_loader *ldr, const char *vol_name);
> +
> #endif /* __IMAGE_LOADER_H */
> --
> 2.53.0
Regards,
SImon
^ permalink raw reply [flat|nested] 88+ messages in thread* Re: [RFC PATCH 08/20] boot: image-loader: add UBI volume backend
2026-02-19 13:09 ` Simon Glass
@ 2026-02-19 16:51 ` Daniel Golle
2026-02-23 17:51 ` Simon Glass
0 siblings, 1 reply; 88+ messages in thread
From: Daniel Golle @ 2026-02-19 16:51 UTC (permalink / raw)
To: Simon Glass
Cc: Tom Rini, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
On Thu, Feb 19, 2026 at 06:09:27AM -0700, Simon Glass wrote:
> Hi Daniel,
>
> On Mon, 16 Feb 2026 at 14:22, Daniel Golle <daniel@makrotopia.org> wrote:
> >
> > Add a UBI volume storage backend for the image_loader framework.
> >
> > image_loader_init_ubi() takes a volume name, ensures the UBI device
> > is attached (auto-attaching if needed), and installs a .read() callback
> > wrapping ubi_volume_read().
> >
> > Auto-attach works by scanning the device tree for the first MTD
> > partition with compatible = "linux,ubi", then calling ubi_part() on
> > that partition. The partition name is resolved using the same
> > precedence as the MTD partition parser: the "label" property first,
> > then "name", then the node name. Since U-Boot only supports a single
> > attached UBI device at a time, only the first matching partition is
> > used. If a UBI device is already attached, the auto-attach step is
> > skipped.
> >
> > UBI handles bad-block management and wear leveling internally, so the
> > read callback is a straightforward passthrough. Note that
> > ubi_volume_read() returns positive errno values; the wrapper negates
> > them for the image_loader convention.
> >
> > Gated by CONFIG_IMAGE_LOADER_UBI (depends on CMD_UBI && IMAGE_LOADER).
> >
> > Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> > ---
> > boot/Kconfig | 8 +++
> > boot/Makefile | 1 +
> > boot/image-loader-ubi.c | 112 ++++++++++++++++++++++++++++++++++++++++
> > include/image-loader.h | 13 +++++
> > 4 files changed, 134 insertions(+)
> > create mode 100644 boot/image-loader-ubi.c
> >
> > diff --git a/boot/Kconfig b/boot/Kconfig
> > index 23848a0f57e..89832014af6 100644
> > --- a/boot/Kconfig
> > +++ b/boot/Kconfig
> > @@ -1203,6 +1203,14 @@ config IMAGE_LOADER_MTD
> > parallel NAND, etc.) using the image_loader framework.
> > NAND bad blocks are skipped transparently.
> >
> > +config IMAGE_LOADER_UBI
> > + bool "UBI volume backend for image loader"
> > + depends on IMAGE_LOADER && CMD_UBI
> > + help
> > + Allows loading images from UBI volumes using the image_loader
> > + framework. Auto-attaches the UBI device from the device tree
> > + if not already attached.
> > +
> > config DISTRO_DEFAULTS
> > bool "(deprecated) Script-based booting of Linux distributions"
> > select CMDLINE
> > diff --git a/boot/Makefile b/boot/Makefile
> > index 1dde16db694..7d1d4a28106 100644
> > --- a/boot/Makefile
> > +++ b/boot/Makefile
> > @@ -76,6 +76,7 @@ obj-$(CONFIG_$(PHASE_)BOOTMETH_ANDROID) += bootmeth_android.o
> > obj-$(CONFIG_IMAGE_LOADER) += image-loader.o
> > obj-$(CONFIG_IMAGE_LOADER_BLK) += image-loader-blk.o
> > obj-$(CONFIG_IMAGE_LOADER_MTD) += image-loader-mtd.o
> > +obj-$(CONFIG_IMAGE_LOADER_UBI) += image-loader-ubi.o
> >
> > obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_ABREC) += vbe_abrec.o vbe_common.o
> > obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_ABREC_FW) += vbe_abrec_fw.o
> > diff --git a/boot/image-loader-ubi.c b/boot/image-loader-ubi.c
> > new file mode 100644
> > index 00000000000..64901a13378
> > --- /dev/null
> > +++ b/boot/image-loader-ubi.c
> > @@ -0,0 +1,112 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * UBI volume backend for image_loader
> > + *
> > + * Copyright (C) 2026 Daniel Golle <daniel@makrotopia.org>
> > + */
> > +
> > +#include <dm/ofnode.h>
> > +#include <image-loader.h>
> > +#include <log.h>
> > +#include <malloc.h>
> > +#include <mtd.h>
> > +#include <ubi_uboot.h>
> > +
> > +struct image_loader_ubi_priv {
> > + char *vol_name;
> > +};
> > +
> > +/**
> > + * ubi_auto_attach() - attach UBI if not already attached
> > + *
> > + * If no UBI device is currently attached, walk the device tree for the
> > + * first MTD partition node with compatible = "linux,ubi", find the
> > + * corresponding MTD device by matching flash_node, and attach UBI to
> > + * it via ubi_part_from_mtd().
> > + *
> > + * Since U-Boot only supports a single attached UBI device at a time,
> > + * only the first matching partition is used.
> > + *
> > + * Return: 0 on success or if already attached, negative errno on failure
> > + */
> > +static int ubi_auto_attach(void)
> > +{
> > + struct mtd_info *mtd;
> > + ofnode node;
> > +
> > + /* Already attached? */
> > + if (ubi_devices[0])
> > + return 0;
> > +
> > + mtd_probe_devices();
>
> This should be handled by your new ubi driver for your uclass.
>
> > +
> > + ofnode_for_each_compatible_node(node, "linux,ubi") {
> > + mtd_for_each_device(mtd) {
> > + if (ofnode_equal(mtd->flash_node, node))
> > + goto found;
> > + }
> > + }
>
> Eek this is really strange. There should be a device so you can use
> uclass_first_device(UCLASS_...).
I carefully considered turning UBI devices into a UCLASS, but it would
be a huge major rewrite of how UBI works in U-Boot, and while that would
be a good thing to do, I consider far beyond the scope of supporting a
boot method for OpenWrt.
Or did you mean the to-be-create UCLASS_IMAGE_MAPPER?
^ permalink raw reply [flat|nested] 88+ messages in thread* Re: [RFC PATCH 08/20] boot: image-loader: add UBI volume backend
2026-02-19 16:51 ` Daniel Golle
@ 2026-02-23 17:51 ` Simon Glass
2026-02-23 19:24 ` Daniel Golle
0 siblings, 1 reply; 88+ messages in thread
From: Simon Glass @ 2026-02-23 17:51 UTC (permalink / raw)
To: Daniel Golle
Cc: Tom Rini, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
Hi Daniel,
On Thu, 19 Feb 2026 at 09:51, Daniel Golle <daniel@makrotopia.org> wrote:
>
> On Thu, Feb 19, 2026 at 06:09:27AM -0700, Simon Glass wrote:
> > Hi Daniel,
> >
> > On Mon, 16 Feb 2026 at 14:22, Daniel Golle <daniel@makrotopia.org> wrote:
> > >
> > > Add a UBI volume storage backend for the image_loader framework.
> > >
> > > image_loader_init_ubi() takes a volume name, ensures the UBI device
> > > is attached (auto-attaching if needed), and installs a .read() callback
> > > wrapping ubi_volume_read().
> > >
> > > Auto-attach works by scanning the device tree for the first MTD
> > > partition with compatible = "linux,ubi", then calling ubi_part() on
> > > that partition. The partition name is resolved using the same
> > > precedence as the MTD partition parser: the "label" property first,
> > > then "name", then the node name. Since U-Boot only supports a single
> > > attached UBI device at a time, only the first matching partition is
> > > used. If a UBI device is already attached, the auto-attach step is
> > > skipped.
> > >
> > > UBI handles bad-block management and wear leveling internally, so the
> > > read callback is a straightforward passthrough. Note that
> > > ubi_volume_read() returns positive errno values; the wrapper negates
> > > them for the image_loader convention.
> > >
> > > Gated by CONFIG_IMAGE_LOADER_UBI (depends on CMD_UBI && IMAGE_LOADER).
> > >
> > > Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> > > ---
> > > boot/Kconfig | 8 +++
> > > boot/Makefile | 1 +
> > > boot/image-loader-ubi.c | 112 ++++++++++++++++++++++++++++++++++++++++
> > > include/image-loader.h | 13 +++++
> > > 4 files changed, 134 insertions(+)
> > > create mode 100644 boot/image-loader-ubi.c
> > >
> > > diff --git a/boot/Kconfig b/boot/Kconfig
> > > index 23848a0f57e..89832014af6 100644
> > > --- a/boot/Kconfig
> > > +++ b/boot/Kconfig
> > > @@ -1203,6 +1203,14 @@ config IMAGE_LOADER_MTD
> > > parallel NAND, etc.) using the image_loader framework.
> > > NAND bad blocks are skipped transparently.
> > >
> > > +config IMAGE_LOADER_UBI
> > > + bool "UBI volume backend for image loader"
> > > + depends on IMAGE_LOADER && CMD_UBI
> > > + help
> > > + Allows loading images from UBI volumes using the image_loader
> > > + framework. Auto-attaches the UBI device from the device tree
> > > + if not already attached.
> > > +
> > > config DISTRO_DEFAULTS
> > > bool "(deprecated) Script-based booting of Linux distributions"
> > > select CMDLINE
> > > diff --git a/boot/Makefile b/boot/Makefile
> > > index 1dde16db694..7d1d4a28106 100644
> > > --- a/boot/Makefile
> > > +++ b/boot/Makefile
> > > @@ -76,6 +76,7 @@ obj-$(CONFIG_$(PHASE_)BOOTMETH_ANDROID) += bootmeth_android.o
> > > obj-$(CONFIG_IMAGE_LOADER) += image-loader.o
> > > obj-$(CONFIG_IMAGE_LOADER_BLK) += image-loader-blk.o
> > > obj-$(CONFIG_IMAGE_LOADER_MTD) += image-loader-mtd.o
> > > +obj-$(CONFIG_IMAGE_LOADER_UBI) += image-loader-ubi.o
> > >
> > > obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_ABREC) += vbe_abrec.o vbe_common.o
> > > obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_ABREC_FW) += vbe_abrec_fw.o
> > > diff --git a/boot/image-loader-ubi.c b/boot/image-loader-ubi.c
> > > new file mode 100644
> > > index 00000000000..64901a13378
> > > --- /dev/null
> > > +++ b/boot/image-loader-ubi.c
> > > @@ -0,0 +1,112 @@
> > > +// SPDX-License-Identifier: GPL-2.0+
> > > +/*
> > > + * UBI volume backend for image_loader
> > > + *
> > > + * Copyright (C) 2026 Daniel Golle <daniel@makrotopia.org>
> > > + */
> > > +
> > > +#include <dm/ofnode.h>
> > > +#include <image-loader.h>
> > > +#include <log.h>
> > > +#include <malloc.h>
> > > +#include <mtd.h>
> > > +#include <ubi_uboot.h>
> > > +
> > > +struct image_loader_ubi_priv {
> > > + char *vol_name;
> > > +};
> > > +
> > > +/**
> > > + * ubi_auto_attach() - attach UBI if not already attached
> > > + *
> > > + * If no UBI device is currently attached, walk the device tree for the
> > > + * first MTD partition node with compatible = "linux,ubi", find the
> > > + * corresponding MTD device by matching flash_node, and attach UBI to
> > > + * it via ubi_part_from_mtd().
> > > + *
> > > + * Since U-Boot only supports a single attached UBI device at a time,
> > > + * only the first matching partition is used.
> > > + *
> > > + * Return: 0 on success or if already attached, negative errno on failure
> > > + */
> > > +static int ubi_auto_attach(void)
> > > +{
> > > + struct mtd_info *mtd;
> > > + ofnode node;
> > > +
> > > + /* Already attached? */
> > > + if (ubi_devices[0])
> > > + return 0;
> > > +
> > > + mtd_probe_devices();
> >
> > This should be handled by your new ubi driver for your uclass.
> >
> > > +
> > > + ofnode_for_each_compatible_node(node, "linux,ubi") {
> > > + mtd_for_each_device(mtd) {
> > > + if (ofnode_equal(mtd->flash_node, node))
> > > + goto found;
> > > + }
> > > + }
> >
> > Eek this is really strange. There should be a device so you can use
> > uclass_first_device(UCLASS_...).
>
> I carefully considered turning UBI devices into a UCLASS, but it would
> be a huge major rewrite of how UBI works in U-Boot, and while that would
> be a good thing to do, I consider far beyond the scope of supporting a
> boot method for OpenWrt.
Oh, I just assumed it supports driver model. But surely it must have a
UCLASS_BLK device?
>
> Or did you mean the to-be-create UCLASS_IMAGE_MAPPER?
No I was talking about ubi.
Regards,
Simon
^ permalink raw reply [flat|nested] 88+ messages in thread* Re: [RFC PATCH 08/20] boot: image-loader: add UBI volume backend
2026-02-23 17:51 ` Simon Glass
@ 2026-02-23 19:24 ` Daniel Golle
2026-02-23 19:30 ` Mikhail Kshevetskiy
0 siblings, 1 reply; 88+ messages in thread
From: Daniel Golle @ 2026-02-23 19:24 UTC (permalink / raw)
To: Simon Glass
Cc: Tom Rini, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
On Mon, Feb 23, 2026 at 10:51:09AM -0700, Simon Glass wrote:
> Hi Daniel,
>
> On Thu, 19 Feb 2026 at 09:51, Daniel Golle <daniel@makrotopia.org> wrote:
> >
> > On Thu, Feb 19, 2026 at 06:09:27AM -0700, Simon Glass wrote:
> > > Hi Daniel,
> > >
> > > On Mon, 16 Feb 2026 at 14:22, Daniel Golle <daniel@makrotopia.org> wrote:
> > > >
> > > > Add a UBI volume storage backend for the image_loader framework.
> > > >
> > > > image_loader_init_ubi() takes a volume name, ensures the UBI device
> > > > is attached (auto-attaching if needed), and installs a .read() callback
> > > > wrapping ubi_volume_read().
> > > >
> > > > Auto-attach works by scanning the device tree for the first MTD
> > > > partition with compatible = "linux,ubi", then calling ubi_part() on
> > > > that partition. The partition name is resolved using the same
> > > > precedence as the MTD partition parser: the "label" property first,
> > > > then "name", then the node name. Since U-Boot only supports a single
> > > > attached UBI device at a time, only the first matching partition is
> > > > used. If a UBI device is already attached, the auto-attach step is
> > > > skipped.
> > > >
> > > > UBI handles bad-block management and wear leveling internally, so the
> > > > read callback is a straightforward passthrough. Note that
> > > > ubi_volume_read() returns positive errno values; the wrapper negates
> > > > them for the image_loader convention.
> > > >
> > > > Gated by CONFIG_IMAGE_LOADER_UBI (depends on CMD_UBI && IMAGE_LOADER).
> > > >
> > > > Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> > > > ---
> > > > boot/Kconfig | 8 +++
> > > > boot/Makefile | 1 +
> > > > boot/image-loader-ubi.c | 112 ++++++++++++++++++++++++++++++++++++++++
> > > > include/image-loader.h | 13 +++++
> > > > 4 files changed, 134 insertions(+)
> > > > create mode 100644 boot/image-loader-ubi.c
> > > >
> > > > diff --git a/boot/Kconfig b/boot/Kconfig
> > > > index 23848a0f57e..89832014af6 100644
> > > > --- a/boot/Kconfig
> > > > +++ b/boot/Kconfig
> > > > @@ -1203,6 +1203,14 @@ config IMAGE_LOADER_MTD
> > > > parallel NAND, etc.) using the image_loader framework.
> > > > NAND bad blocks are skipped transparently.
> > > >
> > > > +config IMAGE_LOADER_UBI
> > > > + bool "UBI volume backend for image loader"
> > > > + depends on IMAGE_LOADER && CMD_UBI
> > > > + help
> > > > + Allows loading images from UBI volumes using the image_loader
> > > > + framework. Auto-attaches the UBI device from the device tree
> > > > + if not already attached.
> > > > +
> > > > config DISTRO_DEFAULTS
> > > > bool "(deprecated) Script-based booting of Linux distributions"
> > > > select CMDLINE
> > > > diff --git a/boot/Makefile b/boot/Makefile
> > > > index 1dde16db694..7d1d4a28106 100644
> > > > --- a/boot/Makefile
> > > > +++ b/boot/Makefile
> > > > @@ -76,6 +76,7 @@ obj-$(CONFIG_$(PHASE_)BOOTMETH_ANDROID) += bootmeth_android.o
> > > > obj-$(CONFIG_IMAGE_LOADER) += image-loader.o
> > > > obj-$(CONFIG_IMAGE_LOADER_BLK) += image-loader-blk.o
> > > > obj-$(CONFIG_IMAGE_LOADER_MTD) += image-loader-mtd.o
> > > > +obj-$(CONFIG_IMAGE_LOADER_UBI) += image-loader-ubi.o
> > > >
> > > > obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_ABREC) += vbe_abrec.o vbe_common.o
> > > > obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_ABREC_FW) += vbe_abrec_fw.o
> > > > diff --git a/boot/image-loader-ubi.c b/boot/image-loader-ubi.c
> > > > new file mode 100644
> > > > index 00000000000..64901a13378
> > > > --- /dev/null
> > > > +++ b/boot/image-loader-ubi.c
> > > > @@ -0,0 +1,112 @@
> > > > +// SPDX-License-Identifier: GPL-2.0+
> > > > +/*
> > > > + * UBI volume backend for image_loader
> > > > + *
> > > > + * Copyright (C) 2026 Daniel Golle <daniel@makrotopia.org>
> > > > + */
> > > > +
> > > > +#include <dm/ofnode.h>
> > > > +#include <image-loader.h>
> > > > +#include <log.h>
> > > > +#include <malloc.h>
> > > > +#include <mtd.h>
> > > > +#include <ubi_uboot.h>
> > > > +
> > > > +struct image_loader_ubi_priv {
> > > > + char *vol_name;
> > > > +};
> > > > +
> > > > +/**
> > > > + * ubi_auto_attach() - attach UBI if not already attached
> > > > + *
> > > > + * If no UBI device is currently attached, walk the device tree for the
> > > > + * first MTD partition node with compatible = "linux,ubi", find the
> > > > + * corresponding MTD device by matching flash_node, and attach UBI to
> > > > + * it via ubi_part_from_mtd().
> > > > + *
> > > > + * Since U-Boot only supports a single attached UBI device at a time,
> > > > + * only the first matching partition is used.
> > > > + *
> > > > + * Return: 0 on success or if already attached, negative errno on failure
> > > > + */
> > > > +static int ubi_auto_attach(void)
> > > > +{
> > > > + struct mtd_info *mtd;
> > > > + ofnode node;
> > > > +
> > > > + /* Already attached? */
> > > > + if (ubi_devices[0])
> > > > + return 0;
> > > > +
> > > > + mtd_probe_devices();
> > >
> > > This should be handled by your new ubi driver for your uclass.
> > >
> > > > +
> > > > + ofnode_for_each_compatible_node(node, "linux,ubi") {
> > > > + mtd_for_each_device(mtd) {
> > > > + if (ofnode_equal(mtd->flash_node, node))
> > > > + goto found;
> > > > + }
> > > > + }
> > >
> > > Eek this is really strange. There should be a device so you can use
> > > uclass_first_device(UCLASS_...).
> >
> > I carefully considered turning UBI devices into a UCLASS, but it would
> > be a huge major rewrite of how UBI works in U-Boot, and while that would
> > be a good thing to do, I consider far beyond the scope of supporting a
> > boot method for OpenWrt.
>
> Oh, I just assumed it supports driver model. But surely it must have a
> UCLASS_BLK device?
Sadly no. Neither UBI devices nor UBI volumes are block storage devices.
Every UBI device does have a UCLASS_MTD parent device, but that
obviously also doesn't uniquely identify the partition on the flash chip
which is used as UBI device -- there can be (and sometimes are, for
dual-boot/redundancy reasons) multiple UBI devices in different
partitions on the same flash.
What I ended up doing now is to move more of the UBI detection and
enumeration logic into ubi_bootdev.c and have imagemap-ubi.c rely
on getting the MTD device and partition index from there.
>
> >
> > Or did you mean the to-be-create UCLASS_IMAGE_MAPPER?
>
> No I was talking about ubi.
>
> Regards,
> Simon
^ permalink raw reply [flat|nested] 88+ messages in thread* Re: [RFC PATCH 08/20] boot: image-loader: add UBI volume backend
2026-02-23 19:24 ` Daniel Golle
@ 2026-02-23 19:30 ` Mikhail Kshevetskiy
2026-02-23 19:32 ` Mikhail Kshevetskiy
2026-02-23 20:06 ` Daniel Golle
0 siblings, 2 replies; 88+ messages in thread
From: Mikhail Kshevetskiy @ 2026-02-23 19:30 UTC (permalink / raw)
To: Daniel Golle, Simon Glass
Cc: Tom Rini, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Michael Trimarchi, Miquel Raynal,
Andrew Goodbody, Yegor Yefremov, Mike Looijmans, Weijie Gao,
Alexander Stein, Neil Armstrong, Mayuresh Chitale, Paul HENRYS,
u-boot, John Crispin, Paul Spooren
On 2/23/26 22:24, Daniel Golle wrote:
> On Mon, Feb 23, 2026 at 10:51:09AM -0700, Simon Glass wrote:
>> Hi Daniel,
>>
>> On Thu, 19 Feb 2026 at 09:51, Daniel Golle <daniel@makrotopia.org> wrote:
>>> On Thu, Feb 19, 2026 at 06:09:27AM -0700, Simon Glass wrote:
>>>> Hi Daniel,
>>>>
>>>> On Mon, 16 Feb 2026 at 14:22, Daniel Golle <daniel@makrotopia.org> wrote:
>>>>> Add a UBI volume storage backend for the image_loader framework.
>>>>>
>>>>> image_loader_init_ubi() takes a volume name, ensures the UBI device
>>>>> is attached (auto-attaching if needed), and installs a .read() callback
>>>>> wrapping ubi_volume_read().
>>>>>
>>>>> Auto-attach works by scanning the device tree for the first MTD
>>>>> partition with compatible = "linux,ubi", then calling ubi_part() on
>>>>> that partition. The partition name is resolved using the same
>>>>> precedence as the MTD partition parser: the "label" property first,
>>>>> then "name", then the node name. Since U-Boot only supports a single
>>>>> attached UBI device at a time, only the first matching partition is
>>>>> used. If a UBI device is already attached, the auto-attach step is
>>>>> skipped.
>>>>>
>>>>> UBI handles bad-block management and wear leveling internally, so the
>>>>> read callback is a straightforward passthrough. Note that
>>>>> ubi_volume_read() returns positive errno values; the wrapper negates
>>>>> them for the image_loader convention.
>>>>>
>>>>> Gated by CONFIG_IMAGE_LOADER_UBI (depends on CMD_UBI && IMAGE_LOADER).
>>>>>
>>>>> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
>>>>> ---
>>>>> boot/Kconfig | 8 +++
>>>>> boot/Makefile | 1 +
>>>>> boot/image-loader-ubi.c | 112 ++++++++++++++++++++++++++++++++++++++++
>>>>> include/image-loader.h | 13 +++++
>>>>> 4 files changed, 134 insertions(+)
>>>>> create mode 100644 boot/image-loader-ubi.c
>>>>>
>>>>> diff --git a/boot/Kconfig b/boot/Kconfig
>>>>> index 23848a0f57e..89832014af6 100644
>>>>> --- a/boot/Kconfig
>>>>> +++ b/boot/Kconfig
>>>>> @@ -1203,6 +1203,14 @@ config IMAGE_LOADER_MTD
>>>>> parallel NAND, etc.) using the image_loader framework.
>>>>> NAND bad blocks are skipped transparently.
>>>>>
>>>>> +config IMAGE_LOADER_UBI
>>>>> + bool "UBI volume backend for image loader"
>>>>> + depends on IMAGE_LOADER && CMD_UBI
>>>>> + help
>>>>> + Allows loading images from UBI volumes using the image_loader
>>>>> + framework. Auto-attaches the UBI device from the device tree
>>>>> + if not already attached.
>>>>> +
>>>>> config DISTRO_DEFAULTS
>>>>> bool "(deprecated) Script-based booting of Linux distributions"
>>>>> select CMDLINE
>>>>> diff --git a/boot/Makefile b/boot/Makefile
>>>>> index 1dde16db694..7d1d4a28106 100644
>>>>> --- a/boot/Makefile
>>>>> +++ b/boot/Makefile
>>>>> @@ -76,6 +76,7 @@ obj-$(CONFIG_$(PHASE_)BOOTMETH_ANDROID) += bootmeth_android.o
>>>>> obj-$(CONFIG_IMAGE_LOADER) += image-loader.o
>>>>> obj-$(CONFIG_IMAGE_LOADER_BLK) += image-loader-blk.o
>>>>> obj-$(CONFIG_IMAGE_LOADER_MTD) += image-loader-mtd.o
>>>>> +obj-$(CONFIG_IMAGE_LOADER_UBI) += image-loader-ubi.o
>>>>>
>>>>> obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_ABREC) += vbe_abrec.o vbe_common.o
>>>>> obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_ABREC_FW) += vbe_abrec_fw.o
>>>>> diff --git a/boot/image-loader-ubi.c b/boot/image-loader-ubi.c
>>>>> new file mode 100644
>>>>> index 00000000000..64901a13378
>>>>> --- /dev/null
>>>>> +++ b/boot/image-loader-ubi.c
>>>>> @@ -0,0 +1,112 @@
>>>>> +// SPDX-License-Identifier: GPL-2.0+
>>>>> +/*
>>>>> + * UBI volume backend for image_loader
>>>>> + *
>>>>> + * Copyright (C) 2026 Daniel Golle <daniel@makrotopia.org>
>>>>> + */
>>>>> +
>>>>> +#include <dm/ofnode.h>
>>>>> +#include <image-loader.h>
>>>>> +#include <log.h>
>>>>> +#include <malloc.h>
>>>>> +#include <mtd.h>
>>>>> +#include <ubi_uboot.h>
>>>>> +
>>>>> +struct image_loader_ubi_priv {
>>>>> + char *vol_name;
>>>>> +};
>>>>> +
>>>>> +/**
>>>>> + * ubi_auto_attach() - attach UBI if not already attached
>>>>> + *
>>>>> + * If no UBI device is currently attached, walk the device tree for the
>>>>> + * first MTD partition node with compatible = "linux,ubi", find the
>>>>> + * corresponding MTD device by matching flash_node, and attach UBI to
>>>>> + * it via ubi_part_from_mtd().
>>>>> + *
>>>>> + * Since U-Boot only supports a single attached UBI device at a time,
>>>>> + * only the first matching partition is used.
>>>>> + *
>>>>> + * Return: 0 on success or if already attached, negative errno on failure
>>>>> + */
>>>>> +static int ubi_auto_attach(void)
>>>>> +{
>>>>> + struct mtd_info *mtd;
>>>>> + ofnode node;
>>>>> +
>>>>> + /* Already attached? */
>>>>> + if (ubi_devices[0])
>>>>> + return 0;
>>>>> +
>>>>> + mtd_probe_devices();
>>>> This should be handled by your new ubi driver for your uclass.
>>>>
>>>>> +
>>>>> + ofnode_for_each_compatible_node(node, "linux,ubi") {
>>>>> + mtd_for_each_device(mtd) {
>>>>> + if (ofnode_equal(mtd->flash_node, node))
>>>>> + goto found;
>>>>> + }
>>>>> + }
>>>> Eek this is really strange. There should be a device so you can use
>>>> uclass_first_device(UCLASS_...).
>>> I carefully considered turning UBI devices into a UCLASS, but it would
>>> be a huge major rewrite of how UBI works in U-Boot, and while that would
>>> be a good thing to do, I consider far beyond the scope of supporting a
>>> boot method for OpenWrt.
>> Oh, I just assumed it supports driver model. But surely it must have a
>> UCLASS_BLK device?
> Sadly no. Neither UBI devices nor UBI volumes are block storage devices.
>
> Every UBI device does have a UCLASS_MTD parent device, but that
> obviously also doesn't uniquely identify the partition on the flash chip
> which is used as UBI device -- there can be (and sometimes are, for
> dual-boot/redundancy reasons) multiple UBI devices in different
> partitions on the same flash.
>
> What I ended up doing now is to move more of the UBI detection and
> enumeration logic into ubi_bootdev.c and have imagemap-ubi.c rely
> on getting the MTD device and partition index from there.
There are UBI based block storage emulation, see CONFIG_UBI_BLOCK
commits:
* 9daad11ad178646c288aca3615a7ba1e6039aed3 ("drivers: introduce UBI
block abstraction")
* aa5b67ce226267440e64fadc57d3a21e5842027c ("disk: support UBI partitions")
>>> Or did you mean the to-be-create UCLASS_IMAGE_MAPPER?
>> No I was talking about ubi.
>>
>> Regards,
>> Simon
^ permalink raw reply [flat|nested] 88+ messages in thread* Re: [RFC PATCH 08/20] boot: image-loader: add UBI volume backend
2026-02-23 19:30 ` Mikhail Kshevetskiy
@ 2026-02-23 19:32 ` Mikhail Kshevetskiy
2026-02-24 0:12 ` Daniel Golle
2026-02-23 20:06 ` Daniel Golle
1 sibling, 1 reply; 88+ messages in thread
From: Mikhail Kshevetskiy @ 2026-02-23 19:32 UTC (permalink / raw)
To: Daniel Golle, Simon Glass
Cc: Tom Rini, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Michael Trimarchi, Miquel Raynal,
Andrew Goodbody, Yegor Yefremov, Mike Looijmans, Weijie Gao,
Alexander Stein, Neil Armstrong, Mayuresh Chitale, Paul HENRYS,
u-boot, John Crispin, Paul Spooren
On 2/23/26 22:30, Mikhail Kshevetskiy wrote:
> On 2/23/26 22:24, Daniel Golle wrote:
>> On Mon, Feb 23, 2026 at 10:51:09AM -0700, Simon Glass wrote:
>>> Hi Daniel,
>>>
>>> On Thu, 19 Feb 2026 at 09:51, Daniel Golle <daniel@makrotopia.org> wrote:
>>>> On Thu, Feb 19, 2026 at 06:09:27AM -0700, Simon Glass wrote:
>>>>> Hi Daniel,
>>>>>
>>>>> On Mon, 16 Feb 2026 at 14:22, Daniel Golle <daniel@makrotopia.org> wrote:
>>>>>> Add a UBI volume storage backend for the image_loader framework.
>>>>>>
>>>>>> image_loader_init_ubi() takes a volume name, ensures the UBI device
>>>>>> is attached (auto-attaching if needed), and installs a .read() callback
>>>>>> wrapping ubi_volume_read().
>>>>>>
>>>>>> Auto-attach works by scanning the device tree for the first MTD
>>>>>> partition with compatible = "linux,ubi", then calling ubi_part() on
>>>>>> that partition. The partition name is resolved using the same
>>>>>> precedence as the MTD partition parser: the "label" property first,
>>>>>> then "name", then the node name. Since U-Boot only supports a single
>>>>>> attached UBI device at a time, only the first matching partition is
>>>>>> used. If a UBI device is already attached, the auto-attach step is
>>>>>> skipped.
>>>>>>
>>>>>> UBI handles bad-block management and wear leveling internally, so the
>>>>>> read callback is a straightforward passthrough. Note that
>>>>>> ubi_volume_read() returns positive errno values; the wrapper negates
>>>>>> them for the image_loader convention.
>>>>>>
>>>>>> Gated by CONFIG_IMAGE_LOADER_UBI (depends on CMD_UBI && IMAGE_LOADER).
>>>>>>
>>>>>> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
>>>>>> ---
>>>>>> boot/Kconfig | 8 +++
>>>>>> boot/Makefile | 1 +
>>>>>> boot/image-loader-ubi.c | 112 ++++++++++++++++++++++++++++++++++++++++
>>>>>> include/image-loader.h | 13 +++++
>>>>>> 4 files changed, 134 insertions(+)
>>>>>> create mode 100644 boot/image-loader-ubi.c
>>>>>>
>>>>>> diff --git a/boot/Kconfig b/boot/Kconfig
>>>>>> index 23848a0f57e..89832014af6 100644
>>>>>> --- a/boot/Kconfig
>>>>>> +++ b/boot/Kconfig
>>>>>> @@ -1203,6 +1203,14 @@ config IMAGE_LOADER_MTD
>>>>>> parallel NAND, etc.) using the image_loader framework.
>>>>>> NAND bad blocks are skipped transparently.
>>>>>>
>>>>>> +config IMAGE_LOADER_UBI
>>>>>> + bool "UBI volume backend for image loader"
>>>>>> + depends on IMAGE_LOADER && CMD_UBI
>>>>>> + help
>>>>>> + Allows loading images from UBI volumes using the image_loader
>>>>>> + framework. Auto-attaches the UBI device from the device tree
>>>>>> + if not already attached.
>>>>>> +
>>>>>> config DISTRO_DEFAULTS
>>>>>> bool "(deprecated) Script-based booting of Linux distributions"
>>>>>> select CMDLINE
>>>>>> diff --git a/boot/Makefile b/boot/Makefile
>>>>>> index 1dde16db694..7d1d4a28106 100644
>>>>>> --- a/boot/Makefile
>>>>>> +++ b/boot/Makefile
>>>>>> @@ -76,6 +76,7 @@ obj-$(CONFIG_$(PHASE_)BOOTMETH_ANDROID) += bootmeth_android.o
>>>>>> obj-$(CONFIG_IMAGE_LOADER) += image-loader.o
>>>>>> obj-$(CONFIG_IMAGE_LOADER_BLK) += image-loader-blk.o
>>>>>> obj-$(CONFIG_IMAGE_LOADER_MTD) += image-loader-mtd.o
>>>>>> +obj-$(CONFIG_IMAGE_LOADER_UBI) += image-loader-ubi.o
>>>>>>
>>>>>> obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_ABREC) += vbe_abrec.o vbe_common.o
>>>>>> obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_ABREC_FW) += vbe_abrec_fw.o
>>>>>> diff --git a/boot/image-loader-ubi.c b/boot/image-loader-ubi.c
>>>>>> new file mode 100644
>>>>>> index 00000000000..64901a13378
>>>>>> --- /dev/null
>>>>>> +++ b/boot/image-loader-ubi.c
>>>>>> @@ -0,0 +1,112 @@
>>>>>> +// SPDX-License-Identifier: GPL-2.0+
>>>>>> +/*
>>>>>> + * UBI volume backend for image_loader
>>>>>> + *
>>>>>> + * Copyright (C) 2026 Daniel Golle <daniel@makrotopia.org>
>>>>>> + */
>>>>>> +
>>>>>> +#include <dm/ofnode.h>
>>>>>> +#include <image-loader.h>
>>>>>> +#include <log.h>
>>>>>> +#include <malloc.h>
>>>>>> +#include <mtd.h>
>>>>>> +#include <ubi_uboot.h>
>>>>>> +
>>>>>> +struct image_loader_ubi_priv {
>>>>>> + char *vol_name;
>>>>>> +};
>>>>>> +
>>>>>> +/**
>>>>>> + * ubi_auto_attach() - attach UBI if not already attached
>>>>>> + *
>>>>>> + * If no UBI device is currently attached, walk the device tree for the
>>>>>> + * first MTD partition node with compatible = "linux,ubi", find the
>>>>>> + * corresponding MTD device by matching flash_node, and attach UBI to
>>>>>> + * it via ubi_part_from_mtd().
>>>>>> + *
>>>>>> + * Since U-Boot only supports a single attached UBI device at a time,
>>>>>> + * only the first matching partition is used.
>>>>>> + *
>>>>>> + * Return: 0 on success or if already attached, negative errno on failure
>>>>>> + */
>>>>>> +static int ubi_auto_attach(void)
>>>>>> +{
>>>>>> + struct mtd_info *mtd;
>>>>>> + ofnode node;
>>>>>> +
>>>>>> + /* Already attached? */
>>>>>> + if (ubi_devices[0])
>>>>>> + return 0;
>>>>>> +
>>>>>> + mtd_probe_devices();
>>>>> This should be handled by your new ubi driver for your uclass.
>>>>>
>>>>>> +
>>>>>> + ofnode_for_each_compatible_node(node, "linux,ubi") {
>>>>>> + mtd_for_each_device(mtd) {
>>>>>> + if (ofnode_equal(mtd->flash_node, node))
>>>>>> + goto found;
>>>>>> + }
>>>>>> + }
>>>>> Eek this is really strange. There should be a device so you can use
>>>>> uclass_first_device(UCLASS_...).
>>>> I carefully considered turning UBI devices into a UCLASS, but it would
>>>> be a huge major rewrite of how UBI works in U-Boot, and while that would
>>>> be a good thing to do, I consider far beyond the scope of supporting a
>>>> boot method for OpenWrt.
>>> Oh, I just assumed it supports driver model. But surely it must have a
>>> UCLASS_BLK device?
>> Sadly no. Neither UBI devices nor UBI volumes are block storage devices.
>>
>> Every UBI device does have a UCLASS_MTD parent device, but that
>> obviously also doesn't uniquely identify the partition on the flash chip
>> which is used as UBI device -- there can be (and sometimes are, for
>> dual-boot/redundancy reasons) multiple UBI devices in different
>> partitions on the same flash.
>>
>> What I ended up doing now is to move more of the UBI detection and
>> enumeration logic into ubi_bootdev.c and have imagemap-ubi.c rely
>> on getting the MTD device and partition index from there.
> There are UBI based block storage emulation, see CONFIG_UBI_BLOCK
> commits:
> * 9daad11ad178646c288aca3615a7ba1e6039aed3 ("drivers: introduce UBI
> block abstraction")
> * aa5b67ce226267440e64fadc57d3a21e5842027c ("disk: support UBI partitions")
>
There is the block storage abstraction for mtd devices as well.
>
>>>> Or did you mean the to-be-create UCLASS_IMAGE_MAPPER?
>>> No I was talking about ubi.
>>>
>>> Regards,
>>> Simon
^ permalink raw reply [flat|nested] 88+ messages in thread* Re: [RFC PATCH 08/20] boot: image-loader: add UBI volume backend
2026-02-23 19:32 ` Mikhail Kshevetskiy
@ 2026-02-24 0:12 ` Daniel Golle
2026-02-24 0:40 ` Mikhail Kshevetskiy
0 siblings, 1 reply; 88+ messages in thread
From: Daniel Golle @ 2026-02-24 0:12 UTC (permalink / raw)
To: Mikhail Kshevetskiy
Cc: Simon Glass, Tom Rini, Quentin Schulz, Kory Maincent,
Mattijs Korpershoek, Martin Schwan, Anshul Dalal,
Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
牛 志宏, Marek Vasut, Heinrich Schuchardt,
Wolfgang Wallner, Frank Wunderlich, David Lechner,
Osama Abdelkader, Michael Trimarchi, Miquel Raynal,
Andrew Goodbody, Yegor Yefremov, Mike Looijmans, Weijie Gao,
Alexander Stein, Neil Armstrong, Mayuresh Chitale, Paul HENRYS,
u-boot, John Crispin, Paul Spooren
On Mon, Feb 23, 2026 at 10:32:32PM +0300, Mikhail Kshevetskiy wrote:
>
> On 2/23/26 22:30, Mikhail Kshevetskiy wrote:
> > There are UBI based block storage emulation, see CONFIG_UBI_BLOCK
> > commits:
> > * 9daad11ad178646c288aca3615a7ba1e6039aed3 ("drivers: introduce UBI
> > block abstraction")
> > * aa5b67ce226267440e64fadc57d3a21e5842027c ("disk: support UBI partitions")
> >
> There is the block storage abstraction for mtd devices as well.
During my evening walk outside I thought about this a bit more, and if
it would be possible to use the ubiblock and mtdblock devices instead of
introducing dedicated bootdevs and imagemap backends for both of them.
In the simple case of only a single boot option this would probably
work: bootmeth_openwrt detects the uImage.FIT on the raw block device
and may go ahead, load and launch it.
However, there is often more than one of them. And as they are stored in
directly on MTD partitions or UBI volumes, knowing the name of the
partition or volume, and which device it sits on is crucial for
bootmeth_openwrt, which should support also complex multi-slot dual-boot
arrangement, typically identifying the slots by UBI volume name or MTD
partition name. When using the mtdblock or ubiblock devices this
information is hidden, and can only be accessed in a very tricky way,
especially for ubiblock devices, which lack a device parent. Matching
the UCLASS type of the device parent of a block device in the bootmeth
also feels sketchy and inappropriate.
So while having mtdblock and ubiblock devices is generally nice,
especially if U-Boot has to access a filesystem (eg. squashfs) stored on
them, for the case of OpenWrt it would at least be a bit ugly, as the
metadata of the actual storage backend (MTD or UBI) is crucial. We need
the UBI volume name or the label of the MTD partition.
In a way you could say that OpenWrt uses block devices (eg. MMC) it
boots from more like MTD, and not the other way around. And in some way,
bootmeth_openwrt will do some extra lifting when started on a block
device (extract the GPT partition name and present it as the bootflow's
fname), while the imagemap API provides an abstraction to read raw
images from all three storage backends (blk, mtd, ubi).
That being said, I'm happy we have overcome things like block2mtd or
gluebi, and do use the appropriate APIs (and filesystem types) on each
class of storage devices in OpenWrt nowadays. Some of the helpful
patterns borrowed from our MTD and UBI boot flows (avoiding a boot
filesystem, identifying storage volumes, ...) remain, however, and are
applied equally also on block storage (ie. MMC).
tl;dr: We'd rather use block2mtd than using mtdblock ;)
^ permalink raw reply [flat|nested] 88+ messages in thread* Re: [RFC PATCH 08/20] boot: image-loader: add UBI volume backend
2026-02-24 0:12 ` Daniel Golle
@ 2026-02-24 0:40 ` Mikhail Kshevetskiy
2026-02-24 1:06 ` Daniel Golle
0 siblings, 1 reply; 88+ messages in thread
From: Mikhail Kshevetskiy @ 2026-02-24 0:40 UTC (permalink / raw)
To: Daniel Golle
Cc: Simon Glass, Tom Rini, Quentin Schulz, Kory Maincent,
Mattijs Korpershoek, Martin Schwan, Anshul Dalal,
Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
牛 志宏, Marek Vasut, Heinrich Schuchardt,
Wolfgang Wallner, Frank Wunderlich, David Lechner,
Osama Abdelkader, Michael Trimarchi, Miquel Raynal,
Andrew Goodbody, Yegor Yefremov, Mike Looijmans, Weijie Gao,
Alexander Stein, Neil Armstrong, Mayuresh Chitale, Paul HENRYS,
u-boot, John Crispin, Paul Spooren
On 2/24/26 03:12, Daniel Golle wrote:
> On Mon, Feb 23, 2026 at 10:32:32PM +0300, Mikhail Kshevetskiy wrote:
>> On 2/23/26 22:30, Mikhail Kshevetskiy wrote:
>>> There are UBI based block storage emulation, see CONFIG_UBI_BLOCK
>>> commits:
>>> * 9daad11ad178646c288aca3615a7ba1e6039aed3 ("drivers: introduce UBI
>>> block abstraction")
>>> * aa5b67ce226267440e64fadc57d3a21e5842027c ("disk: support UBI partitions")
>>>
>> There is the block storage abstraction for mtd devices as well.
> During my evening walk outside I thought about this a bit more, and if
> it would be possible to use the ubiblock and mtdblock devices instead of
> introducing dedicated bootdevs and imagemap backends for both of them.
> In the simple case of only a single boot option this would probably
> work: bootmeth_openwrt detects the uImage.FIT on the raw block device
> and may go ahead, load and launch it.
>
> However, there is often more than one of them. And as they are stored in
> directly on MTD partitions or UBI volumes, knowing the name of the
> partition or volume, and which device it sits on is crucial for
> bootmeth_openwrt, which should support also complex multi-slot dual-boot
> arrangement, typically identifying the slots by UBI volume name or MTD
> partition name. When using the mtdblock or ubiblock devices this
> information is hidden, and can only be accessed in a very tricky way,
> especially for ubiblock devices, which lack a device parent. Matching
> the UCLASS type of the device parent of a block device in the bootmeth
> also feels sketchy and inappropriate.
what about querying available partitions using the command 'read' way?
> So while having mtdblock and ubiblock devices is generally nice,
> especially if U-Boot has to access a filesystem (eg. squashfs) stored on
> them, for the case of OpenWrt it would at least be a bit ugly, as the
> metadata of the actual storage backend (MTD or UBI) is crucial. We need
> the UBI volume name or the label of the MTD partition.
>
> In a way you could say that OpenWrt uses block devices (eg. MMC) it
> boots from more like MTD, and not the other way around. And in some way,
> bootmeth_openwrt will do some extra lifting when started on a block
> device (extract the GPT partition name and present it as the bootflow's
> fname), while the imagemap API provides an abstraction to read raw
> images from all three storage backends (blk, mtd, ubi).
>
> That being said, I'm happy we have overcome things like block2mtd or
> gluebi, and do use the appropriate APIs (and filesystem types) on each
> class of storage devices in OpenWrt nowadays. Some of the helpful
> patterns borrowed from our MTD and UBI boot flows (avoiding a boot
> filesystem, identifying storage volumes, ...) remain, however, and are
> applied equally also on block storage (ie. MMC).
>
> tl;dr: We'd rather use block2mtd than using mtdblock ;)
^ permalink raw reply [flat|nested] 88+ messages in thread* Re: [RFC PATCH 08/20] boot: image-loader: add UBI volume backend
2026-02-24 0:40 ` Mikhail Kshevetskiy
@ 2026-02-24 1:06 ` Daniel Golle
0 siblings, 0 replies; 88+ messages in thread
From: Daniel Golle @ 2026-02-24 1:06 UTC (permalink / raw)
To: Mikhail Kshevetskiy
Cc: Simon Glass, Tom Rini, Quentin Schulz, Kory Maincent,
Mattijs Korpershoek, Martin Schwan, Anshul Dalal,
Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
牛 志宏, Marek Vasut, Heinrich Schuchardt,
Wolfgang Wallner, Frank Wunderlich, David Lechner,
Osama Abdelkader, Michael Trimarchi, Miquel Raynal,
Andrew Goodbody, Yegor Yefremov, Mike Looijmans, Weijie Gao,
Alexander Stein, Neil Armstrong, Mayuresh Chitale, Paul HENRYS,
u-boot, John Crispin, Paul Spooren
On Tue, Feb 24, 2026 at 03:40:47AM +0300, Mikhail Kshevetskiy wrote:
>
> On 2/24/26 03:12, Daniel Golle wrote:
> > On Mon, Feb 23, 2026 at 10:32:32PM +0300, Mikhail Kshevetskiy wrote:
> >> On 2/23/26 22:30, Mikhail Kshevetskiy wrote:
> >>> There are UBI based block storage emulation, see CONFIG_UBI_BLOCK
> >>> commits:
> >>> * 9daad11ad178646c288aca3615a7ba1e6039aed3 ("drivers: introduce UBI
> >>> block abstraction")
> >>> * aa5b67ce226267440e64fadc57d3a21e5842027c ("disk: support UBI partitions")
> >>>
> >> There is the block storage abstraction for mtd devices as well.
> > During my evening walk outside I thought about this a bit more, and if
> > it would be possible to use the ubiblock and mtdblock devices instead of
> > introducing dedicated bootdevs and imagemap backends for both of them.
> > In the simple case of only a single boot option this would probably
> > work: bootmeth_openwrt detects the uImage.FIT on the raw block device
> > and may go ahead, load and launch it.
> >
> > However, there is often more than one of them. And as they are stored in
> > directly on MTD partitions or UBI volumes, knowing the name of the
> > partition or volume, and which device it sits on is crucial for
> > bootmeth_openwrt, which should support also complex multi-slot dual-boot
> > arrangement, typically identifying the slots by UBI volume name or MTD
> > partition name. When using the mtdblock or ubiblock devices this
> > information is hidden, and can only be accessed in a very tricky way,
> > especially for ubiblock devices, which lack a device parent. Matching
> > the UCLASS type of the device parent of a block device in the bootmeth
> > also feels sketchy and inappropriate.
>
> what about querying available partitions using the command 'read' way?
Please explain how you imagine that to work. I mean, sure, we can read
them, and then we know that they contain an uImage.FIT.
mtdblock and ubiblock devices are generally whole-disk devices, each
representing the backing MTD partition or UBI volume.
So first of all, ubiblock devices in U-Boot only exist for already
attached UBI devices, and only one UBI devices can be attached at a
time.
Apart from that problem (which would limit bootmeth_openwrt to operate
on exactly one, and already attached UBI device, which is unfortunate,
but not a complete show-stopper), there are often more than one UBI
volumes holding an uImage.FIT blob. They can be identified by the
out-of-band metadata, ie. the UBI volume name (or MTD partition label),
typically "production" and "recovery", you get the idea, right?
They **cannot** be identified by inband metadata (ie. anything you could
read from inside that volume). While (in theory) we could add metadata
to identify and distinguis "production" from "recovery" images, this
(by definition) isn't possible in case of a symmetric A/B dual boot
arrangement. Also, what I'm creating here is not a new design from
scratch, but rather the formalization of a historically grown
reality, present on countless consumer-grade devices out there.
Making fundamental changes to the design (such as carrying additional
metadata within the firmware image itself, instead of relying on
out-of-band metadata like the MTD partition label or UBI volume name)
will make the newly introduced bootmeth_openwrt de-facto incompatible
with all existing OpenWrt devices out there -- and compatibility is an
important part and purpose of the exercise.
Hence, the user (or the bootmeth) need to identify which of the available
UBI volumes or MTD partitions the system should boot from, and do so
using aforementioned out-of-band, storage-type-specific metadata.
Now, assuming that I'd have to use the ubiblock (for example), the only
way to get back to those crucial bits of metadata would be to traverse
from the blk device to the parent udevice, checking whether it's of type
UCLASS_MTD and if the blk's type is part_type PART_TYPE_UBI, and then
interpreting block_dev->hwpart as the volume name. Imho that's an overly
intrusive operation, messing with the block_dev and ubi subsystem
internals which should not be touched from outside of the respective
subsystems.
For mtdblock devices it's even much worse, I'd have to cast bdev's priv
back into struct mtd_info in order to get to the partition label, and
that feels extremely wrong (arguably, we could patch mtdblock.c to also
set the hwpart string, but even that would still be more of a hack than
actually being correct imho).
>
> > So while having mtdblock and ubiblock devices is generally nice,
> > especially if U-Boot has to access a filesystem (eg. squashfs) stored on
> > them, for the case of OpenWrt it would at least be a bit ugly, as the
> > metadata of the actual storage backend (MTD or UBI) is crucial. We need
> > the UBI volume name or the label of the MTD partition.
> >
> > In a way you could say that OpenWrt uses block devices (eg. MMC) it
> > boots from more like MTD, and not the other way around. And in some way,
> > bootmeth_openwrt will do some extra lifting when started on a block
> > device (extract the GPT partition name and present it as the bootflow's
> > fname), while the imagemap API provides an abstraction to read raw
> > images from all three storage backends (blk, mtd, ubi).
> >
> > That being said, I'm happy we have overcome things like block2mtd or
> > gluebi, and do use the appropriate APIs (and filesystem types) on each
> > class of storage devices in OpenWrt nowadays. Some of the helpful
> > patterns borrowed from our MTD and UBI boot flows (avoiding a boot
> > filesystem, identifying storage volumes, ...) remain, however, and are
> > applied equally also on block storage (ie. MMC).
> >
> > tl;dr: We'd rather use block2mtd than using mtdblock ;)
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [RFC PATCH 08/20] boot: image-loader: add UBI volume backend
2026-02-23 19:30 ` Mikhail Kshevetskiy
2026-02-23 19:32 ` Mikhail Kshevetskiy
@ 2026-02-23 20:06 ` Daniel Golle
1 sibling, 0 replies; 88+ messages in thread
From: Daniel Golle @ 2026-02-23 20:06 UTC (permalink / raw)
To: Mikhail Kshevetskiy
Cc: Simon Glass, Tom Rini, Quentin Schulz, Kory Maincent,
Mattijs Korpershoek, Martin Schwan, Anshul Dalal,
Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
牛 志宏, Marek Vasut, Heinrich Schuchardt,
Wolfgang Wallner, Frank Wunderlich, David Lechner,
Osama Abdelkader, Michael Trimarchi, Miquel Raynal,
Andrew Goodbody, Yegor Yefremov, Mike Looijmans, Weijie Gao,
Alexander Stein, Neil Armstrong, Mayuresh Chitale, Paul HENRYS,
u-boot, John Crispin, Paul Spooren
On Mon, Feb 23, 2026 at 10:30:03PM +0300, Mikhail Kshevetskiy wrote:
>
> On 2/23/26 22:24, Daniel Golle wrote:
> > On Mon, Feb 23, 2026 at 10:51:09AM -0700, Simon Glass wrote:
> >> Hi Daniel,
> >>
> >> On Thu, 19 Feb 2026 at 09:51, Daniel Golle <daniel@makrotopia.org> wrote:
> >>> On Thu, Feb 19, 2026 at 06:09:27AM -0700, Simon Glass wrote:
> >>>> Hi Daniel,
> >>>>
> >>>> On Mon, 16 Feb 2026 at 14:22, Daniel Golle <daniel@makrotopia.org> wrote:
> >>>>> Add a UBI volume storage backend for the image_loader framework.
> >>>>>
> >>>>> image_loader_init_ubi() takes a volume name, ensures the UBI device
> >>>>> is attached (auto-attaching if needed), and installs a .read() callback
> >>>>> wrapping ubi_volume_read().
> >>>>>
> >>>>> Auto-attach works by scanning the device tree for the first MTD
> >>>>> partition with compatible = "linux,ubi", then calling ubi_part() on
> >>>>> that partition. The partition name is resolved using the same
> >>>>> precedence as the MTD partition parser: the "label" property first,
> >>>>> then "name", then the node name. Since U-Boot only supports a single
> >>>>> attached UBI device at a time, only the first matching partition is
> >>>>> used. If a UBI device is already attached, the auto-attach step is
> >>>>> skipped.
> >>>>>
> >>>>> UBI handles bad-block management and wear leveling internally, so the
> >>>>> read callback is a straightforward passthrough. Note that
> >>>>> ubi_volume_read() returns positive errno values; the wrapper negates
> >>>>> them for the image_loader convention.
> >>>>>
> >>>>> Gated by CONFIG_IMAGE_LOADER_UBI (depends on CMD_UBI && IMAGE_LOADER).
> >>>>>
> >>>>> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> >>>>> ---
> >>>>> boot/Kconfig | 8 +++
> >>>>> boot/Makefile | 1 +
> >>>>> boot/image-loader-ubi.c | 112 ++++++++++++++++++++++++++++++++++++++++
> >>>>> include/image-loader.h | 13 +++++
> >>>>> 4 files changed, 134 insertions(+)
> >>>>> create mode 100644 boot/image-loader-ubi.c
> >>>>>
> >>>>> diff --git a/boot/Kconfig b/boot/Kconfig
> >>>>> index 23848a0f57e..89832014af6 100644
> >>>>> --- a/boot/Kconfig
> >>>>> +++ b/boot/Kconfig
> >>>>> @@ -1203,6 +1203,14 @@ config IMAGE_LOADER_MTD
> >>>>> parallel NAND, etc.) using the image_loader framework.
> >>>>> NAND bad blocks are skipped transparently.
> >>>>>
> >>>>> +config IMAGE_LOADER_UBI
> >>>>> + bool "UBI volume backend for image loader"
> >>>>> + depends on IMAGE_LOADER && CMD_UBI
> >>>>> + help
> >>>>> + Allows loading images from UBI volumes using the image_loader
> >>>>> + framework. Auto-attaches the UBI device from the device tree
> >>>>> + if not already attached.
> >>>>> +
> >>>>> config DISTRO_DEFAULTS
> >>>>> bool "(deprecated) Script-based booting of Linux distributions"
> >>>>> select CMDLINE
> >>>>> diff --git a/boot/Makefile b/boot/Makefile
> >>>>> index 1dde16db694..7d1d4a28106 100644
> >>>>> --- a/boot/Makefile
> >>>>> +++ b/boot/Makefile
> >>>>> @@ -76,6 +76,7 @@ obj-$(CONFIG_$(PHASE_)BOOTMETH_ANDROID) += bootmeth_android.o
> >>>>> obj-$(CONFIG_IMAGE_LOADER) += image-loader.o
> >>>>> obj-$(CONFIG_IMAGE_LOADER_BLK) += image-loader-blk.o
> >>>>> obj-$(CONFIG_IMAGE_LOADER_MTD) += image-loader-mtd.o
> >>>>> +obj-$(CONFIG_IMAGE_LOADER_UBI) += image-loader-ubi.o
> >>>>>
> >>>>> obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_ABREC) += vbe_abrec.o vbe_common.o
> >>>>> obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_ABREC_FW) += vbe_abrec_fw.o
> >>>>> diff --git a/boot/image-loader-ubi.c b/boot/image-loader-ubi.c
> >>>>> new file mode 100644
> >>>>> index 00000000000..64901a13378
> >>>>> --- /dev/null
> >>>>> +++ b/boot/image-loader-ubi.c
> >>>>> @@ -0,0 +1,112 @@
> >>>>> +// SPDX-License-Identifier: GPL-2.0+
> >>>>> +/*
> >>>>> + * UBI volume backend for image_loader
> >>>>> + *
> >>>>> + * Copyright (C) 2026 Daniel Golle <daniel@makrotopia.org>
> >>>>> + */
> >>>>> +
> >>>>> +#include <dm/ofnode.h>
> >>>>> +#include <image-loader.h>
> >>>>> +#include <log.h>
> >>>>> +#include <malloc.h>
> >>>>> +#include <mtd.h>
> >>>>> +#include <ubi_uboot.h>
> >>>>> +
> >>>>> +struct image_loader_ubi_priv {
> >>>>> + char *vol_name;
> >>>>> +};
> >>>>> +
> >>>>> +/**
> >>>>> + * ubi_auto_attach() - attach UBI if not already attached
> >>>>> + *
> >>>>> + * If no UBI device is currently attached, walk the device tree for the
> >>>>> + * first MTD partition node with compatible = "linux,ubi", find the
> >>>>> + * corresponding MTD device by matching flash_node, and attach UBI to
> >>>>> + * it via ubi_part_from_mtd().
> >>>>> + *
> >>>>> + * Since U-Boot only supports a single attached UBI device at a time,
> >>>>> + * only the first matching partition is used.
> >>>>> + *
> >>>>> + * Return: 0 on success or if already attached, negative errno on failure
> >>>>> + */
> >>>>> +static int ubi_auto_attach(void)
> >>>>> +{
> >>>>> + struct mtd_info *mtd;
> >>>>> + ofnode node;
> >>>>> +
> >>>>> + /* Already attached? */
> >>>>> + if (ubi_devices[0])
> >>>>> + return 0;
> >>>>> +
> >>>>> + mtd_probe_devices();
> >>>> This should be handled by your new ubi driver for your uclass.
> >>>>
> >>>>> +
> >>>>> + ofnode_for_each_compatible_node(node, "linux,ubi") {
> >>>>> + mtd_for_each_device(mtd) {
> >>>>> + if (ofnode_equal(mtd->flash_node, node))
> >>>>> + goto found;
> >>>>> + }
> >>>>> + }
> >>>> Eek this is really strange. There should be a device so you can use
> >>>> uclass_first_device(UCLASS_...).
> >>> I carefully considered turning UBI devices into a UCLASS, but it would
> >>> be a huge major rewrite of how UBI works in U-Boot, and while that would
> >>> be a good thing to do, I consider far beyond the scope of supporting a
> >>> boot method for OpenWrt.
> >> Oh, I just assumed it supports driver model. But surely it must have a
> >> UCLASS_BLK device?
> > Sadly no. Neither UBI devices nor UBI volumes are block storage devices.
> >
> > Every UBI device does have a UCLASS_MTD parent device, but that
> > obviously also doesn't uniquely identify the partition on the flash chip
> > which is used as UBI device -- there can be (and sometimes are, for
> > dual-boot/redundancy reasons) multiple UBI devices in different
> > partitions on the same flash.
> >
> > What I ended up doing now is to move more of the UBI detection and
> > enumeration logic into ubi_bootdev.c and have imagemap-ubi.c rely
> > on getting the MTD device and partition index from there.
>
> There are UBI based block storage emulation, see CONFIG_UBI_BLOCK
> commits:
> * 9daad11ad178646c288aca3615a7ba1e6039aed3 ("drivers: introduce UBI
> block abstraction")
> * aa5b67ce226267440e64fadc57d3a21e5842027c ("disk: support UBI partitions")
Yes, but that also only works once the UBI device is already attached.
Before that, and for any UBI device not currently attached, there aren't
any UBI_BLOCK devices. However, in both cases we'd also need to scan
them for volumes to be presented as potential bootdevs.
^ permalink raw reply [flat|nested] 88+ messages in thread
* [RFC PATCH 09/20] boot: fit: support on-demand loading in fit_image_load()
2026-02-16 21:21 [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading Daniel Golle
` (7 preceding siblings ...)
2026-02-16 21:22 ` [RFC PATCH 08/20] boot: image-loader: add UBI volume backend Daniel Golle
@ 2026-02-16 21:22 ` Daniel Golle
2026-02-19 13:09 ` Simon Glass
2026-02-16 21:22 ` [RFC PATCH 10/20] cmd: bootm: accept storage device as image source Daniel Golle
` (16 subsequent siblings)
25 siblings, 1 reply; 88+ messages in thread
From: Daniel Golle @ 2026-02-16 21:22 UTC (permalink / raw)
To: Tom Rini, Simon Glass, Quentin Schulz, Daniel Golle,
Kory Maincent, Mattijs Korpershoek, Martin Schwan, Anshul Dalal,
Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
牛 志宏, Marek Vasut, Heinrich Schuchardt,
Wolfgang Wallner, Frank Wunderlich, David Lechner,
Osama Abdelkader, Mikhail Kshevetskiy, Michael Trimarchi,
Miquel Raynal, Andrew Goodbody, Yegor Yefremov, Mike Looijmans,
Weijie Gao, Alexander Stein, Neil Armstrong, Mayuresh Chitale,
Paul HENRYS, u-boot
Cc: John Crispin, Paul Spooren
Add an on-demand loading path to fit_image_load() for use when
images->loader is set (storage-backed boot).
For external-data FIT images, the new path avoids dereferencing
fit + data_offset (which would point into unmapped memory) and
instead:
1. Extracts the data offset and size from FDT properties
(data-position / data-offset + data-size) -- metadata only,
no payload access.
2. Returns early for IH_TYPE_FILESYSTEM sub-images, which stay on
storage and are never loaded into RAM (e.g. squashfs rootfs).
3. Determines the RAM destination:
- If the sub-image has a load address, loads directly there
via image_loader_map_to() (zero-copy).
- Otherwise, allocates scratch RAM via image_loader_map().
4. Verifies the hash/signature in-place using
fit_image_verify_with_data(), which is address-agnostic.
5. Jumps to the common tail, skipping the normal
fit_image_get_data() + memcpy() sequence since data is already
at its final location.
The entire path is gated by 'if (images->loader && external)' and
USE_HOSTCC, so the existing in-memory flow is completely unchanged
when no loader is set or when building host tools.
For inline-data FIT images (no data-position/data-offset), the FDT
structure loaded during format detection already contains all sub-
image data, so the existing path handles them correctly.
Also adds a 'struct image_loader *loader' member to struct
bootm_headers (initialised to NULL).
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
boot/image-fit.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++
include/image.h | 4 ++
2 files changed, 100 insertions(+)
diff --git a/boot/image-fit.c b/boot/image-fit.c
index 3ed69b5f7bc..73d3bf7df08 100644
--- a/boot/image-fit.c
+++ b/boot/image-fit.c
@@ -24,6 +24,7 @@ extern void *aligned_alloc(size_t alignment, size_t size);
#include <linux/sizes.h>
#include <errno.h>
#include <log.h>
+#include <image-loader.h>
#include <mapmem.h>
#include <asm/io.h>
#include <malloc.h>
@@ -2166,6 +2167,100 @@ int fit_image_load(struct bootm_headers *images, ulong addr,
printf(" Trying '%s' %s subimage\n", fit_uname, prop_name);
+#if !defined(USE_HOSTCC) && CONFIG_IS_ENABLED(IMAGE_LOADER)
+ /*
+ * Storage-backed path: when an image_loader is active and the
+ * sub-image uses external data, load the payload from storage
+ * instead of dereferencing fit + data_offset. The FDT structure
+ * (containing all metadata, hashes, and signatures) is already
+ * in RAM; only the payload is on storage.
+ *
+ * Sequence:
+ * 1. Print image info (fit_image_select with verify=0)
+ * 2. Extract data location from FDT properties (no data access)
+ * 3. Skip IH_TYPE_FILESYSTEM sub-images — they stay on storage
+ * 4. Determine RAM destination (load address or scratch area)
+ * 5. Read payload via image_loader_map_to() / image_loader_map()
+ * 6. Verify hash/signature in-place
+ * 7. Jump to common tail (FDT validation, output params)
+ */
+ if (images && images->loader) {
+ int data_off = 0, data_sz = 0;
+ bool external = false;
+ u8 img_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)) {
+ /* Print image info without verifying data */
+ ret = fit_image_select(fit, noffset, 0);
+ if (ret) {
+ bootstage_error(bootstage_id + BOOTSTAGE_SUB_HASH);
+ return ret;
+ }
+
+ /* Skip filesystem images — they stay on storage */
+ if (!fit_image_get_type(fit, noffset, &img_type) &&
+ img_type == IH_TYPE_FILESYSTEM) {
+ *datap = 0;
+ *lenp = 0;
+ return noffset;
+ }
+
+ len = data_sz;
+ comp = IH_COMP_NONE;
+ fit_image_get_comp(fit, noffset, &comp);
+
+ /* Determine the RAM destination */
+ if (load_op == FIT_LOAD_IGNORED) {
+ loadbuf = image_loader_map(images->loader,
+ data_off, len);
+ } else if (!fit_image_get_load(fit, noffset, &load)) {
+ printf(" Loading %s from storage to 0x%08lx\n",
+ prop_name, load);
+ loadbuf = map_sysmem(load, len);
+ loadbuf = image_loader_map_to(images->loader,
+ data_off, len,
+ loadbuf);
+ } else if (load_op == FIT_LOAD_REQUIRED) {
+ printf("Can't get %s subimage load address!\n",
+ prop_name);
+ return -EBADF;
+ } else {
+ loadbuf = image_loader_map(images->loader,
+ data_off, len);
+ }
+
+ if (!loadbuf) {
+ printf("Failed to load %s from storage\n",
+ prop_name);
+ return -EIO;
+ }
+
+ load = map_to_sysmem(loadbuf);
+
+ /* Verify hash/signature in-place */
+ if (images->verify) {
+ puts(" Verifying Hash Integrity ... ");
+ if (!fit_image_verify_with_data(fit, noffset,
+ gd_fdt_blob(),
+ loadbuf, len)) {
+ puts("Bad Data Hash\n");
+ return -EACCES;
+ }
+ puts("OK\n");
+ }
+
+ goto storage_loaded;
+ }
+ }
+#endif /* !USE_HOSTCC && CONFIG_IMAGE_LOADER */
+
ret = fit_image_select(fit, noffset, images->verify);
if (ret) {
bootstage_error(bootstage_id + BOOTSTAGE_SUB_HASH);
@@ -2325,6 +2420,7 @@ int fit_image_load(struct bootm_headers *images, ulong addr,
puts("WARNING: 'compression' nodes for ramdisks are deprecated,"
" please fix your .its file!\n");
+storage_loaded:
/* verify that image data is a proper FDT blob */
if (load_op != FIT_LOAD_IGNORED && image_type == IH_TYPE_FLATDT &&
fdt_check_header(loadbuf)) {
diff --git a/include/image.h b/include/image.h
index 34efac6056d..ecb3fec26c0 100644
--- a/include/image.h
+++ b/include/image.h
@@ -345,6 +345,8 @@ struct image_info {
uint8_t arch; /* CPU architecture */
};
+struct image_loader;
+
/*
* Legacy and FIT format headers used by do_bootm() and do_bootm_<os>()
* routines.
@@ -400,6 +402,8 @@ struct bootm_headers {
int verify; /* env_get("verify")[0] != 'n' */
+ struct image_loader *loader; /* on-demand storage loader, or NULL */
+
#define BOOTM_STATE_START 0x00000001
#define BOOTM_STATE_FINDOS 0x00000002
#define BOOTM_STATE_FINDOTHER 0x00000004
--
2.53.0
^ permalink raw reply related [flat|nested] 88+ messages in thread* Re: [RFC PATCH 09/20] boot: fit: support on-demand loading in fit_image_load()
2026-02-16 21:22 ` [RFC PATCH 09/20] boot: fit: support on-demand loading in fit_image_load() Daniel Golle
@ 2026-02-19 13:09 ` Simon Glass
2026-02-19 16:47 ` Daniel Golle
0 siblings, 1 reply; 88+ messages in thread
From: Simon Glass @ 2026-02-19 13:09 UTC (permalink / raw)
To: Daniel Golle
Cc: Tom Rini, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
Hi Daniel,
On Mon, 16 Feb 2026 at 14:22, Daniel Golle <daniel@makrotopia.org> wrote:
>
> Add an on-demand loading path to fit_image_load() for use when
> images->loader is set (storage-backed boot).
>
> For external-data FIT images, the new path avoids dereferencing
> fit + data_offset (which would point into unmapped memory) and
> instead:
>
> 1. Extracts the data offset and size from FDT properties
> (data-position / data-offset + data-size) -- metadata only,
> no payload access.
>
> 2. Returns early for IH_TYPE_FILESYSTEM sub-images, which stay on
> storage and are never loaded into RAM (e.g. squashfs rootfs).
>
> 3. Determines the RAM destination:
> - If the sub-image has a load address, loads directly there
> via image_loader_map_to() (zero-copy).
> - Otherwise, allocates scratch RAM via image_loader_map().
>
> 4. Verifies the hash/signature in-place using
> fit_image_verify_with_data(), which is address-agnostic.
>
> 5. Jumps to the common tail, skipping the normal
> fit_image_get_data() + memcpy() sequence since data is already
> at its final location.
>
> The entire path is gated by 'if (images->loader && external)' and
> USE_HOSTCC, so the existing in-memory flow is completely unchanged
> when no loader is set or when building host tools.
>
> For inline-data FIT images (no data-position/data-offset), the FDT
> structure loaded during format detection already contains all sub-
> image data, so the existing path handles them correctly.
>
> Also adds a 'struct image_loader *loader' member to struct
> bootm_headers (initialised to NULL).
>
> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> ---
> boot/image-fit.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++
> include/image.h | 4 ++
> 2 files changed, 100 insertions(+)
>
> diff --git a/boot/image-fit.c b/boot/image-fit.c
> index 3ed69b5f7bc..73d3bf7df08 100644
> --- a/boot/image-fit.c
> +++ b/boot/image-fit.c
> @@ -24,6 +24,7 @@ extern void *aligned_alloc(size_t alignment, size_t size);
> #include <linux/sizes.h>
> #include <errno.h>
> #include <log.h>
> +#include <image-loader.h>
> #include <mapmem.h>
> #include <asm/io.h>
> #include <malloc.h>
> @@ -2166,6 +2167,100 @@ int fit_image_load(struct bootm_headers *images, ulong addr,
>
> printf(" Trying '%s' %s subimage\n", fit_uname, prop_name);
>
> +#if !defined(USE_HOSTCC) && CONFIG_IS_ENABLED(IMAGE_LOADER)
This function is far too long so this code should go in its own
function. Also use if() and tools_build()
> + /*
> + * Storage-backed path: when an image_loader is active and the
> + * sub-image uses external data, load the payload from storage
> + * instead of dereferencing fit + data_offset. The FDT structure
> + * (containing all metadata, hashes, and signatures) is already
> + * in RAM; only the payload is on storage.
> + *
> + * Sequence:
> + * 1. Print image info (fit_image_select with verify=0)
> + * 2. Extract data location from FDT properties (no data access)
> + * 3. Skip IH_TYPE_FILESYSTEM sub-images — they stay on storage
> + * 4. Determine RAM destination (load address or scratch area)
> + * 5. Read payload via image_loader_map_to() / image_loader_map()
> + * 6. Verify hash/signature in-place
> + * 7. Jump to common tail (FDT validation, output params)
> + */
> + if (images && images->loader) {
> + int data_off = 0, data_sz = 0;
> + bool external = false;
> + u8 img_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)) {
> + /* Print image info without verifying data */
> + ret = fit_image_select(fit, noffset, 0);
> + if (ret) {
> + bootstage_error(bootstage_id + BOOTSTAGE_SUB_HASH);
> + return ret;
> + }
> +
> + /* Skip filesystem images — they stay on storage */
> + if (!fit_image_get_type(fit, noffset, &img_type) &&
> + img_type == IH_TYPE_FILESYSTEM) {
> + *datap = 0;
> + *lenp = 0;
> + return noffset;
Should add a comment here about why you are not doing verification. I
assume the FS is protected with dm-verify, iwc should the root hash be
stored in the FIT?
> + }
> +
> + len = data_sz;
> + comp = IH_COMP_NONE;
> + fit_image_get_comp(fit, noffset, &comp);
> +
> + /* Determine the RAM destination */
> + if (load_op == FIT_LOAD_IGNORED) {
> + loadbuf = image_loader_map(images->loader,
> + data_off, len);
> + } else if (!fit_image_get_load(fit, noffset, &load)) {
> + printf(" Loading %s from storage to 0x%08lx\n",
> + prop_name, load);
> + loadbuf = map_sysmem(load, len);
> + loadbuf = image_loader_map_to(images->loader,
> + data_off, len,
> + loadbuf);
> + } else if (load_op == FIT_LOAD_REQUIRED) {
> + printf("Can't get %s subimage load address!\n",
> + prop_name);
> + return -EBADF;
> + } else {
> + loadbuf = image_loader_map(images->loader,
> + data_off, len);
> + }
> +
> + if (!loadbuf) {
> + printf("Failed to load %s from storage\n",
> + prop_name);
> + return -EIO;
> + }
> +
> + load = map_to_sysmem(loadbuf);
> +
> + /* Verify hash/signature in-place */
> + if (images->verify) {
> + puts(" Verifying Hash Integrity ... ");
> + if (!fit_image_verify_with_data(fit, noffset,
> + gd_fdt_blob(),
> + loadbuf, len)) {
> + puts("Bad Data Hash\n");
> + return -EACCES;
> + }
> + puts("OK\n");
> + }
> +
> + goto storage_loaded;
> + }
> + }
> +#endif /* !USE_HOSTCC && CONFIG_IMAGE_LOADER */
> +
> ret = fit_image_select(fit, noffset, images->verify);
> if (ret) {
> bootstage_error(bootstage_id + BOOTSTAGE_SUB_HASH);
> @@ -2325,6 +2420,7 @@ int fit_image_load(struct bootm_headers *images, ulong addr,
> puts("WARNING: 'compression' nodes for ramdisks are deprecated,"
> " please fix your .its file!\n");
>
> +storage_loaded:
> /* verify that image data is a proper FDT blob */
> if (load_op != FIT_LOAD_IGNORED && image_type == IH_TYPE_FLATDT &&
> fdt_check_header(loadbuf)) {
> diff --git a/include/image.h b/include/image.h
> index 34efac6056d..ecb3fec26c0 100644
> --- a/include/image.h
> +++ b/include/image.h
> @@ -345,6 +345,8 @@ struct image_info {
> uint8_t arch; /* CPU architecture */
> };
>
> +struct image_loader;
> +
> /*
> * Legacy and FIT format headers used by do_bootm() and do_bootm_<os>()
> * routines.
> @@ -400,6 +402,8 @@ struct bootm_headers {
>
> int verify; /* env_get("verify")[0] != 'n' */
>
> + struct image_loader *loader; /* on-demand storage loader, or NULL */
> +
> #define BOOTM_STATE_START 0x00000001
> #define BOOTM_STATE_FINDOS 0x00000002
> #define BOOTM_STATE_FINDOTHER 0x00000004
> --
> 2.53.0
Regards,
Simon
^ permalink raw reply [flat|nested] 88+ messages in thread* Re: [RFC PATCH 09/20] boot: fit: support on-demand loading in fit_image_load()
2026-02-19 13:09 ` Simon Glass
@ 2026-02-19 16:47 ` Daniel Golle
2026-02-23 17:51 ` Simon Glass
0 siblings, 1 reply; 88+ messages in thread
From: Daniel Golle @ 2026-02-19 16:47 UTC (permalink / raw)
To: Simon Glass
Cc: Tom Rini, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
On Thu, Feb 19, 2026 at 06:09:26AM -0700, Simon Glass wrote:
> Hi Daniel,
>
> On Mon, 16 Feb 2026 at 14:22, Daniel Golle <daniel@makrotopia.org> wrote:
> >
> > Add an on-demand loading path to fit_image_load() for use when
> > images->loader is set (storage-backed boot).
> >
> > For external-data FIT images, the new path avoids dereferencing
> > fit + data_offset (which would point into unmapped memory) and
> > instead:
> >
> > 1. Extracts the data offset and size from FDT properties
> > (data-position / data-offset + data-size) -- metadata only,
> > no payload access.
> >
> > 2. Returns early for IH_TYPE_FILESYSTEM sub-images, which stay on
> > storage and are never loaded into RAM (e.g. squashfs rootfs).
> >
> > 3. Determines the RAM destination:
> > - If the sub-image has a load address, loads directly there
> > via image_loader_map_to() (zero-copy).
> > - Otherwise, allocates scratch RAM via image_loader_map().
> >
> > 4. Verifies the hash/signature in-place using
> > fit_image_verify_with_data(), which is address-agnostic.
> >
> > 5. Jumps to the common tail, skipping the normal
> > fit_image_get_data() + memcpy() sequence since data is already
> > at its final location.
> >
> > The entire path is gated by 'if (images->loader && external)' and
> > USE_HOSTCC, so the existing in-memory flow is completely unchanged
> > when no loader is set or when building host tools.
> >
> > For inline-data FIT images (no data-position/data-offset), the FDT
> > structure loaded during format detection already contains all sub-
> > image data, so the existing path handles them correctly.
> >
> > Also adds a 'struct image_loader *loader' member to struct
> > bootm_headers (initialised to NULL).
> >
> > Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> > ---
> > boot/image-fit.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++
> > include/image.h | 4 ++
> > 2 files changed, 100 insertions(+)
> >
> > diff --git a/boot/image-fit.c b/boot/image-fit.c
> > index 3ed69b5f7bc..73d3bf7df08 100644
> > --- a/boot/image-fit.c
> > +++ b/boot/image-fit.c
> > @@ -24,6 +24,7 @@ extern void *aligned_alloc(size_t alignment, size_t size);
> > #include <linux/sizes.h>
> > #include <errno.h>
> > #include <log.h>
> > +#include <image-loader.h>
> > #include <mapmem.h>
> > #include <asm/io.h>
> > #include <malloc.h>
> > @@ -2166,6 +2167,100 @@ int fit_image_load(struct bootm_headers *images, ulong addr,
> >
> > printf(" Trying '%s' %s subimage\n", fit_uname, prop_name);
> >
> > +#if !defined(USE_HOSTCC) && CONFIG_IS_ENABLED(IMAGE_LOADER)
>
> This function is far too long so this code should go in its own
> function. Also use if() and tools_build()
I have significantly changed this whole part and folded storage access
into fit_image_get_data(). A pre-loading stage in fit_image_load(), much
fewer lines than the current do-it-all storage path, currently still
remains in fit_image_load(). I will present it soon in a (manually...)
reworked RFCv2 once I looked into and adressed all the other comments
received for the initial RFC.
>
> > + /*
> > + * Storage-backed path: when an image_loader is active and the
> > + * sub-image uses external data, load the payload from storage
> > + * instead of dereferencing fit + data_offset. The FDT structure
> > + * (containing all metadata, hashes, and signatures) is already
> > + * in RAM; only the payload is on storage.
> > + *
> > + * Sequence:
> > + * 1. Print image info (fit_image_select with verify=0)
> > + * 2. Extract data location from FDT properties (no data access)
> > + * 3. Skip IH_TYPE_FILESYSTEM sub-images — they stay on storage
> > + * 4. Determine RAM destination (load address or scratch area)
> > + * 5. Read payload via image_loader_map_to() / image_loader_map()
> > + * 6. Verify hash/signature in-place
> > + * 7. Jump to common tail (FDT validation, output params)
> > + */
> > + if (images && images->loader) {
> > + int data_off = 0, data_sz = 0;
> > + bool external = false;
> > + u8 img_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)) {
> > + /* Print image info without verifying data */
> > + ret = fit_image_select(fit, noffset, 0);
> > + if (ret) {
> > + bootstage_error(bootstage_id + BOOTSTAGE_SUB_HASH);
> > + return ret;
> > + }
> > +
> > + /* Skip filesystem images — they stay on storage */
> > + if (!fit_image_get_type(fit, noffset, &img_type) &&
> > + img_type == IH_TYPE_FILESYSTEM) {
> > + *datap = 0;
> > + *lenp = 0;
> > + return noffset;
>
> Should add a comment here about why you are not doing verification. I
> assume the FS is protected with dm-verify, iwc should the root hash be
> stored in the FIT?
Yes, I agree with that. A single property won't be enough, so what I
came up with and currently test is a node under the image, ie.
dm-verity {
block-size = <0x1000>;
data-blocks = <0xdead>;
algo = "sha256";
root-hash = "averylonghashasstring";
salt = "anotherverylonghashasstring";
};
This is sufficient to then let U-Boot generate the dm-mod.create="..."
parameter handed over to the kernel.
Currently I'm just putting this in the its source file, but of course we
could also extend 'mkimage' to call 'veritysetup format ...' and
populate such section like it is done for the hashes.
^ permalink raw reply [flat|nested] 88+ messages in thread* Re: [RFC PATCH 09/20] boot: fit: support on-demand loading in fit_image_load()
2026-02-19 16:47 ` Daniel Golle
@ 2026-02-23 17:51 ` Simon Glass
2026-02-24 12:41 ` Daniel Golle
0 siblings, 1 reply; 88+ messages in thread
From: Simon Glass @ 2026-02-23 17:51 UTC (permalink / raw)
To: Daniel Golle
Cc: Tom Rini, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
Hi Daniel,
On Thu, 19 Feb 2026 at 09:48, Daniel Golle <daniel@makrotopia.org> wrote:
>
> On Thu, Feb 19, 2026 at 06:09:26AM -0700, Simon Glass wrote:
> > Hi Daniel,
> >
> > On Mon, 16 Feb 2026 at 14:22, Daniel Golle <daniel@makrotopia.org> wrote:
> > >
> > > Add an on-demand loading path to fit_image_load() for use when
> > > images->loader is set (storage-backed boot).
> > >
> > > For external-data FIT images, the new path avoids dereferencing
> > > fit + data_offset (which would point into unmapped memory) and
> > > instead:
> > >
> > > 1. Extracts the data offset and size from FDT properties
> > > (data-position / data-offset + data-size) -- metadata only,
> > > no payload access.
> > >
> > > 2. Returns early for IH_TYPE_FILESYSTEM sub-images, which stay on
> > > storage and are never loaded into RAM (e.g. squashfs rootfs).
> > >
> > > 3. Determines the RAM destination:
> > > - If the sub-image has a load address, loads directly there
> > > via image_loader_map_to() (zero-copy).
> > > - Otherwise, allocates scratch RAM via image_loader_map().
> > >
> > > 4. Verifies the hash/signature in-place using
> > > fit_image_verify_with_data(), which is address-agnostic.
> > >
> > > 5. Jumps to the common tail, skipping the normal
> > > fit_image_get_data() + memcpy() sequence since data is already
> > > at its final location.
> > >
> > > The entire path is gated by 'if (images->loader && external)' and
> > > USE_HOSTCC, so the existing in-memory flow is completely unchanged
> > > when no loader is set or when building host tools.
> > >
> > > For inline-data FIT images (no data-position/data-offset), the FDT
> > > structure loaded during format detection already contains all sub-
> > > image data, so the existing path handles them correctly.
> > >
> > > Also adds a 'struct image_loader *loader' member to struct
> > > bootm_headers (initialised to NULL).
> > >
> > > Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> > > ---
> > > boot/image-fit.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++
> > > include/image.h | 4 ++
> > > 2 files changed, 100 insertions(+)
> > >
> > > diff --git a/boot/image-fit.c b/boot/image-fit.c
> > > index 3ed69b5f7bc..73d3bf7df08 100644
> > > --- a/boot/image-fit.c
> > > +++ b/boot/image-fit.c
> > > @@ -24,6 +24,7 @@ extern void *aligned_alloc(size_t alignment, size_t size);
> > > #include <linux/sizes.h>
> > > #include <errno.h>
> > > #include <log.h>
> > > +#include <image-loader.h>
> > > #include <mapmem.h>
> > > #include <asm/io.h>
> > > #include <malloc.h>
> > > @@ -2166,6 +2167,100 @@ int fit_image_load(struct bootm_headers *images, ulong addr,
> > >
> > > printf(" Trying '%s' %s subimage\n", fit_uname, prop_name);
> > >
> > > +#if !defined(USE_HOSTCC) && CONFIG_IS_ENABLED(IMAGE_LOADER)
> >
> > This function is far too long so this code should go in its own
> > function. Also use if() and tools_build()
>
> I have significantly changed this whole part and folded storage access
> into fit_image_get_data(). A pre-loading stage in fit_image_load(), much
> fewer lines than the current do-it-all storage path, currently still
> remains in fit_image_load(). I will present it soon in a (manually...)
> reworked RFCv2 once I looked into and adressed all the other comments
> received for the initial RFC.
OK
>
> >
> > > + /*
> > > + * Storage-backed path: when an image_loader is active and the
> > > + * sub-image uses external data, load the payload from storage
> > > + * instead of dereferencing fit + data_offset. The FDT structure
> > > + * (containing all metadata, hashes, and signatures) is already
> > > + * in RAM; only the payload is on storage.
> > > + *
> > > + * Sequence:
> > > + * 1. Print image info (fit_image_select with verify=0)
> > > + * 2. Extract data location from FDT properties (no data access)
> > > + * 3. Skip IH_TYPE_FILESYSTEM sub-images — they stay on storage
> > > + * 4. Determine RAM destination (load address or scratch area)
> > > + * 5. Read payload via image_loader_map_to() / image_loader_map()
> > > + * 6. Verify hash/signature in-place
> > > + * 7. Jump to common tail (FDT validation, output params)
> > > + */
> > > + if (images && images->loader) {
> > > + int data_off = 0, data_sz = 0;
> > > + bool external = false;
> > > + u8 img_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)) {
> > > + /* Print image info without verifying data */
> > > + ret = fit_image_select(fit, noffset, 0);
> > > + if (ret) {
> > > + bootstage_error(bootstage_id + BOOTSTAGE_SUB_HASH);
> > > + return ret;
> > > + }
> > > +
> > > + /* Skip filesystem images — they stay on storage */
> > > + if (!fit_image_get_type(fit, noffset, &img_type) &&
> > > + img_type == IH_TYPE_FILESYSTEM) {
> > > + *datap = 0;
> > > + *lenp = 0;
> > > + return noffset;
> >
> > Should add a comment here about why you are not doing verification. I
> > assume the FS is protected with dm-verify, iwc should the root hash be
> > stored in the FIT?
>
> Yes, I agree with that. A single property won't be enough, so what I
> came up with and currently test is a node under the image, ie.
>
> dm-verity {
> block-size = <0x1000>;
> data-blocks = <0xdead>;
> algo = "sha256";
> root-hash = "averylonghashasstring";
> salt = "anotherverylonghashasstring";
> };
>
> This is sufficient to then let U-Boot generate the dm-mod.create="..."
> parameter handed over to the kernel.
>
> Currently I'm just putting this in the its source file, but of course we
> could also extend 'mkimage' to call 'veritysetup format ...' and
> populate such section like it is done for the hashes.
>
Yes this looks very nice to me. Don't forget a PR for the FIT spec too.
Regards,
SImon
^ permalink raw reply [flat|nested] 88+ messages in thread* Re: [RFC PATCH 09/20] boot: fit: support on-demand loading in fit_image_load()
2026-02-23 17:51 ` Simon Glass
@ 2026-02-24 12:41 ` Daniel Golle
0 siblings, 0 replies; 88+ messages in thread
From: Daniel Golle @ 2026-02-24 12:41 UTC (permalink / raw)
To: Simon Glass
Cc: Tom Rini, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
On Mon, Feb 23, 2026 at 10:51:07AM -0700, Simon Glass wrote:
> On Thu, 19 Feb 2026 at 09:48, Daniel Golle <daniel@makrotopia.org> wrote:
> > [...]
> > Yes, I agree with that. A single property won't be enough, so what I
> > came up with and currently test is a node under the image, ie.
> >
> > dm-verity {
> > block-size = <0x1000>;
> > data-blocks = <0xdead>;
> > algo = "sha256";
> > root-hash = "averylonghashasstring";
> > salt = "anotherverylonghashasstring";
> > };
> >
> > This is sufficient to then let U-Boot generate the dm-mod.create="..."
> > parameter handed over to the kernel.
> >
> > Currently I'm just putting this in the its source file, but of course we
> > could also extend 'mkimage' to call 'veritysetup format ...' and
> > populate such section like it is done for the hashes.
>
> Yes this looks very nice to me. Don't forget a PR for the FIT spec too.
https://github.com/open-source-firmware/flat-image-tree/pull/37
I hope this is the correct repo. There are quite a few open PR there
which seemingly nobody has ever looked at, all of them opened by the
same user (@sjg20), one of them open for more than 3 years (#5).
They all look very useful too...
^ permalink raw reply [flat|nested] 88+ messages in thread
* [RFC PATCH 10/20] cmd: bootm: accept storage device as image source
2026-02-16 21:21 [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading Daniel Golle
` (8 preceding siblings ...)
2026-02-16 21:22 ` [RFC PATCH 09/20] boot: fit: support on-demand loading in fit_image_load() Daniel Golle
@ 2026-02-16 21:22 ` Daniel Golle
2026-02-19 13:09 ` Simon Glass
2026-02-16 21:22 ` [RFC PATCH 11/20] test: boot: add image_loader unit tests Daniel Golle
` (15 subsequent siblings)
25 siblings, 1 reply; 88+ messages in thread
From: Daniel Golle @ 2026-02-16 21:22 UTC (permalink / raw)
To: Tom Rini, Simon Glass, Quentin Schulz, Daniel Golle,
Kory Maincent, Mattijs Korpershoek, Martin Schwan, Anshul Dalal,
Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
牛 志宏, Marek Vasut, Heinrich Schuchardt,
Wolfgang Wallner, Frank Wunderlich, David Lechner,
Osama Abdelkader, Mikhail Kshevetskiy, Michael Trimarchi,
Miquel Raynal, Andrew Goodbody, Yegor Yefremov, Mike Looijmans,
Weijie Gao, Alexander Stein, Neil Armstrong, Mayuresh Chitale,
Paul HENRYS, u-boot
Cc: John Crispin, Paul Spooren
Extend the bootm command to accept a storage device type keyword as
the first argument:
bootm mmc <dev>:<part> - boot from MMC/SD/eMMC partition
bootm mtd <name> - boot from MTD partition
bootm ubi <volume> - boot from UBI volume
When a known device-type keyword is found, an image_loader is
constructed using the corresponding backend and stored in
images.loader. The rest of the bootm flow proceeds unchanged:
boot_get_kernel() uses image_loader_map() for format detection and
FDT structure loading, then fit_image_load() uses the on-demand
storage path for sub-image data.
If the first argument does not match a device-type keyword (i.e. it
looks like a hex address, or is absent), images.loader remains NULL
and the existing in-memory path is taken -- full backward compatibility.
The #config suffix follows the same convention as existing bootm FIT
config selection. Multiple # suffixes can be chained to select a
base config and one or more device-tree overlays:
bootm mmc 0:4#config-1#overlay-wifi#overlay-lcd
bootm mtd firmware#config-1
Gated by CONFIG_BOOTM_STORAGE.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
boot/Kconfig | 13 +++++
boot/bootm.c | 62 +++++++++++++++++---
cmd/bootm.c | 148 ++++++++++++++++++++++++++++++++++++++++++++++--
include/bootm.h | 2 +
4 files changed, 212 insertions(+), 13 deletions(-)
diff --git a/boot/Kconfig b/boot/Kconfig
index 89832014af6..1f870c7d251 100644
--- a/boot/Kconfig
+++ b/boot/Kconfig
@@ -1211,6 +1211,19 @@ config IMAGE_LOADER_UBI
framework. Auto-attaches the UBI device from the device tree
if not already attached.
+config BOOTM_STORAGE
+ bool "Allow bootm to load images directly from storage"
+ depends on CMDLINE && FIT && IMAGE_LOADER
+ help
+ Extends the bootm command to accept "mmc <dev>:<part>",
+ "mtd <name>", or "ubi <volume>" as the image source instead
+ of a RAM address. Sub-images (kernel, FDT, ramdisk, etc.)
+ are loaded from storage on demand; filesystem sub-images are
+ never read.
+
+ Requires at least one IMAGE_LOADER backend to be enabled
+ (IMAGE_LOADER_BLK, IMAGE_LOADER_MTD, or IMAGE_LOADER_UBI).
+
config DISTRO_DEFAULTS
bool "(deprecated) Script-based booting of Linux distributions"
select CMDLINE
diff --git a/boot/bootm.c b/boot/bootm.c
index 4bdca22ea8c..67d161f6e3a 100644
--- a/boot/bootm.c
+++ b/boot/bootm.c
@@ -13,6 +13,7 @@
#include <env.h>
#include <errno.h>
#include <fdt_support.h>
+#include <image-loader.h>
#include <irq_func.h>
#include <lmb.h>
#include <log.h>
@@ -146,7 +147,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_BOOTM_STORAGE) && images->loader) {
+ /*
+ * 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 = image_loader_map(images->loader, 0, 64);
+ if (!buf) {
+ puts("Cannot read image header from storage\n");
+ return -EIO;
+ }
+ 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:
@@ -192,6 +208,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_BOOTM_STORAGE) && images->loader) {
+ /*
+ * Extend the mapping to cover the full FIT
+ * FDT structure so all metadata is accessible.
+ */
+ size_t fdt_sz = fdt_totalsize(buf);
+
+ buf = image_loader_map(images->loader, 0, fdt_sz);
+ if (!buf) {
+ puts("Cannot read FIT header from storage\n");
+ return -ENOMEM;
+ }
+ 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,
@@ -991,11 +1021,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_BOOTM_STORAGE) && bmi->loader)
+ images->loader = bmi->loader;
+ }
- 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_BOOTM_STORAGE) || !images->loader)
+ ret = bootm_pre_load(bmi->addr_img);
+ }
if (!ret && (states & BOOTM_STATE_FINDOS))
ret = bootm_find_os(bmi->cmd_name, bmi->addr_img);
@@ -1003,8 +1043,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_BOOTM_STORAGE) && images->loader)
+ 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);
}
@@ -1097,8 +1140,13 @@ 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_BOOTM_STORAGE) && images->loader)
+ image_loader_cleanup(images->loader);
+
ret = boot_selected_os(BOOTM_STATE_OS_GO, bmi, boot_fn);
+ }
/* Deal with any fallout */
err:
diff --git a/cmd/bootm.c b/cmd/bootm.c
index 2c5aea26d98..6c26dd8e36d 100644
--- a/cmd/bootm.c
+++ b/cmd/bootm.c
@@ -12,6 +12,7 @@
#include <env.h>
#include <errno.h>
#include <image.h>
+#include <image-loader.h>
#include <malloc.h>
#include <nand.h>
#include <asm/byteorder.h>
@@ -132,14 +133,123 @@ static int do_bootm_subcommand(struct cmd_tbl *cmdtp, int flag, int argc,
/* bootm - boot application image from image in memory */
/*******************************************************************/
+#if IS_ENABLED(CONFIG_BOOTM_STORAGE)
+/**
+ * bootm_init_loader() - Try to parse a storage device spec from argv
+ *
+ * If argv[0] is a known device-type keyword ("mmc", "mtd", "ubi"),
+ * initialise @ldr as the corresponding backend and return the number
+ * of argv entries consumed. The optional #config[#overlay...] suffix
+ * is stripped from the device spec and returned via @conf_name.
+ *
+ * @ldr: loader to initialise
+ * @argc: argument count (after removing the "bootm" verb)
+ * @argv: argument vector
+ * @conf_name: on output, points to the #config string (incl. '#') or NULL
+ * Return: number of argv entries consumed (>0), 0 if no storage keyword
+ * was found, or negative errno on init failure
+ */
+static int bootm_init_loader(struct image_loader *ldr,
+ int argc, char *const argv[],
+ const char **conf_name)
+{
+ const char *devtype;
+ char *devspec;
+ char *hash;
+ int ret;
+
+ if (argc < 2)
+ return 0;
+
+ devtype = argv[0];
+ devspec = (char *)argv[1];
+ *conf_name = NULL;
+
+ /* Locate the first '#' — its meaning depends on the backend */
+ hash = strchr(devspec, '#');
+
+ if (IS_ENABLED(CONFIG_IMAGE_LOADER_BLK) &&
+ !strcmp(devtype, "mmc")) {
+ /*
+ * Block device specs use '#' for partition names when no ':'
+ * precedes it (e.g. "0#kernel"). Only treat the first '#'
+ * as the FIT config separator when ':' appears before it
+ * (e.g. "0:4#config-1"), otherwise the first '#' is the
+ * partition name and a second '#' starts the config
+ * (e.g. "0#kernel#config-1").
+ */
+ char *colon = strchr(devspec, ':');
+
+ if (!(colon && hash && colon < hash) && hash)
+ hash = strchr(hash + 1, '#');
+ if (hash) {
+ *conf_name = hash;
+ *hash = '\0';
+ }
+ ret = image_loader_init_blk(ldr, "mmc", devspec);
+ if (hash)
+ *hash = '#';
+ } else if (IS_ENABLED(CONFIG_IMAGE_LOADER_MTD) &&
+ !strcmp(devtype, "mtd")) {
+ if (hash) {
+ *conf_name = hash;
+ *hash = '\0';
+ }
+ ret = image_loader_init_mtd(ldr, devspec);
+ if (hash)
+ *hash = '#';
+ } else if (IS_ENABLED(CONFIG_IMAGE_LOADER_UBI) &&
+ !strcmp(devtype, "ubi")) {
+ if (hash) {
+ *conf_name = hash;
+ *hash = '\0';
+ }
+ ret = image_loader_init_ubi(ldr, devspec);
+ if (hash)
+ *hash = '#';
+ } else {
+ return 0; /* not a storage keyword */
+ }
+
+ if (ret) {
+ printf("Failed to init %s loader: %d\n", devtype, ret);
+ return ret;
+ }
+
+ return 2; /* consumed: keyword + device-spec */
+}
+#endif
+
int do_bootm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
{
struct bootm_info bmi;
int ret;
+#if IS_ENABLED(CONFIG_BOOTM_STORAGE)
+ struct image_loader ldr = {};
+ const char *conf_name = NULL;
+ int consumed = 0;
+#endif
/* determine if we have a sub command */
argc--; argv++;
+
+#if IS_ENABLED(CONFIG_BOOTM_STORAGE)
+ /* Try storage device spec before checking for hex/subcommand */
+ consumed = bootm_init_loader(&ldr, argc, argv, &conf_name);
+ if (consumed < 0)
+ return CMD_RET_FAILURE;
+ if (consumed > 0) {
+ ldr.alloc_ptr = image_load_addr;
+ argc -= consumed;
+ argv += consumed;
+ }
+#endif
+
+#if IS_ENABLED(CONFIG_BOOTM_STORAGE)
+ if (argc > 0 && !consumed) {
+#else
if (argc > 0) {
+#endif
char *endp;
hextoul(argv[0], &endp);
@@ -156,12 +266,25 @@ int do_bootm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
}
bootm_init(&bmi);
- if (argc)
- bmi.addr_img = argv[0];
- if (argc > 1)
- bmi.conf_ramdisk = argv[1];
- if (argc > 2)
- bmi.conf_fdt = argv[2];
+#if IS_ENABLED(CONFIG_BOOTM_STORAGE)
+ if (consumed > 0) {
+ bmi.addr_img = conf_name; /* "#config-1" or NULL */
+ bmi.loader = &ldr;
+ if (argc > 0)
+ bmi.conf_ramdisk = argv[0];
+ if (argc > 1)
+ bmi.conf_fdt = argv[1];
+ } else {
+#else
+ {
+#endif
+ if (argc)
+ bmi.addr_img = argv[0];
+ if (argc > 1)
+ bmi.conf_ramdisk = argv[1];
+ if (argc > 2)
+ bmi.conf_fdt = argv[2];
+ }
/* set up argc and argv[] since some OSes use them */
bmi.argc = argc;
@@ -169,6 +292,11 @@ int do_bootm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
ret = bootm_run(&bmi);
+#if IS_ENABLED(CONFIG_BOOTM_STORAGE)
+ if (ldr.read)
+ image_loader_cleanup(&ldr);
+#endif
+
return ret ? CMD_RET_FAILURE : 0;
}
@@ -204,6 +332,14 @@ U_BOOT_LONGHELP(bootm,
"\taddr#<conf_uname> - configuration specification\n"
"\tUse iminfo command to get the list of existing component\n"
"\timages and configurations.\n"
+#endif
+#if IS_ENABLED(CONFIG_BOOTM_STORAGE)
+ "\nAlternatively, boot directly from a storage device:\n"
+ "\tbootm mmc <dev>:<part>[#conf] - boot from MMC/SD partition\n"
+ "\tbootm mtd <name>[#conf] - boot from MTD partition\n"
+ "\tbootm ubi <volume>[#conf] - boot from UBI volume\n"
+ "\tThe #conf suffix selects a FIT configuration; additional\n"
+ "\t#overlay suffixes select device-tree overlays.\n"
#endif
"\nSub-commands to do part of the bootm sequence. The sub-commands "
"must be\n"
diff --git a/include/bootm.h b/include/bootm.h
index 4060cec7fc0..ebc014b3468 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
+ * @loader: image loader 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 image_loader *loader;
const char *cmd_name;
int argc;
char *const *argv;
--
2.53.0
^ permalink raw reply related [flat|nested] 88+ messages in thread* Re: [RFC PATCH 10/20] cmd: bootm: accept storage device as image source
2026-02-16 21:22 ` [RFC PATCH 10/20] cmd: bootm: accept storage device as image source Daniel Golle
@ 2026-02-19 13:09 ` Simon Glass
0 siblings, 0 replies; 88+ messages in thread
From: Simon Glass @ 2026-02-19 13:09 UTC (permalink / raw)
To: Daniel Golle
Cc: Tom Rini, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
Hi Daniel,
On Mon, 16 Feb 2026 at 14:22, Daniel Golle <daniel@makrotopia.org> wrote:
>
> Extend the bootm command to accept a storage device type keyword as
> the first argument:
>
> bootm mmc <dev>:<part> - boot from MMC/SD/eMMC partition
> bootm mtd <name> - boot from MTD partition
> bootm ubi <volume> - boot from UBI volume
>
> When a known device-type keyword is found, an image_loader is
> constructed using the corresponding backend and stored in
> images.loader. The rest of the bootm flow proceeds unchanged:
> boot_get_kernel() uses image_loader_map() for format detection and
> FDT structure loading, then fit_image_load() uses the on-demand
> storage path for sub-image data.
>
> If the first argument does not match a device-type keyword (i.e. it
> looks like a hex address, or is absent), images.loader remains NULL
> and the existing in-memory path is taken -- full backward compatibility.
>
> The #config suffix follows the same convention as existing bootm FIT
> config selection. Multiple # suffixes can be chained to select a
> base config and one or more device-tree overlays:
>
> bootm mmc 0:4#config-1#overlay-wifi#overlay-lcd
> bootm mtd firmware#config-1
>
> Gated by CONFIG_BOOTM_STORAGE.
>
> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> ---
> boot/Kconfig | 13 +++++
> boot/bootm.c | 62 +++++++++++++++++---
> cmd/bootm.c | 148 ++++++++++++++++++++++++++++++++++++++++++++++--
> include/bootm.h | 2 +
> 4 files changed, 212 insertions(+), 13 deletions(-)
>
> diff --git a/boot/Kconfig b/boot/Kconfig
> index 89832014af6..1f870c7d251 100644
> --- a/boot/Kconfig
> +++ b/boot/Kconfig
> @@ -1211,6 +1211,19 @@ config IMAGE_LOADER_UBI
> framework. Auto-attaches the UBI device from the device tree
> if not already attached.
>
> +config BOOTM_STORAGE
> + bool "Allow bootm to load images directly from storage"
> + depends on CMDLINE && FIT && IMAGE_LOADER
> + help
> + Extends the bootm command to accept "mmc <dev>:<part>",
> + "mtd <name>", or "ubi <volume>" as the image source instead
> + of a RAM address. Sub-images (kernel, FDT, ramdisk, etc.)
> + are loaded from storage on demand; filesystem sub-images are
> + never read.
> +
> + Requires at least one IMAGE_LOADER backend to be enabled
> + (IMAGE_LOADER_BLK, IMAGE_LOADER_MTD, or IMAGE_LOADER_UBI).
> +
> config DISTRO_DEFAULTS
> bool "(deprecated) Script-based booting of Linux distributions"
> select CMDLINE
> diff --git a/boot/bootm.c b/boot/bootm.c
> index 4bdca22ea8c..67d161f6e3a 100644
> --- a/boot/bootm.c
> +++ b/boot/bootm.c
> @@ -13,6 +13,7 @@
> #include <env.h>
> #include <errno.h>
> #include <fdt_support.h>
> +#include <image-loader.h>
> #include <irq_func.h>
> #include <lmb.h>
> #include <log.h>
> @@ -146,7 +147,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_BOOTM_STORAGE) && images->loader) {
> + /*
> + * 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 = image_loader_map(images->loader, 0, 64);
> + if (!buf) {
> + puts("Cannot read image header from storage\n");
> + return -EIO;
> + }
> + 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:
> @@ -192,6 +208,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_BOOTM_STORAGE) && images->loader) {
> + /*
> + * Extend the mapping to cover the full FIT
> + * FDT structure so all metadata is accessible.
> + */
> + size_t fdt_sz = fdt_totalsize(buf);
> +
> + buf = image_loader_map(images->loader, 0, fdt_sz);
> + if (!buf) {
> + puts("Cannot read FIT header from storage\n");
> + return -ENOMEM;
> + }
> + 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,
> @@ -991,11 +1021,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_BOOTM_STORAGE) && bmi->loader)
> + images->loader = bmi->loader;
> + }
>
> - 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_BOOTM_STORAGE) || !images->loader)
> + ret = bootm_pre_load(bmi->addr_img);
> + }
>
> if (!ret && (states & BOOTM_STATE_FINDOS))
> ret = bootm_find_os(bmi->cmd_name, bmi->addr_img);
> @@ -1003,8 +1043,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_BOOTM_STORAGE) && images->loader)
> + 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);
> }
> @@ -1097,8 +1140,13 @@ 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_BOOTM_STORAGE) && images->loader)
> + image_loader_cleanup(images->loader);
> +
> ret = boot_selected_os(BOOTM_STATE_OS_GO, bmi, boot_fn);
> + }
>
> /* Deal with any fallout */
> err:
> diff --git a/cmd/bootm.c b/cmd/bootm.c
> index 2c5aea26d98..6c26dd8e36d 100644
> --- a/cmd/bootm.c
> +++ b/cmd/bootm.c
> @@ -12,6 +12,7 @@
> #include <env.h>
> #include <errno.h>
> #include <image.h>
> +#include <image-loader.h>
> #include <malloc.h>
> #include <nand.h>
> #include <asm/byteorder.h>
> @@ -132,14 +133,123 @@ static int do_bootm_subcommand(struct cmd_tbl *cmdtp, int flag, int argc,
> /* bootm - boot application image from image in memory */
> /*******************************************************************/
>
> +#if IS_ENABLED(CONFIG_BOOTM_STORAGE)
Avoid #if - use if() and the toolchain will handle eliminating the code.
> +/**
> + * bootm_init_loader() - Try to parse a storage device spec from argv
> + *
> + * If argv[0] is a known device-type keyword ("mmc", "mtd", "ubi"),
> + * initialise @ldr as the corresponding backend and return the number
> + * of argv entries consumed. The optional #config[#overlay...] suffix
> + * is stripped from the device spec and returned via @conf_name.
> + *
> + * @ldr: loader to initialise
> + * @argc: argument count (after removing the "bootm" verb)
> + * @argv: argument vector
> + * @conf_name: on output, points to the #config string (incl. '#') or NULL
> + * Return: number of argv entries consumed (>0), 0 if no storage keyword
> + * was found, or negative errno on init failure
> + */
> +static int bootm_init_loader(struct image_loader *ldr,
> + int argc, char *const argv[],
> + const char **conf_name)
> +{
> + const char *devtype;
> + char *devspec;
> + char *hash;
> + int ret;
> +
> + if (argc < 2)
> + return 0;
> +
> + devtype = argv[0];
> + devspec = (char *)argv[1];
> + *conf_name = NULL;
> +
> + /* Locate the first '#' — its meaning depends on the backend */
> + hash = strchr(devspec, '#');
> +
> + if (IS_ENABLED(CONFIG_IMAGE_LOADER_BLK) &&
> + !strcmp(devtype, "mmc")) {
> + /*
> + * Block device specs use '#' for partition names when no ':'
> + * precedes it (e.g. "0#kernel"). Only treat the first '#'
> + * as the FIT config separator when ':' appears before it
> + * (e.g. "0:4#config-1"), otherwise the first '#' is the
> + * partition name and a second '#' starts the config
> + * (e.g. "0#kernel#config-1").
> + */
> + char *colon = strchr(devspec, ':');
> +
> + if (!(colon && hash && colon < hash) && hash)
> + hash = strchr(hash + 1, '#');
> + if (hash) {
> + *conf_name = hash;
> + *hash = '\0';
> + }
> + ret = image_loader_init_blk(ldr, "mmc", devspec);
> + if (hash)
> + *hash = '#';
> + } else if (IS_ENABLED(CONFIG_IMAGE_LOADER_MTD) &&
> + !strcmp(devtype, "mtd")) {
> + if (hash) {
> + *conf_name = hash;
> + *hash = '\0';
> + }
> + ret = image_loader_init_mtd(ldr, devspec);
> + if (hash)
> + *hash = '#';
> + } else if (IS_ENABLED(CONFIG_IMAGE_LOADER_UBI) &&
> + !strcmp(devtype, "ubi")) {
> + if (hash) {
> + *conf_name = hash;
> + *hash = '\0';
> + }
> + ret = image_loader_init_ubi(ldr, devspec);
> + if (hash)
> + *hash = '#';
> + } else {
> + return 0; /* not a storage keyword */
> + }
> +
> + if (ret) {
> + printf("Failed to init %s loader: %d\n", devtype, ret);
> + return ret;
> + }
> +
> + return 2; /* consumed: keyword + device-spec */
> +}
> +#endif
> +
> int do_bootm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
> {
> struct bootm_info bmi;
> int ret;
> +#if IS_ENABLED(CONFIG_BOOTM_STORAGE)
> + struct image_loader ldr = {};
> + const char *conf_name = NULL;
> + int consumed = 0;
> +#endif
>
> /* determine if we have a sub command */
> argc--; argv++;
> +
> +#if IS_ENABLED(CONFIG_BOOTM_STORAGE)
> + /* Try storage device spec before checking for hex/subcommand */
> + consumed = bootm_init_loader(&ldr, argc, argv, &conf_name);
> + if (consumed < 0)
> + return CMD_RET_FAILURE;
> + if (consumed > 0) {
> + ldr.alloc_ptr = image_load_addr;
> + argc -= consumed;
> + argv += consumed;
> + }
> +#endif
> +
> +#if IS_ENABLED(CONFIG_BOOTM_STORAGE)
> + if (argc > 0 && !consumed) {
> +#else
> if (argc > 0) {
> +#endif
> char *endp;
>
> hextoul(argv[0], &endp);
> @@ -156,12 +266,25 @@ int do_bootm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
> }
>
> bootm_init(&bmi);
> - if (argc)
> - bmi.addr_img = argv[0];
> - if (argc > 1)
> - bmi.conf_ramdisk = argv[1];
> - if (argc > 2)
> - bmi.conf_fdt = argv[2];
> +#if IS_ENABLED(CONFIG_BOOTM_STORAGE)
> + if (consumed > 0) {
> + bmi.addr_img = conf_name; /* "#config-1" or NULL */
> + bmi.loader = &ldr;
> + if (argc > 0)
> + bmi.conf_ramdisk = argv[0];
> + if (argc > 1)
> + bmi.conf_fdt = argv[1];
> + } else {
> +#else
> + {
> +#endif
> + if (argc)
> + bmi.addr_img = argv[0];
> + if (argc > 1)
> + bmi.conf_ramdisk = argv[1];
> + if (argc > 2)
> + bmi.conf_fdt = argv[2];
> + }
>
> /* set up argc and argv[] since some OSes use them */
> bmi.argc = argc;
> @@ -169,6 +292,11 @@ int do_bootm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
>
> ret = bootm_run(&bmi);
>
> +#if IS_ENABLED(CONFIG_BOOTM_STORAGE)
> + if (ldr.read)
> + image_loader_cleanup(&ldr);
> +#endif
> +
> return ret ? CMD_RET_FAILURE : 0;
> }
>
> @@ -204,6 +332,14 @@ U_BOOT_LONGHELP(bootm,
> "\taddr#<conf_uname> - configuration specification\n"
> "\tUse iminfo command to get the list of existing component\n"
> "\timages and configurations.\n"
> +#endif
> +#if IS_ENABLED(CONFIG_BOOTM_STORAGE)
> + "\nAlternatively, boot directly from a storage device:\n"
> + "\tbootm mmc <dev>:<part>[#conf] - boot from MMC/SD partition\n"
> + "\tbootm mtd <name>[#conf] - boot from MTD partition\n"
> + "\tbootm ubi <volume>[#conf] - boot from UBI volume\n"
> + "\tThe #conf suffix selects a FIT configuration; additional\n"
> + "\t#overlay suffixes select device-tree overlays.\n"
> #endif
> "\nSub-commands to do part of the bootm sequence. The sub-commands "
> "must be\n"
> diff --git a/include/bootm.h b/include/bootm.h
> index 4060cec7fc0..ebc014b3468 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
> + * @loader: image loader 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 image_loader *loader;
This will probably end up being a struct udevice *
> const char *cmd_name;
> int argc;
> char *const *argv;
> --
> 2.53.0
Regards,
Simon
^ permalink raw reply [flat|nested] 88+ messages in thread
* [RFC PATCH 11/20] test: boot: add image_loader unit tests
2026-02-16 21:21 [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading Daniel Golle
` (9 preceding siblings ...)
2026-02-16 21:22 ` [RFC PATCH 10/20] cmd: bootm: accept storage device as image source Daniel Golle
@ 2026-02-16 21:22 ` Daniel Golle
2026-02-17 19:05 ` Tom Rini
2026-02-19 13:10 ` Simon Glass
2026-02-16 21:23 ` [RFC PATCH 12/20] doc: bootm: document direct storage boot Daniel Golle
` (14 subsequent siblings)
25 siblings, 2 replies; 88+ messages in thread
From: Daniel Golle @ 2026-02-16 21:22 UTC (permalink / raw)
To: Tom Rini, Simon Glass, Quentin Schulz, Daniel Golle,
Kory Maincent, Mattijs Korpershoek, Martin Schwan, Anshul Dalal,
Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
牛 志宏, Marek Vasut, Heinrich Schuchardt,
Wolfgang Wallner, Frank Wunderlich, David Lechner,
Osama Abdelkader, Mikhail Kshevetskiy, Michael Trimarchi,
Miquel Raynal, Andrew Goodbody, Yegor Yefremov, Mike Looijmans,
Weijie Gao, Alexander Stein, Neil Armstrong, Mayuresh Chitale,
Paul HENRYS, u-boot
Cc: John Crispin, Paul Spooren
Add unit tests for the image_loader framework covering its core
logic with a mock storage backend:
- map() allocates, reads and records a region
- map() returns cached pointer for already-mapped range
- map() returns correct offset within a larger region
- map() re-reads when extending a region to a larger size
- map_to() reads to a specified address and records it
- lookup() returns NULL for unmapped ranges
- alloc_ptr advances with correct alignment
- map() returns NULL when the translation table is full
- cleanup() calls backend and resets state
- map() with multiple disjoint regions
- read beyond image size returns error
Also fix IMAGE_LOADER_MAX_REGIONS Kconfig to depend on IMAGE_LOADER
and default to 16 unconditionally (the previous 'default 0' fallback
caused the regions array to be zero-sized when IMAGE_LOADER was
enabled after initial defconfig generation).
Register the new 'image_loader' test suite in test/cmd_ut.c so it
can be run via 'ut image_loader'.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
boot/Kconfig | 4 +-
test/boot/Makefile | 2 +
test/boot/image_loader.c | 429 +++++++++++++++++++++++++++++++++++++++
test/cmd_ut.c | 2 +
4 files changed, 435 insertions(+), 2 deletions(-)
create mode 100644 test/boot/image_loader.c
diff --git a/boot/Kconfig b/boot/Kconfig
index 1f870c7d251..efc06f3cd1a 100644
--- a/boot/Kconfig
+++ b/boot/Kconfig
@@ -1179,8 +1179,8 @@ config IMAGE_LOADER
config IMAGE_LOADER_MAX_REGIONS
int "Maximum number of mapped regions in image loader"
- default 16 if IMAGE_LOADER
- default 0
+ depends on IMAGE_LOADER
+ default 16
help
Maximum number of distinct image regions that can be mapped
into RAM simultaneously. 16 is sufficient for typical FIT
diff --git a/test/boot/Makefile b/test/boot/Makefile
index 89538d4f0a6..6fd349a65bc 100644
--- a/test/boot/Makefile
+++ b/test/boot/Makefile
@@ -23,3 +23,5 @@ endif
obj-$(CONFIG_BOOTMETH_VBE) += vbe_fixup.o
obj-$(CONFIG_UPL) += upl.o
+
+obj-$(CONFIG_IMAGE_LOADER) += image_loader.o
diff --git a/test/boot/image_loader.c b/test/boot/image_loader.c
new file mode 100644
index 00000000000..dc4b0b4173a
--- /dev/null
+++ b/test/boot/image_loader.c
@@ -0,0 +1,429 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for image_loader framework
+ *
+ * Copyright (C) 2026 Daniel Golle <daniel@makrotopia.org>
+ */
+
+#include <image-loader.h>
+#include <mapmem.h>
+#include <malloc.h>
+#include <asm/cache.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+#define IMG_LOADER_TEST(_name, _flags) \
+ UNIT_TEST(_name, _flags, image_loader)
+
+/* Synthetic image size used throughout the tests */
+#define IMAGE_SIZE 4096
+
+/**
+ * struct mock_priv - private data for the mock storage backend
+ *
+ * @image: pointer to synthetic image data in RAM
+ * @image_size: size of the synthetic image
+ * @read_count: number of times .read() was called
+ * @last_off: offset from the most recent .read() call
+ * @last_size: size from the most recent .read() call
+ */
+struct mock_priv {
+ const void *image;
+ size_t image_size;
+ int read_count;
+ ulong last_off;
+ ulong last_size;
+};
+
+static int mock_read(struct image_loader *ldr, ulong src, ulong size,
+ void *dst)
+{
+ struct mock_priv *p = ldr->priv;
+
+ if (src + size > p->image_size)
+ return -EINVAL;
+
+ memcpy(dst, (const char *)p->image + src, size);
+ p->read_count++;
+ p->last_off = src;
+ p->last_size = size;
+
+ return 0;
+}
+
+static void mock_cleanup(struct image_loader *ldr)
+{
+ /* Nothing dynamic to free — just verify it's called */
+}
+
+/**
+ * init_mock_loader() - set up a loader with the mock backend
+ *
+ * @ldr: loader to initialise
+ * @priv: mock private data (caller-allocated)
+ * @image: synthetic image buffer
+ * @image_size: size of @image
+ * @alloc_base: RAM address to use as alloc_ptr base
+ */
+static void init_mock_loader(struct image_loader *ldr, struct mock_priv *priv,
+ const void *image, size_t image_size,
+ ulong alloc_base)
+{
+ memset(ldr, 0, sizeof(*ldr));
+ memset(priv, 0, sizeof(*priv));
+
+ priv->image = image;
+ priv->image_size = image_size;
+
+ ldr->read = mock_read;
+ ldr->cleanup = mock_cleanup;
+ ldr->priv = priv;
+ ldr->alloc_ptr = alloc_base;
+}
+
+/* Test: map() allocates, reads and records a region */
+static int image_loader_test_map_basic(struct unit_test_state *uts)
+{
+ struct image_loader ldr;
+ struct mock_priv mock;
+ u8 image[IMAGE_SIZE];
+ void *p;
+
+ /* Fill image with a recognisable pattern */
+ for (int i = 0; i < IMAGE_SIZE; i++)
+ image[i] = (u8)(i & 0xff);
+
+ init_mock_loader(&ldr, &mock, image, IMAGE_SIZE, 0x1000000);
+
+ /* Map a 64-byte region at offset 0 */
+ p = image_loader_map(&ldr, 0, 64);
+ ut_assertnonnull(p);
+ ut_asserteq_mem(image, p, 64);
+ ut_asserteq(1, mock.read_count);
+ ut_asserteq(1, ldr.nr_regions);
+ ut_asserteq(0, (int)ldr.regions[0].img_offset);
+ ut_asserteq(64, (int)ldr.regions[0].size);
+
+ return 0;
+}
+
+IMG_LOADER_TEST(image_loader_test_map_basic, 0);
+
+/* Test: map() returns cached pointer for already-mapped range */
+static int image_loader_test_map_cached(struct unit_test_state *uts)
+{
+ struct image_loader ldr;
+ struct mock_priv mock;
+ u8 image[IMAGE_SIZE];
+ void *p1, *p2;
+
+ memset(image, 0xaa, IMAGE_SIZE);
+ init_mock_loader(&ldr, &mock, image, IMAGE_SIZE, 0x1000000);
+
+ p1 = image_loader_map(&ldr, 0, 128);
+ ut_assertnonnull(p1);
+ ut_asserteq(1, mock.read_count);
+
+ /* Same range — should return same pointer, no new read */
+ p2 = image_loader_map(&ldr, 0, 128);
+ ut_asserteq_ptr(p1, p2);
+ ut_asserteq(1, mock.read_count);
+
+ /* Subset of the already-mapped range — still cached */
+ p2 = image_loader_map(&ldr, 0, 64);
+ ut_asserteq_ptr(p1, p2);
+ ut_asserteq(1, mock.read_count);
+
+ return 0;
+}
+
+IMG_LOADER_TEST(image_loader_test_map_cached, 0);
+
+/* Test: map() returns correct offset within a larger region */
+static int image_loader_test_map_offset(struct unit_test_state *uts)
+{
+ struct image_loader ldr;
+ struct mock_priv mock;
+ u8 image[IMAGE_SIZE];
+ void *p1, *p2;
+
+ for (int i = 0; i < IMAGE_SIZE; i++)
+ image[i] = (u8)(i & 0xff);
+
+ init_mock_loader(&ldr, &mock, image, IMAGE_SIZE, 0x1000000);
+
+ /* Map a 256-byte region starting at offset 0 */
+ p1 = image_loader_map(&ldr, 0, 256);
+ ut_assertnonnull(p1);
+
+ /* Request a sub-range within the previously mapped region */
+ p2 = image_loader_map(&ldr, 64, 64);
+ ut_assertnonnull(p2);
+ /* p2 should point 64 bytes into p1 */
+ ut_asserteq_ptr((char *)p1 + 64, p2);
+ ut_asserteq_mem(image + 64, p2, 64);
+ /* Only one read should have occurred */
+ ut_asserteq(1, mock.read_count);
+
+ return 0;
+}
+
+IMG_LOADER_TEST(image_loader_test_map_offset, 0);
+
+/* Test: map() re-reads when extending a region to a larger size */
+static int image_loader_test_map_extend(struct unit_test_state *uts)
+{
+ struct image_loader ldr;
+ struct mock_priv mock;
+ u8 image[IMAGE_SIZE];
+ void *p1, *p2;
+
+ for (int i = 0; i < IMAGE_SIZE; i++)
+ image[i] = (u8)(i & 0xff);
+
+ init_mock_loader(&ldr, &mock, image, IMAGE_SIZE, 0x1000000);
+
+ /* Initial small mapping */
+ p1 = image_loader_map(&ldr, 0, 64);
+ ut_assertnonnull(p1);
+ ut_asserteq(1, mock.read_count);
+
+ /* Request larger range at same base — should re-read (extend) */
+ p2 = image_loader_map(&ldr, 0, 256);
+ ut_assertnonnull(p2);
+ ut_asserteq(2, mock.read_count);
+ ut_asserteq_ptr(p1, p2); /* same RAM base */
+ ut_asserteq_mem(image, p2, 256);
+
+ /* Region count should still be 1 (updated, not added) */
+ ut_asserteq(1, ldr.nr_regions);
+ ut_asserteq(256, (int)ldr.regions[0].size);
+
+ /* alloc_ptr must have advanced past the extended region */
+ ut_assert(ldr.alloc_ptr >= 0x1000000 + 256);
+
+ /*
+ * Map a new region after the extend — it must not overlap the
+ * extended first region. This is the exact pattern that bit us
+ * on real hardware: FIT header at offset 0 extended from 64 to
+ * 4096, then kernel payload at offset 4096 was allocated at
+ * alloc_ptr that hadn't been advanced, clobbering the header.
+ */
+ {
+ void *p3 = image_loader_map(&ldr, 256, 128);
+
+ ut_assertnonnull(p3);
+ ut_asserteq(2, ldr.nr_regions);
+ /* New region must start at or after the extended region end */
+ ut_assert((ulong)map_to_sysmem(p3) >= 0x1000000 + 256);
+ ut_asserteq_mem(image + 256, p3, 128);
+ }
+
+ return 0;
+}
+
+IMG_LOADER_TEST(image_loader_test_map_extend, 0);
+
+/* Test: map_to() reads to a specified address and records it */
+static int image_loader_test_map_to(struct unit_test_state *uts)
+{
+ struct image_loader ldr;
+ struct mock_priv mock;
+ u8 image[IMAGE_SIZE];
+ u8 dst[256];
+ void *p;
+
+ for (int i = 0; i < IMAGE_SIZE; i++)
+ image[i] = (u8)(i & 0xff);
+
+ init_mock_loader(&ldr, &mock, image, IMAGE_SIZE, 0x1000000);
+
+ p = image_loader_map_to(&ldr, 128, 256, dst);
+ ut_asserteq_ptr(dst, p);
+ ut_asserteq_mem(image + 128, dst, 256);
+ ut_asserteq(1, mock.read_count);
+ ut_asserteq(1, ldr.nr_regions);
+ ut_asserteq(128, (int)ldr.regions[0].img_offset);
+ ut_asserteq(256, (int)ldr.regions[0].size);
+ ut_asserteq_ptr(dst, ldr.regions[0].ram);
+
+ return 0;
+}
+
+IMG_LOADER_TEST(image_loader_test_map_to, 0);
+
+/* Test: lookup() returns NULL for unmapped ranges */
+static int image_loader_test_lookup_miss(struct unit_test_state *uts)
+{
+ struct image_loader ldr;
+ struct mock_priv mock;
+ u8 image[IMAGE_SIZE];
+ void *p;
+
+ init_mock_loader(&ldr, &mock, image, IMAGE_SIZE, 0x1000000);
+
+ /* Nothing mapped yet — should return NULL */
+ p = image_loader_lookup(&ldr, 0, 64);
+ ut_assertnull(p);
+
+ /* Map a region at offset 0 */
+ ut_assertnonnull(image_loader_map(&ldr, 0, 64));
+
+ /* Lookup within the mapped region — should succeed */
+ p = image_loader_lookup(&ldr, 0, 32);
+ ut_assertnonnull(p);
+
+ /* Lookup at a different offset — should miss */
+ p = image_loader_lookup(&ldr, 128, 32);
+ ut_assertnull(p);
+
+ /* Lookup extending beyond the mapped region — should miss */
+ p = image_loader_lookup(&ldr, 0, 128);
+ ut_assertnull(p);
+
+ return 0;
+}
+
+IMG_LOADER_TEST(image_loader_test_lookup_miss, 0);
+
+/* Test: alloc_ptr advances with correct alignment */
+static int image_loader_test_alloc_advance(struct unit_test_state *uts)
+{
+ struct image_loader ldr;
+ struct mock_priv mock;
+ u8 image[IMAGE_SIZE];
+ ulong base = 0x1000000;
+ ulong expected;
+
+ init_mock_loader(&ldr, &mock, image, IMAGE_SIZE, base);
+
+ /* Map 100 bytes — alloc_ptr should advance to ALIGN(base + 100) */
+ ut_assertnonnull(image_loader_map(&ldr, 0, 100));
+ expected = ALIGN(base + 100, ARCH_DMA_MINALIGN);
+ ut_asserteq(expected, ldr.alloc_ptr);
+
+ /* Map another 200 bytes at a different offset */
+ ut_assertnonnull(image_loader_map(&ldr, 200, 200));
+ expected = ALIGN(expected + 200, ARCH_DMA_MINALIGN);
+ ut_asserteq(expected, ldr.alloc_ptr);
+
+ return 0;
+}
+
+IMG_LOADER_TEST(image_loader_test_alloc_advance, 0);
+
+/* Test: map() returns NULL when the translation table is full */
+static int image_loader_test_table_full(struct unit_test_state *uts)
+{
+ struct image_loader ldr;
+ struct mock_priv mock;
+ u8 image[IMAGE_SIZE];
+ void *p;
+ int i;
+
+ init_mock_loader(&ldr, &mock, image, IMAGE_SIZE, 0x1000000);
+
+ /* Fill all region slots with distinct offsets */
+ for (i = 0; i < CONFIG_IMAGE_LOADER_MAX_REGIONS; i++) {
+ p = image_loader_map(&ldr, i * 16, 8);
+ ut_assertnonnull(p);
+ }
+
+ ut_asserteq(CONFIG_IMAGE_LOADER_MAX_REGIONS, ldr.nr_regions);
+
+ /* Next map at a new offset should fail (table full) */
+ p = image_loader_map(&ldr, CONFIG_IMAGE_LOADER_MAX_REGIONS * 16, 8);
+ ut_assertnull(p);
+
+ return 0;
+}
+
+IMG_LOADER_TEST(image_loader_test_table_full, 0);
+
+/* Test: cleanup() calls the backend and resets state */
+static int image_loader_test_cleanup(struct unit_test_state *uts)
+{
+ struct image_loader ldr;
+ struct mock_priv mock;
+ u8 image[IMAGE_SIZE];
+
+ init_mock_loader(&ldr, &mock, image, IMAGE_SIZE, 0x1000000);
+
+ /* Map something so nr_regions > 0 */
+ ut_assertnonnull(image_loader_map(&ldr, 0, 64));
+ ut_asserteq(1, ldr.nr_regions);
+
+ image_loader_cleanup(&ldr);
+
+ ut_assertnull(ldr.read);
+ ut_assertnull(ldr.cleanup);
+ ut_assertnull(ldr.priv);
+ ut_asserteq(0, ldr.nr_regions);
+
+ return 0;
+}
+
+IMG_LOADER_TEST(image_loader_test_cleanup, 0);
+
+/* Test: map() with multiple disjoint regions */
+static int image_loader_test_multi_region(struct unit_test_state *uts)
+{
+ struct image_loader ldr;
+ struct mock_priv mock;
+ u8 image[IMAGE_SIZE];
+ void *p1, *p2, *p3;
+
+ for (int i = 0; i < IMAGE_SIZE; i++)
+ image[i] = (u8)(i & 0xff);
+
+ init_mock_loader(&ldr, &mock, image, IMAGE_SIZE, 0x1000000);
+
+ p1 = image_loader_map(&ldr, 0, 64);
+ ut_assertnonnull(p1);
+ p2 = image_loader_map(&ldr, 512, 128);
+ ut_assertnonnull(p2);
+ p3 = image_loader_map(&ldr, 1024, 256);
+ ut_assertnonnull(p3);
+
+ ut_asserteq(3, ldr.nr_regions);
+ ut_asserteq(3, mock.read_count);
+
+ /* Verify data in each region */
+ ut_asserteq_mem(image, p1, 64);
+ ut_asserteq_mem(image + 512, p2, 128);
+ ut_asserteq_mem(image + 1024, p3, 256);
+
+ /* Lookup each region */
+ ut_asserteq_ptr(p1, image_loader_lookup(&ldr, 0, 64));
+ ut_asserteq_ptr(p2, image_loader_lookup(&ldr, 512, 128));
+ ut_asserteq_ptr(p3, image_loader_lookup(&ldr, 1024, 256));
+
+ return 0;
+}
+
+IMG_LOADER_TEST(image_loader_test_multi_region, 0);
+
+/* Test: read beyond image size returns error */
+static int image_loader_test_read_oob(struct unit_test_state *uts)
+{
+ struct image_loader ldr;
+ struct mock_priv mock;
+ u8 image[IMAGE_SIZE];
+ void *p;
+
+ init_mock_loader(&ldr, &mock, image, IMAGE_SIZE, 0x1000000);
+
+ /* Attempt to map beyond the end of the image */
+ p = image_loader_map(&ldr, IMAGE_SIZE - 32, 64);
+ ut_assertnull(p);
+
+ /* map_to should also fail */
+ u8 dst[64];
+
+ p = image_loader_map_to(&ldr, IMAGE_SIZE - 32, 64, dst);
+ ut_assertnull(p);
+
+ return 0;
+}
+
+IMG_LOADER_TEST(image_loader_test_read_oob, 0);
diff --git a/test/cmd_ut.c b/test/cmd_ut.c
index 44e5fdfdaa6..3b907a12e4e 100644
--- a/test/cmd_ut.c
+++ b/test/cmd_ut.c
@@ -71,6 +71,7 @@ SUITE_DECL(optee);
SUITE_DECL(pci_mps);
SUITE_DECL(seama);
SUITE_DECL(setexpr);
+SUITE_DECL(image_loader);
SUITE_DECL(upl);
static struct suite suites[] = {
@@ -98,6 +99,7 @@ static struct suite suites[] = {
SUITE(pci_mps, "PCI Express Maximum Payload Size"),
SUITE(seama, "seama command parameters loading and decoding"),
SUITE(setexpr, "setexpr command"),
+ SUITE(image_loader, "image_loader on-demand storage loading"),
SUITE(upl, "Universal payload support"),
};
--
2.53.0
^ permalink raw reply related [flat|nested] 88+ messages in thread* Re: [RFC PATCH 11/20] test: boot: add image_loader unit tests
2026-02-16 21:22 ` [RFC PATCH 11/20] test: boot: add image_loader unit tests Daniel Golle
@ 2026-02-17 19:05 ` Tom Rini
2026-02-19 13:10 ` Simon Glass
1 sibling, 0 replies; 88+ messages in thread
From: Tom Rini @ 2026-02-17 19:05 UTC (permalink / raw)
To: Daniel Golle
Cc: Simon Glass, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
[-- Attachment #1: Type: text/plain, Size: 1186 bytes --]
On Mon, Feb 16, 2026 at 09:22:59PM +0000, Daniel Golle wrote:
> Add unit tests for the image_loader framework covering its core
> logic with a mock storage backend:
>
> - map() allocates, reads and records a region
> - map() returns cached pointer for already-mapped range
> - map() returns correct offset within a larger region
> - map() re-reads when extending a region to a larger size
> - map_to() reads to a specified address and records it
> - lookup() returns NULL for unmapped ranges
> - alloc_ptr advances with correct alignment
> - map() returns NULL when the translation table is full
> - cleanup() calls backend and resets state
> - map() with multiple disjoint regions
> - read beyond image size returns error
>
> Also fix IMAGE_LOADER_MAX_REGIONS Kconfig to depend on IMAGE_LOADER
> and default to 16 unconditionally (the previous 'default 0' fallback
> caused the regions array to be zero-sized when IMAGE_LOADER was
> enabled after initial defconfig generation).
Since this is introduced earlier in the series, it should just be done
correctly to start with. The bias in me wants to say the AI didn't catch
this and should have.
--
Tom
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [RFC PATCH 11/20] test: boot: add image_loader unit tests
2026-02-16 21:22 ` [RFC PATCH 11/20] test: boot: add image_loader unit tests Daniel Golle
2026-02-17 19:05 ` Tom Rini
@ 2026-02-19 13:10 ` Simon Glass
2026-02-19 14:04 ` Tom Rini
1 sibling, 1 reply; 88+ messages in thread
From: Simon Glass @ 2026-02-19 13:10 UTC (permalink / raw)
To: Daniel Golle
Cc: Tom Rini, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
Hi Daniel,
On Mon, 16 Feb 2026 at 14:23, Daniel Golle <daniel@makrotopia.org> wrote:
>
> Add unit tests for the image_loader framework covering its core
> logic with a mock storage backend:
>
> - map() allocates, reads and records a region
> - map() returns cached pointer for already-mapped range
> - map() returns correct offset within a larger region
> - map() re-reads when extending a region to a larger size
> - map_to() reads to a specified address and records it
> - lookup() returns NULL for unmapped ranges
> - alloc_ptr advances with correct alignment
> - map() returns NULL when the translation table is full
> - cleanup() calls backend and resets state
> - map() with multiple disjoint regions
> - read beyond image size returns error
>
> Also fix IMAGE_LOADER_MAX_REGIONS Kconfig to depend on IMAGE_LOADER
> and default to 16 unconditionally (the previous 'default 0' fallback
> caused the regions array to be zero-sized when IMAGE_LOADER was
> enabled after initial defconfig generation).
>
> Register the new 'image_loader' test suite in test/cmd_ut.c so it
> can be run via 'ut image_loader'.
>
> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> ---
> boot/Kconfig | 4 +-
> test/boot/Makefile | 2 +
> test/boot/image_loader.c | 429 +++++++++++++++++++++++++++++++++++++++
> test/cmd_ut.c | 2 +
> 4 files changed, 435 insertions(+), 2 deletions(-)
> create mode 100644 test/boot/image_loader.c
>
> diff --git a/boot/Kconfig b/boot/Kconfig
> index 1f870c7d251..efc06f3cd1a 100644
> --- a/boot/Kconfig
> +++ b/boot/Kconfig
> @@ -1179,8 +1179,8 @@ config IMAGE_LOADER
>
> config IMAGE_LOADER_MAX_REGIONS
> int "Maximum number of mapped regions in image loader"
> - default 16 if IMAGE_LOADER
> - default 0
> + depends on IMAGE_LOADER
> + default 16
> help
> Maximum number of distinct image regions that can be mapped
> into RAM simultaneously. 16 is sufficient for typical FIT
As Tom mentioned, this is in the wrong patch. But really we should
just remove it and use an alist
> diff --git a/test/boot/Makefile b/test/boot/Makefile
> index 89538d4f0a6..6fd349a65bc 100644
> --- a/test/boot/Makefile
> +++ b/test/boot/Makefile
> @@ -23,3 +23,5 @@ endif
> obj-$(CONFIG_BOOTMETH_VBE) += vbe_fixup.o
>
> obj-$(CONFIG_UPL) += upl.o
> +
> +obj-$(CONFIG_IMAGE_LOADER) += image_loader.o
> diff --git a/test/boot/image_loader.c b/test/boot/image_loader.c
> new file mode 100644
> index 00000000000..dc4b0b4173a
> --- /dev/null
> +++ b/test/boot/image_loader.c
> @@ -0,0 +1,429 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Tests for image_loader framework
> + *
> + * Copyright (C) 2026 Daniel Golle <daniel@makrotopia.org>
> + */
> +
> +#include <image-loader.h>
> +#include <mapmem.h>
> +#include <malloc.h>
> +#include <asm/cache.h>
> +#include <test/test.h>
> +#include <test/ut.h>
> +
> +#define IMG_LOADER_TEST(_name, _flags) \
> + UNIT_TEST(_name, _flags, image_loader)
> +
> +/* Synthetic image size used throughout the tests */
> +#define IMAGE_SIZE 4096
> +
> +/**
> + * struct mock_priv - private data for the mock storage backend
> + *
> + * @image: pointer to synthetic image data in RAM
> + * @image_size: size of the synthetic image
> + * @read_count: number of times .read() was called
> + * @last_off: offset from the most recent .read() call
> + * @last_size: size from the most recent .read() call
> + */
> +struct mock_priv {
> + const void *image;
> + size_t image_size;
> + int read_count;
> + ulong last_off;
> + ulong last_size;
> +};
> +
[..]
> +/* Test: lookup() returns NULL for unmapped ranges */
> +static int image_loader_test_lookup_miss(struct unit_test_state *uts)
> +{
> + struct image_loader ldr;
> + struct mock_priv mock;
> + u8 image[IMAGE_SIZE];
> + void *p;
> +
> + init_mock_loader(&ldr, &mock, image, IMAGE_SIZE, 0x1000000);
> +
> + /* Nothing mapped yet — should return NULL */
> + p = image_loader_lookup(&ldr, 0, 64);
> + ut_assertnull(p);
AI tends to write it like this, but you can just do:
ut_assertnull(image_loader_lookup(&ldr, 0, 64));
There are various examples of this in this patch.
The test declarations should immediately follow the } of the function
they refer to
image_loader_test_map_basic()
{
...
}
IMG_LOADER_TEST(image_loader_test_map_basic, 0);
Regards,
Simon
^ permalink raw reply [flat|nested] 88+ messages in thread* Re: [RFC PATCH 11/20] test: boot: add image_loader unit tests
2026-02-19 13:10 ` Simon Glass
@ 2026-02-19 14:04 ` Tom Rini
2026-02-19 14:34 ` Simon Glass
0 siblings, 1 reply; 88+ messages in thread
From: Tom Rini @ 2026-02-19 14:04 UTC (permalink / raw)
To: Simon Glass
Cc: Daniel Golle, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
[-- Attachment #1: Type: text/plain, Size: 2400 bytes --]
On Thu, Feb 19, 2026 at 06:10:58AM -0700, Simon Glass wrote:
> Hi Daniel,
>
> On Mon, 16 Feb 2026 at 14:23, Daniel Golle <daniel@makrotopia.org> wrote:
> >
> > Add unit tests for the image_loader framework covering its core
> > logic with a mock storage backend:
> >
> > - map() allocates, reads and records a region
> > - map() returns cached pointer for already-mapped range
> > - map() returns correct offset within a larger region
> > - map() re-reads when extending a region to a larger size
> > - map_to() reads to a specified address and records it
> > - lookup() returns NULL for unmapped ranges
> > - alloc_ptr advances with correct alignment
> > - map() returns NULL when the translation table is full
> > - cleanup() calls backend and resets state
> > - map() with multiple disjoint regions
> > - read beyond image size returns error
> >
> > Also fix IMAGE_LOADER_MAX_REGIONS Kconfig to depend on IMAGE_LOADER
> > and default to 16 unconditionally (the previous 'default 0' fallback
> > caused the regions array to be zero-sized when IMAGE_LOADER was
> > enabled after initial defconfig generation).
> >
> > Register the new 'image_loader' test suite in test/cmd_ut.c so it
> > can be run via 'ut image_loader'.
> >
> > Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> > ---
> > boot/Kconfig | 4 +-
> > test/boot/Makefile | 2 +
> > test/boot/image_loader.c | 429 +++++++++++++++++++++++++++++++++++++++
> > test/cmd_ut.c | 2 +
> > 4 files changed, 435 insertions(+), 2 deletions(-)
> > create mode 100644 test/boot/image_loader.c
> >
> > diff --git a/boot/Kconfig b/boot/Kconfig
> > index 1f870c7d251..efc06f3cd1a 100644
> > --- a/boot/Kconfig
> > +++ b/boot/Kconfig
> > @@ -1179,8 +1179,8 @@ config IMAGE_LOADER
> >
> > config IMAGE_LOADER_MAX_REGIONS
> > int "Maximum number of mapped regions in image loader"
> > - default 16 if IMAGE_LOADER
> > - default 0
> > + depends on IMAGE_LOADER
> > + default 16
> > help
> > Maximum number of distinct image regions that can be mapped
> > into RAM simultaneously. 16 is sufficient for typical FIT
>
> As Tom mentioned, this is in the wrong patch. But really we should
> just remove it and use an alist
Lets not have another argument about your alist stuff please.
--
Tom
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [RFC PATCH 11/20] test: boot: add image_loader unit tests
2026-02-19 14:04 ` Tom Rini
@ 2026-02-19 14:34 ` Simon Glass
2026-02-19 15:41 ` Tom Rini
0 siblings, 1 reply; 88+ messages in thread
From: Simon Glass @ 2026-02-19 14:34 UTC (permalink / raw)
To: Tom Rini
Cc: Daniel Golle, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
Hi Tom,
On Thu, 19 Feb 2026 at 07:04, Tom Rini <trini@konsulko.com> wrote:
>
> On Thu, Feb 19, 2026 at 06:10:58AM -0700, Simon Glass wrote:
> > Hi Daniel,
> >
> > On Mon, 16 Feb 2026 at 14:23, Daniel Golle <daniel@makrotopia.org> wrote:
> > >
> > > Add unit tests for the image_loader framework covering its core
> > > logic with a mock storage backend:
> > >
> > > - map() allocates, reads and records a region
> > > - map() returns cached pointer for already-mapped range
> > > - map() returns correct offset within a larger region
> > > - map() re-reads when extending a region to a larger size
> > > - map_to() reads to a specified address and records it
> > > - lookup() returns NULL for unmapped ranges
> > > - alloc_ptr advances with correct alignment
> > > - map() returns NULL when the translation table is full
> > > - cleanup() calls backend and resets state
> > > - map() with multiple disjoint regions
> > > - read beyond image size returns error
> > >
> > > Also fix IMAGE_LOADER_MAX_REGIONS Kconfig to depend on IMAGE_LOADER
> > > and default to 16 unconditionally (the previous 'default 0' fallback
> > > caused the regions array to be zero-sized when IMAGE_LOADER was
> > > enabled after initial defconfig generation).
> > >
> > > Register the new 'image_loader' test suite in test/cmd_ut.c so it
> > > can be run via 'ut image_loader'.
> > >
> > > Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> > > ---
> > > boot/Kconfig | 4 +-
> > > test/boot/Makefile | 2 +
> > > test/boot/image_loader.c | 429 +++++++++++++++++++++++++++++++++++++++
> > > test/cmd_ut.c | 2 +
> > > 4 files changed, 435 insertions(+), 2 deletions(-)
> > > create mode 100644 test/boot/image_loader.c
> > >
> > > diff --git a/boot/Kconfig b/boot/Kconfig
> > > index 1f870c7d251..efc06f3cd1a 100644
> > > --- a/boot/Kconfig
> > > +++ b/boot/Kconfig
> > > @@ -1179,8 +1179,8 @@ config IMAGE_LOADER
> > >
> > > config IMAGE_LOADER_MAX_REGIONS
> > > int "Maximum number of mapped regions in image loader"
> > > - default 16 if IMAGE_LOADER
> > > - default 0
> > > + depends on IMAGE_LOADER
> > > + default 16
> > > help
> > > Maximum number of distinct image regions that can be mapped
> > > into RAM simultaneously. 16 is sufficient for typical FIT
> >
> > As Tom mentioned, this is in the wrong patch. But really we should
> > just remove it and use an alist
>
> Lets not have another argument about your alist stuff please.
Then perhaps another type of list that can grow as needed.
Regards,
Simon
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [RFC PATCH 11/20] test: boot: add image_loader unit tests
2026-02-19 14:34 ` Simon Glass
@ 2026-02-19 15:41 ` Tom Rini
0 siblings, 0 replies; 88+ messages in thread
From: Tom Rini @ 2026-02-19 15:41 UTC (permalink / raw)
To: Simon Glass
Cc: Daniel Golle, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
[-- Attachment #1: Type: text/plain, Size: 2883 bytes --]
On Thu, Feb 19, 2026 at 07:34:21AM -0700, Simon Glass wrote:
> Hi Tom,
>
> On Thu, 19 Feb 2026 at 07:04, Tom Rini <trini@konsulko.com> wrote:
> >
> > On Thu, Feb 19, 2026 at 06:10:58AM -0700, Simon Glass wrote:
> > > Hi Daniel,
> > >
> > > On Mon, 16 Feb 2026 at 14:23, Daniel Golle <daniel@makrotopia.org> wrote:
> > > >
> > > > Add unit tests for the image_loader framework covering its core
> > > > logic with a mock storage backend:
> > > >
> > > > - map() allocates, reads and records a region
> > > > - map() returns cached pointer for already-mapped range
> > > > - map() returns correct offset within a larger region
> > > > - map() re-reads when extending a region to a larger size
> > > > - map_to() reads to a specified address and records it
> > > > - lookup() returns NULL for unmapped ranges
> > > > - alloc_ptr advances with correct alignment
> > > > - map() returns NULL when the translation table is full
> > > > - cleanup() calls backend and resets state
> > > > - map() with multiple disjoint regions
> > > > - read beyond image size returns error
> > > >
> > > > Also fix IMAGE_LOADER_MAX_REGIONS Kconfig to depend on IMAGE_LOADER
> > > > and default to 16 unconditionally (the previous 'default 0' fallback
> > > > caused the regions array to be zero-sized when IMAGE_LOADER was
> > > > enabled after initial defconfig generation).
> > > >
> > > > Register the new 'image_loader' test suite in test/cmd_ut.c so it
> > > > can be run via 'ut image_loader'.
> > > >
> > > > Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> > > > ---
> > > > boot/Kconfig | 4 +-
> > > > test/boot/Makefile | 2 +
> > > > test/boot/image_loader.c | 429 +++++++++++++++++++++++++++++++++++++++
> > > > test/cmd_ut.c | 2 +
> > > > 4 files changed, 435 insertions(+), 2 deletions(-)
> > > > create mode 100644 test/boot/image_loader.c
> > > >
> > > > diff --git a/boot/Kconfig b/boot/Kconfig
> > > > index 1f870c7d251..efc06f3cd1a 100644
> > > > --- a/boot/Kconfig
> > > > +++ b/boot/Kconfig
> > > > @@ -1179,8 +1179,8 @@ config IMAGE_LOADER
> > > >
> > > > config IMAGE_LOADER_MAX_REGIONS
> > > > int "Maximum number of mapped regions in image loader"
> > > > - default 16 if IMAGE_LOADER
> > > > - default 0
> > > > + depends on IMAGE_LOADER
> > > > + default 16
> > > > help
> > > > Maximum number of distinct image regions that can be mapped
> > > > into RAM simultaneously. 16 is sufficient for typical FIT
> > >
> > > As Tom mentioned, this is in the wrong patch. But really we should
> > > just remove it and use an alist
> >
> > Lets not have another argument about your alist stuff please.
>
> Then perhaps another type of list that can grow as needed.
A problem to worry about later, yes.
--
Tom
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 88+ messages in thread
* [RFC PATCH 12/20] doc: bootm: document direct storage boot
2026-02-16 21:21 [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading Daniel Golle
` (10 preceding siblings ...)
2026-02-16 21:22 ` [RFC PATCH 11/20] test: boot: add image_loader unit tests Daniel Golle
@ 2026-02-16 21:23 ` Daniel Golle
2026-02-16 21:23 ` [RFC PATCH 13/20] boot: bootmeth: add OpenWrt boot method skeleton Daniel Golle
` (13 subsequent siblings)
25 siblings, 0 replies; 88+ messages in thread
From: Daniel Golle @ 2026-02-16 21:23 UTC (permalink / raw)
To: Tom Rini, Simon Glass, Quentin Schulz, Daniel Golle,
Kory Maincent, Mattijs Korpershoek, Martin Schwan, Anshul Dalal,
Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
牛 志宏, Marek Vasut, Heinrich Schuchardt,
Wolfgang Wallner, Frank Wunderlich, David Lechner,
Osama Abdelkader, Mikhail Kshevetskiy, Michael Trimarchi,
Miquel Raynal, Andrew Goodbody, Yegor Yefremov, Mike Looijmans,
Weijie Gao, Alexander Stein, Neil Armstrong, Mayuresh Chitale,
Paul HENRYS, u-boot
Cc: John Crispin, Paul Spooren
Add user and developer documentation for the bootm storage loading
feature.
doc/usage/fit/storage-boot.rst:
User-facing documentation with syntax reference, examples for MMC,
MTD, and UBI, description of how the on-demand loading works, how
filesystem sub-images are skipped, and the relevant Kconfig options.
doc/develop/bootm-storage.rst:
Developer documentation covering the image_loader architecture,
translation table design, API reference (map, map_to, lookup,
cleanup), storage backend interface with step-by-step guide for
adding new backends, FIT integration points in boot_get_kernel()
and fit_image_load(), and testing instructions.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
doc/develop/bootm-storage.rst | 210 +++++++++++++++++++++++++++++++++
doc/develop/index.rst | 1 +
doc/usage/fit/index.rst | 1 +
doc/usage/fit/storage-boot.rst | 201 +++++++++++++++++++++++++++++++
4 files changed, 413 insertions(+)
create mode 100644 doc/develop/bootm-storage.rst
create mode 100644 doc/usage/fit/storage-boot.rst
diff --git a/doc/develop/bootm-storage.rst b/doc/develop/bootm-storage.rst
new file mode 100644
index 00000000000..d779049388a
--- /dev/null
+++ b/doc/develop/bootm-storage.rst
@@ -0,0 +1,210 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+On-Demand Image Loading from Storage
+=====================================
+
+This document describes the ``image_loader`` framework that enables
+``bootm`` to load FIT images directly from storage devices without first
+copying the entire image into RAM.
+
+Architecture Overview
+---------------------
+
+The framework is built around ``struct image_loader``
+(``include/image-loader.h``), which provides:
+
+``read(ldr, src, size, dst)``
+ Backend callback that reads ``size`` bytes starting at byte offset
+ ``src`` within the source image into the RAM buffer at ``dst``.
+
+``cleanup(ldr)``
+ Optional callback to release device references, free private state,
+ etc. Called at the end of the boot attempt.
+
+``priv``
+ Opaque pointer to backend-specific context (block device descriptor,
+ MTD device, UBI volume info, etc.).
+
+``regions[]``
+ Translation table of ``struct image_loader_region`` entries. Each
+ entry records that bytes ``[img_offset, img_offset + size)`` of the
+ source image have been loaded into RAM at address ``ram``.
+
+``nr_regions``
+ Number of entries currently used in the translation table.
+
+``alloc_ptr``
+ Next free RAM address for scratch allocations. Advanced (aligned to
+ ``ARCH_DMA_MINALIGN``) after each new mapping.
+
+API Reference
+-------------
+
+``image_loader_map(ldr, img_offset, size)``
+ Ensure that image bytes ``[img_offset, img_offset + size)`` are
+ accessible in RAM. Returns a pointer to the data.
+
+ - If the range is fully covered by an existing translation table
+ entry, returns the cached pointer (no I/O).
+ - If an entry exists at the same base offset but is smaller, the
+ entry is extended in place (re-read to the same RAM base).
+ - Otherwise, allocates from ``alloc_ptr``, reads from storage,
+ records the new mapping, and advances ``alloc_ptr``.
+
+``image_loader_map_to(ldr, img_offset, size, dst)``
+ Like ``map()`` but reads into a caller-specified RAM address instead
+ of allocating from the scratch area. Used when the sub-image has a
+ known load address (zero-copy path).
+
+``image_loader_lookup(ldr, img_offset, size)``
+ Check the translation table for an existing mapping. Returns the
+ RAM pointer on hit, ``NULL`` on miss. Does not trigger any I/O.
+
+``image_loader_cleanup(ldr)``
+ Call the backend ``cleanup`` callback (if set) and reset all loader
+ state. Safe to call multiple times.
+
+Storage Backends
+----------------
+
+Each backend implements an ``image_loader_init_*()`` function that
+resolves the device, installs ``.read()`` and ``.cleanup()`` callbacks,
+and stores backend-specific context in ``.priv``.
+
+Block (``boot/image-loader-blk.c``)
+ ``image_loader_init_blk(ldr, ifname, dev_part_str)``
+
+ Resolves the partition via ``part_get_info_by_dev_and_name_or_num()``
+ and reads via ``blk_dread()``. Handles unaligned head/tail via a
+ bounce buffer.
+
+MTD (``boot/image-loader-mtd.c``)
+ ``image_loader_init_mtd(ldr, name)``
+
+ Resolves via ``get_mtd_device_nm()`` and reads via
+ ``mtd_read_skip_bad()``. On NAND, bad blocks are transparently
+ skipped.
+
+UBI (``boot/image-loader-ubi.c``)
+ ``image_loader_init_ubi(ldr, vol_name)``
+
+ Auto-attaches a UBI device from the device tree (scanning
+ ``linux,ubi`` compatible nodes) if not already attached. Reads via
+ ``ubi_volume_read()``.
+
+Writing a New Backend
+^^^^^^^^^^^^^^^^^^^^^
+
+To add support for a new storage type:
+
+1. Create ``boot/image-loader-foo.c``.
+
+2. Define a private context struct and a read callback::
+
+ struct foo_priv { ... };
+
+ static int foo_read(struct image_loader *ldr, ulong src,
+ ulong size, void *dst)
+ {
+ struct foo_priv *p = ldr->priv;
+ /* read 'size' bytes at offset 'src' into 'dst' */
+ return 0; /* or negative errno */
+ }
+
+3. Optionally define a cleanup callback::
+
+ static void foo_cleanup(struct image_loader *ldr)
+ {
+ struct foo_priv *p = ldr->priv;
+ /* release resources */
+ free(p);
+ }
+
+4. Implement the init function::
+
+ int image_loader_init_foo(struct image_loader *ldr, ...)
+ {
+ struct foo_priv *p = calloc(1, sizeof(*p));
+ if (!p)
+ return -ENOMEM;
+ /* resolve device, fill p->... */
+ ldr->read = foo_read;
+ ldr->cleanup = foo_cleanup;
+ ldr->priv = p;
+ return 0;
+ }
+
+5. Add the prototype to ``include/image-loader.h``.
+
+6. Add a Kconfig symbol in ``boot/Kconfig`` and build rule in
+ ``boot/Makefile``.
+
+7. Add the keyword dispatch in ``bootm_init_loader()`` in
+ ``cmd/bootm.c``.
+
+FIT Integration
+---------------
+
+External Data vs. Embedded Data
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The selective loading optimisation is only possible with **external-data**
+FIT images (built with ``mkimage -E``). In this layout, the FDT
+structure is a compact metadata-only blob and each sub-image's payload is
+stored at a separate offset recorded via ``data-position``/``data-offset``
+and ``data-size`` properties.
+
+When ``fit_image_load()`` encounters a sub-image with external data and
+``images->loader`` is set, it reads only that sub-image's payload from
+storage — the code path is gated on ``external`` being true (i.e.
+``data-position`` or ``data-offset`` exists in the node).
+
+With **embedded-data** FIT images, all payloads live inside FDT ``data``
+properties. The entire FDT — including all payloads — is loaded when
+``boot_get_kernel()`` maps the FDT structure. The storage-backed path
+still works (the image is valid), but:
+
+- No RAM is saved because the full image is read as the "FDT structure".
+- ``IH_TYPE_FILESYSTEM`` sub-images cannot be skipped since their data
+ is embedded in the FDT blob.
+- ``fit_image_load()`` takes the normal embedded-data code path (the
+ ``data`` property points into the already-loaded FDT) and the
+ ``image_loader`` is not involved for individual sub-images.
+
+Backend developers do not need to handle this distinction — it is
+managed entirely by ``boot_get_kernel()`` and ``fit_image_load()``.
+
+Access Points
+^^^^^^^^^^^^^
+
+**Access Point 1: boot_get_kernel() (boot/bootm.c)**
+
+When ``images->loader`` is set, ``boot_get_kernel()`` uses
+``image_loader_map()`` to read the first 64 bytes for format detection,
+then extends to the full ``fdt_totalsize()`` for FIT images. For
+external-data images this is just the compact metadata; for embedded-data
+images this is the entire file. This replaces the normal
+``map_sysmem(img_addr, 0)`` path.
+
+**Access Point 2: fit_image_load() (boot/image-fit.c)**
+
+When processing a FIT sub-image with external data and
+``images->loader`` is set, ``fit_image_load()`` uses
+``image_loader_map()`` or ``image_loader_map_to()`` to load just the
+sub-image data on demand. Sub-images of type ``IH_TYPE_FILESYSTEM`` are
+skipped entirely. Verification uses ``fit_image_verify_with_data()`` to
+check hashes against the loaded data.
+
+For embedded-data sub-images, the existing in-memory code path is taken
+since the data is already present in the FDT mapping from Access Point 1.
+
+Testing
+-------
+
+Unit tests are in ``test/boot/image_loader.c`` and can be run via::
+
+ ./u-boot -T -c "ut image_loader"
+
+The tests use a mock backend that copies from a RAM buffer, exercising
+all core logic paths: mapping, caching, extending, lookup, table-full
+handling, and cleanup.
diff --git a/doc/develop/index.rst b/doc/develop/index.rst
index 3c044e67927..90bd6e8ac8a 100644
--- a/doc/develop/index.rst
+++ b/doc/develop/index.rst
@@ -32,6 +32,7 @@ Implementation
directories
bloblist
+ bootm-storage
bootstd/index
ci_testing
commands
diff --git a/doc/usage/fit/index.rst b/doc/usage/fit/index.rst
index 6c78d8584ed..d3ad4595892 100644
--- a/doc/usage/fit/index.rst
+++ b/doc/usage/fit/index.rst
@@ -26,6 +26,7 @@ images that it reads and boots. Documentation about FIT is available in
sign-configs
sign-images
source_file_format
+ storage-boot
uefi
update3
update_uboot
diff --git a/doc/usage/fit/storage-boot.rst b/doc/usage/fit/storage-boot.rst
new file mode 100644
index 00000000000..55b1d6090de
--- /dev/null
+++ b/doc/usage/fit/storage-boot.rst
@@ -0,0 +1,201 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+Booting from Storage Devices
+============================
+
+Overview
+--------
+
+Traditionally, ``bootm`` expects a FIT image to already be present in RAM
+at a given address. This requires a separate ``load`` command (e.g.
+``fatload``, ``nand read``, ``ubi read``) to bring the entire image into
+memory before ``bootm`` can inspect it.
+
+With storage-backed boot, ``bootm`` can read a FIT image directly from a
+block device partition, MTD partition, or UBI volume. Only the metadata
+(FDT structure) is loaded initially; sub-images (kernel, device tree,
+ramdisk, loadables) are loaded on demand as ``bootm`` processes them.
+Filesystem sub-images (``IH_TYPE_FILESYSTEM``) are never read at all.
+
+This is particularly useful for:
+
+- Boards with limited RAM where loading the full image is impractical
+- FIT images containing large filesystem sub-images that should not be
+ loaded into RAM
+- Simplifying boot scripts by combining the load and boot steps
+
+Syntax
+------
+
+.. code-block:: none
+
+ bootm mmc <dev>:<part>[#conf[#overlay...]] [initrd [fdt]]
+ bootm mtd <name>[#conf[#overlay...]] [initrd [fdt]]
+ bootm ubi <volume>[#conf[#overlay...]] [initrd [fdt]]
+
+The device-type keyword (``mmc``, ``mtd``, ``ubi``) selects the storage
+backend. The device specifier identifies the source:
+
+============ ======================================
+Keyword Device specifier
+============ ======================================
+``mmc`` ``<dev>:<part>`` or ``<dev>#<name>`` — partition by number or name
+``mtd`` MTD device or partition name
+``ubi`` UBI volume name
+============ ======================================
+
+The optional ``#conf`` suffix selects a FIT configuration by name,
+exactly as with the traditional ``bootm addr#conf`` syntax. Additional
+``#overlay`` suffixes select device-tree overlays to apply on top of the
+base configuration.
+
+Examples
+--------
+
+Boot from eMMC partition 4::
+
+ bootm mmc 0:4
+
+Boot from eMMC partition 4, selecting FIT configuration ``config-2``::
+
+ bootm mmc 0:4#config-2
+
+Boot from eMMC with a configuration and two overlays::
+
+ bootm mmc 0:4#config-1#overlay-wifi#overlay-lcd
+
+Boot from an MTD partition named ``firmware``::
+
+ bootm mtd firmware
+
+Boot from an MTD partition with a specific configuration::
+
+ bootm mtd firmware#config-1
+
+Boot from a UBI volume named ``kernel``::
+
+ bootm ubi kernel
+
+Boot from a UBI volume with a configuration::
+
+ bootm ubi kernel#config-1
+
+How It Works
+------------
+
+1. ``bootm`` detects the device-type keyword and initialises an
+ ``image_loader`` with the corresponding backend.
+
+2. The first 64 bytes of the image are read to detect the image format.
+
+3. For FIT images, the full FDT structure (metadata) is loaded so that
+ all sub-image nodes, hashes, and configuration references are
+ accessible.
+
+4. As ``bootm`` processes each sub-image (kernel, FDT, ramdisk,
+ loadables), ``fit_image_load()`` reads only the needed data from
+ storage — either to the sub-image's load address or to a scratch
+ area.
+
+5. A translation table tracks which regions have already been loaded,
+ avoiding redundant reads. Overlapping or sub-range requests are
+ served from the existing mapping.
+
+6. After the boot attempt completes (whether successful or not), the
+ loader's cleanup callback releases any held device references.
+
+Image Requirements: External Data
+---------------------------------
+
+Selective sub-image loading **requires** that the FIT image is built with
+**external data** (``mkimage -E``). In an external-data FIT, the FDT
+structure (metadata) and the sub-image payloads are stored in separate
+regions of the file:
+
+.. code-block:: none
+
+ +------------------+
+ | FDT structure | ← metadata: image descriptions, hashes, configs
+ | (compact) |
+ +------------------+
+ | padding |
+ +------------------+
+ | kernel data | ← loaded on demand
+ +------------------+
+ | fdt data | ← loaded on demand
+ +------------------+
+ | ramdisk data | ← loaded on demand
+ +------------------+
+ | rootfs data | ← skipped (IH_TYPE_FILESYSTEM)
+ +------------------+
+
+Each sub-image node in the FDT records its ``data-position`` (or
+``data-offset``) and ``data-size``, allowing the loader to seek directly
+to the needed payload without reading any other sub-image data.
+
+To create a FIT with external data::
+
+ mkimage -E -f image.its image.itb
+
+The ``-E`` flag places sub-image payloads after the FDT structure. An
+optional ``-p <pad>`` argument inserts extra padding between the FDT and
+the data area for later in-place signing or updates.
+
+With **embedded data** (the default when ``-E`` is omitted), all sub-image
+payloads are stored inline within the FDT ``data`` properties. The
+entire FDT structure — including all payloads — must be loaded to parse
+even the metadata. This means:
+
+- The ``bootm`` storage path will still work, but it loads the entire
+ FIT blob (metadata + all payloads) into RAM when it reads the FDT
+ structure. There is no selective loading benefit.
+- Filesystem sub-images embedded in the FDT cannot be skipped — they
+ are part of the FDT blob that must be read to access the metadata.
+
+In summary:
+
+================ =============== ================ ====================
+FIT type Selective load Skip filesystem RAM savings
+================ =============== ================ ====================
+External data Yes Yes Significant
+Embedded data No No None (full image)
+================ =============== ================ ====================
+
+**Recommendation:** Always use ``mkimage -E`` when building FIT images
+intended for storage-backed boot.
+
+Filesystem Sub-images
+---------------------
+
+FIT images may contain sub-images of type ``IH_TYPE_FILESYSTEM`` (e.g. a
+root filesystem squashfs). These are intended to remain on the storage
+device and be mounted at runtime — not loaded into RAM.
+
+The storage-backed boot path recognises this type and skips it entirely:
+no data is read from storage and no RAM is consumed. This is one of the
+key advantages over the traditional ``load`` + ``bootm`` flow, where the
+entire image (including any large filesystem blobs) must fit in RAM.
+
+Configuration
+-------------
+
+The feature is gated by several Kconfig options:
+
+``CONFIG_BOOTM_STORAGE``
+ Master switch. Depends on ``CMDLINE``, ``FIT``, and
+ ``IMAGE_LOADER``.
+
+``CONFIG_IMAGE_LOADER``
+ The core on-demand loading framework.
+
+``CONFIG_IMAGE_LOADER_BLK``
+ Block device backend (MMC, SATA, USB, etc.).
+
+``CONFIG_IMAGE_LOADER_MTD``
+ MTD backend (SPI-NOR, SPI-NAND, parallel NAND, etc.).
+
+``CONFIG_IMAGE_LOADER_UBI``
+ UBI volume backend.
+
+At least one backend must be enabled for ``CONFIG_BOOTM_STORAGE`` to be
+useful.
--
2.53.0
^ permalink raw reply related [flat|nested] 88+ messages in thread* [RFC PATCH 13/20] boot: bootmeth: add OpenWrt boot method skeleton
2026-02-16 21:21 [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading Daniel Golle
` (11 preceding siblings ...)
2026-02-16 21:23 ` [RFC PATCH 12/20] doc: bootm: document direct storage boot Daniel Golle
@ 2026-02-16 21:23 ` Daniel Golle
2026-02-19 13:09 ` Simon Glass
2026-02-16 21:23 ` [RFC PATCH 14/20] boot: bootmeth: openwrt: implement read_bootflow for block devices Daniel Golle
` (12 subsequent siblings)
25 siblings, 1 reply; 88+ messages in thread
From: Daniel Golle @ 2026-02-16 21:23 UTC (permalink / raw)
To: Tom Rini, Simon Glass, Quentin Schulz, Daniel Golle,
Kory Maincent, Mattijs Korpershoek, Martin Schwan, Anshul Dalal,
Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
牛 志宏, Marek Vasut, Heinrich Schuchardt,
Wolfgang Wallner, Frank Wunderlich, David Lechner,
Osama Abdelkader, Mikhail Kshevetskiy, Michael Trimarchi,
Miquel Raynal, Andrew Goodbody, Yegor Yefremov, Mike Looijmans,
Weijie Gao, Alexander Stein, Neil Armstrong, Mayuresh Chitale,
Paul HENRYS, u-boot
Cc: John Crispin, Paul Spooren
Add the initial bootmeth_openwrt driver with:
- UCLASS_BOOTMETH driver, compatible "u-boot,openwrt"
- check(): accept block devices via bootflow_iter_check_blk()
- bind(): set BOOTMETHF_ANY_PART so bootstd iterates all partitions
without requiring a filesystem
- read_bootflow(): stub returning -ENOENT
- boot(): stub returning -ENOSYS
- Kconfig: CONFIG_BOOTMETH_OPENWRT depending on FIT, BOOTSTD,
BOOTM_STORAGE
This is the foundation for booting OpenWrt-style uImage.FIT firmware
images stored directly on raw storage partitions. The bootmeth will
delegate actual image loading to the existing bootm storage path via
image_loader.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
boot/Kconfig | 13 +++++++++
boot/Makefile | 1 +
boot/bootmeth_openwrt.c | 65 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 79 insertions(+)
create mode 100644 boot/bootmeth_openwrt.c
diff --git a/boot/Kconfig b/boot/Kconfig
index efc06f3cd1a..d8c7b8360ac 100644
--- a/boot/Kconfig
+++ b/boot/Kconfig
@@ -585,6 +585,19 @@ config BOOTMETH_CROS
Note that only x86 devices are supported at present.
+config BOOTMETH_OPENWRT
+ bool "Bootdev support for OpenWrt"
+ depends on FIT
+ depends on BOOTSTD
+ depends on BOOTM_STORAGE
+ help
+ Enables support for booting OpenWrt-style uImage.FIT firmware
+ images stored directly on raw storage (block device partitions,
+ MTD partitions, or UBI volumes). The boot method loads the FIT
+ metadata, selectively reads only the kernel and FDT sub-images
+ from storage, and skips filesystem sub-images that Linux maps
+ directly from flash.
+
config BOOTMETH_EXTLINUX
bool "Bootdev support for extlinux boot"
select PXE_UTILS
diff --git a/boot/Makefile b/boot/Makefile
index 7d1d4a28106..7b42358eb0c 100644
--- a/boot/Makefile
+++ b/boot/Makefile
@@ -72,6 +72,7 @@ obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_SIMPLE_FW) += vbe_simple_fw.o
obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_SIMPLE_OS) += vbe_simple_os.o
obj-$(CONFIG_$(PHASE_)BOOTMETH_ANDROID) += bootmeth_android.o
+obj-$(CONFIG_$(PHASE_)BOOTMETH_OPENWRT) += bootmeth_openwrt.o
obj-$(CONFIG_IMAGE_LOADER) += image-loader.o
obj-$(CONFIG_IMAGE_LOADER_BLK) += image-loader-blk.o
diff --git a/boot/bootmeth_openwrt.c b/boot/bootmeth_openwrt.c
new file mode 100644
index 00000000000..02bf543031b
--- /dev/null
+++ b/boot/bootmeth_openwrt.c
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Bootmethod for OpenWrt
+ *
+ * Copyright (C) 2026 Daniel Golle <daniel@makrotopia.org>
+ */
+
+#define LOG_CATEGORY UCLASS_BOOTSTD
+
+#include <blk.h>
+#include <bootdev.h>
+#include <bootflow.h>
+#include <bootm.h>
+#include <bootmeth.h>
+#include <dm.h>
+#include <image-loader.h>
+#include <malloc.h>
+#include <mapmem.h>
+
+static int openwrt_check(struct udevice *dev, struct bootflow_iter *iter)
+{
+ if (bootflow_iter_check_blk(iter))
+ return log_msg_ret("blk", -EOPNOTSUPP);
+
+ return 0;
+}
+
+static int openwrt_read_bootflow(struct udevice *dev, struct bootflow *bflow)
+{
+ return log_msg_ret("nyi", -ENOENT);
+}
+
+static int openwrt_boot(struct udevice *dev, struct bootflow *bflow)
+{
+ return log_msg_ret("nyi", -ENOSYS);
+}
+
+static int openwrt_bootmeth_bind(struct udevice *dev)
+{
+ struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
+
+ plat->desc = "OpenWrt";
+ plat->flags = BOOTMETHF_ANY_PART;
+
+ return 0;
+}
+
+static struct bootmeth_ops openwrt_bootmeth_ops = {
+ .check = openwrt_check,
+ .read_bootflow = openwrt_read_bootflow,
+ .boot = openwrt_boot,
+};
+
+static const struct udevice_id openwrt_bootmeth_ids[] = {
+ { .compatible = "u-boot,openwrt" },
+ { }
+};
+
+U_BOOT_DRIVER(bootmeth_openwrt) = {
+ .name = "bootmeth_openwrt",
+ .id = UCLASS_BOOTMETH,
+ .of_match = openwrt_bootmeth_ids,
+ .ops = &openwrt_bootmeth_ops,
+ .bind = openwrt_bootmeth_bind,
+};
--
2.53.0
^ permalink raw reply related [flat|nested] 88+ messages in thread* Re: [RFC PATCH 13/20] boot: bootmeth: add OpenWrt boot method skeleton
2026-02-16 21:23 ` [RFC PATCH 13/20] boot: bootmeth: add OpenWrt boot method skeleton Daniel Golle
@ 2026-02-19 13:09 ` Simon Glass
2026-02-19 14:00 ` Tom Rini
2026-02-19 16:52 ` Daniel Golle
0 siblings, 2 replies; 88+ messages in thread
From: Simon Glass @ 2026-02-19 13:09 UTC (permalink / raw)
To: Daniel Golle
Cc: Tom Rini, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
On Mon, 16 Feb 2026 at 14:23, Daniel Golle <daniel@makrotopia.org> wrote:
>
> Add the initial bootmeth_openwrt driver with:
>
> - UCLASS_BOOTMETH driver, compatible "u-boot,openwrt"
> - check(): accept block devices via bootflow_iter_check_blk()
> - bind(): set BOOTMETHF_ANY_PART so bootstd iterates all partitions
> without requiring a filesystem
> - read_bootflow(): stub returning -ENOENT
> - boot(): stub returning -ENOSYS
> - Kconfig: CONFIG_BOOTMETH_OPENWRT depending on FIT, BOOTSTD,
> BOOTM_STORAGE
We don't normally write out this much detail in the code
>
> This is the foundation for booting OpenWrt-style uImage.FIT firmware
> images stored directly on raw storage partitions. The bootmeth will
> delegate actual image loading to the existing bootm storage path via
> image_loader.
>
> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> ---
> boot/Kconfig | 13 +++++++++
> boot/Makefile | 1 +
> boot/bootmeth_openwrt.c | 65 +++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 79 insertions(+)
> create mode 100644 boot/bootmeth_openwrt.c
Reviewed-by: Simon Glass <simon.glass@canonical.com>
>
> diff --git a/boot/Kconfig b/boot/Kconfig
> index efc06f3cd1a..d8c7b8360ac 100644
> --- a/boot/Kconfig
> +++ b/boot/Kconfig
> @@ -585,6 +585,19 @@ config BOOTMETH_CROS
>
> Note that only x86 devices are supported at present.
>
> +config BOOTMETH_OPENWRT
> + bool "Bootdev support for OpenWrt"
> + depends on FIT
> + depends on BOOTSTD
> + depends on BOOTM_STORAGE
> + help
> + Enables support for booting OpenWrt-style uImage.FIT firmware
> + images stored directly on raw storage (block device partitions,
> + MTD partitions, or UBI volumes). The boot method loads the FIT
> + metadata, selectively reads only the kernel and FDT sub-images
> + from storage, and skips filesystem sub-images that Linux maps
> + directly from flash.
> +
> config BOOTMETH_EXTLINUX
> bool "Bootdev support for extlinux boot"
> select PXE_UTILS
> diff --git a/boot/Makefile b/boot/Makefile
> index 7d1d4a28106..7b42358eb0c 100644
> --- a/boot/Makefile
> +++ b/boot/Makefile
> @@ -72,6 +72,7 @@ obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_SIMPLE_FW) += vbe_simple_fw.o
> obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_SIMPLE_OS) += vbe_simple_os.o
>
> obj-$(CONFIG_$(PHASE_)BOOTMETH_ANDROID) += bootmeth_android.o
> +obj-$(CONFIG_$(PHASE_)BOOTMETH_OPENWRT) += bootmeth_openwrt.o
>
> obj-$(CONFIG_IMAGE_LOADER) += image-loader.o
> obj-$(CONFIG_IMAGE_LOADER_BLK) += image-loader-blk.o
> diff --git a/boot/bootmeth_openwrt.c b/boot/bootmeth_openwrt.c
> new file mode 100644
> index 00000000000..02bf543031b
> --- /dev/null
> +++ b/boot/bootmeth_openwrt.c
> @@ -0,0 +1,65 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Bootmethod for OpenWrt
> + *
> + * Copyright (C) 2026 Daniel Golle <daniel@makrotopia.org>
> + */
> +
> +#define LOG_CATEGORY UCLASS_BOOTSTD
> +
> +#include <blk.h>
> +#include <bootdev.h>
> +#include <bootflow.h>
> +#include <bootm.h>
> +#include <bootmeth.h>
> +#include <dm.h>
> +#include <image-loader.h>
> +#include <malloc.h>
> +#include <mapmem.h>
> +
> +static int openwrt_check(struct udevice *dev, struct bootflow_iter *iter)
> +{
> + if (bootflow_iter_check_blk(iter))
> + return log_msg_ret("blk", -EOPNOTSUPP);
> +
> + return 0;
> +}
> +
> +static int openwrt_read_bootflow(struct udevice *dev, struct bootflow *bflow)
> +{
> + return log_msg_ret("nyi", -ENOENT);
> +}
> +
> +static int openwrt_boot(struct udevice *dev, struct bootflow *bflow)
> +{
> + return log_msg_ret("nyi", -ENOSYS);
> +}
> +
> +static int openwrt_bootmeth_bind(struct udevice *dev)
> +{
> + struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
> +
> + plat->desc = "OpenWrt";
> + plat->flags = BOOTMETHF_ANY_PART;
> +
> + return 0;
> +}
> +
> +static struct bootmeth_ops openwrt_bootmeth_ops = {
> + .check = openwrt_check,
> + .read_bootflow = openwrt_read_bootflow,
> + .boot = openwrt_boot,
> +};
> +
> +static const struct udevice_id openwrt_bootmeth_ids[] = {
> + { .compatible = "u-boot,openwrt" },
> + { }
> +};
> +
> +U_BOOT_DRIVER(bootmeth_openwrt) = {
> + .name = "bootmeth_openwrt",
> + .id = UCLASS_BOOTMETH,
> + .of_match = openwrt_bootmeth_ids,
> + .ops = &openwrt_bootmeth_ops,
> + .bind = openwrt_bootmeth_bind,
> +};
> --
> 2.53.0
^ permalink raw reply [flat|nested] 88+ messages in thread* Re: [RFC PATCH 13/20] boot: bootmeth: add OpenWrt boot method skeleton
2026-02-19 13:09 ` Simon Glass
@ 2026-02-19 14:00 ` Tom Rini
2026-02-19 14:31 ` Simon Glass
2026-02-19 16:52 ` Daniel Golle
1 sibling, 1 reply; 88+ messages in thread
From: Tom Rini @ 2026-02-19 14:00 UTC (permalink / raw)
To: Simon Glass, Daniel Golle
Cc: Quentin Schulz, Kory Maincent, Mattijs Korpershoek, Martin Schwan,
Anshul Dalal, Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
牛 志宏, Marek Vasut, Heinrich Schuchardt,
Wolfgang Wallner, Frank Wunderlich, David Lechner,
Osama Abdelkader, Mikhail Kshevetskiy, Michael Trimarchi,
Miquel Raynal, Andrew Goodbody, Yegor Yefremov, Mike Looijmans,
Weijie Gao, Alexander Stein, Neil Armstrong, Mayuresh Chitale,
Paul HENRYS, u-boot, John Crispin, Paul Spooren
[-- Attachment #1: Type: text/plain, Size: 919 bytes --]
On Thu, Feb 19, 2026 at 06:09:28AM -0700, Simon Glass wrote:
> On Mon, 16 Feb 2026 at 14:23, Daniel Golle <daniel@makrotopia.org> wrote:
> >
> > Add the initial bootmeth_openwrt driver with:
> >
> > - UCLASS_BOOTMETH driver, compatible "u-boot,openwrt"
> > - check(): accept block devices via bootflow_iter_check_blk()
> > - bind(): set BOOTMETHF_ANY_PART so bootstd iterates all partitions
> > without requiring a filesystem
> > - read_bootflow(): stub returning -ENOENT
> > - boot(): stub returning -ENOSYS
> > - Kconfig: CONFIG_BOOTMETH_OPENWRT depending on FIT, BOOTSTD,
> > BOOTM_STORAGE
>
> We don't normally write out this much detail in the code
I assume you mean the commit message? And yes, this reads like an
AI genreated (then human proof-read) commit message. Which is something
I wanted to address, once done with the open questions I still have in
the cover letter.
--
Tom
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [RFC PATCH 13/20] boot: bootmeth: add OpenWrt boot method skeleton
2026-02-19 14:00 ` Tom Rini
@ 2026-02-19 14:31 ` Simon Glass
2026-02-19 15:31 ` Tom Rini
0 siblings, 1 reply; 88+ messages in thread
From: Simon Glass @ 2026-02-19 14:31 UTC (permalink / raw)
To: Tom Rini
Cc: Daniel Golle, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
Hi Tom,
On Thu, 19 Feb 2026 at 07:00, Tom Rini <trini@konsulko.com> wrote:
>
> On Thu, Feb 19, 2026 at 06:09:28AM -0700, Simon Glass wrote:
> > On Mon, 16 Feb 2026 at 14:23, Daniel Golle <daniel@makrotopia.org> wrote:
> > >
> > > Add the initial bootmeth_openwrt driver with:
> > >
> > > - UCLASS_BOOTMETH driver, compatible "u-boot,openwrt"
> > > - check(): accept block devices via bootflow_iter_check_blk()
> > > - bind(): set BOOTMETHF_ANY_PART so bootstd iterates all partitions
> > > without requiring a filesystem
> > > - read_bootflow(): stub returning -ENOENT
> > > - boot(): stub returning -ENOSYS
> > > - Kconfig: CONFIG_BOOTMETH_OPENWRT depending on FIT, BOOTSTD,
> > > BOOTM_STORAGE
> >
> > We don't normally write out this much detail in the code
>
> I assume you mean the commit message? And yes, this reads like an
> AI genreated (then human proof-read) commit message. Which is something
> I wanted to address, once done with the open questions I still have in
> the cover letter.
Yes I meant 'about' the code not 'in' the code.
You can normally fix it by asking for prose, or giving it some general
instructions.
Regards,
Simon
>
> --
> Tom
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [RFC PATCH 13/20] boot: bootmeth: add OpenWrt boot method skeleton
2026-02-19 14:31 ` Simon Glass
@ 2026-02-19 15:31 ` Tom Rini
0 siblings, 0 replies; 88+ messages in thread
From: Tom Rini @ 2026-02-19 15:31 UTC (permalink / raw)
To: Simon Glass
Cc: Daniel Golle, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
[-- Attachment #1: Type: text/plain, Size: 1512 bytes --]
On Thu, Feb 19, 2026 at 07:31:17AM -0700, Simon Glass wrote:
> Hi Tom,
>
> On Thu, 19 Feb 2026 at 07:00, Tom Rini <trini@konsulko.com> wrote:
> >
> > On Thu, Feb 19, 2026 at 06:09:28AM -0700, Simon Glass wrote:
> > > On Mon, 16 Feb 2026 at 14:23, Daniel Golle <daniel@makrotopia.org> wrote:
> > > >
> > > > Add the initial bootmeth_openwrt driver with:
> > > >
> > > > - UCLASS_BOOTMETH driver, compatible "u-boot,openwrt"
> > > > - check(): accept block devices via bootflow_iter_check_blk()
> > > > - bind(): set BOOTMETHF_ANY_PART so bootstd iterates all partitions
> > > > without requiring a filesystem
> > > > - read_bootflow(): stub returning -ENOENT
> > > > - boot(): stub returning -ENOSYS
> > > > - Kconfig: CONFIG_BOOTMETH_OPENWRT depending on FIT, BOOTSTD,
> > > > BOOTM_STORAGE
> > >
> > > We don't normally write out this much detail in the code
> >
> > I assume you mean the commit message? And yes, this reads like an
> > AI genreated (then human proof-read) commit message. Which is something
> > I wanted to address, once done with the open questions I still have in
> > the cover letter.
>
> Yes I meant 'about' the code not 'in' the code.
>
> You can normally fix it by asking for prose, or giving it some general
> instructions.
If that's even a good idea, sure. Some of the policies I was reading
yesterday disallow having the AI generate the commit message because the
human should be able to read and understand and explain the code.
--
Tom
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [RFC PATCH 13/20] boot: bootmeth: add OpenWrt boot method skeleton
2026-02-19 13:09 ` Simon Glass
2026-02-19 14:00 ` Tom Rini
@ 2026-02-19 16:52 ` Daniel Golle
1 sibling, 0 replies; 88+ messages in thread
From: Daniel Golle @ 2026-02-19 16:52 UTC (permalink / raw)
To: Simon Glass
Cc: Tom Rini, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
On Thu, Feb 19, 2026 at 06:09:28AM -0700, Simon Glass wrote:
> On Mon, 16 Feb 2026 at 14:23, Daniel Golle <daniel@makrotopia.org> wrote:
> >
> > Add the initial bootmeth_openwrt driver with:
> >
> > - UCLASS_BOOTMETH driver, compatible "u-boot,openwrt"
> > - check(): accept block devices via bootflow_iter_check_blk()
> > - bind(): set BOOTMETHF_ANY_PART so bootstd iterates all partitions
> > without requiring a filesystem
> > - read_bootflow(): stub returning -ENOENT
> > - boot(): stub returning -ENOSYS
> > - Kconfig: CONFIG_BOOTMETH_OPENWRT depending on FIT, BOOTSTD,
> > BOOTM_STORAGE
>
> We don't normally write out this much detail in the code
Yeah, that's Claude does, I'll drop the code description and only keep
the part below.
>
> >
> > This is the foundation for booting OpenWrt-style uImage.FIT firmware
> > images stored directly on raw storage partitions. The bootmeth will
> > delegate actual image loading to the existing bootm storage path via
> > image_loader.
> >
> > Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> > ---
> > boot/Kconfig | 13 +++++++++
> > boot/Makefile | 1 +
> > boot/bootmeth_openwrt.c | 65 +++++++++++++++++++++++++++++++++++++++++
> > 3 files changed, 79 insertions(+)
> > create mode 100644 boot/bootmeth_openwrt.c
>
> Reviewed-by: Simon Glass <simon.glass@canonical.com>
>
>
> >
> > diff --git a/boot/Kconfig b/boot/Kconfig
> > index efc06f3cd1a..d8c7b8360ac 100644
> > --- a/boot/Kconfig
> > +++ b/boot/Kconfig
> > @@ -585,6 +585,19 @@ config BOOTMETH_CROS
> >
> > Note that only x86 devices are supported at present.
> >
> > +config BOOTMETH_OPENWRT
> > + bool "Bootdev support for OpenWrt"
> > + depends on FIT
> > + depends on BOOTSTD
> > + depends on BOOTM_STORAGE
> > + help
> > + Enables support for booting OpenWrt-style uImage.FIT firmware
> > + images stored directly on raw storage (block device partitions,
> > + MTD partitions, or UBI volumes). The boot method loads the FIT
> > + metadata, selectively reads only the kernel and FDT sub-images
> > + from storage, and skips filesystem sub-images that Linux maps
> > + directly from flash.
> > +
> > config BOOTMETH_EXTLINUX
> > bool "Bootdev support for extlinux boot"
> > select PXE_UTILS
> > diff --git a/boot/Makefile b/boot/Makefile
> > index 7d1d4a28106..7b42358eb0c 100644
> > --- a/boot/Makefile
> > +++ b/boot/Makefile
> > @@ -72,6 +72,7 @@ obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_SIMPLE_FW) += vbe_simple_fw.o
> > obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_SIMPLE_OS) += vbe_simple_os.o
> >
> > obj-$(CONFIG_$(PHASE_)BOOTMETH_ANDROID) += bootmeth_android.o
> > +obj-$(CONFIG_$(PHASE_)BOOTMETH_OPENWRT) += bootmeth_openwrt.o
> >
> > obj-$(CONFIG_IMAGE_LOADER) += image-loader.o
> > obj-$(CONFIG_IMAGE_LOADER_BLK) += image-loader-blk.o
> > diff --git a/boot/bootmeth_openwrt.c b/boot/bootmeth_openwrt.c
> > new file mode 100644
> > index 00000000000..02bf543031b
> > --- /dev/null
> > +++ b/boot/bootmeth_openwrt.c
> > @@ -0,0 +1,65 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * Bootmethod for OpenWrt
> > + *
> > + * Copyright (C) 2026 Daniel Golle <daniel@makrotopia.org>
> > + */
> > +
> > +#define LOG_CATEGORY UCLASS_BOOTSTD
> > +
> > +#include <blk.h>
> > +#include <bootdev.h>
> > +#include <bootflow.h>
> > +#include <bootm.h>
> > +#include <bootmeth.h>
> > +#include <dm.h>
> > +#include <image-loader.h>
> > +#include <malloc.h>
> > +#include <mapmem.h>
> > +
> > +static int openwrt_check(struct udevice *dev, struct bootflow_iter *iter)
> > +{
> > + if (bootflow_iter_check_blk(iter))
> > + return log_msg_ret("blk", -EOPNOTSUPP);
> > +
> > + return 0;
> > +}
> > +
> > +static int openwrt_read_bootflow(struct udevice *dev, struct bootflow *bflow)
> > +{
> > + return log_msg_ret("nyi", -ENOENT);
> > +}
> > +
> > +static int openwrt_boot(struct udevice *dev, struct bootflow *bflow)
> > +{
> > + return log_msg_ret("nyi", -ENOSYS);
> > +}
> > +
> > +static int openwrt_bootmeth_bind(struct udevice *dev)
> > +{
> > + struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
> > +
> > + plat->desc = "OpenWrt";
> > + plat->flags = BOOTMETHF_ANY_PART;
> > +
> > + return 0;
> > +}
> > +
> > +static struct bootmeth_ops openwrt_bootmeth_ops = {
> > + .check = openwrt_check,
> > + .read_bootflow = openwrt_read_bootflow,
> > + .boot = openwrt_boot,
> > +};
> > +
> > +static const struct udevice_id openwrt_bootmeth_ids[] = {
> > + { .compatible = "u-boot,openwrt" },
> > + { }
> > +};
> > +
> > +U_BOOT_DRIVER(bootmeth_openwrt) = {
> > + .name = "bootmeth_openwrt",
> > + .id = UCLASS_BOOTMETH,
> > + .of_match = openwrt_bootmeth_ids,
> > + .ops = &openwrt_bootmeth_ops,
> > + .bind = openwrt_bootmeth_bind,
> > +};
> > --
> > 2.53.0
^ permalink raw reply [flat|nested] 88+ messages in thread
* [RFC PATCH 14/20] boot: bootmeth: openwrt: implement read_bootflow for block devices
2026-02-16 21:21 [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading Daniel Golle
` (12 preceding siblings ...)
2026-02-16 21:23 ` [RFC PATCH 13/20] boot: bootmeth: add OpenWrt boot method skeleton Daniel Golle
@ 2026-02-16 21:23 ` Daniel Golle
2026-02-19 13:09 ` Simon Glass
2026-02-16 21:23 ` [RFC PATCH 15/20] boot: bootmeth: openwrt: implement boot via bootm storage path Daniel Golle
` (11 subsequent siblings)
25 siblings, 1 reply; 88+ messages in thread
From: Daniel Golle @ 2026-02-16 21:23 UTC (permalink / raw)
To: Tom Rini, Simon Glass, Quentin Schulz, Daniel Golle,
Kory Maincent, Mattijs Korpershoek, Martin Schwan, Anshul Dalal,
Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
牛 志宏, Marek Vasut, Heinrich Schuchardt,
Wolfgang Wallner, Frank Wunderlich, David Lechner,
Osama Abdelkader, Mikhail Kshevetskiy, Michael Trimarchi,
Miquel Raynal, Andrew Goodbody, Yegor Yefremov, Mike Looijmans,
Weijie Gao, Alexander Stein, Neil Armstrong, Mayuresh Chitale,
Paul HENRYS, u-boot
Cc: John Crispin, Paul Spooren
Implement openwrt_read_bootflow() to discover OpenWrt FIT images on
raw block device partitions.
For each partition bootstd offers, read the first block and check for
a valid FDT header via fdt_check_header(). If the header is valid the
partition likely contains a FIT image and the bootflow is marked
BOOTFLOWST_READY.
Full FIT validation, configuration resolution, and sub-image loading
are all deferred to boot() which uses image_loader — keeping the scan
path fast and avoiding large reads for embedded-data FIT images that
can be many megabytes.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
boot/bootmeth_openwrt.c | 43 ++++++++++++++++++++++++++++++++++++++++-
1 file changed, 42 insertions(+), 1 deletion(-)
diff --git a/boot/bootmeth_openwrt.c b/boot/bootmeth_openwrt.c
index 02bf543031b..08c3f98957a 100644
--- a/boot/bootmeth_openwrt.c
+++ b/boot/bootmeth_openwrt.c
@@ -16,6 +16,10 @@
#include <image-loader.h>
#include <malloc.h>
#include <mapmem.h>
+#include <memalign.h>
+#include <part.h>
+#include <linux/libfdt.h>
+#include <linux/sizes.h>
static int openwrt_check(struct udevice *dev, struct bootflow_iter *iter)
{
@@ -27,7 +31,44 @@ static int openwrt_check(struct udevice *dev, struct bootflow_iter *iter)
static int openwrt_read_bootflow(struct udevice *dev, struct bootflow *bflow)
{
- return log_msg_ret("nyi", -ENOENT);
+ struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
+ const char *part_name = NULL;
+ struct disk_partition info;
+ void *buf;
+ int ret;
+
+ /* Get partition geometry */
+ ret = part_get_info(desc, bflow->part, &info);
+ if (ret)
+ return log_msg_ret("part", ret);
+
+ part_name = (const char *)info.name;
+
+ /* Read first block to probe for an FDT/FIT header */
+ buf = memalign(SZ_1K, desc->blksz);
+ if (!buf)
+ return log_msg_ret("mem", -ENOMEM);
+
+ ret = blk_read(bflow->blk, info.start, 1, buf);
+ if (ret != 1) {
+ free(buf);
+ return log_msg_ret("rd", -EIO);
+ }
+
+ /* Must start with a valid FDT header */
+ if (fdt_check_header(buf)) {
+ free(buf);
+ return -ENOENT;
+ }
+
+ free(buf);
+
+ /* Show the GPT partition label as Filename */
+ bflow->fname = strdup(part_name);
+
+ bflow->state = BOOTFLOWST_READY;
+
+ return 0;
}
static int openwrt_boot(struct udevice *dev, struct bootflow *bflow)
--
2.53.0
^ permalink raw reply related [flat|nested] 88+ messages in thread* Re: [RFC PATCH 14/20] boot: bootmeth: openwrt: implement read_bootflow for block devices
2026-02-16 21:23 ` [RFC PATCH 14/20] boot: bootmeth: openwrt: implement read_bootflow for block devices Daniel Golle
@ 2026-02-19 13:09 ` Simon Glass
0 siblings, 0 replies; 88+ messages in thread
From: Simon Glass @ 2026-02-19 13:09 UTC (permalink / raw)
To: Daniel Golle
Cc: Tom Rini, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
Hi Daniel,
On Mon, 16 Feb 2026 at 14:23, Daniel Golle <daniel@makrotopia.org> wrote:
>
> Implement openwrt_read_bootflow() to discover OpenWrt FIT images on
> raw block device partitions.
>
> For each partition bootstd offers, read the first block and check for
> a valid FDT header via fdt_check_header(). If the header is valid the
> partition likely contains a FIT image and the bootflow is marked
> BOOTFLOWST_READY.
>
> Full FIT validation, configuration resolution, and sub-image loading
> are all deferred to boot() which uses image_loader — keeping the scan
> path fast and avoiding large reads for embedded-data FIT images that
> can be many megabytes.
>
> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> ---
> boot/bootmeth_openwrt.c | 43 ++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 42 insertions(+), 1 deletion(-)
>
Reviewed-by: Simon Glass <simon.glass@canonical.com>
See below
> diff --git a/boot/bootmeth_openwrt.c b/boot/bootmeth_openwrt.c
> index 02bf543031b..08c3f98957a 100644
> --- a/boot/bootmeth_openwrt.c
> +++ b/boot/bootmeth_openwrt.c
> @@ -16,6 +16,10 @@
> #include <image-loader.h>
> #include <malloc.h>
> #include <mapmem.h>
> +#include <memalign.h>
> +#include <part.h>
> +#include <linux/libfdt.h>
> +#include <linux/sizes.h>
>
> static int openwrt_check(struct udevice *dev, struct bootflow_iter *iter)
> {
> @@ -27,7 +31,44 @@ static int openwrt_check(struct udevice *dev, struct bootflow_iter *iter)
>
> static int openwrt_read_bootflow(struct udevice *dev, struct bootflow *bflow)
> {
> - return log_msg_ret("nyi", -ENOENT);
> + struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
> + const char *part_name = NULL;
> + struct disk_partition info;
> + void *buf;
> + int ret;
> +
> + /* Get partition geometry */
> + ret = part_get_info(desc, bflow->part, &info);
> + if (ret)
> + return log_msg_ret("part", ret);
> +
> + part_name = (const char *)info.name;
> +
> + /* Read first block to probe for an FDT/FIT header */
> + buf = memalign(SZ_1K, desc->blksz);
Could you use ALLOC_CACHE_ALIGN_BUFFER() ?
> + if (!buf)
> + return log_msg_ret("mem", -ENOMEM);
> +
> + ret = blk_read(bflow->blk, info.start, 1, buf);
> + if (ret != 1) {
> + free(buf);
> + return log_msg_ret("rd", -EIO);
> + }
> +
> + /* Must start with a valid FDT header */
> + if (fdt_check_header(buf)) {
> + free(buf);
> + return -ENOENT;
> + }
> +
> + free(buf);
> +
> + /* Show the GPT partition label as Filename */
> + bflow->fname = strdup(part_name);
> +
> + bflow->state = BOOTFLOWST_READY;
At this point there is no indication that we actually have anything,
so I think this should move to a later patch.
> +
> + return 0;
> }
>
> static int openwrt_boot(struct udevice *dev, struct bootflow *bflow)
> --
> 2.53.0
Regards,
Simon
^ permalink raw reply [flat|nested] 88+ messages in thread
* [RFC PATCH 15/20] boot: bootmeth: openwrt: implement boot via bootm storage path
2026-02-16 21:21 [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading Daniel Golle
` (13 preceding siblings ...)
2026-02-16 21:23 ` [RFC PATCH 14/20] boot: bootmeth: openwrt: implement read_bootflow for block devices Daniel Golle
@ 2026-02-16 21:23 ` Daniel Golle
2026-02-19 13:09 ` Simon Glass
2026-02-16 21:23 ` [RFC PATCH 16/20] boot: bootdev: add MTD boot device Daniel Golle
` (10 subsequent siblings)
25 siblings, 1 reply; 88+ messages in thread
From: Daniel Golle @ 2026-02-16 21:23 UTC (permalink / raw)
To: Tom Rini, Simon Glass, Quentin Schulz, Daniel Golle,
Kory Maincent, Mattijs Korpershoek, Martin Schwan, Anshul Dalal,
Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
牛 志宏, Marek Vasut, Heinrich Schuchardt,
Wolfgang Wallner, Frank Wunderlich, David Lechner,
Osama Abdelkader, Mikhail Kshevetskiy, Michael Trimarchi,
Miquel Raynal, Andrew Goodbody, Yegor Yefremov, Mike Looijmans,
Weijie Gao, Alexander Stein, Neil Armstrong, Mayuresh Chitale,
Paul HENRYS, u-boot
Cc: John Crispin, Paul Spooren
Implement openwrt_boot() to boot a discovered OpenWrt FIT image:
1. Get the block interface name via blk_get_devtype() and construct
the dev_part_str (e.g. "0:3") from the block descriptor and
bootflow partition number
2. Initialise an image_loader for block devices via
image_loader_init_blk(), with alloc_ptr set to image_load_addr
3. Optionally read the 'bootconf' env variable and pass it as
'#config' in bmi.addr_img for FIT configuration selection
4. Call bootm_run() which runs the full bootm state machine:
START -> FINDOS -> FINDOTHER -> LOADOS -> OS_PREP -> OS_GO
The image_loader handles on-demand reading of the FIT metadata and
sub-images (kernel, FDT) directly from the raw partition. The
existing cleanup call in bootm_run_states() releases the loader just
before OS_GO. If bootm_run() returns (boot failure), cleanup is
called explicitly.
This completes the core block-device boot path for bootmeth_openwrt.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
boot/bootmeth_openwrt.c | 37 ++++++++++++++++++++++++++++++++++++-
1 file changed, 36 insertions(+), 1 deletion(-)
diff --git a/boot/bootmeth_openwrt.c b/boot/bootmeth_openwrt.c
index 08c3f98957a..82f39c17d7f 100644
--- a/boot/bootmeth_openwrt.c
+++ b/boot/bootmeth_openwrt.c
@@ -13,6 +13,8 @@
#include <bootm.h>
#include <bootmeth.h>
#include <dm.h>
+#include <env.h>
+#include <image.h>
#include <image-loader.h>
#include <malloc.h>
#include <mapmem.h>
@@ -73,7 +75,40 @@ static int openwrt_read_bootflow(struct udevice *dev, struct bootflow *bflow)
static int openwrt_boot(struct udevice *dev, struct bootflow *bflow)
{
- return log_msg_ret("nyi", -ENOSYS);
+ struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
+ const char *ifname = blk_get_devtype(bflow->blk);
+ struct image_loader ldr = {};
+ struct bootm_info bmi;
+ char dev_part_str[32];
+ char addr_img[64];
+ const char *conf;
+ int ret;
+
+ snprintf(dev_part_str, sizeof(dev_part_str), "%d:%d",
+ desc->devnum, bflow->part);
+
+ ret = image_loader_init_blk(&ldr, ifname, dev_part_str);
+ if (ret)
+ return log_msg_ret("ldr", ret);
+
+ ldr.alloc_ptr = image_load_addr;
+
+ bootm_init(&bmi);
+ bmi.loader = &ldr;
+
+ /* FIT config selection via #conf suffix */
+ conf = env_get("bootconf");
+ if (conf) {
+ snprintf(addr_img, sizeof(addr_img), "#%s", conf);
+ bmi.addr_img = addr_img;
+ }
+
+ ret = bootm_run(&bmi);
+
+ /* Clean up if bootm_run() returned (i.e. boot failed) */
+ image_loader_cleanup(&ldr);
+
+ return log_msg_ret("run", ret);
}
static int openwrt_bootmeth_bind(struct udevice *dev)
--
2.53.0
^ permalink raw reply related [flat|nested] 88+ messages in thread* Re: [RFC PATCH 15/20] boot: bootmeth: openwrt: implement boot via bootm storage path
2026-02-16 21:23 ` [RFC PATCH 15/20] boot: bootmeth: openwrt: implement boot via bootm storage path Daniel Golle
@ 2026-02-19 13:09 ` Simon Glass
0 siblings, 0 replies; 88+ messages in thread
From: Simon Glass @ 2026-02-19 13:09 UTC (permalink / raw)
To: Daniel Golle
Cc: Tom Rini, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
Hi Daniel,
On Mon, 16 Feb 2026 at 14:23, Daniel Golle <daniel@makrotopia.org> wrote:
>
> Implement openwrt_boot() to boot a discovered OpenWrt FIT image:
>
> 1. Get the block interface name via blk_get_devtype() and construct
> the dev_part_str (e.g. "0:3") from the block descriptor and
> bootflow partition number
> 2. Initialise an image_loader for block devices via
> image_loader_init_blk(), with alloc_ptr set to image_load_addr
> 3. Optionally read the 'bootconf' env variable and pass it as
> '#config' in bmi.addr_img for FIT configuration selection
> 4. Call bootm_run() which runs the full bootm state machine:
> START -> FINDOS -> FINDOTHER -> LOADOS -> OS_PREP -> OS_GO
>
> The image_loader handles on-demand reading of the FIT metadata and
> sub-images (kernel, FDT) directly from the raw partition. The
> existing cleanup call in bootm_run_states() releases the loader just
> before OS_GO. If bootm_run() returns (boot failure), cleanup is
> called explicitly.
>
> This completes the core block-device boot path for bootmeth_openwrt.
>
> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> ---
> boot/bootmeth_openwrt.c | 37 ++++++++++++++++++++++++++++++++++++-
> 1 file changed, 36 insertions(+), 1 deletion(-)
>
> diff --git a/boot/bootmeth_openwrt.c b/boot/bootmeth_openwrt.c
> index 08c3f98957a..82f39c17d7f 100644
> --- a/boot/bootmeth_openwrt.c
> +++ b/boot/bootmeth_openwrt.c
> @@ -13,6 +13,8 @@
> #include <bootm.h>
> #include <bootmeth.h>
> #include <dm.h>
> +#include <env.h>
> +#include <image.h>
> #include <image-loader.h>
> #include <malloc.h>
> #include <mapmem.h>
> @@ -73,7 +75,40 @@ static int openwrt_read_bootflow(struct udevice *dev, struct bootflow *bflow)
>
> static int openwrt_boot(struct udevice *dev, struct bootflow *bflow)
> {
> - return log_msg_ret("nyi", -ENOSYS);
> + struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
> + const char *ifname = blk_get_devtype(bflow->blk);
> + struct image_loader ldr = {};
> + struct bootm_info bmi;
> + char dev_part_str[32];
> + char addr_img[64];
> + const char *conf;
> + int ret;
> +
> + snprintf(dev_part_str, sizeof(dev_part_str), "%d:%d",
> + desc->devnum, bflow->part);
> +
> + ret = image_loader_init_blk(&ldr, ifname, dev_part_str);
> + if (ret)
> + return log_msg_ret("ldr", ret);
> +
> + ldr.alloc_ptr = image_load_addr;
> +
> + bootm_init(&bmi);
> + bmi.loader = &ldr;
> +
> + /* FIT config selection via #conf suffix */
> + conf = env_get("bootconf");
> + if (conf) {
> + snprintf(addr_img, sizeof(addr_img), "#%s", conf);
> + bmi.addr_img = addr_img;
> + }
> +
> + ret = bootm_run(&bmi);
> +
> + /* Clean up if bootm_run() returned (i.e. boot failed) */
> + image_loader_cleanup(&ldr);
> +
> + return log_msg_ret("run", ret);
> }
>
> static int openwrt_bootmeth_bind(struct udevice *dev)
> --
> 2.53.0
This looks OK so far as it goes, but will need rework for the driver change.
Regards,
Simon
^ permalink raw reply [flat|nested] 88+ messages in thread
* [RFC PATCH 16/20] boot: bootdev: add MTD boot device
2026-02-16 21:21 [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading Daniel Golle
` (14 preceding siblings ...)
2026-02-16 21:23 ` [RFC PATCH 15/20] boot: bootmeth: openwrt: implement boot via bootm storage path Daniel Golle
@ 2026-02-16 21:23 ` Daniel Golle
2026-02-19 13:09 ` Simon Glass
2026-02-16 21:24 ` [RFC PATCH 17/20] boot: bootdev: add UBI " Daniel Golle
` (9 subsequent siblings)
25 siblings, 1 reply; 88+ messages in thread
From: Daniel Golle @ 2026-02-16 21:23 UTC (permalink / raw)
To: Tom Rini, Simon Glass, Quentin Schulz, Daniel Golle,
Kory Maincent, Mattijs Korpershoek, Martin Schwan, Anshul Dalal,
Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
牛 志宏, Marek Vasut, Heinrich Schuchardt,
Wolfgang Wallner, Frank Wunderlich, David Lechner,
Osama Abdelkader, Mikhail Kshevetskiy, Michael Trimarchi,
Miquel Raynal, Andrew Goodbody, Yegor Yefremov, Mike Looijmans,
Weijie Gao, Alexander Stein, Neil Armstrong, Mayuresh Chitale,
Paul HENRYS, u-boot
Cc: John Crispin, Paul Spooren
Add a boot device driver for MTD (Memory Technology Devices) that
enables bootstd to scan MTD partitions for FIT images.
Add a mtd_bootdev_hunt() callback that:
- Calls mtd_probe_devices() once to probe all MTD devices (both
UCLASS_MTD and UCLASS_SPI_FLASH) and parse their partitions
- Iterates the MTD subsystem device list and binds an mtd_bootdev for
any top-level MTD device that has partitions but is not in UCLASS_MTD
(those already get their bootdev from mtd_post_bind)
- Uses bootdev_bind() directly since sf_bootdev may already occupy the
standard bootdev child slot
The MTD bootdev calls bootmeth_check() at the start of get_bootflow()
so that only compatible bootmeths (those that accept MTD bootdevs)
produce bootflows. Until bootmeth_openwrt's check() is extended to
accept MTD bootdevs, this driver is inert — each commit compiles and
introduces no regressions independently.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
boot/Kconfig | 9 +++
boot/Makefile | 1 +
boot/mtd_bootdev.c | 150 +++++++++++++++++++++++++++++++++++++++
drivers/mtd/mtd-uclass.c | 15 ++++
4 files changed, 175 insertions(+)
create mode 100644 boot/mtd_bootdev.c
diff --git a/boot/Kconfig b/boot/Kconfig
index d8c7b8360ac..63e373cc62d 100644
--- a/boot/Kconfig
+++ b/boot/Kconfig
@@ -585,6 +585,15 @@ config BOOTMETH_CROS
Note that only x86 devices are supported at present.
+config BOOTDEV_MTD
+ bool "MTD bootdev support"
+ depends on DM_MTD
+ depends on BOOTSTD
+ help
+ Enable a boot device for MTD (Memory Technology Devices).
+ This scans MTD partitions for uImage.FIT firmware images,
+ enabling raw-flash boot via the OpenWrt boot method.
+
config BOOTMETH_OPENWRT
bool "Bootdev support for OpenWrt"
depends on FIT
diff --git a/boot/Makefile b/boot/Makefile
index 7b42358eb0c..feeed4924dd 100644
--- a/boot/Makefile
+++ b/boot/Makefile
@@ -72,6 +72,7 @@ obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_SIMPLE_FW) += vbe_simple_fw.o
obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_SIMPLE_OS) += vbe_simple_os.o
obj-$(CONFIG_$(PHASE_)BOOTMETH_ANDROID) += bootmeth_android.o
+obj-$(CONFIG_$(PHASE_)BOOTDEV_MTD) += mtd_bootdev.o
obj-$(CONFIG_$(PHASE_)BOOTMETH_OPENWRT) += bootmeth_openwrt.o
obj-$(CONFIG_IMAGE_LOADER) += image-loader.o
diff --git a/boot/mtd_bootdev.c b/boot/mtd_bootdev.c
new file mode 100644
index 00000000000..8a3304be988
--- /dev/null
+++ b/boot/mtd_bootdev.c
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * MTD boot device
+ *
+ * Copyright (C) 2026 Daniel Golle <daniel@makrotopia.org>
+ */
+
+#define LOG_CATEGORY UCLASS_BOOTSTD
+
+#include <bootdev.h>
+#include <bootflow.h>
+#include <bootmeth.h>
+#include <dm.h>
+#include <malloc.h>
+#include <mtd.h>
+#include <linux/libfdt.h>
+
+static int mtd_bootdev_get_bootflow(struct udevice *dev,
+ struct bootflow_iter *iter,
+ struct bootflow *bflow)
+{
+ struct udevice *parent = dev_get_parent(dev);
+ struct mtd_info *mtd, *part;
+ u8 buf[40];
+ size_t retlen;
+ char dname[60];
+ int n = 0;
+ int ret;
+
+ ret = bootmeth_check(bflow->method, iter);
+ if (ret)
+ return log_msg_ret("chk", ret);
+
+ /* Find the top-level MTD device matching our parent */
+ mtd_for_each_device(mtd) {
+ if (!mtd_is_partition(mtd) && mtd->dev == parent)
+ break;
+ }
+ if (!mtd || mtd->dev != parent)
+ return log_msg_ret("mtd", -ESHUTDOWN);
+
+ /* Count partitions so the scanning framework knows the bound */
+ list_for_each_entry(part, &mtd->partitions, node)
+ n++;
+ if (n)
+ iter->max_part = n - 1;
+
+ n = 0;
+
+ /* Walk to the iter->part'th sub-partition */
+ list_for_each_entry(part, &mtd->partitions, node) {
+ if (n == iter->part)
+ goto found;
+ n++;
+ }
+ return -ESHUTDOWN;
+
+found:
+ ret = mtd_read(part, 0, sizeof(buf), &retlen, buf);
+ if (ret || retlen < sizeof(buf))
+ return log_msg_ret("rd", -EIO);
+
+ if (fdt_check_header(buf))
+ return log_msg_ret("fdt", -ENOENT);
+
+ /* Device-style name and partition index for bootflow list display */
+ snprintf(dname, sizeof(dname), "%s.part_%x", dev->name, iter->part);
+ bflow->name = strdup(dname);
+ bflow->part = iter->part;
+ bflow->fname = strdup(part->name);
+ bflow->bootmeth_priv = strdup(part->name);
+ bflow->state = BOOTFLOWST_MEDIA;
+
+ return bootmeth_read_bootflow(bflow->method, bflow);
+}
+
+static int mtd_bootdev_bind(struct udevice *dev)
+{
+ struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev);
+
+ ucp->prio = BOOTDEVP_4_SCAN_FAST;
+
+ return 0;
+}
+
+struct bootdev_ops mtd_bootdev_ops = {
+ .get_bootflow = mtd_bootdev_get_bootflow,
+};
+
+static const struct udevice_id mtd_bootdev_ids[] = {
+ { .compatible = "u-boot,bootdev-mtd" },
+ { }
+};
+
+U_BOOT_DRIVER(mtd_bootdev) = {
+ .name = "mtd_bootdev",
+ .id = UCLASS_BOOTDEV,
+ .ops = &mtd_bootdev_ops,
+ .bind = mtd_bootdev_bind,
+ .of_match = mtd_bootdev_ids,
+};
+
+/**
+ * mtd_bootdev_hunt() - probe MTD devices and bind bootdevs
+ *
+ * Call mtd_probe_devices() to ensure all MTD devices (including SPI NOR
+ * flash via CONFIG_SPI_FLASH_MTD) are probed and their partitions parsed.
+ *
+ * UCLASS_MTD devices already get an mtd_bootdev via mtd_post_bind().
+ * This creates mtd_bootdev instances for other MTD devices (e.g. SPI NOR
+ * in UCLASS_SPI_FLASH) that have partitions but would otherwise lack one.
+ * bootdev_bind() is used directly because sf_bootdev may already occupy
+ * the standard bootdev child slot.
+ */
+static int mtd_bootdev_hunt(struct bootdev_hunter *info, bool show)
+{
+ struct mtd_info *mtd;
+ struct udevice *bdev;
+
+ mtd_probe_devices();
+
+ mtd_for_each_device(mtd) {
+ /* Only top-level MTD devices, not partitions */
+ if (mtd_is_partition(mtd))
+ continue;
+
+ /* Must have a DM device */
+ if (!mtd->dev)
+ continue;
+
+ /* UCLASS_MTD devices already have mtd_bootdev from post_bind */
+ if (device_get_uclass_id(mtd->dev) == UCLASS_MTD)
+ continue;
+
+ /* Only interested in MTD devices that have partitions */
+ if (list_empty(&mtd->partitions))
+ continue;
+
+ bootdev_bind(mtd->dev, "mtd_bootdev", "mtdbootdev", &bdev);
+ }
+
+ return 0;
+}
+
+BOOTDEV_HUNTER(mtd_bootdev_hunter) = {
+ .prio = BOOTDEVP_4_SCAN_FAST,
+ .uclass = UCLASS_MTD,
+ .drv = DM_DRIVER_REF(mtd_bootdev),
+ .hunt = mtd_bootdev_hunt,
+};
diff --git a/drivers/mtd/mtd-uclass.c b/drivers/mtd/mtd-uclass.c
index 720bd824c4d..0c637d3f5ec 100644
--- a/drivers/mtd/mtd-uclass.c
+++ b/drivers/mtd/mtd-uclass.c
@@ -5,11 +5,25 @@
#define LOG_CATEGORY UCLASS_MTD
+#include <bootdev.h>
#include <dm.h>
#include <dm/device-internal.h>
#include <errno.h>
#include <mtd.h>
+static int mtd_post_bind(struct udevice *dev)
+{
+ if (CONFIG_IS_ENABLED(BOOTDEV_MTD)) {
+ int ret;
+
+ ret = bootdev_setup_for_dev(dev, "mtd_bootdev");
+ if (ret)
+ return log_msg_ret("bd", ret);
+ }
+
+ return 0;
+}
+
/*
* Implement a MTD uclass which should include most flash drivers.
* The uclass private is pointed to mtd_info.
@@ -18,5 +32,6 @@
UCLASS_DRIVER(mtd) = {
.id = UCLASS_MTD,
.name = "mtd",
+ .post_bind = mtd_post_bind,
.per_device_auto = sizeof(struct mtd_info),
};
--
2.53.0
^ permalink raw reply related [flat|nested] 88+ messages in thread* Re: [RFC PATCH 16/20] boot: bootdev: add MTD boot device
2026-02-16 21:23 ` [RFC PATCH 16/20] boot: bootdev: add MTD boot device Daniel Golle
@ 2026-02-19 13:09 ` Simon Glass
0 siblings, 0 replies; 88+ messages in thread
From: Simon Glass @ 2026-02-19 13:09 UTC (permalink / raw)
To: Daniel Golle
Cc: Tom Rini, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
Hi Daniel,
On Mon, 16 Feb 2026 at 14:23, Daniel Golle <daniel@makrotopia.org> wrote:
>
> Add a boot device driver for MTD (Memory Technology Devices) that
> enables bootstd to scan MTD partitions for FIT images.
>
> Add a mtd_bootdev_hunt() callback that:
> - Calls mtd_probe_devices() once to probe all MTD devices (both
> UCLASS_MTD and UCLASS_SPI_FLASH) and parse their partitions
> - Iterates the MTD subsystem device list and binds an mtd_bootdev for
> any top-level MTD device that has partitions but is not in UCLASS_MTD
> (those already get their bootdev from mtd_post_bind)
> - Uses bootdev_bind() directly since sf_bootdev may already occupy the
> standard bootdev child slot
>
> The MTD bootdev calls bootmeth_check() at the start of get_bootflow()
> so that only compatible bootmeths (those that accept MTD bootdevs)
> produce bootflows. Until bootmeth_openwrt's check() is extended to
> accept MTD bootdevs, this driver is inert — each commit compiles and
> introduces no regressions independently.
>
> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> ---
> boot/Kconfig | 9 +++
> boot/Makefile | 1 +
> boot/mtd_bootdev.c | 150 +++++++++++++++++++++++++++++++++++++++
> drivers/mtd/mtd-uclass.c | 15 ++++
> 4 files changed, 175 insertions(+)
> create mode 100644 boot/mtd_bootdev.c
>
Reviewed-by: Simon Glass <simon.glass@canonical.com>
nits below
> diff --git a/boot/Kconfig b/boot/Kconfig
> index d8c7b8360ac..63e373cc62d 100644
> --- a/boot/Kconfig
> +++ b/boot/Kconfig
> @@ -585,6 +585,15 @@ config BOOTMETH_CROS
>
> Note that only x86 devices are supported at present.
>
> +config BOOTDEV_MTD
> + bool "MTD bootdev support"
> + depends on DM_MTD
> + depends on BOOTSTD
> + help
> + Enable a boot device for MTD (Memory Technology Devices).
> + This scans MTD partitions for uImage.FIT firmware images,
> + enabling raw-flash boot via the OpenWrt boot method.
> +
> config BOOTMETH_OPENWRT
> bool "Bootdev support for OpenWrt"
> depends on FIT
> diff --git a/boot/Makefile b/boot/Makefile
> index 7b42358eb0c..feeed4924dd 100644
> --- a/boot/Makefile
> +++ b/boot/Makefile
> @@ -72,6 +72,7 @@ obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_SIMPLE_FW) += vbe_simple_fw.o
> obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_SIMPLE_OS) += vbe_simple_os.o
>
> obj-$(CONFIG_$(PHASE_)BOOTMETH_ANDROID) += bootmeth_android.o
> +obj-$(CONFIG_$(PHASE_)BOOTDEV_MTD) += mtd_bootdev.o
> obj-$(CONFIG_$(PHASE_)BOOTMETH_OPENWRT) += bootmeth_openwrt.o
>
> obj-$(CONFIG_IMAGE_LOADER) += image-loader.o
> diff --git a/boot/mtd_bootdev.c b/boot/mtd_bootdev.c
> new file mode 100644
> index 00000000000..8a3304be988
> --- /dev/null
> +++ b/boot/mtd_bootdev.c
> @@ -0,0 +1,150 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * MTD boot device
> + *
> + * Copyright (C) 2026 Daniel Golle <daniel@makrotopia.org>
> + */
> +
> +#define LOG_CATEGORY UCLASS_BOOTSTD
> +
> +#include <bootdev.h>
> +#include <bootflow.h>
> +#include <bootmeth.h>
> +#include <dm.h>
> +#include <malloc.h>
> +#include <mtd.h>
> +#include <linux/libfdt.h>
> +
> +static int mtd_bootdev_get_bootflow(struct udevice *dev,
> + struct bootflow_iter *iter,
> + struct bootflow *bflow)
> +{
> + struct udevice *parent = dev_get_parent(dev);
> + struct mtd_info *mtd, *part;
> + u8 buf[40];
> + size_t retlen;
> + char dname[60];
> + int n = 0;
> + int ret;
> +
> + ret = bootmeth_check(bflow->method, iter);
> + if (ret)
> + return log_msg_ret("chk", ret);
> +
> + /* Find the top-level MTD device matching our parent */
> + mtd_for_each_device(mtd) {
> + if (!mtd_is_partition(mtd) && mtd->dev == parent)
> + break;
> + }
> + if (!mtd || mtd->dev != parent)
> + return log_msg_ret("mtd", -ESHUTDOWN);
> +
> + /* Count partitions so the scanning framework knows the bound */
> + list_for_each_entry(part, &mtd->partitions, node)
> + n++;
> + if (n)
> + iter->max_part = n - 1;
> +
> + n = 0;
> +
> + /* Walk to the iter->part'th sub-partition */
> + list_for_each_entry(part, &mtd->partitions, node) {
> + if (n == iter->part)
> + goto found;
> + n++;
> + }
blank line here
> + return -ESHUTDOWN;
> +
> +found:
> + ret = mtd_read(part, 0, sizeof(buf), &retlen, buf);
> + if (ret || retlen < sizeof(buf))
> + return log_msg_ret("rd", -EIO);
> +
> + if (fdt_check_header(buf))
> + return log_msg_ret("fdt", -ENOENT);
> +
> + /* Device-style name and partition index for bootflow list display */
> + snprintf(dname, sizeof(dname), "%s.part_%x", dev->name, iter->part);
> + bflow->name = strdup(dname);
> + bflow->part = iter->part;
> + bflow->fname = strdup(part->name);
> + bflow->bootmeth_priv = strdup(part->name);
> + bflow->state = BOOTFLOWST_MEDIA;
> +
> + return bootmeth_read_bootflow(bflow->method, bflow);
> +}
> +
> +static int mtd_bootdev_bind(struct udevice *dev)
> +{
> + struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev);
> +
> + ucp->prio = BOOTDEVP_4_SCAN_FAST;
> +
> + return 0;
> +}
> +
> +struct bootdev_ops mtd_bootdev_ops = {
> + .get_bootflow = mtd_bootdev_get_bootflow,
> +};
> +
> +static const struct udevice_id mtd_bootdev_ids[] = {
> + { .compatible = "u-boot,bootdev-mtd" },
> + { }
> +};
> +
> +U_BOOT_DRIVER(mtd_bootdev) = {
> + .name = "mtd_bootdev",
> + .id = UCLASS_BOOTDEV,
> + .ops = &mtd_bootdev_ops,
> + .bind = mtd_bootdev_bind,
> + .of_match = mtd_bootdev_ids,
> +};
> +
> +/**
> + * mtd_bootdev_hunt() - probe MTD devices and bind bootdevs
> + *
> + * Call mtd_probe_devices() to ensure all MTD devices (including SPI NOR
> + * flash via CONFIG_SPI_FLASH_MTD) are probed and their partitions parsed.
> + *
> + * UCLASS_MTD devices already get an mtd_bootdev via mtd_post_bind().
> + * This creates mtd_bootdev instances for other MTD devices (e.g. SPI NOR
> + * in UCLASS_SPI_FLASH) that have partitions but would otherwise lack one.
> + * bootdev_bind() is used directly because sf_bootdev may already occupy
> + * the standard bootdev child slot.
> + */
> +static int mtd_bootdev_hunt(struct bootdev_hunter *info, bool show)
> +{
> + struct mtd_info *mtd;
> + struct udevice *bdev;
> +
> + mtd_probe_devices();
> +
> + mtd_for_each_device(mtd) {
> + /* Only top-level MTD devices, not partitions */
> + if (mtd_is_partition(mtd))
> + continue;
> +
> + /* Must have a DM device */
> + if (!mtd->dev)
> + continue;
> +
> + /* UCLASS_MTD devices already have mtd_bootdev from post_bind */
> + if (device_get_uclass_id(mtd->dev) == UCLASS_MTD)
> + continue;
> +
> + /* Only interested in MTD devices that have partitions */
> + if (list_empty(&mtd->partitions))
> + continue;
> +
> + bootdev_bind(mtd->dev, "mtd_bootdev", "mtdbootdev", &bdev);
Check error
> + }
> +
> + return 0;
> +}
> +
> +BOOTDEV_HUNTER(mtd_bootdev_hunter) = {
> + .prio = BOOTDEVP_4_SCAN_FAST,
> + .uclass = UCLASS_MTD,
> + .drv = DM_DRIVER_REF(mtd_bootdev),
> + .hunt = mtd_bootdev_hunt,
> +};
> diff --git a/drivers/mtd/mtd-uclass.c b/drivers/mtd/mtd-uclass.c
> index 720bd824c4d..0c637d3f5ec 100644
> --- a/drivers/mtd/mtd-uclass.c
> +++ b/drivers/mtd/mtd-uclass.c
> @@ -5,11 +5,25 @@
>
> #define LOG_CATEGORY UCLASS_MTD
>
> +#include <bootdev.h>
> #include <dm.h>
> #include <dm/device-internal.h>
> #include <errno.h>
> #include <mtd.h>
>
> +static int mtd_post_bind(struct udevice *dev)
> +{
> + if (CONFIG_IS_ENABLED(BOOTDEV_MTD)) {
> + int ret;
> +
> + ret = bootdev_setup_for_dev(dev, "mtd_bootdev");
> + if (ret)
> + return log_msg_ret("bd", ret);
> + }
> +
> + return 0;
> +}
> +
> /*
> * Implement a MTD uclass which should include most flash drivers.
> * The uclass private is pointed to mtd_info.
> @@ -18,5 +32,6 @@
> UCLASS_DRIVER(mtd) = {
> .id = UCLASS_MTD,
> .name = "mtd",
> + .post_bind = mtd_post_bind,
> .per_device_auto = sizeof(struct mtd_info),
> };
> --
> 2.53.0
Regards,
Simon
^ permalink raw reply [flat|nested] 88+ messages in thread
* [RFC PATCH 17/20] boot: bootdev: add UBI boot device
2026-02-16 21:21 [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading Daniel Golle
` (15 preceding siblings ...)
2026-02-16 21:23 ` [RFC PATCH 16/20] boot: bootdev: add MTD boot device Daniel Golle
@ 2026-02-16 21:24 ` Daniel Golle
2026-02-19 13:09 ` Simon Glass
2026-02-16 21:24 ` [RFC PATCH 18/20] boot: bootmeth: openwrt: support MTD and UBI bootdevs Daniel Golle
` (8 subsequent siblings)
25 siblings, 1 reply; 88+ messages in thread
From: Daniel Golle @ 2026-02-16 21:24 UTC (permalink / raw)
To: Tom Rini, Simon Glass, Quentin Schulz, Daniel Golle,
Kory Maincent, Mattijs Korpershoek, Martin Schwan, Anshul Dalal,
Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
牛 志宏, Marek Vasut, Heinrich Schuchardt,
Wolfgang Wallner, Frank Wunderlich, David Lechner,
Osama Abdelkader, Mikhail Kshevetskiy, Michael Trimarchi,
Miquel Raynal, Andrew Goodbody, Yegor Yefremov, Mike Looijmans,
Weijie Gao, Alexander Stein, Neil Armstrong, Mayuresh Chitale,
Paul HENRYS, u-boot
Cc: John Crispin, Paul Spooren
Add a boot device driver for UBI (Unsorted Block Images) that enables
bootstd to scan UBI volumes for FIT images.
The driver:
- Has a hunt callback that auto-attaches UBI: walks the DT for the
first partition with compatible = "linux,ubi", finds the matching
MTD device, and calls ubi_part_from_mtd() to attach
- Binds directly via bootdev_bind() as a child of the top-level MTD
device, using a distinct name ("ubibootdev") to coexist with the
MTD bootdev
- Iterates UBI volumes using iter->part as an index
- Reads the first bytes of each volume and checks for a valid FDT
header via fdt_check_header()
- Stores the UBI volume name in bflow->bootmeth_priv for the
bootmeth's boot() to pass to image_loader_init_ubi()
- Returns -ESHUTDOWN when all volumes are exhausted
Like the MTD bootdev, calls bootmeth_check() first so that only
compatible bootmeths produce bootflows. Inert until bootmeth_openwrt
is extended to accept UBI bootdevs.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
boot/Kconfig | 9 +++
boot/Makefile | 1 +
boot/ubi_bootdev.c | 180 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 190 insertions(+)
create mode 100644 boot/ubi_bootdev.c
diff --git a/boot/Kconfig b/boot/Kconfig
index 63e373cc62d..75d40744e69 100644
--- a/boot/Kconfig
+++ b/boot/Kconfig
@@ -594,6 +594,15 @@ config BOOTDEV_MTD
This scans MTD partitions for uImage.FIT firmware images,
enabling raw-flash boot via the OpenWrt boot method.
+config BOOTDEV_UBI
+ bool "UBI bootdev support"
+ depends on CMD_UBI
+ depends on BOOTSTD
+ help
+ Enable a boot device for UBI (Unsorted Block Images).
+ This scans UBI volumes for uImage.FIT firmware images,
+ enabling raw-flash boot via the OpenWrt boot method.
+
config BOOTMETH_OPENWRT
bool "Bootdev support for OpenWrt"
depends on FIT
diff --git a/boot/Makefile b/boot/Makefile
index feeed4924dd..aa7968c3932 100644
--- a/boot/Makefile
+++ b/boot/Makefile
@@ -73,6 +73,7 @@ obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_SIMPLE_OS) += vbe_simple_os.o
obj-$(CONFIG_$(PHASE_)BOOTMETH_ANDROID) += bootmeth_android.o
obj-$(CONFIG_$(PHASE_)BOOTDEV_MTD) += mtd_bootdev.o
+obj-$(CONFIG_$(PHASE_)BOOTDEV_UBI) += ubi_bootdev.o
obj-$(CONFIG_$(PHASE_)BOOTMETH_OPENWRT) += bootmeth_openwrt.o
obj-$(CONFIG_IMAGE_LOADER) += image-loader.o
diff --git a/boot/ubi_bootdev.c b/boot/ubi_bootdev.c
new file mode 100644
index 00000000000..03131a4ee1b
--- /dev/null
+++ b/boot/ubi_bootdev.c
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * UBI boot device
+ *
+ * Copyright (C) 2026 Daniel Golle <daniel@makrotopia.org>
+ */
+
+#define LOG_CATEGORY UCLASS_BOOTSTD
+
+#include <bootdev.h>
+#include <bootflow.h>
+#include <bootmeth.h>
+#include <dm.h>
+#include <dm/ofnode.h>
+#include <malloc.h>
+#include <mtd.h>
+#include <ubi_uboot.h>
+#include <linux/libfdt.h>
+
+static int ubi_bootdev_get_bootflow(struct udevice *dev,
+ struct bootflow_iter *iter,
+ struct bootflow *bflow)
+{
+ struct ubi_device *ubi;
+ struct ubi_volume *vol;
+ struct mtd_info *ubi_mtd, *top_mtd, *part;
+ char buf[40];
+ char dname[60];
+ int ubi_part_idx = 0;
+ int n = 0;
+ int i, ret;
+
+ ret = bootmeth_check(bflow->method, iter);
+ if (ret)
+ return log_msg_ret("chk", ret);
+
+ ubi = ubi_devices[0];
+ if (!ubi)
+ return log_msg_ret("ubi", -ENODEV);
+
+ /* Count volumes so the scanning framework knows the bound */
+ for (i = 0; i < ubi->vtbl_slots; i++) {
+ if (ubi->volumes[i])
+ n++;
+ }
+ if (n)
+ iter->max_part = n - 1;
+
+ n = 0;
+
+ /* Walk to the iter->part'th UBI volume */
+ for (i = 0; i < ubi->vtbl_slots; i++) {
+ vol = ubi->volumes[i];
+ if (!vol)
+ continue;
+ if (n == iter->part)
+ goto found;
+ n++;
+ }
+ return -ESHUTDOWN;
+
+found:
+ ret = ubi_volume_read(vol->name, buf, 0, sizeof(buf));
+ if (ret)
+ return log_msg_ret("rd", -EIO);
+
+ if (fdt_check_header(buf))
+ return log_msg_ret("fdt", -ENOENT);
+
+ /*
+ * Find the MTD partition index hosting UBI so we can display
+ * a meaningful partition number in the bootflow listing.
+ */
+ ubi_mtd = ubi->mtd;
+ top_mtd = ubi_mtd;
+ while (top_mtd->parent)
+ top_mtd = top_mtd->parent;
+
+ n = 0;
+ list_for_each_entry(part, &top_mtd->partitions, node) {
+ if (part == ubi_mtd) {
+ ubi_part_idx = n;
+ break;
+ }
+ n++;
+ }
+
+ /* Device-style name and partition index for bootflow list display */
+ snprintf(dname, sizeof(dname), "%s.part_%x", dev->name, ubi_part_idx);
+ bflow->name = strdup(dname);
+ bflow->part = ubi_part_idx;
+ bflow->fname = strdup(vol->name);
+ bflow->bootmeth_priv = strdup(vol->name);
+ bflow->state = BOOTFLOWST_MEDIA;
+
+ return bootmeth_read_bootflow(bflow->method, bflow);
+}
+
+static int ubi_bootdev_bind(struct udevice *dev)
+{
+ struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev);
+
+ ucp->prio = BOOTDEVP_4_SCAN_FAST;
+
+ return 0;
+}
+
+/**
+ * ubi_bootdev_hunt() - attach UBI and bind a bootdev for it
+ *
+ * Walk the DT for the first partition with compatible = "linux,ubi",
+ * find its MTD device, attach UBI via ubi_part_from_mtd(), then bind
+ * a ubi_bootdev as a child of the top-level MTD DM device.
+ */
+static int ubi_bootdev_hunt(struct bootdev_hunter *info, bool show)
+{
+ struct udevice *bdev;
+ struct mtd_info *mtd;
+ ofnode node;
+ int ret;
+
+ mtd_probe_devices();
+
+ if (!ubi_devices[0]) {
+ ofnode_for_each_compatible_node(node, "linux,ubi") {
+ mtd_for_each_device(mtd) {
+ if (ofnode_equal(mtd->flash_node, node))
+ goto found;
+ }
+ }
+ return -ENOENT;
+found:
+ ret = ubi_part_from_mtd(mtd);
+ if (ret)
+ return log_msg_ret("att", -ENOENT);
+ }
+
+ /* Find the top-level MTD device for the attached UBI */
+ mtd = ubi_devices[0]->mtd;
+ while (mtd->parent)
+ mtd = mtd->parent;
+
+ if (!mtd->dev)
+ return log_msg_ret("dev", -ENODEV);
+
+ /*
+ * Bind directly — bootdev_setup_for_dev() cannot be used here
+ * because the MTD bootdev may already occupy the single bootdev
+ * child slot.
+ */
+ ret = bootdev_bind(mtd->dev, "ubi_bootdev", "ubibootdev", &bdev);
+ if (ret)
+ return log_msg_ret("bd", ret);
+
+ return 0;
+}
+
+struct bootdev_ops ubi_bootdev_ops = {
+ .get_bootflow = ubi_bootdev_get_bootflow,
+};
+
+static const struct udevice_id ubi_bootdev_ids[] = {
+ { .compatible = "u-boot,bootdev-ubi" },
+ { }
+};
+
+U_BOOT_DRIVER(ubi_bootdev) = {
+ .name = "ubi_bootdev",
+ .id = UCLASS_BOOTDEV,
+ .ops = &ubi_bootdev_ops,
+ .bind = ubi_bootdev_bind,
+ .of_match = ubi_bootdev_ids,
+};
+
+BOOTDEV_HUNTER(ubi_bootdev_hunter) = {
+ .prio = BOOTDEVP_4_SCAN_FAST,
+ .uclass = UCLASS_MTD,
+ .drv = DM_DRIVER_REF(ubi_bootdev),
+ .hunt = ubi_bootdev_hunt,
+};
--
2.53.0
^ permalink raw reply related [flat|nested] 88+ messages in thread* Re: [RFC PATCH 17/20] boot: bootdev: add UBI boot device
2026-02-16 21:24 ` [RFC PATCH 17/20] boot: bootdev: add UBI " Daniel Golle
@ 2026-02-19 13:09 ` Simon Glass
2026-02-19 16:56 ` Daniel Golle
0 siblings, 1 reply; 88+ messages in thread
From: Simon Glass @ 2026-02-19 13:09 UTC (permalink / raw)
To: Daniel Golle
Cc: Tom Rini, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
Hi Daniel,
On Mon, 16 Feb 2026 at 14:24, Daniel Golle <daniel@makrotopia.org> wrote:
>
> Add a boot device driver for UBI (Unsorted Block Images) that enables
> bootstd to scan UBI volumes for FIT images.
>
> The driver:
> - Has a hunt callback that auto-attaches UBI: walks the DT for the
> first partition with compatible = "linux,ubi", finds the matching
> MTD device, and calls ubi_part_from_mtd() to attach
> - Binds directly via bootdev_bind() as a child of the top-level MTD
> device, using a distinct name ("ubibootdev") to coexist with the
> MTD bootdev
> - Iterates UBI volumes using iter->part as an index
> - Reads the first bytes of each volume and checks for a valid FDT
> header via fdt_check_header()
> - Stores the UBI volume name in bflow->bootmeth_priv for the
> bootmeth's boot() to pass to image_loader_init_ubi()
> - Returns -ESHUTDOWN when all volumes are exhausted
>
> Like the MTD bootdev, calls bootmeth_check() first so that only
> compatible bootmeths produce bootflows. Inert until bootmeth_openwrt
> is extended to accept UBI bootdevs.
>
> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> ---
> boot/Kconfig | 9 +++
> boot/Makefile | 1 +
> boot/ubi_bootdev.c | 180 +++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 190 insertions(+)
> create mode 100644 boot/ubi_bootdev.c
>
> diff --git a/boot/Kconfig b/boot/Kconfig
> index 63e373cc62d..75d40744e69 100644
> --- a/boot/Kconfig
> +++ b/boot/Kconfig
> @@ -594,6 +594,15 @@ config BOOTDEV_MTD
> This scans MTD partitions for uImage.FIT firmware images,
> enabling raw-flash boot via the OpenWrt boot method.
>
> +config BOOTDEV_UBI
> + bool "UBI bootdev support"
> + depends on CMD_UBI
> + depends on BOOTSTD
> + help
> + Enable a boot device for UBI (Unsorted Block Images).
> + This scans UBI volumes for uImage.FIT firmware images,
> + enabling raw-flash boot via the OpenWrt boot method.
> +
> config BOOTMETH_OPENWRT
> bool "Bootdev support for OpenWrt"
> depends on FIT
> diff --git a/boot/Makefile b/boot/Makefile
> index feeed4924dd..aa7968c3932 100644
> --- a/boot/Makefile
> +++ b/boot/Makefile
> @@ -73,6 +73,7 @@ obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_SIMPLE_OS) += vbe_simple_os.o
>
> obj-$(CONFIG_$(PHASE_)BOOTMETH_ANDROID) += bootmeth_android.o
> obj-$(CONFIG_$(PHASE_)BOOTDEV_MTD) += mtd_bootdev.o
> +obj-$(CONFIG_$(PHASE_)BOOTDEV_UBI) += ubi_bootdev.o
> obj-$(CONFIG_$(PHASE_)BOOTMETH_OPENWRT) += bootmeth_openwrt.o
>
> obj-$(CONFIG_IMAGE_LOADER) += image-loader.o
> diff --git a/boot/ubi_bootdev.c b/boot/ubi_bootdev.c
> new file mode 100644
> index 00000000000..03131a4ee1b
> --- /dev/null
> +++ b/boot/ubi_bootdev.c
> @@ -0,0 +1,180 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * UBI boot device
> + *
> + * Copyright (C) 2026 Daniel Golle <daniel@makrotopia.org>
> + */
> +
> +#define LOG_CATEGORY UCLASS_BOOTSTD
> +
> +#include <bootdev.h>
> +#include <bootflow.h>
> +#include <bootmeth.h>
> +#include <dm.h>
> +#include <dm/ofnode.h>
> +#include <malloc.h>
> +#include <mtd.h>
> +#include <ubi_uboot.h>
> +#include <linux/libfdt.h>
> +
> +static int ubi_bootdev_get_bootflow(struct udevice *dev,
> + struct bootflow_iter *iter,
> + struct bootflow *bflow)
> +{
> + struct ubi_device *ubi;
> + struct ubi_volume *vol;
> + struct mtd_info *ubi_mtd, *top_mtd, *part;
> + char buf[40];
> + char dname[60];
> + int ubi_part_idx = 0;
> + int n = 0;
> + int i, ret;
> +
> + ret = bootmeth_check(bflow->method, iter);
> + if (ret)
> + return log_msg_ret("chk", ret);
> +
> + ubi = ubi_devices[0];
> + if (!ubi)
> + return log_msg_ret("ubi", -ENODEV);
> +
> + /* Count volumes so the scanning framework knows the bound */
> + for (i = 0; i < ubi->vtbl_slots; i++) {
> + if (ubi->volumes[i])
> + n++;
> + }
> + if (n)
> + iter->max_part = n - 1;
This can just be done once, when !iter->part, since it cannot change.
> +
> + n = 0;
> +
> + /* Walk to the iter->part'th UBI volume */
> + for (i = 0; i < ubi->vtbl_slots; i++) {
> + vol = ubi->volumes[i];
> + if (!vol)
> + continue;
> + if (n == iter->part)
> + goto found;
> + n++;
> + }
> + return -ESHUTDOWN;
> +
> +found:
The above code need tightening up a bit!
> + ret = ubi_volume_read(vol->name, buf, 0, sizeof(buf));
> + if (ret)
> + return log_msg_ret("rd", -EIO);
> +
> + if (fdt_check_header(buf))
> + return log_msg_ret("fdt", -ENOENT);
> +
> + /*
> + * Find the MTD partition index hosting UBI so we can display
> + * a meaningful partition number in the bootflow listing.
> + */
> + ubi_mtd = ubi->mtd;
> + top_mtd = ubi_mtd;
> + while (top_mtd->parent)
> + top_mtd = top_mtd->parent;
> +
> + n = 0;
> + list_for_each_entry(part, &top_mtd->partitions, node) {
> + if (part == ubi_mtd) {
> + ubi_part_idx = n;
> + break;
> + }
> + n++;
> + }
> +
> + /* Device-style name and partition index for bootflow list display */
> + snprintf(dname, sizeof(dname), "%s.part_%x", dev->name, ubi_part_idx);
> + bflow->name = strdup(dname);
> + bflow->part = ubi_part_idx;
> + bflow->fname = strdup(vol->name);
> + bflow->bootmeth_priv = strdup(vol->name);
> + bflow->state = BOOTFLOWST_MEDIA;
> +
> + return bootmeth_read_bootflow(bflow->method, bflow);
> +}
> +
> +static int ubi_bootdev_bind(struct udevice *dev)
> +{
> + struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev);
> +
> + ucp->prio = BOOTDEVP_4_SCAN_FAST;
> +
> + return 0;
> +}
> +
> +/**
> + * ubi_bootdev_hunt() - attach UBI and bind a bootdev for it
> + *
> + * Walk the DT for the first partition with compatible = "linux,ubi",
> + * find its MTD device, attach UBI via ubi_part_from_mtd(), then bind
> + * a ubi_bootdev as a child of the top-level MTD DM device.
> + */
> +static int ubi_bootdev_hunt(struct bootdev_hunter *info, bool show)
> +{
> + struct udevice *bdev;
> + struct mtd_info *mtd;
> + ofnode node;
> + int ret;
> +
> + mtd_probe_devices();
> +
> + if (!ubi_devices[0]) {
> + ofnode_for_each_compatible_node(node, "linux,ubi") {
> + mtd_for_each_device(mtd) {
> + if (ofnode_equal(mtd->flash_node, node))
> + goto found;
> + }
> + }
> + return -ENOENT;
Eek no we need to find the device, not the node. There must surely be
a link between the UBI and MTD in the data structures somewhere.
> +found:
> + ret = ubi_part_from_mtd(mtd);
> + if (ret)
> + return log_msg_ret("att", -ENOENT);
> + }
> +
> + /* Find the top-level MTD device for the attached UBI */
> + mtd = ubi_devices[0]->mtd;
> + while (mtd->parent)
> + mtd = mtd->parent;
> +
> + if (!mtd->dev)
> + return log_msg_ret("dev", -ENODEV);
> +
> + /*
> + * Bind directly — bootdev_setup_for_dev() cannot be used here
> + * because the MTD bootdev may already occupy the single bootdev
> + * child slot.
> + */
> + ret = bootdev_bind(mtd->dev, "ubi_bootdev", "ubibootdev", &bdev);
> + if (ret)
> + return log_msg_ret("bd", ret);
> +
> + return 0;
> +}
> +
> +struct bootdev_ops ubi_bootdev_ops = {
> + .get_bootflow = ubi_bootdev_get_bootflow,
> +};
> +
> +static const struct udevice_id ubi_bootdev_ids[] = {
> + { .compatible = "u-boot,bootdev-ubi" },
> + { }
> +};
> +
> +U_BOOT_DRIVER(ubi_bootdev) = {
> + .name = "ubi_bootdev",
> + .id = UCLASS_BOOTDEV,
> + .ops = &ubi_bootdev_ops,
> + .bind = ubi_bootdev_bind,
> + .of_match = ubi_bootdev_ids,
> +};
> +
> +BOOTDEV_HUNTER(ubi_bootdev_hunter) = {
> + .prio = BOOTDEVP_4_SCAN_FAST,
> + .uclass = UCLASS_MTD,
> + .drv = DM_DRIVER_REF(ubi_bootdev),
> + .hunt = ubi_bootdev_hunt,
> +};
> --
> 2.53.0
Regards,
Simon
^ permalink raw reply [flat|nested] 88+ messages in thread* Re: [RFC PATCH 17/20] boot: bootdev: add UBI boot device
2026-02-19 13:09 ` Simon Glass
@ 2026-02-19 16:56 ` Daniel Golle
2026-02-23 17:51 ` Simon Glass
0 siblings, 1 reply; 88+ messages in thread
From: Daniel Golle @ 2026-02-19 16:56 UTC (permalink / raw)
To: Simon Glass
Cc: Tom Rini, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
On Thu, Feb 19, 2026 at 06:09:31AM -0700, Simon Glass wrote:
> On Mon, 16 Feb 2026 at 14:24, Daniel Golle <daniel@makrotopia.org> wrote:
> > [...]
> > +/**
> > + * ubi_bootdev_hunt() - attach UBI and bind a bootdev for it
> > + *
> > + * Walk the DT for the first partition with compatible = "linux,ubi",
> > + * find its MTD device, attach UBI via ubi_part_from_mtd(), then bind
> > + * a ubi_bootdev as a child of the top-level MTD DM device.
> > + */
> > +static int ubi_bootdev_hunt(struct bootdev_hunter *info, bool show)
> > +{
> > + struct udevice *bdev;
> > + struct mtd_info *mtd;
> > + ofnode node;
> > + int ret;
> > +
> > + mtd_probe_devices();
> > +
> > + if (!ubi_devices[0]) {
> > + ofnode_for_each_compatible_node(node, "linux,ubi") {
> > + mtd_for_each_device(mtd) {
> > + if (ofnode_equal(mtd->flash_node, node))
> > + goto found;
> > + }
> > + }
> > + return -ENOENT;
>
> Eek no we need to find the device, not the node. There must surely be
> a link between the UBI and MTD in the data structures somewhere.
Yes, but only once UBI is already attached. We can move the responsibiltiy
to auto-attach the first suitable UBI volume having the 'linux, ubi'
compatible to the moment the MTD partition devices are created, so UBI
will always already be attached. However, that will then waste time to
do the UBI scanning even on devices not using UBI to boot from (but eg.
only as auxilary storage while booting from NOR flash). Hence I kinda
liked taking care of the UBI attachment in the hunter...
^ permalink raw reply [flat|nested] 88+ messages in thread* Re: [RFC PATCH 17/20] boot: bootdev: add UBI boot device
2026-02-19 16:56 ` Daniel Golle
@ 2026-02-23 17:51 ` Simon Glass
0 siblings, 0 replies; 88+ messages in thread
From: Simon Glass @ 2026-02-23 17:51 UTC (permalink / raw)
To: Daniel Golle
Cc: Tom Rini, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
Hi Daniel,
On Thu, 19 Feb 2026 at 09:57, Daniel Golle <daniel@makrotopia.org> wrote:
>
> On Thu, Feb 19, 2026 at 06:09:31AM -0700, Simon Glass wrote:
> > On Mon, 16 Feb 2026 at 14:24, Daniel Golle <daniel@makrotopia.org> wrote:
> > > [...]
> > > +/**
> > > + * ubi_bootdev_hunt() - attach UBI and bind a bootdev for it
> > > + *
> > > + * Walk the DT for the first partition with compatible = "linux,ubi",
> > > + * find its MTD device, attach UBI via ubi_part_from_mtd(), then bind
> > > + * a ubi_bootdev as a child of the top-level MTD DM device.
> > > + */
> > > +static int ubi_bootdev_hunt(struct bootdev_hunter *info, bool show)
> > > +{
> > > + struct udevice *bdev;
> > > + struct mtd_info *mtd;
> > > + ofnode node;
> > > + int ret;
> > > +
> > > + mtd_probe_devices();
> > > +
> > > + if (!ubi_devices[0]) {
> > > + ofnode_for_each_compatible_node(node, "linux,ubi") {
> > > + mtd_for_each_device(mtd) {
> > > + if (ofnode_equal(mtd->flash_node, node))
> > > + goto found;
> > > + }
> > > + }
> > > + return -ENOENT;
> >
> > Eek no we need to find the device, not the node. There must surely be
> > a link between the UBI and MTD in the data structures somewhere.
>
> Yes, but only once UBI is already attached. We can move the responsibiltiy
> to auto-attach the first suitable UBI volume having the 'linux, ubi'
> compatible to the moment the MTD partition devices are created, so UBI
> will always already be attached. However, that will then waste time to
> do the UBI scanning even on devices not using UBI to boot from (but eg.
> only as auxilary storage while booting from NOR flash). Hence I kinda
> liked taking care of the UBI attachment in the hunter...
OK...well from your other email it seems that UBI is pretty old code.
So I suppose we do need this sort of conversion.
I could perhaps take a look at converting UBI to driver model, but
that would be separate from this series and would have to come
afterwards.
Regards,
Simon
^ permalink raw reply [flat|nested] 88+ messages in thread
* [RFC PATCH 18/20] boot: bootmeth: openwrt: support MTD and UBI bootdevs
2026-02-16 21:21 [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading Daniel Golle
` (16 preceding siblings ...)
2026-02-16 21:24 ` [RFC PATCH 17/20] boot: bootdev: add UBI " Daniel Golle
@ 2026-02-16 21:24 ` Daniel Golle
2026-02-19 13:09 ` Simon Glass
2026-02-16 21:24 ` [RFC PATCH 19/20] boot: bootmeth: openwrt: add openwrt_boot_script hook for bootconf Daniel Golle
` (7 subsequent siblings)
25 siblings, 1 reply; 88+ messages in thread
From: Daniel Golle @ 2026-02-16 21:24 UTC (permalink / raw)
To: Tom Rini, Simon Glass, Quentin Schulz, Daniel Golle,
Kory Maincent, Mattijs Korpershoek, Martin Schwan, Anshul Dalal,
Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
牛 志宏, Marek Vasut, Heinrich Schuchardt,
Wolfgang Wallner, Frank Wunderlich, David Lechner,
Osama Abdelkader, Mikhail Kshevetskiy, Michael Trimarchi,
Miquel Raynal, Andrew Goodbody, Yegor Yefremov, Mike Looijmans,
Weijie Gao, Alexander Stein, Neil Armstrong, Mayuresh Chitale,
Paul HENRYS, u-boot
Cc: John Crispin, Paul Spooren
Extend bootmeth_openwrt's boot() to handle all three storage backends:
- Block devices: detected via bflow->blk, uses image_loader_init_blk()
(existing path, refactored into openwrt_boot_blk() helper)
- UBI volumes: detected by checking the bootdev driver name
("ubi_bootdev"), uses image_loader_init_ubi() with the volume name
from bflow->bootmeth_priv
- MTD partitions: fallback for non-block bootflows with a priv string,
uses image_loader_init_mtd() with the partition name
The check() function does not need changes: bootflow_iter_check_blk()
already accepts non-network bootdevs including UCLASS_MTD parents.
The rest of boot() remains identical across backends — bootm_init(),
optional bootconf config selection, bootm_run(), cleanup on error.
Each backend path is guarded by IS_ENABLED(CONFIG_BOOTDEV_*) to
avoid link errors when not configured.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
boot/bootmeth_openwrt.c | 29 +++++++++++++++++++++++------
1 file changed, 23 insertions(+), 6 deletions(-)
diff --git a/boot/bootmeth_openwrt.c b/boot/bootmeth_openwrt.c
index 82f39c17d7f..e1f47b58f61 100644
--- a/boot/bootmeth_openwrt.c
+++ b/boot/bootmeth_openwrt.c
@@ -73,21 +73,38 @@ static int openwrt_read_bootflow(struct udevice *dev, struct bootflow *bflow)
return 0;
}
-static int openwrt_boot(struct udevice *dev, struct bootflow *bflow)
+static int openwrt_boot_blk(struct bootflow *bflow,
+ struct image_loader *ldr)
{
struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
const char *ifname = blk_get_devtype(bflow->blk);
+ char dev_part_str[32];
+
+ snprintf(dev_part_str, sizeof(dev_part_str), "%d:%d",
+ desc->devnum, bflow->part);
+
+ return image_loader_init_blk(ldr, ifname, dev_part_str);
+}
+
+static int openwrt_boot(struct udevice *dev, struct bootflow *bflow)
+{
struct image_loader ldr = {};
struct bootm_info bmi;
- char dev_part_str[32];
char addr_img[64];
const char *conf;
int ret;
- snprintf(dev_part_str, sizeof(dev_part_str), "%d:%d",
- desc->devnum, bflow->part);
-
- ret = image_loader_init_blk(&ldr, ifname, dev_part_str);
+ if (bflow->blk) {
+ ret = openwrt_boot_blk(bflow, &ldr);
+ } else if (IS_ENABLED(CONFIG_BOOTDEV_UBI) &&
+ !strcmp(bflow->dev->driver->name, "ubi_bootdev")) {
+ ret = image_loader_init_ubi(&ldr, bflow->bootmeth_priv);
+ } else if (IS_ENABLED(CONFIG_BOOTDEV_MTD) &&
+ bflow->bootmeth_priv) {
+ ret = image_loader_init_mtd(&ldr, bflow->bootmeth_priv);
+ } else {
+ return log_msg_ret("typ", -ENODEV);
+ }
if (ret)
return log_msg_ret("ldr", ret);
--
2.53.0
^ permalink raw reply related [flat|nested] 88+ messages in thread* Re: [RFC PATCH 18/20] boot: bootmeth: openwrt: support MTD and UBI bootdevs
2026-02-16 21:24 ` [RFC PATCH 18/20] boot: bootmeth: openwrt: support MTD and UBI bootdevs Daniel Golle
@ 2026-02-19 13:09 ` Simon Glass
0 siblings, 0 replies; 88+ messages in thread
From: Simon Glass @ 2026-02-19 13:09 UTC (permalink / raw)
To: Daniel Golle
Cc: Tom Rini, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
Hi Daniel,
On Mon, 16 Feb 2026 at 14:24, Daniel Golle <daniel@makrotopia.org> wrote:
>
> Extend bootmeth_openwrt's boot() to handle all three storage backends:
>
> - Block devices: detected via bflow->blk, uses image_loader_init_blk()
> (existing path, refactored into openwrt_boot_blk() helper)
> - UBI volumes: detected by checking the bootdev driver name
> ("ubi_bootdev"), uses image_loader_init_ubi() with the volume name
> from bflow->bootmeth_priv
> - MTD partitions: fallback for non-block bootflows with a priv string,
> uses image_loader_init_mtd() with the partition name
>
> The check() function does not need changes: bootflow_iter_check_blk()
> already accepts non-network bootdevs including UCLASS_MTD parents.
>
> The rest of boot() remains identical across backends — bootm_init(),
> optional bootconf config selection, bootm_run(), cleanup on error.
> Each backend path is guarded by IS_ENABLED(CONFIG_BOOTDEV_*) to
> avoid link errors when not configured.
>
> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> ---
> boot/bootmeth_openwrt.c | 29 +++++++++++++++++++++++------
> 1 file changed, 23 insertions(+), 6 deletions(-)
>
> diff --git a/boot/bootmeth_openwrt.c b/boot/bootmeth_openwrt.c
> index 82f39c17d7f..e1f47b58f61 100644
> --- a/boot/bootmeth_openwrt.c
> +++ b/boot/bootmeth_openwrt.c
> @@ -73,21 +73,38 @@ static int openwrt_read_bootflow(struct udevice *dev, struct bootflow *bflow)
> return 0;
> }
>
> -static int openwrt_boot(struct udevice *dev, struct bootflow *bflow)
> +static int openwrt_boot_blk(struct bootflow *bflow,
> + struct image_loader *ldr)
> {
> struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
> const char *ifname = blk_get_devtype(bflow->blk);
> + char dev_part_str[32];
> +
> + snprintf(dev_part_str, sizeof(dev_part_str), "%d:%d",
> + desc->devnum, bflow->part);
> +
> + return image_loader_init_blk(ldr, ifname, dev_part_str);
> +}
> +
> +static int openwrt_boot(struct udevice *dev, struct bootflow *bflow)
> +{
> struct image_loader ldr = {};
> struct bootm_info bmi;
> - char dev_part_str[32];
> char addr_img[64];
> const char *conf;
> int ret;
>
> - snprintf(dev_part_str, sizeof(dev_part_str), "%d:%d",
> - desc->devnum, bflow->part);
> -
> - ret = image_loader_init_blk(&ldr, ifname, dev_part_str);
Something odd here, removing code added in a previous patch.
> + if (bflow->blk) {
> + ret = openwrt_boot_blk(bflow, &ldr);
> + } else if (IS_ENABLED(CONFIG_BOOTDEV_UBI) &&
> + !strcmp(bflow->dev->driver->name, "ubi_bootdev")) {
> + ret = image_loader_init_ubi(&ldr, bflow->bootmeth_priv);
> + } else if (IS_ENABLED(CONFIG_BOOTDEV_MTD) &&
> + bflow->bootmeth_priv) {
> + ret = image_loader_init_mtd(&ldr, bflow->bootmeth_priv);
> + } else {
> + return log_msg_ret("typ", -ENODEV);
> + }
With driver model I am hoping you can just probe your device and avoid
all of this boilerplate.
> if (ret)
> return log_msg_ret("ldr", ret);
>
> --
> 2.53.0
Regards,
Simon
^ permalink raw reply [flat|nested] 88+ messages in thread
* [RFC PATCH 19/20] boot: bootmeth: openwrt: add openwrt_boot_script hook for bootconf
2026-02-16 21:21 [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading Daniel Golle
` (17 preceding siblings ...)
2026-02-16 21:24 ` [RFC PATCH 18/20] boot: bootmeth: openwrt: support MTD and UBI bootdevs Daniel Golle
@ 2026-02-16 21:24 ` Daniel Golle
2026-02-19 13:11 ` Simon Glass
2026-02-16 21:24 ` [RFC PATCH 20/20] boot: bootmeth: openwrt: add slot configuration from environment Daniel Golle
` (6 subsequent siblings)
25 siblings, 1 reply; 88+ messages in thread
From: Daniel Golle @ 2026-02-16 21:24 UTC (permalink / raw)
To: Tom Rini, Simon Glass, Quentin Schulz, Daniel Golle,
Kory Maincent, Mattijs Korpershoek, Martin Schwan, Anshul Dalal,
Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
牛 志宏, Marek Vasut, Heinrich Schuchardt,
Wolfgang Wallner, Frank Wunderlich, David Lechner,
Osama Abdelkader, Mikhail Kshevetskiy, Michael Trimarchi,
Miquel Raynal, Andrew Goodbody, Yegor Yefremov, Mike Looijmans,
Weijie Gao, Alexander Stein, Neil Armstrong, Mayuresh Chitale,
Paul HENRYS, u-boot
Cc: John Crispin, Paul Spooren
Some boards need to probe hardware at boot time to determine the
correct FIT configuration. For example, the BPi-R3 checks whether
SPI-NAND or SPI-NOR is present and selects the matching FIT config
overlay accordingly.
Add an optional script hook: if the environment variable
openwrt_boot_script is set, its content is executed as a U-Boot
command string right before bootconf is read. This lets board
configurations assemble the correct bootconf value dynamically:
setenv openwrt_boot_script 'if nand info; then
setenv bootconf $bootconf_base#$bootconf_emmc#$bootconf_nand;
else
setenv bootconf $bootconf_base#$bootconf_emmc#$bootconf_nor;
fi'
The hook runs once per boot attempt, after the image loader is set up
but before bootm_run() is called.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
boot/bootmeth_openwrt.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/boot/bootmeth_openwrt.c b/boot/bootmeth_openwrt.c
index e1f47b58f61..d448697fe08 100644
--- a/boot/bootmeth_openwrt.c
+++ b/boot/bootmeth_openwrt.c
@@ -12,6 +12,7 @@
#include <bootflow.h>
#include <bootm.h>
#include <bootmeth.h>
+#include <command.h>
#include <dm.h>
#include <env.h>
#include <image.h>
@@ -113,6 +114,17 @@ static int openwrt_boot(struct udevice *dev, struct bootflow *bflow)
bootm_init(&bmi);
bmi.loader = &ldr;
+ /*
+ * Optional script hook: run the command in openwrt_boot_script
+ * before reading bootconf. Boards use this to probe hardware
+ * and assemble the correct FIT configuration string, e.g.:
+ *
+ * setenv openwrt_boot_script 'if nand info; then ...'
+ */
+ conf = env_get("openwrt_boot_script");
+ if (conf)
+ run_command(conf, 0);
+
/* FIT config selection via #conf suffix */
conf = env_get("bootconf");
if (conf) {
--
2.53.0
^ permalink raw reply related [flat|nested] 88+ messages in thread* Re: [RFC PATCH 19/20] boot: bootmeth: openwrt: add openwrt_boot_script hook for bootconf
2026-02-16 21:24 ` [RFC PATCH 19/20] boot: bootmeth: openwrt: add openwrt_boot_script hook for bootconf Daniel Golle
@ 2026-02-19 13:11 ` Simon Glass
0 siblings, 0 replies; 88+ messages in thread
From: Simon Glass @ 2026-02-19 13:11 UTC (permalink / raw)
To: Daniel Golle
Cc: Tom Rini, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
Hi Daniel,
On Mon, 16 Feb 2026 at 14:24, Daniel Golle <daniel@makrotopia.org> wrote:
>
> Some boards need to probe hardware at boot time to determine the
> correct FIT configuration. For example, the BPi-R3 checks whether
> SPI-NAND or SPI-NOR is present and selects the matching FIT config
> overlay accordingly.
>
> Add an optional script hook: if the environment variable
> openwrt_boot_script is set, its content is executed as a U-Boot
> command string right before bootconf is read. This lets board
> configurations assemble the correct bootconf value dynamically:
>
> setenv openwrt_boot_script 'if nand info; then
> setenv bootconf $bootconf_base#$bootconf_emmc#$bootconf_nand;
> else
> setenv bootconf $bootconf_base#$bootconf_emmc#$bootconf_nor;
> fi'
>
> The hook runs once per boot attempt, after the image loader is set up
> but before bootm_run() is called.
>
> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> ---
> boot/bootmeth_openwrt.c | 12 ++++++++++++
> 1 file changed, 12 insertions(+)
>
> diff --git a/boot/bootmeth_openwrt.c b/boot/bootmeth_openwrt.c
> index e1f47b58f61..d448697fe08 100644
> --- a/boot/bootmeth_openwrt.c
> +++ b/boot/bootmeth_openwrt.c
> @@ -12,6 +12,7 @@
> #include <bootflow.h>
> #include <bootm.h>
> #include <bootmeth.h>
> +#include <command.h>
> #include <dm.h>
> #include <env.h>
> #include <image.h>
> @@ -113,6 +114,17 @@ static int openwrt_boot(struct udevice *dev, struct bootflow *bflow)
> bootm_init(&bmi);
> bmi.loader = &ldr;
>
> + /*
> + * Optional script hook: run the command in openwrt_boot_script
> + * before reading bootconf. Boards use this to probe hardware
> + * and assemble the correct FIT configuration string, e.g.:
> + *
> + * setenv openwrt_boot_script 'if nand info; then ...'
> + */
> + conf = env_get("openwrt_boot_script");
> + if (conf)
> + run_command(conf, 0);
> +
> /* FIT config selection via #conf suffix */
> conf = env_get("bootconf");
> if (conf) {
> --
> 2.53.0
Down the track it would be nice to build this into bootstd too, as we
are trying to avoid scripts.
Regards,
Simon
^ permalink raw reply [flat|nested] 88+ messages in thread
* [RFC PATCH 20/20] boot: bootmeth: openwrt: add slot configuration from environment
2026-02-16 21:21 [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading Daniel Golle
` (18 preceding siblings ...)
2026-02-16 21:24 ` [RFC PATCH 19/20] boot: bootmeth: openwrt: add openwrt_boot_script hook for bootconf Daniel Golle
@ 2026-02-16 21:24 ` Daniel Golle
2026-02-19 13:11 ` Simon Glass
2026-02-16 22:16 ` [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading Marek Vasut
` (5 subsequent siblings)
25 siblings, 1 reply; 88+ messages in thread
From: Daniel Golle @ 2026-02-16 21:24 UTC (permalink / raw)
To: Tom Rini, Simon Glass, Quentin Schulz, Daniel Golle,
Kory Maincent, Mattijs Korpershoek, Martin Schwan, Anshul Dalal,
Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
牛 志宏, Marek Vasut, Heinrich Schuchardt,
Wolfgang Wallner, Frank Wunderlich, David Lechner,
Osama Abdelkader, Mikhail Kshevetskiy, Michael Trimarchi,
Miquel Raynal, Andrew Goodbody, Yegor Yefremov, Mike Looijmans,
Weijie Gao, Alexander Stein, Neil Armstrong, Mayuresh Chitale,
Paul HENRYS, u-boot
Cc: John Crispin, Paul Spooren
Add configurable multi-slot boot with slot names and boot order
defined via environment variables. This enables production/recovery
dual-boot (or any number of named slots).
New environment variables:
- openwrt_slot_<name>=<location> -- defines a named slot. <name> is
an arbitrary label (e.g. "production", "recovery"). <location> is
a GPT partition label, MTD partition name, or UBI volume name.
- openwrt_boot_order=<name1> <name2> ... -- space-separated list of
slot names that are eligible for booting. Only partitions/volumes
whose name matches a configured slot's location are probed.
Without openwrt_boot_order, all partitions/volumes are probed
(backward-compatible with the Milestone 1/2 behavior). When set,
only partitions matching a defined slot pass the filter.
The matched slot name is stored in bflow->os_name for later use by
boot-loop avoidance (Milestone 4) and boot menu display.
The MTD and UBI bootdevs now delegate to bootmeth_read_bootflow()
instead of setting BOOTFLOWST_READY directly, so that slot filtering
is applied uniformly across all storage backends.
Example configuration:
openwrt_slot_production=firmware
openwrt_slot_recovery=recovery
openwrt_boot_order=production recovery
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
boot/bootmeth_openwrt.c | 130 ++++++++++++++++++++++++++++++++--------
1 file changed, 104 insertions(+), 26 deletions(-)
diff --git a/boot/bootmeth_openwrt.c b/boot/bootmeth_openwrt.c
index d448697fe08..6e90a203ed6 100644
--- a/boot/bootmeth_openwrt.c
+++ b/boot/bootmeth_openwrt.c
@@ -24,6 +24,63 @@
#include <linux/libfdt.h>
#include <linux/sizes.h>
+/**
+ * openwrt_match_slot() - check if a partition name matches a configured slot
+ * @part_name: GPT label, MTD partition name, or UBI volume name
+ * @slot_namep: set to strdup'd slot name on match (caller must free)
+ *
+ * When ``openwrt_boot_order`` is set in the environment, only partitions
+ * whose name matches one of the ``openwrt_slot_<name>`` locations are
+ * accepted. Without ``openwrt_boot_order``, all partitions pass.
+ *
+ * Return: 0 if accepted, -ENOENT if filtered out
+ */
+static int openwrt_match_slot(const char *part_name, char **slot_namep)
+{
+ const char *order, *p;
+
+ *slot_namep = NULL;
+
+ order = env_get("openwrt_boot_order");
+ if (!order)
+ return 0;
+
+ if (!part_name)
+ return -ENOENT;
+
+ p = order;
+ while (*p) {
+ char name[64], var[80];
+ const char *location, *end;
+ int len;
+
+ while (*p == ' ')
+ p++;
+ if (!*p)
+ break;
+
+ end = p;
+ while (*end && *end != ' ')
+ end++;
+
+ len = end - p;
+ if (len >= sizeof(name))
+ len = sizeof(name) - 1;
+ memcpy(name, p, len);
+ name[len] = '\0';
+ p = end;
+
+ snprintf(var, sizeof(var), "openwrt_slot_%s", name);
+ location = env_get(var);
+ if (location && !strcmp(part_name, location)) {
+ *slot_namep = strdup(name);
+ return 0;
+ }
+ }
+
+ return -ENOENT;
+}
+
static int openwrt_check(struct udevice *dev, struct bootflow_iter *iter)
{
if (bootflow_iter_check_blk(iter))
@@ -34,40 +91,61 @@ static int openwrt_check(struct udevice *dev, struct bootflow_iter *iter)
static int openwrt_read_bootflow(struct udevice *dev, struct bootflow *bflow)
{
- struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
const char *part_name = NULL;
- struct disk_partition info;
- void *buf;
+ char *slot_name = NULL;
int ret;
- /* Get partition geometry */
- ret = part_get_info(desc, bflow->part, &info);
- if (ret)
- return log_msg_ret("part", ret);
-
- part_name = (const char *)info.name;
-
- /* Read first block to probe for an FDT/FIT header */
- buf = memalign(SZ_1K, desc->blksz);
- if (!buf)
- return log_msg_ret("mem", -ENOMEM);
+ if (bflow->blk) {
+ struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
+ struct disk_partition info;
+ void *buf;
+
+ ret = part_get_info(desc, bflow->part, &info);
+ if (ret)
+ return log_msg_ret("part", ret);
+
+ part_name = (const char *)info.name;
+
+ /* Check slot filter before expensive I/O */
+ ret = openwrt_match_slot(part_name, &slot_name);
+ if (ret)
+ return -ENOENT;
+
+ /* Read first block to probe for an FDT/FIT header */
+ buf = memalign(SZ_1K, desc->blksz);
+ if (!buf) {
+ free(slot_name);
+ return log_msg_ret("mem", -ENOMEM);
+ }
+
+ ret = blk_read(bflow->blk, info.start, 1, buf);
+ if (ret != 1) {
+ free(buf);
+ free(slot_name);
+ return log_msg_ret("rd", -EIO);
+ }
+
+ if (fdt_check_header(buf)) {
+ free(buf);
+ free(slot_name);
+ return -ENOENT;
+ }
- ret = blk_read(bflow->blk, info.start, 1, buf);
- if (ret != 1) {
free(buf);
- return log_msg_ret("rd", -EIO);
- }
- /* Must start with a valid FDT header */
- if (fdt_check_header(buf)) {
- free(buf);
- return -ENOENT;
- }
+ /* Show the GPT partition label as Filename */
+ bflow->fname = strdup(part_name);
+ } else {
+ /* MTD or UBI — partition/volume name in bootmeth_priv */
+ part_name = bflow->bootmeth_priv;
- free(buf);
+ ret = openwrt_match_slot(part_name, &slot_name);
+ if (ret)
+ return -ENOENT;
+ }
- /* Show the GPT partition label as Filename */
- bflow->fname = strdup(part_name);
+ if (slot_name)
+ bflow->os_name = slot_name;
bflow->state = BOOTFLOWST_READY;
--
2.53.0
^ permalink raw reply related [flat|nested] 88+ messages in thread* Re: [RFC PATCH 20/20] boot: bootmeth: openwrt: add slot configuration from environment
2026-02-16 21:24 ` [RFC PATCH 20/20] boot: bootmeth: openwrt: add slot configuration from environment Daniel Golle
@ 2026-02-19 13:11 ` Simon Glass
0 siblings, 0 replies; 88+ messages in thread
From: Simon Glass @ 2026-02-19 13:11 UTC (permalink / raw)
To: Daniel Golle
Cc: Tom Rini, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
Hi Daniel,
On Mon, 16 Feb 2026 at 14:24, Daniel Golle <daniel@makrotopia.org> wrote:
>
> Add configurable multi-slot boot with slot names and boot order
> defined via environment variables. This enables production/recovery
> dual-boot (or any number of named slots).
>
> New environment variables:
>
> - openwrt_slot_<name>=<location> -- defines a named slot. <name> is
> an arbitrary label (e.g. "production", "recovery"). <location> is
> a GPT partition label, MTD partition name, or UBI volume name.
>
> - openwrt_boot_order=<name1> <name2> ... -- space-separated list of
> slot names that are eligible for booting. Only partitions/volumes
> whose name matches a configured slot's location are probed.
>
> Without openwrt_boot_order, all partitions/volumes are probed
> (backward-compatible with the Milestone 1/2 behavior). When set,
> only partitions matching a defined slot pass the filter.
>
> The matched slot name is stored in bflow->os_name for later use by
> boot-loop avoidance (Milestone 4) and boot menu display.
>
> The MTD and UBI bootdevs now delegate to bootmeth_read_bootflow()
> instead of setting BOOTFLOWST_READY directly, so that slot filtering
> is applied uniformly across all storage backends.
>
> Example configuration:
>
> openwrt_slot_production=firmware
> openwrt_slot_recovery=recovery
> openwrt_boot_order=production recovery
>
> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> ---
> boot/bootmeth_openwrt.c | 130 ++++++++++++++++++++++++++++++++--------
> 1 file changed, 104 insertions(+), 26 deletions(-)
>
> diff --git a/boot/bootmeth_openwrt.c b/boot/bootmeth_openwrt.c
> index d448697fe08..6e90a203ed6 100644
> --- a/boot/bootmeth_openwrt.c
> +++ b/boot/bootmeth_openwrt.c
> @@ -24,6 +24,63 @@
> #include <linux/libfdt.h>
> #include <linux/sizes.h>
>
> +/**
> + * openwrt_match_slot() - check if a partition name matches a configured slot
> + * @part_name: GPT label, MTD partition name, or UBI volume name
> + * @slot_namep: set to strdup'd slot name on match (caller must free)
> + *
> + * When ``openwrt_boot_order`` is set in the environment, only partitions
> + * whose name matches one of the ``openwrt_slot_<name>`` locations are
> + * accepted. Without ``openwrt_boot_order``, all partitions pass.
> + *
> + * Return: 0 if accepted, -ENOENT if filtered out
> + */
> +static int openwrt_match_slot(const char *part_name, char **slot_namep)
> +{
> + const char *order, *p;
> +
> + *slot_namep = NULL;
> +
> + order = env_get("openwrt_boot_order");
> + if (!order)
> + return 0;
> +
> + if (!part_name)
> + return -ENOENT;
> +
> + p = order;
> + while (*p) {
> + char name[64], var[80];
> + const char *location, *end;
> + int len;
> +
> + while (*p == ' ')
> + p++;
> + if (!*p)
> + break;
> +
> + end = p;
> + while (*end && *end != ' ')
> + end++;
> +
> + len = end - p;
> + if (len >= sizeof(name))
> + len = sizeof(name) - 1;
> + memcpy(name, p, len);
> + name[len] = '\0';
> + p = end;
> +
> + snprintf(var, sizeof(var), "openwrt_slot_%s", name);
> + location = env_get(var);
> + if (location && !strcmp(part_name, location)) {
> + *slot_namep = strdup(name);
> + return 0;
> + }
> + }
> +
> + return -ENOENT;
> +}
> +
> static int openwrt_check(struct udevice *dev, struct bootflow_iter *iter)
> {
> if (bootflow_iter_check_blk(iter))
> @@ -34,40 +91,61 @@ static int openwrt_check(struct udevice *dev, struct bootflow_iter *iter)
>
> static int openwrt_read_bootflow(struct udevice *dev, struct bootflow *bflow)
> {
> - struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
> const char *part_name = NULL;
> - struct disk_partition info;
> - void *buf;
> + char *slot_name = NULL;
> int ret;
>
> - /* Get partition geometry */
> - ret = part_get_info(desc, bflow->part, &info);
> - if (ret)
> - return log_msg_ret("part", ret);
> -
> - part_name = (const char *)info.name;
> -
> - /* Read first block to probe for an FDT/FIT header */
> - buf = memalign(SZ_1K, desc->blksz);
> - if (!buf)
> - return log_msg_ret("mem", -ENOMEM);
> + if (bflow->blk) {
> + struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
> + struct disk_partition info;
> + void *buf;
> +
> + ret = part_get_info(desc, bflow->part, &info);
> + if (ret)
> + return log_msg_ret("part", ret);
> +
> + part_name = (const char *)info.name;
> +
> + /* Check slot filter before expensive I/O */
> + ret = openwrt_match_slot(part_name, &slot_name);
> + if (ret)
> + return -ENOENT;
> +
> + /* Read first block to probe for an FDT/FIT header */
> + buf = memalign(SZ_1K, desc->blksz);
> + if (!buf) {
> + free(slot_name);
> + return log_msg_ret("mem", -ENOMEM);
> + }
> +
> + ret = blk_read(bflow->blk, info.start, 1, buf);
> + if (ret != 1) {
> + free(buf);
> + free(slot_name);
> + return log_msg_ret("rd", -EIO);
> + }
> +
> + if (fdt_check_header(buf)) {
> + free(buf);
> + free(slot_name);
> + return -ENOENT;
> + }
The code above would benefit from being in its own function.
>
> - ret = blk_read(bflow->blk, info.start, 1, buf);
> - if (ret != 1) {
> free(buf);
> - return log_msg_ret("rd", -EIO);
> - }
>
> - /* Must start with a valid FDT header */
> - if (fdt_check_header(buf)) {
> - free(buf);
> - return -ENOENT;
> - }
> + /* Show the GPT partition label as Filename */
> + bflow->fname = strdup(part_name);
> + } else {
> + /* MTD or UBI — partition/volume name in bootmeth_priv */
> + part_name = bflow->bootmeth_priv;
>
> - free(buf);
> + ret = openwrt_match_slot(part_name, &slot_name);
> + if (ret)
> + return -ENOENT;
> + }
>
> - /* Show the GPT partition label as Filename */
> - bflow->fname = strdup(part_name);
> + if (slot_name)
> + bflow->os_name = slot_name;
>
> bflow->state = BOOTFLOWST_READY;
>
> --
> 2.53.0
Regards,
SIMon
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading
2026-02-16 21:21 [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading Daniel Golle
` (19 preceding siblings ...)
2026-02-16 21:24 ` [RFC PATCH 20/20] boot: bootmeth: openwrt: add slot configuration from environment Daniel Golle
@ 2026-02-16 22:16 ` Marek Vasut
2026-02-17 1:18 ` Daniel Golle
2026-02-17 13:32 ` Simon Glass
` (4 subsequent siblings)
25 siblings, 1 reply; 88+ messages in thread
From: Marek Vasut @ 2026-02-16 22:16 UTC (permalink / raw)
To: Daniel Golle, Tom Rini, Simon Glass, Quentin Schulz,
Kory Maincent, Mattijs Korpershoek, Martin Schwan, Anshul Dalal,
Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
牛 志宏, Heinrich Schuchardt, Wolfgang Wallner,
Frank Wunderlich, David Lechner, Osama Abdelkader,
Mikhail Kshevetskiy, Michael Trimarchi, Miquel Raynal,
Andrew Goodbody, Yegor Yefremov, Mike Looijmans, Weijie Gao,
Alexander Stein, Neil Armstrong, Mayuresh Chitale, Paul HENRYS,
u-boot
Cc: John Crispin, Paul Spooren
On 2/16/26 10:21 PM, Daniel Golle wrote:
Hi,
> This RFC series adds a new boot method for OpenWrt's "uImage.FIT with
> embedded rootfs" firmware model, along with the underlying infrastructure
> to load FIT images on-demand directly from storage devices without copying
> them entirely to RAM first.
>
> I would like to discuss the design with U-Boot maintainers and fellow
> OpenWrt developers before submitting a formal patch series.
[...]
> 4. On-demand loading: None of the existing methods support loading FIT
> subimages directly from storage. OpenWrt's FIT images typically
> contain a 5-20 MB squashfs that does NOT need to be copied to RAM —
> the kernel maps it directly from flash. The bootloader only needs
> to load the kernel and DTB (~5-10 MB), not the entire 20-50 MB
> container. This requires a new loading abstraction.
Isn't this partial loading exactly what SPL does when the fitImage is
generated with external data (mkimage -E) ? SPL loads and traverses the
tree, and then loads the remaining chunks (files) only when needed if I
recall it right ?
Can that SPL code be reused instead ?
^ permalink raw reply [flat|nested] 88+ messages in thread* Re: [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading
2026-02-16 22:16 ` [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading Marek Vasut
@ 2026-02-17 1:18 ` Daniel Golle
2026-02-17 2:04 ` Marek Vasut
0 siblings, 1 reply; 88+ messages in thread
From: Daniel Golle @ 2026-02-17 1:18 UTC (permalink / raw)
To: Marek Vasut
Cc: Tom Rini, Simon Glass, Quentin Schulz, Kory Maincent,
Mattijs Korpershoek, Martin Schwan, Anshul Dalal,
Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
牛 志宏, Heinrich Schuchardt, Wolfgang Wallner,
Frank Wunderlich, David Lechner, Osama Abdelkader,
Mikhail Kshevetskiy, Michael Trimarchi, Miquel Raynal,
Andrew Goodbody, Yegor Yefremov, Mike Looijmans, Weijie Gao,
Alexander Stein, Neil Armstrong, Mayuresh Chitale, Paul HENRYS,
u-boot, John Crispin, Paul Spooren
Hi Marek,
thanks for taking a look at the series!
Let me reply inline below:
On Mon, Feb 16, 2026 at 11:16:24PM +0100, Marek Vasut wrote:
> On 2/16/26 10:21 PM, Daniel Golle wrote:
>
> Hi,
>
> > This RFC series adds a new boot method for OpenWrt's "uImage.FIT with
> > embedded rootfs" firmware model, along with the underlying infrastructure
> > to load FIT images on-demand directly from storage devices without copying
> > them entirely to RAM first.
> >
> > I would like to discuss the design with U-Boot maintainers and fellow
> > OpenWrt developers before submitting a formal patch series.
>
> [...]
>
> > 4. On-demand loading: None of the existing methods support loading FIT
> > subimages directly from storage. OpenWrt's FIT images typically
> > contain a 5-20 MB squashfs that does NOT need to be copied to RAM —
> > the kernel maps it directly from flash. The bootloader only needs
> > to load the kernel and DTB (~5-10 MB), not the entire 20-50 MB
> > container. This requires a new loading abstraction.
>
> Isn't this partial loading exactly what SPL does when the fitImage is
> generated with external data (mkimage -E) ? SPL loads and traverses the
> tree, and then loads the remaining chunks (files) only when needed if I
> recall it right ?
Yes, the image_loader abstraction in this series is essentially the
main-U-Boot equivalent of SPL's spl_load_info.read(), adapted for the
richer set of storage backends, byte-addressed, providing an interface
for both "load this to where ever" and "load this to a specific target
address" (image_loader_map() vs. image_loader_map_to()), and the full
fit_image_load() verification pipeline. The integration point in
fit_image_load() (patch 09/20) is ~50 lines of new code gated by if
(images->loader && external_data) - it reuses all existing FIT property
parsing, load address negotiation, and hash verification unchanged.
That said, the image_loader abstraction itself is format-agnostic - it
only deals with byte offsets, lengths, and RAM destinations. The same
three storage backends could be wired into other executable formats with
minimal effort, such as ELF, legacy uImage or UEFI PE.
Likewise, adding a backend based on fs_read() would be trivial,
extending U-Boot's wget to support range requests and using it as
image_loader backend would not be hard either.
> Can that SPL code be reused instead ?
I considered factoring out a shared "FIT external data reader" between
SPL and U-Boot proper, but the two callers want fundamentally different
things: SPL wants minimal code size and populates spl_image_info; U-Boot
proper wants full verification and populates bootm_headers. The shared
part wouldn't be much more than just the two fdt_getprop_u32() calls to
read data-offset and data-size, which didn't seem worth an abstraction
layer.
- SPL doesn't track what's already in RAM. If it needs the same data
twice (e.g. re-reading the FDT after discovering its full size), it
reads from storage again.
- No caching - no equivalent of image_loader_lookup() that returns a
previously-loaded region.
- No allocation tracking - no alloc_ptr bump allocator. SPL code
manually manages destination addresses.
- No map() / map_to() distinction - SPL always provides an explicit
destination buffer. There's no "load this somewhere, I don't care where"
semantic.
- No cleanup - no resource lifetime management, no refcounting of
underlying devices.
Anyway, I'm happy to reconsider if there's a specific shared interface
you have in mind, or if you believe it's worth (or even possible due
to size constraints) to extend SPL's loader code.
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading
2026-02-17 1:18 ` Daniel Golle
@ 2026-02-17 2:04 ` Marek Vasut
2026-02-17 13:02 ` Daniel Golle
0 siblings, 1 reply; 88+ messages in thread
From: Marek Vasut @ 2026-02-17 2:04 UTC (permalink / raw)
To: Daniel Golle
Cc: Tom Rini, Simon Glass, Quentin Schulz, Kory Maincent,
Mattijs Korpershoek, Martin Schwan, Anshul Dalal,
Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
牛 志宏, Heinrich Schuchardt, Wolfgang Wallner,
Frank Wunderlich, David Lechner, Osama Abdelkader,
Mikhail Kshevetskiy, Michael Trimarchi, Miquel Raynal,
Andrew Goodbody, Yegor Yefremov, Mike Looijmans, Weijie Gao,
Alexander Stein, Neil Armstrong, Mayuresh Chitale, Paul HENRYS,
u-boot, John Crispin, Paul Spooren
On 2/17/26 2:18 AM, Daniel Golle wrote:
> Hi Marek,
>
> thanks for taking a look at the series!
>
> Let me reply inline below:
>
> On Mon, Feb 16, 2026 at 11:16:24PM +0100, Marek Vasut wrote:
>> On 2/16/26 10:21 PM, Daniel Golle wrote:
>>
>> Hi,
>>
>>> This RFC series adds a new boot method for OpenWrt's "uImage.FIT with
>>> embedded rootfs" firmware model, along with the underlying infrastructure
>>> to load FIT images on-demand directly from storage devices without copying
>>> them entirely to RAM first.
>>>
>>> I would like to discuss the design with U-Boot maintainers and fellow
>>> OpenWrt developers before submitting a formal patch series.
>>
>> [...]
>>
>>> 4. On-demand loading: None of the existing methods support loading FIT
>>> subimages directly from storage. OpenWrt's FIT images typically
>>> contain a 5-20 MB squashfs that does NOT need to be copied to RAM —
>>> the kernel maps it directly from flash. The bootloader only needs
>>> to load the kernel and DTB (~5-10 MB), not the entire 20-50 MB
>>> container. This requires a new loading abstraction.
>>
>> Isn't this partial loading exactly what SPL does when the fitImage is
>> generated with external data (mkimage -E) ? SPL loads and traverses the
>> tree, and then loads the remaining chunks (files) only when needed if I
>> recall it right ?
>
> Yes, the image_loader abstraction in this series is essentially the
> main-U-Boot equivalent of SPL's spl_load_info.read(), adapted for the
> richer set of storage backends, byte-addressed, providing an interface
> for both "load this to where ever" and "load this to a specific target
> address" (image_loader_map() vs. image_loader_map_to()), and the full
> fit_image_load() verification pipeline. The integration point in
> fit_image_load() (patch 09/20) is ~50 lines of new code gated by if
> (images->loader && external_data) - it reuses all existing FIT property
> parsing, load address negotiation, and hash verification unchanged.
>
> That said, the image_loader abstraction itself is format-agnostic - it
> only deals with byte offsets, lengths, and RAM destinations. The same
> three storage backends could be wired into other executable formats with
> minimal effort, such as ELF, legacy uImage or UEFI PE.
>
> Likewise, adding a backend based on fs_read() would be trivial,
> extending U-Boot's wget to support range requests and using it as
> image_loader backend would not be hard either.
>
>> Can that SPL code be reused instead ?
>
> I considered factoring out a shared "FIT external data reader" between
> SPL and U-Boot proper, but the two callers want fundamentally different
> things: SPL wants minimal code size and populates spl_image_info; U-Boot
> proper wants full verification and populates bootm_headers.
I suspect the feature set of each loading stage can be configured e.g.
using "if (IS_ENABLED(...))" to keep the size under control ?
I would be very happy if we could have ONE consistent code base used for
loading in all of TPL/SPL/U-Boot . Custom special loader in U-Boot and
different special loader in SPL and so on, will only lead to
inconsistency and increased maintenance burden. Worse, it will lead to
obscure bugs which will differ between U-Boot and SPL, or bugs being
fixed in one and not the other.
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading
2026-02-17 2:04 ` Marek Vasut
@ 2026-02-17 13:02 ` Daniel Golle
2026-02-17 19:15 ` Tom Rini
0 siblings, 1 reply; 88+ messages in thread
From: Daniel Golle @ 2026-02-17 13:02 UTC (permalink / raw)
To: Marek Vasut
Cc: Tom Rini, Simon Glass, Quentin Schulz, Kory Maincent,
Mattijs Korpershoek, Martin Schwan, Anshul Dalal,
Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
牛 志宏, Heinrich Schuchardt, Wolfgang Wallner,
Frank Wunderlich, David Lechner, Osama Abdelkader,
Mikhail Kshevetskiy, Michael Trimarchi, Miquel Raynal,
Andrew Goodbody, Yegor Yefremov, Mike Looijmans, Weijie Gao,
Alexander Stein, Neil Armstrong, Mayuresh Chitale, Paul HENRYS,
u-boot, John Crispin, Paul Spooren
On Tue, Feb 17, 2026 at 03:04:09AM +0100, Marek Vasut wrote:
> On 2/17/26 2:18 AM, Daniel Golle wrote:
> > Hi Marek,
> >
> > thanks for taking a look at the series!
> >
> > Let me reply inline below:
> >
> > On Mon, Feb 16, 2026 at 11:16:24PM +0100, Marek Vasut wrote:
> > > On 2/16/26 10:21 PM, Daniel Golle wrote:
> > >
> > > Hi,
> > >
> > > > This RFC series adds a new boot method for OpenWrt's "uImage.FIT with
> > > > embedded rootfs" firmware model, along with the underlying infrastructure
> > > > to load FIT images on-demand directly from storage devices without copying
> > > > them entirely to RAM first.
> > > >
> > > > I would like to discuss the design with U-Boot maintainers and fellow
> > > > OpenWrt developers before submitting a formal patch series.
> > >
> > > [...]
> > >
> > > > 4. On-demand loading: None of the existing methods support loading FIT
> > > > subimages directly from storage. OpenWrt's FIT images typically
> > > > contain a 5-20 MB squashfs that does NOT need to be copied to RAM —
> > > > the kernel maps it directly from flash. The bootloader only needs
> > > > to load the kernel and DTB (~5-10 MB), not the entire 20-50 MB
> > > > container. This requires a new loading abstraction.
> > >
> > > Isn't this partial loading exactly what SPL does when the fitImage is
> > > generated with external data (mkimage -E) ? SPL loads and traverses the
> > > tree, and then loads the remaining chunks (files) only when needed if I
> > > recall it right ?
> >
> > Yes, the image_loader abstraction in this series is essentially the
> > main-U-Boot equivalent of SPL's spl_load_info.read(), adapted for the
> > richer set of storage backends, byte-addressed, providing an interface
> > for both "load this to where ever" and "load this to a specific target
> > address" (image_loader_map() vs. image_loader_map_to()), and the full
> > fit_image_load() verification pipeline. The integration point in
> > fit_image_load() (patch 09/20) is ~50 lines of new code gated by if
> > (images->loader && external_data) - it reuses all existing FIT property
> > parsing, load address negotiation, and hash verification unchanged.
> >
> > That said, the image_loader abstraction itself is format-agnostic - it
> > only deals with byte offsets, lengths, and RAM destinations. The same
> > three storage backends could be wired into other executable formats with
> > minimal effort, such as ELF, legacy uImage or UEFI PE.
> >
> > Likewise, adding a backend based on fs_read() would be trivial,
> > extending U-Boot's wget to support range requests and using it as
> > image_loader backend would not be hard either.
> >
> > > Can that SPL code be reused instead ?
> >
> > I considered factoring out a shared "FIT external data reader" between
> > SPL and U-Boot proper, but the two callers want fundamentally different
> > things: SPL wants minimal code size and populates spl_image_info; U-Boot
> > proper wants full verification and populates bootm_headers.
>
> I suspect the feature set of each loading stage can be configured e.g. using
> "if (IS_ENABLED(...))" to keep the size under control ?
>
> I would be very happy if we could have ONE consistent code base used for
> loading in all of TPL/SPL/U-Boot . Custom special loader in U-Boot and
> different special loader in SPL and so on, will only lead to inconsistency
> and increased maintenance burden. Worse, it will lead to obscure bugs which
> will differ between U-Boot and SPL, or bugs being fixed in one and not the
> other.
This implies changing the parameter "ulong sector" to a byte offset,
which means all existing readers and users will have to be changed as
well, and also code to deal with unaligned start offsets will have to be
added. Other than that it's mostly a matter of adding some #ifdef-ery,
conditionally extending struct spl_load_info with
- a cleanup callback function pointer
- alloc_ptr for bump allocator
- the array of already mapped regions
None of that is used by the readers, so the low-level spl_mmc reader
could be reused and extended -- but it's already very messy with many
hacks and special cases, it's own device and partition hunters, ...
and would basically need adding almost all of the code which is currently
in image-loader-blk.c (to handle the unaligned start offset, ...)
The other SPL readers are even more unsuitable:
- The SPI-NOR and NOR readers lack support for MTD partitions, instead
they are using a downstream DT property to specify *one* static offset
('u-boot,spl-payload-offset')
- The UBI reader deals only with static UBI volumes, and support for UBI
in SPL is a light-weight minimal thing incompatible with the full UBI
implementation in U-Boot proper.
- The FIT reader in SPL uses a very different approach from the
image_loader approach I suggested. It is basically a minimal
implementation of a FIT parser, while I'm suggesting to glue
support for the image_loader into boot/image-fit.c of U-Boot proper,
hence keeping and reusing all of its features.
See patch "boot: fit: support on-demand loading in fit_image_load()".
So there really isn't much overlap other than the fact that there is a
struct with a priv pointer and a .read callback, and even that uses a
different addressing parameter (sectors vs. bytes).
Imho, trying to unite the two "with a hammer" will do more harm than good.
^ permalink raw reply [flat|nested] 88+ messages in thread* Re: [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading
2026-02-17 13:02 ` Daniel Golle
@ 2026-02-17 19:15 ` Tom Rini
0 siblings, 0 replies; 88+ messages in thread
From: Tom Rini @ 2026-02-17 19:15 UTC (permalink / raw)
To: Daniel Golle
Cc: Marek Vasut, Simon Glass, Quentin Schulz, Kory Maincent,
Mattijs Korpershoek, Martin Schwan, Anshul Dalal,
Ilias Apalodimas, Sughosh Ganu, Aristo Chen,
牛 志宏, Heinrich Schuchardt, Wolfgang Wallner,
Frank Wunderlich, David Lechner, Osama Abdelkader,
Mikhail Kshevetskiy, Michael Trimarchi, Miquel Raynal,
Andrew Goodbody, Yegor Yefremov, Mike Looijmans, Weijie Gao,
Alexander Stein, Neil Armstrong, Mayuresh Chitale, Paul HENRYS,
u-boot, John Crispin, Paul Spooren
[-- Attachment #1: Type: text/plain, Size: 6936 bytes --]
On Tue, Feb 17, 2026 at 01:02:12PM +0000, Daniel Golle wrote:
> On Tue, Feb 17, 2026 at 03:04:09AM +0100, Marek Vasut wrote:
> > On 2/17/26 2:18 AM, Daniel Golle wrote:
> > > Hi Marek,
> > >
> > > thanks for taking a look at the series!
> > >
> > > Let me reply inline below:
> > >
> > > On Mon, Feb 16, 2026 at 11:16:24PM +0100, Marek Vasut wrote:
> > > > On 2/16/26 10:21 PM, Daniel Golle wrote:
> > > >
> > > > Hi,
> > > >
> > > > > This RFC series adds a new boot method for OpenWrt's "uImage.FIT with
> > > > > embedded rootfs" firmware model, along with the underlying infrastructure
> > > > > to load FIT images on-demand directly from storage devices without copying
> > > > > them entirely to RAM first.
> > > > >
> > > > > I would like to discuss the design with U-Boot maintainers and fellow
> > > > > OpenWrt developers before submitting a formal patch series.
> > > >
> > > > [...]
> > > >
> > > > > 4. On-demand loading: None of the existing methods support loading FIT
> > > > > subimages directly from storage. OpenWrt's FIT images typically
> > > > > contain a 5-20 MB squashfs that does NOT need to be copied to RAM —
> > > > > the kernel maps it directly from flash. The bootloader only needs
> > > > > to load the kernel and DTB (~5-10 MB), not the entire 20-50 MB
> > > > > container. This requires a new loading abstraction.
> > > >
> > > > Isn't this partial loading exactly what SPL does when the fitImage is
> > > > generated with external data (mkimage -E) ? SPL loads and traverses the
> > > > tree, and then loads the remaining chunks (files) only when needed if I
> > > > recall it right ?
> > >
> > > Yes, the image_loader abstraction in this series is essentially the
> > > main-U-Boot equivalent of SPL's spl_load_info.read(), adapted for the
> > > richer set of storage backends, byte-addressed, providing an interface
> > > for both "load this to where ever" and "load this to a specific target
> > > address" (image_loader_map() vs. image_loader_map_to()), and the full
> > > fit_image_load() verification pipeline. The integration point in
> > > fit_image_load() (patch 09/20) is ~50 lines of new code gated by if
> > > (images->loader && external_data) - it reuses all existing FIT property
> > > parsing, load address negotiation, and hash verification unchanged.
> > >
> > > That said, the image_loader abstraction itself is format-agnostic - it
> > > only deals with byte offsets, lengths, and RAM destinations. The same
> > > three storage backends could be wired into other executable formats with
> > > minimal effort, such as ELF, legacy uImage or UEFI PE.
> > >
> > > Likewise, adding a backend based on fs_read() would be trivial,
> > > extending U-Boot's wget to support range requests and using it as
> > > image_loader backend would not be hard either.
> > >
> > > > Can that SPL code be reused instead ?
> > >
> > > I considered factoring out a shared "FIT external data reader" between
> > > SPL and U-Boot proper, but the two callers want fundamentally different
> > > things: SPL wants minimal code size and populates spl_image_info; U-Boot
> > > proper wants full verification and populates bootm_headers.
> >
> > I suspect the feature set of each loading stage can be configured e.g. using
> > "if (IS_ENABLED(...))" to keep the size under control ?
> >
> > I would be very happy if we could have ONE consistent code base used for
> > loading in all of TPL/SPL/U-Boot . Custom special loader in U-Boot and
> > different special loader in SPL and so on, will only lead to inconsistency
> > and increased maintenance burden. Worse, it will lead to obscure bugs which
> > will differ between U-Boot and SPL, or bugs being fixed in one and not the
> > other.
>
> This implies changing the parameter "ulong sector" to a byte offset,
> which means all existing readers and users will have to be changed as
> well, and also code to deal with unaligned start offsets will have to be
> added. Other than that it's mostly a matter of adding some #ifdef-ery,
> conditionally extending struct spl_load_info with
> - a cleanup callback function pointer
> - alloc_ptr for bump allocator
> - the array of already mapped regions
>
> None of that is used by the readers, so the low-level spl_mmc reader
> could be reused and extended -- but it's already very messy with many
> hacks and special cases, it's own device and partition hunters, ...
> and would basically need adding almost all of the code which is currently
> in image-loader-blk.c (to handle the unaligned start offset, ...)
>
> The other SPL readers are even more unsuitable:
>
> - The SPI-NOR and NOR readers lack support for MTD partitions, instead
> they are using a downstream DT property to specify *one* static offset
> ('u-boot,spl-payload-offset')
>
> - The UBI reader deals only with static UBI volumes, and support for UBI
> in SPL is a light-weight minimal thing incompatible with the full UBI
> implementation in U-Boot proper.
>
> - The FIT reader in SPL uses a very different approach from the
> image_loader approach I suggested. It is basically a minimal
> implementation of a FIT parser, while I'm suggesting to glue
> support for the image_loader into boot/image-fit.c of U-Boot proper,
> hence keeping and reusing all of its features.
> See patch "boot: fit: support on-demand loading in fit_image_load()".
>
> So there really isn't much overlap other than the fact that there is a
> struct with a priv pointer and a .read callback, and even that uses a
> different addressing parameter (sectors vs. bytes).
>
> Imho, trying to unite the two "with a hammer" will do more harm than good.
So, we have some conflicting problems and solutions here, in general.
Outside of this thread, we have:
https://lore.kernel.org/u-boot/20260217112156.272154-1-a-dutta@ti.com/
with the biggest feedback being to look at:
https://lore.kernel.org/u-boot/2b5ebaa9-76f1-e2b0-4e8c-fc8b6c197136@gmail.com/T/#m4f33f294f050574baafef1cbfaa7e88a2a47eb5c
Because yes, we need better support in SPL for more fully-featured flash
access (on platforms with the initial memory space to support it).
There's also as you note earlier, a reasonable abstraction for reading
from a filesystem, but there's not an equivalent for reading from a
block device directly. Something like CMD_FS_GENERIC (and another
symbol if useful just for the library portion of it) is what the image
loading section seems to do, and feels entirely reasonable. That being
its own series would be something that can be reviewed on its own, too.
And from there, we can see if that can be leveraged to work with SPL
too, as there's both not a generic solution within SPL today nor a
generic solution (for both raw and filesystems) outside of SPL.
--
Tom
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading
2026-02-16 21:21 [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading Daniel Golle
` (20 preceding siblings ...)
2026-02-16 22:16 ` [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading Marek Vasut
@ 2026-02-17 13:32 ` Simon Glass
2026-02-17 15:08 ` Tom Rini
` (3 subsequent siblings)
25 siblings, 0 replies; 88+ messages in thread
From: Simon Glass @ 2026-02-17 13:32 UTC (permalink / raw)
To: Daniel Golle
Cc: Tom Rini, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
Hi Daniel,
On Mon, 16 Feb 2026 at 14:21, Daniel Golle <daniel@makrotopia.org> wrote:
>
> Hi all,
>
> This RFC series adds a new boot method for OpenWrt's "uImage.FIT with
> embedded rootfs" firmware model, along with the underlying infrastructure
> to load FIT images on-demand directly from storage devices without copying
> them entirely to RAM first.
>
> I would like to discuss the design with U-Boot maintainers and fellow
> OpenWrt developers before submitting a formal patch series.
>
> Background: Why OpenWrt needs its own boot method
> ==================================================
>
> OpenWrt's modern embedded boot model uses a single uImage.FIT container
> that includes the Linux kernel, device tree, and a read-only root
> filesystem (squashfs or more recently erofs).
>
> At boot, the kernel maps the embedded squashfs directly from flash as a
> block device (/dev/fit0 via the fitblk driver[1]). No separate boot
> partition, boot filesystem, or initrd is required.
>
> This creates a monolithic, deterministic, flash-native firmware image that
> is fundamentally different from what U-Boot's existing boot methods
> (distroboot, EFI, extlinux) are designed for. Those methods assume:
>
> - A partition table with a boot filesystem (FAT ESP, ext4 /boot)
> - Kernel and initrd as separate files on that filesystem
> - Mutable boot state managed by filesystem metadata
> - Multi-OS flexibility on replaceable storage
>
> OpenWrt's model assumes:
>
> - A single-flash embedded appliance with no removable storage
> - One immutable firmware blob per slot
> - No boot filesystem at all
> - Boot-medium agnostic deployment (the same .itb image works on eMMC,
> SD, SPI-NOR, SPI-NAND/UBI, and raw NAND)
>
> [1]: https://github.com/openwrt/openwrt/blob/30ac12f4b4682207c5b0501b3ffc50d56f58b690/target/linux/generic/pending-6.12/510-block-add-uImage.FIT-subimage-block-driver.patch
>
> One image for all use cases
> ===========================
>
> The same .itb image can be:
>
> - Used as a sysupgrade image inside a running OpenWrt system
> - Flashed from U-Boot via TFTP or HTTP
> - Written to an eMMC or SD partition via dd
> - Stored in a UBI volume (SPI-NAND, raw NAND)
> - Stored in a raw MTD partition (SPI-NOR, parallel NOR)
>
> It is entirely boot-medium agnostic. UEFI and distroboot by contrast
> assume a block-oriented storage device with a specific partition layout
> and boot filesystem, and cannot easily be used on raw flash devices or
> UBI.
>
> Why boot filesystems are a problem for embedded
> ===============================================
>
> Boot filesystems (FAT ESPs, ext4 /boot) introduce:
>
> - Metadata corruption risks on power loss
> - fsck requirements after unclean shutdown
> - State drift across firmware upgrades
> - A structural single point of failure
>
> These are not theoretical concerns. OpenWrt is deployed in telecom
> infrastructure, industrial control systems, and ISP CPE devices —
> environments where thousands (sometimes millions) of devices must boot
> and receive software updates reliably for years without physical
> access. A corrupted FAT partition on a rooftop wireless backhaul
> device can mean a truck roll, in a private home often means scheduling
> a costly visit by the ISPs service team.
>
> The FIT-with-rootfs model eliminates this entire failure class:
>
> - No filesystem metadata to corrupt
> - No fsck needed after power loss
> - No mutable boot artifacts
> - Bit-identical system partitions across the entire fleet
>
> This is critical for QA reproducibility, telecom deployments, and any
> fleet where deterministic firmware state is a hard requirement.
>
> Reduced failure surface
> =======================
>
> A typical UEFI / distroboot path:
>
> Bootloader -> Filesystem driver -> Directory traversal -> File read
> -> Kernel -> Initrd -> Pivot to rootfs
>
> The OpenWrt uImage.FIT path:
>
> Bootloader -> Read FIT blob from partition -> Kernel
> -> squashfs/erofs rootfs mapped from same blob
>
> Fewer parsing stages mean fewer corruption vectors and lower boot-path
> complexity. The boot chain is shorter and each step is simpler.
>
> Secure boot friendly
> ====================
>
> FIT natively supports cryptographic signatures and verified boot chains.
> Because kernel, DTB, and rootfs are inside the same signed container,
> they are verified together — no unsigned kernel swapping, no mismatched
> kernel/rootfs pairs. This is structurally cleaner than verifying
> separate files scattered across a boot filesystem.
>
> Real-world adoption
> ===================
>
> This boot method is not a theoretical proposal — it is already the
> standard production boot method for a large number of shipping devices.
>
> In OpenWrt's MediaTek Filogic target alone, over 40 boards use
> fit_do_upgrade() for their primary firmware upgrade path. See
> target/linux/mediatek/filogic/base-files/lib/upgrade/platform.sh in
> openwrt.git for the full list.
>
> Beyond MediaTek, the SiFlower SF21-based BananaPi BPi-RV2 (NAND) also
> uses this exact method. Countless additional boards use minor variations,
> for example, appending the squashfs blob after the uImage.FIT container
> instead of using an IH_TYPE_FILESYSTEM uImage.FIT sub-image.
>
> Production / recovery architecture
> ===================================
>
> The slot configuration in this series supports:
>
> - A production FIT image
> - A recovery FIT image
> - Separate persistent data partition
>
> Recovery can reflash production remotely and preserve configuration.
> This dual-slot model — which Android eventually adopted for similar
> embedded constraints — is a natural fit for the OpenWrt deployment
> model.
>
> Why existing boot methods cannot be reused
> ==========================================
>
> 1. distroboot / extlinux: Require a boot filesystem containing
> extlinux.conf. OpenWrt avoids using a boot filesystem.
>
> 2. EFI: Requires an EFI System Partition with a PE binary. OpenWrt's
> firmware is a flat FIT blob, not an EFI application. EFI also mandates
> booting from a block-oriented storage device or PXE, while devices
> supported by OpenWrt often come with SPI-NAND or SPI-NOR flash,
> sometimes as little as 32 MiB in total.
>
> 3. script-based sf_bootdev: Reads a boot script from a fixed offset in
> SPI flash. It does not understand partitions, does not iterate MTD
> or UBI devices, and does not support FIT image detection.
>
> 4. On-demand loading: None of the existing methods support loading FIT
> subimages directly from storage. OpenWrt's FIT images typically
> contain a 5-20 MB squashfs that does NOT need to be copied to RAM —
> the kernel maps it directly from flash. The bootloader only needs
> to load the kernel and DTB (~5-10 MB), not the entire 20-50 MB
> container. This requires a new loading abstraction.
>
> What this series adds
> =====================
>
> The series is structured in two logical parts:
>
> Part 1: On-demand FIT loading infrastructure (patches 1-12)
> -----------------------------------------------------------
>
> A new image_loader abstraction that provides a read callback for loading
> data from storage on demand, rather than requiring the entire image to
> reside in RAM. This is wired into fit_image_load() so that bootm can
> load individual FIT subimages directly from block devices, MTD
> partitions, or UBI volumes.
>
> Three storage backends:
>
> - Block device (eMMC, SD, SATA, NVMe, USB mass storage, virtio)
> - MTD (SPI-NOR, raw NOR, raw NAND with bad block skipping)
> - UBI volume (SPI-NAND, raw NAND)
>
> The "bootm" command is extended to accept a storage device specification
> instead of a RAM address:
>
> bootm mmc 0:4 # boot FIT from eMMC partition 4
> bootm mtd recovery # boot FIT from MTD partition "recovery"
> bootm ubi recovery # boot FIT from UBI volume "recovery"
>
> This infrastructure is independently useful beyond the OpenWrt boot
> method. Any board that stores a FIT image directly in a partition
> (rather than as a file on a filesystem) can benefit from on-demand
> subimage loading.
>
> Part 2: OpenWrt boot method and bootdevs (patches 13-20)
> ---------------------------------------------------------
>
> A proper bootstd boot method (bootmeth_openwrt) that:
>
> - Scans block device partitions, MTD partitions, and UBI volumes
> for raw FIT images (detected by fdt_check_header on the first bytes)
> - Boots via the image_loader / bootm storage path
> - Supports configurable dual-slot boot (production + recovery) via
> environment variables
> - Provides a script hook (openwrt_boot_script) for boards that need
> to probe hardware before selecting the FIT configuration
>
> Two new boot devices are introduced:
>
> - mtd_bootdev: Iterates MTD partitions. A hunt callback walks the
> MTD subsystem device list to bind bootdevs for both UCLASS_MTD
> (SPI-NAND) and UCLASS_SPI_FLASH (SPI-NOR) devices.
>
> - ubi_bootdev: Auto-attaches UBI from DT (compatible = "linux,ubi")
> and iterates UBI volumes.
>
> The boot method integrates with bootstd's existing scan/priority
> framework. "bootflow scan" discovers OpenWrt firmware on all attached
> storage, and "bootflow boot" boots the first valid one — exactly like
> distroboot or EFI, but for raw FIT images.
>
> Slot configuration example:
>
> openwrt_slot_production=firmware
> openwrt_slot_recovery=recovery
> openwrt_boot_order=production recovery
>
> This enables production/recovery dual-boot, boot-loop detection (planned
> for a future series via pstore), and boot menu integration.
>
> Testing
> =======
>
> The BananaPi BPi-R3 (MT7986) is the ideal demonstration device for this
> series because it is the only commonly available board that exposes all
> four storage types — SPI-NOR, SPI-NAND, eMMC, and a microSD slot — and
> can boot from any of them using the very same uImage.FIT image. This
> makes it possible to test every image_loader backend and the full
> cross-media boot flow on a single board.
>
> - sandbox: unit tests for image_loader
> - BananaPi BPi-R3 (MT7986): real hardware testing with SPI-NOR,
> SPI-NAND (UBI), eMMC, and SD card — including combinations of
> present/absent flash devices and deliberate probe failures
>
> Patch overview
> ==============
>
> On-demand FIT loading (generic infrastructure):
>
> 01/20 boot: add image_loader on-demand loading abstraction
> 02/20 boot: image-loader: add block device backend
> 03/20 mtd: add mtd_read_skip_bad() helper
> 04/20 boot: image-loader: add MTD backend
> 05/20 cmd: ubi: export ubi_find_volume()
> 06/20 mtd: set flash_node on DT-created partitions
> 07/20 cmd: ubi: add ubi_part_from_mtd()
> 08/20 boot: image-loader: add UBI volume backend
> 09/20 boot: fit: support on-demand loading in fit_image_load()
> 10/20 cmd: bootm: accept storage device as image source
> 11/20 test: boot: add image_loader unit tests
> 12/20 doc: bootm: document direct storage boot
>
> OpenWrt boot method:
>
> 13/20 boot: bootmeth: add OpenWrt boot method skeleton
> 14/20 boot: bootmeth: openwrt: implement read_bootflow for block devices
> 15/20 boot: bootmeth: openwrt: implement boot via bootm storage path
> 16/20 boot: bootdev: add MTD boot device
> 17/20 boot: bootdev: add UBI boot device
> 18/20 boot: bootmeth: openwrt: support MTD and UBI bootdevs
> 19/20 boot: bootmeth: openwrt: add openwrt_boot_script hook for bootconf
> 20/20 boot: bootmeth: openwrt: add slot configuration from environment
>
> Diffstat summary:
>
> 29 files changed, 2666 insertions(+), 45 deletions(-)
>
> Planned future work
> ===================
>
> - pstore-based boot state tracking for automatic fallback to recovery
> after repeated boot failures and boot-loop avoidance
> - Comprehensive documentation and board migration guide
> - dm-verity integration for runtime integrity verification of the
> rootfs image, complementing FIT signature verification at boot
> - Board enablement patches for MediaTek (MT7986, MT7981, MT7988),
> Qualcomm (IPQ807x, IPQ60xx), and other OpenWrt-supported SoCs
>
> Comments, questions, and review very much appreciated.
Thanks for the detailed explanation. This seems fine to me and I
suspect it will be useful elsewhere. I will take a look at the patches
this week.
>
> AI tool disclosure
> ==================
>
> Major parts of this series were developed with assistance from GitHub
> Copilot (Claude Opus 4.6, Anthropic). The AI was used as a coding
> partner for scaffolding boilerplate, drafting documentation and commit
> messages, running checkpatch sweeps, and iterating on review feedback.
> All architectural decisions, U-Boot subsystem integration, hardware
> testing, and final review were done by the human author. Every line of
> code was reviewed and tested on real hardware before inclusion.
Regards,
Simon
>
>
> Cheers
>
>
> Daniel
>
>
> Daniel Golle (20):
> boot: add image_loader on-demand loading abstraction
> boot: image-loader: add block device backend
> mtd: add mtd_read_skip_bad() helper
> boot: image-loader: add MTD backend
> cmd: ubi: export ubi_find_volume()
> mtd: set flash_node on DT-created partitions
> cmd: ubi: add ubi_part_from_mtd()
> boot: image-loader: add UBI volume backend
> boot: fit: support on-demand loading in fit_image_load()
> cmd: bootm: accept storage device as image source
> test: boot: add image_loader unit tests
> doc: bootm: document direct storage boot
> boot: bootmeth: add OpenWrt boot method skeleton
> boot: bootmeth: openwrt: implement read_bootflow for block devices
> boot: bootmeth: openwrt: implement boot via bootm storage path
> boot: bootdev: add MTD boot device
> boot: bootdev: add UBI boot device
> boot: bootmeth: openwrt: support MTD and UBI bootdevs
> boot: bootmeth: openwrt: add openwrt_boot_script hook for bootconf
> boot: bootmeth: openwrt: add slot configuration from environment
>
> boot/Kconfig | 88 +++++++
> boot/Makefile | 8 +
> boot/bootm.c | 62 ++++-
> boot/bootmeth_openwrt.c | 248 +++++++++++++++++++
> boot/image-fit.c | 96 ++++++++
> boot/image-loader-blk.c | 133 ++++++++++
> boot/image-loader-mtd.c | 103 ++++++++
> boot/image-loader-ubi.c | 112 +++++++++
> boot/image-loader.c | 163 +++++++++++++
> boot/mtd_bootdev.c | 150 ++++++++++++
> boot/ubi_bootdev.c | 180 ++++++++++++++
> cmd/bootm.c | 148 +++++++++++-
> cmd/mtd.c | 65 ++---
> cmd/ubi.c | 33 ++-
> doc/develop/bootm-storage.rst | 210 ++++++++++++++++
> doc/develop/index.rst | 1 +
> doc/usage/fit/index.rst | 1 +
> doc/usage/fit/storage-boot.rst | 201 +++++++++++++++
> drivers/mtd/mtd-uclass.c | 15 ++
> drivers/mtd/mtdcore.c | 45 ++++
> drivers/mtd/mtdpart.c | 2 +
> include/bootm.h | 2 +
> include/image-loader.h | 188 +++++++++++++++
> include/image.h | 4 +
> include/linux/mtd/mtd.h | 24 ++
> include/ubi_uboot.h | 2 +
> test/boot/Makefile | 2 +
> test/boot/image_loader.c | 429 +++++++++++++++++++++++++++++++++
> test/cmd_ut.c | 2 +
> 29 files changed, 2673 insertions(+), 44 deletions(-)
> create mode 100644 boot/bootmeth_openwrt.c
> create mode 100644 boot/image-loader-blk.c
> create mode 100644 boot/image-loader-mtd.c
> create mode 100644 boot/image-loader-ubi.c
> create mode 100644 boot/image-loader.c
> create mode 100644 boot/mtd_bootdev.c
> create mode 100644 boot/ubi_bootdev.c
> create mode 100644 doc/develop/bootm-storage.rst
> create mode 100644 doc/usage/fit/storage-boot.rst
> create mode 100644 include/image-loader.h
> create mode 100644 test/boot/image_loader.c
>
> --
> 2.53.0
Regards,
Simon
^ permalink raw reply [flat|nested] 88+ messages in thread* Re: [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading
2026-02-16 21:21 [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading Daniel Golle
` (21 preceding siblings ...)
2026-02-17 13:32 ` Simon Glass
@ 2026-02-17 15:08 ` Tom Rini
2026-02-17 17:46 ` Tom Rini
` (2 subsequent siblings)
25 siblings, 0 replies; 88+ messages in thread
From: Tom Rini @ 2026-02-17 15:08 UTC (permalink / raw)
To: Daniel Golle
Cc: Simon Glass, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
[-- Attachment #1: Type: text/plain, Size: 845 bytes --]
On Mon, Feb 16, 2026 at 09:21:14PM +0000, Daniel Golle wrote:
> Hi all,
>
> This RFC series adds a new boot method for OpenWrt's "uImage.FIT with
> embedded rootfs" firmware model, along with the underlying infrastructure
> to load FIT images on-demand directly from storage devices without copying
> them entirely to RAM first.
Setting aside specifics, which I need to get in to later today, please
make sure this passes CI
(https://docs.u-boot.org/en/latest/develop/ci_testing.html) (I saw an
rst error CI would fail over) and do some size comparison before/after
on this series, both when it is and is not enabled. Using
https://source.denx.de/u-boot/u-boot-extras/-/blob/master/contrib/trini/u-boot-size-test.sh?ref_type=heads
can be a handy wrapper around buildman's built-in functionality for
that. Thanks.
--
Tom
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 88+ messages in thread* Re: [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading
2026-02-16 21:21 [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading Daniel Golle
` (22 preceding siblings ...)
2026-02-17 15:08 ` Tom Rini
@ 2026-02-17 17:46 ` Tom Rini
2026-02-23 19:32 ` Tom Rini
2026-02-17 18:13 ` Tom Rini
2026-02-17 19:20 ` Tom Rini
25 siblings, 1 reply; 88+ messages in thread
From: Tom Rini @ 2026-02-17 17:46 UTC (permalink / raw)
To: Daniel Golle
Cc: Simon Glass, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
[-- Attachment #1: Type: text/plain, Size: 1959 bytes --]
On Mon, Feb 16, 2026 at 09:21:14PM +0000, Daniel Golle wrote:
> Hi all,
>
> This RFC series adds a new boot method for OpenWrt's "uImage.FIT with
> embedded rootfs" firmware model, along with the underlying infrastructure
> to load FIT images on-demand directly from storage devices without copying
> them entirely to RAM first.
[snip]
> AI tool disclosure
> ==================
>
> Major parts of this series were developed with assistance from GitHub
> Copilot (Claude Opus 4.6, Anthropic). The AI was used as a coding
> partner for scaffolding boilerplate, drafting documentation and commit
> messages, running checkpatch sweeps, and iterating on review feedback.
> All architectural decisions, U-Boot subsystem integration, hardware
> testing, and final review were done by the human author. Every line of
> code was reviewed and tested on real hardware before inclusion.
First, I appreciate your honesty and explanation in the disclosure here.
This topic comes up, and will keep coming up, and as a project we have
not yet decided on a position. I know that the Linux Kernel has come up
with:
https://docs.kernel.org/next/process/generated-content.html
so far. But I think that:
https://docs.postmarketos.org/policies-and-processes/development/contributing-and-ai.html
brings up points that are quite relevant too. Absolutely no one has been
happy with when gitlab or patchwork were unusable / unreachable (and for
some people are still unusable) but it's because of all the AI scrapers
that things were unusable or now have anubis in front of them (blocking
other humans now).
With that said, I want to stress the "human is responsible" portion of
what both links say, and that given where exactly these changes are
aimed for, extra scrutiny is required. Things like:
https://cyberplace.social/@GossiTheDog/116080909947754833
show just how bad they are about introducing security bugs these days.
--
Tom
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 88+ messages in thread* Re: [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading
2026-02-17 17:46 ` Tom Rini
@ 2026-02-23 19:32 ` Tom Rini
2026-02-24 11:57 ` Daniel Golle
0 siblings, 1 reply; 88+ messages in thread
From: Tom Rini @ 2026-02-23 19:32 UTC (permalink / raw)
To: Daniel Golle
Cc: Simon Glass, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
[-- Attachment #1: Type: text/plain, Size: 2282 bytes --]
On Tue, Feb 17, 2026 at 11:46:17AM -0600, Tom Rini wrote:
> On Mon, Feb 16, 2026 at 09:21:14PM +0000, Daniel Golle wrote:
>
> > Hi all,
> >
> > This RFC series adds a new boot method for OpenWrt's "uImage.FIT with
> > embedded rootfs" firmware model, along with the underlying infrastructure
> > to load FIT images on-demand directly from storage devices without copying
> > them entirely to RAM first.
> [snip]
> > AI tool disclosure
> > ==================
> >
> > Major parts of this series were developed with assistance from GitHub
> > Copilot (Claude Opus 4.6, Anthropic). The AI was used as a coding
> > partner for scaffolding boilerplate, drafting documentation and commit
> > messages, running checkpatch sweeps, and iterating on review feedback.
> > All architectural decisions, U-Boot subsystem integration, hardware
> > testing, and final review were done by the human author. Every line of
> > code was reviewed and tested on real hardware before inclusion.
>
> First, I appreciate your honesty and explanation in the disclosure here.
>
> This topic comes up, and will keep coming up, and as a project we have
> not yet decided on a position. I know that the Linux Kernel has come up
> with:
> https://docs.kernel.org/next/process/generated-content.html
> so far. But I think that:
> https://docs.postmarketos.org/policies-and-processes/development/contributing-and-ai.html
> brings up points that are quite relevant too. Absolutely no one has been
> happy with when gitlab or patchwork were unusable / unreachable (and for
> some people are still unusable) but it's because of all the AI scrapers
> that things were unusable or now have anubis in front of them (blocking
> other humans now).
>
> With that said, I want to stress the "human is responsible" portion of
> what both links say, and that given where exactly these changes are
> aimed for, extra scrutiny is required. Things like:
> https://cyberplace.social/@GossiTheDog/116080909947754833
> show just how bad they are about introducing security bugs these days.
Following up on this part I suppose, I see in CI you're working on v2
but please make sure to follow up with the other outstanding questions
I've asked before posting that. Thanks!
--
Tom
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading
2026-02-23 19:32 ` Tom Rini
@ 2026-02-24 11:57 ` Daniel Golle
2026-02-24 17:24 ` Tom Rini
0 siblings, 1 reply; 88+ messages in thread
From: Daniel Golle @ 2026-02-24 11:57 UTC (permalink / raw)
To: Tom Rini
Cc: Simon Glass, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
On Mon, Feb 23, 2026 at 01:32:19PM -0600, Tom Rini wrote:
> On Tue, Feb 17, 2026 at 11:46:17AM -0600, Tom Rini wrote:
> > On Mon, Feb 16, 2026 at 09:21:14PM +0000, Daniel Golle wrote:
> >
> > > Hi all,
> > >
> > > This RFC series adds a new boot method for OpenWrt's "uImage.FIT with
> > > embedded rootfs" firmware model, along with the underlying infrastructure
> > > to load FIT images on-demand directly from storage devices without copying
> > > them entirely to RAM first.
> > [snip]
> > > AI tool disclosure
> > > ==================
> > >
> > > Major parts of this series were developed with assistance from GitHub
> > > Copilot (Claude Opus 4.6, Anthropic). The AI was used as a coding
> > > partner for scaffolding boilerplate, drafting documentation and commit
> > > messages, running checkpatch sweeps, and iterating on review feedback.
> > > All architectural decisions, U-Boot subsystem integration, hardware
> > > testing, and final review were done by the human author. Every line of
> > > code was reviewed and tested on real hardware before inclusion.
> >
> > First, I appreciate your honesty and explanation in the disclosure here.
> >
> > This topic comes up, and will keep coming up, and as a project we have
> > not yet decided on a position. I know that the Linux Kernel has come up
> > with:
> > https://docs.kernel.org/next/process/generated-content.html
> > so far. But I think that:
> > https://docs.postmarketos.org/policies-and-processes/development/contributing-and-ai.html
> > brings up points that are quite relevant too. Absolutely no one has been
> > happy with when gitlab or patchwork were unusable / unreachable (and for
> > some people are still unusable) but it's because of all the AI scrapers
> > that things were unusable or now have anubis in front of them (blocking
> > other humans now).
> >
> > With that said, I want to stress the "human is responsible" portion of
> > what both links say, and that given where exactly these changes are
> > aimed for, extra scrutiny is required. Things like:
> > https://cyberplace.social/@GossiTheDog/116080909947754833
> > show just how bad they are about introducing security bugs these days.
>
> Following up on this part I suppose, I see in CI you're working on v2
> but please make sure to follow up with the other outstanding questions
> I've asked before posting that. Thanks!
I've carefully reiterated over all your emails regarding the series and
my replies to them. Apart from your request to share an image making use
of the proposal to include dm-verity parameters in uImage.FIT (and use
the presence to decide whether has varifications of that subimage may be
skipped) I haven't found anything which hasn't been answered. But maybe
I missed something, of course.
As it is difficult to share large binaries without paying for hosting or
exposing my home router public IP address to a public mailing list I
have sent a decompiled FIT header. If you would like me to share an
actual image (or even the whole SD card image for the BPi-R3, or images
for the OpenWrt One), please contact me off-list and I shall find a way
to share the binaries with you.
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading
2026-02-24 11:57 ` Daniel Golle
@ 2026-02-24 17:24 ` Tom Rini
2026-02-25 14:34 ` Daniel Golle
0 siblings, 1 reply; 88+ messages in thread
From: Tom Rini @ 2026-02-24 17:24 UTC (permalink / raw)
To: Daniel Golle
Cc: Simon Glass, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
[-- Attachment #1: Type: text/plain, Size: 4357 bytes --]
On Tue, Feb 24, 2026 at 11:57:23AM +0000, Daniel Golle wrote:
> On Mon, Feb 23, 2026 at 01:32:19PM -0600, Tom Rini wrote:
> > On Tue, Feb 17, 2026 at 11:46:17AM -0600, Tom Rini wrote:
> > > On Mon, Feb 16, 2026 at 09:21:14PM +0000, Daniel Golle wrote:
> > >
> > > > Hi all,
> > > >
> > > > This RFC series adds a new boot method for OpenWrt's "uImage.FIT with
> > > > embedded rootfs" firmware model, along with the underlying infrastructure
> > > > to load FIT images on-demand directly from storage devices without copying
> > > > them entirely to RAM first.
> > > [snip]
> > > > AI tool disclosure
> > > > ==================
> > > >
> > > > Major parts of this series were developed with assistance from GitHub
> > > > Copilot (Claude Opus 4.6, Anthropic). The AI was used as a coding
> > > > partner for scaffolding boilerplate, drafting documentation and commit
> > > > messages, running checkpatch sweeps, and iterating on review feedback.
> > > > All architectural decisions, U-Boot subsystem integration, hardware
> > > > testing, and final review were done by the human author. Every line of
> > > > code was reviewed and tested on real hardware before inclusion.
> > >
> > > First, I appreciate your honesty and explanation in the disclosure here.
> > >
> > > This topic comes up, and will keep coming up, and as a project we have
> > > not yet decided on a position. I know that the Linux Kernel has come up
> > > with:
> > > https://docs.kernel.org/next/process/generated-content.html
> > > so far. But I think that:
> > > https://docs.postmarketos.org/policies-and-processes/development/contributing-and-ai.html
> > > brings up points that are quite relevant too. Absolutely no one has been
> > > happy with when gitlab or patchwork were unusable / unreachable (and for
> > > some people are still unusable) but it's because of all the AI scrapers
> > > that things were unusable or now have anubis in front of them (blocking
> > > other humans now).
> > >
> > > With that said, I want to stress the "human is responsible" portion of
> > > what both links say, and that given where exactly these changes are
> > > aimed for, extra scrutiny is required. Things like:
> > > https://cyberplace.social/@GossiTheDog/116080909947754833
> > > show just how bad they are about introducing security bugs these days.
> >
> > Following up on this part I suppose, I see in CI you're working on v2
> > but please make sure to follow up with the other outstanding questions
> > I've asked before posting that. Thanks!
>
> I've carefully reiterated over all your emails regarding the series and
> my replies to them. Apart from your request to share an image making use
> of the proposal to include dm-verity parameters in uImage.FIT (and use
> the presence to decide whether has varifications of that subimage may be
> skipped) I haven't found anything which hasn't been answered. But maybe
> I missed something, of course.
There's:
https://lore.kernel.org/u-boot/20260217150820.GB2747538@bill-the-cat/
And I see you're using CI, but I didn't see an ack about the size
investigations portion.
There's:
https://lore.kernel.org/u-boot/20260217192000.GH2747538@bill-the-cat/
And I didn't see anything about that.
There's:
https://lore.kernel.org/u-boot/20260217190554.GF2747538@bill-the-cat/
https://lore.kernel.org/u-boot/20260219153100.GL3233182@bill-the-cat/
Which go together and I think "human written only commit message" might
well be a good policy as it helps ensure the patch in question has been
read and summarized by the human author (and enforcement is on the
honor system).
Finally there's:
https://lore.kernel.org/u-boot/20260217191536.GG2747538@bill-the-cat/
where I was hoping for more feedback.
I don't recall if we came to a conclusion, or just look again later,
about if we should have this functionality both exposed via "bootm" and
the new distro bootmeth, or not. It might be "talk more" and I'm leaning
currently on saying the migration path is something openwrt holds
downstream while getting things migrated?
And in sum, this series will need to be split up a bit I strongly
suspect. Being clear about the end goal is good but implementing each of
the backends as well in a single series will make this too large to
review.
--
Tom
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading
2026-02-24 17:24 ` Tom Rini
@ 2026-02-25 14:34 ` Daniel Golle
2026-02-25 22:16 ` Tom Rini
0 siblings, 1 reply; 88+ messages in thread
From: Daniel Golle @ 2026-02-25 14:34 UTC (permalink / raw)
To: Tom Rini
Cc: Simon Glass, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
On Tue, Feb 24, 2026 at 11:24:44AM -0600, Tom Rini wrote:
> On Tue, Feb 24, 2026 at 11:57:23AM +0000, Daniel Golle wrote:
> > On Mon, Feb 23, 2026 at 01:32:19PM -0600, Tom Rini wrote:
> > > On Tue, Feb 17, 2026 at 11:46:17AM -0600, Tom Rini wrote:
> > > > On Mon, Feb 16, 2026 at 09:21:14PM +0000, Daniel Golle wrote:
> > > >
> > > > > Hi all,
> > > > >
> > > > > This RFC series adds a new boot method for OpenWrt's "uImage.FIT with
> > > > > embedded rootfs" firmware model, along with the underlying infrastructure
> > > > > to load FIT images on-demand directly from storage devices without copying
> > > > > them entirely to RAM first.
> > > > [snip]
> > > > > AI tool disclosure
> > > > > ==================
> > > > >
> > > > > Major parts of this series were developed with assistance from GitHub
> > > > > Copilot (Claude Opus 4.6, Anthropic). The AI was used as a coding
> > > > > partner for scaffolding boilerplate, drafting documentation and commit
> > > > > messages, running checkpatch sweeps, and iterating on review feedback.
> > > > > All architectural decisions, U-Boot subsystem integration, hardware
> > > > > testing, and final review were done by the human author. Every line of
> > > > > code was reviewed and tested on real hardware before inclusion.
> > > >
> > > > First, I appreciate your honesty and explanation in the disclosure here.
> > > >
> > > > This topic comes up, and will keep coming up, and as a project we have
> > > > not yet decided on a position. I know that the Linux Kernel has come up
> > > > with:
> > > > https://docs.kernel.org/next/process/generated-content.html
> > > > so far. But I think that:
> > > > https://docs.postmarketos.org/policies-and-processes/development/contributing-and-ai.html
> > > > brings up points that are quite relevant too. Absolutely no one has been
> > > > happy with when gitlab or patchwork were unusable / unreachable (and for
> > > > some people are still unusable) but it's because of all the AI scrapers
> > > > that things were unusable or now have anubis in front of them (blocking
> > > > other humans now).
> > > >
> > > > With that said, I want to stress the "human is responsible" portion of
> > > > what both links say, and that given where exactly these changes are
> > > > aimed for, extra scrutiny is required. Things like:
> > > > https://cyberplace.social/@GossiTheDog/116080909947754833
> > > > show just how bad they are about introducing security bugs these days.
> > >
> > > Following up on this part I suppose, I see in CI you're working on v2
> > > but please make sure to follow up with the other outstanding questions
> > > I've asked before posting that. Thanks!
> >
> > I've carefully reiterated over all your emails regarding the series and
> > my replies to them. Apart from your request to share an image making use
> > of the proposal to include dm-verity parameters in uImage.FIT (and use
> > the presence to decide whether has varifications of that subimage may be
> > skipped) I haven't found anything which hasn't been answered. But maybe
> > I missed something, of course.
>
> There's:
Thanks for digging all of them out. I took the freedom to answer all of
them here as it makes it easier to keep track of all the topics being in
a single thread.
> https://lore.kernel.org/u-boot/20260217150820.GB2747538@bill-the-cat/
> And I see you're using CI, but I didn't see an ack about the size
> investigations portion.
I had to remove the -E (errors-as-warnings) option from the script
because my host toolchain is too new and caused a few warnings about
discarded 'const' qualifiers. Most can be fixed by updating dtc to
that in upstream Linux, the other one is in
tools/atmelimage.c:64:31: error: assignment discards ‘const’ qualifier from pointer target type [-Werror=discarded-qualifiers]
and can also be fixed easily. I'll post the patch for that after I
send this email.
Updating dtc, however, caused a cascade of new warnings in existing
device tree files, newly introduce node-name checks, ... so at that
point I decided to just remove the -E option passed from the script
to buildman...
Results:
For any board having CONFIG_FIT enabled but none of the new features
there is a slight increase:
aarch64: (for 1/1 boards) all +8.0 text +8.0
mt7622_rfb : all +8 text +8
u-boot: add: 0/0, grow: 1/0 bytes: 8/0 (8)
function old new delta
fit_image_load 1632 1640 +8
The increase by 8 bytes is caused by this hunk:
--- a/boot/image-fit.c
+++ b/boot/image-fit.c
@@ -2279,8 +2362,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 (data != load)
+ printf(" Loading %s from 0x%08lx to 0x%08lx\n",
+ prop_name, data, load);
} else {
load = data; /* No load address specified */
}
It could obviously also be guarded by some
if (!CONFIG_IS_ENABLED(IMAGEMAP) || data != load)
so the compiler would end up with byte-identical code in case
CONFIG_IS_ENABLED(IMAGEMAP) is false.
Do you think that'd be worth it?
Enabling the new features for the BanananPi R3 required also enabling
CONFIG_FIT which previously wasn't selected for that board.
With all the new options (CONFIG_FIT, all imagemap backends, OpenWrt bootmeth):
aarch64: (for 1/1 boards) all +47701.0 data +848.0 rodata +5001.0 text +41852.0
mt7986a_bpir3_emmc: all +47701 data +848 rodata +5001 text +41852
u-boot: add: 142/0, grow: 22/-1 bytes: 27785/-4 (27781)
function old new delta
MD5Transform - 2528 +2528
fit_image_load - 1980 +1980
fit_print_contents - 1228 +1228
fit_verity_build_params - 948 +948
fit_image_print - 780 +780
cmdline_set_arg - 688 +688
imagemap_map - 640 +640
bootmeth_vbe_ft_fixup - 640 +640
bootm_run_states 1908 2436 +528
fit_image_verify_with_data - 520 +520
openwrt_boot - 516 +516
sha256_finish - 404 +404
image_locate_script 272 668 +396
boot_get_fdt_fit - 396 +396
fit_image_print_data - 392 +392
boot_get_loadable - 384 +384
fdt_check_full - 332 +332
bootm_help_text 910 1234 +324
sha1_finish - 300 +300
bootflow_scan_next 424 724 +300
bootmeth_vbe_simple_ft_fixup - 288 +288
hash_command 188 468 +280
hash_algo - 280 +280
fit_all_image_verify - 272 +272
bootmeth_vbe_simple_probe - 264 +264
MD5Update - 260 +260
do_imgextract 668 924 +256
boot_get_fdt 592 848 +256
static.sha1_update - 240 +240
boot_get_ramdisk 480 720 +240
static.CSWTCH 139 371 +232
copy_in - 228 +228
fit_image_get_data - 224 +224
static.sha256_update - 220 +220
fit_conf_get_node - 216 +216
imagemap_map_to - 212 +212
openwrt_read_bootflow - 204 +204
fit_conf_get_prop_node - 192 +192
MD5Final - 188 +188
imagemap_record - 180 +180
imagemap_create - 176 +176
fit_image_print_verification_data - 176 +176
source_help_text 40 212 +172
event_notify - 168 +168
vbe_simple_fixup_node - 164 +164
bootflow_cmdline_set_arg - 164 +164
fit_check_format - 152 +152
vbe_get_blk - 148 +148
vbe_read_nvdata - 140 +140
ofnode_add_subnode - 140 +140
imagemap_cleanup - 136 +136
fit_image_verify - 136 +136
fit_image_get_address - 136 +136
ofnode_copy_props - 120 +120
boot_get_setup_fit - 120 +120
_u_boot_list_2_uclass_driver_2_imagemap - 120 +120
_u_boot_list_2_driver_2_vbe_simple - 120 +120
_u_boot_list_2_driver_2_bootmeth_openwrt - 120 +120
next_glob_bootmeth - 116 +116
fit_parse_subimage - 116 +116
fit_parse_conf - 116 +116
calculate_hash - 116 +116
vbe_simple_get_state_desc - 112 +112
sha1_csum_wd - 112 +112
ofnode_write_prop - 112 +112
imagemap_lookup - 112 +112
hash_lookup_algo - 112 +112
vbe_simple_read_state - 100 +100
vbe_read_version - 100 +100
vbe_find_first_device - 96 +96
uimage_phase - 96 +96
ofnode_write_u32 - 96 +96
md5_wd - 96 +96
genimg_get_kernel_addr_fit 24 120 +96
imgextract_help_text 85 178 +93
board_init_r 572 664 +92
vbe_find_next_device - 88 +88
simple_read_nvdata - 88 +88
sha256_starts - 88 +88
fit_get_node_from_config - 88 +88
bootmeth_get_bootflow - 88 +88
bootflow_check - 88 +88
bootmeth_setup_iter_order 284 368 +84
bootmeth_glob_allowed - 84 +84
board_init_f 1168 1252 +84
image_info 208 288 +80
get_table_entry_id - 80 +80
fit_image_hash_get_value - 80 +80
fit_image_get_emb_data - 80 +80
fit_image_get_type - 76 +76
fit_image_get_phase - 76 +76
fit_image_get_os - 76 +76
fit_image_get_arch - 76 +76
bootflow_cmdline_set - 76 +76
sha256_csum_wd - 72 +72
fit_image_get_node - 72 +72
fit_image_get_data_size - 72 +72
fit_image_get_data_position - 72 +72
fit_image_get_data_offset - 72 +72
fit_image_get_comp - 68 +68
vbe_reqs - 64 +64
sha256_padding - 64 +64
sha1_padding - 64 +64
image_setup_libfdt 344 408 +64
hash_finish_sha256 - 64 +64
hash_finish_sha1 - 64 +64
fit_image_check_type - 60 +60
fit_image_check_os - 60 +60
fit_image_check_comp - 60 +60
fit_conf_get_prop_node_index - 60 +60
do_source 84 144 +60
crc32_wd_buf - 60 +60
crc16_ccitt_wd_buf - 60 +60
sha1_starts - 56 +56
openwrt_bootmeth_ops - 56 +56
oftree_path - 56 +56
ofnode_write_string - 56 +56
hash_finish_crc32 - 56 +56
hash_finish_crc16_ccitt - 56 +56
fit_image_hash_get_algo - 56 +56
fit_get_desc - 56 +56
bootmeth_vbe_simple_ops - 56 +56
hash_update_crc32 - 52 +52
hash_update_crc16_ccitt - 52 +52
hash_init_sha256 - 52 +52
hash_init_sha1 - 52 +52
crc8 - 52 +52
hash_init_crc32 - 48 +48
hash_init_crc16_ccitt - 48 +48
alist_uninit - 48 +48
openwrt_bootmeth_bind - 44 +44
bootmeth_vbe_simple_bind - 44 +44
openwrt_check - 36 +36
imagemap_post_probe - 36 +36
hash_update_sha256 - 36 +36
hash_update_sha1 - 36 +36
vbe_simple_read_file - 32 +32
openwrt_bootmeth_ids - 32 +32
generic_simple_vbe_simple_ids - 32 +32
bootflow_scan_first 236 264 +28
genimg_get_type_id - 24 +24
genimg_get_phase_name - 24 +24
genimg_get_phase_id - 24 +24
genimg_get_os_id - 24 +24
genimg_get_comp_id - 24 +24
genimg_get_arch_id - 24 +24
fit_image_get_load - 16 +16
fit_image_get_entry - 16 +16
fit_get_end - 16 +16
bootm_find_images 268 284 +16
_u_boot_list_2_evspy_info_2_EVT_FT_FIXUP_3_bootmeth_vbe_simple_ft_fixup - 16 +16
_u_boot_list_2_evspy_info_2_EVT_FT_FIXUP_3_bootmeth_vbe_ft_fixup - 16 +16
sha256_update - 12 +12
sha1_update - 12 +12
genimg_get_kernel_addr 20 32 +12
event_type_name - 12 +12
event_notify_null - 12 +12
vbe_simple_read_bootflow - 8 +8
vbe_req_random_seed - 8 +8
vbe_req_efi_runtime_rand - 8 +8
vbe_req_aslr_rand - 8 +8
vbe_req_aslr_move - 8 +8
oftree_root - 8 +8
genimg_has_config 8 16 +8
boot_get_setup 8 4 -4
This seems excessive, but it's mostly due to CONFIG_FIT not being enabled
in the upstream U-Boot defconfig for the BananaPi R3.
With CONFIG_FIT enabled already before, the changes are more reasonable:
aarch64: (for 1/1 boards) all +6057.0 data +296.0 rodata +513.0 text +5248.0
mt7986a_bpir3_emmc: all +6057 data +296 rodata +513 text +5248
u-boot: add: 21/0, grow: 3/0 bytes: 5336/0 (5336)
function old new delta
fit_verity_build_params - 948 +948
cmdline_set_arg - 688 +688
imagemap_map - 640 +640
openwrt_boot - 516 +516
fit_image_load 1632 1980 +348
copy_in - 228 +228
imagemap_map_to - 212 +212
openwrt_read_bootflow - 204 +204
imagemap_record - 180 +180
bootm_run_states 2256 2436 +180
imagemap_create - 176 +176
bootflow_cmdline_set_arg - 164 +164
imagemap_cleanup - 136 +136
_u_boot_list_2_uclass_driver_2_imagemap - 120 +120
_u_boot_list_2_driver_2_bootmeth_openwrt - 120 +120
imagemap_lookup - 112 +112
bootflow_cmdline_set - 76 +76
openwrt_bootmeth_ops - 56 +56
alist_uninit - 48 +48
openwrt_bootmeth_bind - 44 +44
openwrt_check - 36 +36
imagemap_post_probe - 36 +36
fit_image_get_data 188 224 +36
openwrt_bootmeth_ids - 32 +32
>
> There's:
> https://lore.kernel.org/u-boot/20260217192000.GH2747538@bill-the-cat/
> And I didn't see anything about that.
Re: boot: add OpenWrt boot method and on-demand FIT loading
(Re: not enabled on any board nor in sandbox)
In my new series I'll add the OpenWrt One as a sample board and also
enable the new features on the BananaPi R3.
Should I also enable the imagemap base feature in sandbox as well so the
unit tests can easily be run?
(Re: splitting series)
There aren't any actual fixes part of the series. Of course, some
smaller changes and refactoring to create the helpers needed by
imagemap and the bootmeth could be broken out as indidivual patches
sent separately in advance, but they wouldn't have any in-tree users
at this point. Eg.
- mtd: set flash_node on DT-created partitions
- mtd: add mtd_read_skip_bad() helper
- cmd: ubi: add ubi_part_from_mtd()
- cmd: ubi: export ubi_find_volume()
I also think it makes sense to discuss the new feature (not part of the
original RFC series) to store dm-verity parameters for IH_TYPE_FILESYSTEM
subimages in the FIT structure separately, and that would start with
discussing the FIT spec amendment for it:
https://github.com/open-source-firmware/flat-image-tree/pull/37
> There's:
> https://lore.kernel.org/u-boot/20260217190554.GF2747538@bill-the-cat/
Re: test: boot: add image_loader unit tests
(Re: AI fixes in the wrong commit)
Yes, this was an oversight and I've of course folded in that change,
and later, upon Simon's comment, switched to use alist instead of a
fixed-size array anyway.
> https://lore.kernel.org/u-boot/20260219153100.GL3233182@bill-the-cat/
> Which go together and I think "human written only commit message" might
> well be a good policy as it helps ensure the patch in question has been
> read and summarized by the human author (and enforcement is on the
> honor system).
While it was very useful to use an LLM for quickly drafting the intial
RFC and receive feedback for the overall approach, it became less and
less useful at later stages -- instead of ending of micro-managing the
LLM for each little change and fix it's easier to just do it yourself
from some point on.
So what you are going to see in v2 surely still got some onchanged lines
of code, comments and parts of commit messages which have intially been
fabricated with the help of the LLM, but most of it is by now the work
of a human (me).
Regarding the commit messages, I get the idea of enforcing a
human-written message, however, when it comes to the use of English
language (I'm not a native speaker) I think the use of things like
deepl.com does create more benefit than potential damage even though it
can also be considered an AI/LLM tool.
> Finally there's:
> https://lore.kernel.org/u-boot/20260217191536.GG2747538@bill-the-cat/
> where I was hoping for more feedback.
Re: reuse imageloader (by now renamed to "imagemap") in SPL
First of all, I don't have any boards which use SPL. All modern
platforms I deal with use U-Boot as "Non-Trusted Firmware (BL33)" or
the OpenSBI equivalent of that on platforms using RISC-V.
Especially regarding the UBI part, the implementation intended for SPL
is way too basic to be useful as imagemap backend.
For BLK (ie. MMC) or SPI-NOR it would be a possibility to reuse
imagemap in SPL -- but (as I've replied earlier) I don't think it
makes sense to merge it with the exising FIT reader in SPL for the
reasons I've explained:
|> So there really isn't much overlap other than the fact that there is a
|> struct with a priv pointer and a .read callback, and even that uses a
|> different addressing parameter (sectors vs. bytes).
> I don't recall if we came to a conclusion, or just look again later,
> about if we should have this functionality both exposed via "bootm" and
> the new distro bootmeth, or not. It might be "talk more" and I'm leaning
> currently on saying the migration path is something openwrt holds
> downstream while getting things migrated?
That's also what I understood from our discussion. Adding new cmdline
features to 'bootm' can be considered a liability, so I've dropped that
from the v2 series and only use the direct-storage FIT loading in
bootmeth_openwrt. We can still add the 'bootm' features as an
intermediate step to easy migration of all our boards as downstream
patch (and later remove it again once all boards and features work with
the bootmeth).
> And in sum, this series will need to be split up a bit I strongly
> suspect. Being clear about the end goal is good but implementing each of
> the backends as well in a single series will make this too large to
> review.
The current state of the series looks pretty much like what I've pushed
to Github for the sake of getting CI exposure.
Do you agree with the submission strategy drafted below?
Commits (reverse git order, starting from the one directly
on top of u-boot.git master branch, ie. in the order of
the patches to be submitted starting with the first to be
sent).
SERIES 1: dm-verity paramters for FIT images
- boot: fit: support generating DM verity cmdline parameters
- doc: fit: add dm-verity boot parameter documentation
To be discussed together with
https://github.com/open-source-firmware/flat-image-tree/pull/37
---
SERIES 2: imagemap storage abstraction
- boot: add imagemap on-demand loading abstraction
- boot: imagemap: add block device backend
- doc: imagemap: document on-demand loading framework
- test: boot: add imagemap unit tests
This would be a meaningful series together with enabling imagemap
in sandbox so the unit tests are run there.
---
SERIES 3: imagemap MTD and UBI backends
- mtd: add mtd_read_skip_bad() helper
- boot: imagemap: add MTD backend
- mtd: set flash_node on DT-created partitions
- cmd: ubi: export ubi_find_volume()
- cmd: ubi: add ubi_part_from_mtd()
- boot: imagemap: add UBI volume backend
This would be one or two small series adding the backends.
Maybe including another commit completing the documentation
to cover the newly added backends.
---
single PATCH
- boot: fit: support on-demand loading in fit_image_load()
This would be good to be discussed individually as it is
the most intrusive change and needs the most eyeballs.
---
SERIES 4: OpenWrt boot method
- boot: bootmeth: add OpenWrt boot method skeleton
- boot: bootmeth: openwrt: implement read_bootflow
- boot: bootmeth: openwrt: implement boot via imagemap
- boot: bootdev: add MTD boot device
- boot: bootdev: add UBI boot device
- doc: bootstd: add OpenWrt boot method documentation
- board: mediatek: add OpenWrt One support
- mt7986a-bpi-r3: add OpenWrt boot method
Cheers
Daniel
^ permalink raw reply [flat|nested] 88+ messages in thread* Re: [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading
2026-02-25 14:34 ` Daniel Golle
@ 2026-02-25 22:16 ` Tom Rini
2026-02-25 23:49 ` Daniel Golle
0 siblings, 1 reply; 88+ messages in thread
From: Tom Rini @ 2026-02-25 22:16 UTC (permalink / raw)
To: Daniel Golle
Cc: Simon Glass, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
[-- Attachment #1: Type: text/plain, Size: 31989 bytes --]
On Wed, Feb 25, 2026 at 02:34:05PM +0000, Daniel Golle wrote:
> On Tue, Feb 24, 2026 at 11:24:44AM -0600, Tom Rini wrote:
> > On Tue, Feb 24, 2026 at 11:57:23AM +0000, Daniel Golle wrote:
> > > On Mon, Feb 23, 2026 at 01:32:19PM -0600, Tom Rini wrote:
> > > > On Tue, Feb 17, 2026 at 11:46:17AM -0600, Tom Rini wrote:
> > > > > On Mon, Feb 16, 2026 at 09:21:14PM +0000, Daniel Golle wrote:
> > > > >
> > > > > > Hi all,
> > > > > >
> > > > > > This RFC series adds a new boot method for OpenWrt's "uImage.FIT with
> > > > > > embedded rootfs" firmware model, along with the underlying infrastructure
> > > > > > to load FIT images on-demand directly from storage devices without copying
> > > > > > them entirely to RAM first.
> > > > > [snip]
> > > > > > AI tool disclosure
> > > > > > ==================
> > > > > >
> > > > > > Major parts of this series were developed with assistance from GitHub
> > > > > > Copilot (Claude Opus 4.6, Anthropic). The AI was used as a coding
> > > > > > partner for scaffolding boilerplate, drafting documentation and commit
> > > > > > messages, running checkpatch sweeps, and iterating on review feedback.
> > > > > > All architectural decisions, U-Boot subsystem integration, hardware
> > > > > > testing, and final review were done by the human author. Every line of
> > > > > > code was reviewed and tested on real hardware before inclusion.
> > > > >
> > > > > First, I appreciate your honesty and explanation in the disclosure here.
> > > > >
> > > > > This topic comes up, and will keep coming up, and as a project we have
> > > > > not yet decided on a position. I know that the Linux Kernel has come up
> > > > > with:
> > > > > https://docs.kernel.org/next/process/generated-content.html
> > > > > so far. But I think that:
> > > > > https://docs.postmarketos.org/policies-and-processes/development/contributing-and-ai.html
> > > > > brings up points that are quite relevant too. Absolutely no one has been
> > > > > happy with when gitlab or patchwork were unusable / unreachable (and for
> > > > > some people are still unusable) but it's because of all the AI scrapers
> > > > > that things were unusable or now have anubis in front of them (blocking
> > > > > other humans now).
> > > > >
> > > > > With that said, I want to stress the "human is responsible" portion of
> > > > > what both links say, and that given where exactly these changes are
> > > > > aimed for, extra scrutiny is required. Things like:
> > > > > https://cyberplace.social/@GossiTheDog/116080909947754833
> > > > > show just how bad they are about introducing security bugs these days.
> > > >
> > > > Following up on this part I suppose, I see in CI you're working on v2
> > > > but please make sure to follow up with the other outstanding questions
> > > > I've asked before posting that. Thanks!
> > >
> > > I've carefully reiterated over all your emails regarding the series and
> > > my replies to them. Apart from your request to share an image making use
> > > of the proposal to include dm-verity parameters in uImage.FIT (and use
> > > the presence to decide whether has varifications of that subimage may be
> > > skipped) I haven't found anything which hasn't been answered. But maybe
> > > I missed something, of course.
> >
> > There's:
>
> Thanks for digging all of them out. I took the freedom to answer all of
> them here as it makes it easier to keep track of all the topics being in
> a single thread.
>
> > https://lore.kernel.org/u-boot/20260217150820.GB2747538@bill-the-cat/
> > And I see you're using CI, but I didn't see an ack about the size
> > investigations portion.
>
> I had to remove the -E (errors-as-warnings) option from the script
> because my host toolchain is too new and caused a few warnings about
> discarded 'const' qualifiers. Most can be fixed by updating dtc to
> that in upstream Linux, the other one is in
> tools/atmelimage.c:64:31: error: assignment discards ‘const’ qualifier from pointer target type [-Werror=discarded-qualifiers]
> and can also be fixed easily. I'll post the patch for that after I
> send this email.
>
> Updating dtc, however, caused a cascade of new warnings in existing
> device tree files, newly introduce node-name checks, ... so at that
> point I decided to just remove the -E option passed from the script
> to buildman...
>
> Results:
> For any board having CONFIG_FIT enabled but none of the new features
> there is a slight increase:
> aarch64: (for 1/1 boards) all +8.0 text +8.0
> mt7622_rfb : all +8 text +8
> u-boot: add: 0/0, grow: 1/0 bytes: 8/0 (8)
> function old new delta
> fit_image_load 1632 1640 +8
>
> The increase by 8 bytes is caused by this hunk:
> --- a/boot/image-fit.c
> +++ b/boot/image-fit.c
> @@ -2279,8 +2362,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 (data != load)
> + printf(" Loading %s from 0x%08lx to 0x%08lx\n",
> + prop_name, data, load);
> } else {
> load = data; /* No load address specified */
> }
>
> It could obviously also be guarded by some
> if (!CONFIG_IS_ENABLED(IMAGEMAP) || data != load)
> so the compiler would end up with byte-identical code in case
> CONFIG_IS_ENABLED(IMAGEMAP) is false.
> Do you think that'd be worth it?
So 8 byte growth here is fine, it would be more unreadable to avoid that
than to accept it, and 8 bytes is nothing really.
> Enabling the new features for the BanananPi R3 required also enabling
> CONFIG_FIT which previously wasn't selected for that board.
> With all the new options (CONFIG_FIT, all imagemap backends, OpenWrt bootmeth):
> aarch64: (for 1/1 boards) all +47701.0 data +848.0 rodata +5001.0 text +41852.0
> mt7986a_bpir3_emmc: all +47701 data +848 rodata +5001 text +41852
> u-boot: add: 142/0, grow: 22/-1 bytes: 27785/-4 (27781)
> function old new delta
> MD5Transform - 2528 +2528
> fit_image_load - 1980 +1980
> fit_print_contents - 1228 +1228
> fit_verity_build_params - 948 +948
> fit_image_print - 780 +780
> cmdline_set_arg - 688 +688
> imagemap_map - 640 +640
> bootmeth_vbe_ft_fixup - 640 +640
> bootm_run_states 1908 2436 +528
> fit_image_verify_with_data - 520 +520
> openwrt_boot - 516 +516
> sha256_finish - 404 +404
> image_locate_script 272 668 +396
> boot_get_fdt_fit - 396 +396
> fit_image_print_data - 392 +392
> boot_get_loadable - 384 +384
> fdt_check_full - 332 +332
> bootm_help_text 910 1234 +324
> sha1_finish - 300 +300
> bootflow_scan_next 424 724 +300
> bootmeth_vbe_simple_ft_fixup - 288 +288
> hash_command 188 468 +280
> hash_algo - 280 +280
> fit_all_image_verify - 272 +272
> bootmeth_vbe_simple_probe - 264 +264
> MD5Update - 260 +260
> do_imgextract 668 924 +256
> boot_get_fdt 592 848 +256
> static.sha1_update - 240 +240
> boot_get_ramdisk 480 720 +240
> static.CSWTCH 139 371 +232
> copy_in - 228 +228
> fit_image_get_data - 224 +224
> static.sha256_update - 220 +220
> fit_conf_get_node - 216 +216
> imagemap_map_to - 212 +212
> openwrt_read_bootflow - 204 +204
> fit_conf_get_prop_node - 192 +192
> MD5Final - 188 +188
> imagemap_record - 180 +180
> imagemap_create - 176 +176
> fit_image_print_verification_data - 176 +176
> source_help_text 40 212 +172
> event_notify - 168 +168
> vbe_simple_fixup_node - 164 +164
> bootflow_cmdline_set_arg - 164 +164
> fit_check_format - 152 +152
> vbe_get_blk - 148 +148
> vbe_read_nvdata - 140 +140
> ofnode_add_subnode - 140 +140
> imagemap_cleanup - 136 +136
> fit_image_verify - 136 +136
> fit_image_get_address - 136 +136
> ofnode_copy_props - 120 +120
> boot_get_setup_fit - 120 +120
> _u_boot_list_2_uclass_driver_2_imagemap - 120 +120
> _u_boot_list_2_driver_2_vbe_simple - 120 +120
> _u_boot_list_2_driver_2_bootmeth_openwrt - 120 +120
> next_glob_bootmeth - 116 +116
> fit_parse_subimage - 116 +116
> fit_parse_conf - 116 +116
> calculate_hash - 116 +116
> vbe_simple_get_state_desc - 112 +112
> sha1_csum_wd - 112 +112
> ofnode_write_prop - 112 +112
> imagemap_lookup - 112 +112
> hash_lookup_algo - 112 +112
> vbe_simple_read_state - 100 +100
> vbe_read_version - 100 +100
> vbe_find_first_device - 96 +96
> uimage_phase - 96 +96
> ofnode_write_u32 - 96 +96
> md5_wd - 96 +96
> genimg_get_kernel_addr_fit 24 120 +96
> imgextract_help_text 85 178 +93
> board_init_r 572 664 +92
> vbe_find_next_device - 88 +88
> simple_read_nvdata - 88 +88
> sha256_starts - 88 +88
> fit_get_node_from_config - 88 +88
> bootmeth_get_bootflow - 88 +88
> bootflow_check - 88 +88
> bootmeth_setup_iter_order 284 368 +84
> bootmeth_glob_allowed - 84 +84
> board_init_f 1168 1252 +84
> image_info 208 288 +80
> get_table_entry_id - 80 +80
> fit_image_hash_get_value - 80 +80
> fit_image_get_emb_data - 80 +80
> fit_image_get_type - 76 +76
> fit_image_get_phase - 76 +76
> fit_image_get_os - 76 +76
> fit_image_get_arch - 76 +76
> bootflow_cmdline_set - 76 +76
> sha256_csum_wd - 72 +72
> fit_image_get_node - 72 +72
> fit_image_get_data_size - 72 +72
> fit_image_get_data_position - 72 +72
> fit_image_get_data_offset - 72 +72
> fit_image_get_comp - 68 +68
> vbe_reqs - 64 +64
> sha256_padding - 64 +64
> sha1_padding - 64 +64
> image_setup_libfdt 344 408 +64
> hash_finish_sha256 - 64 +64
> hash_finish_sha1 - 64 +64
> fit_image_check_type - 60 +60
> fit_image_check_os - 60 +60
> fit_image_check_comp - 60 +60
> fit_conf_get_prop_node_index - 60 +60
> do_source 84 144 +60
> crc32_wd_buf - 60 +60
> crc16_ccitt_wd_buf - 60 +60
> sha1_starts - 56 +56
> openwrt_bootmeth_ops - 56 +56
> oftree_path - 56 +56
> ofnode_write_string - 56 +56
> hash_finish_crc32 - 56 +56
> hash_finish_crc16_ccitt - 56 +56
> fit_image_hash_get_algo - 56 +56
> fit_get_desc - 56 +56
> bootmeth_vbe_simple_ops - 56 +56
> hash_update_crc32 - 52 +52
> hash_update_crc16_ccitt - 52 +52
> hash_init_sha256 - 52 +52
> hash_init_sha1 - 52 +52
> crc8 - 52 +52
> hash_init_crc32 - 48 +48
> hash_init_crc16_ccitt - 48 +48
> alist_uninit - 48 +48
> openwrt_bootmeth_bind - 44 +44
> bootmeth_vbe_simple_bind - 44 +44
> openwrt_check - 36 +36
> imagemap_post_probe - 36 +36
> hash_update_sha256 - 36 +36
> hash_update_sha1 - 36 +36
> vbe_simple_read_file - 32 +32
> openwrt_bootmeth_ids - 32 +32
> generic_simple_vbe_simple_ids - 32 +32
> bootflow_scan_first 236 264 +28
> genimg_get_type_id - 24 +24
> genimg_get_phase_name - 24 +24
> genimg_get_phase_id - 24 +24
> genimg_get_os_id - 24 +24
> genimg_get_comp_id - 24 +24
> genimg_get_arch_id - 24 +24
> fit_image_get_load - 16 +16
> fit_image_get_entry - 16 +16
> fit_get_end - 16 +16
> bootm_find_images 268 284 +16
> _u_boot_list_2_evspy_info_2_EVT_FT_FIXUP_3_bootmeth_vbe_simple_ft_fixup - 16 +16
> _u_boot_list_2_evspy_info_2_EVT_FT_FIXUP_3_bootmeth_vbe_ft_fixup - 16 +16
> sha256_update - 12 +12
> sha1_update - 12 +12
> genimg_get_kernel_addr 20 32 +12
> event_type_name - 12 +12
> event_notify_null - 12 +12
> vbe_simple_read_bootflow - 8 +8
> vbe_req_random_seed - 8 +8
> vbe_req_efi_runtime_rand - 8 +8
> vbe_req_aslr_rand - 8 +8
> vbe_req_aslr_move - 8 +8
> oftree_root - 8 +8
> genimg_has_config 8 16 +8
> boot_get_setup 8 4 -4
>
> This seems excessive, but it's mostly due to CONFIG_FIT not being enabled
> in the upstream U-Boot defconfig for the BananaPi R3.
>
> With CONFIG_FIT enabled already before, the changes are more reasonable:
> aarch64: (for 1/1 boards) all +6057.0 data +296.0 rodata +513.0 text +5248.0
> mt7986a_bpir3_emmc: all +6057 data +296 rodata +513 text +5248
> u-boot: add: 21/0, grow: 3/0 bytes: 5336/0 (5336)
> function old new delta
> fit_verity_build_params - 948 +948
> cmdline_set_arg - 688 +688
> imagemap_map - 640 +640
> openwrt_boot - 516 +516
> fit_image_load 1632 1980 +348
> copy_in - 228 +228
> imagemap_map_to - 212 +212
> openwrt_read_bootflow - 204 +204
> imagemap_record - 180 +180
> bootm_run_states 2256 2436 +180
> imagemap_create - 176 +176
> bootflow_cmdline_set_arg - 164 +164
> imagemap_cleanup - 136 +136
> _u_boot_list_2_uclass_driver_2_imagemap - 120 +120
> _u_boot_list_2_driver_2_bootmeth_openwrt - 120 +120
> imagemap_lookup - 112 +112
> bootflow_cmdline_set - 76 +76
> openwrt_bootmeth_ops - 56 +56
> alist_uninit - 48 +48
> openwrt_bootmeth_bind - 44 +44
> openwrt_check - 36 +36
> imagemap_post_probe - 36 +36
> fit_image_get_data 188 224 +36
> openwrt_bootmeth_ids - 32 +32
Adding 5K or so isn't unreasonable, but I'd still suggest looking around
to see if there's any easy optimizations.
> > There's:
> > https://lore.kernel.org/u-boot/20260217192000.GH2747538@bill-the-cat/
> > And I didn't see anything about that.
>
> Re: boot: add OpenWrt boot method and on-demand FIT loading
>
> (Re: not enabled on any board nor in sandbox)
> In my new series I'll add the OpenWrt One as a sample board and also
> enable the new features on the BananaPi R3.
OK, thanks.
> Should I also enable the imagemap base feature in sandbox as well so the
> unit tests can easily be run?
Absolutely.
> (Re: splitting series)
> There aren't any actual fixes part of the series. Of course, some
> smaller changes and refactoring to create the helpers needed by
> imagemap and the bootmeth could be broken out as indidivual patches
> sent separately in advance, but they wouldn't have any in-tree users
> at this point. Eg.
> - mtd: set flash_node on DT-created partitions
> - mtd: add mtd_read_skip_bad() helper
> - cmd: ubi: add ubi_part_from_mtd()
> - cmd: ubi: export ubi_find_volume()
Maybe I'm mis-recalling but some of the changes seemed to be worded /
implied they were fixing existing problems, not adding things to be used
later.
But also, those changes should have some impact on size growth? To make
sure to look at other platforms as well. The goal is not zero growth,
but rather explainable growth. I'm not asking you to do the world
before/after that I do, but making use of qconfig.py -f CONFIG_FOO to
find boards that enable code you're changing and then checking them and
seeing if it's tens of bytes or hundreds of bytes of growth on something
unsupported (and likely never supported) by OpenWrt is important.
> I also think it makes sense to discuss the new feature (not part of the
> original RFC series) to store dm-verity parameters for IH_TYPE_FILESYSTEM
> subimages in the FIT structure separately, and that would start with
> discussing the FIT spec amendment for it:
>
> https://github.com/open-source-firmware/flat-image-tree/pull/37
Yes, that's a separate discussion.
> > There's:
> > https://lore.kernel.org/u-boot/20260217190554.GF2747538@bill-the-cat/
>
> Re: test: boot: add image_loader unit tests
> (Re: AI fixes in the wrong commit)
> Yes, this was an oversight and I've of course folded in that change,
> and later, upon Simon's comment, switched to use alist instead of a
> fixed-size array anyway.
> > https://lore.kernel.org/u-boot/20260219153100.GL3233182@bill-the-cat/
> > Which go together and I think "human written only commit message" might
> > well be a good policy as it helps ensure the patch in question has been
> > read and summarized by the human author (and enforcement is on the
> > honor system).
>
> While it was very useful to use an LLM for quickly drafting the intial
> RFC and receive feedback for the overall approach, it became less and
> less useful at later stages -- instead of ending of micro-managing the
> LLM for each little change and fix it's easier to just do it yourself
> from some point on.
>
> So what you are going to see in v2 surely still got some onchanged lines
> of code, comments and parts of commit messages which have intially been
> fabricated with the help of the LLM, but most of it is by now the work
> of a human (me).
OK.
> Regarding the commit messages, I get the idea of enforcing a
> human-written message, however, when it comes to the use of English
> language (I'm not a native speaker) I think the use of things like
> deepl.com does create more benefit than potential damage even though it
> can also be considered an AI/LLM tool.
This is a project that was started by Germans and initially contributed
to by other Europeans and has an extremely long history of global and
non-native-English speakers contributing, I would much rather see some
imperfect English than overly polished and often useless LLM messages.
It is sometimes joked that LLMs speak great Manager-English, but that's
not what we want here.
> > Finally there's:
> > https://lore.kernel.org/u-boot/20260217191536.GG2747538@bill-the-cat/
> > where I was hoping for more feedback.
> Re: reuse imageloader (by now renamed to "imagemap") in SPL
>
> First of all, I don't have any boards which use SPL. All modern
> platforms I deal with use U-Boot as "Non-Trusted Firmware (BL33)" or
> the OpenSBI equivalent of that on platforms using RISC-V.
>
> Especially regarding the UBI part, the implementation intended for SPL
> is way too basic to be useful as imagemap backend.
>
> For BLK (ie. MMC) or SPI-NOR it would be a possibility to reuse
> imagemap in SPL -- but (as I've replied earlier) I don't think it
> makes sense to merge it with the exising FIT reader in SPL for the
> reasons I've explained:
> |> So there really isn't much overlap other than the fact that there is a
> |> struct with a priv pointer and a .read callback, and even that uses a
> |> different addressing parameter (sectors vs. bytes).
You should go back in to that thread, so the other interested parties
see this as well and also keep in mind that you're adding something that
may be useful in other cases, not that you should use what we have today
instead. Especially for the cases where people are trying to add
less-basic support in SPL for various flashes.
> > I don't recall if we came to a conclusion, or just look again later,
> > about if we should have this functionality both exposed via "bootm" and
> > the new distro bootmeth, or not. It might be "talk more" and I'm leaning
> > currently on saying the migration path is something openwrt holds
> > downstream while getting things migrated?
>
> That's also what I understood from our discussion. Adding new cmdline
> features to 'bootm' can be considered a liability, so I've dropped that
> from the v2 series and only use the direct-storage FIT loading in
> bootmeth_openwrt. We can still add the 'bootm' features as an
> intermediate step to easy migration of all our boards as downstream
> patch (and later remove it again once all boards and features work with
> the bootmeth).
OK, thanks.
> > And in sum, this series will need to be split up a bit I strongly
> > suspect. Being clear about the end goal is good but implementing each of
> > the backends as well in a single series will make this too large to
> > review.
>
> The current state of the series looks pretty much like what I've pushed
> to Github for the sake of getting CI exposure.
I don't look at all the emails it generates and sends to me, but it was
20 commits at least?
> Do you agree with the submission strategy drafted below?
>
> Commits (reverse git order, starting from the one directly
> on top of u-boot.git master branch, ie. in the order of
> the patches to be submitted starting with the first to be
> sent).
>
> SERIES 1: dm-verity paramters for FIT images
>
> - boot: fit: support generating DM verity cmdline parameters
> - doc: fit: add dm-verity boot parameter documentation
>
> To be discussed together with
> https://github.com/open-source-firmware/flat-image-tree/pull/37
This is a stand-alone thing yes, and should wait for that discussion to
be resolved / merged.
> ---
> SERIES 2: imagemap storage abstraction
>
> - boot: add imagemap on-demand loading abstraction
> - boot: imagemap: add block device backend
> - doc: imagemap: document on-demand loading framework
> - test: boot: add imagemap unit tests
>
> This would be a meaningful series together with enabling imagemap
> in sandbox so the unit tests are run there.
> ---
> SERIES 3: imagemap MTD and UBI backends
>
> - mtd: add mtd_read_skip_bad() helper
> - boot: imagemap: add MTD backend
> - mtd: set flash_node on DT-created partitions
> - cmd: ubi: export ubi_find_volume()
> - cmd: ubi: add ubi_part_from_mtd()
> - boot: imagemap: add UBI volume backend
>
> This would be one or two small series adding the backends.
> Maybe including another commit completing the documentation
> to cover the newly added backends.
> ---
> single PATCH
>
> - boot: fit: support on-demand loading in fit_image_load()
>
> This would be good to be discussed individually as it is
> the most intrusive change and needs the most eyeballs.
> ---
> SERIES 4: OpenWrt boot method
>
> - boot: bootmeth: add OpenWrt boot method skeleton
> - boot: bootmeth: openwrt: implement read_bootflow
> - boot: bootmeth: openwrt: implement boot via imagemap
> - boot: bootdev: add MTD boot device
> - boot: bootdev: add UBI boot device
> - doc: bootstd: add OpenWrt boot method documentation
> - board: mediatek: add OpenWrt One support
> - mt7986a-bpi-r3: add OpenWrt boot method
This kind of split doesn't help with review because series 2, 3 and 4
have to be seen as a whole for them to be useful and understood. It had
sounded like earlier there was support for block devices (eMMC/SD for
example) but that's not included yet? I was going to suggest excluding
them, or just including the smallest backend support to start with, and
follow-up with other backends. So I'd rather see 2, 3 and 4 as a single
series than split up, so that it's possible to understand real usage.
Thanks.
--
Tom
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 88+ messages in thread* Re: [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading
2026-02-25 22:16 ` Tom Rini
@ 2026-02-25 23:49 ` Daniel Golle
2026-02-26 18:45 ` Tom Rini
0 siblings, 1 reply; 88+ messages in thread
From: Daniel Golle @ 2026-02-25 23:49 UTC (permalink / raw)
To: Tom Rini
Cc: Simon Glass, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
[-- Attachment #1: Type: text/plain, Size: 19567 bytes --]
On Wed, Feb 25, 2026 at 04:16:11PM -0600, Tom Rini wrote:
> On Wed, Feb 25, 2026 at 02:34:05PM +0000, Daniel Golle wrote:
> > Thanks for digging all of them out. I took the freedom to answer all of
> > them here as it makes it easier to keep track of all the topics being in
> > a single thread.
> >
> > > https://lore.kernel.org/u-boot/20260217150820.GB2747538@bill-the-cat/
> > > And I see you're using CI, but I didn't see an ack about the size
> > > investigations portion.
> >
> > I had to remove the -E (errors-as-warnings) option from the script
> > because my host toolchain is too new and caused a few warnings about
> > discarded 'const' qualifiers. Most can be fixed by updating dtc to
> > that in upstream Linux, the other one is in
> > tools/atmelimage.c:64:31: error: assignment discards ‘const’ qualifier from pointer target type [-Werror=discarded-qualifiers]
> > and can also be fixed easily. I'll post the patch for that after I
> > send this email.
> >
> > Updating dtc, however, caused a cascade of new warnings in existing
> > device tree files, newly introduce node-name checks, ... so at that
> > point I decided to just remove the -E option passed from the script
> > to buildman...
> >
> > Results:
> > For any board having CONFIG_FIT enabled but none of the new features
> > there is a slight increase:
> > aarch64: (for 1/1 boards) all +8.0 text +8.0
> > mt7622_rfb : all +8 text +8
> > u-boot: add: 0/0, grow: 1/0 bytes: 8/0 (8)
> > function old new delta
> > fit_image_load 1632 1640 +8
> >
> > The increase by 8 bytes is caused by this hunk:
> > --- a/boot/image-fit.c
> > +++ b/boot/image-fit.c
> > @@ -2279,8 +2362,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 (data != load)
> > + printf(" Loading %s from 0x%08lx to 0x%08lx\n",
> > + prop_name, data, load);
> > } else {
> > load = data; /* No load address specified */
> > }
> >
> > It could obviously also be guarded by some
> > if (!CONFIG_IS_ENABLED(IMAGEMAP) || data != load)
> > so the compiler would end up with byte-identical code in case
> > CONFIG_IS_ENABLED(IMAGEMAP) is false.
> > Do you think that'd be worth it?
>
> So 8 byte growth here is fine, it would be more unreadable to avoid that
> than to accept it, and 8 bytes is nothing really.
As keeping things byte-identical in case !CONFIG_IS_ENABLED(IMAGEMAP)
I've added that additional condition, and now indeed end up with 0
size increase.
> > With CONFIG_FIT enabled already before, the changes are more reasonable:
> > aarch64: (for 1/1 boards) all +6057.0 data +296.0 rodata +513.0 text +5248.0
> > mt7986a_bpir3_emmc: all +6057 data +296 rodata +513 text +5248
> > u-boot: add: 21/0, grow: 3/0 bytes: 5336/0 (5336)
> > function old new delta
> > fit_verity_build_params - 948 +948
> > cmdline_set_arg - 688 +688
> > imagemap_map - 640 +640
> > openwrt_boot - 516 +516
> > fit_image_load 1632 1980 +348
> > copy_in - 228 +228
> > imagemap_map_to - 212 +212
> > openwrt_read_bootflow - 204 +204
> > imagemap_record - 180 +180
> > bootm_run_states 2256 2436 +180
> > imagemap_create - 176 +176
> > bootflow_cmdline_set_arg - 164 +164
> > imagemap_cleanup - 136 +136
> > _u_boot_list_2_uclass_driver_2_imagemap - 120 +120
> > _u_boot_list_2_driver_2_bootmeth_openwrt - 120 +120
> > imagemap_lookup - 112 +112
> > bootflow_cmdline_set - 76 +76
> > openwrt_bootmeth_ops - 56 +56
> > alist_uninit - 48 +48
> > openwrt_bootmeth_bind - 44 +44
> > openwrt_check - 36 +36
> > imagemap_post_probe - 36 +36
> > fit_image_get_data 188 224 +36
> > openwrt_bootmeth_ids - 32 +32
>
> Adding 5K or so isn't unreasonable, but I'd still suggest looking around
> to see if there's any easy optimizations.
One thing which I'm anaway not interely decided about is whether to
build fit_verity_build_params() (the function which reads the dm-verity
parameters from FIT and generates the Linux cmdline arguments from that)
on top of bootflow_cmdline_set_arg() or to just generate the whole
string and use env_get(), env_set() on the "bootargs" env variable.
Not using bootflow_cmdline_set_arg() would also mean that in the case of
bootmeth_openwrt nothing would be using it, and that would free 688
bytes, while fit_verity_build_params() might get a very tiny bit larger,
certainly less than that.
The thing is that it looks to me like bootflow/bootmeth is meant to ignore
"bootargs" and overwrites it just before boot. On the other hand, having
a generic feature such as generting the dm-mod.create=... Linux kernel
cmdline parameter from the values stored in FIT depend on bootmeth, or
even a specific bootmeth, also seems wrong.
I'd be happy to hear your opinion about that.
>
> > > There's:
> > > https://lore.kernel.org/u-boot/20260217192000.GH2747538@bill-the-cat/
> > > And I didn't see anything about that.
> >
> > Re: boot: add OpenWrt boot method and on-demand FIT loading
> >
> > (Re: not enabled on any board nor in sandbox)
> > In my new series I'll add the OpenWrt One as a sample board and also
> > enable the new features on the BananaPi R3.
>
> OK, thanks.
>
> > Should I also enable the imagemap base feature in sandbox as well so the
> > unit tests can easily be run?
>
> Absolutely.
>
> > (Re: splitting series)
> > There aren't any actual fixes part of the series. Of course, some
> > smaller changes and refactoring to create the helpers needed by
> > imagemap and the bootmeth could be broken out as indidivual patches
> > sent separately in advance, but they wouldn't have any in-tree users
> > at this point. Eg.
> > - mtd: set flash_node on DT-created partitions
> > - mtd: add mtd_read_skip_bad() helper
> > - cmd: ubi: add ubi_part_from_mtd()
> > - cmd: ubi: export ubi_find_volume()
>
> Maybe I'm mis-recalling but some of the changes seemed to be worded /
> implied they were fixing existing problems, not adding things to be used
> later.
"mtd: set flash_node on DT-created partitions"[1] could be considered a
fix, however, up to now nothing within U-Boot was ever interested in the
of_node of an MTD partition, so because of that it also doesn't really
fix anything. After I had already posted the RFC series I found a
theoretical infinite-loop issue when all blocks are bad which also
exists in the original code, and the new mtd_read_skip_bad() helper
fixes that.
All the rest is adding helpers or refactoring existing code into
reusable helpers, and then adding stuff on top of that using them. Plus
documentation, unit tests and all that.
[1]: https://patchwork.ozlabs.org/project/uboot/patch/e21bf2c12eb5a4b7954f50c8739655d749e6d38e.1771275704.git.daniel@makrotopia.org/
>
> But also, those changes should have some impact on size growth? To make
> sure to look at other platforms as well. The goal is not zero growth,
> but rather explainable growth. I'm not asking you to do the world
> before/after that I do, but making use of qconfig.py -f CONFIG_FOO to
> find boards that enable code you're changing and then checking them and
> seeing if it's tens of bytes or hundreds of bytes of growth on something
> unsupported (and likely never supported) by OpenWrt is important.
Yes, boards using CONFIG_MTD=y or CONFIG_UBI=y will see minimal increase:
mt7987_emmc_rfb: all +552 text +552
u-boot: add: 3/0, grow: 2/0 bytes: 528/0 (528)
function old new delta
mtd_read_skip_bad - 256 +256
mtd_read - 196 +196
do_mtd_io 1624 1688 +64
mtd_post_bind - 8 +8
add_mtd_partitions_of 400 404 +4
mvebu_ac5_rd : all +48 text +48
u-boot: add: 1/0, grow: 2/-1 bytes: 104/-56 (48)
function old new delta
static.ubi_dev_scan - 88 +88
ubi_find_volume 124 136 +12
add_mtd_partitions_of 400 404 +4
ubi_part 280 224 -56
The groth of the boards using CONFIG_MTD seems larger than I would have thought,
I'll figure out that is happening there and try to reduce the binary growth,
especially the mtd_read() function should be smaller and not bigger now that
it calls mtd_read_skip_bad() -- but may calling a function has a larger footprint
than the actual code itself. In every case, 196 bytes still seems too much.
>
> > I also think it makes sense to discuss the new feature (not part of the
> > original RFC series) to store dm-verity parameters for IH_TYPE_FILESYSTEM
> > subimages in the FIT structure separately, and that would start with
> > discussing the FIT spec amendment for it:
> >
> > https://github.com/open-source-firmware/flat-image-tree/pull/37
>
> Yes, that's a separate discussion.
>
> > > There's:
> > > https://lore.kernel.org/u-boot/20260217190554.GF2747538@bill-the-cat/
> >
> > Re: test: boot: add image_loader unit tests
> > (Re: AI fixes in the wrong commit)
> > Yes, this was an oversight and I've of course folded in that change,
> > and later, upon Simon's comment, switched to use alist instead of a
> > fixed-size array anyway.
>
> > > https://lore.kernel.org/u-boot/20260219153100.GL3233182@bill-the-cat/
> > > Which go together and I think "human written only commit message" might
> > > well be a good policy as it helps ensure the patch in question has been
> > > read and summarized by the human author (and enforcement is on the
> > > honor system).
> >
> > While it was very useful to use an LLM for quickly drafting the intial
> > RFC and receive feedback for the overall approach, it became less and
> > less useful at later stages -- instead of ending of micro-managing the
> > LLM for each little change and fix it's easier to just do it yourself
> > from some point on.
> >
> > So what you are going to see in v2 surely still got some onchanged lines
> > of code, comments and parts of commit messages which have intially been
> > fabricated with the help of the LLM, but most of it is by now the work
> > of a human (me).
>
> OK.
>
> > Regarding the commit messages, I get the idea of enforcing a
> > human-written message, however, when it comes to the use of English
> > language (I'm not a native speaker) I think the use of things like
> > deepl.com does create more benefit than potential damage even though it
> > can also be considered an AI/LLM tool.
>
> This is a project that was started by Germans and initially contributed
> to by other Europeans and has an extremely long history of global and
> non-native-English speakers contributing, I would much rather see some
> imperfect English than overly polished and often useless LLM messages.
>
> It is sometimes joked that LLMs speak great Manager-English, but that's
> not what we want here.
No but we do want accuracy, and sometimes using ones native language to
describe something throughing that into a machine translator can produce
more accurate results than trying to use a foreign language directly.
I sometimes do both and then compare the outcomes, for example.
>
> > > Finally there's:
> > > https://lore.kernel.org/u-boot/20260217191536.GG2747538@bill-the-cat/
> > > where I was hoping for more feedback.
> > Re: reuse imageloader (by now renamed to "imagemap") in SPL
> >
> > First of all, I don't have any boards which use SPL. All modern
> > platforms I deal with use U-Boot as "Non-Trusted Firmware (BL33)" or
> > the OpenSBI equivalent of that on platforms using RISC-V.
> >
> > Especially regarding the UBI part, the implementation intended for SPL
> > is way too basic to be useful as imagemap backend.
> >
> > For BLK (ie. MMC) or SPI-NOR it would be a possibility to reuse
> > imagemap in SPL -- but (as I've replied earlier) I don't think it
> > makes sense to merge it with the exising FIT reader in SPL for the
> > reasons I've explained:
> > |> So there really isn't much overlap other than the fact that there is a
> > |> struct with a priv pointer and a .read callback, and even that uses a
> > |> different addressing parameter (sectors vs. bytes).
>
> You should go back in to that thread, so the other interested parties
> see this as well and also keep in mind that you're adding something that
> may be useful in other cases, not that you should use what we have today
> instead. Especially for the cases where people are trying to add
> less-basic support in SPL for various flashes.
The quote above *is* from the original thread. Or do you mean the
threads you have linked in the reply above?
Building imagemap with SPL in mind or not still makes a difference when
it comes to depending on LMB or alist imho (both come very handy, and as
discussed earlier, open-coding a naive allocator or using a fixed-size
array comes with disadvantages and risks).
>
> > > I don't recall if we came to a conclusion, or just look again later,
> > > about if we should have this functionality both exposed via "bootm" and
> > > the new distro bootmeth, or not. It might be "talk more" and I'm leaning
> > > currently on saying the migration path is something openwrt holds
> > > downstream while getting things migrated?
> >
> > That's also what I understood from our discussion. Adding new cmdline
> > features to 'bootm' can be considered a liability, so I've dropped that
> > from the v2 series and only use the direct-storage FIT loading in
> > bootmeth_openwrt. We can still add the 'bootm' features as an
> > intermediate step to easy migration of all our boards as downstream
> > patch (and later remove it again once all boards and features work with
> > the bootmeth).
>
> OK, thanks.
>
> > > And in sum, this series will need to be split up a bit I strongly
> > > suspect. Being clear about the end goal is good but implementing each of
> > > the backends as well in a single series will make this too large to
> > > review.
> >
> > The current state of the series looks pretty much like what I've pushed
> > to Github for the sake of getting CI exposure.
>
> I don't look at all the emails it generates and sends to me, but it was
> 20 commits at least?
Yes, it's 22 commits by now. Our of which 3 are documentation, 1 is for the
unit tests, and 3 are for adding/modifying boards to use the new features.
So 15 out of 22 actually adding/touching code.
>
> > Do you agree with the submission strategy drafted below?
> >
> > Commits (reverse git order, starting from the one directly
> > on top of u-boot.git master branch, ie. in the order of
> > the patches to be submitted starting with the first to be
> > sent).
> >
> > SERIES 1: dm-verity paramters for FIT images
> >
> > - boot: fit: support generating DM verity cmdline parameters
> > - doc: fit: add dm-verity boot parameter documentation
> >
> > To be discussed together with
> > https://github.com/open-source-firmware/flat-image-tree/pull/37
>
> This is a stand-alone thing yes, and should wait for that discussion to
> be resolved / merged.
>
> > ---
> > SERIES 2: imagemap storage abstraction
> >
> > - boot: add imagemap on-demand loading abstraction
> > - boot: imagemap: add block device backend
> > - doc: imagemap: document on-demand loading framework
> > - test: boot: add imagemap unit tests
> >
> > This would be a meaningful series together with enabling imagemap
> > in sandbox so the unit tests are run there.
> > ---
> > SERIES 3: imagemap MTD and UBI backends
> >
> > - mtd: add mtd_read_skip_bad() helper
> > - boot: imagemap: add MTD backend
> > - mtd: set flash_node on DT-created partitions
> > - cmd: ubi: export ubi_find_volume()
> > - cmd: ubi: add ubi_part_from_mtd()
> > - boot: imagemap: add UBI volume backend
> >
> > This would be one or two small series adding the backends.
> > Maybe including another commit completing the documentation
> > to cover the newly added backends.
> > ---
> > single PATCH
> >
> > - boot: fit: support on-demand loading in fit_image_load()
> >
> > This would be good to be discussed individually as it is
> > the most intrusive change and needs the most eyeballs.
> > ---
> > SERIES 4: OpenWrt boot method
> >
> > - boot: bootmeth: add OpenWrt boot method skeleton
> > - boot: bootmeth: openwrt: implement read_bootflow
> > - boot: bootmeth: openwrt: implement boot via imagemap
> > - boot: bootdev: add MTD boot device
> > - boot: bootdev: add UBI boot device
> > - doc: bootstd: add OpenWrt boot method documentation
> > - board: mediatek: add OpenWrt One support
> > - mt7986a-bpi-r3: add OpenWrt boot method
>
> This kind of split doesn't help with review because series 2, 3 and 4
> have to be seen as a whole for them to be useful and understood. It had
> sounded like earlier there was support for block devices (eMMC/SD for
> example) but that's not included yet? I was going to suggest excluding
> them, or just including the smallest backend support to start with, and
> follow-up with other backends. So I'd rather see 2, 3 and 4 as a single
> series than split up, so that it's possible to understand real usage.
> Thanks.
Another way could be to skip series 3 (imagemap MTD and UBI backends)
as well as the MTD and UBI related patches in series 4, and then
have 2 follow-up series adding MTD and UBI features respectively to
imagemap and bootdev/bootmeth_openwrt.
In that way you'd end up with something complete and useful on the
shortest path, and more features (MTD, UBI) are added on top after.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 88+ messages in thread* Re: [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading
2026-02-25 23:49 ` Daniel Golle
@ 2026-02-26 18:45 ` Tom Rini
2026-02-26 23:44 ` Simon Glass
0 siblings, 1 reply; 88+ messages in thread
From: Tom Rini @ 2026-02-26 18:45 UTC (permalink / raw)
To: Daniel Golle
Cc: Simon Glass, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
[-- Attachment #1: Type: text/plain, Size: 21478 bytes --]
On Wed, Feb 25, 2026 at 11:49:23PM +0000, Daniel Golle wrote:
> On Wed, Feb 25, 2026 at 04:16:11PM -0600, Tom Rini wrote:
> > On Wed, Feb 25, 2026 at 02:34:05PM +0000, Daniel Golle wrote:
> > > Thanks for digging all of them out. I took the freedom to answer all of
> > > them here as it makes it easier to keep track of all the topics being in
> > > a single thread.
> > >
> > > > https://lore.kernel.org/u-boot/20260217150820.GB2747538@bill-the-cat/
> > > > And I see you're using CI, but I didn't see an ack about the size
> > > > investigations portion.
> > >
> > > I had to remove the -E (errors-as-warnings) option from the script
> > > because my host toolchain is too new and caused a few warnings about
> > > discarded 'const' qualifiers. Most can be fixed by updating dtc to
> > > that in upstream Linux, the other one is in
> > > tools/atmelimage.c:64:31: error: assignment discards ‘const’ qualifier from pointer target type [-Werror=discarded-qualifiers]
> > > and can also be fixed easily. I'll post the patch for that after I
> > > send this email.
> > >
> > > Updating dtc, however, caused a cascade of new warnings in existing
> > > device tree files, newly introduce node-name checks, ... so at that
> > > point I decided to just remove the -E option passed from the script
> > > to buildman...
> > >
> > > Results:
> > > For any board having CONFIG_FIT enabled but none of the new features
> > > there is a slight increase:
> > > aarch64: (for 1/1 boards) all +8.0 text +8.0
> > > mt7622_rfb : all +8 text +8
> > > u-boot: add: 0/0, grow: 1/0 bytes: 8/0 (8)
> > > function old new delta
> > > fit_image_load 1632 1640 +8
> > >
> > > The increase by 8 bytes is caused by this hunk:
> > > --- a/boot/image-fit.c
> > > +++ b/boot/image-fit.c
> > > @@ -2279,8 +2362,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 (data != load)
> > > + printf(" Loading %s from 0x%08lx to 0x%08lx\n",
> > > + prop_name, data, load);
> > > } else {
> > > load = data; /* No load address specified */
> > > }
> > >
> > > It could obviously also be guarded by some
> > > if (!CONFIG_IS_ENABLED(IMAGEMAP) || data != load)
> > > so the compiler would end up with byte-identical code in case
> > > CONFIG_IS_ENABLED(IMAGEMAP) is false.
> > > Do you think that'd be worth it?
> >
> > So 8 byte growth here is fine, it would be more unreadable to avoid that
> > than to accept it, and 8 bytes is nothing really.
>
> As keeping things byte-identical in case !CONFIG_IS_ENABLED(IMAGEMAP)
> I've added that additional condition, and now indeed end up with 0
> size increase.
OK.
> > > With CONFIG_FIT enabled already before, the changes are more reasonable:
> > > aarch64: (for 1/1 boards) all +6057.0 data +296.0 rodata +513.0 text +5248.0
> > > mt7986a_bpir3_emmc: all +6057 data +296 rodata +513 text +5248
> > > u-boot: add: 21/0, grow: 3/0 bytes: 5336/0 (5336)
> > > function old new delta
> > > fit_verity_build_params - 948 +948
> > > cmdline_set_arg - 688 +688
> > > imagemap_map - 640 +640
> > > openwrt_boot - 516 +516
> > > fit_image_load 1632 1980 +348
> > > copy_in - 228 +228
> > > imagemap_map_to - 212 +212
> > > openwrt_read_bootflow - 204 +204
> > > imagemap_record - 180 +180
> > > bootm_run_states 2256 2436 +180
> > > imagemap_create - 176 +176
> > > bootflow_cmdline_set_arg - 164 +164
> > > imagemap_cleanup - 136 +136
> > > _u_boot_list_2_uclass_driver_2_imagemap - 120 +120
> > > _u_boot_list_2_driver_2_bootmeth_openwrt - 120 +120
> > > imagemap_lookup - 112 +112
> > > bootflow_cmdline_set - 76 +76
> > > openwrt_bootmeth_ops - 56 +56
> > > alist_uninit - 48 +48
> > > openwrt_bootmeth_bind - 44 +44
> > > openwrt_check - 36 +36
> > > imagemap_post_probe - 36 +36
> > > fit_image_get_data 188 224 +36
> > > openwrt_bootmeth_ids - 32 +32
> >
> > Adding 5K or so isn't unreasonable, but I'd still suggest looking around
> > to see if there's any easy optimizations.
>
>
> One thing which I'm anaway not interely decided about is whether to
> build fit_verity_build_params() (the function which reads the dm-verity
> parameters from FIT and generates the Linux cmdline arguments from that)
> on top of bootflow_cmdline_set_arg() or to just generate the whole
> string and use env_get(), env_set() on the "bootargs" env variable.
>
>
> Not using bootflow_cmdline_set_arg() would also mean that in the case of
> bootmeth_openwrt nothing would be using it, and that would free 688
> bytes, while fit_verity_build_params() might get a very tiny bit larger,
> certainly less than that.
>
> The thing is that it looks to me like bootflow/bootmeth is meant to ignore
> "bootargs" and overwrites it just before boot. On the other hand, having
> a generic feature such as generting the dm-mod.create=... Linux kernel
> cmdline parameter from the values stored in FIT depend on bootmeth, or
> even a specific bootmeth, also seems wrong.
>
> I'd be happy to hear your opinion about that.
It should probably exist outside of the bootflow/bootmeth code, yes. As
we start expanding the real usage of bootflow/bootmeth we do need to
make sure that it's not doing things that violate user expectations, so
I'm not 100% sure if we are or aren't doing anything odd with bootargs,
depending on the flow.
> > > > There's:
> > > > https://lore.kernel.org/u-boot/20260217192000.GH2747538@bill-the-cat/
> > > > And I didn't see anything about that.
> > >
> > > Re: boot: add OpenWrt boot method and on-demand FIT loading
> > >
> > > (Re: not enabled on any board nor in sandbox)
> > > In my new series I'll add the OpenWrt One as a sample board and also
> > > enable the new features on the BananaPi R3.
> >
> > OK, thanks.
> >
> > > Should I also enable the imagemap base feature in sandbox as well so the
> > > unit tests can easily be run?
> >
> > Absolutely.
> >
> > > (Re: splitting series)
> > > There aren't any actual fixes part of the series. Of course, some
> > > smaller changes and refactoring to create the helpers needed by
> > > imagemap and the bootmeth could be broken out as indidivual patches
> > > sent separately in advance, but they wouldn't have any in-tree users
> > > at this point. Eg.
> > > - mtd: set flash_node on DT-created partitions
> > > - mtd: add mtd_read_skip_bad() helper
> > > - cmd: ubi: add ubi_part_from_mtd()
> > > - cmd: ubi: export ubi_find_volume()
> >
> > Maybe I'm mis-recalling but some of the changes seemed to be worded /
> > implied they were fixing existing problems, not adding things to be used
> > later.
>
> "mtd: set flash_node on DT-created partitions"[1] could be considered a
> fix, however, up to now nothing within U-Boot was ever interested in the
> of_node of an MTD partition, so because of that it also doesn't really
> fix anything. After I had already posted the RFC series I found a
> theoretical infinite-loop issue when all blocks are bad which also
> exists in the original code, and the new mtd_read_skip_bad() helper
> fixes that.
>
> All the rest is adding helpers or refactoring existing code into
> reusable helpers, and then adding stuff on top of that using them. Plus
> documentation, unit tests and all that.
>
> [1]: https://patchwork.ozlabs.org/project/uboot/patch/e21bf2c12eb5a4b7954f50c8739655d749e6d38e.1771275704.git.daniel@makrotopia.org/
OK. I think this really falls under reviewing the commit messages then.
> > But also, those changes should have some impact on size growth? To make
> > sure to look at other platforms as well. The goal is not zero growth,
> > but rather explainable growth. I'm not asking you to do the world
> > before/after that I do, but making use of qconfig.py -f CONFIG_FOO to
> > find boards that enable code you're changing and then checking them and
> > seeing if it's tens of bytes or hundreds of bytes of growth on something
> > unsupported (and likely never supported) by OpenWrt is important.
>
> Yes, boards using CONFIG_MTD=y or CONFIG_UBI=y will see minimal increase:
>
> mt7987_emmc_rfb: all +552 text +552
> u-boot: add: 3/0, grow: 2/0 bytes: 528/0 (528)
> function old new delta
> mtd_read_skip_bad - 256 +256
> mtd_read - 196 +196
> do_mtd_io 1624 1688 +64
> mtd_post_bind - 8 +8
> add_mtd_partitions_of 400 404 +4
>
>
> mvebu_ac5_rd : all +48 text +48
> u-boot: add: 1/0, grow: 2/-1 bytes: 104/-56 (48)
> function old new delta
> static.ubi_dev_scan - 88 +88
> ubi_find_volume 124 136 +12
> add_mtd_partitions_of 400 404 +4
> ubi_part 280 224 -56
>
> The groth of the boards using CONFIG_MTD seems larger than I would have thought,
> I'll figure out that is happening there and try to reduce the binary growth,
> especially the mtd_read() function should be smaller and not bigger now that
> it calls mtd_read_skip_bad() -- but may calling a function has a larger footprint
> than the actual code itself. In every case, 196 bytes still seems too much.
Thanks.
> > > I also think it makes sense to discuss the new feature (not part of the
> > > original RFC series) to store dm-verity parameters for IH_TYPE_FILESYSTEM
> > > subimages in the FIT structure separately, and that would start with
> > > discussing the FIT spec amendment for it:
> > >
> > > https://github.com/open-source-firmware/flat-image-tree/pull/37
> >
> > Yes, that's a separate discussion.
> >
> > > > There's:
> > > > https://lore.kernel.org/u-boot/20260217190554.GF2747538@bill-the-cat/
> > >
> > > Re: test: boot: add image_loader unit tests
> > > (Re: AI fixes in the wrong commit)
> > > Yes, this was an oversight and I've of course folded in that change,
> > > and later, upon Simon's comment, switched to use alist instead of a
> > > fixed-size array anyway.
> >
> > > > https://lore.kernel.org/u-boot/20260219153100.GL3233182@bill-the-cat/
> > > > Which go together and I think "human written only commit message" might
> > > > well be a good policy as it helps ensure the patch in question has been
> > > > read and summarized by the human author (and enforcement is on the
> > > > honor system).
> > >
> > > While it was very useful to use an LLM for quickly drafting the intial
> > > RFC and receive feedback for the overall approach, it became less and
> > > less useful at later stages -- instead of ending of micro-managing the
> > > LLM for each little change and fix it's easier to just do it yourself
> > > from some point on.
> > >
> > > So what you are going to see in v2 surely still got some onchanged lines
> > > of code, comments and parts of commit messages which have intially been
> > > fabricated with the help of the LLM, but most of it is by now the work
> > > of a human (me).
> >
> > OK.
> >
> > > Regarding the commit messages, I get the idea of enforcing a
> > > human-written message, however, when it comes to the use of English
> > > language (I'm not a native speaker) I think the use of things like
> > > deepl.com does create more benefit than potential damage even though it
> > > can also be considered an AI/LLM tool.
> >
> > This is a project that was started by Germans and initially contributed
> > to by other Europeans and has an extremely long history of global and
> > non-native-English speakers contributing, I would much rather see some
> > imperfect English than overly polished and often useless LLM messages.
> >
> > It is sometimes joked that LLMs speak great Manager-English, but that's
> > not what we want here.
>
> No but we do want accuracy, and sometimes using ones native language to
> describe something throughing that into a machine translator can produce
> more accurate results than trying to use a foreign language directly.
> I sometimes do both and then compare the outcomes, for example.
Right. At the end of the day, getting a word choice or two from whatever
(it's hard to find translation sites that aren't LLM rather than
dictionary these days) is fine. But LLM drafted and then human tweaked
isn't right. That's the difference I'm trying to get it. And for what
it's worth, my own experiences with translating are a lot more mixed
quality.
> > > > Finally there's:
> > > > https://lore.kernel.org/u-boot/20260217191536.GG2747538@bill-the-cat/
> > > > where I was hoping for more feedback.
> > > Re: reuse imageloader (by now renamed to "imagemap") in SPL
> > >
> > > First of all, I don't have any boards which use SPL. All modern
> > > platforms I deal with use U-Boot as "Non-Trusted Firmware (BL33)" or
> > > the OpenSBI equivalent of that on platforms using RISC-V.
> > >
> > > Especially regarding the UBI part, the implementation intended for SPL
> > > is way too basic to be useful as imagemap backend.
> > >
> > > For BLK (ie. MMC) or SPI-NOR it would be a possibility to reuse
> > > imagemap in SPL -- but (as I've replied earlier) I don't think it
> > > makes sense to merge it with the exising FIT reader in SPL for the
> > > reasons I've explained:
> > > |> So there really isn't much overlap other than the fact that there is a
> > > |> struct with a priv pointer and a .read callback, and even that uses a
> > > |> different addressing parameter (sectors vs. bytes).
> >
> > You should go back in to that thread, so the other interested parties
> > see this as well and also keep in mind that you're adding something that
> > may be useful in other cases, not that you should use what we have today
> > instead. Especially for the cases where people are trying to add
> > less-basic support in SPL for various flashes.
>
> The quote above *is* from the original thread. Or do you mean the
> threads you have linked in the reply above?
Yes, I mean go back and reply in that thread, which has a different set
of people on CC please.
> Building imagemap with SPL in mind or not still makes a difference when
> it comes to depending on LMB or alist imho (both come very handy, and as
> discussed earlier, open-coding a naive allocator or using a fixed-size
> array comes with disadvantages and risks).
>
> >
> > > > I don't recall if we came to a conclusion, or just look again later,
> > > > about if we should have this functionality both exposed via "bootm" and
> > > > the new distro bootmeth, or not. It might be "talk more" and I'm leaning
> > > > currently on saying the migration path is something openwrt holds
> > > > downstream while getting things migrated?
> > >
> > > That's also what I understood from our discussion. Adding new cmdline
> > > features to 'bootm' can be considered a liability, so I've dropped that
> > > from the v2 series and only use the direct-storage FIT loading in
> > > bootmeth_openwrt. We can still add the 'bootm' features as an
> > > intermediate step to easy migration of all our boards as downstream
> > > patch (and later remove it again once all boards and features work with
> > > the bootmeth).
> >
> > OK, thanks.
> >
> > > > And in sum, this series will need to be split up a bit I strongly
> > > > suspect. Being clear about the end goal is good but implementing each of
> > > > the backends as well in a single series will make this too large to
> > > > review.
> > >
> > > The current state of the series looks pretty much like what I've pushed
> > > to Github for the sake of getting CI exposure.
> >
> > I don't look at all the emails it generates and sends to me, but it was
> > 20 commits at least?
>
> Yes, it's 22 commits by now. Our of which 3 are documentation, 1 is for the
> unit tests, and 3 are for adding/modifying boards to use the new features.
> So 15 out of 22 actually adding/touching code.
>
> >
> > > Do you agree with the submission strategy drafted below?
> > >
> > > Commits (reverse git order, starting from the one directly
> > > on top of u-boot.git master branch, ie. in the order of
> > > the patches to be submitted starting with the first to be
> > > sent).
> > >
> > > SERIES 1: dm-verity paramters for FIT images
> > >
> > > - boot: fit: support generating DM verity cmdline parameters
> > > - doc: fit: add dm-verity boot parameter documentation
> > >
> > > To be discussed together with
> > > https://github.com/open-source-firmware/flat-image-tree/pull/37
> >
> > This is a stand-alone thing yes, and should wait for that discussion to
> > be resolved / merged.
> >
> > > ---
> > > SERIES 2: imagemap storage abstraction
> > >
> > > - boot: add imagemap on-demand loading abstraction
> > > - boot: imagemap: add block device backend
> > > - doc: imagemap: document on-demand loading framework
> > > - test: boot: add imagemap unit tests
> > >
> > > This would be a meaningful series together with enabling imagemap
> > > in sandbox so the unit tests are run there.
> > > ---
> > > SERIES 3: imagemap MTD and UBI backends
> > >
> > > - mtd: add mtd_read_skip_bad() helper
> > > - boot: imagemap: add MTD backend
> > > - mtd: set flash_node on DT-created partitions
> > > - cmd: ubi: export ubi_find_volume()
> > > - cmd: ubi: add ubi_part_from_mtd()
> > > - boot: imagemap: add UBI volume backend
> > >
> > > This would be one or two small series adding the backends.
> > > Maybe including another commit completing the documentation
> > > to cover the newly added backends.
> > > ---
> > > single PATCH
> > >
> > > - boot: fit: support on-demand loading in fit_image_load()
> > >
> > > This would be good to be discussed individually as it is
> > > the most intrusive change and needs the most eyeballs.
> > > ---
> > > SERIES 4: OpenWrt boot method
> > >
> > > - boot: bootmeth: add OpenWrt boot method skeleton
> > > - boot: bootmeth: openwrt: implement read_bootflow
> > > - boot: bootmeth: openwrt: implement boot via imagemap
> > > - boot: bootdev: add MTD boot device
> > > - boot: bootdev: add UBI boot device
> > > - doc: bootstd: add OpenWrt boot method documentation
> > > - board: mediatek: add OpenWrt One support
> > > - mt7986a-bpi-r3: add OpenWrt boot method
> >
> > This kind of split doesn't help with review because series 2, 3 and 4
> > have to be seen as a whole for them to be useful and understood. It had
> > sounded like earlier there was support for block devices (eMMC/SD for
> > example) but that's not included yet? I was going to suggest excluding
> > them, or just including the smallest backend support to start with, and
> > follow-up with other backends. So I'd rather see 2, 3 and 4 as a single
> > series than split up, so that it's possible to understand real usage.
> > Thanks.
>
> Another way could be to skip series 3 (imagemap MTD and UBI backends)
> as well as the MTD and UBI related patches in series 4, and then
> have 2 follow-up series adding MTD and UBI features respectively to
> imagemap and bootdev/bootmeth_openwrt.
>
> In that way you'd end up with something complete and useful on the
> shortest path, and more features (MTD, UBI) are added on top after.
OK. What I want to avoid is a series where we introduce imagemap but
can't really evaluate it because there's also not users present (which
wasn't an issue so much when it also was added to "bootm").
--
Tom
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 88+ messages in thread* Re: [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading
2026-02-26 18:45 ` Tom Rini
@ 2026-02-26 23:44 ` Simon Glass
0 siblings, 0 replies; 88+ messages in thread
From: Simon Glass @ 2026-02-26 23:44 UTC (permalink / raw)
To: Tom Rini
Cc: Daniel Golle, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
Hi,
On Thu, 26 Feb 2026 at 11:45, Tom Rini <trini@konsulko.com> wrote:
>
> On Wed, Feb 25, 2026 at 11:49:23PM +0000, Daniel Golle wrote:
> > On Wed, Feb 25, 2026 at 04:16:11PM -0600, Tom Rini wrote:
> > > On Wed, Feb 25, 2026 at 02:34:05PM +0000, Daniel Golle wrote:
[..]
> > One thing which I'm anaway not interely decided about is whether to
> > build fit_verity_build_params() (the function which reads the dm-verity
> > parameters from FIT and generates the Linux cmdline arguments from that)
> > on top of bootflow_cmdline_set_arg() or to just generate the whole
> > string and use env_get(), env_set() on the "bootargs" env variable.
> >
> >
> > Not using bootflow_cmdline_set_arg() would also mean that in the case of
> > bootmeth_openwrt nothing would be using it, and that would free 688
> > bytes, while fit_verity_build_params() might get a very tiny bit larger,
> > certainly less than that.
> >
> > The thing is that it looks to me like bootflow/bootmeth is meant to ignore
> > "bootargs" and overwrites it just before boot. On the other hand, having
> > a generic feature such as generting the dm-mod.create=... Linux kernel
> > cmdline parameter from the values stored in FIT depend on bootmeth, or
> > even a specific bootmeth, also seems wrong.
> >
> > I'd be happy to hear your opinion about that.
>
> It should probably exist outside of the bootflow/bootmeth code, yes. As
> we start expanding the real usage of bootflow/bootmeth we do need to
> make sure that it's not doing things that violate user expectations, so
> I'm not 100% sure if we are or aren't doing anything odd with bootargs,
> depending on the flow.
We do have 'bootflow read' which reads the files and sets things up
ready to boot. Bootstd is designed to keep the bootargs env var
aligned which whatever is the current bootflow (bootflow select).
But yes, it is useful to be able to do things 'manually' and we should
try to avoid something which is automatic but cannot be decomposed for
debugging, etc.
[..]
Regards,
Simon
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading
2026-02-16 21:21 [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading Daniel Golle
` (23 preceding siblings ...)
2026-02-17 17:46 ` Tom Rini
@ 2026-02-17 18:13 ` Tom Rini
2026-02-17 19:28 ` Daniel Golle
2026-02-17 19:20 ` Tom Rini
25 siblings, 1 reply; 88+ messages in thread
From: Tom Rini @ 2026-02-17 18:13 UTC (permalink / raw)
To: Daniel Golle
Cc: Simon Glass, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
[-- Attachment #1: Type: text/plain, Size: 1612 bytes --]
On Mon, Feb 16, 2026 at 09:21:14PM +0000, Daniel Golle wrote:
> Hi all,
>
> This RFC series adds a new boot method for OpenWrt's "uImage.FIT with
> embedded rootfs" firmware model, along with the underlying infrastructure
> to load FIT images on-demand directly from storage devices without copying
> them entirely to RAM first.
>
> I would like to discuss the design with U-Boot maintainers and fellow
> OpenWrt developers before submitting a formal patch series.
[snip]
> Three storage backends:
>
> - Block device (eMMC, SD, SATA, NVMe, USB mass storage, virtio)
> - MTD (SPI-NOR, raw NOR, raw NAND with bad block skipping)
> - UBI volume (SPI-NAND, raw NAND)
>
> The "bootm" command is extended to accept a storage device specification
> instead of a RAM address:
>
> bootm mmc 0:4 # boot FIT from eMMC partition 4
> bootm mtd recovery # boot FIT from MTD partition "recovery"
> bootm ubi recovery # boot FIT from UBI volume "recovery"
>
> This infrastructure is independently useful beyond the OpenWrt boot
> method. Any board that stores a FIT image directly in a partition
> (rather than as a file on a filesystem) can benefit from on-demand
> subimage loading.
For the user interface portion of this, since this is implementing
bootmeths, this should all be handled under bootflow/bootdev/etc
commands, and not adding further options to bootm.
Since you're also talking about an A/B mechanism, looking at the RAUC
support may also be helpful as that's another widely deployed
methodology we support now via standard boot.
--
Tom
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 88+ messages in thread* Re: [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading
2026-02-17 18:13 ` Tom Rini
@ 2026-02-17 19:28 ` Daniel Golle
2026-02-17 19:35 ` Tom Rini
0 siblings, 1 reply; 88+ messages in thread
From: Daniel Golle @ 2026-02-17 19:28 UTC (permalink / raw)
To: Tom Rini
Cc: Simon Glass, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
On Tue, Feb 17, 2026 at 12:13:58PM -0600, Tom Rini wrote:
> On Mon, Feb 16, 2026 at 09:21:14PM +0000, Daniel Golle wrote:
> > Hi all,
> >
> > This RFC series adds a new boot method for OpenWrt's "uImage.FIT with
> > embedded rootfs" firmware model, along with the underlying infrastructure
> > to load FIT images on-demand directly from storage devices without copying
> > them entirely to RAM first.
> >
> > I would like to discuss the design with U-Boot maintainers and fellow
> > OpenWrt developers before submitting a formal patch series.
> [snip]
> > Three storage backends:
> >
> > - Block device (eMMC, SD, SATA, NVMe, USB mass storage, virtio)
> > - MTD (SPI-NOR, raw NOR, raw NAND with bad block skipping)
> > - UBI volume (SPI-NAND, raw NAND)
> >
> > The "bootm" command is extended to accept a storage device specification
> > instead of a RAM address:
> >
> > bootm mmc 0:4 # boot FIT from eMMC partition 4
> > bootm mtd recovery # boot FIT from MTD partition "recovery"
> > bootm ubi recovery # boot FIT from UBI volume "recovery"
> >
> > This infrastructure is independently useful beyond the OpenWrt boot
> > method. Any board that stores a FIT image directly in a partition
> > (rather than as a file on a filesystem) can benefit from on-demand
> > subimage loading.
>
> For the user interface portion of this, since this is implementing
> bootmeths, this should all be handled under bootflow/bootdev/etc
> commands, and not adding further options to bootm.
I thought it would be useful independently of bootmeth/bootflow/bootdev
for `bootm` to have these options, as it will allow to gradually migrate
a large number of our downstream boards. Many of them at this point
still require scripts to handle things like extracting MAC addresses
from flash (in ways the original vendor has decided to store them),
updating U-Boot or TF-A blobs, ... and migrating all of that to use
bootmeth/bootflow/... will take time.
In the meantime, already getting rid of (duplicate) scripts "read
firmware from mmc/ubi/mtd" would be feasible, is less of a burden and
easy to test for people who got the respective board at hand.
Also, testing loading (partial) images directly from storage outside
of bootmeth has been very useful during development.
Would it be an option to hide the new bootm cmdline features behind an
additional Kconfig option maybe?
>
> Since you're also talking about an A/B mechanism, looking at the RAUC
> support may also be helpful as that's another widely deployed
> methodology we support now via standard boot.
Yes, I've looked into RAUC and it has (more or less) recently gained
support for UBI, which indeed brings it closer to being useful in the
context of OpenWrt.
Due to OpenWrt having a read/write overlayfs typically covering all
raiming space on flash, using a symmetric A/B mechanism is not feasible
in many cases, especially if space is limited (typical: 128 MB
SPI-NAND), as both A and B would require independent r/w overlayfs,
effectively halfing the space available for users. Configuration being
synced across A and B (which may be different versions) is another
non-trivial problem. Hence, OpenWrt has opted to implement a production
vs. recovery dual-boot model when ever we have the choice to do so. The
recovery system doesn't have any persistent state, hence elimintating
the need for finding an arrangement regarding the read/write overlay
filesystem, or keeping configuration in-sync across different versions
of the firmware.
RAUC also defines the firmware format, which for OpenWrt often isn't an
option as we like to remain compatible with the vendor/stock firmware
formats (in 90%+ of cases: uImage.FIT or legacy uImage, sometimes with
appended metadata)
Apart from that, the dependencies required for RAUC on the target
(OpenSSL, libglib2, libdbus, expat, ...) are overkill for many smaller
OpenWrt devices. We've made big efforts to avoid FreeDesktop-land and
have created userspace tools which require only a fraction of the
resources. We offer free choice of the TLS provider library (OpenSSL,
mbedTLS and wolfSSL are 1st class citizens; there is some support gnuTLS
as well), and having only one of them installed. A hard dependency on
OpenSSL alone is already a show-stopper for anything to be considered
becoming part of the core infrastructure. Requiring an XML parser would
end the conversation.
I consider it as extremely unlikely that this will ever change for
OpenWrt, and we do consider that as one of our strengths. The total size
of a fully usable OpenWrt installation currently starts at around 6 MiB
on low-end targets (that does include the kernel and all drivers, as
well as mbedTLS and everything else needed for a router or WiFi access
point, including a Web UI).
Random examples
- ultra low-end device: NEC Aterm WR8750N
https://downloads.openwrt.org/releases/24.10.5/targets/ath79/tiny/openwrt-24.10.5-ath79-tiny-nec_wr8750n-squashfs-sysupgrade.bin
- aforementioned BananaPi R3 WiFi 6 DBDC router:
https://downloads.openwrt.org/releases/24.10.5/targets/mediatek/filogic/openwrt-24.10.5-mediatek-filogic-bananapi_bpi-r3-sdcard.img.gz
(SD card image which can be used directly, or to install to SPI-NAND,
SPI-NOR or eMMC)
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading
2026-02-17 19:28 ` Daniel Golle
@ 2026-02-17 19:35 ` Tom Rini
2026-02-17 21:07 ` Daniel Golle
0 siblings, 1 reply; 88+ messages in thread
From: Tom Rini @ 2026-02-17 19:35 UTC (permalink / raw)
To: Daniel Golle
Cc: Simon Glass, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
[-- Attachment #1: Type: text/plain, Size: 3763 bytes --]
On Tue, Feb 17, 2026 at 07:28:03PM +0000, Daniel Golle wrote:
> On Tue, Feb 17, 2026 at 12:13:58PM -0600, Tom Rini wrote:
> > On Mon, Feb 16, 2026 at 09:21:14PM +0000, Daniel Golle wrote:
> > > Hi all,
> > >
> > > This RFC series adds a new boot method for OpenWrt's "uImage.FIT with
> > > embedded rootfs" firmware model, along with the underlying infrastructure
> > > to load FIT images on-demand directly from storage devices without copying
> > > them entirely to RAM first.
> > >
> > > I would like to discuss the design with U-Boot maintainers and fellow
> > > OpenWrt developers before submitting a formal patch series.
> > [snip]
> > > Three storage backends:
> > >
> > > - Block device (eMMC, SD, SATA, NVMe, USB mass storage, virtio)
> > > - MTD (SPI-NOR, raw NOR, raw NAND with bad block skipping)
> > > - UBI volume (SPI-NAND, raw NAND)
> > >
> > > The "bootm" command is extended to accept a storage device specification
> > > instead of a RAM address:
> > >
> > > bootm mmc 0:4 # boot FIT from eMMC partition 4
> > > bootm mtd recovery # boot FIT from MTD partition "recovery"
> > > bootm ubi recovery # boot FIT from UBI volume "recovery"
> > >
> > > This infrastructure is independently useful beyond the OpenWrt boot
> > > method. Any board that stores a FIT image directly in a partition
> > > (rather than as a file on a filesystem) can benefit from on-demand
> > > subimage loading.
> >
> > For the user interface portion of this, since this is implementing
> > bootmeths, this should all be handled under bootflow/bootdev/etc
> > commands, and not adding further options to bootm.
>
> I thought it would be useful independently of bootmeth/bootflow/bootdev
> for `bootm` to have these options, as it will allow to gradually migrate
> a large number of our downstream boards. Many of them at this point
> still require scripts to handle things like extracting MAC addresses
> from flash (in ways the original vendor has decided to store them),
> updating U-Boot or TF-A blobs, ... and migrating all of that to use
> bootmeth/bootflow/... will take time.
>
> In the meantime, already getting rid of (duplicate) scripts "read
> firmware from mmc/ubi/mtd" would be feasible, is less of a burden and
> easy to test for people who got the respective board at hand.
>
> Also, testing loading (partial) images directly from storage outside
> of bootmeth has been very useful during development.
>
> Would it be an option to hide the new bootm cmdline features behind an
> additional Kconfig option maybe?
I worry that if we add this to bootm upstream, it'll be another
interface that can't ever go away. Building off of another bit of
feedback I sent after this message here, I think the testing side of
this support should be handled with CMD_something.._GENERIC, similar to
CMD_FS_GENERIC. But perhaps not as one other part of this I wanted to
ask about is, does this only support reading FIT images which set their
load address? Or is there a call somewhere to lmb_alloc to grab an
arbitrary hunk of memory somewhere?
> > Since you're also talking about an A/B mechanism, looking at the RAUC
> > support may also be helpful as that's another widely deployed
> > methodology we support now via standard boot.
>
> Yes, I've looked into RAUC and it has (more or less) recently gained
> support for UBI, which indeed brings it closer to being useful in the
> context of OpenWrt.
To be clear, I just meant in terms of how to implement a bootmeth that
is not just EFI or distro boot but instead something else also aimed at
real world end user devices. Lots of people have a Steam Deck and a
router built on top of OpenWrt :)
--
Tom
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading
2026-02-17 19:35 ` Tom Rini
@ 2026-02-17 21:07 ` Daniel Golle
2026-02-17 21:18 ` Tom Rini
0 siblings, 1 reply; 88+ messages in thread
From: Daniel Golle @ 2026-02-17 21:07 UTC (permalink / raw)
To: Tom Rini
Cc: Simon Glass, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
On Tue, Feb 17, 2026 at 01:35:34PM -0600, Tom Rini wrote:
> On Tue, Feb 17, 2026 at 07:28:03PM +0000, Daniel Golle wrote:
> > On Tue, Feb 17, 2026 at 12:13:58PM -0600, Tom Rini wrote:
> > > On Mon, Feb 16, 2026 at 09:21:14PM +0000, Daniel Golle wrote:
> > > > Hi all,
> > > >
> > > > This RFC series adds a new boot method for OpenWrt's "uImage.FIT with
> > > > embedded rootfs" firmware model, along with the underlying infrastructure
> > > > to load FIT images on-demand directly from storage devices without copying
> > > > them entirely to RAM first.
> > > >
> > > > I would like to discuss the design with U-Boot maintainers and fellow
> > > > OpenWrt developers before submitting a formal patch series.
> > > [snip]
> > > > Three storage backends:
> > > >
> > > > - Block device (eMMC, SD, SATA, NVMe, USB mass storage, virtio)
> > > > - MTD (SPI-NOR, raw NOR, raw NAND with bad block skipping)
> > > > - UBI volume (SPI-NAND, raw NAND)
> > > >
> > > > The "bootm" command is extended to accept a storage device specification
> > > > instead of a RAM address:
> > > >
> > > > bootm mmc 0:4 # boot FIT from eMMC partition 4
> > > > bootm mtd recovery # boot FIT from MTD partition "recovery"
> > > > bootm ubi recovery # boot FIT from UBI volume "recovery"
> > > >
> > > > This infrastructure is independently useful beyond the OpenWrt boot
> > > > method. Any board that stores a FIT image directly in a partition
> > > > (rather than as a file on a filesystem) can benefit from on-demand
> > > > subimage loading.
> > >
> > > For the user interface portion of this, since this is implementing
> > > bootmeths, this should all be handled under bootflow/bootdev/etc
> > > commands, and not adding further options to bootm.
> >
> > I thought it would be useful independently of bootmeth/bootflow/bootdev
> > for `bootm` to have these options, as it will allow to gradually migrate
> > a large number of our downstream boards. Many of them at this point
> > still require scripts to handle things like extracting MAC addresses
> > from flash (in ways the original vendor has decided to store them),
> > updating U-Boot or TF-A blobs, ... and migrating all of that to use
> > bootmeth/bootflow/... will take time.
> >
> > In the meantime, already getting rid of (duplicate) scripts "read
> > firmware from mmc/ubi/mtd" would be feasible, is less of a burden and
> > easy to test for people who got the respective board at hand.
> >
> > Also, testing loading (partial) images directly from storage outside
> > of bootmeth has been very useful during development.
> >
> > Would it be an option to hide the new bootm cmdline features behind an
> > additional Kconfig option maybe?
>
> I worry that if we add this to bootm upstream, it'll be another
> interface that can't ever go away. Building off of another bit of
> feedback I sent after this message here, I think the testing side of
> this support should be handled with CMD_something.._GENERIC, similar to
> CMD_FS_GENERIC.
Hm... The tricky part is kinda not the "load from somewhere" as that
just ends up calling the .read() op, which usually is more or less
identical to what some existing commands are already doing, hence easy
to use 'mmc read ...', 'mtd read ...', 'ubi read ...' for testing.
Seeing that all the steps in 'bootm' are working, sub-images are
correctly loaded (or skipped), verification steps (hashes, signature,
...) are all working was the more difficult part...
> But perhaps not as one other part of this I wanted to ask about is,
> does this only support reading FIT images which set their load
> address? Or is there a call somewhere to lmb_alloc to grab an arbitrary
> hunk of memory somewhere?
In order to kinda do the same as was happening previously when first
loading the whole image from flash to $loadaddr, I've implemented a
trivial allocator for chunks with no defined load address in FIT.
In this case, image_loader_map() is called and loads to alloc_ptr,
which points to $loadaddr on init, and is then bumped by the
size loaded. I can use the lmb allocator instead, it's cleaner than
open-coding this.
In case of known load address (ie. specified for that sub-image in FIT)
and compression being IH_COMP_NONE, image_loader_map_to() is called
which directly loads that sub-image to the specified target address, no
allocation what-so-ever.
>
> > > Since you're also talking about an A/B mechanism, looking at the RAUC
> > > support may also be helpful as that's another widely deployed
> > > methodology we support now via standard boot.
> >
> > Yes, I've looked into RAUC and it has (more or less) recently gained
> > support for UBI, which indeed brings it closer to being useful in the
> > context of OpenWrt.
>
> To be clear, I just meant in terms of how to implement a bootmeth that
> is not just EFI or distro boot but instead something else also aimed at
> real world end user devices. Lots of people have a Steam Deck and a
> router built on top of OpenWrt :)
+1
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading
2026-02-17 21:07 ` Daniel Golle
@ 2026-02-17 21:18 ` Tom Rini
[not found] ` <aZTqyRfqYe1iJ9EY@makrotopia.org>
0 siblings, 1 reply; 88+ messages in thread
From: Tom Rini @ 2026-02-17 21:18 UTC (permalink / raw)
To: Daniel Golle
Cc: Simon Glass, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
[-- Attachment #1: Type: text/plain, Size: 5348 bytes --]
On Tue, Feb 17, 2026 at 09:07:31PM +0000, Daniel Golle wrote:
> On Tue, Feb 17, 2026 at 01:35:34PM -0600, Tom Rini wrote:
> > On Tue, Feb 17, 2026 at 07:28:03PM +0000, Daniel Golle wrote:
> > > On Tue, Feb 17, 2026 at 12:13:58PM -0600, Tom Rini wrote:
> > > > On Mon, Feb 16, 2026 at 09:21:14PM +0000, Daniel Golle wrote:
> > > > > Hi all,
> > > > >
> > > > > This RFC series adds a new boot method for OpenWrt's "uImage.FIT with
> > > > > embedded rootfs" firmware model, along with the underlying infrastructure
> > > > > to load FIT images on-demand directly from storage devices without copying
> > > > > them entirely to RAM first.
> > > > >
> > > > > I would like to discuss the design with U-Boot maintainers and fellow
> > > > > OpenWrt developers before submitting a formal patch series.
> > > > [snip]
> > > > > Three storage backends:
> > > > >
> > > > > - Block device (eMMC, SD, SATA, NVMe, USB mass storage, virtio)
> > > > > - MTD (SPI-NOR, raw NOR, raw NAND with bad block skipping)
> > > > > - UBI volume (SPI-NAND, raw NAND)
> > > > >
> > > > > The "bootm" command is extended to accept a storage device specification
> > > > > instead of a RAM address:
> > > > >
> > > > > bootm mmc 0:4 # boot FIT from eMMC partition 4
> > > > > bootm mtd recovery # boot FIT from MTD partition "recovery"
> > > > > bootm ubi recovery # boot FIT from UBI volume "recovery"
> > > > >
> > > > > This infrastructure is independently useful beyond the OpenWrt boot
> > > > > method. Any board that stores a FIT image directly in a partition
> > > > > (rather than as a file on a filesystem) can benefit from on-demand
> > > > > subimage loading.
> > > >
> > > > For the user interface portion of this, since this is implementing
> > > > bootmeths, this should all be handled under bootflow/bootdev/etc
> > > > commands, and not adding further options to bootm.
> > >
> > > I thought it would be useful independently of bootmeth/bootflow/bootdev
> > > for `bootm` to have these options, as it will allow to gradually migrate
> > > a large number of our downstream boards. Many of them at this point
> > > still require scripts to handle things like extracting MAC addresses
> > > from flash (in ways the original vendor has decided to store them),
> > > updating U-Boot or TF-A blobs, ... and migrating all of that to use
> > > bootmeth/bootflow/... will take time.
> > >
> > > In the meantime, already getting rid of (duplicate) scripts "read
> > > firmware from mmc/ubi/mtd" would be feasible, is less of a burden and
> > > easy to test for people who got the respective board at hand.
> > >
> > > Also, testing loading (partial) images directly from storage outside
> > > of bootmeth has been very useful during development.
> > >
> > > Would it be an option to hide the new bootm cmdline features behind an
> > > additional Kconfig option maybe?
> >
> > I worry that if we add this to bootm upstream, it'll be another
> > interface that can't ever go away. Building off of another bit of
> > feedback I sent after this message here, I think the testing side of
> > this support should be handled with CMD_something.._GENERIC, similar to
> > CMD_FS_GENERIC.
>
> Hm... The tricky part is kinda not the "load from somewhere" as that
> just ends up calling the .read() op, which usually is more or less
> identical to what some existing commands are already doing, hence easy
> to use 'mmc read ...', 'mtd read ...', 'ubi read ...' for testing.
Yes, this part would be more of just an easier syntax, perhps "block
read type ...".
> Seeing that all the steps in 'bootm' are working, sub-images are
> correctly loaded (or skipped), verification steps (hashes, signature,
> ...) are all working was the more difficult part...
Presumably because of the partial read part? That does get back to
Marek's earlier question about if you can't get that already with
"mkimage -E" and I didn't see the answer, sorry.
> > But perhaps not as one other part of this I wanted to ask about is,
> > does this only support reading FIT images which set their load
> > address? Or is there a call somewhere to lmb_alloc to grab an arbitrary
> > hunk of memory somewhere?
>
> In order to kinda do the same as was happening previously when first
> loading the whole image from flash to $loadaddr, I've implemented a
> trivial allocator for chunks with no defined load address in FIT.
> In this case, image_loader_map() is called and loads to alloc_ptr,
> which points to $loadaddr on init, and is then bumped by the
> size loaded. I can use the lmb allocator instead, it's cleaner than
> open-coding this.
Oh yes, this cannot be open coded. The lmb framework is how we avoid
some classes of security issues and just open coding that brings them
all right back. But this is also something that's solved, today, for FIT
images of KERNEL_NOLOAD so we shouldn't need anything new here, either.
> In case of known load address (ie. specified for that sub-image in FIT)
> and compression being IH_COMP_NONE, image_loader_map_to() is called
> which directly loads that sub-image to the specified target address, no
> allocation what-so-ever.
Is that with the existing code, or did you write something new here as
well?
--
Tom
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading
2026-02-16 21:21 [RFC PATCH 00/20] boot: add OpenWrt boot method and on-demand FIT loading Daniel Golle
` (24 preceding siblings ...)
2026-02-17 18:13 ` Tom Rini
@ 2026-02-17 19:20 ` Tom Rini
25 siblings, 0 replies; 88+ messages in thread
From: Tom Rini @ 2026-02-17 19:20 UTC (permalink / raw)
To: Daniel Golle
Cc: Simon Glass, Quentin Schulz, Kory Maincent, Mattijs Korpershoek,
Martin Schwan, Anshul Dalal, Ilias Apalodimas, Sughosh Ganu,
Aristo Chen, 牛 志宏, Marek Vasut,
Heinrich Schuchardt, Wolfgang Wallner, Frank Wunderlich,
David Lechner, Osama Abdelkader, Mikhail Kshevetskiy,
Michael Trimarchi, Miquel Raynal, Andrew Goodbody, Yegor Yefremov,
Mike Looijmans, Weijie Gao, Alexander Stein, Neil Armstrong,
Mayuresh Chitale, Paul HENRYS, u-boot, John Crispin, Paul Spooren
[-- Attachment #1: Type: text/plain, Size: 2660 bytes --]
On Mon, Feb 16, 2026 at 09:21:14PM +0000, Daniel Golle wrote:
> Hi all,
>
> This RFC series adds a new boot method for OpenWrt's "uImage.FIT with
> embedded rootfs" firmware model, along with the underlying infrastructure
> to load FIT images on-demand directly from storage devices without copying
> them entirely to RAM first.
>
> I would like to discuss the design with U-Boot maintainers and fellow
> OpenWrt developers before submitting a formal patch series.
[snip]
> boot/Kconfig | 88 +++++++
> boot/Makefile | 8 +
> boot/bootm.c | 62 ++++-
> boot/bootmeth_openwrt.c | 248 +++++++++++++++++++
> boot/image-fit.c | 96 ++++++++
> boot/image-loader-blk.c | 133 ++++++++++
> boot/image-loader-mtd.c | 103 ++++++++
> boot/image-loader-ubi.c | 112 +++++++++
> boot/image-loader.c | 163 +++++++++++++
> boot/mtd_bootdev.c | 150 ++++++++++++
> boot/ubi_bootdev.c | 180 ++++++++++++++
> cmd/bootm.c | 148 +++++++++++-
> cmd/mtd.c | 65 ++---
> cmd/ubi.c | 33 ++-
> doc/develop/bootm-storage.rst | 210 ++++++++++++++++
> doc/develop/index.rst | 1 +
> doc/usage/fit/index.rst | 1 +
> doc/usage/fit/storage-boot.rst | 201 +++++++++++++++
> drivers/mtd/mtd-uclass.c | 15 ++
> drivers/mtd/mtdcore.c | 45 ++++
> drivers/mtd/mtdpart.c | 2 +
> include/bootm.h | 2 +
> include/image-loader.h | 188 +++++++++++++++
> include/image.h | 4 +
> include/linux/mtd/mtd.h | 24 ++
> include/ubi_uboot.h | 2 +
> test/boot/Makefile | 2 +
> test/boot/image_loader.c | 429 +++++++++++++++++++++++++++++++++
> test/cmd_ut.c | 2 +
This isn't enabled on any platforms, and not enabled on sandbox either
so the tests aren't run. Please do that for the next iteration as well.
Since you mentioned one of the Banana Pi boards, including that would be
helpful too. If you can also include support for the openwrt one (and
instructions on how to flash it, if
https://openwrt.org/toh/openwrt/one#uart_boot) isn't enough that would
be great since I have one of those at hand.
Finally, aside from the feedback I just gave about splitting the image
portion out, it seemed like a few other changes here could be considered
stand-alone bug fixes themselves. Posting them outside of a series helps
both for ease of review and ease of merging. Thanks.
--
Tom
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 88+ messages in thread