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)
15static 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 = dectoul(argv[5], &unit);
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
83read:
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;
97write:
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
112exit:
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
123U_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