[PATCH v2 10/11] test: boot: add imagemap unit tests

Daniel Golle <[email protected]>
Newsgroups org.u-boot-project.lists.u-boot
Message-ID <fea21cdbf185af8f83553bd78b45d51abd6ef9dd.1787673209.git.daniel@makrotopia.org>
Add a test suite exercising imagemap:
 - the translation-table core (map, lookup, map_to, cleanup, region
   growth, the block-aligned read path, and edge cases) using a mock
   RAM-backed reader, so most tests need no real storage;

 - a NOR read through mtd_blk: write a known pattern into a
   device-tree partition of the sandbox SPI-NOR and read it back
   through imagemap, exercising imagemap -> spl_load_region ->
   blk_dread -> mtd_blk_read -> mtd_read for real;

 - a UBI-volume read through ubiblock: attach UBI on the sandbox NAND,
   create a volume, write a pattern and read it back through imagemap
   on the ubi_blk device.

Registered under ut_boot_imagemap in cmd_ut.c.

Signed-off-by: Daniel Golle <[email protected]>
---
 MAINTAINERS          |   1 +
 test/boot/Makefile   |   2 +
 test/boot/imagemap.c | 645 +++++++++++++++++++++++++++++++++++++++++++
 test/cmd_ut.c        |   2 +
 4 files changed, 650 insertions(+)
 create mode 100644 test/boot/imagemap.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 48ee01e033d..e8f0e94597f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1192,6 +1192,7 @@ L:	[email protected]
 S:	Maintained
 F:	boot/imagemap.c
 F:	include/imagemap.h
+F:	test/boot/imagemap.c
 
 INTERCONNECT
 M:	Neil Armstrong <[email protected]>
diff --git a/test/boot/Makefile b/test/boot/Makefile
index 59a87028704..8dbecabde79 100644
--- a/test/boot/Makefile
+++ b/test/boot/Makefile
@@ -27,3 +27,5 @@ endif
 obj-$(CONFIG_BOOTMETH_VBE) += vbe_fixup.o
 
 obj-$(CONFIG_UPL) += upl.o
