[RFC PATCH 02/20] boot: image-loader: add block device backend
Daniel Golle
daniel at makrotopia.org
Mon Feb 16 22:21:33 CET 2026
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 at 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 at 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
More information about the U-Boot
mailing list