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

Reply via email to