Merge branch '2020-06-03-misc-bugfixes'
[oweals/u-boot.git] / tools / kwboot.c
1 /*
2  * Boot a Marvell SoC, with Xmodem over UART0.
3  *  supports Kirkwood, Dove, Armada 370, Armada XP
4  *
5  * (c) 2012 Daniel Stodden <daniel.stodden@gmail.com>
6  *
7  * References: marvell.com, "88F6180, 88F6190, 88F6192, and 88F6281
8  *   Integrated Controller: Functional Specifications" December 2,
9  *   2008. Chapter 24.2 "BootROM Firmware".
10  */
11
12 #include "kwbimage.h"
13 #include "mkimage.h"
14
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <string.h>
18 #include <stdarg.h>
19 #include <image.h>
20 #include <libgen.h>
21 #include <fcntl.h>
22 #include <errno.h>
23 #include <unistd.h>
24 #include <stdint.h>
25 #include <termios.h>
26 #include <sys/mman.h>
27 #include <sys/stat.h>
28
29 #ifdef __GNUC__
30 #define PACKED __attribute((packed))
31 #else
32 #define PACKED
33 #endif
34
35 /*
36  * Marvell BootROM UART Sensing
37  */
38
39 static unsigned char kwboot_msg_boot[] = {
40         0xBB, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77
41 };
42
43 static unsigned char kwboot_msg_debug[] = {
44         0xDD, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77
45 };
46
47 /* Defines known to work on Kirkwood */
48 #define KWBOOT_MSG_REQ_DELAY    10 /* ms */
49 #define KWBOOT_MSG_RSP_TIMEO    50 /* ms */
50
51 /* Defines known to work on Armada XP */
52 #define KWBOOT_MSG_REQ_DELAY_AXP        1000 /* ms */
53 #define KWBOOT_MSG_RSP_TIMEO_AXP        1000 /* ms */
54
55 /*
56  * Xmodem Transfers
57  */
58
59 #define SOH     1       /* sender start of block header */
60 #define EOT     4       /* sender end of block transfer */
61 #define ACK     6       /* target block ack */
62 #define NAK     21      /* target block negative ack */
63 #define CAN     24      /* target/sender transfer cancellation */
64
65 struct kwboot_block {
66         uint8_t soh;
67         uint8_t pnum;
68         uint8_t _pnum;
69         uint8_t data[128];
70         uint8_t csum;
71 } PACKED;
72
73 #define KWBOOT_BLK_RSP_TIMEO 1000 /* ms */
74
75 static int kwboot_verbose;
76
77 static int msg_req_delay = KWBOOT_MSG_REQ_DELAY;
78 static int msg_rsp_timeo = KWBOOT_MSG_RSP_TIMEO;
79 static int blk_rsp_timeo = KWBOOT_BLK_RSP_TIMEO;
80
81 static void
82 kwboot_printv(const char *fmt, ...)
83 {
84         va_list ap;
85
86         if (kwboot_verbose) {
87                 va_start(ap, fmt);
88                 vprintf(fmt, ap);
89                 va_end(ap);
90                 fflush(stdout);
91         }
92 }
93
94 static void
95 __spinner(void)
96 {
97         const char seq[] = { '-', '\\', '|', '/' };
98         const int div = 8;
99         static int state, bs;
100
101         if (state % div == 0) {
102                 fputc(bs, stdout);
103                 fputc(seq[state / div % sizeof(seq)], stdout);
104                 fflush(stdout);
105         }
106
107         bs = '\b';
108         state++;
109 }
110
111 static void
112 kwboot_spinner(void)
113 {
114         if (kwboot_verbose)
115                 __spinner();
116 }
117
118 static void
119 __progress(int pct, char c)
120 {
121         const int width = 70;
122         static const char *nl = "";
123         static int pos;
124
125         if (pos % width == 0)
126                 printf("%s%3d %% [", nl, pct);
127
128         fputc(c, stdout);
129
130         nl = "]\n";
131         pos++;
132
133         if (pct == 100) {
134                 while (pos++ < width)
135                         fputc(' ', stdout);
136                 fputs(nl, stdout);
137         }
138
139         fflush(stdout);
140
141 }
142
143 static void
144 kwboot_progress(int _pct, char c)
145 {
146         static int pct;
147
148         if (_pct != -1)
149                 pct = _pct;
150
151         if (kwboot_verbose)
152                 __progress(pct, c);
153 }
154
155 static int
156 kwboot_tty_recv(int fd, void *buf, size_t len, int timeo)
157 {
158         int rc, nfds;
159         fd_set rfds;
160         struct timeval tv;
161         ssize_t n;
162
163         rc = -1;
164
165         FD_ZERO(&rfds);
166         FD_SET(fd, &rfds);
167
168         tv.tv_sec = 0;
169         tv.tv_usec = timeo * 1000;
170         if (tv.tv_usec > 1000000) {
171                 tv.tv_sec += tv.tv_usec / 1000000;
172                 tv.tv_usec %= 1000000;
173         }
174
175         do {
176                 nfds = select(fd + 1, &rfds, NULL, NULL, &tv);
177                 if (nfds < 0)
178                         goto out;
179                 if (!nfds) {
180                         errno = ETIMEDOUT;
181                         goto out;
182                 }
183
184                 n = read(fd, buf, len);
185                 if (n <= 0)
186                         goto out;
187
188                 buf = (char *)buf + n;
189                 len -= n;
190         } while (len > 0);
191
192         rc = 0;
193 out:
194         return rc;
195 }
196
197 static int
198 kwboot_tty_send(int fd, const void *buf, size_t len)
199 {
200         int rc;
201         ssize_t n;
202
203         if (!buf)
204                 return 0;
205
206         rc = -1;
207
208         do {
209                 n = write(fd, buf, len);
210                 if (n < 0)
211                         goto out;
212
213                 buf = (char *)buf + n;
214                 len -= n;
215         } while (len > 0);
216
217         rc = tcdrain(fd);
218 out:
219         return rc;
220 }
221
222 static int
223 kwboot_tty_send_char(int fd, unsigned char c)
224 {
225         return kwboot_tty_send(fd, &c, 1);
226 }
227
228 static speed_t
229 kwboot_tty_speed(int baudrate)
230 {
231         switch (baudrate) {
232         case 115200:
233                 return B115200;
234         case 57600:
235                 return B57600;
236         case 38400:
237                 return B38400;
238         case 19200:
239                 return B19200;
240         case 9600:
241                 return B9600;
242         }
243
244         return -1;
245 }
246
247 static int
248 kwboot_open_tty(const char *path, speed_t speed)
249 {
250         int rc, fd;
251         struct termios tio;
252
253         rc = -1;
254
255         fd = open(path, O_RDWR|O_NOCTTY|O_NDELAY);
256         if (fd < 0)
257                 goto out;
258
259         memset(&tio, 0, sizeof(tio));
260
261         tio.c_iflag = 0;
262         tio.c_cflag = CREAD|CLOCAL|CS8;
263
264         tio.c_cc[VMIN] = 1;
265         tio.c_cc[VTIME] = 10;
266
267         cfsetospeed(&tio, speed);
268         cfsetispeed(&tio, speed);
269
270         rc = tcsetattr(fd, TCSANOW, &tio);
271         if (rc)
272                 goto out;
273
274         rc = fd;
275 out:
276         if (rc < 0) {
277                 if (fd >= 0)
278                         close(fd);
279         }
280
281         return rc;
282 }
283
284 static int
285 kwboot_bootmsg(int tty, void *msg)
286 {
287         int rc;
288         char c;
289         int count;
290
291         if (msg == NULL)
292                 kwboot_printv("Please reboot the target into UART boot mode...");
293         else
294                 kwboot_printv("Sending boot message. Please reboot the target...");
295
296         do {
297                 rc = tcflush(tty, TCIOFLUSH);
298                 if (rc)
299                         break;
300
301                 for (count = 0; count < 128; count++) {
302                         rc = kwboot_tty_send(tty, msg, 8);
303                         if (rc) {
304                                 usleep(msg_req_delay * 1000);
305                                 continue;
306                         }
307                 }
308
309                 rc = kwboot_tty_recv(tty, &c, 1, msg_rsp_timeo);
310
311                 kwboot_spinner();
312
313         } while (rc || c != NAK);
314
315         kwboot_printv("\n");
316
317         return rc;
318 }
319
320 static int
321 kwboot_debugmsg(int tty, void *msg)
322 {
323         int rc;
324
325         kwboot_printv("Sending debug message. Please reboot the target...");
326
327         do {
328                 char buf[16];
329
330                 rc = tcflush(tty, TCIOFLUSH);
331                 if (rc)
332                         break;
333
334                 rc = kwboot_tty_send(tty, msg, 8);
335                 if (rc) {
336                         usleep(msg_req_delay * 1000);
337                         continue;
338                 }
339
340                 rc = kwboot_tty_recv(tty, buf, 16, msg_rsp_timeo);
341
342                 kwboot_spinner();
343
344         } while (rc);
345
346         kwboot_printv("\n");
347
348         return rc;
349 }
350
351 static int
352 kwboot_xm_makeblock(struct kwboot_block *block, const void *data,
353                     size_t size, int pnum)
354 {
355         const size_t blksz = sizeof(block->data);
356         size_t n;
357         int i;
358
359         block->soh = SOH;
360         block->pnum = pnum;
361         block->_pnum = ~block->pnum;
362
363         n = size < blksz ? size : blksz;
364         memcpy(&block->data[0], data, n);
365         memset(&block->data[n], 0, blksz - n);
366
367         block->csum = 0;
368         for (i = 0; i < n; i++)
369                 block->csum += block->data[i];
370
371         return n;
372 }
373
374 static int
375 kwboot_xm_sendblock(int fd, struct kwboot_block *block)
376 {
377         int rc, retries;
378         char c;
379
380         retries = 16;
381         do {
382                 rc = kwboot_tty_send(fd, block, sizeof(*block));
383                 if (rc)
384                         break;
385
386                 do {
387                         rc = kwboot_tty_recv(fd, &c, 1, blk_rsp_timeo);
388                         if (rc)
389                                 break;
390
391                         if (c != ACK && c != NAK && c != CAN)
392                                 printf("%c", c);
393
394                 } while (c != ACK && c != NAK && c != CAN);
395
396                 if (c != ACK)
397                         kwboot_progress(-1, '+');
398
399         } while (c == NAK && retries-- > 0);
400
401         rc = -1;
402
403         switch (c) {
404         case ACK:
405                 rc = 0;
406                 break;
407         case NAK:
408                 errno = EBADMSG;
409                 break;
410         case CAN:
411                 errno = ECANCELED;
412                 break;
413         default:
414                 errno = EPROTO;
415                 break;
416         }
417
418         return rc;
419 }
420
421 static int
422 kwboot_xmodem(int tty, const void *_data, size_t size)
423 {
424         const uint8_t *data = _data;
425         int rc, pnum, N, err;
426
427         pnum = 1;
428         N = 0;
429
430         kwboot_printv("Sending boot image...\n");
431
432         sleep(2); /* flush isn't effective without it */
433         tcflush(tty, TCIOFLUSH);
434
435         do {
436                 struct kwboot_block block;
437                 int n;
438
439                 n = kwboot_xm_makeblock(&block,
440                                         data + N, size - N,
441                                         pnum++);
442                 if (n < 0)
443                         goto can;
444
445                 if (!n)
446                         break;
447
448                 rc = kwboot_xm_sendblock(tty, &block);
449                 if (rc)
450                         goto out;
451
452                 N += n;
453                 kwboot_progress(N * 100 / size, '.');
454         } while (1);
455
456         rc = kwboot_tty_send_char(tty, EOT);
457
458 out:
459         return rc;
460
461 can:
462         err = errno;
463         kwboot_tty_send_char(tty, CAN);
464         errno = err;
465         goto out;
466 }
467
468 static int
469 kwboot_term_pipe(int in, int out, char *quit, int *s)
470 {
471         ssize_t nin, nout;
472         char _buf[128], *buf = _buf;
473
474         nin = read(in, buf, sizeof(buf));
475         if (nin <= 0)
476                 return -1;
477
478         if (quit) {
479                 int i;
480
481                 for (i = 0; i < nin; i++) {
482                         if (*buf == quit[*s]) {
483                                 (*s)++;
484                                 if (!quit[*s])
485                                         return 0;
486                                 buf++;
487                                 nin--;
488                         } else
489                                 while (*s > 0) {
490                                         nout = write(out, quit, *s);
491                                         if (nout <= 0)
492                                                 return -1;
493                                         (*s) -= nout;
494                                 }
495                 }
496         }
497
498         while (nin > 0) {
499                 nout = write(out, buf, nin);
500                 if (nout <= 0)
501                         return -1;
502                 nin -= nout;
503         }
504
505         return 0;
506 }
507
508 static int
509 kwboot_terminal(int tty)
510 {
511         int rc, in, s;
512         char *quit = "\34c";
513         struct termios otio, tio;
514
515         rc = -1;
516
517         in = STDIN_FILENO;
518         if (isatty(in)) {
519                 rc = tcgetattr(in, &otio);
520                 if (!rc) {
521                         tio = otio;
522                         cfmakeraw(&tio);
523                         rc = tcsetattr(in, TCSANOW, &tio);
524                 }
525                 if (rc) {
526                         perror("tcsetattr");
527                         goto out;
528                 }
529
530                 kwboot_printv("[Type Ctrl-%c + %c to quit]\r\n",
531                               quit[0]|0100, quit[1]);
532         } else
533                 in = -1;
534
535         rc = 0;
536         s = 0;
537
538         do {
539                 fd_set rfds;
540                 int nfds = 0;
541
542                 FD_SET(tty, &rfds);
543                 nfds = nfds < tty ? tty : nfds;
544
545                 if (in >= 0) {
546                         FD_SET(in, &rfds);
547                         nfds = nfds < in ? in : nfds;
548                 }
549
550                 nfds = select(nfds + 1, &rfds, NULL, NULL, NULL);
551                 if (nfds < 0)
552                         break;
553
554                 if (FD_ISSET(tty, &rfds)) {
555                         rc = kwboot_term_pipe(tty, STDOUT_FILENO, NULL, NULL);
556                         if (rc)
557                                 break;
558                 }
559
560                 if (FD_ISSET(in, &rfds)) {
561                         rc = kwboot_term_pipe(in, tty, quit, &s);
562                         if (rc)
563                                 break;
564                 }
565         } while (quit[s] != 0);
566
567         tcsetattr(in, TCSANOW, &otio);
568 out:
569         return rc;
570 }
571
572 static void *
573 kwboot_mmap_image(const char *path, size_t *size, int prot)
574 {
575         int rc, fd, flags;
576         struct stat st;
577         void *img;
578
579         rc = -1;
580         img = NULL;
581
582         fd = open(path, O_RDONLY);
583         if (fd < 0)
584                 goto out;
585
586         rc = fstat(fd, &st);
587         if (rc)
588                 goto out;
589
590         flags = (prot & PROT_WRITE) ? MAP_PRIVATE : MAP_SHARED;
591
592         img = mmap(NULL, st.st_size, prot, flags, fd, 0);
593         if (img == MAP_FAILED) {
594                 img = NULL;
595                 goto out;
596         }
597
598         rc = 0;
599         *size = st.st_size;
600 out:
601         if (rc && img) {
602                 munmap(img, st.st_size);
603                 img = NULL;
604         }
605         if (fd >= 0)
606                 close(fd);
607
608         return img;
609 }
610
611 static uint8_t
612 kwboot_img_csum8(void *_data, size_t size)
613 {
614         uint8_t *data = _data, csum;
615
616         for (csum = 0; size-- > 0; data++)
617                 csum += *data;
618
619         return csum;
620 }
621
622 static int
623 kwboot_img_patch_hdr(void *img, size_t size)
624 {
625         int rc;
626         struct main_hdr_v1 *hdr;
627         uint8_t csum;
628         size_t hdrsz = sizeof(*hdr);
629         int image_ver;
630
631         rc = -1;
632         hdr = img;
633
634         if (size < hdrsz) {
635                 errno = EINVAL;
636                 goto out;
637         }
638
639         image_ver = image_version(img);
640         if (image_ver < 0) {
641                 fprintf(stderr, "Invalid image header version\n");
642                 errno = EINVAL;
643                 goto out;
644         }
645
646         if (image_ver == 0)
647                 hdrsz = sizeof(*hdr);
648         else
649                 hdrsz = KWBHEADER_V1_SIZE(hdr);
650
651         csum = kwboot_img_csum8(hdr, hdrsz) - hdr->checksum;
652         if (csum != hdr->checksum) {
653                 errno = EINVAL;
654                 goto out;
655         }
656
657         if (hdr->blockid == IBR_HDR_UART_ID) {
658                 rc = 0;
659                 goto out;
660         }
661
662         hdr->blockid = IBR_HDR_UART_ID;
663
664         if (image_ver == 0) {
665                 struct main_hdr_v0 *hdr_v0 = img;
666
667                 hdr_v0->nandeccmode = IBR_HDR_ECC_DISABLED;
668                 hdr_v0->nandpagesize = 0;
669
670                 hdr_v0->srcaddr = hdr_v0->ext
671                         ? sizeof(struct kwb_header)
672                         : sizeof(*hdr_v0);
673         }
674
675         hdr->checksum = kwboot_img_csum8(hdr, hdrsz) - csum;
676
677         rc = 0;
678 out:
679         return rc;
680 }
681
682 static void
683 kwboot_usage(FILE *stream, char *progname)
684 {
685         fprintf(stream,
686                 "Usage: %s [OPTIONS] [-b <image> | -D <image> ] [-B <baud> ] <TTY>\n",
687                 progname);
688         fprintf(stream, "\n");
689         fprintf(stream,
690                 "  -b <image>: boot <image> with preamble (Kirkwood, Armada 370/XP)\n");
691         fprintf(stream, "  -p: patch <image> to type 0x69 (uart boot)\n");
692         fprintf(stream,
693                 "  -D <image>: boot <image> without preamble (Dove)\n");
694         fprintf(stream, "  -d: enter debug mode\n");
695         fprintf(stream, "  -a: use timings for Armada XP\n");
696         fprintf(stream, "  -q <req-delay>:  use specific request-delay\n");
697         fprintf(stream, "  -s <resp-timeo>: use specific response-timeout\n");
698         fprintf(stream,
699                 "  -o <block-timeo>: use specific xmodem block timeout\n");
700         fprintf(stream, "\n");
701         fprintf(stream, "  -t: mini terminal\n");
702         fprintf(stream, "\n");
703         fprintf(stream, "  -B <baud>: set baud rate\n");
704         fprintf(stream, "\n");
705 }
706
707 int
708 main(int argc, char **argv)
709 {
710         const char *ttypath, *imgpath;
711         int rv, rc, tty, term, prot, patch;
712         void *bootmsg;
713         void *debugmsg;
714         void *img;
715         size_t size;
716         speed_t speed;
717
718         rv = 1;
719         tty = -1;
720         bootmsg = NULL;
721         debugmsg = NULL;
722         imgpath = NULL;
723         img = NULL;
724         term = 0;
725         patch = 0;
726         size = 0;
727         speed = B115200;
728
729         kwboot_verbose = isatty(STDOUT_FILENO);
730
731         do {
732                 int c = getopt(argc, argv, "hb:ptaB:dD:q:s:o:");
733                 if (c < 0)
734                         break;
735
736                 switch (c) {
737                 case 'b':
738                         bootmsg = kwboot_msg_boot;
739                         imgpath = optarg;
740                         break;
741
742                 case 'D':
743                         bootmsg = NULL;
744                         imgpath = optarg;
745                         break;
746
747                 case 'd':
748                         debugmsg = kwboot_msg_debug;
749                         break;
750
751                 case 'p':
752                         patch = 1;
753                         break;
754
755                 case 't':
756                         term = 1;
757                         break;
758
759                 case 'a':
760                         msg_req_delay = KWBOOT_MSG_REQ_DELAY_AXP;
761                         msg_rsp_timeo = KWBOOT_MSG_RSP_TIMEO_AXP;
762                         break;
763
764                 case 'q':
765                         msg_req_delay = atoi(optarg);
766                         break;
767
768                 case 's':
769                         msg_rsp_timeo = atoi(optarg);
770                         break;
771
772                 case 'o':
773                         blk_rsp_timeo = atoi(optarg);
774                         break;
775
776                 case 'B':
777                         speed = kwboot_tty_speed(atoi(optarg));
778                         if (speed == -1)
779                                 goto usage;
780                         break;
781
782                 case 'h':
783                         rv = 0;
784                 default:
785                         goto usage;
786                 }
787         } while (1);
788
789         if (!bootmsg && !term && !debugmsg)
790                 goto usage;
791
792         if (patch && !imgpath)
793                 goto usage;
794
795         if (argc - optind < 1)
796                 goto usage;
797
798         ttypath = argv[optind++];
799
800         tty = kwboot_open_tty(ttypath, speed);
801         if (tty < 0) {
802                 perror(ttypath);
803                 goto out;
804         }
805
806         if (imgpath) {
807                 prot = PROT_READ | (patch ? PROT_WRITE : 0);
808
809                 img = kwboot_mmap_image(imgpath, &size, prot);
810                 if (!img) {
811                         perror(imgpath);
812                         goto out;
813                 }
814         }
815
816         if (patch) {
817                 rc = kwboot_img_patch_hdr(img, size);
818                 if (rc) {
819                         fprintf(stderr, "%s: Invalid image.\n", imgpath);
820                         goto out;
821                 }
822         }
823
824         if (debugmsg) {
825                 rc = kwboot_debugmsg(tty, debugmsg);
826                 if (rc) {
827                         perror("debugmsg");
828                         goto out;
829                 }
830         } else if (bootmsg) {
831                 rc = kwboot_bootmsg(tty, bootmsg);
832                 if (rc) {
833                         perror("bootmsg");
834                         goto out;
835                 }
836         }
837
838         if (img) {
839                 rc = kwboot_xmodem(tty, img, size);
840                 if (rc) {
841                         perror("xmodem");
842                         goto out;
843                 }
844         }
845
846         if (term) {
847                 rc = kwboot_terminal(tty);
848                 if (rc && !(errno == EINTR)) {
849                         perror("terminal");
850                         goto out;
851                 }
852         }
853
854         rv = 0;
855 out:
856         if (tty >= 0)
857                 close(tty);
858
859         if (img)
860                 munmap(img, size);
861
862         return rv;
863
864 usage:
865         kwboot_usage(rv ? stderr : stdout, basename(argv[0]));
866         goto out;
867 }