From 4a4830cf915e76f07cff5ce346c3ccbc987c1557 Mon Sep 17 00:00:00 2001 From: John Chau Date: Thu, 2 Jul 2020 12:01:21 +0800 Subject: [PATCH] cmd: add clone command This patch adds a feature for block device cloning similar to dd command, this should be useful for boot-strapping a device where usb gadget or networking is not available. For instance one can clone a factory image into a blank emmc from an external sd card. Signed-off-by: John Chau --- arch/Kconfig | 1 + cmd/Kconfig | 8 ++++ cmd/Makefile | 1 + cmd/clone.c | 126 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 136 insertions(+) create mode 100644 cmd/clone.c diff --git a/arch/Kconfig b/arch/Kconfig index 7f3cbe2ec8..2174336fa0 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -139,6 +139,7 @@ config SANDBOX imply ACPI_PMC imply ACPI_PMC_SANDBOX imply CMD_PMC + imply CMD_CLONE config SH bool "SuperH architecture" diff --git a/cmd/Kconfig b/cmd/Kconfig index d7136b0e79..e11176451b 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -1148,6 +1148,14 @@ config CMD_MMC_SWRITE endif +config CMD_CLONE + bool "clone" + depends on BLK + help + Enable storage cloning over block devices, useful for + initial flashing by external block device without network + or usb support. + config CMD_MTD bool "mtd" depends on MTD diff --git a/cmd/Makefile b/cmd/Makefile index 6e0086ba07..70750375d1 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -98,6 +98,7 @@ obj-$(CONFIG_CMD_MMC) += mmc.o obj-$(CONFIG_MP) += mp.o obj-$(CONFIG_CMD_MTD) += mtd.o obj-$(CONFIG_CMD_MTDPARTS) += mtdparts.o +obj-$(CONFIG_CMD_CLONE) += clone.o ifneq ($(CONFIG_CMD_NAND)$(CONFIG_CMD_SF),) obj-y += legacy-mtd-utils.o endif diff --git a/cmd/clone.c b/cmd/clone.c new file mode 100644 index 0000000000..97747f8f08 --- /dev/null +++ b/cmd/clone.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020 John Chau + * + */ + +#include +#include +#include +#include +#include +#include + +#define BUFSIZE (1 * 1024 * 1024) +static int do_clone(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]) +{ + int srcdev, destdev; + struct blk_desc *srcdesc, *destdesc; + int srcbz, destbz, ret; + char *unit, *buf; + unsigned long wrcnt, rdcnt, requested, srcblk, destblk; + unsigned long timer; + const unsigned long buffersize = 1024 * 1024; + + if (argc < 6) + return CMD_RET_USAGE; + + srcdev = blk_get_device_by_str(argv[1], argv[2], &srcdesc); + destdev = blk_get_device_by_str(argv[3], argv[4], &destdesc); + if (srcdev < 0) { + printf("Unable to open source device\n"); + return 1; + } else if (destdev < 0) { + printf("Unable to open destination device\n"); + return 1; + } + requested = simple_strtoul(argv[5], &unit, 10); + srcbz = srcdesc->blksz; + destbz = destdesc->blksz; + + if ((srcbz * (buffersize / srcbz) != buffersize) && + (destbz * (buffersize / destbz) != buffersize)) { + printf("failed: cannot match device block sizes\n"); + return 1; + } + if (requested == 0) { + unsigned long a = srcdesc->lba * srcdesc->blksz; + unsigned long b = destdesc->lba * destdesc->blksz; + + if (a > b) + requested = a; + else + requested = b; + } else { + switch (unit[0]) { + case 'g': + case 'G': + requested *= 1024; + case 'm': + case 'M': + requested *= 1024; + case 'k': + case 'K': + requested *= 1024; + break; + } + } + printf("Copying %ld bytes from %s:%s to %s:%s\n", + requested, argv[1], argv[2], argv[3], argv[4]); + wrcnt = 0; + rdcnt = 0; + buf = (char *)malloc(BUFSIZE); + srcblk = 0; + destblk = 0; + timer = get_timer(0); + while (wrcnt < requested) { + unsigned long toread = BUFSIZE / srcbz; + unsigned long towrite = BUFSIZE / destbz; + unsigned long offset = 0; + +read: + ret = blk_dread(srcdesc, srcblk, toread, buf + offset); + if (ret < 0) { + printf("Src read error @blk %ld\n", srcblk); + goto exit; + } + rdcnt += ret * srcbz; + srcblk += ret; + if (ret < toread) { + toread -= ret; + offset += ret * srcbz; + goto read; + } + offset = 0; +write: + ret = blk_dwrite(destdesc, destblk, towrite, buf + offset); + if (ret < 0) { + printf("Dest write error @blk %ld\n", srcblk); + goto exit; + } + wrcnt += ret * destbz; + destblk += ret; + if (ret < towrite) { + towrite -= ret; + offset += ret * destbz; + goto write; + } + } + +exit: + timer = get_timer(timer); + timer = 1000 * timer / CONFIG_SYS_HZ; + printf("%ld read\n", rdcnt); + printf("%ld written\n", wrcnt); + printf("%ldms, %ldkB/s\n", timer, wrcnt / timer); + free(buf); + + return 0; +} + +U_BOOT_CMD( + clone, 6, 1, do_clone, + "simple storage cloning", + " \n" + "clone storage from 'src dev' on 'src interface' to 'dest dev' on 'dest interface' with maximum 'size' bytes (or 0 for clone to end)" +); -- 2.39.5