| // SPDX-License-Identifier: GPL-2.0+ | |
| /* | |
| * Copyright (C) 2020 John Chau <john@harmon.hk> | |
| * | |
| */ | |
| #include <command.h> | |
| #include <malloc.h> | |
| #include <part.h> | |
| #include <blk.h> | |
| #include <time.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 = dectoul(argv[5], &unit); | |
| 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 * 1024 * 1024; | |
| break; | |
| case 'm': | |
| case 'M': | |
| requested *= 1024 * 1024; | |
| break; | |
| 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)" | |
| ); |