diff mbox series

cmd: add clone command

Message ID 20200627031549.4156-1-johnchau.2nd@gmail.com
State New
Headers show
Series cmd: add clone command | expand

Commit Message

John Chau June 27, 2020, 3:15 a.m. UTC
From: John Chau <john at 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 <john at harmon.hk>
---
 cmd/Kconfig  |   9 +++++
 cmd/Makefile |   1 +
 cmd/clone.c  | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 122 insertions(+)
 create mode 100644 cmd/clone.c

Comments

Tom Rini June 29, 2020, 3:54 p.m. UTC | #1
On Sat, Jun 27, 2020 at 11:15:49AM +0800, John Chau wrote:

> From: John Chau <john at 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 <john at harmon.hk>

In general, checkpatch.pl complains a lot and those need to be fixed.

[snip]
> +config CMD_CLONE
> +	bool "clone"
> +	depends on BLK
> +	select CLONE

There's no "config CLONE" so we don't need this line.

[snip]
> +//FIXME: we assume blk size of both devices can be divided by 1M, which should be normal
> +#define BUFSIZE (1 * 1024 * 1024)

Can we do some run-time checks of the block sizes of each device and
confirm this assumption and fail if it's not true?

Finally, we should also enable this on sandbox so that it's build-tested
and put through coverity.  Thanks!
diff mbox series

Patch

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 <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)"
+);