+
+obj-$(CONFIG_IMAGEMAP) += imagemap.o
diff --git a/test/boot/imagemap.c b/test/boot/imagemap.c
new file mode 100644
index 00000000000..da038a33243
--- /dev/null
+++ b/test/boot/imagemap.c
@@ -0,0 +1,645 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for imagemap framework (UCLASS_IMAGEMAP)
+ *
+ * Copyright (C) 2026 Daniel Golle <[email protected]>
+ */
+
+#include <dm.h>
+#include <blk.h>
+#include <imagemap.h>
+#include <mtd.h>
+#include <part.h>
+#include <ubi_uboot.h>
+#include <linux/err.h>
+#include <linux/mtd/mtd.h>
+#include <lmb.h>
+#include <mapmem.h>
+#include <malloc.h>
+#include <asm/cache.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dm/root.h>
+#include <dm/uclass-internal.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+#define IMAGEMAP_TEST(_name, _flags) \
+	UNIT_TEST(_name, _flags, imagemap)
+
+/* 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;
+	loff_t last_off;
+	ulong last_size;
+};
+
+static ulong mock_reader(struct spl_load_info *info, ulong sector,
+			 ulong count, void *buf)
+{
+	struct mock_priv *p = info->priv;
+
+	if (sector + count > p->image_size)
+		return 0;
+
+	memcpy(buf, (const char *)p->image + sector, count);
+	p->read_count++;
+	p->last_off = sector;
+	p->last_size = count;
+
+	return count;
+}
+
+U_BOOT_DRIVER(imagemap_mock) = {
+	.name		= "imagemap_mock",
+	.id		= UCLASS_IMAGEMAP,
+	.priv_auto	= sizeof(struct mock_priv),
+};
+
+/**
+ * create_mock_loader() - create a mock imagemap device for testing
+ *
+ * @image:	synthetic image buffer
+ * @image_size:	size of @image
+ * @devp:	on success, the new imagemap device
+ * Return: 0 on success, negative errno on failure
+ */
+static int create_mock_loader_bl(const void *image, size_t image_size,
+				 uint bl_len, struct udevice **devp)
+{
+	struct udevice *dev;
+	struct imagemap_priv *im;
+	struct mock_priv *priv;
+	int ret;
+
+	ret = device_bind_driver(dm_root(), "imagemap_mock",
+				 "imagemap-test", &dev);
+	if (ret)
+		return ret;
+
+	ret = device_probe(dev);
+	if (ret) {
+		device_unbind(dev);
+		return ret;
+	}
+
+	priv = dev_get_priv(dev);
+	priv->image = image;
+	priv->image_size = image_size;
+	priv->read_count = 0;
+
+	/* Point the imagemap's shared load-to-mem reader at the mock image */
+	im = dev_get_uclass_priv(dev);
+	spl_load_init(&im->info, mock_reader, priv, bl_len);
+
+	*devp = dev;
+
+	return 0;
+}
+
+static int create_mock_loader(const void *image, size_t image_size,
+			      struct udevice **devp)
+{
+	return create_mock_loader_bl(image, image_size, 1, devp);
+}
+
+/**
+ * get_mock_priv() - get mock private data from an imagemap device
+ */
+static struct mock_priv *get_mock_priv(struct udevice *dev)
+{
+	return dev_get_priv(dev);
+}
+
+/* Test: map() allocates, reads and records a region */
+static int imagemap_test_map_basic(struct unit_test_state *uts)
+{
+	struct udevice *dev;
+	struct imagemap_priv *priv;
+	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);
+
+	ut_assertok(create_mock_loader(image, IMAGE_SIZE, &dev));
+	mock = get_mock_priv(dev);
+	priv = dev_get_uclass_priv(dev);
+
+	/* Map a 64-byte region at offset 0 */
+	p = imagemap_map(dev, 0, 64);
+	ut_assert(!IS_ERR(p));
+	ut_asserteq_mem(image, p, 64);
+	ut_asserteq(1, mock->read_count);
+	ut_asserteq(1, priv->regions.count);
+	ut_asserteq(0, (int)alist_get(&priv->regions, 0, struct imagemap_region)->img_offset);
+	ut_asserteq(64, (int)alist_get(&priv->regions, 0, struct imagemap_region)->size);
+
+	imagemap_cleanup(dev);
+
+	return 0;
+}
+
+IMAGEMAP_TEST(imagemap_test_map_basic, 0);
+
+/* Test: map() returns cached pointer for already-mapped range */
+static int imagemap_test_map_cached(struct unit_test_state *uts)
+{
+	struct udevice *dev;
+	struct mock_priv *mock;
+	u8 image[IMAGE_SIZE];
+	void *p1, *p2;
+
+	memset(image, 0xaa, IMAGE_SIZE);
+	ut_assertok(create_mock_loader(image, IMAGE_SIZE, &dev));
+	mock = get_mock_priv(dev);
+
+	p1 = imagemap_map(dev, 0, 128);
+	ut_assert(!IS_ERR(p1));
+	ut_asserteq(1, mock->read_count);
+
+	/* Same range - should return same pointer, no new read */
+	p2 = imagemap_map(dev, 0, 128);
+	ut_asserteq_ptr(p1, p2);
+	ut_asserteq(1, mock->read_count);
+
+	/* Subset of the already-mapped range - still cached */
+	p2 = imagemap_map(dev, 0, 64);
+	ut_asserteq_ptr(p1, p2);
+	ut_asserteq(1, mock->read_count);
+
+	imagemap_cleanup(dev);
+
+	return 0;
+}
+
+IMAGEMAP_TEST(imagemap_test_map_cached, 0);
+
+/* Test: map() returns correct offset within a larger region */
+static int imagemap_test_map_offset(struct unit_test_state *uts)
+{
+	struct udevice *dev;
+	struct mock_priv *mock;
+	u8 image[IMAGE_SIZE];
+	void *p1, *p2;
+
+	for (int i = 0; i < IMAGE_SIZE; i++)
+		image[i] = (u8)(i & 0xff);
+
+	ut_assertok(create_mock_loader(image, IMAGE_SIZE, &dev));
+	mock = get_mock_priv(dev);
+
+	/* Map a 256-byte region starting at offset 0 */
+	p1 = imagemap_map(dev, 0, 256);
+	ut_assert(!IS_ERR(p1));
+
+	/* Request a sub-range within the previously mapped region */
+	p2 = imagemap_map(dev, 64, 64);
+	ut_assert(!IS_ERR(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);
+
+	imagemap_cleanup(dev);
+
+	return 0;
+}
+
+IMAGEMAP_TEST(imagemap_test_map_offset, 0);
+
+/* Test: map() re-reads when extending a region to a larger size */
+static int imagemap_test_map_extend(struct unit_test_state *uts)
+{
+	struct udevice *dev;
+	struct imagemap_priv *priv;
+	struct mock_priv *mock;
+	u8 image[IMAGE_SIZE];
+	void *p1, *p2;
+
+	for (int i = 0; i < IMAGE_SIZE; i++)
+		image[i] = (u8)(i & 0xff);
+
+	ut_assertok(create_mock_loader(image, IMAGE_SIZE, &dev));
+	mock = get_mock_priv(dev);
+	priv = dev_get_uclass_priv(dev);
+
+	/* Initial small mapping */
+	p1 = imagemap_map(dev, 0, 64);
+	ut_assert(!IS_ERR(p1));
+	ut_asserteq(1, mock->read_count);
+
+	/* Request larger range at same base - should re-read (extend) */
+	p2 = imagemap_map(dev, 0, 256);
+	ut_assert(!IS_ERR(p2));
+	ut_asserteq(2, mock->read_count);
+	ut_asserteq_mem(image, p2, 256);
+
+	/* Region count should still be 1 (updated, not added) */
+	ut_asserteq(1, priv->regions.count);
+	ut_asserteq(256, (int)alist_get(&priv->regions, 0, struct imagemap_region)->size);
+
+	/*
+	 * 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
+	 * an address that overlapped the header, clobbering it.
+	 * With LMB allocation this cannot happen because each allocation
+	 * reserves distinct memory.
+	 */
+	{
+		void *p3 = imagemap_map(dev, 256, 128);
+		ulong p2_end = map_to_sysmem(p2) + 256;
+
+		ut_assert(!IS_ERR(p3));
+		ut_asserteq(2, priv->regions.count);
+		/* New region must not overlap the extended first region */
+		ut_assert(map_to_sysmem(p3) >= p2_end ||
+			  map_to_sysmem(p3) + 128 <= map_to_sysmem(p2));
+		ut_asserteq_mem(image + 256, p3, 128);
+	}
+
+	imagemap_cleanup(dev);
+
+	return 0;
+}
+
+IMAGEMAP_TEST(imagemap_test_map_extend, 0);
+
+/* Test: map_to() reads to a specified address and records it */
+static int imagemap_test_map_to(struct unit_test_state *uts)
+{
+	struct udevice *dev;
+	struct imagemap_priv *priv;
+	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);
+
+	ut_assertok(create_mock_loader(image, IMAGE_SIZE, &dev));
+	mock = get_mock_priv(dev);
+	priv = dev_get_uclass_priv(dev);
+
+	p = imagemap_map_to(dev, 128, 256, dst);
+	ut_assert(!IS_ERR(p));
+	ut_asserteq_ptr(dst, p);
+	ut_asserteq_mem(image + 128, dst, 256);
+	ut_asserteq(1, mock->read_count);
+	ut_asserteq(1, priv->regions.count);
+	ut_asserteq(128, (int)alist_get(&priv->regions, 0, struct imagemap_region)->img_offset);
+	ut_asserteq(256, (int)alist_get(&priv->regions, 0, struct imagemap_region)->size);
+	ut_asserteq_ptr(dst, alist_get(&priv->regions, 0, struct imagemap_region)->ram);
+
+	imagemap_cleanup(dev);
+
+	return 0;
+}
+
+IMAGEMAP_TEST(imagemap_test_map_to, 0);
+
+/* Test: lookup() returns NULL for unmapped ranges */
+static int imagemap_test_lookup_miss(struct unit_test_state *uts)
+{
+	struct udevice *dev;
+	u8 image[IMAGE_SIZE];
+	void *p;
+
+	ut_assertok(create_mock_loader(image, IMAGE_SIZE, &dev));
+
+	/* Nothing mapped yet - should return NULL */
+	p = imagemap_lookup(dev, 0, 64);
+	ut_assertnull(p);
+
+	/* Map a region at offset 0 */
+	ut_assert(!IS_ERR(imagemap_map(dev, 0, 64)));
+
+	/* Lookup within the mapped region - should succeed */
+	p = imagemap_lookup(dev, 0, 32);
+	ut_assertnonnull(p);
+
+	/* Lookup at a different offset - should miss */
+	p = imagemap_lookup(dev, 128, 32);
+	ut_assertnull(p);
+
+	/* Lookup extending beyond the mapped region - should miss */
+	p = imagemap_lookup(dev, 0, 128);
+	ut_assertnull(p);
+
+	imagemap_cleanup(dev);
+
+	return 0;
+}
+
+IMAGEMAP_TEST(imagemap_test_lookup_miss, 0);
+
+/* Test: LMB reservations are made for map() allocations */
+static int imagemap_test_lmb_reserve(struct unit_test_state *uts)
+{
+	struct udevice *dev;
+	u8 image[IMAGE_SIZE];
+	void *p1, *p2;
+
+	ut_assertok(create_mock_loader(image, IMAGE_SIZE, &dev));
+
+	/* Map 100 bytes - should be LMB reserved */
+	p1 = imagemap_map(dev, 0, 100);
+	ut_assert(!IS_ERR(p1));
+	ut_assert(lmb_is_reserved_flags(map_to_sysmem(p1), LMB_NONE));
+
+	/* Map another 200 bytes at a different offset */
+	p2 = imagemap_map(dev, 200, 200);
+	ut_assert(!IS_ERR(p2));
+	ut_assert(lmb_is_reserved_flags(map_to_sysmem(p2), LMB_NONE));
+
+	/* Regions must not overlap */
+	ut_assert(map_to_sysmem(p2) >= map_to_sysmem(p1) + 100 ||
+		  map_to_sysmem(p1) >= map_to_sysmem(p2) + 200);
+
+	/* Cleanup frees the LMB reservations */
+	imagemap_cleanup(dev);
+	ut_assert(!lmb_is_reserved_flags(map_to_sysmem(p1), LMB_NONE));
+	ut_assert(!lmb_is_reserved_flags(map_to_sysmem(p2), LMB_NONE));
+
+	return 0;
+}
+
+IMAGEMAP_TEST(imagemap_test_lmb_reserve, 0);
+
+/* Test: cleanup() removes the device */
+static int imagemap_test_cleanup(struct unit_test_state *uts)
+{
+	struct udevice *dev;
+	u8 image[IMAGE_SIZE];
+
+	ut_assertok(create_mock_loader(image, IMAGE_SIZE, &dev));
+
+	/* Map something so regions are not empty */
+	ut_assert(!IS_ERR(imagemap_map(dev, 0, 64)));
+
+	/* Cleanup should remove and unbind the device */
+	imagemap_cleanup(dev);
+
+	/* NULL cleanup should be safe */
+	imagemap_cleanup(NULL);
+
+	return 0;
+}
+
+IMAGEMAP_TEST(imagemap_test_cleanup, 0);
+
+/* Test: map() with multiple disjoint regions */
+static int imagemap_test_multi_region(struct unit_test_state *uts)
+{
+	struct udevice *dev;
+	struct imagemap_priv *priv;
+	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);
+
+	ut_assertok(create_mock_loader(image, IMAGE_SIZE, &dev));
+	mock = get_mock_priv(dev);
+	priv = dev_get_uclass_priv(dev);
+
+	p1 = imagemap_map(dev, 0, 64);
+	ut_assert(!IS_ERR(p1));
+	p2 = imagemap_map(dev, 512, 128);
+	ut_assert(!IS_ERR(p2));
+	p3 = imagemap_map(dev, 1024, 256);
+	ut_assert(!IS_ERR(p3));
+
+	ut_asserteq(3, priv->regions.count);
+	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, imagemap_lookup(dev, 0, 64));
+	ut_asserteq_ptr(p2, imagemap_lookup(dev, 512, 128));
+	ut_asserteq_ptr(p3, imagemap_lookup(dev, 1024, 256));
+
+	imagemap_cleanup(dev);
+
+	return 0;
+}
+
+IMAGEMAP_TEST(imagemap_test_multi_region, 0);
+
+/* Test: read beyond image size returns error */
+static int imagemap_test_read_oob(struct unit_test_state *uts)
+{
+	struct udevice *dev;
+	u8 image[IMAGE_SIZE];
+	void *p;
+
+	ut_assertok(create_mock_loader(image, IMAGE_SIZE, &dev));
+
+	/* Attempt to map beyond the end of the image */
+	p = imagemap_map(dev, IMAGE_SIZE - 32, 64);
+	ut_assert(IS_ERR(p));
+
+	/* map_to should also fail */
+	u8 dst[64];
+
+	p = imagemap_map_to(dev, IMAGE_SIZE - 32, 64, dst);
+	ut_assert(IS_ERR(p));
+
+	imagemap_cleanup(dev);
+
+	return 0;
+}
+
+IMAGEMAP_TEST(imagemap_test_read_oob, 0);
+
+/*
+ * Test: block-addressed backend (bl_len > 1) still returns/places the
+ * wanted bytes byte-exact for unaligned offsets and multi-block ranges.
+ */
+static int imagemap_test_blocklen(struct unit_test_state *uts)
+{
+	struct udevice *dev;
+	u8 image[IMAGE_SIZE];
+	u8 dst[1024];
+	void *p;
+
+	for (int i = 0; i < IMAGE_SIZE; i++)
+		image[i] = (u8)(i & 0xff);
+
+	ut_assertok(create_mock_loader_bl(image, IMAGE_SIZE, 512, &dev));
+
+	/* map() at an unaligned offset returns byte-exact data */
+	p = imagemap_map(dev, 100, 200);
+	ut_assert(!IS_ERR(p));
+	ut_asserteq_mem(image + 100, p, 200);
+
+	/* map_to() places the payload byte-exact at dst (partial head only) */
+	p = imagemap_map_to(dev, 100, 200, dst);
+	ut_assert(!IS_ERR(p));
+	ut_asserteq_ptr(dst, p);
+	ut_asserteq_mem(image + 100, dst, 200);
+
+	/* map_to() spanning partial head + aligned middle + partial tail */
+	p = imagemap_map_to(dev, 500, 600, dst);
+	ut_assert(!IS_ERR(p));
+	ut_asserteq_ptr(dst, p);
+	ut_asserteq_mem(image + 500, dst, 600);
+
+	imagemap_cleanup(dev);
+
+	return 0;
+}
+
+IMAGEMAP_TEST(imagemap_test_blocklen, 0);
+
+#if CONFIG_IS_ENABLED(MTD_BLOCK)
+/*
+ * End-to-end: read a known pattern back from a bare (non-NAND) MTD
+ * partition through imagemap on the mtd_blk block device. Exercises the
+ * real path imagemap -> spl_load_region -> blk_dread -> mtd_blk_read ->
+ * mtd_read on the sandbox SPI-NOR (no mock).
+ */
+static int imagemap_test_mtd_blk(struct unit_test_state *uts)
+{
+	const ulong blks = 4, len = blks * 512;
+	struct udevice *blk = NULL, *dev, *imdev;
+	struct blk_desc *desc = NULL;
+	struct disk_partition info;
+	void *mapped;
+	u8 *wbuf;
+	int i;
+
+	/*
+	 * Probe the SPI-NOR (add_mtd_device() then binds its mtd_blk) and
+	 * parse its device-tree partitions.
+	 */
+	mtd_probe_devices();
+
+	/* Find the bound mtd_blk (a PART_TYPE_MTD block device) */
+	for (uclass_find_first_device(UCLASS_BLK, &dev); dev;
+	     uclass_find_next_device(&dev)) {
+		desc = dev_get_uclass_plat(dev);
+		if (desc->part_type == PART_TYPE_MTD) {
+			blk = dev;
+			break;
+		}
+	}
+	ut_assertnonnull(blk);
+	ut_assertok(device_probe(blk));
+	desc = dev_get_uclass_plat(blk);
+
+	/* Write a known pattern into the "nor-fit" partition */
+	ut_assert(part_get_info_by_name(desc, "nor-fit", &info) >= 1);
+	wbuf = malloc(len);
+	ut_assertnonnull(wbuf);
+	for (i = 0; i < len; i++)
+		wbuf[i] = (u8)(i * 5 + 1);
+	ut_asserteq(blks, blk_dwrite(desc, info.start, blks, wbuf));
+
+	/* Read it back through imagemap on the mtd_blk device */
+	ut_assertok(imagemap_create(blk, "nor-fit", 0, &imdev));
+	mapped = imagemap_map(imdev, 0, len);
+	ut_assert(!IS_ERR(mapped));
+	ut_asserteq_mem(wbuf, mapped, len);
+
+	imagemap_cleanup(imdev);
+	free(wbuf);
+
+	return 0;
+}
+IMAGEMAP_TEST(imagemap_test_mtd_blk, 0);
+#endif /* MTD_BLOCK */
+
+#if CONFIG_IS_ENABLED(MTD_UBI) && CONFIG_IS_ENABLED(UBI_BLOCK)
+/*
+ * End-to-end: attach UBI on the sandbox NAND, create a volume, write a
+ * known pattern and read it back through imagemap on the ubi_blk block
+ * device. Exercises imagemap -> spl_load_region -> blk_dread ->
+ * ubi_bread -> ubi_volume_read for real (no mock).
+ */
+static int imagemap_test_ubiblock(struct unit_test_state *uts)
+{
+	const ulong len = 4096;
+	struct udevice *blk = NULL, *dev, *imdev;
+	struct erase_info ei;
+	struct mtd_info *mtd;
+	void *mapped;
+	u8 *wbuf;
+	u64 off;
+	int i;
+
+	mtd_probe_devices();
+	mtd = get_mtd_device_nm("nand2");
+	ut_assert(!IS_ERR_OR_NULL(mtd));
+
+	/* Erase so UBI can format the (empty) clean chip */
+	for (off = 0; off < mtd->size; off += mtd->erasesize) {
+		if (mtd_block_isbad(mtd, off))
+			continue;
+		memset(&ei, 0, sizeof(ei));
+		ei.mtd = mtd;
+		ei.addr = off;
+		ei.len = mtd->erasesize;
+		ut_assertok(mtd_erase(mtd, &ei));
+	}
+	put_mtd_device(mtd);
+
+	/* Attach UBI by name; this binds the ubi_blk block device */
+	ut_assertok(ubi_part("nand2", NULL));
+	ut_assertok(ubi_create_vol("vol0", 0x10000, true, UBI_VOL_NUM_AUTO,
+				   false));
+
+	wbuf = malloc(len);
+	ut_assertnonnull(wbuf);
+	for (i = 0; i < len; i++)
+		wbuf[i] = (u8)(i * 7 + 3);
+	ut_assertok(ubi_volume_write("vol0", wbuf, 0, len));
+
+	/* Find the ubi_blk block device */
+	for (uclass_find_first_device(UCLASS_BLK, &dev); dev;
+	     uclass_find_next_device(&dev)) {
+		if (dev->driver == DM_DRIVER_GET(ubi_blk)) {
+			blk = dev;
+			break;
+		}
+	}
+	ut_assertnonnull(blk);
+	ut_assertok(device_probe(blk));
+
+	/* Read the volume back through imagemap and compare */
+	ut_assertok(imagemap_create(blk, "vol0", 0, &imdev));
+	mapped = imagemap_map(imdev, 0, len);
+	ut_assert(!IS_ERR(mapped));
+	ut_asserteq_mem(wbuf, mapped, len);
+
+	imagemap_cleanup(imdev);
+	free(wbuf);
+
+	return 0;
+}
+IMAGEMAP_TEST(imagemap_test_ubiblock, 0);
+#endif /* MTD_UBI && UBI_BLOCK */
diff --git a/test/cmd_ut.c b/test/cmd_ut.c
index 4328670d0d6..17610cdc2af 100644
--- a/test/cmd_ut.c
+++ b/test/cmd_ut.c
@@ -73,6 +73,7 @@ SUITE_DECL(optee);
 SUITE_DECL(pci_mps);
 SUITE_DECL(seama);
 SUITE_DECL(setexpr);
+SUITE_DECL(imagemap);
 SUITE_DECL(upl);
 
 static struct suite suites[] = {
@@ -102,6 +103,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(imagemap, "imagemap on-demand storage loading"),
 	SUITE(upl, "Universal payload support"),
 };
 
-- 
2.55.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.