1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2020 John Chau <john@harmon.hk>
4  *
5  */
6 
7 #include <common.h>
8 #include <command.h>
9 #include <malloc.h>
10 #include <part.h>
11 #include <blk.h>
12 #include <vsprintf.h>
13 
14 #define BUFSIZE (1 * 1024 * 1024)
do_clone(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])15 static int do_clone(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
16 {
17 	int srcdev, destdev;
18 	struct blk_desc *srcdesc, *destdesc;
19 	int srcbz, destbz, ret;
20 	char *unit, *buf;
21 	unsigned long wrcnt, rdcnt, requested, srcblk, destblk;
22 	unsigned long timer;
23 	const unsigned long buffersize = 1024 * 1024;
24 
25 	if (argc < 6)
26 		return CMD_RET_USAGE;
27 
28 	srcdev = blk_get_device_by_str(argv[1], argv[2], &srcdesc);
29 	destdev = blk_get_device_by_str(argv[3], argv[4], &destdesc);
30 	if (srcdev < 0) {
31 		printf("Unable to open source device\n");
32 		return 1;
33 	} else if (destdev < 0) {
34 		printf("Unable to open destination device\n");
35 		return 1;
36 	}
37 	requested = simple_strtoul(argv[5], &unit, 10);
38 	srcbz = srcdesc->blksz;
39 	destbz = destdesc->blksz;
40 
41 	if ((srcbz * (buffersize / srcbz) != buffersize) ||
42 	    (destbz * (buffersize / destbz) != buffersize)) {
43 		printf("failed: cannot match device block sizes\n");
44 		return 1;
45 	}
46 	if (requested == 0) {
47 		unsigned long a = srcdesc->lba * srcdesc->blksz;
48 		unsigned long b = destdesc->lba * destdesc->blksz;
49 
50 		if (a > b)
51 			requested = a;
52 		else
53 			requested = b;
54 	} else {
55 		switch (unit[0]) {
56 		case 'g':
57 		case 'G':
58 			requested *= 1024 * 1024 * 1024;
59 			break;
60 		case 'm':
61 		case 'M':
62 			requested *= 1024 * 1024;
63 			break;
64 		case 'k':
65 		case 'K':
66 			requested *= 1024;
67 			break;
68 		}
69 	}
70 	printf("Copying %ld bytes from %s:%s to %s:%s\n",
71 	       requested, argv[1], argv[2], argv[3], argv[4]);
72 	wrcnt = 0;
73 	rdcnt = 0;
74 	buf = (char *)malloc(BUFSIZE);
75 	srcblk = 0;
76 	destblk = 0;
77 	timer = get_timer(0);
78 	while (wrcnt < requested) {
79 		unsigned long toread = BUFSIZE / srcbz;
80 		unsigned long towrite = BUFSIZE / destbz;
81 		unsigned long offset = 0;
82 
83 read:
84 		ret = blk_dread(srcdesc, srcblk, toread, buf + offset);
85 		if (ret < 0) {
86 			printf("Src read error @blk %ld\n", srcblk);
87 			goto exit;
88 		}
89 		rdcnt += ret * srcbz;
90 		srcblk += ret;
91 		if (ret < toread) {
92 			toread -= ret;
93 			offset += ret * srcbz;
94 			goto read;
95 		}
96 		offset = 0;
97 write:
98 		ret = blk_dwrite(destdesc, destblk, towrite, buf + offset);
99 		if (ret < 0) {
100 			printf("Dest write error @blk %ld\n", srcblk);
101 			goto exit;
102 		}
103 		wrcnt += ret * destbz;
104 		destblk += ret;
105 		if (ret < towrite) {
106 			towrite -= ret;
107 			offset += ret * destbz;
108 			goto write;
109 		}
110 	}
111 
112 exit:
113 	timer = get_timer(timer);
114 	timer = 1000 * timer / CONFIG_SYS_HZ;
115 	printf("%ld read\n", rdcnt);
116 	printf("%ld written\n", wrcnt);
117 	printf("%ldms, %ldkB/s\n", timer, wrcnt / timer);
118 	free(buf);
119 
120 	return 0;
121 }
122 
123 U_BOOT_CMD(
124 	clone, 6, 1, do_clone,
125 	"simple storage cloning",
126 	"<src interface> <src dev> <dest interface> <dest dev> <size[K/M/G]>\n"
127 	"clone storage from 'src dev' on 'src interface' to 'dest dev' on 'dest interface' with maximum 'size' bytes (or 0 for clone to end)"
128 );
129