// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2020 John Chau * */ #include #include #include #include #include #include #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 * 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", " \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)" );