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