From patchwork Thu Jul 2 03:38:38 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Chau X-Patchwork-Id: 240650 List-Id: U-Boot discussion From: johnchau.2nd at gmail.com (John Chau) Date: Thu, 2 Jul 2020 11:38:38 +0800 Subject: [PATCH v2] cmd: add clone command In-Reply-To: <20200629155441.GU8432@bill-the-cat> References: <20200629155441.GU8432@bill-the-cat> Message-ID: <20200702033837.22799-1-johnchau.2nd@gmail.com> From: John Chau 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 --- Changes for v2: - Coding styles cleanup - removed useless "select CLONE" line from cmd/Kconfig - added buffer size check against block size of both devices - enabled this by default on sandbox in arch/Kconfig arch/Kconfig | 1 + cmd/Kconfig | 8 ++++ cmd/Makefile | 1 + cmd/clone.c | 125 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 135 insertions(+) create mode 100644 cmd/clone.c diff --git a/arch/Kconfig b/arch/Kconfig index ae9c93ed7b..4d7415d426 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -136,6 +136,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 6403bc45a5..1e95de249f 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -1067,6 +1067,14 @@ config CMD_MMC_SWRITE Enable support for the "mmc swrite" command to write Android sparse images to eMMC. +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 f1dd513a4b..02663a1c73 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -95,6 +95,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..e3aba1b796 --- /dev/null +++ b/cmd/clone.c @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020 John Chau + * + */ + +#include +#include +#include +#include +#include + +#define BUFSIZE (1 * 1024 * 1024) +static int do_clone(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int srcdev, destdev; + const 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)" +);