mirror of
https://xff.cz/git/u-boot/
synced 2025-09-03 17:52:07 +02:00
cmd: add clone command
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>
This commit is contained in:
@@ -139,6 +139,7 @@ config SANDBOX
|
|||||||
imply ACPI_PMC
|
imply ACPI_PMC
|
||||||
imply ACPI_PMC_SANDBOX
|
imply ACPI_PMC_SANDBOX
|
||||||
imply CMD_PMC
|
imply CMD_PMC
|
||||||
|
imply CMD_CLONE
|
||||||
|
|
||||||
config SH
|
config SH
|
||||||
bool "SuperH architecture"
|
bool "SuperH architecture"
|
||||||
|
@@ -1148,6 +1148,14 @@ config CMD_MMC_SWRITE
|
|||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
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
|
config CMD_MTD
|
||||||
bool "mtd"
|
bool "mtd"
|
||||||
depends on MTD
|
depends on MTD
|
||||||
|
@@ -98,6 +98,7 @@ obj-$(CONFIG_CMD_MMC) += mmc.o
|
|||||||
obj-$(CONFIG_MP) += mp.o
|
obj-$(CONFIG_MP) += mp.o
|
||||||
obj-$(CONFIG_CMD_MTD) += mtd.o
|
obj-$(CONFIG_CMD_MTD) += mtd.o
|
||||||
obj-$(CONFIG_CMD_MTDPARTS) += mtdparts.o
|
obj-$(CONFIG_CMD_MTDPARTS) += mtdparts.o
|
||||||
|
obj-$(CONFIG_CMD_CLONE) += clone.o
|
||||||
ifneq ($(CONFIG_CMD_NAND)$(CONFIG_CMD_SF),)
|
ifneq ($(CONFIG_CMD_NAND)$(CONFIG_CMD_SF),)
|
||||||
obj-y += legacy-mtd-utils.o
|
obj-y += legacy-mtd-utils.o
|
||||||
endif
|
endif
|
||||||
|
126
cmd/clone.c
Normal file
126
cmd/clone.c
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 John Chau <john@harmon.hk>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <common.h>
|
||||||
|
#include <command.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <part.h>
|
||||||
|
#include <blk.h>
|
||||||
|
#include <vsprintf.h>
|
||||||
|
|
||||||
|
#define BUFSIZE (1 * 1024 * 1024)
|
||||||
|
static int do_clone(struct cmd_tbl *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)"
|
||||||
|
);
|
Reference in New Issue
Block a user