From: John Chau <j...@harmon.hk>

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 <j...@harmon.hk>
---
 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 <j...@harmon.hk>
+ *
+ */
+
+#include <common.h>
+#include <command.h>
+#include <malloc.h>
+#include <blk.h>
+#include <vsprintf.h>
+
+//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",
+       "<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)"
+);
-- 
2.25.1

Reply via email to