Root/package/uboot-kirkwood/patches/0002-kwboot.patch

1http://lists.denx.de/pipermail/u-boot/2012-May/125296.html
2http://patchwork.ozlabs.org/patch/161566/
3---
4
5diff --git a/doc/kwboot.1 b/doc/kwboot.1
6new file mode 100644
7index 0000000..ed08398
8--- /dev/null
9+++ b/doc/kwboot.1
10@@ -0,0 +1,84 @@
11+.TH KWBOOT 1 "2012-05-19"
12+
13+.SH NAME
14+kwboot \- Boot Marvell Kirkwood SoCs over a serial link.
15+.SH SYNOPSIS
16+.B kwboot
17+.RB [ "-b \fIimage\fP" ]
18+.RB [ "-p" ]
19+.RB [ "-t" ]
20+.RB [ "-B \fIbaudrate\fP" ]
21+.RB \fITTY\fP
22+.SH "DESCRIPTION"
23+
24+The \fBmkimage\fP program boots boards based on Marvell's Kirkwood
25+platform over their integrated UART. Boot image files will typically
26+contain a second stage boot loader, such as U-Boot. The image file
27+must conform to Marvell's BootROM firmware image format
28+(\fIkwbimage\fP), created using a tool such as \fBmkimage\fP.
29+
30+Following power-up or a system reset, system BootROM code polls the
31+UART for a brief period of time, sensing a handshake message which
32+initiates an image upload. This program sends this boot message until
33+it receives a positive acknowledgement. The image is transfered using
34+Xmodem.
35+
36+Additionally, this program implements a minimal terminal mode, which
37+can be used either standalone, or entered immediately following boot
38+image transfer completion. This is often useful to catch early boot
39+messages, or to manually interrupt a default boot procedure performed
40+by the second-stage loader.
41+
42+.SH "OPTIONS"
43+
44+.TP
45+.BI "\-b \fIimage\fP"
46+Handshake; then upload file \fIimage\fP over \fITTY\fP.
47+
48+Note that for the encapsulated boot code to be executed, \fIimage\fP
49+must be of type "UART boot" (0x69). Boot images of different types,
50+such as backup images of vendor firmware downloaded from flash memory
51+(type 0x8B), will not work (or not as expected). See \fB-p\fP for a
52+workaround.
53+
54+This mode writes handshake status and upload progress indication to
55+stdout.
56+
57+.TP
58+.BI "\-p"
59+In combination with \fB-b\fP, patches the header in \fIimage\fP prior
60+to upload, to "UART boot" type.
61+
62+This option attempts on-the-fly conversion of some none-UART image
63+types, such as images which were originally formatted to be stored in
64+flash memory.
65+
66+Conversion is performed in memory. The contents of \fIimage\fP will
67+not be altered.
68+
69+.TP
70+.BI "\-t"
71+Run a terminal program, connecting standard input and output to
72+.RB \fITTY\fP.
73+
74+If used in combination with \fB-b\fP, terminal mode is entered
75+immediately following a successful image upload.
76+
77+If standard I/O streams connect to a console, this mode will terminate
78+after receiving 'ctrl-\\' followed by 'c' from console input.
79+
80+.TP
81+.BI "\-B \fIbaudrate\fP"
82+Adjust the baud rate on \fITTY\fP. Default rate is 115200.
83+
84+.SH "SEE ALSO"
85+.PP
86+\fBmkimage\fP(1)
87+
88+.SH "AUTHORS"
89+
90+Daniel Stodden <daniel.stodden@gmail.com>
91+.br
92+Luka Perkov <uboot@lukaperkov.net>
93+.br
94+David Purdy <david.c.purdy@gmail.com>
95diff --git a/tools/Makefile b/tools/Makefile
96index 8993fdd..8097d95 100644
97--- a/tools/Makefile
98+++ b/tools/Makefile
99@@ -72,6 +72,7 @@ BIN_FILES-$(CONFIG_SMDK5250) += mksmdk5250spl$(SFX)
100 BIN_FILES-$(CONFIG_MX28) += mxsboot$(SFX)
101 BIN_FILES-$(CONFIG_NETCONSOLE) += ncb$(SFX)
102 BIN_FILES-$(CONFIG_SHA1_CHECK_UB_IMG) += ubsha1$(SFX)
103+BIN_FILES-$(CONFIG_KIRKWOOD) += kwboot$(SFX)
104 
105 # Source files which exist outside the tools directory
106 EXT_OBJ_FILES-$(CONFIG_BUILD_ENVCRC) += common/env_embedded.o
107@@ -101,6 +102,7 @@ OBJ_FILES-$(CONFIG_NETCONSOLE) += ncb.o
108 NOPED_OBJ_FILES-y += os_support.o
109 OBJ_FILES-$(CONFIG_SHA1_CHECK_UB_IMG) += ubsha1.o
110 NOPED_OBJ_FILES-y += ublimage.o
111+OBJ_FILES-$(CONFIG_KIRKWOOD) += kwboot.o
112 
113 # Don't build by default
114 #ifeq ($(ARCH),ppc)
115@@ -234,6 +236,10 @@ $(obj)ncb$(SFX): $(obj)ncb.o
116 $(obj)ubsha1$(SFX): $(obj)os_support.o $(obj)sha1.o $(obj)ubsha1.o
117     $(HOSTCC) $(HOSTCFLAGS) $(HOSTLDFLAGS) -o $@ $^
118 
119+$(obj)kwboot$(SFX): $(obj)kwboot.o
120+ $(HOSTCC) $(HOSTCFLAGS) $(HOSTLDFLAGS) -o $@ $^
121+ $(HOSTSTRIP) $@
122+
123 # Some of the tool objects need to be accessed from outside the tools directory
124 $(obj)%.o: $(SRCTREE)/common/%.c
125     $(HOSTCC) -g $(HOSTCFLAGS_NOPED) -c -o $@ $<
126diff --git a/tools/kwboot.c b/tools/kwboot.c
127new file mode 100644
128index 0000000..e773f01
129--- /dev/null
130+++ b/tools/kwboot.c
131@@ -0,0 +1,742 @@
132+/*
133+ * Boot a Marvell Kirkwood SoC, with Xmodem over UART0.
134+ *
135+ * (c) 2012 Daniel Stodden <daniel.stodden@gmail.com>
136+ *
137+ * References: marvell.com, "88F6180, 88F6190, 88F6192, and 88F6281
138+ * Integrated Controller: Functional Specifications" December 2,
139+ * 2008. Chapter 24.2 "BootROM Firmware".
140+ */
141+
142+#include <stdlib.h>
143+#include <stdio.h>
144+#include <string.h>
145+#include <stdarg.h>
146+#include <libgen.h>
147+#include <fcntl.h>
148+#include <errno.h>
149+#include <unistd.h>
150+#include <stdint.h>
151+#include <termios.h>
152+#include <sys/mman.h>
153+#include <sys/stat.h>
154+
155+#include "kwbimage.h"
156+
157+#ifdef __GNUC__
158+#define PACKED __attribute((packed))
159+#else
160+#define PACKED
161+#endif
162+
163+/*
164+ * Marvell BootROM UART Sensing
165+ */
166+
167+static unsigned char kwboot_msg_boot[] = {
168+ 0xBB, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77
169+};
170+
171+#define KWBOOT_MSG_REQ_DELAY 10 /* ms */
172+#define KWBOOT_MSG_RSP_TIMEO 50 /* ms */
173+
174+/*
175+ * Xmodem Transfers
176+ */
177+
178+#define SOH 1 /* sender start of block header */
179+#define EOT 4 /* sender end of block transfer */
180+#define ACK 6 /* target block ack */
181+#define NAK 21 /* target block negative ack */
182+#define CAN 24 /* target/sender transfer cancellation */
183+
184+struct kwboot_block {
185+ uint8_t soh;
186+ uint8_t pnum;
187+ uint8_t _pnum;
188+ uint8_t data[128];
189+ uint8_t csum;
190+} PACKED;
191+
192+#define KWBOOT_BLK_RSP_TIMEO 1000 /* ms */
193+
194+static int kwboot_verbose;
195+
196+static void
197+kwboot_printv(const char *fmt, ...)
198+{
199+ va_list ap;
200+
201+ if (kwboot_verbose) {
202+ va_start(ap, fmt);
203+ vprintf(fmt, ap);
204+ va_end(ap);
205+ fflush(stdout);
206+ }
207+}
208+
209+static void
210+__spinner(void)
211+{
212+ const char seq[] = { '-', '\\', '|', '/' };
213+ const int div = 8;
214+ static int state, bs;
215+
216+ if (state % div == 0) {
217+ fputc(bs, stdout);
218+ fputc(seq[state / div % sizeof(seq)], stdout);
219+ fflush(stdout);
220+ }
221+
222+ bs = '\b';
223+ state++;
224+}
225+
226+static void
227+kwboot_spinner(void)
228+{
229+ if (kwboot_verbose)
230+ __spinner();
231+}
232+
233+static void
234+__progress(int pct, char c)
235+{
236+ const int width = 70;
237+ static const char *nl = "";
238+ static int pos;
239+
240+ if (pos % width == 0)
241+ printf("%s%3d %% [", nl, pct);
242+
243+ fputc(c, stdout);
244+
245+ nl = "]\n";
246+ pos++;
247+
248+ if (pct == 100) {
249+ while (pos++ < width)
250+ fputc(' ', stdout);
251+ fputs(nl, stdout);
252+ }
253+
254+ fflush(stdout);
255+
256+}
257+
258+static void
259+kwboot_progress(int _pct, char c)
260+{
261+ static int pct;
262+
263+ if (_pct != -1)
264+ pct = _pct;
265+
266+ if (kwboot_verbose)
267+ __progress(pct, c);
268+}
269+
270+static int
271+kwboot_tty_recv(int fd, void *buf, size_t len, int timeo)
272+{
273+ int rc, nfds;
274+ fd_set rfds;
275+ struct timeval tv;
276+ ssize_t n;
277+
278+ rc = -1;
279+
280+ FD_ZERO(&rfds);
281+ FD_SET(fd, &rfds);
282+
283+ tv.tv_sec = 0;
284+ tv.tv_usec = timeo * 1000;
285+ if (tv.tv_usec > 1000000) {
286+ tv.tv_sec += tv.tv_usec / 1000000;
287+ tv.tv_usec %= 1000000;
288+ }
289+
290+ do {
291+ nfds = select(fd + 1, &rfds, NULL, NULL, &tv);
292+ if (nfds < 0)
293+ goto out;
294+ if (!nfds) {
295+ errno = ETIMEDOUT;
296+ goto out;
297+ }
298+
299+ n = read(fd, buf, len);
300+ if (n < 0)
301+ goto out;
302+
303+ buf = (char *)buf + n;
304+ len -= n;
305+ } while (len > 0);
306+
307+ rc = 0;
308+out:
309+ return rc;
310+}
311+
312+static int
313+kwboot_tty_send(int fd, const void *buf, size_t len)
314+{
315+ int rc;
316+ ssize_t n;
317+
318+ rc = -1;
319+
320+ do {
321+ n = write(fd, buf, len);
322+ if (n < 0)
323+ goto out;
324+
325+ buf = (char *)buf + n;
326+ len -= n;
327+ } while (len > 0);
328+
329+ rc = tcdrain(fd);
330+out:
331+ return rc;
332+}
333+
334+static int
335+kwboot_tty_send_char(int fd, unsigned char c)
336+{
337+ return kwboot_tty_send(fd, &c, 1);
338+}
339+
340+static speed_t
341+kwboot_tty_speed(int baudrate)
342+{
343+ switch (baudrate) {
344+ case 115200:
345+ return B115200;
346+ case 57600:
347+ return B57600;
348+ case 38400:
349+ return B38400;
350+ case 19200:
351+ return B19200;
352+ case 9600:
353+ return B9600;
354+ }
355+
356+ return -1;
357+}
358+
359+static int
360+kwboot_open_tty(const char *path, speed_t speed)
361+{
362+ int rc, fd;
363+ struct termios tio;
364+
365+ rc = -1;
366+
367+ fd = open(path, O_RDWR|O_NOCTTY|O_NDELAY);
368+ if (fd < 0)
369+ goto out;
370+
371+ memset(&tio, 0, sizeof(tio));
372+
373+ tio.c_iflag = 0;
374+ tio.c_cflag = CREAD|CLOCAL|CS8;
375+
376+ tio.c_cc[VMIN] = 1;
377+ tio.c_cc[VTIME] = 10;
378+
379+ cfsetospeed(&tio, speed);
380+ cfsetispeed(&tio, speed);
381+
382+ rc = tcsetattr(fd, TCSANOW, &tio);
383+ if (rc)
384+ goto out;
385+
386+ rc = fd;
387+out:
388+ if (rc < 0) {
389+ if (fd >= 0)
390+ close(fd);
391+ }
392+
393+ return rc;
394+}
395+
396+static int
397+kwboot_bootmsg(int tty, void *msg)
398+{
399+ int rc;
400+ char c;
401+
402+ kwboot_printv("Sending boot message. Please reboot the target...");
403+
404+ do {
405+ rc = tcflush(tty, TCIOFLUSH);
406+ if (rc)
407+ break;
408+
409+ rc = kwboot_tty_send(tty, msg, 8);
410+ if (rc) {
411+ usleep(KWBOOT_MSG_REQ_DELAY * 1000);
412+ continue;
413+ }
414+
415+ rc = kwboot_tty_recv(tty, &c, 1, KWBOOT_MSG_RSP_TIMEO);
416+
417+ kwboot_spinner();
418+
419+ } while (rc || c != NAK);
420+
421+ kwboot_printv("\n");
422+
423+ return rc;
424+}
425+
426+static int
427+kwboot_xm_makeblock(struct kwboot_block *block, const void *data,
428+ size_t size, int pnum)
429+{
430+ const size_t blksz = sizeof(block->data);
431+ size_t n;
432+ int i;
433+
434+ block->pnum = pnum;
435+ block->_pnum = ~block->pnum;
436+
437+ n = size < blksz ? size : blksz;
438+ memcpy(&block->data[0], data, n);
439+ memset(&block->data[n], 0, blksz - n);
440+
441+ block->csum = 0;
442+ for (i = 0; i < n; i++)
443+ block->csum += block->data[i];
444+
445+ return n;
446+}
447+
448+static int
449+kwboot_xm_sendblock(int fd, struct kwboot_block *block)
450+{
451+ int rc, retries;
452+ char c;
453+
454+ retries = 16;
455+ do {
456+ rc = kwboot_tty_send(fd, block, sizeof(*block));
457+ if (rc)
458+ break;
459+
460+ rc = kwboot_tty_recv(fd, &c, 1, KWBOOT_BLK_RSP_TIMEO);
461+ if (rc)
462+ break;
463+
464+ if (c != ACK)
465+ kwboot_progress(-1, '+');
466+
467+ } while (c == NAK && retries-- > 0);
468+
469+ rc = -1;
470+
471+ switch (c) {
472+ case ACK:
473+ rc = 0;
474+ break;
475+ case NAK:
476+ errno = EBADMSG;
477+ break;
478+ case CAN:
479+ errno = ECANCELED;
480+ break;
481+ default:
482+ errno = EPROTO;
483+ break;
484+ }
485+
486+ return rc;
487+}
488+
489+static int
490+kwboot_xmodem(int tty, const void *_data, size_t size)
491+{
492+ const uint8_t *data = _data;
493+ int rc, pnum, N, err;
494+
495+ pnum = 1;
496+ N = 0;
497+
498+ kwboot_printv("Sending boot image...\n");
499+
500+ do {
501+ struct kwboot_block block;
502+ int n;
503+
504+ n = kwboot_xm_makeblock(&block,
505+ data + N, size - N,
506+ pnum++);
507+ if (n < 0)
508+ goto can;
509+
510+ if (!n)
511+ break;
512+
513+ rc = kwboot_xm_sendblock(tty, &block);
514+ if (rc)
515+ goto out;
516+
517+ N += n;
518+ kwboot_progress(N * 100 / size, '.');
519+ } while (1);
520+
521+ rc = kwboot_tty_send_char(tty, EOT);
522+
523+out:
524+ return rc;
525+
526+can:
527+ err = errno;
528+ kwboot_tty_send_char(tty, CAN);
529+ errno = err;
530+ goto out;
531+}
532+
533+static int
534+kwboot_term_pipe(int in, int out, char *quit, int *s)
535+{
536+ ssize_t nin, nout;
537+ char _buf[128], *buf = _buf;
538+
539+ nin = read(in, buf, sizeof(buf));
540+ if (nin < 0)
541+ return -1;
542+
543+ if (quit) {
544+ int i;
545+
546+ for (i = 0; i < nin; i++) {
547+ if (*buf == quit[*s]) {
548+ (*s)++;
549+ if (!quit[*s])
550+ return 0;
551+ buf++;
552+ nin--;
553+ } else
554+ while (*s > 0) {
555+ nout = write(out, quit, *s);
556+ if (nout <= 0)
557+ return -1;
558+ (*s) -= nout;
559+ }
560+ }
561+ }
562+
563+ while (nin > 0) {
564+ nout = write(out, buf, nin);
565+ if (nout <= 0)
566+ return -1;
567+ nin -= nout;
568+ }
569+
570+ return 0;
571+}
572+
573+static int
574+kwboot_terminal(int tty)
575+{
576+ int rc, in, s;
577+ char *quit = "\34c";
578+ struct termios otio, tio;
579+
580+ rc = -1;
581+
582+ in = STDIN_FILENO;
583+ if (isatty(in)) {
584+ rc = tcgetattr(in, &otio);
585+ if (!rc) {
586+ tio = otio;
587+ cfmakeraw(&tio);
588+ rc = tcsetattr(in, TCSANOW, &tio);
589+ }
590+ if (rc) {
591+ perror("tcsetattr");
592+ goto out;
593+ }
594+
595+ kwboot_printv("[Type Ctrl-%c + %c to quit]\r\n",
596+ quit[0]|0100, quit[1]);
597+ } else
598+ in = -1;
599+
600+ rc = 0;
601+ s = 0;
602+
603+ do {
604+ fd_set rfds;
605+ int nfds = 0;
606+
607+ FD_SET(tty, &rfds);
608+ nfds = nfds < tty ? tty : nfds;
609+
610+ if (in >= 0) {
611+ FD_SET(in, &rfds);
612+ nfds = nfds < in ? in : nfds;
613+ }
614+
615+ nfds = select(nfds + 1, &rfds, NULL, NULL, NULL);
616+ if (nfds < 0)
617+ break;
618+
619+ if (FD_ISSET(tty, &rfds)) {
620+ rc = kwboot_term_pipe(tty, STDOUT_FILENO, NULL, NULL);
621+ if (rc)
622+ break;
623+ }
624+
625+ if (FD_ISSET(in, &rfds)) {
626+ rc = kwboot_term_pipe(in, tty, quit, &s);
627+ if (rc)
628+ break;
629+ }
630+ } while (quit[s] != 0);
631+
632+ tcsetattr(in, TCSANOW, &otio);
633+out:
634+ return rc;
635+}
636+
637+static void *
638+kwboot_mmap_image(const char *path, size_t *size, int prot)
639+{
640+ int rc, fd, flags;
641+ struct stat st;
642+ void *img;
643+
644+ rc = -1;
645+ fd = -1;
646+ img = NULL;
647+
648+ fd = open(path, O_RDONLY);
649+ if (fd < 0)
650+ goto out;
651+
652+ rc = fstat(fd, &st);
653+ if (rc)
654+ goto out;
655+
656+ flags = (prot & PROT_WRITE) ? MAP_PRIVATE : MAP_SHARED;
657+
658+ img = mmap(NULL, st.st_size, prot, flags, fd, 0);
659+ if (img == MAP_FAILED) {
660+ img = NULL;
661+ goto out;
662+ }
663+
664+ rc = 0;
665+ *size = st.st_size;
666+out:
667+ if (rc && img) {
668+ munmap(img, st.st_size);
669+ img = NULL;
670+ }
671+ if (fd >= 0)
672+ close(fd);
673+
674+ return img;
675+}
676+
677+static uint8_t
678+kwboot_img_csum8(void *_data, size_t size)
679+{
680+ uint8_t *data = _data, csum;
681+
682+ for (csum = 0; size-- > 0; data++)
683+ csum += *data;
684+
685+ return csum;
686+}
687+
688+static int
689+kwboot_img_patch_hdr(void *img, size_t size)
690+{
691+ int rc;
692+ bhr_t *hdr;
693+ uint8_t csum;
694+ const size_t hdrsz = sizeof(*hdr);
695+
696+ rc = -1;
697+ hdr = img;
698+
699+ if (size < hdrsz) {
700+ errno = EINVAL;
701+ goto out;
702+ }
703+
704+ csum = kwboot_img_csum8(hdr, hdrsz) - hdr->checkSum;
705+ if (csum != hdr->checkSum) {
706+ errno = EINVAL;
707+ goto out;
708+ }
709+
710+ if (hdr->blockid == IBR_HDR_UART_ID) {
711+ rc = 0;
712+ goto out;
713+ }
714+
715+ hdr->blockid = IBR_HDR_UART_ID;
716+
717+ hdr->nandeccmode = IBR_HDR_ECC_DISABLED;
718+ hdr->nandpagesize = 0;
719+
720+ hdr->srcaddr = hdr->ext
721+ ? sizeof(struct kwb_header)
722+ : sizeof(*hdr);
723+
724+ hdr->checkSum = kwboot_img_csum8(hdr, hdrsz) - csum;
725+
726+ rc = 0;
727+out:
728+ return rc;
729+}
730+
731+static void
732+kwboot_usage(FILE *stream, char *progname)
733+{
734+ fprintf(stream,
735+ "Usage: %s -b <image> [ -p ] [ -t ] "
736+ "[-B <baud> ] <TTY>\n", progname);
737+ fprintf(stream, "\n");
738+ fprintf(stream, " -b <image>: boot <image>\n");
739+ fprintf(stream, " -p: patch <image> to type 0x69 (uart boot)\n");
740+ fprintf(stream, "\n");
741+ fprintf(stream, " -t: mini terminal\n");
742+ fprintf(stream, "\n");
743+ fprintf(stream, " -B <baud>: set baud rate\n");
744+ fprintf(stream, "\n");
745+}
746+
747+int
748+main(int argc, char **argv)
749+{
750+ const char *ttypath, *imgpath;
751+ int rv, rc, tty, term, prot, patch;
752+ void *bootmsg;
753+ void *img;
754+ size_t size;
755+ speed_t speed;
756+
757+ rv = 1;
758+ tty = -1;
759+ bootmsg = NULL;
760+ imgpath = NULL;
761+ img = NULL;
762+ term = 0;
763+ patch = 0;
764+ size = 0;
765+ speed = B115200;
766+
767+ kwboot_verbose = isatty(STDOUT_FILENO);
768+
769+ do {
770+ int c = getopt(argc, argv, "hb:ptB:");
771+ if (c < 0)
772+ break;
773+
774+ switch (c) {
775+ case 'b':
776+ bootmsg = kwboot_msg_boot;
777+ imgpath = optarg;
778+ break;
779+
780+ case 'p':
781+ patch = 1;
782+ break;
783+
784+ case 't':
785+ term = 1;
786+ break;
787+
788+ case 'B':
789+ speed = kwboot_tty_speed(atoi(optarg));
790+ if (speed == -1)
791+ goto usage;
792+ break;
793+
794+ case 'h':
795+ rv = 0;
796+ default:
797+ goto usage;
798+ }
799+ } while (1);
800+
801+ if (!bootmsg && !term)
802+ goto usage;
803+
804+ if (patch && !imgpath)
805+ goto usage;
806+
807+ if (argc - optind < 1)
808+ goto usage;
809+
810+ ttypath = argv[optind++];
811+
812+ tty = kwboot_open_tty(ttypath, speed);
813+ if (tty < 0) {
814+ perror(ttypath);
815+ goto out;
816+ }
817+
818+ if (imgpath) {
819+ prot = PROT_READ | (patch ? PROT_WRITE : 0);
820+
821+ img = kwboot_mmap_image(imgpath, &size, prot);
822+ if (!img) {
823+ perror(imgpath);
824+ goto out;
825+ }
826+ }
827+
828+ if (patch) {
829+ rc = kwboot_img_patch_hdr(img, size);
830+ if (rc) {
831+ fprintf(stderr, "%s: Invalid image.\n", imgpath);
832+ goto out;
833+ }
834+ }
835+
836+ if (bootmsg) {
837+ rc = kwboot_bootmsg(tty, bootmsg);
838+ if (rc) {
839+ perror("bootmsg");
840+ goto out;
841+ }
842+ }
843+
844+ if (img) {
845+ rc = kwboot_xmodem(tty, img, size);
846+ if (rc) {
847+ perror("xmodem");
848+ goto out;
849+ }
850+ }
851+
852+ if (term) {
853+ rc = kwboot_terminal(tty);
854+ if (rc && !(errno == EINTR)) {
855+ perror("terminal");
856+ goto out;
857+ }
858+ }
859+
860+ rv = 0;
861+out:
862+ if (tty >= 0)
863+ close(tty);
864+
865+ if (img)
866+ munmap(img, size);
867+
868+ return rv;
869+
870+usage:
871+ kwboot_usage(rv ? stderr : stdout, basename(argv[0]));
872+ goto out;
873+}
874

Archive Download this file



interactive