Home | History | Annotate | Download | only in tools
      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 (at) 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 }
    862