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