diff options
author | Tom Rini | 2023-04-05 18:59:47 -0400 |
---|---|---|
committer | Tom Rini | 2023-04-05 18:59:47 -0400 |
commit | 487e42f7bc5e685c9337890a38358581bb4f31bc (patch) | |
tree | 03133e406371e92ce12b03b50c61a82637eea827 | |
parent | 25eeda170c5e533ca0e3837c8b2d7404cdd749d1 (diff) | |
parent | 272ec6b453049beaf0de6654dabf9bbd5f617022 (diff) |
Merge branch '2023-04-05-blkmap-composable-virtual-block-devices'
To quote the author:
Block maps are a way of looking at various sources of data through the
lens of a regular block device. It lets you treat devices that are not
block devices, like RAM, as if they were. It also lets you export a
slice of an existing block device, which does not have to correspond to
a partition boundary, as a new block device.
This is primarily useful because U-Boot's filesystem drivers only
operate on block devices, so a block map lets you access filesystems
wherever they might be located.
The implementation is loosely modeled on Linux's "Device Mapper"
subsystem, see the kernel documentation [1] for more information.
The primary use-cases are to access filesystem images stored in RAM, and
within FIT images stored on disk. See doc/usage/blkmap.rst for more
details.
The architecture is pluggable, so adding other types of mappings should
be quite easy.
[1]: https://docs.kernel.org/admin-guide/device-mapper/index.html
-rw-r--r-- | MAINTAINERS | 9 | ||||
-rw-r--r-- | boot/image-board.c | 3 | ||||
-rw-r--r-- | cmd/Kconfig | 19 | ||||
-rw-r--r-- | cmd/Makefile | 1 | ||||
-rw-r--r-- | cmd/blk_common.c | 15 | ||||
-rw-r--r-- | cmd/blkmap.c | 233 | ||||
-rw-r--r-- | configs/sandbox_defconfig | 1 | ||||
-rw-r--r-- | disk/part.c | 1 | ||||
-rw-r--r-- | doc/usage/blkmap.rst | 111 | ||||
-rw-r--r-- | doc/usage/index.rst | 1 | ||||
-rw-r--r-- | drivers/block/Kconfig | 18 | ||||
-rw-r--r-- | drivers/block/Makefile | 1 | ||||
-rw-r--r-- | drivers/block/blk-uclass.c | 1 | ||||
-rw-r--r-- | drivers/block/blkmap.c | 519 | ||||
-rw-r--r-- | include/blkmap.h | 77 | ||||
-rw-r--r-- | include/dm/uclass-id.h | 1 | ||||
-rw-r--r-- | include/efi_loader.h | 4 | ||||
-rw-r--r-- | lib/efi_loader/efi_device_path.c | 30 | ||||
-rw-r--r-- | test/dm/Makefile | 1 | ||||
-rw-r--r-- | test/dm/blkmap.c | 201 |
20 files changed, 1242 insertions, 5 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index d2e245e5e90..4c17c6cb9f1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -793,6 +793,15 @@ M: Alper Nebi Yasak <alpernebiyasak@gmail.com> S: Maintained F: tools/binman/ +BLKMAP +M: Tobias Waldekranz <tobias@waldekranz.com> +S: Maintained +F: cmd/blkmap.c +F: doc/usage/blkmap.rst +F: drivers/block/blkmap.c +F: include/blkmap.h +F: test/dm/blkmap.c + BOOTDEVICE M: Simon Glass <sjg@chromium.org> S: Maintained diff --git a/boot/image-board.c b/boot/image-board.c index 7dd0c32e6e1..c602832249e 100644 --- a/boot/image-board.c +++ b/boot/image-board.c @@ -1126,7 +1126,8 @@ fallback: } /* get script subimage data address and length */ - if (fit_image_get_data(fit_hdr, noffset, &fit_data, &fit_len)) { + if (fit_image_get_data_and_size(fit_hdr, noffset, + &fit_data, &fit_len)) { puts("Could not find script subimage data\n"); return 1; } diff --git a/cmd/Kconfig b/cmd/Kconfig index 8c9b430f99f..bab35fc6678 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -1980,6 +1980,25 @@ config CMD_BLOCK_CACHE during development, but also allows the cache to be disabled when it might hurt performance (e.g. when using the ums command). +config CMD_BLKMAP + bool "blkmap - Composable virtual block devices" + depends on BLKMAP + default y if BLKMAP + help + Create virtual block devices that are backed by various sources, + e.g. RAM, or parts of an existing block device. Though much more + rudimentary, it borrows a lot of ideas from Linux's device mapper + subsystem. + + Example use-cases: + - Treat a region of RAM as a block device, i.e. a RAM disk. This let's + you extract files from filesystem images stored in RAM (perhaps as a + result of a TFTP transfer). + - Create a virtual partition on an existing device. This let's you + access filesystems that aren't stored at an exact partition + boundary. A common example is a filesystem image embedded in an FIT + image. + config CMD_BUTTON bool "button" depends on BUTTON diff --git a/cmd/Makefile b/cmd/Makefile index e032091621d..054ef420177 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_CMD_BCB) += bcb.o obj-$(CONFIG_CMD_BDI) += bdinfo.o obj-$(CONFIG_CMD_BIND) += bind.o obj-$(CONFIG_CMD_BINOP) += binop.o +obj-$(CONFIG_CMD_BLKMAP) += blkmap.o obj-$(CONFIG_CMD_BLOBLIST) += bloblist.o obj-$(CONFIG_CMD_BLOCK_CACHE) += blkcache.o obj-$(CONFIG_CMD_BMP) += bmp.o diff --git a/cmd/blk_common.c b/cmd/blk_common.c index 75a072caf51..9f9d4327a99 100644 --- a/cmd/blk_common.c +++ b/cmd/blk_common.c @@ -11,6 +11,7 @@ #include <common.h> #include <blk.h> #include <command.h> +#include <mapmem.h> int blk_common_cmd(int argc, char *const argv[], enum uclass_id uclass_id, int *cur_devnump) @@ -63,31 +64,37 @@ int blk_common_cmd(int argc, char *const argv[], enum uclass_id uclass_id, default: /* at least 4 args */ if (strcmp(argv[1], "read") == 0) { - ulong addr = hextoul(argv[2], NULL); + phys_addr_t paddr = hextoul(argv[2], NULL); lbaint_t blk = hextoul(argv[3], NULL); ulong cnt = hextoul(argv[4], NULL); + void *vaddr; ulong n; printf("\n%s read: device %d block # "LBAFU", count %lu ... ", if_name, *cur_devnump, blk, cnt); + vaddr = map_sysmem(paddr, 512 * cnt); n = blk_read_devnum(uclass_id, *cur_devnump, blk, cnt, - (ulong *)addr); + vaddr); + unmap_sysmem(vaddr); printf("%ld blocks read: %s\n", n, n == cnt ? "OK" : "ERROR"); return n == cnt ? 0 : 1; } else if (strcmp(argv[1], "write") == 0) { - ulong addr = hextoul(argv[2], NULL); + phys_addr_t paddr = hextoul(argv[2], NULL); lbaint_t blk = hextoul(argv[3], NULL); ulong cnt = hextoul(argv[4], NULL); + void *vaddr; ulong n; printf("\n%s write: device %d block # "LBAFU", count %lu ... ", if_name, *cur_devnump, blk, cnt); + vaddr = map_sysmem(paddr, 512 * cnt); n = blk_write_devnum(uclass_id, *cur_devnump, blk, cnt, - (ulong *)addr); + vaddr); + unmap_sysmem(vaddr); printf("%ld blocks written: %s\n", n, n == cnt ? "OK" : "ERROR"); diff --git a/cmd/blkmap.c b/cmd/blkmap.c new file mode 100644 index 00000000000..b34c0130728 --- /dev/null +++ b/cmd/blkmap.c @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2023 Addiva Elektronik + * Author: Tobias Waldekranz <tobias@waldekranz.com> + */ + +#include <blk.h> +#include <blkmap.h> +#include <common.h> +#include <command.h> +#include <malloc.h> +#include <dm/device.h> + +static int blkmap_curr_dev; + +struct map_ctx { + struct udevice *dev; + lbaint_t blknr, blkcnt; +}; + +typedef int (*map_parser_fn)(struct map_ctx *ctx, int argc, char *const argv[]); + +struct map_handler { + const char *name; + map_parser_fn fn; +}; + +int do_blkmap_map_linear(struct map_ctx *ctx, int argc, char *const argv[]) +{ + struct blk_desc *lbd; + int err, ldevnum; + lbaint_t lblknr; + + if (argc < 4) + return CMD_RET_USAGE; + + ldevnum = dectoul(argv[2], NULL); + lblknr = dectoul(argv[3], NULL); + + lbd = blk_get_devnum_by_uclass_idname(argv[1], ldevnum); + if (!lbd) { + printf("Found no device matching \"%s %d\"\n", + argv[1], ldevnum); + return CMD_RET_FAILURE; + } + + err = blkmap_map_linear(ctx->dev, ctx->blknr, ctx->blkcnt, + lbd->bdev, lblknr); + if (err) { + printf("Unable to map \"%s %d\" at block 0x" LBAF ": %d\n", + argv[1], ldevnum, ctx->blknr, err); + + return CMD_RET_FAILURE; + } + + printf("Block 0x" LBAF "+0x" LBAF " mapped to block 0x" LBAF " of \"%s %d\"\n", + ctx->blknr, ctx->blkcnt, lblknr, argv[1], ldevnum); + return CMD_RET_SUCCESS; +} + +int do_blkmap_map_mem(struct map_ctx *ctx, int argc, char *const argv[]) +{ + phys_addr_t addr; + int err; + + if (argc < 2) + return CMD_RET_USAGE; + + addr = hextoul(argv[1], NULL); + + err = blkmap_map_pmem(ctx->dev, ctx->blknr, ctx->blkcnt, addr); + if (err) { + printf("Unable to map %#llx at block 0x" LBAF ": %d\n", + (unsigned long long)addr, ctx->blknr, err); + return CMD_RET_FAILURE; + } + + printf("Block 0x" LBAF "+0x" LBAF " mapped to %#llx\n", + ctx->blknr, ctx->blkcnt, (unsigned long long)addr); + return CMD_RET_SUCCESS; +} + +struct map_handler map_handlers[] = { + { .name = "linear", .fn = do_blkmap_map_linear }, + { .name = "mem", .fn = do_blkmap_map_mem }, + + { .name = NULL } +}; + +static int do_blkmap_map(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + struct map_handler *handler; + struct map_ctx ctx; + + if (argc < 5) + return CMD_RET_USAGE; + + ctx.dev = blkmap_from_label(argv[1]); + if (!ctx.dev) { + printf("\"%s\" is not the name of any known blkmap\n", argv[1]); + return CMD_RET_FAILURE; + } + + ctx.blknr = hextoul(argv[2], NULL); + ctx.blkcnt = hextoul(argv[3], NULL); + argc -= 4; + argv += 4; + + for (handler = map_handlers; handler->name; handler++) { + if (!strcmp(handler->name, argv[0])) + return handler->fn(&ctx, argc, argv); + } + + printf("Unknown map type \"%s\"\n", argv[0]); + return CMD_RET_USAGE; +} + +static int do_blkmap_create(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + const char *label; + int err; + + if (argc != 2) + return CMD_RET_USAGE; + + label = argv[1]; + + err = blkmap_create(label, NULL); + if (err) { + printf("Unable to create \"%s\": %d\n", label, err); + return CMD_RET_FAILURE; + } + + printf("Created \"%s\"\n", label); + return CMD_RET_SUCCESS; +} + +static int do_blkmap_destroy(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + struct udevice *dev; + const char *label; + int err; + + if (argc != 2) + return CMD_RET_USAGE; + + label = argv[1]; + + dev = blkmap_from_label(label); + if (!dev) { + printf("\"%s\" is not the name of any known blkmap\n", label); + return CMD_RET_FAILURE; + } + + err = blkmap_destroy(dev); + if (err) { + printf("Unable to destroy \"%s\": %d\n", label, err); + return CMD_RET_FAILURE; + } + + printf("Destroyed \"%s\"\n", label); + return CMD_RET_SUCCESS; +} + +static int do_blkmap_get(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + struct udevice *dev; + const char *label; + int err; + + if (argc < 3) + return CMD_RET_USAGE; + + label = argv[1]; + + dev = blkmap_from_label(label); + if (!dev) { + printf("\"%s\" is not the name of any known blkmap\n", label); + return CMD_RET_FAILURE; + } + + if (!strcmp(argv[2], "dev")) { + if (argc == 3) { + printf("%d\n", dev_seq(dev)); + } else { + err = env_set_hex(argv[3], dev_seq(dev)); + if (err) + return CMD_RET_FAILURE; + } + } else { + return CMD_RET_USAGE; + } + + return CMD_RET_SUCCESS; +} + +static int do_blkmap_common(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + /* The subcommand parsing pops the original argv[0] ("blkmap") + * which blk_common_cmd expects. Push it back again. + */ + argc++; + argv--; + + return blk_common_cmd(argc, argv, UCLASS_BLKMAP, &blkmap_curr_dev); +} + +U_BOOT_CMD_WITH_SUBCMDS( + blkmap, "Composeable virtual block devices", + "info - list configured devices\n" + "blkmap part - list available partitions on current blkmap device\n" + "blkmap dev [<dev>] - show or set current blkmap device\n" + "blkmap read <addr> <blk#> <cnt>\n" + "blkmap write <addr> <blk#> <cnt>\n" + "blkmap get <label> dev [<var>] - store device number in variable\n" + "blkmap create <label> - create device\n" + "blkmap destroy <label> - destroy device\n" + "blkmap map <label> <blk#> <cnt> linear <interface> <dev> <blk#> - device mapping\n" + "blkmap map <label> <blk#> <cnt> mem <addr> - memory mapping\n", + U_BOOT_SUBCMD_MKENT(info, 2, 1, do_blkmap_common), + U_BOOT_SUBCMD_MKENT(part, 2, 1, do_blkmap_common), + U_BOOT_SUBCMD_MKENT(dev, 4, 1, do_blkmap_common), + U_BOOT_SUBCMD_MKENT(read, 5, 1, do_blkmap_common), + U_BOOT_SUBCMD_MKENT(write, 5, 1, do_blkmap_common), + U_BOOT_SUBCMD_MKENT(get, 5, 1, do_blkmap_get), + U_BOOT_SUBCMD_MKENT(create, 2, 1, do_blkmap_create), + U_BOOT_SUBCMD_MKENT(destroy, 2, 1, do_blkmap_destroy), + U_BOOT_SUBCMD_MKENT(map, 32, 1, do_blkmap_map)); diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index cbace259f80..3a1f14c60fd 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -147,6 +147,7 @@ CONFIG_ADC=y CONFIG_ADC_SANDBOX=y CONFIG_AXI=y CONFIG_AXI_SANDBOX=y +CONFIG_BLKMAP=y CONFIG_SYS_IDE_MAXBUS=1 CONFIG_SYS_ATA_BASE_ADDR=0x100 CONFIG_SYS_ATA_STRIDE=4 diff --git a/disk/part.c b/disk/part.c index d449635254e..35300df5903 100644 --- a/disk/part.c +++ b/disk/part.c @@ -140,6 +140,7 @@ void dev_print(struct blk_desc *dev_desc) case UCLASS_NVME: case UCLASS_PVBLOCK: case UCLASS_HOST: + case UCLASS_BLKMAP: printf ("Vendor: %s Rev: %s Prod: %s\n", dev_desc->vendor, dev_desc->revision, diff --git a/doc/usage/blkmap.rst b/doc/usage/blkmap.rst new file mode 100644 index 00000000000..dbfa8e5aad7 --- /dev/null +++ b/doc/usage/blkmap.rst @@ -0,0 +1,111 @@ +.. SPDX-License-Identifier: GPL-2.0+ +.. +.. Copyright (c) 2023 Addiva Elektronik +.. Author: Tobias Waldekranz <tobias@waldekranz.com> + +Block Maps (blkmap) +=================== + +Block maps are a way of looking at various sources of data through the +lens of a regular block device. It lets you treat devices that are not +block devices, like RAM, as if they were. It also lets you export a +slice of an existing block device, which does not have to correspond +to a partition boundary, as a new block device. + +This is primarily useful because U-Boot's filesystem drivers only +operate on block devices, so a block map lets you access filesystems +wherever they might be located. + +The implementation is loosely modeled on Linux's "Device Mapper" +subsystem, see `kernel documentation`_ for more information. + +.. _kernel documentation: https://docs.kernel.org/admin-guide/device-mapper/index.html + + +Example: Netbooting an Ext4 Image +--------------------------------- + +Say that our system is using an Ext4 filesystem as its rootfs, where +the kernel is stored in ``/boot``. This image is then typically stored +in an eMMC partition. In this configuration, we can use something like +``load mmc 0 ${kernel_addr_r} /boot/Image`` to load the kernel image +into the expected location, and then boot the system. No problems. + +Now imagine that during development, or as a recovery mechanism, we +want to boot the same type of image by downloading it over the +network. Getting the image to the target is easy enough: + +:: + + dhcp ${ramdisk_addr_r} rootfs.ext4 + +But now we are faced with a predicament: how to we extract the kernel +image? Block maps to the rescue! + +We start by creating a new device: + +:: + + blkmap create netboot + +Before setting up the mapping, we figure out the size of the +downloaded file, in blocks: + +:: + + setexpr fileblks ${filesize} + 0x1ff + setexpr fileblks ${filesize} / 0x200 + +Then we can add a mapping to the start of our device, backed by the +memory at `${loadaddr}`: + +:: + + blkmap map netboot 0 ${fileblks} mem ${fileaddr} + +Now we can access the filesystem via the virtual device: + +:: + + blkmap get netboot dev devnum + load blkmap ${devnum} ${kernel_addr_r} /boot/Image + + +Example: Accessing a filesystem inside an FIT image +--------------------------------------------------- + +In this example, an FIT image is stored in an eMMC partition. We would +like to read the file ``/etc/version``, stored inside a Squashfs image +in the FIT. Since the Squashfs image is not stored on a partition +boundary, there is no way of accessing it via ``load mmc ...``. + +What we can to instead is to first figure out the offset and size of +the filesystem: + +:: + + mmc dev 0 + mmc read ${loadaddr} 0 0x100 + + fdt addr ${loadaddr} + fdt get value squashaddr /images/ramdisk data-position + fdt get value squashsize /images/ramdisk data-size + + setexpr squashblk ${squashaddr} / 0x200 + setexpr squashsize ${squashsize} + 0x1ff + setexpr squashsize ${squashsize} / 0x200 + +Then we can create a block map that maps to that slice of the full +partition: + +:: + + blkmap create sq + blkmap map sq 0 ${squashsize} linear mmc 0 ${squashblk} + +Now we can access the filesystem: + +:: + + blkmap get sq dev devnum + load blkmap ${devnum} ${loadaddr} /etc/version diff --git a/doc/usage/index.rst b/doc/usage/index.rst index bc85e1d49a9..729541bcff5 100644 --- a/doc/usage/index.rst +++ b/doc/usage/index.rst @@ -4,6 +4,7 @@ Use U-Boot .. toctree:: :maxdepth: 1 + blkmap dfu environment fdt_overlays diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index e95da48bdc0..5a1aeb3d2b4 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -67,6 +67,24 @@ config BLOCK_CACHE it will prevent repeated reads from directory structures and other filesystem data structures. +config BLKMAP + bool "Composable virtual block devices (blkmap)" + depends on BLK + help + Create virtual block devices that are backed by various sources, + e.g. RAM, or parts of an existing block device. Though much more + rudimentary, it borrows a lot of ideas from Linux's device mapper + subsystem. + + Example use-cases: + - Treat a region of RAM as a block device, i.e. a RAM disk. This let's + you extract files from filesystem images stored in RAM (perhaps as a + result of a TFTP transfer). + - Create a virtual partition on an existing device. This let's you + access filesystems that aren't stored at an exact partition + boundary. A common example is a filesystem image embedded in an FIT + image. + config SPL_BLOCK_CACHE bool "Use block device cache in SPL" depends on SPL_BLK diff --git a/drivers/block/Makefile b/drivers/block/Makefile index f12447d78d8..a161d145fd3 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_IDE) += ide.o endif obj-$(CONFIG_SANDBOX) += sandbox.o host-uclass.o host_dev.o obj-$(CONFIG_$(SPL_TPL_)BLOCK_CACHE) += blkcache.o +obj-$(CONFIG_BLKMAP) += blkmap.o obj-$(CONFIG_EFI_MEDIA) += efi-media-uclass.o obj-$(CONFIG_EFI_MEDIA_SANDBOX) += sb_efi_media.o diff --git a/drivers/block/blk-uclass.c b/drivers/block/blk-uclass.c index c69fc4d5182..cb73faaedaf 100644 --- a/drivers/block/blk-uclass.c +++ b/drivers/block/blk-uclass.c @@ -32,6 +32,7 @@ static struct { { UCLASS_EFI_LOADER, "efiloader" }, { UCLASS_VIRTIO, "virtio" }, { UCLASS_PVBLOCK, "pvblock" }, + { UCLASS_BLKMAP, "blkmap" }, }; static enum uclass_id uclass_name_to_iftype(const char *uclass_idname) diff --git a/drivers/block/blkmap.c b/drivers/block/blkmap.c new file mode 100644 index 00000000000..2bb0acc20f2 --- /dev/null +++ b/drivers/block/blkmap.c @@ -0,0 +1,519 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2023 Addiva Elektronik + * Author: Tobias Waldekranz <tobias@waldekranz.com> + */ + +#include <common.h> +#include <blk.h> +#include <blkmap.h> +#include <dm.h> +#include <malloc.h> +#include <mapmem.h> +#include <part.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dm/root.h> + +struct blkmap; + +/** + * struct blkmap_slice - Region mapped to a blkmap + * + * Common data for a region mapped to a blkmap, specialized by each + * map type. + * + * @node: List node used to associate this slice with a blkmap + * @blknr: Start block number of the mapping + * @blkcnt: Number of blocks covered by this mapping + */ +struct blkmap_slice { + struct list_head node; + + lbaint_t blknr; + lbaint_t blkcnt; + + /** + * @read: - Read from slice + * + * @read.bm: Blkmap to which this slice belongs + * @read.bms: This slice + * @read.blknr: Start block number to read from + * @read.blkcnt: Number of blocks to read + * @read.buffer: Buffer to store read data to + */ + ulong (*read)(struct blkmap *bm, struct blkmap_slice *bms, + lbaint_t blknr, lbaint_t blkcnt, void *buffer); + + /** + * @write: - Write to slice + * + * @write.bm: Blkmap to which this slice belongs + * @write.bms: This slice + * @write.blknr: Start block number to write to + * @write.blkcnt: Number of blocks to write + * @write.buffer: Data to be written + */ + ulong (*write)(struct blkmap *bm, struct blkmap_slice *bms, + lbaint_t blknr, lbaint_t blkcnt, const void *buffer); + + /** + * @destroy: - Tear down slice + * + * @read.bm: Blkmap to which this slice belongs + * @read.bms: This slice + */ + void (*destroy)(struct blkmap *bm, struct blkmap_slice *bms); +}; + +/** + * struct blkmap - Block map + * + * Data associated with a blkmap. + * + * @label: Human readable name of this blkmap + * @blk: Underlying block device + * @slices: List of slices associated with this blkmap + */ +struct blkmap { + char *label; + struct udevice *blk; + struct list_head slices; +}; + +static bool blkmap_slice_contains(struct blkmap_slice *bms, lbaint_t blknr) +{ + return (blknr >= bms->blknr) && (blknr < (bms->blknr + bms->blkcnt)); +} + +static bool blkmap_slice_available(struct blkmap *bm, struct blkmap_slice *new) +{ + struct blkmap_slice *bms; + lbaint_t first, last; + + first = new->blknr; + last = new->blknr + new->blkcnt - 1; + + list_for_each_entry(bms, &bm->slices, node) { + if (blkmap_slice_contains(bms, first) || + blkmap_slice_contains(bms, last) || + blkmap_slice_contains(new, bms->blknr) || + blkmap_slice_contains(new, bms->blknr + bms->blkcnt - 1)) + return false; + } + + return true; +} + +static int blkmap_slice_add(struct blkmap *bm, struct blkmap_slice *new) +{ + struct blk_desc *bd = dev_get_uclass_plat(bm->blk); + struct list_head *insert = &bm->slices; + struct blkmap_slice *bms; + + if (!blkmap_slice_available(bm, new)) + return -EBUSY; + + list_for_each_entry(bms, &bm->slices, node) { + if (bms->blknr < new->blknr) + continue; + + insert = &bms->node; + break; + } + + list_add_tail(&new->node, insert); + + /* Disk might have grown, update the size */ + bms = list_last_entry(&bm->slices, struct blkmap_slice, node); + bd->lba = bms->blknr + bms->blkcnt; + return 0; +} + +/** + * struct blkmap_linear - Linear mapping to other block device + * + * @slice: Common map data + * @blk: Target block device of this mapping + * @blknr: Start block number of the target device + */ +struct blkmap_linear { + struct blkmap_slice slice; + + struct udevice *blk; + lbaint_t blknr; +}; + +static ulong blkmap_linear_read(struct blkmap *bm, struct blkmap_slice *bms, + lbaint_t blknr, lbaint_t blkcnt, void *buffer) +{ + struct blkmap_linear *bml = container_of(bms, struct blkmap_linear, slice); + + return blk_read(bml->blk, bml->blknr + blknr, blkcnt, buffer); +} + +static ulong blkmap_linear_write(struct blkmap *bm, struct blkmap_slice *bms, + lbaint_t blknr, lbaint_t blkcnt, + const void *buffer) +{ + struct blkmap_linear *bml = container_of(bms, struct blkmap_linear, slice); + + return blk_write(bml->blk, bml->blknr + blknr, blkcnt, buffer); +} + +int blkmap_map_linear(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, + struct udevice *lblk, lbaint_t lblknr) +{ + struct blkmap *bm = dev_get_plat(dev); + struct blkmap_linear *linear; + struct blk_desc *bd, *lbd; + int err; + + bd = dev_get_uclass_plat(bm->blk); + lbd = dev_get_uclass_plat(lblk); + if (lbd->blksz != bd->blksz) + /* We could support block size translation, but we + * don't yet. + */ + return -EINVAL; + + linear = malloc(sizeof(*linear)); + if (!linear) + return -ENOMEM; + + *linear = (struct blkmap_linear) { + .slice = { + .blknr = blknr, + .blkcnt = blkcnt, + + .read = blkmap_linear_read, + .write = blkmap_linear_write, + }, + + .blk = lblk, + .blknr = lblknr, + }; + + err = blkmap_slice_add(bm, &linear->slice); + if (err) + free(linear); + + return err; +} + +/** + * struct blkmap_mem - Memory mapping + * + * @slice: Common map data + * @addr: Target memory region of this mapping + * @remapped: True if @addr is backed by a physical to virtual memory + * mapping that must be torn down at the end of this mapping's + * lifetime. + */ +struct blkmap_mem { + struct blkmap_slice slice; + void *addr; + bool remapped; +}; + +static ulong blkmap_mem_read(struct blkmap *bm, struct blkmap_slice *bms, + lbaint_t blknr, lbaint_t blkcnt, void *buffer) +{ + struct blkmap_mem *bmm = container_of(bms, struct blkmap_mem, slice); + struct blk_desc *bd = dev_get_uclass_plat(bm->blk); + char *src; + + src = bmm->addr + (blknr << bd->log2blksz); + memcpy(buffer, src, blkcnt << bd->log2blksz); + return blkcnt; +} + +static ulong blkmap_mem_write(struct blkmap *bm, struct blkmap_slice *bms, + lbaint_t blknr, lbaint_t blkcnt, + const void *buffer) +{ + struct blkmap_mem *bmm = container_of(bms, struct blkmap_mem, slice); + struct blk_desc *bd = dev_get_uclass_plat(bm->blk); + char *dst; + + dst = bmm->addr + (blknr << bd->log2blksz); + memcpy(dst, buffer, blkcnt << bd->log2blksz); + return blkcnt; +} + +static void blkmap_mem_destroy(struct blkmap *bm, struct blkmap_slice *bms) +{ + struct blkmap_mem *bmm = container_of(bms, struct blkmap_mem, slice); + + if (bmm->remapped) + unmap_sysmem(bmm->addr); +} + +int __blkmap_map_mem(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, + void *addr, bool remapped) +{ + struct blkmap *bm = dev_get_plat(dev); + struct blkmap_mem *bmm; + int err; + + bmm = malloc(sizeof(*bmm)); + if (!bmm) + return -ENOMEM; + + *bmm = (struct blkmap_mem) { + .slice = { + .blknr = blknr, + .blkcnt = blkcnt, + + .read = blkmap_mem_read, + .write = blkmap_mem_write, + .destroy = blkmap_mem_destroy, + }, + + .addr = addr, + .remapped = remapped, + }; + + err = blkmap_slice_add(bm, &bmm->slice); + if (err) + free(bmm); + + return err; +} + +int blkmap_map_mem(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, + void *addr) +{ + return __blkmap_map_mem(dev, blknr, blkcnt, addr, false); +} + +int blkmap_map_pmem(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, + phys_addr_t paddr) +{ + struct blkmap *bm = dev_get_plat(dev); + struct blk_desc *bd = dev_get_uclass_plat(bm->blk); + void *addr; + int err; + + addr = map_sysmem(paddr, blkcnt << bd->log2blksz); + if (!addr) + return -ENOMEM; + + err = __blkmap_map_mem(dev, blknr, blkcnt, addr, true); + if (err) + unmap_sysmem(addr); + + return err; +} + +static ulong blkmap_blk_read_slice(struct blkmap *bm, struct blkmap_slice *bms, + lbaint_t blknr, lbaint_t blkcnt, + void *buffer) +{ + lbaint_t nr, cnt; + + nr = blknr - bms->blknr; + cnt = (blkcnt < bms->blkcnt) ? blkcnt : bms->blkcnt; + return bms->read(bm, bms, nr, cnt, buffer); +} + +static ulong blkmap_blk_read(struct udevice *dev, lbaint_t blknr, + lbaint_t blkcnt, void *buffer) +{ + struct blk_desc *bd = dev_get_uclass_plat(dev); + struct blkmap *bm = dev_get_plat(dev->parent); + struct blkmap_slice *bms; + lbaint_t cnt, total = 0; + + list_for_each_entry(bms, &bm->slices, node) { + if (!blkmap_slice_contains(bms, blknr)) + continue; + + cnt = blkmap_blk_read_slice(bm, bms, blknr, blkcnt, buffer); + blknr += cnt; + blkcnt -= cnt; + buffer += cnt << bd->log2blksz; + total += cnt; + } + + return total; +} + +static ulong blkmap_blk_write_slice(struct blkmap *bm, struct blkmap_slice *bms, + lbaint_t blknr, lbaint_t blkcnt, + const void *buffer) +{ + lbaint_t nr, cnt; + + nr = blknr - bms->blknr; + cnt = (blkcnt < bms->blkcnt) ? blkcnt : bms->blkcnt; + return bms->write(bm, bms, nr, cnt, buffer); +} + +static ulong blkmap_blk_write(struct udevice *dev, lbaint_t blknr, + lbaint_t blkcnt, const void *buffer) +{ + struct blk_desc *bd = dev_get_uclass_plat(dev); + struct blkmap *bm = dev_get_plat(dev->parent); + struct blkmap_slice *bms; + lbaint_t cnt, total = 0; + + list_for_each_entry(bms, &bm->slices, node) { + if (!blkmap_slice_contains(bms, blknr)) + continue; + + cnt = blkmap_blk_write_slice(bm, bms, blknr, blkcnt, buffer); + blknr += cnt; + blkcnt -= cnt; + buffer += cnt << bd->log2blksz; + total += cnt; + } + + return total; +} + +static const struct blk_ops blkmap_blk_ops = { + .read = blkmap_blk_read, + .write = blkmap_blk_write, +}; + +U_BOOT_DRIVER(blkmap_blk) = { + .name = "blkmap_blk", + .id = UCLASS_BLK, + .ops = &blkmap_blk_ops, +}; + +int blkmap_dev_bind(struct udevice *dev) +{ + struct blkmap *bm = dev_get_plat(dev); + struct blk_desc *bd; + int err; + + err = blk_create_devicef(dev, "blkmap_blk", "blk", UCLASS_BLKMAP, + dev_seq(dev), 512, 0, &bm->blk); + if (err) + return log_msg_ret("blk", err); + + INIT_LIST_HEAD(&bm->slices); + + bd = dev_get_uclass_plat(bm->blk); + snprintf(bd->vendor, BLK_VEN_SIZE, "U-Boot"); + snprintf(bd->product, BLK_PRD_SIZE, "blkmap"); + snprintf(bd->revision, BLK_REV_SIZE, "1.0"); + + /* EFI core isn't keen on zero-sized disks, so we lie. This is + * updated with the correct size once the user adds a + * mapping. + */ + bd->lba = 1; + + return 0; +} + +int blkmap_dev_unbind(struct udevice *dev) +{ + struct blkmap *bm = dev_get_plat(dev); + struct blkmap_slice *bms, *tmp; + int err; + + list_for_each_entry_safe(bms, tmp, &bm->slices, node) { + list_del(&bms->node); + free(bms); + } + + err = device_remove(bm->blk, DM_REMOVE_NORMAL); + if (err) + return err; + + return device_unbind(bm->blk); +} + +U_BOOT_DRIVER(blkmap_root) = { + .name = "blkmap_dev", + .id = UCLASS_BLKMAP, + .bind = blkmap_dev_bind, + .unbind = blkmap_dev_unbind, + .plat_auto = sizeof(struct blkmap), +}; + +struct udevice *blkmap_from_label(const char *label) +{ + struct udevice *dev; + struct uclass *uc; + struct blkmap *bm; + + uclass_id_foreach_dev(UCLASS_BLKMAP, dev, uc) { + bm = dev_get_plat(dev); + if (bm->label && !strcmp(label, bm->label)) + return dev; + } + + return NULL; +} + +int blkmap_create(const char *label, struct udevice **devp) +{ + char *hname, *hlabel; + struct udevice *dev; + struct blkmap *bm; + size_t namelen; + int err; + + dev = blkmap_from_label(label); + if (dev) { + err = -EBUSY; + goto err; + } + + hlabel = strdup(label); + if (!hlabel) { + err = -ENOMEM; + goto err; + } + + namelen = strlen("blkmap-") + strlen(label) + 1; + hname = malloc(namelen); + if (!hname) { + err = -ENOMEM; + goto err_free_hlabel; + } + + strlcpy(hname, "blkmap-", namelen); + strlcat(hname, label, namelen); + + err = device_bind_driver(dm_root(), "blkmap_dev", hname, &dev); + if (err) + goto err_free_hname; + + device_set_name_alloced(dev); + bm = dev_get_plat(dev); + bm->label = hlabel; + + if (devp) + *devp = dev; + + return 0; + +err_free_hname: + free(hname); +err_free_hlabel: + free(hlabel); +err: + return err; +} + +int blkmap_destroy(struct udevice *dev) +{ + int err; + + err = device_remove(dev, DM_REMOVE_NORMAL); + if (err) + return err; + + return device_unbind(dev); +} + +UCLASS_DRIVER(blkmap) = { + .id = UCLASS_BLKMAP, + .name = "blkmap", +}; diff --git a/include/blkmap.h b/include/blkmap.h new file mode 100644 index 00000000000..af54583c7dd --- /dev/null +++ b/include/blkmap.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2023 Addiva Elektronik + * Author: Tobias Waldekranz <tobias@waldekranz.com> + */ + +#ifndef _BLKMAP_H +#define _BLKMAP_H + +/** + * blkmap_map_linear() - Map region of other block device + * + * @dev: Blkmap to create the mapping on + * @blknr: Start block number of the mapping + * @blkcnt: Number of blocks to map + * @lblk: The target block device of the mapping + * @lblknr: The start block number of the target device + * Returns: 0 on success, negative error code on failure + */ +int blkmap_map_linear(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, + struct udevice *lblk, lbaint_t lblknr); + +/** + * blkmap_map_mem() - Map region of memory + * + * @dev: Blkmap to create the mapping on + * @blknr: Start block number of the mapping + * @blkcnt: Number of blocks to map + * @addr: The target memory address of the mapping + * Returns: 0 on success, negative error code on failure + */ +int blkmap_map_mem(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, + void *addr); + +/** + * blkmap_map_pmem() - Map region of physical memory + * + * Ensures that a valid physical to virtual memory mapping for the + * requested region is valid for the lifetime of the mapping, on + * architectures that require it (sandbox). + * + * @dev: Blkmap to create the mapping on + * @blknr: Start block number of the mapping + * @blkcnt: Number of blocks to map + * @paddr: The target physical memory address of the mapping + * Returns: 0 on success, negative error code on failure + */ +int blkmap_map_pmem(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, + phys_addr_t paddr); + + +/** + * blkmap_from_label() - Find blkmap from label + * + * @label: Label of the requested blkmap + * Returns: A pointer to the blkmap on success, NULL on failure + */ +struct udevice *blkmap_from_label(const char *label); + +/** + * blkmap_create() - Create new blkmap + * + * @label: Label of the new blkmap + * @devp: If not NULL, updated with the address of the resulting device + * Returns: 0 on success, negative error code on failure + */ +int blkmap_create(const char *label, struct udevice **devp); + +/** + * blkmap_destroy() - Destroy blkmap + * + * @dev: The blkmap to be destroyed + * Returns: 0 on success, negative error code on failure + */ +int blkmap_destroy(struct udevice *dev); + +#endif /* _BLKMAP_H */ diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 33e43c20db6..576237b9548 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -37,6 +37,7 @@ enum uclass_id { UCLASS_AUDIO_CODEC, /* Audio codec with control and data path */ UCLASS_AXI, /* AXI bus */ UCLASS_BLK, /* Block device */ + UCLASS_BLKMAP, /* Composable virtual block device */ UCLASS_BOOTCOUNT, /* Bootcount backing store */ UCLASS_BOOTDEV, /* Boot device for locating an OS to boot */ UCLASS_BOOTMETH, /* Bootmethod for booting an OS */ diff --git a/include/efi_loader.h b/include/efi_loader.h index cee04cbb9dc..4b89b9816d4 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -134,6 +134,10 @@ static inline efi_status_t efi_launch_capsules(void) #define U_BOOT_GUID \ EFI_GUID(0xe61d73b9, 0xa384, 0x4acc, \ 0xae, 0xab, 0x82, 0xe8, 0x28, 0xf3, 0x62, 0x8b) +/* GUID used as root for blkmap devices */ +#define U_BOOT_BLKMAP_DEV_GUID \ + EFI_GUID(0x4cad859d, 0xd644, 0x42ff, \ + 0x87, 0x0b, 0xc0, 0x2e, 0xac, 0x05, 0x58, 0x63) /* GUID used as host device on sandbox */ #define U_BOOT_HOST_DEV_GUID \ EFI_GUID(0xbbe4e671, 0x5773, 0x4ea1, \ diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index ea097839dbe..e2e98a39be1 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -21,6 +21,9 @@ #include <asm-generic/unaligned.h> #include <linux/compat.h> /* U16_MAX */ +#ifdef CONFIG_BLKMAP +const efi_guid_t efi_guid_blkmap_dev = U_BOOT_BLKMAP_DEV_GUID; +#endif #ifdef CONFIG_SANDBOX const efi_guid_t efi_guid_host_dev = U_BOOT_HOST_DEV_GUID; #endif @@ -556,6 +559,16 @@ __maybe_unused static unsigned int dp_size(struct udevice *dev) return dp_size(dev->parent) + sizeof(struct efi_device_path_vendor) + 1; #endif +#ifdef CONFIG_BLKMAP + case UCLASS_BLKMAP: + /* + * blkmap devices will be represented as a vendor + * device node with an extra byte for the device + * number. + */ + return dp_size(dev->parent) + + sizeof(struct efi_device_path_vendor) + 1; +#endif default: return dp_size(dev->parent); } @@ -613,6 +626,23 @@ __maybe_unused static void *dp_fill(void *buf, struct udevice *dev) #endif case UCLASS_BLK: switch (dev->parent->uclass->uc_drv->id) { +#ifdef CONFIG_BLKMAP + case UCLASS_BLKMAP: { + struct efi_device_path_vendor *dp; + struct blk_desc *desc = dev_get_uclass_plat(dev); + + dp_fill(buf, dev->parent); + dp = buf; + ++dp; + dp->dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE; + dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_VENDOR; + dp->dp.length = sizeof(*dp) + 1; + memcpy(&dp->guid, &efi_guid_blkmap_dev, + sizeof(efi_guid_t)); + dp->vendor_data[0] = desc->devnum; + return &dp->vendor_data[1]; + } +#endif #ifdef CONFIG_SANDBOX case UCLASS_HOST: { /* stop traversing parents at this point: */ diff --git a/test/dm/Makefile b/test/dm/Makefile index 7a79b6e1a25..e15bdbf04bc 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_ADC) += adc.o obj-$(CONFIG_SOUND) += audio.o obj-$(CONFIG_AXI) += axi.o obj-$(CONFIG_BLK) += blk.o +obj-$(CONFIG_BLKMAP) += blkmap.o obj-$(CONFIG_BUTTON) += button.o obj-$(CONFIG_DM_BOOTCOUNT) += bootcount.o obj-$(CONFIG_DM_REBOOT_MODE) += reboot-mode.o diff --git a/test/dm/blkmap.c b/test/dm/blkmap.c new file mode 100644 index 00000000000..7a163d6eaef --- /dev/null +++ b/test/dm/blkmap.c @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2023 Addiva Elektronik + * Author: Tobias Waldekranz <tobias@waldekranz.com> + */ + +#include <common.h> +#include <blk.h> +#include <blkmap.h> +#include <dm.h> +#include <asm/test.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +#define BLKSZ 0x200 + +struct mapping { + int src; + int cnt; + int dst; +}; + +const struct mapping unordered_mapping[] = { + { 0, 1, 3 }, + { 1, 3, 0 }, + { 4, 2, 6 }, + { 6, 2, 4 }, + + { 0, 0, 0 } +}; + +const struct mapping identity_mapping[] = { + { 0, 8, 0 }, + + { 0, 0, 0 } +}; + +static char identity[8 * BLKSZ]; +static char unordered[8 * BLKSZ]; +static char buffer[8 * BLKSZ]; + +static void mkblob(void *base, const struct mapping *m) +{ + int nr; + + for (; m->cnt; m++) { + for (nr = 0; nr < m->cnt; nr++) { + memset(base + (m->dst + nr) * BLKSZ, + m->src + nr, BLKSZ); + } + } +} + +static int dm_test_blkmap_read(struct unit_test_state *uts) +{ + struct udevice *dev, *blk; + const struct mapping *m; + + ut_assertok(blkmap_create("rdtest", &dev)); + ut_assertok(blk_get_from_parent(dev, &blk)); + + /* Generate an ordered and an unordered pattern in memory */ + mkblob(unordered, unordered_mapping); + mkblob(identity, identity_mapping); + + /* Create a blkmap that cancels out the disorder */ + for (m = unordered_mapping; m->cnt; m++) { + ut_assertok(blkmap_map_mem(dev, m->src, m->cnt, + unordered + m->dst * BLKSZ)); + } + + /* Read out the data via the blkmap device to another area, + * and verify that it matches the ordered pattern. + */ + ut_asserteq(8, blk_read(blk, 0, 8, buffer)); + ut_assertok(memcmp(buffer, identity, sizeof(buffer))); + + ut_assertok(blkmap_destroy(dev)); + return 0; +} +DM_TEST(dm_test_blkmap_read, 0); + +static int dm_test_blkmap_write(struct unit_test_state *uts) +{ + struct udevice *dev, *blk; + const struct mapping *m; + + ut_assertok(blkmap_create("wrtest", &dev)); + ut_assertok(blk_get_from_parent(dev, &blk)); + + /* Generate an ordered and an unordered pattern in memory */ + mkblob(unordered, unordered_mapping); + mkblob(identity, identity_mapping); + + /* Create a blkmap that mimics the disorder */ + for (m = unordered_mapping; m->cnt; m++) { + ut_assertok(blkmap_map_mem(dev, m->src, m->cnt, + buffer + m->dst * BLKSZ)); + } + + /* Write the ordered data via the blkmap device to another + * area, and verify that the result matches the unordered + * pattern. + */ + ut_asserteq(8, blk_write(blk, 0, 8, identity)); + ut_assertok(memcmp(buffer, unordered, sizeof(buffer))); + + ut_assertok(blkmap_destroy(dev)); + return 0; +} +DM_TEST(dm_test_blkmap_write, 0); + +static int dm_test_blkmap_slicing(struct unit_test_state *uts) +{ + struct udevice *dev; + + ut_assertok(blkmap_create("slicetest", &dev)); + + ut_assertok(blkmap_map_mem(dev, 8, 8, NULL)); + + /* Can't overlap on the low end */ + ut_asserteq(-EBUSY, blkmap_map_mem(dev, 4, 5, NULL)); + /* Can't be inside */ + ut_asserteq(-EBUSY, blkmap_map_mem(dev, 10, 2, NULL)); + /* Can't overlap on the high end */ + ut_asserteq(-EBUSY, blkmap_map_mem(dev, 15, 4, NULL)); + + /* But we should be able to add slices right before and + * after + */ + ut_assertok(blkmap_map_mem(dev, 4, 4, NULL)); + ut_assertok(blkmap_map_mem(dev, 16, 4, NULL)); + + ut_assertok(blkmap_destroy(dev)); + return 0; +} +DM_TEST(dm_test_blkmap_slicing, 0); + +static int dm_test_blkmap_creation(struct unit_test_state *uts) +{ + struct udevice *first, *second; + + ut_assertok(blkmap_create("first", &first)); + + /* Can't have two "first"s */ + ut_asserteq(-EBUSY, blkmap_create("first", &second)); + + /* But "second" should be fine */ + ut_assertok(blkmap_create("second", &second)); + + /* Once "first" is destroyed, we should be able to create it + * again + */ + ut_assertok(blkmap_destroy(first)); + ut_assertok(blkmap_create("first", &first)); + + ut_assertok(blkmap_destroy(first)); + ut_assertok(blkmap_destroy(second)); + return 0; +} +DM_TEST(dm_test_blkmap_creation, 0); + +static int dm_test_cmd_blkmap(struct unit_test_state *uts) +{ + ulong loadaddr = env_get_hex("loadaddr", 0); + struct udevice *dev; + + console_record_reset(); + + ut_assertok(run_command("blkmap info", 0)); + ut_assert_console_end(); + + ut_assertok(run_command("blkmap create ramdisk", 0)); + ut_assert_nextline("Created \"ramdisk\""); + ut_assert_console_end(); + + ut_assertnonnull((dev = blkmap_from_label("ramdisk"))); + + ut_assertok(run_commandf("blkmap map ramdisk 0 800 mem 0x%lx", loadaddr)); + ut_assert_nextline("Block 0x0+0x800 mapped to 0x%lx", loadaddr); + ut_assert_console_end(); + + ut_assertok(run_command("blkmap info", 0)); + ut_assert_nextline("Device 0: Vendor: U-Boot Rev: 1.0 Prod: blkmap"); + ut_assert_nextline(" Type: Hard Disk"); + ut_assert_nextline(" Capacity: 1.0 MB = 0.0 GB (2048 x 512)"); + ut_assert_console_end(); + + ut_assertok(run_command("blkmap get ramdisk dev devnum", 0)); + ut_asserteq(dev_seq(dev), env_get_hex("devnum", 0xdeadbeef)); + + ut_assertok(run_command("blkmap destroy ramdisk", 0)); + ut_assert_nextline("Destroyed \"ramdisk\""); + ut_assert_console_end(); + + ut_assertok(run_command("blkmap info", 0)); + ut_assert_console_end(); + return 0; +} +DM_TEST(dm_test_cmd_blkmap, 0); |