blob: 905ade3b01236d11e4727e32a354c9d866ca618a [file] [log] [blame]
Luka Perkovd131ad62012-05-27 11:44:51 +00001/*
Stefan Roese84899e22014-10-22 12:13:21 +02002 * Boot a Marvell SoC, with Xmodem over UART0.
3 * supports Kirkwood, Dove, Armada 370, Armada XP
Luka Perkovd131ad62012-05-27 11:44:51 +00004 *
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
Stefan Roesef4db6c92016-01-07 14:12:04 +010012#include "kwbimage.h"
13#include "mkimage.h"
14
Luka Perkovd131ad62012-05-27 11:44:51 +000015#include <stdlib.h>
16#include <stdio.h>
17#include <string.h>
18#include <stdarg.h>
Stefan Roesef4db6c92016-01-07 14:12:04 +010019#include <image.h>
Luka Perkovd131ad62012-05-27 11:44:51 +000020#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
Luka Perkovd131ad62012-05-27 11:44:51 +000029#ifdef __GNUC__
30#define PACKED __attribute((packed))
31#else
32#define PACKED
33#endif
34
35/*
36 * Marvell BootROM UART Sensing
37 */
38
39static unsigned char kwboot_msg_boot[] = {
40 0xBB, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77
41};
42
Stefan Roese84899e22014-10-22 12:13:21 +020043static unsigned char kwboot_msg_debug[] = {
44 0xDD, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77
45};
46
47/* Defines known to work on Kirkwood */
Luka Perkovd131ad62012-05-27 11:44:51 +000048#define KWBOOT_MSG_REQ_DELAY 10 /* ms */
49#define KWBOOT_MSG_RSP_TIMEO 50 /* ms */
50
Stefan Roese84899e22014-10-22 12:13:21 +020051/* 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
Luka Perkovd131ad62012-05-27 11:44:51 +000055/*
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
65struct 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
75static int kwboot_verbose;
76
Stefan Roese84899e22014-10-22 12:13:21 +020077static int msg_req_delay = KWBOOT_MSG_REQ_DELAY;
78static int msg_rsp_timeo = KWBOOT_MSG_RSP_TIMEO;
79
Luka Perkovd131ad62012-05-27 11:44:51 +000080static void
81kwboot_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
93static 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
110static void
111kwboot_spinner(void)
112{
113 if (kwboot_verbose)
114 __spinner();
115}
116
117static 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
142static void
143kwboot_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
154static int
155kwboot_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;
192out:
193 return rc;
194}
195
196static int
197kwboot_tty_send(int fd, const void *buf, size_t len)
198{
199 int rc;
200 ssize_t n;
201
Stefan Roese84899e22014-10-22 12:13:21 +0200202 if (!buf)
203 return 0;
204
Luka Perkovd131ad62012-05-27 11:44:51 +0000205 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);
217out:
218 return rc;
219}
220
221static int
222kwboot_tty_send_char(int fd, unsigned char c)
223{
224 return kwboot_tty_send(fd, &c, 1);
225}
226
227static speed_t
228kwboot_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
246static int
247kwboot_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;
274out:
275 if (rc < 0) {
276 if (fd >= 0)
277 close(fd);
278 }
279
280 return rc;
281}
282
283static int
284kwboot_bootmsg(int tty, void *msg)
285{
286 int rc;
287 char c;
288
Stefan Roese84899e22014-10-22 12:13:21 +0200289 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...");
Luka Perkovd131ad62012-05-27 11:44:51 +0000293
294 do {
295 rc = tcflush(tty, TCIOFLUSH);
296 if (rc)
297 break;
298
299 rc = kwboot_tty_send(tty, msg, 8);
300 if (rc) {
Stefan Roese84899e22014-10-22 12:13:21 +0200301 usleep(msg_req_delay * 1000);
Luka Perkovd131ad62012-05-27 11:44:51 +0000302 continue;
303 }
304
Stefan Roese84899e22014-10-22 12:13:21 +0200305 rc = kwboot_tty_recv(tty, &c, 1, msg_rsp_timeo);
Luka Perkovd131ad62012-05-27 11:44:51 +0000306
307 kwboot_spinner();
308
309 } while (rc || c != NAK);
310
311 kwboot_printv("\n");
312
313 return rc;
314}
315
316static int
Stefan Roese84899e22014-10-22 12:13:21 +0200317kwboot_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
347static int
Luka Perkovd131ad62012-05-27 11:44:51 +0000348kwboot_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
Stefan Roese84899e22014-10-22 12:13:21 +0200355 block->soh = SOH;
Luka Perkovd131ad62012-05-27 11:44:51 +0000356 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
370static int
371kwboot_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
Stefan Roese84899e22014-10-22 12:13:21 +0200382 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);
Luka Perkovd131ad62012-05-27 11:44:51 +0000391
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
417static int
418kwboot_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
451out:
452 return rc;
453
454can:
455 err = errno;
456 kwboot_tty_send_char(tty, CAN);
457 errno = err;
458 goto out;
459}
460
461static int
462kwboot_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
501static int
502kwboot_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);
561out:
562 return rc;
563}
564
565static void *
566kwboot_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;
Luka Perkovd131ad62012-05-27 11:44:51 +0000573 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;
593out:
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
604static uint8_t
605kwboot_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
615static int
616kwboot_img_patch_hdr(void *img, size_t size)
617{
618 int rc;
Stefan Roesee29f1db2015-09-29 09:19:59 +0200619 struct main_hdr_v1 *hdr;
Luka Perkovd131ad62012-05-27 11:44:51 +0000620 uint8_t csum;
Stefan Roesee29f1db2015-09-29 09:19:59 +0200621 size_t hdrsz = sizeof(*hdr);
622 int image_ver;
Luka Perkovd131ad62012-05-27 11:44:51 +0000623
624 rc = -1;
625 hdr = img;
626
627 if (size < hdrsz) {
628 errno = EINVAL;
629 goto out;
630 }
631
Stefan Roesee29f1db2015-09-29 09:19:59 +0200632 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) {
Luka Perkovd131ad62012-05-27 11:44:51 +0000646 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
Stefan Roesef4db6c92016-01-07 14:12:04 +0100657 /*
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
Stefan Roesee29f1db2015-09-29 09:19:59 +0200665 if (image_ver == 0) {
666 struct main_hdr_v0 *hdr_v0 = img;
Luka Perkovd131ad62012-05-27 11:44:51 +0000667
Stefan Roesee29f1db2015-09-29 09:19:59 +0200668 hdr_v0->nandeccmode = IBR_HDR_ECC_DISABLED;
669 hdr_v0->nandpagesize = 0;
Luka Perkovd131ad62012-05-27 11:44:51 +0000670
Stefan Roesee29f1db2015-09-29 09:19:59 +0200671 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;
Luka Perkovd131ad62012-05-27 11:44:51 +0000677
678 rc = 0;
679out:
680 return rc;
681}
682
683static void
684kwboot_usage(FILE *stream, char *progname)
685{
686 fprintf(stream,
Stefan Roese1c0df9e2015-05-29 13:25:04 +0200687 "Usage: %s [-d | -a | -q <req-delay> | -s <resp-timeo> | -b <image> | -D <image> ] [ -t ] [-B <baud> ] <TTY>\n",
Stefan Roese84899e22014-10-22 12:13:21 +0200688 progname);
Luka Perkovd131ad62012-05-27 11:44:51 +0000689 fprintf(stream, "\n");
Stefan Roese84899e22014-10-22 12:13:21 +0200690 fprintf(stream,
691 " -b <image>: boot <image> with preamble (Kirkwood, Armada 370/XP)\n");
Luka Perkovd131ad62012-05-27 11:44:51 +0000692 fprintf(stream, " -p: patch <image> to type 0x69 (uart boot)\n");
Stefan Roese84899e22014-10-22 12:13:21 +0200693 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");
Stefan Roese1c0df9e2015-05-29 13:25:04 +0200697 fprintf(stream, " -q <req-delay>: use specific request-delay\n");
698 fprintf(stream, " -s <resp-timeo>: use specific response-timeout\n");
Luka Perkovd131ad62012-05-27 11:44:51 +0000699 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
706int
707main(int argc, char **argv)
708{
709 const char *ttypath, *imgpath;
710 int rv, rc, tty, term, prot, patch;
711 void *bootmsg;
Stefan Roese84899e22014-10-22 12:13:21 +0200712 void *debugmsg;
Luka Perkovd131ad62012-05-27 11:44:51 +0000713 void *img;
714 size_t size;
715 speed_t speed;
716
717 rv = 1;
718 tty = -1;
719 bootmsg = NULL;
Stefan Roese84899e22014-10-22 12:13:21 +0200720 debugmsg = NULL;
Luka Perkovd131ad62012-05-27 11:44:51 +0000721 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 {
Stefan Roese1c0df9e2015-05-29 13:25:04 +0200731 int c = getopt(argc, argv, "hb:ptaB:dD:q:s:");
Luka Perkovd131ad62012-05-27 11:44:51 +0000732 if (c < 0)
733 break;
734
735 switch (c) {
736 case 'b':
737 bootmsg = kwboot_msg_boot;
738 imgpath = optarg;
739 break;
740
Stefan Roese84899e22014-10-22 12:13:21 +0200741 case 'D':
742 bootmsg = NULL;
743 imgpath = optarg;
744 break;
745
746 case 'd':
747 debugmsg = kwboot_msg_debug;
748 break;
749
Luka Perkovd131ad62012-05-27 11:44:51 +0000750 case 'p':
751 patch = 1;
752 break;
753
754 case 't':
755 term = 1;
756 break;
757
Stefan Roese84899e22014-10-22 12:13:21 +0200758 case 'a':
759 msg_req_delay = KWBOOT_MSG_REQ_DELAY_AXP;
760 msg_rsp_timeo = KWBOOT_MSG_RSP_TIMEO_AXP;
761 break;
762
Stefan Roese1c0df9e2015-05-29 13:25:04 +0200763 case 'q':
764 msg_req_delay = atoi(optarg);
765 break;
766
767 case 's':
768 msg_rsp_timeo = atoi(optarg);
769 break;
770
Luka Perkovd131ad62012-05-27 11:44:51 +0000771 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
Stefan Roese84899e22014-10-22 12:13:21 +0200784 if (!bootmsg && !term && !debugmsg)
Luka Perkovd131ad62012-05-27 11:44:51 +0000785 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
Stefan Roese84899e22014-10-22 12:13:21 +0200819 if (debugmsg) {
820 rc = kwboot_debugmsg(tty, debugmsg);
821 if (rc) {
822 perror("debugmsg");
823 goto out;
824 }
825 } else {
Luka Perkovd131ad62012-05-27 11:44:51 +0000826 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;
850out:
851 if (tty >= 0)
852 close(tty);
853
854 if (img)
855 munmap(img, size);
856
857 return rv;
858
859usage:
860 kwboot_usage(rv ? stderr : stdout, basename(argv[0]));
861 goto out;
862}