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