@@ -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
@@ -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
new file mode 100644
@@ -0,0 +1,112 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 John Chau <john at 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)"
+);