5d8a58738935b9b495fbdd84a3cc38711d94f895
[flashkit-mdc.git] / flashkit.c
1 /*
2  * Tool for USB serial communication with Krikzz's FlashKit-MD
3  * Copyright (c) 2017 notaz
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining
6  * a copy of this software and associated documentation files (the
7  * "Software"), to deal in the Software without restriction, including
8  * without limitation the rights to use, copy, modify, merge, publish,
9  * distribute, sublicense, and/or sell copies of the Software, and to
10  * permit persons to whom the Software is furnished to do so, subject to
11  * the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be 
14  * included in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23  * SOFTWARE.
24  */
25
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <stdint.h>
30 #include <assert.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34 #include <arpa/inet.h> // hton
35 #include <termios.h>
36 #include <unistd.h>
37
38 #ifndef min
39 #define min(x, y) ((x) < (y) ? (x) : (y))
40 #define max(x, y) ((x) > (y) ? (x) : (y))
41 #endif
42
43 enum dev_cmd {
44         CMD_ADDR = 0,
45         CMD_LEN = 1,
46         CMD_RD = 2,
47         CMD_WR = 3,
48         CMD_RY = 4,
49         CMD_DELAY = 5,
50 };
51 #define PAR_MODE8  (1 << 4)
52 #define PAR_DEV_ID (1 << 5)
53 #define PAR_SINGE  (1 << 6)
54 #define PAR_INC    (1 << 7)
55
56 static int setup(int fd)
57 {
58         struct termios tty;
59         int ret;
60
61         memset(&tty, 0, sizeof(tty));
62
63         ret = tcgetattr(fd, &tty);
64         if (ret != 0)
65         {
66                 perror("tcgetattr");
67                 return 1;
68         }
69
70         tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
71                         | INLCR | IGNCR | ICRNL | IXON);
72         tty.c_oflag &= ~OPOST;
73         tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
74         tty.c_cflag &= ~(CSIZE | PARENB);
75         tty.c_cflag |= CS8;
76
77         //tty.c_cc[VMIN] = 1;
78         //tty.c_cc[VTIME] = 5;            // 0.5 seconds read timeout
79
80         ret = tcsetattr(fd, TCSANOW, &tty);
81         if (ret != 0) {
82                 perror("tcsetattr");
83                 return ret;
84         }
85
86         return 0;
87 }
88
89 static int write_serial(int fd, const void *data, size_t size)
90 {
91         int ret;
92
93         ret = write(fd, data, size);
94         if (ret != size) {
95                 fprintf(stderr, "write %d/%zd: ", ret, size);
96                 perror("");
97                 exit(1);
98         }
99
100         return 0;
101 }
102
103 static int read_serial(int fd, void *data, size_t size)
104 {
105         size_t got = 0;
106         int ret;
107
108         while (got < size) {
109                 ret = read(fd, (char *)data + got, size - got);
110                 if (ret <= 0) {
111                         fprintf(stderr, "read %d %zd/%zd: ",
112                                 ret, got, size);
113                         perror("");
114                         exit(1);
115                 }
116                 got += ret;
117         }
118
119         return 0;
120 }
121
122 /* addr arg is always byte address */
123 static void set_addr8(int fd, uint32_t addr)
124 {
125         uint8_t cmd[6] = {
126                 CMD_ADDR, addr >> 16,
127                 CMD_ADDR, addr >> 8,
128                 CMD_ADDR, addr >> 0
129         };
130         write_serial(fd, cmd, sizeof(cmd));
131 }
132
133 static void set_addr16(int fd, uint32_t addr)
134 {
135         set_addr8(fd, addr >> 1);
136 }
137
138 static uint16_t read_bus8(int fd, uint32_t addr)
139 {
140         uint8_t cmd[7] = {
141                 CMD_ADDR, addr >> 16,
142                 CMD_ADDR, addr >> 8,
143                 CMD_ADDR, addr >> 0,
144                 CMD_RD | PAR_SINGE | PAR_MODE8
145         };
146         uint8_t r;
147
148         write_serial(fd, cmd, sizeof(cmd));
149         read_serial(fd, &r, sizeof(r));
150         return r;
151 }
152
153 static uint16_t read_bus16(int fd, uint32_t addr)
154 {
155         uint8_t cmd[7] = {
156                 CMD_ADDR, addr >> 17,
157                 CMD_ADDR, addr >> 9,
158                 CMD_ADDR, addr >> 1,
159                 CMD_RD | PAR_SINGE
160         };
161         uint16_t r;
162
163         write_serial(fd, cmd, sizeof(cmd));
164         read_serial(fd, &r, sizeof(r));
165         return ntohs(r);
166 }
167
168 static void write_bus8(int fd, uint32_t addr, uint16_t d)
169 {
170         uint8_t cmd[8] = {
171                 CMD_ADDR, addr >> 16,
172                 CMD_ADDR, addr >> 8,
173                 CMD_ADDR, addr >> 0,
174                 CMD_WR | PAR_SINGE | PAR_MODE8,
175                 d
176         };
177
178         write_serial(fd, cmd, sizeof(cmd));
179 }
180
181 static void write_bus16(int fd, uint32_t addr, uint16_t d)
182 {
183         uint8_t cmd[9] = {
184                 CMD_ADDR, addr >> 17,
185                 CMD_ADDR, addr >> 9,
186                 CMD_ADDR, addr >> 1,
187                 CMD_WR | PAR_SINGE,
188                 d >> 8, d
189         };
190
191         write_serial(fd, cmd, sizeof(cmd));
192 }
193
194 static void read_block8(int fd, void *dst, uint32_t size)
195 {
196         // PAR_MODE8 does not work, so read as 16bit and throw away MSB
197         uint8_t tmp[0x10000], *d8 = dst;
198         uint8_t cmd[5] = {
199                 CMD_LEN, size >> 8,
200                 CMD_LEN, size >> 0,
201                 CMD_RD | PAR_INC
202         };
203         int i;
204
205         assert(size <= 0x10000);
206         write_serial(fd, cmd, sizeof(cmd));
207         read_serial(fd, dst, size);
208         read_serial(fd, tmp, size);
209
210         for (i = 0; i < size / 2; i++)
211                 d8[i] = d8[i * 2 + 1];
212         d8 += size / 2;
213         for (i = 0; i < size / 2; i++)
214                 d8[i] = tmp[i * 2 + 1];
215 }
216
217 static void read_block16(int fd, void *dst, uint32_t size)
218 {
219         uint8_t cmd[5] = {
220                 CMD_LEN, size >> 9,
221                 CMD_LEN, size >> 1,
222                 CMD_RD | PAR_INC
223         };
224
225         assert(size <= 0x10000);
226         write_serial(fd, cmd, sizeof(cmd));
227         read_serial(fd, dst, size);
228 }
229
230 static void flash_seq_write8(int fd, uint32_t addr, const uint8_t *d)
231 {
232         uint8_t cmd[] = {
233                 // unlock
234                 CMD_ADDR, 0,
235                 CMD_ADDR, 0x0a,
236                 CMD_ADDR, 0xaa,
237                 CMD_WR | PAR_SINGE | PAR_MODE8, 0xaa,
238                 CMD_ADDR, 0,
239                 CMD_ADDR, 0x05,
240                 CMD_ADDR, 0x55,
241                 CMD_WR | PAR_SINGE | PAR_MODE8, 0x55,
242                 // program setup
243                 CMD_ADDR, 0,
244                 CMD_ADDR, 0x0a,
245                 CMD_ADDR, 0xaa,
246                 CMD_WR | PAR_SINGE | PAR_MODE8, 0xa0,
247                 // program data
248                 CMD_ADDR, addr >> 16,
249                 CMD_ADDR, addr >> 8,
250                 CMD_ADDR, addr >> 0,
251                 CMD_WR | PAR_SINGE | PAR_MODE8, *d,
252                 CMD_RY
253         };
254
255         write_serial(fd, cmd, sizeof(cmd));
256 }
257
258 static void flash_seq_write16(int fd, uint32_t addr, const uint8_t *d)
259 {
260         uint8_t cmd[] = {
261                 // unlock
262                 CMD_ADDR, 0,
263                 CMD_ADDR, 0x05,
264                 CMD_ADDR, 0x55,
265                 CMD_WR | PAR_SINGE | PAR_MODE8, 0xaa,
266                 CMD_ADDR, 0,
267                 CMD_ADDR, 0x02,
268                 CMD_ADDR, 0xaa,
269                 CMD_WR | PAR_SINGE | PAR_MODE8, 0x55,
270                 // program setup
271                 CMD_ADDR, 0,
272                 CMD_ADDR, 0x05,
273                 CMD_ADDR, 0x55,
274                 CMD_WR | PAR_SINGE | PAR_MODE8, 0xa0,
275                 // program data
276                 CMD_ADDR, addr >> 17,
277                 CMD_ADDR, addr >> 9,
278                 CMD_ADDR, addr >> 1,
279                 CMD_WR | PAR_SINGE, d[0], d[1],
280                 CMD_RY
281         };
282
283         write_serial(fd, cmd, sizeof(cmd));
284 }
285
286 static const struct iof
287 {
288         void     (*set_addr)(int fd, uint32_t addr);
289         uint16_t (*read_bus)(int fd, uint32_t addr);
290         void     (*write_bus)(int fd, uint32_t addr, uint16_t d);
291         void     (*read_block)(int fd, void *dst, uint32_t size);
292         void     (*flash_seq_write)(int fd, uint32_t addr, const uint8_t *d);
293 }
294 io_ops[] =
295 {
296         { set_addr8,  read_bus8,  write_bus8,  read_block8,  flash_seq_write8  },
297         { set_addr16, read_bus16, write_bus16, read_block16, flash_seq_write16 },
298 };
299
300 static const struct iof *io = &io_ops[1];
301
302 static uint16_t flash_seq_r(int fd, uint8_t cmd, uint32_t addr)
303 {
304         // unlock
305         io->write_bus(fd, 0xaaa, 0xaa);
306         io->write_bus(fd, 0x555, 0x55);
307
308         io->write_bus(fd, 0xaaa, cmd);
309         return io->read_bus(fd, addr);
310 }
311
312 static void flash_seq_erase(int fd, uint32_t addr)
313 {
314         // printf("erase %06x\n", addr);
315         io->write_bus(fd, 0xaaa, 0xaa);
316         io->write_bus(fd, 0x555, 0x55);
317         io->write_bus(fd, 0xaaa, 0x80);
318
319         io->write_bus(fd, 0xaaa, 0xaa);
320         io->write_bus(fd, 0x555, 0x55);
321         io->write_bus(fd, addr, 0x30);
322 }
323
324 // status wait + dummy read to cause a wait?
325 static uint16_t ry_read(int fd)
326 {
327         uint8_t cmd[2] = { CMD_RY, CMD_RD | PAR_SINGE };
328         uint16_t rv = 0;
329
330         write_serial(fd, cmd, sizeof(cmd));
331         read_serial(fd, &rv, sizeof(rv));
332         return ntohs(rv);
333 }
334
335 static void set_delay(int fd, uint8_t delay)
336 {
337         uint8_t cmd[2] = { CMD_DELAY, delay };
338
339         write_serial(fd, cmd, sizeof(cmd));
340 }
341
342 static struct flash_info {
343         uint16_t mid;
344         uint16_t did;
345         uint32_t size;
346         uint16_t region_cnt;
347         struct {
348                 uint32_t block_size;
349                 uint32_t block_count;
350                 uint32_t start;
351                 uint32_t size;
352         } region[4];
353 } info;
354
355 static void read_info(int fd)
356 {
357         static const uint16_t qry[3] = { 'Q', 'R', 'Y' };
358         uint32_t total = 0;
359         uint16_t resp[3];
360         uint32_t i, a;
361
362         info.mid = flash_seq_r(fd, 0x90, 0); // autoselect
363         info.did = io->read_bus(fd, 2);
364
365         // could enter CFI directly, but there seems to be a "stack"
366         // of modes, so 2 exits would be needed
367         io->write_bus(fd, 0, 0xf0);
368
369         io->write_bus(fd, 0xaa, 0x98); // CFI Query
370         resp[0] = io->read_bus(fd, 0x20);
371         resp[1] = io->read_bus(fd, 0x22);
372         resp[2] = io->read_bus(fd, 0x24);
373         if (memcmp(resp, qry, sizeof(resp))) {
374                 fprintf(stderr, "unexpected CFI response: %04x %04x %04x\n",
375                         resp[0], resp[1], resp[2]);
376                 exit(1);
377         }
378         info.size = 1u << io->read_bus(fd, 0x4e);
379         info.region_cnt = io->read_bus(fd, 0x58);
380         assert(0 < info.region_cnt && info.region_cnt <= 4);
381         for (i = 0, a = 0x5a; i < info.region_cnt; i++, a += 8) {
382                 info.region[i].block_count = io->read_bus(fd, a + 0) + 1;
383                 info.region[i].block_count += io->read_bus(fd, a + 2) << 8;
384                 info.region[i].block_size = io->read_bus(fd, a + 4) << 8;
385                 info.region[i].block_size |= io->read_bus(fd, a + 6) << 16;
386                 info.region[i].start = total;
387                 info.region[i].size =
388                         info.region[i].block_size * info.region[i].block_count;
389                 assert(info.region[i].size);
390                 total += info.region[i].size;
391         }
392
393         io->write_bus(fd, 0, 0xf0); // flash reset
394
395         printf("Flash info:\n");
396         printf("Manufacturer ID: %04x\n", info.mid);
397         printf("Device ID: %04x\n", info.did);
398         printf("size: %u\n", info.size);
399         printf("Erase Block Regions: %u\n", info.region_cnt);
400         for (i = 0; i < info.region_cnt; i++)
401                 printf("  %5u x %u\n", info.region[i].block_size,
402                         info.region[i].block_count);
403         if (info.size != total)
404                 fprintf(stderr, "warning: total is %u, bad CFI?\n", total);
405 }
406
407 static uint32_t get_block_addr(uint32_t addr, uint32_t blk_offset)
408 {
409         uint32_t i;
410
411         assert(info.region_cnt);
412         for (i = 0; i < info.region_cnt; i++) {
413                 if (info.region[i].start <= addr
414                     && addr < info.region[i].start + info.region[i].size)
415                 {
416                         uint32_t blk = (addr - info.region[i].start)
417                                         / info.region[i].block_size
418                                         + blk_offset;
419                         return info.region[i].start
420                                 + blk * info.region[i].block_size;
421                 }
422         }
423
424         fprintf(stderr, "\naddress out of range: 0x%x\n", addr);
425         exit(1);
426 }
427
428 static void print_progress(uint32_t done, uint32_t total)
429 {
430         int i, step;
431
432         printf("\r%06x/%06x |", done, total);
433
434         step = (total + 19) / 20;
435         for (i = step; i <= total; i += step)
436                 fputc(done >= i ? '=' : '-', stdout);
437         printf("| %3d%%", done * 100 / total);
438         fflush(stdout);
439         if (done >= total)
440                 fputc('\n', stdout);
441 }
442
443 static void usage(const char *argv0)
444 {
445         printf("usage:\n"
446                 "%s [options]\n"
447                 "  -d <ttydevice>      (default /dev/ttyUSB0)\n"
448                 "  -r <file> [size]    dump the cart (default 4MB)\n"
449                 "  -w <file> [size]    flash the cart (file size)\n"
450                 "  -e <size>           erase (rounds to block size)\n"
451                 "  -a <start_address>  cart start address (default 0)\n"
452                 "  -8                  8bit flash\n"
453                 "  -v                  verify written data\n"
454                 "  -i                  get info about the flash chip\n"
455                 , argv0);
456         exit(1);
457 }
458
459 static void invarg(int argc, char *argv[], int arg)
460 {
461         if (arg < argc)
462                 fprintf(stderr, "invalid arg %d: \"%s\"\n", arg, argv[arg]);
463         else
464                 fprintf(stderr, "missing required argument %d\n", arg);
465         exit(1);
466 }
467
468 static void *getarg(int argc, char *argv[], int arg)
469 {
470         if (arg >= argc)
471                 invarg(argc, argv, arg);
472         return argv[arg];
473 }
474
475 static uint8_t g_block[0x10000];
476 static uint8_t g_block2[0x10000];
477
478 int main(int argc, char *argv[])
479 {
480         const char *portname = "/dev/ttyUSB0";
481         const char *fname_w = NULL;
482         const char *fname_r = NULL;
483         long size_w = 0;
484         long size_r = 0;
485         long size_e = 0;
486         long size_v = 0;
487         long len, address_in = 0;
488         long a, a_blk, end;
489         int do_info = 0;
490         int do_verify = 0;
491         int write_step = 2;
492         FILE *f_w = NULL;
493         FILE *f_r = NULL;
494         uint8_t id[2] = { 0, 0 };
495         uint8_t cmd;
496         uint16_t rv;
497         int arg = 1;
498         int fd;
499
500         if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
501                 usage(argv[0]);
502
503         for (arg = 1; arg < argc; arg++) {
504                 if (!strcmp(argv[arg], "-d")) {
505                         portname = getarg(argc, argv, ++arg);
506                         continue;
507                 }
508                 if (!strcmp(argv[arg], "-r")) {
509                         fname_r = getarg(argc, argv, ++arg);
510                         if (arg + 1 < argc && argv[arg + 1][0] != '-') {
511                                 size_r = strtol(argv[++arg], NULL, 0);
512                                 if (size_r <= 0)
513                                         invarg(argc, argv, arg);
514                         }
515                         continue;
516                 }
517                 if (!strcmp(argv[arg], "-w")) {
518                         fname_w = getarg(argc, argv, ++arg);
519                         if (arg + 1 < argc && argv[arg + 1][0] != '-') {
520                                 size_w = strtol(argv[++arg], NULL, 0);
521                                 if (size_w <= 0)
522                                         invarg(argc, argv, arg);
523                         }
524                         continue;
525                 }
526                 if (!strcmp(argv[arg], "-a")) {
527                         address_in = strtol(getarg(argc, argv, ++arg), NULL, 0);
528                         if (address_in < 0 || (address_in & 1))
529                                 invarg(argc, argv, arg);
530                         continue;
531                 }
532                 if (!strcmp(argv[arg], "-e")) {
533                         size_e = strtol(getarg(argc, argv, ++arg), NULL, 0);
534                         if (size_e <= 0)
535                                 invarg(argc, argv, arg);
536                         continue;
537                 }
538                 if (!strcmp(argv[arg], "-8")) {
539                         io = &io_ops[0];
540                         write_step = 1;
541                         continue;
542                 }
543                 if (!strcmp(argv[arg], "-v")) {
544                         do_verify = 1;
545                         continue;
546                 }
547                 if (!strcmp(argv[arg], "-i")) {
548                         do_info = 1;
549                         continue;
550                 }
551                 invarg(argc, argv, arg);
552         }
553
554         if (fname_r && size_r == 0)
555                 size_r = 0x400000;
556
557         if (fname_w) {
558                 f_w = fopen(fname_w, "rb");
559                 if (!f_w) {
560                         fprintf(stderr, "fopen %s: ", fname_w);
561                         perror("");
562                         return 1;
563                 }
564                 if (size_w <= 0) {
565                         fseek(f_w, 0, SEEK_END);
566                         size_w = ftell(f_w);
567                         fseek(f_w, 0, SEEK_SET);
568                 }
569                 if (size_w <= 0) {
570                         fprintf(stderr, "size of %s is %ld\n",
571                                 fname_w, size_w);
572                         return 1;
573                 }
574                 if (size_e < size_w)
575                         size_e = size_w;
576                 if (do_verify)
577                         size_v = size_w;
578         }
579
580         fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
581         if (fd < 0) {
582                 fprintf(stderr, "open %s: ", portname);
583                 perror("");
584                 return 1;
585         }
586
587         setup(fd);
588
589         cmd = CMD_RD | PAR_SINGE | PAR_DEV_ID;
590         write_serial(fd, &cmd, sizeof(cmd));
591         read_serial(fd, id, sizeof(id));
592         if (id[0] != id[1] || id[0] == 0) {
593                 fprintf(stderr, "unexpected id: %02x %02x\n", id[0], id[1]);
594                 return 1;
595         }
596         printf("flashkit id: %02x\n", id[0]);
597
598         set_delay(fd, 1);
599         io->write_bus(fd, 0, 0xf0); // flash reset
600
601         if (do_info || size_e)
602                 read_info(fd);
603
604         if (size_e) {
605                 // set_delay(fd, 0); // ?
606                 a_blk = get_block_addr(address_in, 0);
607                 end = address_in + size_e;
608
609                 printf("erasing %ld bytes:\n", size_e);
610                 print_progress(0, size_e);
611                 for (a = address_in; a < end; ) {
612                         flash_seq_erase(fd, a_blk);
613                         rv = ry_read(fd);
614                         if (rv != 0xffff) {
615                                 fprintf(stderr, "\nerase error: %lx %04x\n",
616                                         a_blk, rv);
617                         }
618
619                         a_blk = get_block_addr(a_blk, 1);
620                         a += a_blk - a;
621                         print_progress(a - address_in, size_e);
622                 }
623         }
624         if (f_w != NULL) {
625                 uint8_t b[2];
626                 set_delay(fd, 0);
627                 printf("writing %ld bytes:\n", size_w);
628                 for (a = 0; a < size_w; a += write_step) {
629                         ssize_t r;
630
631                         b[1] = 0xff;
632                         len = min(size_w - a, write_step);
633                         r = fread(b, 1, len, f_w);
634                         if (r != len) {
635                                 perror("\nfread");
636                                 return 1;
637                         }
638                         io->flash_seq_write(fd, address_in + a, b);
639
640                         if (!(a & 0x3ff))
641                                 print_progress(a, size_w);
642                 }
643                 print_progress(a, size_w);
644                 rv = ry_read(fd);
645                 if (write_step == 2 && rv != ((b[0] << 8) | b[1]))
646                         fprintf(stderr, "warning: last bytes: %04x %02x%02x\n",
647                                 rv, b[0], b[1]);
648                 rewind(f_w);
649                 set_delay(fd, 1);
650         }
651
652         if (fname_r || size_v) {
653                 long blks, blks_v, done, verify_diff = 0;
654
655                 blks = (size_r + sizeof(g_block) - 1) / sizeof(g_block);
656                 blks_v = (size_v + sizeof(g_block) - 1) / sizeof(g_block);
657                 blks = max(blks, blks_v);
658                 if (fname_r) {
659                         f_r = fopen(fname_r, "wb");
660                         if (!f_r) {
661                                 fprintf(stderr, "fopen %s: ", fname_r);
662                                 perror("");
663                                 return 1;
664                         }
665                 }
666
667                 printf("reading %ld bytes:\n", max(size_r, size_v));
668                 print_progress(0, blks * sizeof(g_block));
669                 io->set_addr(fd, address_in);
670                 for (done = 0; done < size_r || done < size_v; ) {
671                         io->read_block(fd, g_block, sizeof(g_block));
672                         if (f_r && done < size_r) {
673                                 len = min(size_r - done, sizeof(g_block));
674                                 if (fwrite(g_block, 1, len, f_r) != len) {
675                                         perror("fwrite");
676                                         return 1;
677                                 }
678                         }
679                         if (done < size_v) {
680                                 len = min(size_v - done, sizeof(g_block));
681                                 if (fread(g_block2, 1, len, f_w) != len) {
682                                         perror("fread");
683                                         return 1;
684                                 }
685                                 verify_diff |= memcmp(g_block, g_block2, len);
686                         }
687                         done += sizeof(g_block);
688                         print_progress(done, blks * sizeof(g_block));
689                 }
690                 if (verify_diff) {
691                         fprintf(stderr, "verify FAILED\n");
692                         return 1;
693                 }
694         }
695         if (f_r)
696                 fclose(f_r);
697         if (f_w)
698                 fclose(f_w);
699
700         return 0;
701 }