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