1
2
3
4
5
6
7
8
9
10
11
12
13
14
15#include "kwbimage.h"
16#include "mkimage.h"
17#include "version.h"
18
19#include <stdlib.h>
20#include <stdio.h>
21#include <string.h>
22#include <stdarg.h>
23#include <image.h>
24#include <libgen.h>
25#include <fcntl.h>
26#include <errno.h>
27#include <unistd.h>
28#include <stdint.h>
29#include <time.h>
30#include <sys/stat.h>
31
32#ifdef __linux__
33#include "termios_linux.h"
34#else
35#include <termios.h>
36#endif
37
38
39
40
41
42static unsigned char kwboot_msg_boot[] = {
43 0xBB, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77
44};
45
46static unsigned char kwboot_msg_debug[] = {
47 0xDD, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77
48};
49
50
51#define KWBOOT_MSG_REQ_DELAY 10
52#define KWBOOT_MSG_RSP_TIMEO 50
53
54
55#define KWBOOT_MSG_REQ_DELAY_AXP 1000
56#define KWBOOT_MSG_RSP_TIMEO_AXP 1000
57
58
59
60
61
62#define SOH 1
63#define EOT 4
64#define ACK 6
65#define NAK 21
66#define CAN 24
67
68#define KWBOOT_XM_BLKSZ 128
69
70struct kwboot_block {
71 uint8_t soh;
72 uint8_t pnum;
73 uint8_t _pnum;
74 uint8_t data[KWBOOT_XM_BLKSZ];
75 uint8_t csum;
76} __packed;
77
78#define KWBOOT_BLK_RSP_TIMEO 1000
79#define KWBOOT_HDR_RSP_TIMEO 10000
80
81
82static unsigned char kwboot_baud_code[] = {
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112 0x0d, 0x02, 0xa0, 0xe3,
113 0x12, 0x0a, 0x80, 0xe3,
114
115
116
117
118 0x14, 0x10, 0x90, 0xe5,
119 0x40, 0x00, 0x11, 0xe3,
120 0xfc, 0xff, 0xff, 0x0a,
121
122
123
124 0x0c, 0x10, 0x90, 0xe5,
125 0x80, 0x10, 0x81, 0xe3,
126 0x0c, 0x10, 0x80, 0xe5,
127
128
129
130 0x00, 0x10, 0x90, 0xe5,
131 0xff, 0x10, 0x01, 0xe2,
132 0x01, 0x20, 0xa0, 0xe1,
133 0x04, 0x10, 0x90, 0xe5,
134 0xff, 0x10, 0x01, 0xe2,
135 0x41, 0x14, 0xa0, 0xe1,
136 0x02, 0x10, 0x81, 0xe1,
137
138
139
140 0x74, 0x20, 0x9f, 0xe5,
141
142
143
144 0x92, 0x01, 0x01, 0xe0,
145
146
147
148 0x70, 0x20, 0x9f, 0xe5,
149
150
151
152
153 0xa2, 0x10, 0x81, 0xe0,
154 0x02, 0x40, 0xa0, 0xe1,
155 0xa1, 0x00, 0x54, 0xe1,
156
157 0x84, 0x40, 0xa0, 0x91,
158 0xa1, 0x00, 0x54, 0xe1,
159 0xfc, 0xff, 0xff, 0x9a,
160 0x00, 0x30, 0xa0, 0xe3,
161
162 0x04, 0x00, 0x51, 0xe1,
163 0x04, 0x10, 0x41, 0x20,
164 0x03, 0x30, 0xa3, 0xe0,
165 0xa4, 0x40, 0xa0, 0xe1,
166 0x02, 0x00, 0x54, 0xe1,
167 0xf9, 0xff, 0xff, 0x2a,
168 0x03, 0x10, 0xa0, 0xe1,
169
170
171
172 0x01, 0x20, 0xa0, 0xe1,
173 0xff, 0x20, 0x02, 0xe2,
174 0x00, 0x20, 0x80, 0xe5,
175
176
177
178 0x41, 0x24, 0xa0, 0xe1,
179 0xff, 0x20, 0x02, 0xe2,
180 0x04, 0x20, 0x80, 0xe5,
181
182
183
184 0x0c, 0x10, 0x90, 0xe5,
185 0x80, 0x10, 0xc1, 0xe3,
186 0x0c, 0x10, 0x80, 0xe5,
187
188
189
190
191 0xb7, 0x19, 0xa0, 0xe3,
192
193 0x01, 0x10, 0x41, 0xe2,
194 0x00, 0x00, 0x51, 0xe3,
195 0xfc, 0xff, 0xff, 0x1a,
196
197
198 0x01, 0x00, 0x00, 0xea,
199
200
201
202 0x00, 0x00, 0x00, 0x00,
203
204
205
206 0x00, 0x00, 0x00, 0x00,
207
208
209};
210
211
212static unsigned char kwboot_baud_code_binhdr_pre[] = {
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230 0xfe, 0x5f, 0x2d, 0xe9,
231
232
233 0x0d, 0x02, 0xa0, 0xe3,
234 0x12, 0x0a, 0x80, 0xe3,
235
236
237 0x00, 0x20, 0x8f, 0xe2,
238
239
240 0x03, 0x00, 0x00, 0xea,
241
242
243
244 0x24, 0x62, 0x61, 0x75,
245 0x64, 0x72, 0x61, 0x74,
246 0x65, 0x63, 0x68, 0x61,
247 0x6e, 0x67, 0x65, 0x00,
248
249
250
251
252
253
254
255 0x14, 0x10, 0x90, 0xe5,
256 0x20, 0x00, 0x11, 0xe3,
257 0xfc, 0xff, 0xff, 0x0a,
258
259
260
261 0x01, 0x10, 0xd2, 0xe4,
262
263 0x00, 0x10, 0x80, 0xe5,
264
265
266 0x00, 0x00, 0x51, 0xe3,
267 0xf8, 0xff, 0xff, 0x1a,
268};
269
270
271static unsigned char kwboot_baud_code_binhdr_post[] = {
272
273 0x00, 0x00, 0xa0, 0xe3,
274 0xfe, 0x9f, 0xbd, 0xe8,
275};
276
277
278static unsigned char kwboot_baud_code_data_jump[] = {
279 0x04, 0xf0, 0x1f, 0xe5,
280
281
282 0x00, 0x00, 0x00, 0x00,
283};
284
285static const char kwb_baud_magic[16] = "$baudratechange";
286
287static int kwboot_verbose;
288
289static int msg_req_delay = KWBOOT_MSG_REQ_DELAY;
290static int msg_rsp_timeo = KWBOOT_MSG_RSP_TIMEO;
291static int blk_rsp_timeo = KWBOOT_BLK_RSP_TIMEO;
292
293static ssize_t
294kwboot_write(int fd, const char *buf, size_t len)
295{
296 size_t tot = 0;
297
298 while (tot < len) {
299 ssize_t wr = write(fd, buf + tot, len - tot);
300
301 if (wr < 0)
302 return -1;
303
304 tot += wr;
305 }
306
307 return tot;
308}
309
310static void
311kwboot_printv(const char *fmt, ...)
312{
313 va_list ap;
314
315 if (kwboot_verbose) {
316 va_start(ap, fmt);
317 vprintf(fmt, ap);
318 va_end(ap);
319 fflush(stdout);
320 }
321}
322
323static void
324__spinner(void)
325{
326 const char seq[] = { '-', '\\', '|', '/' };
327 const int div = 8;
328 static int state, bs;
329
330 if (state % div == 0) {
331 fputc(bs, stdout);
332 fputc(seq[state / div % sizeof(seq)], stdout);
333 fflush(stdout);
334 }
335
336 bs = '\b';
337 state++;
338}
339
340static void
341kwboot_spinner(void)
342{
343 if (kwboot_verbose)
344 __spinner();
345}
346
347static void
348__progress(int pct, char c)
349{
350 const int width = 70;
351 static const char *nl = "";
352 static int pos;
353
354 if (pos % width == 0)
355 printf("%s%3d %% [", nl, pct);
356
357 fputc(c, stdout);
358
359 nl = "]\n";
360 pos = (pos + 1) % width;
361
362 if (pct == 100) {
363 while (pos && pos++ < width)
364 fputc(' ', stdout);
365 fputs(nl, stdout);
366 nl = "";
367 pos = 0;
368 }
369
370 fflush(stdout);
371
372}
373
374static void
375kwboot_progress(int _pct, char c)
376{
377 static int pct;
378
379 if (_pct != -1)
380 pct = _pct;
381
382 if (kwboot_verbose)
383 __progress(pct, c);
384
385 if (pct == 100)
386 pct = 0;
387}
388
389static int
390kwboot_tty_recv(int fd, void *buf, size_t len, int timeo)
391{
392 int rc, nfds;
393 fd_set rfds;
394 struct timeval tv;
395 ssize_t n;
396
397 rc = -1;
398
399 FD_ZERO(&rfds);
400 FD_SET(fd, &rfds);
401
402 tv.tv_sec = 0;
403 tv.tv_usec = timeo * 1000;
404 if (tv.tv_usec > 1000000) {
405 tv.tv_sec += tv.tv_usec / 1000000;
406 tv.tv_usec %= 1000000;
407 }
408
409 do {
410 nfds = select(fd + 1, &rfds, NULL, NULL, &tv);
411 if (nfds < 0)
412 goto out;
413 if (!nfds) {
414 errno = ETIMEDOUT;
415 goto out;
416 }
417
418 n = read(fd, buf, len);
419 if (n <= 0)
420 goto out;
421
422 buf = (char *)buf + n;
423 len -= n;
424 } while (len > 0);
425
426 rc = 0;
427out:
428 return rc;
429}
430
431static int
432kwboot_tty_send(int fd, const void *buf, size_t len, int nodrain)
433{
434 if (!buf)
435 return 0;
436
437 if (kwboot_write(fd, buf, len) < 0)
438 return -1;
439
440 if (nodrain)
441 return 0;
442
443 return tcdrain(fd);
444}
445
446static int
447kwboot_tty_send_char(int fd, unsigned char c)
448{
449 return kwboot_tty_send(fd, &c, 1, 0);
450}
451
452static speed_t
453kwboot_tty_baudrate_to_speed(int baudrate)
454{
455 switch (baudrate) {
456#ifdef B4000000
457 case 4000000:
458 return B4000000;
459#endif
460#ifdef B3500000
461 case 3500000:
462 return B3500000;
463#endif
464#ifdef B3000000
465 case 3000000:
466 return B3000000;
467#endif
468#ifdef B2500000
469 case 2500000:
470 return B2500000;
471#endif
472#ifdef B2000000
473 case 2000000:
474 return B2000000;
475#endif
476#ifdef B1500000
477 case 1500000:
478 return B1500000;
479#endif
480#ifdef B1152000
481 case 1152000:
482 return B1152000;
483#endif
484#ifdef B1000000
485 case 1000000:
486 return B1000000;
487#endif
488#ifdef B921600
489 case 921600:
490 return B921600;
491#endif
492#ifdef B614400
493 case 614400:
494 return B614400;
495#endif
496#ifdef B576000
497 case 576000:
498 return B576000;
499#endif
500#ifdef B500000
501 case 500000:
502 return B500000;
503#endif
504#ifdef B460800
505 case 460800:
506 return B460800;
507#endif
508#ifdef B307200
509 case 307200:
510 return B307200;
511#endif
512#ifdef B230400
513 case 230400:
514 return B230400;
515#endif
516#ifdef B153600
517 case 153600:
518 return B153600;
519#endif
520#ifdef B115200
521 case 115200:
522 return B115200;
523#endif
524#ifdef B76800
525 case 76800:
526 return B76800;
527#endif
528#ifdef B57600
529 case 57600:
530 return B57600;
531#endif
532#ifdef B38400
533 case 38400:
534 return B38400;
535#endif
536#ifdef B19200
537 case 19200:
538 return B19200;
539#endif
540#ifdef B9600
541 case 9600:
542 return B9600;
543#endif
544#ifdef B4800
545 case 4800:
546 return B4800;
547#endif
548#ifdef B2400
549 case 2400:
550 return B2400;
551#endif
552#ifdef B1800
553 case 1800:
554 return B1800;
555#endif
556#ifdef B1200
557 case 1200:
558 return B1200;
559#endif
560#ifdef B600
561 case 600:
562 return B600;
563#endif
564#ifdef B300
565 case 300:
566 return B300;
567#endif
568#ifdef B200
569 case 200:
570 return B200;
571#endif
572#ifdef B150
573 case 150:
574 return B150;
575#endif
576#ifdef B134
577 case 134:
578 return B134;
579#endif
580#ifdef B110
581 case 110:
582 return B110;
583#endif
584#ifdef B75
585 case 75:
586 return B75;
587#endif
588#ifdef B50
589 case 50:
590 return B50;
591#endif
592 default:
593#ifdef BOTHER
594 return BOTHER;
595#else
596 return B0;
597#endif
598 }
599}
600
601static int
602_is_within_tolerance(int value, int reference, int tolerance)
603{
604 return 100 * value >= reference * (100 - tolerance) &&
605 100 * value <= reference * (100 + tolerance);
606}
607
608static int
609kwboot_tty_change_baudrate(int fd, int baudrate)
610{
611 struct termios tio;
612 speed_t speed;
613 int rc;
614
615 rc = tcgetattr(fd, &tio);
616 if (rc)
617 return rc;
618
619 speed = kwboot_tty_baudrate_to_speed(baudrate);
620 if (speed == B0) {
621 errno = EINVAL;
622 return -1;
623 }
624
625#ifdef BOTHER
626 if (speed == BOTHER)
627 tio.c_ospeed = tio.c_ispeed = baudrate;
628#endif
629
630 rc = cfsetospeed(&tio, speed);
631 if (rc)
632 return rc;
633
634 rc = cfsetispeed(&tio, speed);
635 if (rc)
636 return rc;
637
638 rc = tcsetattr(fd, TCSANOW, &tio);
639 if (rc)
640 return rc;
641
642 rc = tcgetattr(fd, &tio);
643 if (rc)
644 return rc;
645
646 if (cfgetospeed(&tio) != speed || cfgetispeed(&tio) != speed)
647 goto baud_fail;
648
649#ifdef BOTHER
650
651
652
653
654
655 if (!_is_within_tolerance(tio.c_ospeed, baudrate, 3))
656 goto baud_fail;
657
658 if (!_is_within_tolerance(tio.c_ispeed, baudrate, 3))
659 goto baud_fail;
660#endif
661
662 return 0;
663
664baud_fail:
665 fprintf(stderr, "Could not set baudrate to requested value\n");
666 errno = EINVAL;
667 return -1;
668}
669
670static int
671kwboot_open_tty(const char *path, int baudrate)
672{
673 int rc, fd, flags;
674 struct termios tio;
675
676 rc = -1;
677
678 fd = open(path, O_RDWR | O_NOCTTY | O_NDELAY);
679 if (fd < 0)
680 goto out;
681
682 rc = tcgetattr(fd, &tio);
683 if (rc)
684 goto out;
685
686 cfmakeraw(&tio);
687 tio.c_cflag |= CREAD | CLOCAL;
688 tio.c_cflag &= ~(CSTOPB | HUPCL | CRTSCTS);
689 tio.c_cc[VMIN] = 1;
690 tio.c_cc[VTIME] = 0;
691
692 rc = tcsetattr(fd, TCSANOW, &tio);
693 if (rc)
694 goto out;
695
696 flags = fcntl(fd, F_GETFL);
697 if (flags < 0)
698 goto out;
699
700 rc = fcntl(fd, F_SETFL, flags & ~O_NDELAY);
701 if (rc)
702 goto out;
703
704 rc = kwboot_tty_change_baudrate(fd, baudrate);
705 if (rc)
706 goto out;
707
708 rc = fd;
709out:
710 if (rc < 0) {
711 if (fd >= 0)
712 close(fd);
713 }
714
715 return rc;
716}
717
718static int
719kwboot_bootmsg(int tty, void *msg)
720{
721 int rc;
722 char c;
723 int count;
724
725 if (msg == NULL)
726 kwboot_printv("Please reboot the target into UART boot mode...");
727 else
728 kwboot_printv("Sending boot message. Please reboot the target...");
729
730 do {
731 rc = tcflush(tty, TCIOFLUSH);
732 if (rc)
733 break;
734
735 for (count = 0; count < 128; count++) {
736 rc = kwboot_tty_send(tty, msg, 8, 0);
737 if (rc) {
738 usleep(msg_req_delay * 1000);
739 continue;
740 }
741 }
742
743 rc = kwboot_tty_recv(tty, &c, 1, msg_rsp_timeo);
744
745 kwboot_spinner();
746
747 } while (rc || c != NAK);
748
749 kwboot_printv("\n");
750
751 return rc;
752}
753
754static int
755kwboot_debugmsg(int tty, void *msg)
756{
757 int rc;
758
759 kwboot_printv("Sending debug message. Please reboot the target...");
760
761 do {
762 char buf[16];
763
764 rc = tcflush(tty, TCIOFLUSH);
765 if (rc)
766 break;
767
768 rc = kwboot_tty_send(tty, msg, 8, 0);
769 if (rc) {
770 usleep(msg_req_delay * 1000);
771 continue;
772 }
773
774 rc = kwboot_tty_recv(tty, buf, 16, msg_rsp_timeo);
775
776 kwboot_spinner();
777
778 } while (rc);
779
780 kwboot_printv("\n");
781
782 return rc;
783}
784
785static size_t
786kwboot_xm_makeblock(struct kwboot_block *block, const void *data,
787 size_t size, int pnum)
788{
789 size_t i, n;
790
791 block->soh = SOH;
792 block->pnum = pnum;
793 block->_pnum = ~block->pnum;
794
795 n = size < KWBOOT_XM_BLKSZ ? size : KWBOOT_XM_BLKSZ;
796 memcpy(&block->data[0], data, n);
797 memset(&block->data[n], 0, KWBOOT_XM_BLKSZ - n);
798
799 block->csum = 0;
800 for (i = 0; i < n; i++)
801 block->csum += block->data[i];
802
803 return n;
804}
805
806static uint64_t
807_now(void)
808{
809 struct timespec ts;
810
811 if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
812 static int err_print;
813
814 if (!err_print) {
815 perror("clock_gettime() does not work");
816 err_print = 1;
817 }
818
819
820 return -1ULL;
821 }
822
823 return ts.tv_sec * 1000ULL + (ts.tv_nsec + 500000) / 1000000;
824}
825
826static int
827_is_xm_reply(char c)
828{
829 return c == ACK || c == NAK || c == CAN;
830}
831
832static int
833_xm_reply_to_error(int c)
834{
835 int rc = -1;
836
837 switch (c) {
838 case ACK:
839 rc = 0;
840 break;
841 case NAK:
842 errno = EBADMSG;
843 break;
844 case CAN:
845 errno = ECANCELED;
846 break;
847 default:
848 errno = EPROTO;
849 break;
850 }
851
852 return rc;
853}
854
855static int
856kwboot_baud_magic_handle(int fd, char c, int baudrate)
857{
858 static size_t rcv_len;
859
860 if (rcv_len < sizeof(kwb_baud_magic)) {
861
862 if (c == kwb_baud_magic[rcv_len]) {
863 rcv_len++;
864 } else {
865 printf("%.*s%c", (int)rcv_len, kwb_baud_magic, c);
866 fflush(stdout);
867 rcv_len = 0;
868 }
869 }
870
871 if (rcv_len == sizeof(kwb_baud_magic)) {
872
873 kwboot_printv("\nChanging baudrate to %d Bd\n", baudrate);
874
875 return kwboot_tty_change_baudrate(fd, baudrate) ? : 1;
876 } else {
877 return 0;
878 }
879}
880
881static int
882kwboot_xm_recv_reply(int fd, char *c, int nak_on_non_xm,
883 int allow_non_xm, int *non_xm_print,
884 int baudrate, int *baud_changed)
885{
886 int timeout = allow_non_xm ? KWBOOT_HDR_RSP_TIMEO : blk_rsp_timeo;
887 uint64_t recv_until = _now() + timeout;
888 int rc;
889
890 while (1) {
891 rc = kwboot_tty_recv(fd, c, 1, timeout);
892 if (rc) {
893 if (errno != ETIMEDOUT)
894 return rc;
895 else if (allow_non_xm && *non_xm_print)
896 return -1;
897 else
898 *c = NAK;
899 }
900
901
902 if (_is_xm_reply(*c))
903 break;
904
905
906
907
908
909
910
911
912
913
914 if (allow_non_xm) {
915 recv_until = _now() + timeout;
916
917 if (baudrate && !*baud_changed) {
918 rc = kwboot_baud_magic_handle(fd, *c, baudrate);
919 if (rc == 1)
920 *baud_changed = 1;
921 else if (!rc)
922 *non_xm_print = 1;
923 else
924 return rc;
925 } else if (!baudrate || !*baud_changed) {
926 putchar(*c);
927 fflush(stdout);
928 *non_xm_print = 1;
929 }
930 } else {
931 if (nak_on_non_xm) {
932 *c = NAK;
933 break;
934 }
935 timeout = recv_until - _now();
936 if (timeout < 0) {
937 errno = ETIMEDOUT;
938 return -1;
939 }
940 }
941 }
942
943 return 0;
944}
945
946static int
947kwboot_xm_sendblock(int fd, struct kwboot_block *block, int allow_non_xm,
948 int *done_print, int baudrate)
949{
950 int non_xm_print, baud_changed;
951 int rc, err, retries;
952 char c;
953
954 *done_print = 0;
955 non_xm_print = 0;
956 baud_changed = 0;
957
958 retries = 0;
959 do {
960 rc = kwboot_tty_send(fd, block, sizeof(*block), 1);
961 if (rc)
962 return rc;
963
964 if (allow_non_xm && !*done_print) {
965 kwboot_progress(100, '.');
966 kwboot_printv("Done\n");
967 *done_print = 1;
968 }
969
970 rc = kwboot_xm_recv_reply(fd, &c, retries < 3,
971 allow_non_xm, &non_xm_print,
972 baudrate, &baud_changed);
973 if (rc)
974 goto can;
975
976 if (!allow_non_xm && c != ACK)
977 kwboot_progress(-1, '+');
978 } while (c == NAK && retries++ < 16);
979
980 if (non_xm_print)
981 kwboot_printv("\n");
982
983 if (allow_non_xm && baudrate && !baud_changed) {
984 fprintf(stderr, "Baudrate was not changed\n");
985 rc = -1;
986 errno = EPROTO;
987 goto can;
988 }
989
990 return _xm_reply_to_error(c);
991can:
992 err = errno;
993 kwboot_tty_send_char(fd, CAN);
994 kwboot_printv("\n");
995 errno = err;
996 return rc;
997}
998
999static int
1000kwboot_xm_finish(int fd)
1001{
1002 int rc, retries;
1003 char c;
1004
1005 kwboot_printv("Finishing transfer\n");
1006
1007 retries = 0;
1008 do {
1009 rc = kwboot_tty_send_char(fd, EOT);
1010 if (rc)
1011 return rc;
1012
1013 rc = kwboot_xm_recv_reply(fd, &c, retries < 3,
1014 0, NULL, 0, NULL);
1015 if (rc)
1016 return rc;
1017 } while (c == NAK && retries++ < 16);
1018
1019 return _xm_reply_to_error(c);
1020}
1021
1022static int
1023kwboot_xmodem_one(int tty, int *pnum, int header, const uint8_t *data,
1024 size_t size, int baudrate)
1025{
1026 int done_print = 0;
1027 size_t sent, left;
1028 int rc;
1029
1030 kwboot_printv("Sending boot image %s (%zu bytes)...\n",
1031 header ? "header" : "data", size);
1032
1033 left = size;
1034 sent = 0;
1035
1036 while (sent < size) {
1037 struct kwboot_block block;
1038 int last_block;
1039 size_t blksz;
1040
1041 blksz = kwboot_xm_makeblock(&block, data, left, (*pnum)++);
1042 data += blksz;
1043
1044 last_block = (left <= blksz);
1045
1046 rc = kwboot_xm_sendblock(tty, &block, header && last_block,
1047 &done_print, baudrate);
1048 if (rc)
1049 goto out;
1050
1051 sent += blksz;
1052 left -= blksz;
1053
1054 if (!done_print)
1055 kwboot_progress(sent * 100 / size, '.');
1056 }
1057
1058 if (!done_print)
1059 kwboot_printv("Done\n");
1060
1061 return 0;
1062out:
1063 kwboot_printv("\n");
1064 return rc;
1065}
1066
1067static int
1068kwboot_xmodem(int tty, const void *_img, size_t size, int baudrate)
1069{
1070 const uint8_t *img = _img;
1071 int rc, pnum;
1072 size_t hdrsz;
1073
1074 hdrsz = kwbheader_size(img);
1075
1076
1077
1078
1079
1080
1081
1082 hdrsz += (KWBOOT_XM_BLKSZ - hdrsz % KWBOOT_XM_BLKSZ) % KWBOOT_XM_BLKSZ;
1083
1084 kwboot_printv("Waiting 2s and flushing tty\n");
1085 sleep(2);
1086 tcflush(tty, TCIOFLUSH);
1087
1088 pnum = 1;
1089
1090 rc = kwboot_xmodem_one(tty, &pnum, 1, img, hdrsz, baudrate);
1091 if (rc)
1092 return rc;
1093
1094
1095
1096
1097
1098 if (hdrsz < size) {
1099 img += hdrsz;
1100 size -= hdrsz;
1101 rc = kwboot_xmodem_one(tty, &pnum, 0, img, size, 0);
1102 if (rc)
1103 return rc;
1104 }
1105
1106 rc = kwboot_xm_finish(tty);
1107 if (rc)
1108 return rc;
1109
1110 if (baudrate) {
1111 kwboot_printv("\nChanging baudrate back to 115200 Bd\n\n");
1112 rc = kwboot_tty_change_baudrate(tty, 115200);
1113 if (rc)
1114 return rc;
1115 }
1116
1117 return 0;
1118}
1119
1120static int
1121kwboot_term_pipe(int in, int out, const char *quit, int *s)
1122{
1123 ssize_t nin;
1124 char _buf[128], *buf = _buf;
1125
1126 nin = read(in, buf, sizeof(_buf));
1127 if (nin <= 0)
1128 return -1;
1129
1130 if (quit) {
1131 int i;
1132
1133 for (i = 0; i < nin; i++) {
1134 if (*buf == quit[*s]) {
1135 (*s)++;
1136 if (!quit[*s])
1137 return 0;
1138 buf++;
1139 nin--;
1140 } else {
1141 if (kwboot_write(out, quit, *s) < 0)
1142 return -1;
1143 *s = 0;
1144 }
1145 }
1146 }
1147
1148 if (kwboot_write(out, buf, nin) < 0)
1149 return -1;
1150
1151 return 0;
1152}
1153
1154static int
1155kwboot_terminal(int tty)
1156{
1157 int rc, in, s;
1158 const char *quit = "\34c";
1159 struct termios otio, tio;
1160
1161 rc = -1;
1162
1163 in = STDIN_FILENO;
1164 if (isatty(in)) {
1165 rc = tcgetattr(in, &otio);
1166 if (!rc) {
1167 tio = otio;
1168 cfmakeraw(&tio);
1169 rc = tcsetattr(in, TCSANOW, &tio);
1170 }
1171 if (rc) {
1172 perror("tcsetattr");
1173 goto out;
1174 }
1175
1176 kwboot_printv("[Type Ctrl-%c + %c to quit]\r\n",
1177 quit[0] | 0100, quit[1]);
1178 } else
1179 in = -1;
1180
1181 rc = 0;
1182 s = 0;
1183
1184 do {
1185 fd_set rfds;
1186 int nfds = 0;
1187
1188 FD_ZERO(&rfds);
1189 FD_SET(tty, &rfds);
1190 nfds = nfds < tty ? tty : nfds;
1191
1192 if (in >= 0) {
1193 FD_SET(in, &rfds);
1194 nfds = nfds < in ? in : nfds;
1195 }
1196
1197 nfds = select(nfds + 1, &rfds, NULL, NULL, NULL);
1198 if (nfds < 0)
1199 break;
1200
1201 if (FD_ISSET(tty, &rfds)) {
1202 rc = kwboot_term_pipe(tty, STDOUT_FILENO, NULL, NULL);
1203 if (rc)
1204 break;
1205 }
1206
1207 if (in >= 0 && FD_ISSET(in, &rfds)) {
1208 rc = kwboot_term_pipe(in, tty, quit, &s);
1209 if (rc)
1210 break;
1211 }
1212 } while (quit[s] != 0);
1213
1214 if (in >= 0)
1215 tcsetattr(in, TCSANOW, &otio);
1216 printf("\n");
1217out:
1218 return rc;
1219}
1220
1221static void *
1222kwboot_read_image(const char *path, size_t *size, size_t reserve)
1223{
1224 int rc, fd;
1225 struct stat st;
1226 void *img;
1227 off_t tot;
1228
1229 rc = -1;
1230 img = NULL;
1231
1232 fd = open(path, O_RDONLY);
1233 if (fd < 0)
1234 goto out;
1235
1236 rc = fstat(fd, &st);
1237 if (rc)
1238 goto out;
1239
1240 img = malloc(st.st_size + reserve);
1241 if (!img)
1242 goto out;
1243
1244 tot = 0;
1245 while (tot < st.st_size) {
1246 ssize_t rd = read(fd, img + tot, st.st_size - tot);
1247
1248 if (rd < 0)
1249 goto out;
1250
1251 tot += rd;
1252
1253 if (!rd && tot < st.st_size) {
1254 errno = EIO;
1255 goto out;
1256 }
1257 }
1258
1259 rc = 0;
1260 *size = st.st_size;
1261out:
1262 if (rc && img) {
1263 free(img);
1264 img = NULL;
1265 }
1266 if (fd >= 0)
1267 close(fd);
1268
1269 return img;
1270}
1271
1272static uint8_t
1273kwboot_hdr_csum8(const void *hdr)
1274{
1275 const uint8_t *data = hdr;
1276 uint8_t csum;
1277 size_t size;
1278
1279 size = kwbheader_size_for_csum(hdr);
1280
1281 for (csum = 0; size-- > 0; data++)
1282 csum += *data;
1283
1284 return csum;
1285}
1286
1287static uint32_t *
1288kwboot_img_csum32_ptr(void *img)
1289{
1290 struct main_hdr_v1 *hdr = img;
1291 uint32_t datasz;
1292
1293 datasz = le32_to_cpu(hdr->blocksize) - sizeof(uint32_t);
1294
1295 return img + le32_to_cpu(hdr->srcaddr) + datasz;
1296}
1297
1298static uint32_t
1299kwboot_img_csum32(const void *img)
1300{
1301 const struct main_hdr_v1 *hdr = img;
1302 uint32_t datasz, csum = 0;
1303 const uint32_t *data;
1304
1305 datasz = le32_to_cpu(hdr->blocksize) - sizeof(csum);
1306 if (datasz % sizeof(uint32_t))
1307 return 0;
1308
1309 data = img + le32_to_cpu(hdr->srcaddr);
1310 while (datasz > 0) {
1311 csum += le32_to_cpu(*data++);
1312 datasz -= 4;
1313 }
1314
1315 return cpu_to_le32(csum);
1316}
1317
1318static int
1319kwboot_img_is_secure(void *img)
1320{
1321 struct opt_hdr_v1 *ohdr;
1322
1323 for_each_opt_hdr_v1 (ohdr, img)
1324 if (ohdr->headertype == OPT_HDR_V1_SECURE_TYPE)
1325 return 1;
1326
1327 return 0;
1328}
1329
1330static void *
1331kwboot_img_grow_data_right(void *img, size_t *size, size_t grow)
1332{
1333 struct main_hdr_v1 *hdr = img;
1334 void *result;
1335
1336
1337
1338
1339
1340
1341
1342 result = kwboot_img_csum32_ptr(img);
1343 hdr->blocksize = cpu_to_le32(le32_to_cpu(hdr->blocksize) + grow);
1344 *size += grow;
1345
1346 return result;
1347}
1348
1349static void
1350kwboot_img_grow_hdr(void *img, size_t *size, size_t grow)
1351{
1352 uint32_t hdrsz, datasz, srcaddr;
1353 struct main_hdr_v1 *hdr = img;
1354 struct opt_hdr_v1 *ohdr;
1355 uint8_t *data;
1356
1357 srcaddr = le32_to_cpu(hdr->srcaddr);
1358
1359
1360 if (kwbimage_version(img) == 0) {
1361 hdrsz = kwbheader_size(img);
1362 } else {
1363 hdrsz = sizeof(*hdr);
1364 for_each_opt_hdr_v1 (ohdr, hdr)
1365 hdrsz += opt_hdr_v1_size(ohdr);
1366 }
1367
1368 data = (uint8_t *)img + srcaddr;
1369 datasz = *size - srcaddr;
1370
1371
1372 if (hdrsz + grow > srcaddr) {
1373 size_t need = hdrsz + grow - srcaddr;
1374
1375
1376 memmove(data + need, data, datasz);
1377
1378 hdr->srcaddr = cpu_to_le32(srcaddr + need);
1379 *size += need;
1380 }
1381
1382 if (kwbimage_version(img) == 1) {
1383 hdrsz += grow;
1384 if (hdrsz > kwbheader_size(img)) {
1385 hdr->headersz_msb = hdrsz >> 16;
1386 hdr->headersz_lsb = cpu_to_le16(hdrsz & 0xffff);
1387 }
1388 }
1389}
1390
1391static void *
1392kwboot_add_bin_ohdr_v1(void *img, size_t *size, uint32_t binsz)
1393{
1394 struct main_hdr_v1 *hdr = img;
1395 struct opt_hdr_v1 *ohdr;
1396 uint32_t num_args;
1397 uint32_t offset;
1398 uint32_t ohdrsz;
1399 uint8_t *prev_ext;
1400
1401 if (hdr->ext & 0x1) {
1402 for_each_opt_hdr_v1 (ohdr, img)
1403 if (opt_hdr_v1_next(ohdr) == NULL)
1404 break;
1405
1406 prev_ext = opt_hdr_v1_ext(ohdr);
1407 ohdr = _opt_hdr_v1_next(ohdr);
1408 } else {
1409 ohdr = (void *)(hdr + 1);
1410 prev_ext = &hdr->ext;
1411 }
1412
1413
1414
1415
1416
1417
1418
1419 offset = &ohdr->data[4] - (char *)img;
1420 num_args = ((16 - offset % 16) % 16) / sizeof(uint32_t);
1421
1422 ohdrsz = sizeof(*ohdr) + 4 + 4 * num_args + binsz + 4;
1423 kwboot_img_grow_hdr(hdr, size, ohdrsz);
1424
1425 *prev_ext |= 1;
1426
1427 ohdr->headertype = OPT_HDR_V1_BINARY_TYPE;
1428 ohdr->headersz_msb = ohdrsz >> 16;
1429 ohdr->headersz_lsb = cpu_to_le16(ohdrsz & 0xffff);
1430
1431 memset(&ohdr->data[0], 0, ohdrsz - sizeof(*ohdr));
1432 *(uint32_t *)&ohdr->data[0] = cpu_to_le32(num_args);
1433
1434 return &ohdr->data[4 + 4 * num_args];
1435}
1436
1437static void
1438_inject_baudrate_change_code(void *img, size_t *size, int for_data,
1439 int old_baud, int new_baud)
1440{
1441 struct main_hdr_v1 *hdr = img;
1442 uint32_t orig_datasz;
1443 uint32_t codesz;
1444 uint8_t *code;
1445
1446 if (for_data) {
1447 orig_datasz = le32_to_cpu(hdr->blocksize) - sizeof(uint32_t);
1448
1449 codesz = sizeof(kwboot_baud_code) +
1450 sizeof(kwboot_baud_code_data_jump);
1451 code = kwboot_img_grow_data_right(img, size, codesz);
1452 } else {
1453 codesz = sizeof(kwboot_baud_code_binhdr_pre) +
1454 sizeof(kwboot_baud_code) +
1455 sizeof(kwboot_baud_code_binhdr_post);
1456 code = kwboot_add_bin_ohdr_v1(img, size, codesz);
1457
1458 codesz = sizeof(kwboot_baud_code_binhdr_pre);
1459 memcpy(code, kwboot_baud_code_binhdr_pre, codesz);
1460 code += codesz;
1461 }
1462
1463 codesz = sizeof(kwboot_baud_code) - 2 * sizeof(uint32_t);
1464 memcpy(code, kwboot_baud_code, codesz);
1465 code += codesz;
1466 *(uint32_t *)code = cpu_to_le32(old_baud);
1467 code += sizeof(uint32_t);
1468 *(uint32_t *)code = cpu_to_le32(new_baud);
1469 code += sizeof(uint32_t);
1470
1471 if (for_data) {
1472 codesz = sizeof(kwboot_baud_code_data_jump) - sizeof(uint32_t);
1473 memcpy(code, kwboot_baud_code_data_jump, codesz);
1474 code += codesz;
1475 *(uint32_t *)code = hdr->execaddr;
1476 code += sizeof(uint32_t);
1477 hdr->execaddr = cpu_to_le32(le32_to_cpu(hdr->destaddr) + orig_datasz);
1478 } else {
1479 codesz = sizeof(kwboot_baud_code_binhdr_post);
1480 memcpy(code, kwboot_baud_code_binhdr_post, codesz);
1481 code += codesz;
1482 }
1483}
1484
1485static int
1486kwboot_img_patch(void *img, size_t *size, int baudrate)
1487{
1488 struct main_hdr_v1 *hdr;
1489 uint32_t srcaddr;
1490 uint8_t csum;
1491 size_t hdrsz;
1492 int image_ver;
1493 int is_secure;
1494
1495 hdr = img;
1496
1497 if (*size < sizeof(struct main_hdr_v1))
1498 goto err;
1499
1500 image_ver = kwbimage_version(img);
1501 if (image_ver != 0 && image_ver != 1) {
1502 fprintf(stderr, "Invalid image header version\n");
1503 goto err;
1504 }
1505
1506 hdrsz = kwbheader_size(hdr);
1507
1508 if (*size < hdrsz)
1509 goto err;
1510
1511 csum = kwboot_hdr_csum8(hdr) - hdr->checksum;
1512 if (csum != hdr->checksum)
1513 goto err;
1514
1515 srcaddr = le32_to_cpu(hdr->srcaddr);
1516
1517 switch (hdr->blockid) {
1518 case IBR_HDR_SATA_ID:
1519 if (srcaddr < 1)
1520 goto err;
1521
1522 hdr->srcaddr = cpu_to_le32((srcaddr - 1) * 512);
1523 break;
1524
1525 case IBR_HDR_SDIO_ID:
1526 hdr->srcaddr = cpu_to_le32(srcaddr * 512);
1527 break;
1528
1529 case IBR_HDR_PEX_ID:
1530 if (srcaddr == 0xFFFFFFFF)
1531 hdr->srcaddr = cpu_to_le32(hdrsz);
1532 break;
1533
1534 case IBR_HDR_SPI_ID:
1535 if (hdr->destaddr == cpu_to_le32(0xFFFFFFFF)) {
1536 kwboot_printv("Patching destination and execution addresses from SPI/NOR XIP area to DDR area 0x00800000\n");
1537 hdr->destaddr = cpu_to_le32(0x00800000);
1538 hdr->execaddr = cpu_to_le32(0x00800000);
1539 }
1540 break;
1541 }
1542
1543 if (hdrsz > le32_to_cpu(hdr->srcaddr) ||
1544 *size < le32_to_cpu(hdr->srcaddr) + le32_to_cpu(hdr->blocksize))
1545 goto err;
1546
1547 if (kwboot_img_csum32(img) != *kwboot_img_csum32_ptr(img))
1548 goto err;
1549
1550 is_secure = kwboot_img_is_secure(img);
1551
1552 if (hdr->blockid != IBR_HDR_UART_ID) {
1553 if (is_secure) {
1554 fprintf(stderr,
1555 "Image has secure header with signature for non-UART booting\n");
1556 goto err;
1557 }
1558
1559 kwboot_printv("Patching image boot signature to UART\n");
1560 hdr->blockid = IBR_HDR_UART_ID;
1561 }
1562
1563 if (!is_secure) {
1564 if (image_ver == 1) {
1565
1566
1567
1568
1569
1570
1571 hdr->options &= ~0x1F;
1572 hdr->options |= MAIN_HDR_V1_OPT_BAUD_DEFAULT;
1573 hdr->options |= 0 << 3;
1574 }
1575 if (image_ver == 0)
1576 ((struct main_hdr_v0 *)img)->nandeccmode = IBR_HDR_ECC_DISABLED;
1577 hdr->nandpagesize = 0;
1578 }
1579
1580 if (baudrate) {
1581 if (image_ver == 0) {
1582 fprintf(stderr,
1583 "Cannot inject code for changing baudrate into v0 image header\n");
1584 goto err;
1585 }
1586
1587 if (is_secure) {
1588 fprintf(stderr,
1589 "Cannot inject code for changing baudrate into image with secure header\n");
1590 goto err;
1591 }
1592
1593
1594
1595
1596
1597
1598
1599 kwboot_printv("Injecting binary header code for changing baudrate to %d Bd\n",
1600 baudrate);
1601 _inject_baudrate_change_code(img, size, 0, 115200, baudrate);
1602
1603
1604
1605
1606
1607
1608
1609 kwboot_printv("Injecting code for changing baudrate back\n");
1610 _inject_baudrate_change_code(img, size, 1, baudrate, 115200);
1611
1612
1613 *kwboot_img_csum32_ptr(img) = kwboot_img_csum32(img);
1614
1615
1616 hdrsz = kwbheader_size(hdr);
1617 }
1618
1619 if (hdrsz % KWBOOT_XM_BLKSZ) {
1620 size_t grow = KWBOOT_XM_BLKSZ - hdrsz % KWBOOT_XM_BLKSZ;
1621
1622 if (is_secure) {
1623 fprintf(stderr, "Cannot align image with secure header\n");
1624 goto err;
1625 }
1626
1627 kwboot_printv("Aligning image header to Xmodem block size\n");
1628 kwboot_img_grow_hdr(img, size, grow);
1629 }
1630
1631 hdr->checksum = kwboot_hdr_csum8(hdr) - csum;
1632
1633 *size = le32_to_cpu(hdr->srcaddr) + le32_to_cpu(hdr->blocksize);
1634 return 0;
1635err:
1636 errno = EINVAL;
1637 return -1;
1638}
1639
1640static void
1641kwboot_usage(FILE *stream, char *progname)
1642{
1643 fprintf(stream,
1644 "Usage: %s [OPTIONS] [-b <image> | -D <image> ] [-B <baud> ] <TTY>\n",
1645 progname);
1646 fprintf(stream, "\n");
1647 fprintf(stream,
1648 " -b <image>: boot <image> with preamble (Kirkwood, Armada 370/XP)\n");
1649 fprintf(stream,
1650 " -D <image>: boot <image> without preamble (Dove)\n");
1651 fprintf(stream, " -d: enter debug mode\n");
1652 fprintf(stream, " -a: use timings for Armada XP\n");
1653 fprintf(stream, " -q <req-delay>: use specific request-delay\n");
1654 fprintf(stream, " -s <resp-timeo>: use specific response-timeout\n");
1655 fprintf(stream,
1656 " -o <block-timeo>: use specific xmodem block timeout\n");
1657 fprintf(stream, "\n");
1658 fprintf(stream, " -t: mini terminal\n");
1659 fprintf(stream, "\n");
1660 fprintf(stream, " -B <baud>: set baud rate\n");
1661 fprintf(stream, "\n");
1662}
1663
1664int
1665main(int argc, char **argv)
1666{
1667 const char *ttypath, *imgpath;
1668 int rv, rc, tty, term;
1669 void *bootmsg;
1670 void *debugmsg;
1671 void *img;
1672 size_t size;
1673 size_t after_img_rsv;
1674 int baudrate;
1675
1676 rv = 1;
1677 tty = -1;
1678 bootmsg = NULL;
1679 debugmsg = NULL;
1680 imgpath = NULL;
1681 img = NULL;
1682 term = 0;
1683 size = 0;
1684 after_img_rsv = KWBOOT_XM_BLKSZ;
1685 baudrate = 115200;
1686
1687 printf("kwboot version %s\n", PLAIN_VERSION);
1688
1689 kwboot_verbose = isatty(STDOUT_FILENO);
1690
1691 do {
1692 int c = getopt(argc, argv, "hb:ptaB:dD:q:s:o:");
1693 if (c < 0)
1694 break;
1695
1696 switch (c) {
1697 case 'b':
1698 bootmsg = kwboot_msg_boot;
1699 imgpath = optarg;
1700 break;
1701
1702 case 'D':
1703 bootmsg = NULL;
1704 imgpath = optarg;
1705 break;
1706
1707 case 'd':
1708 debugmsg = kwboot_msg_debug;
1709 break;
1710
1711 case 'p':
1712
1713 break;
1714
1715 case 't':
1716 term = 1;
1717 break;
1718
1719 case 'a':
1720 msg_req_delay = KWBOOT_MSG_REQ_DELAY_AXP;
1721 msg_rsp_timeo = KWBOOT_MSG_RSP_TIMEO_AXP;
1722 break;
1723
1724 case 'q':
1725 msg_req_delay = atoi(optarg);
1726 break;
1727
1728 case 's':
1729 msg_rsp_timeo = atoi(optarg);
1730 break;
1731
1732 case 'o':
1733 blk_rsp_timeo = atoi(optarg);
1734 break;
1735
1736 case 'B':
1737 baudrate = atoi(optarg);
1738 break;
1739
1740 case 'h':
1741 rv = 0;
1742 default:
1743 goto usage;
1744 }
1745 } while (1);
1746
1747 if (!bootmsg && !term && !debugmsg)
1748 goto usage;
1749
1750 if (argc - optind < 1)
1751 goto usage;
1752
1753 ttypath = argv[optind++];
1754
1755 tty = kwboot_open_tty(ttypath, imgpath ? 115200 : baudrate);
1756 if (tty < 0) {
1757 perror(ttypath);
1758 goto out;
1759 }
1760
1761 if (baudrate == 115200)
1762
1763 baudrate = 0;
1764 else
1765
1766 after_img_rsv += sizeof(struct opt_hdr_v1) + 8 + 16 +
1767 sizeof(kwboot_baud_code_binhdr_pre) +
1768 sizeof(kwboot_baud_code) +
1769 sizeof(kwboot_baud_code_binhdr_post) +
1770 KWBOOT_XM_BLKSZ +
1771 sizeof(kwboot_baud_code) +
1772 sizeof(kwboot_baud_code_data_jump) +
1773 KWBOOT_XM_BLKSZ;
1774
1775 if (imgpath) {
1776 img = kwboot_read_image(imgpath, &size, after_img_rsv);
1777 if (!img) {
1778 perror(imgpath);
1779 goto out;
1780 }
1781
1782 rc = kwboot_img_patch(img, &size, baudrate);
1783 if (rc) {
1784 fprintf(stderr, "%s: Invalid image.\n", imgpath);
1785 goto out;
1786 }
1787 }
1788
1789 if (debugmsg) {
1790 rc = kwboot_debugmsg(tty, debugmsg);
1791 if (rc) {
1792 perror("debugmsg");
1793 goto out;
1794 }
1795 } else if (bootmsg) {
1796 rc = kwboot_bootmsg(tty, bootmsg);
1797 if (rc) {
1798 perror("bootmsg");
1799 goto out;
1800 }
1801 }
1802
1803 if (img) {
1804 rc = kwboot_xmodem(tty, img, size, baudrate);
1805 if (rc) {
1806 perror("xmodem");
1807 goto out;
1808 }
1809 }
1810
1811 if (term) {
1812 rc = kwboot_terminal(tty);
1813 if (rc && !(errno == EINTR)) {
1814 perror("terminal");
1815 goto out;
1816 }
1817 }
1818
1819 rv = 0;
1820out:
1821 if (tty >= 0)
1822 close(tty);
1823
1824 if (img)
1825 free(img);
1826
1827 return rv;
1828
1829usage:
1830 kwboot_usage(rv ? stderr : stdout, basename(argv[0]));
1831 goto out;
1832}
1833