]> git.sur5r.net Git - u-boot/blob - tools/kwboot.c
env: Relocate env drivers if manual reloc is required
[u-boot] / 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
290         if (msg == NULL)
291                 kwboot_printv("Please reboot the target into UART boot mode...");
292         else
293                 kwboot_printv("Sending boot message. Please reboot the target...");
294
295         do {
296                 rc = tcflush(tty, TCIOFLUSH);
297                 if (rc)
298                         break;
299
300                 rc = kwboot_tty_send(tty, msg, 8);
301                 if (rc) {
302                         usleep(msg_req_delay * 1000);
303                         continue;
304                 }
305
306                 rc = kwboot_tty_recv(tty, &c, 1, msg_rsp_timeo);
307
308                 kwboot_spinner();
309
310         } while (rc || c != NAK);
311
312         kwboot_printv("\n");
313
314         return rc;
315 }
316
317 static int
318 kwboot_debugmsg(int tty, void *msg)
319 {
320         int rc;
321
322         kwboot_printv("Sending debug message. Please reboot the target...");
323
324         do {
325                 char buf[16];
326
327                 rc = tcflush(tty, TCIOFLUSH);
328                 if (rc)
329                         break;
330
331                 rc = kwboot_tty_send(tty, msg, 8);
332                 if (rc) {
333                         usleep(msg_req_delay * 1000);
334                         continue;
335                 }
336
337                 rc = kwboot_tty_recv(tty, buf, 16, msg_rsp_timeo);
338
339                 kwboot_spinner();
340
341         } while (rc);
342
343         kwboot_printv("\n");
344
345         return rc;
346 }
347
348 static int
349 kwboot_xm_makeblock(struct kwboot_block *block, const void *data,
350                     size_t size, int pnum)
351 {
352         const size_t blksz = sizeof(block->data);
353         size_t n;
354         int i;
355
356         block->soh = SOH;
357         block->pnum = pnum;
358         block->_pnum = ~block->pnum;
359
360         n = size < blksz ? size : blksz;
361         memcpy(&block->data[0], data, n);
362         memset(&block->data[n], 0, blksz - n);
363
364         block->csum = 0;
365         for (i = 0; i < n; i++)
366                 block->csum += block->data[i];
367
368         return n;
369 }
370
371 static int
372 kwboot_xm_sendblock(int fd, struct kwboot_block *block)
373 {
374         int rc, retries;
375         char c;
376
377         retries = 16;
378         do {
379                 rc = kwboot_tty_send(fd, block, sizeof(*block));
380                 if (rc)
381                         break;
382
383                 do {
384                         rc = kwboot_tty_recv(fd, &c, 1, blk_rsp_timeo);
385                         if (rc)
386                                 break;
387
388                         if (c != ACK && c != NAK && c != CAN)
389                                 printf("%c", c);
390
391                 } while (c != ACK && c != NAK && c != CAN);
392
393                 if (c != ACK)
394                         kwboot_progress(-1, '+');
395
396         } while (c == NAK && retries-- > 0);
397
398         rc = -1;
399
400         switch (c) {
401         case ACK:
402                 rc = 0;
403                 break;
404         case NAK:
405                 errno = EBADMSG;
406                 break;
407         case CAN:
408                 errno = ECANCELED;
409                 break;
410         default:
411                 errno = EPROTO;
412                 break;
413         }
414
415         return rc;
416 }
417
418 static int
419 kwboot_xmodem(int tty, const void *_data, size_t size)
420 {
421         const uint8_t *data = _data;
422         int rc, pnum, N, err;
423
424         pnum = 1;
425         N = 0;
426
427         kwboot_printv("Sending boot image...\n");
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         while (nin > 0) {
493                 nout = write(out, buf, nin);
494                 if (nout <= 0)
495                         return -1;
496                 nin -= nout;
497         }
498
499         return 0;
500 }
501
502 static int
503 kwboot_terminal(int tty)
504 {
505         int rc, in, s;
506         char *quit = "\34c";
507         struct termios otio, tio;
508
509         rc = -1;
510
511         in = STDIN_FILENO;
512         if (isatty(in)) {
513                 rc = tcgetattr(in, &otio);
514                 if (!rc) {
515                         tio = otio;
516                         cfmakeraw(&tio);
517                         rc = tcsetattr(in, TCSANOW, &tio);
518                 }
519                 if (rc) {
520                         perror("tcsetattr");
521                         goto out;
522                 }
523
524                 kwboot_printv("[Type Ctrl-%c + %c to quit]\r\n",
525                               quit[0]|0100, quit[1]);
526         } else
527                 in = -1;
528
529         rc = 0;
530         s = 0;
531
532         do {
533                 fd_set rfds;
534                 int nfds = 0;
535
536                 FD_SET(tty, &rfds);
537                 nfds = nfds < tty ? tty : nfds;
538
539                 if (in >= 0) {
540                         FD_SET(in, &rfds);
541                         nfds = nfds < in ? in : nfds;
542                 }
543
544                 nfds = select(nfds + 1, &rfds, NULL, NULL, NULL);
545                 if (nfds < 0)
546                         break;
547
548                 if (FD_ISSET(tty, &rfds)) {
549                         rc = kwboot_term_pipe(tty, STDOUT_FILENO, NULL, NULL);
550                         if (rc)
551                                 break;
552                 }
553
554                 if (FD_ISSET(in, &rfds)) {
555                         rc = kwboot_term_pipe(in, tty, quit, &s);
556                         if (rc)
557                                 break;
558                 }
559         } while (quit[s] != 0);
560
561         tcsetattr(in, TCSANOW, &otio);
562 out:
563         return rc;
564 }
565
566 static void *
567 kwboot_mmap_image(const char *path, size_t *size, int prot)
568 {
569         int rc, fd, flags;
570         struct stat st;
571         void *img;
572
573         rc = -1;
574         img = NULL;
575
576         fd = open(path, O_RDONLY);
577         if (fd < 0)
578                 goto out;
579
580         rc = fstat(fd, &st);
581         if (rc)
582                 goto out;
583
584         flags = (prot & PROT_WRITE) ? MAP_PRIVATE : MAP_SHARED;
585
586         img = mmap(NULL, st.st_size, prot, flags, fd, 0);
587         if (img == MAP_FAILED) {
588                 img = NULL;
589                 goto out;
590         }
591
592         rc = 0;
593         *size = st.st_size;
594 out:
595         if (rc && img) {
596                 munmap(img, st.st_size);
597                 img = NULL;
598         }
599         if (fd >= 0)
600                 close(fd);
601
602         return img;
603 }
604
605 static uint8_t
606 kwboot_img_csum8(void *_data, size_t size)
607 {
608         uint8_t *data = _data, csum;
609
610         for (csum = 0; size-- > 0; data++)
611                 csum += *data;
612
613         return csum;
614 }
615
616 static int
617 kwboot_img_patch_hdr(void *img, size_t size)
618 {
619         int rc;
620         struct main_hdr_v1 *hdr;
621         uint8_t csum;
622         size_t hdrsz = sizeof(*hdr);
623         int image_ver;
624
625         rc = -1;
626         hdr = img;
627
628         if (size < hdrsz) {
629                 errno = EINVAL;
630                 goto out;
631         }
632
633         image_ver = image_version(img);
634         if (image_ver < 0) {
635                 fprintf(stderr, "Invalid image header version\n");
636                 errno = EINVAL;
637                 goto out;
638         }
639
640         if (image_ver == 0)
641                 hdrsz = sizeof(*hdr);
642         else
643                 hdrsz = KWBHEADER_V1_SIZE(hdr);
644
645         csum = kwboot_img_csum8(hdr, hdrsz) - hdr->checksum;
646         if (csum != hdr->checksum) {
647                 errno = EINVAL;
648                 goto out;
649         }
650
651         if (hdr->blockid == IBR_HDR_UART_ID) {
652                 rc = 0;
653                 goto out;
654         }
655
656         hdr->blockid = IBR_HDR_UART_ID;
657
658         if (image_ver == 0) {
659                 struct main_hdr_v0 *hdr_v0 = img;
660
661                 hdr_v0->nandeccmode = IBR_HDR_ECC_DISABLED;
662                 hdr_v0->nandpagesize = 0;
663
664                 hdr_v0->srcaddr = hdr_v0->ext
665                         ? sizeof(struct kwb_header)
666                         : sizeof(*hdr_v0);
667         }
668
669         hdr->checksum = kwboot_img_csum8(hdr, hdrsz) - csum;
670
671         rc = 0;
672 out:
673         return rc;
674 }
675
676 static void
677 kwboot_usage(FILE *stream, char *progname)
678 {
679         fprintf(stream,
680                 "Usage: %s [OPTIONS] [-b <image> | -D <image> ] [-B <baud> ] <TTY>\n",
681                 progname);
682         fprintf(stream, "\n");
683         fprintf(stream,
684                 "  -b <image>: boot <image> with preamble (Kirkwood, Armada 370/XP)\n");
685         fprintf(stream, "  -p: patch <image> to type 0x69 (uart boot)\n");
686         fprintf(stream,
687                 "  -D <image>: boot <image> without preamble (Dove)\n");
688         fprintf(stream, "  -d: enter debug mode\n");
689         fprintf(stream, "  -a: use timings for Armada XP\n");
690         fprintf(stream, "  -q <req-delay>:  use specific request-delay\n");
691         fprintf(stream, "  -s <resp-timeo>: use specific response-timeout\n");
692         fprintf(stream,
693                 "  -o <block-timeo>: use specific xmodem block timeout\n");
694         fprintf(stream, "\n");
695         fprintf(stream, "  -t: mini terminal\n");
696         fprintf(stream, "\n");
697         fprintf(stream, "  -B <baud>: set baud rate\n");
698         fprintf(stream, "\n");
699 }
700
701 int
702 main(int argc, char **argv)
703 {
704         const char *ttypath, *imgpath;
705         int rv, rc, tty, term, prot, patch;
706         void *bootmsg;
707         void *debugmsg;
708         void *img;
709         size_t size;
710         speed_t speed;
711
712         rv = 1;
713         tty = -1;
714         bootmsg = NULL;
715         debugmsg = NULL;
716         imgpath = NULL;
717         img = NULL;
718         term = 0;
719         patch = 0;
720         size = 0;
721         speed = B115200;
722
723         kwboot_verbose = isatty(STDOUT_FILENO);
724
725         do {
726                 int c = getopt(argc, argv, "hb:ptaB:dD:q:s:o:");
727                 if (c < 0)
728                         break;
729
730                 switch (c) {
731                 case 'b':
732                         bootmsg = kwboot_msg_boot;
733                         imgpath = optarg;
734                         break;
735
736                 case 'D':
737                         bootmsg = NULL;
738                         imgpath = optarg;
739                         break;
740
741                 case 'd':
742                         debugmsg = kwboot_msg_debug;
743                         break;
744
745                 case 'p':
746                         patch = 1;
747                         break;
748
749                 case 't':
750                         term = 1;
751                         break;
752
753                 case 'a':
754                         msg_req_delay = KWBOOT_MSG_REQ_DELAY_AXP;
755                         msg_rsp_timeo = KWBOOT_MSG_RSP_TIMEO_AXP;
756                         break;
757
758                 case 'q':
759                         msg_req_delay = atoi(optarg);
760                         break;
761
762                 case 's':
763                         msg_rsp_timeo = atoi(optarg);
764                         break;
765
766                 case 'o':
767                         blk_rsp_timeo = atoi(optarg);
768                         break;
769
770                 case 'B':
771                         speed = kwboot_tty_speed(atoi(optarg));
772                         if (speed == -1)
773                                 goto usage;
774                         break;
775
776                 case 'h':
777                         rv = 0;
778                 default:
779                         goto usage;
780                 }
781         } while (1);
782
783         if (!bootmsg && !term && !debugmsg)
784                 goto usage;
785
786         if (patch && !imgpath)
787                 goto usage;
788
789         if (argc - optind < 1)
790                 goto usage;
791
792         ttypath = argv[optind++];
793
794         tty = kwboot_open_tty(ttypath, speed);
795         if (tty < 0) {
796                 perror(ttypath);
797                 goto out;
798         }
799
800         if (imgpath) {
801                 prot = PROT_READ | (patch ? PROT_WRITE : 0);
802
803                 img = kwboot_mmap_image(imgpath, &size, prot);
804                 if (!img) {
805                         perror(imgpath);
806                         goto out;
807                 }
808         }
809
810         if (patch) {
811                 rc = kwboot_img_patch_hdr(img, size);
812                 if (rc) {
813                         fprintf(stderr, "%s: Invalid image.\n", imgpath);
814                         goto out;
815                 }
816         }
817
818         if (debugmsg) {
819                 rc = kwboot_debugmsg(tty, debugmsg);
820                 if (rc) {
821                         perror("debugmsg");
822                         goto out;
823                 }
824         } else {
825                 rc = kwboot_bootmsg(tty, bootmsg);
826                 if (rc) {
827                         perror("bootmsg");
828                         goto out;
829                 }
830         }
831
832         if (img) {
833                 rc = kwboot_xmodem(tty, img, size);
834                 if (rc) {
835                         perror("xmodem");
836                         goto out;
837                 }
838         }
839
840         if (term) {
841                 rc = kwboot_terminal(tty);
842                 if (rc && !(errno == EINTR)) {
843                         perror("terminal");
844                         goto out;
845                 }
846         }
847
848         rv = 0;
849 out:
850         if (tty >= 0)
851                 close(tty);
852
853         if (img)
854                 munmap(img, size);
855
856         return rv;
857
858 usage:
859         kwboot_usage(rv ? stderr : stdout, basename(argv[0]));
860         goto out;
861 }