[PATCH v3] cmd: add clone command
John Chau
johnchau.2nd at gmail.com
Thu Jul 2 06:01:21 CEST 2020
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>
---
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
Changes for v3:
- fixed buffer size check (condition inverted)
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 --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..9ece465537
--- /dev/null
+++ b/cmd/clone.c
@@ -0,0 +1,125 @@
+// 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>
+
+#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;
+ 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)"
+);
--
2.25.1
More information about the U-Boot
mailing list