summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Chau <john@harmon.hk>2020-07-02 12:01:21 +0800
committerTom Rini <trini@konsulko.com>2020-08-04 23:29:59 -0400
commit4a4830cf915e76f07cff5ce346c3ccbc987c1557 (patch)
tree42ebbb70628121c40e6cc4e757dd55d56cad003d
parent5b3ddb17baec13b4386620b533527d0f53ddeddf (diff)
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 <john@harmon.hk>
-rw-r--r--arch/Kconfig1
-rw-r--r--cmd/Kconfig8
-rw-r--r--cmd/Makefile1
-rw-r--r--cmd/clone.c126
4 files changed, 136 insertions, 0 deletions
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 <john@harmon.hk>
+ *
+ */
+
+#include <common.h>
+#include <command.h>
+#include <malloc.h>
+#include <part.h>
+#include <blk.h>
+#include <vsprintf.h>
+
+#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",
+ "<src interface> <src dev> <dest interface> <dest dev> <size[K/M/G]>\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)"
+);