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 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; +}; + +/** + * 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

