From patchwork Sat Jun 27 03:15:49 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Chau X-Patchwork-Id: 243016 List-Id: U-Boot discussion From: johnchau.2nd at gmail.com (John Chau) Date: Sat, 27 Jun 2020 11:15:49 +0800 Subject: [PATCH] cmd: add clone command Message-ID: <20200627031549.4156-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 --- cmd/Kconfig | 9 +++++ cmd/Makefile | 1 + cmd/clone.c | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 122 insertions(+) create mode 100644 cmd/clone.c diff --git a/cmd/Kconfig b/cmd/Kconfig index 6403bc45a5..44f96dedcd 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -1067,6 +1067,15 @@ 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 + select CLONE + help + Enable storage cloning over block devices, useful for + initial flashing by external block device without network + or usb gadget 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..ee36c7698c --- /dev/null +++ b/cmd/clone.c @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020 John Chau + * + */ + +#include +#include +#include +#include +#include + +//FIXME: we assume blk size of both devices can be divided by 1M, which should be normal +#define BUFSIZE (1 * 1024 * 1024) +static int do_clone(cmd_tbl_t *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; + + if (argc < 6) { + return CMD_RET_USAGE; + } + printf("\n"); + 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 (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; + } + } + 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 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; + } + unsigned long toWrite = BUFSIZE / destbz; + 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)" +);