John Chau | fce6f98 | 2020-07-02 12:01:21 +0800 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0+ |
| 2 | /* |
| 3 | * Copyright (C) 2020 John Chau <john@harmon.hk> |
| 4 | * |
| 5 | */ |
| 6 | |
John Chau | fce6f98 | 2020-07-02 12:01:21 +0800 | [diff] [blame] | 7 | #include <command.h> |
| 8 | #include <malloc.h> |
| 9 | #include <part.h> |
| 10 | #include <blk.h> |
Tom Rini | 5c1444f | 2024-04-27 08:10:59 -0600 | [diff] [blame] | 11 | #include <time.h> |
John Chau | fce6f98 | 2020-07-02 12:01:21 +0800 | [diff] [blame] | 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 | } |
Simon Glass | ff9b903 | 2021-07-24 09:03:30 -0600 | [diff] [blame] | 37 | requested = dectoul(argv[5], &unit); |
John Chau | fce6f98 | 2020-07-02 12:01:21 +0800 | [diff] [blame] | 38 | srcbz = srcdesc->blksz; |
| 39 | destbz = destdesc->blksz; |
| 40 | |
John Chau | a4016cb | 2020-08-17 15:53:01 +0800 | [diff] [blame] | 41 | if ((srcbz * (buffersize / srcbz) != buffersize) || |
John Chau | fce6f98 | 2020-07-02 12:01:21 +0800 | [diff] [blame] | 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': |
John Chau | a4016cb | 2020-08-17 15:53:01 +0800 | [diff] [blame] | 58 | requested *= 1024 * 1024 * 1024; |
| 59 | break; |
John Chau | fce6f98 | 2020-07-02 12:01:21 +0800 | [diff] [blame] | 60 | case 'm': |
| 61 | case 'M': |
John Chau | a4016cb | 2020-08-17 15:53:01 +0800 | [diff] [blame] | 62 | requested *= 1024 * 1024; |
| 63 | break; |
John Chau | fce6f98 | 2020-07-02 12:01:21 +0800 | [diff] [blame] | 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 | ); |