cmd: add clone command
[platform/kernel/u-boot.git] / cmd / clone.c
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)
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;
59                 case 'm':
60                 case 'M':
61                         requested *= 1024;
62                 case 'k':
63                 case 'K':
64                         requested *= 1024;
65                         break;
66                 }
67         }
68         printf("Copying %ld bytes from %s:%s to %s:%s\n",
69                requested, argv[1], argv[2], argv[3], argv[4]);
70         wrcnt = 0;
71         rdcnt = 0;
72         buf = (char *)malloc(BUFSIZE);
73         srcblk = 0;
74         destblk = 0;
75         timer = get_timer(0);
76         while (wrcnt < requested) {
77                 unsigned long toread = BUFSIZE / srcbz;
78                 unsigned long towrite = BUFSIZE / destbz;
79                 unsigned long offset = 0;
80
81 read:
82                 ret = blk_dread(srcdesc, srcblk, toread, buf + offset);
83                 if (ret < 0) {
84                         printf("Src read error @blk %ld\n", srcblk);
85                         goto exit;
86                 }
87                 rdcnt += ret * srcbz;
88                 srcblk += ret;
89                 if (ret < toread) {
90                         toread -= ret;
91                         offset += ret * srcbz;
92                         goto read;
93                 }
94                 offset = 0;
95 write:
96                 ret = blk_dwrite(destdesc, destblk, towrite, buf + offset);
97                 if (ret < 0) {
98                         printf("Dest write error @blk %ld\n", srcblk);
99                         goto exit;
100                 }
101                 wrcnt += ret * destbz;
102                 destblk += ret;
103                 if (ret < towrite) {
104                         towrite -= ret;
105                         offset += ret * destbz;
106                         goto write;
107                 }
108         }
109
110 exit:
111         timer = get_timer(timer);
112         timer = 1000 * timer / CONFIG_SYS_HZ;
113         printf("%ld read\n", rdcnt);
114         printf("%ld written\n", wrcnt);
115         printf("%ldms, %ldkB/s\n", timer, wrcnt / timer);
116         free(buf);
117
118         return 0;
119 }
120
121 U_BOOT_CMD(
122         clone, 6, 1, do_clone,
123         "simple storage cloning",
124         "<src interface> <src dev> <dest interface> <dest dev> <size[K/M/G]>\n"
125         "clone storage from 'src dev' on 'src interface' to 'dest dev' on 'dest interface' with maximum 'size' bytes (or 0 for clone to end)"
126 );