Hi Daniel, On Mon, 16 Feb 2026 at 14:21, Daniel Golle <[email protected]> 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 <[email protected]> > --- > 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 <[email protected]> > + */ > + > +#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

