diff mbox series

[v2] cmd: add clone command

Message ID 20200702033837.22799-1-johnchau.2nd@gmail.com
State Superseded
Delegated to: Tom Rini
Headers show
Series [v2] cmd: add clone command | expand

Commit Message

John Chau July 2, 2020, 3:38 a.m. UTC
From: John Chau <john@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@harmon.hk>
---
Changes for v2:
	- Coding styles cleanup
	- removed useless "select CLONE" line from cmd/Kconfig
	- added buffer size check against block size of both devices
	- enabled this by default on sandbox in arch/Kconfig
	 
 arch/Kconfig |   1 +
 cmd/Kconfig  |   8 ++++
 cmd/Makefile |   1 +
 cmd/clone.c  | 125 +++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 135 insertions(+)
 create mode 100644 cmd/clone.c
diff mbox series

Patch

diff --git a/arch/Kconfig b/arch/Kconfig
index ae9c93ed7b..4d7415d426 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -136,6 +136,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 6403bc45a5..1e95de249f 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -1067,6 +1067,14 @@  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
+	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 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..e3aba1b796
--- /dev/null
+++ b/cmd/clone.c
@@ -0,0 +1,125 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 John Chau <john@harmon.hk>
+ *
+ */
+
+#include <common.h>
+#include <command.h>
+#include <malloc.h>
+#include <blk.h>
+#include <vsprintf.h>
+
+#define BUFSIZE (1 * 1024 * 1024)
+static int do_clone(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	int srcdev, destdev;
+	const 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)"
+);