[PATCH] cmd: add clone command

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

On Sat, Jun 27, 2020 at 11:15:49AM +0800, John Chau wrote:
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
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!

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

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

On Thu, Jul 02, 2020 at 12:01:21PM +0800, John Chau wrote:
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
Applied to u-boot/master, thanks!
participants (2)
-
John Chau
-
Tom Rini