xref: /openbmc/u-boot/tools/kwboot.c (revision d131ad68)
1*d131ad68SLuka Perkov /*
2*d131ad68SLuka Perkov  * Boot a Marvell Kirkwood SoC, with Xmodem over UART0.
3*d131ad68SLuka Perkov  *
4*d131ad68SLuka Perkov  * (c) 2012 Daniel Stodden <daniel.stodden@gmail.com>
5*d131ad68SLuka Perkov  *
6*d131ad68SLuka Perkov  * References: marvell.com, "88F6180, 88F6190, 88F6192, and 88F6281
7*d131ad68SLuka Perkov  *   Integrated Controller: Functional Specifications" December 2,
8*d131ad68SLuka Perkov  *   2008. Chapter 24.2 "BootROM Firmware".
9*d131ad68SLuka Perkov  */
10*d131ad68SLuka Perkov 
11*d131ad68SLuka Perkov #include <stdlib.h>
12*d131ad68SLuka Perkov #include <stdio.h>
13*d131ad68SLuka Perkov #include <string.h>
14*d131ad68SLuka Perkov #include <stdarg.h>
15*d131ad68SLuka Perkov #include <libgen.h>
16*d131ad68SLuka Perkov #include <fcntl.h>
17*d131ad68SLuka Perkov #include <errno.h>
18*d131ad68SLuka Perkov #include <unistd.h>
19*d131ad68SLuka Perkov #include <stdint.h>
20*d131ad68SLuka Perkov #include <termios.h>
21*d131ad68SLuka Perkov #include <sys/mman.h>
22*d131ad68SLuka Perkov #include <sys/stat.h>
23*d131ad68SLuka Perkov 
24*d131ad68SLuka Perkov #include "kwbimage.h"
25*d131ad68SLuka Perkov 
26*d131ad68SLuka Perkov #ifdef __GNUC__
27*d131ad68SLuka Perkov #define PACKED __attribute((packed))
28*d131ad68SLuka Perkov #else
29*d131ad68SLuka Perkov #define PACKED
30*d131ad68SLuka Perkov #endif
31*d131ad68SLuka Perkov 
32*d131ad68SLuka Perkov /*
33*d131ad68SLuka Perkov  * Marvell BootROM UART Sensing
34*d131ad68SLuka Perkov  */
35*d131ad68SLuka Perkov 
36*d131ad68SLuka Perkov static unsigned char kwboot_msg_boot[] = {
37*d131ad68SLuka Perkov 	0xBB, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77
38*d131ad68SLuka Perkov };
39*d131ad68SLuka Perkov 
40*d131ad68SLuka Perkov #define KWBOOT_MSG_REQ_DELAY	10 /* ms */
41*d131ad68SLuka Perkov #define KWBOOT_MSG_RSP_TIMEO	50 /* ms */
42*d131ad68SLuka Perkov 
43*d131ad68SLuka Perkov /*
44*d131ad68SLuka Perkov  * Xmodem Transfers
45*d131ad68SLuka Perkov  */
46*d131ad68SLuka Perkov 
47*d131ad68SLuka Perkov #define SOH	1	/* sender start of block header */
48*d131ad68SLuka Perkov #define EOT	4	/* sender end of block transfer */
49*d131ad68SLuka Perkov #define ACK	6	/* target block ack */
50*d131ad68SLuka Perkov #define NAK	21	/* target block negative ack */
51*d131ad68SLuka Perkov #define CAN	24	/* target/sender transfer cancellation */
52*d131ad68SLuka Perkov 
53*d131ad68SLuka Perkov struct kwboot_block {
54*d131ad68SLuka Perkov 	uint8_t soh;
55*d131ad68SLuka Perkov 	uint8_t pnum;
56*d131ad68SLuka Perkov 	uint8_t _pnum;
57*d131ad68SLuka Perkov 	uint8_t data[128];
58*d131ad68SLuka Perkov 	uint8_t csum;
59*d131ad68SLuka Perkov } PACKED;
60*d131ad68SLuka Perkov 
61*d131ad68SLuka Perkov #define KWBOOT_BLK_RSP_TIMEO 1000 /* ms */
62*d131ad68SLuka Perkov 
63*d131ad68SLuka Perkov static int kwboot_verbose;
64*d131ad68SLuka Perkov 
65*d131ad68SLuka Perkov static void
66*d131ad68SLuka Perkov kwboot_printv(const char *fmt, ...)
67*d131ad68SLuka Perkov {
68*d131ad68SLuka Perkov 	va_list ap;
69*d131ad68SLuka Perkov 
70*d131ad68SLuka Perkov 	if (kwboot_verbose) {
71*d131ad68SLuka Perkov 		va_start(ap, fmt);
72*d131ad68SLuka Perkov 		vprintf(fmt, ap);
73*d131ad68SLuka Perkov 		va_end(ap);
74*d131ad68SLuka Perkov 		fflush(stdout);
75*d131ad68SLuka Perkov 	}
76*d131ad68SLuka Perkov }
77*d131ad68SLuka Perkov 
78*d131ad68SLuka Perkov static void
79*d131ad68SLuka Perkov __spinner(void)
80*d131ad68SLuka Perkov {
81*d131ad68SLuka Perkov 	const char seq[] = { '-', '\\', '|', '/' };
82*d131ad68SLuka Perkov 	const int div = 8;
83*d131ad68SLuka Perkov 	static int state, bs;
84*d131ad68SLuka Perkov 
85*d131ad68SLuka Perkov 	if (state % div == 0) {
86*d131ad68SLuka Perkov 		fputc(bs, stdout);
87*d131ad68SLuka Perkov 		fputc(seq[state / div % sizeof(seq)], stdout);
88*d131ad68SLuka Perkov 		fflush(stdout);
89*d131ad68SLuka Perkov 	}
90*d131ad68SLuka Perkov 
91*d131ad68SLuka Perkov 	bs = '\b';
92*d131ad68SLuka Perkov 	state++;
93*d131ad68SLuka Perkov }
94*d131ad68SLuka Perkov 
95*d131ad68SLuka Perkov static void
96*d131ad68SLuka Perkov kwboot_spinner(void)
97*d131ad68SLuka Perkov {
98*d131ad68SLuka Perkov 	if (kwboot_verbose)
99*d131ad68SLuka Perkov 		__spinner();
100*d131ad68SLuka Perkov }
101*d131ad68SLuka Perkov 
102*d131ad68SLuka Perkov static void
103*d131ad68SLuka Perkov __progress(int pct, char c)
104*d131ad68SLuka Perkov {
105*d131ad68SLuka Perkov 	const int width = 70;
106*d131ad68SLuka Perkov 	static const char *nl = "";
107*d131ad68SLuka Perkov 	static int pos;
108*d131ad68SLuka Perkov 
109*d131ad68SLuka Perkov 	if (pos % width == 0)
110*d131ad68SLuka Perkov 		printf("%s%3d %% [", nl, pct);
111*d131ad68SLuka Perkov 
112*d131ad68SLuka Perkov 	fputc(c, stdout);
113*d131ad68SLuka Perkov 
114*d131ad68SLuka Perkov 	nl = "]\n";
115*d131ad68SLuka Perkov 	pos++;
116*d131ad68SLuka Perkov 
117*d131ad68SLuka Perkov 	if (pct == 100) {
118*d131ad68SLuka Perkov 		while (pos++ < width)
119*d131ad68SLuka Perkov 			fputc(' ', stdout);
120*d131ad68SLuka Perkov 		fputs(nl, stdout);
121*d131ad68SLuka Perkov 	}
122*d131ad68SLuka Perkov 
123*d131ad68SLuka Perkov 	fflush(stdout);
124*d131ad68SLuka Perkov 
125*d131ad68SLuka Perkov }
126*d131ad68SLuka Perkov 
127*d131ad68SLuka Perkov static void
128*d131ad68SLuka Perkov kwboot_progress(int _pct, char c)
129*d131ad68SLuka Perkov {
130*d131ad68SLuka Perkov 	static int pct;
131*d131ad68SLuka Perkov 
132*d131ad68SLuka Perkov 	if (_pct != -1)
133*d131ad68SLuka Perkov 		pct = _pct;
134*d131ad68SLuka Perkov 
135*d131ad68SLuka Perkov 	if (kwboot_verbose)
136*d131ad68SLuka Perkov 		__progress(pct, c);
137*d131ad68SLuka Perkov }
138*d131ad68SLuka Perkov 
139*d131ad68SLuka Perkov static int
140*d131ad68SLuka Perkov kwboot_tty_recv(int fd, void *buf, size_t len, int timeo)
141*d131ad68SLuka Perkov {
142*d131ad68SLuka Perkov 	int rc, nfds;
143*d131ad68SLuka Perkov 	fd_set rfds;
144*d131ad68SLuka Perkov 	struct timeval tv;
145*d131ad68SLuka Perkov 	ssize_t n;
146*d131ad68SLuka Perkov 
147*d131ad68SLuka Perkov 	rc = -1;
148*d131ad68SLuka Perkov 
149*d131ad68SLuka Perkov 	FD_ZERO(&rfds);
150*d131ad68SLuka Perkov 	FD_SET(fd, &rfds);
151*d131ad68SLuka Perkov 
152*d131ad68SLuka Perkov 	tv.tv_sec = 0;
153*d131ad68SLuka Perkov 	tv.tv_usec = timeo * 1000;
154*d131ad68SLuka Perkov 	if (tv.tv_usec > 1000000) {
155*d131ad68SLuka Perkov 		tv.tv_sec += tv.tv_usec / 1000000;
156*d131ad68SLuka Perkov 		tv.tv_usec %= 1000000;
157*d131ad68SLuka Perkov 	}
158*d131ad68SLuka Perkov 
159*d131ad68SLuka Perkov 	do {
160*d131ad68SLuka Perkov 		nfds = select(fd + 1, &rfds, NULL, NULL, &tv);
161*d131ad68SLuka Perkov 		if (nfds < 0)
162*d131ad68SLuka Perkov 			goto out;
163*d131ad68SLuka Perkov 		if (!nfds) {
164*d131ad68SLuka Perkov 			errno = ETIMEDOUT;
165*d131ad68SLuka Perkov 			goto out;
166*d131ad68SLuka Perkov 		}
167*d131ad68SLuka Perkov 
168*d131ad68SLuka Perkov 		n = read(fd, buf, len);
169*d131ad68SLuka Perkov 		if (n < 0)
170*d131ad68SLuka Perkov 			goto out;
171*d131ad68SLuka Perkov 
172*d131ad68SLuka Perkov 		buf = (char *)buf + n;
173*d131ad68SLuka Perkov 		len -= n;
174*d131ad68SLuka Perkov 	} while (len > 0);
175*d131ad68SLuka Perkov 
176*d131ad68SLuka Perkov 	rc = 0;
177*d131ad68SLuka Perkov out:
178*d131ad68SLuka Perkov 	return rc;
179*d131ad68SLuka Perkov }
180*d131ad68SLuka Perkov 
181*d131ad68SLuka Perkov static int
182*d131ad68SLuka Perkov kwboot_tty_send(int fd, const void *buf, size_t len)
183*d131ad68SLuka Perkov {
184*d131ad68SLuka Perkov 	int rc;
185*d131ad68SLuka Perkov 	ssize_t n;
186*d131ad68SLuka Perkov 
187*d131ad68SLuka Perkov 	rc = -1;
188*d131ad68SLuka Perkov 
189*d131ad68SLuka Perkov 	do {
190*d131ad68SLuka Perkov 		n = write(fd, buf, len);
191*d131ad68SLuka Perkov 		if (n < 0)
192*d131ad68SLuka Perkov 			goto out;
193*d131ad68SLuka Perkov 
194*d131ad68SLuka Perkov 		buf = (char *)buf + n;
195*d131ad68SLuka Perkov 		len -= n;
196*d131ad68SLuka Perkov 	} while (len > 0);
197*d131ad68SLuka Perkov 
198*d131ad68SLuka Perkov 	rc = tcdrain(fd);
199*d131ad68SLuka Perkov out:
200*d131ad68SLuka Perkov 	return rc;
201*d131ad68SLuka Perkov }
202*d131ad68SLuka Perkov 
203*d131ad68SLuka Perkov static int
204*d131ad68SLuka Perkov kwboot_tty_send_char(int fd, unsigned char c)
205*d131ad68SLuka Perkov {
206*d131ad68SLuka Perkov 	return kwboot_tty_send(fd, &c, 1);
207*d131ad68SLuka Perkov }
208*d131ad68SLuka Perkov 
209*d131ad68SLuka Perkov static speed_t
210*d131ad68SLuka Perkov kwboot_tty_speed(int baudrate)
211*d131ad68SLuka Perkov {
212*d131ad68SLuka Perkov 	switch (baudrate) {
213*d131ad68SLuka Perkov 	case 115200:
214*d131ad68SLuka Perkov 		return B115200;
215*d131ad68SLuka Perkov 	case 57600:
216*d131ad68SLuka Perkov 		return B57600;
217*d131ad68SLuka Perkov 	case 38400:
218*d131ad68SLuka Perkov 		return B38400;
219*d131ad68SLuka Perkov 	case 19200:
220*d131ad68SLuka Perkov 		return B19200;
221*d131ad68SLuka Perkov 	case 9600:
222*d131ad68SLuka Perkov 		return B9600;
223*d131ad68SLuka Perkov 	}
224*d131ad68SLuka Perkov 
225*d131ad68SLuka Perkov 	return -1;
226*d131ad68SLuka Perkov }
227*d131ad68SLuka Perkov 
228*d131ad68SLuka Perkov static int
229*d131ad68SLuka Perkov kwboot_open_tty(const char *path, speed_t speed)
230*d131ad68SLuka Perkov {
231*d131ad68SLuka Perkov 	int rc, fd;
232*d131ad68SLuka Perkov 	struct termios tio;
233*d131ad68SLuka Perkov 
234*d131ad68SLuka Perkov 	rc = -1;
235*d131ad68SLuka Perkov 
236*d131ad68SLuka Perkov 	fd = open(path, O_RDWR|O_NOCTTY|O_NDELAY);
237*d131ad68SLuka Perkov 	if (fd < 0)
238*d131ad68SLuka Perkov 		goto out;
239*d131ad68SLuka Perkov 
240*d131ad68SLuka Perkov 	memset(&tio, 0, sizeof(tio));
241*d131ad68SLuka Perkov 
242*d131ad68SLuka Perkov 	tio.c_iflag = 0;
243*d131ad68SLuka Perkov 	tio.c_cflag = CREAD|CLOCAL|CS8;
244*d131ad68SLuka Perkov 
245*d131ad68SLuka Perkov 	tio.c_cc[VMIN] = 1;
246*d131ad68SLuka Perkov 	tio.c_cc[VTIME] = 10;
247*d131ad68SLuka Perkov 
248*d131ad68SLuka Perkov 	cfsetospeed(&tio, speed);
249*d131ad68SLuka Perkov 	cfsetispeed(&tio, speed);
250*d131ad68SLuka Perkov 
251*d131ad68SLuka Perkov 	rc = tcsetattr(fd, TCSANOW, &tio);
252*d131ad68SLuka Perkov 	if (rc)
253*d131ad68SLuka Perkov 		goto out;
254*d131ad68SLuka Perkov 
255*d131ad68SLuka Perkov 	rc = fd;
256*d131ad68SLuka Perkov out:
257*d131ad68SLuka Perkov 	if (rc < 0) {
258*d131ad68SLuka Perkov 		if (fd >= 0)
259*d131ad68SLuka Perkov 			close(fd);
260*d131ad68SLuka Perkov 	}
261*d131ad68SLuka Perkov 
262*d131ad68SLuka Perkov 	return rc;
263*d131ad68SLuka Perkov }
264*d131ad68SLuka Perkov 
265*d131ad68SLuka Perkov static int
266*d131ad68SLuka Perkov kwboot_bootmsg(int tty, void *msg)
267*d131ad68SLuka Perkov {
268*d131ad68SLuka Perkov 	int rc;
269*d131ad68SLuka Perkov 	char c;
270*d131ad68SLuka Perkov 
271*d131ad68SLuka Perkov 	kwboot_printv("Sending boot message. Please reboot the target...");
272*d131ad68SLuka Perkov 
273*d131ad68SLuka Perkov 	do {
274*d131ad68SLuka Perkov 		rc = tcflush(tty, TCIOFLUSH);
275*d131ad68SLuka Perkov 		if (rc)
276*d131ad68SLuka Perkov 			break;
277*d131ad68SLuka Perkov 
278*d131ad68SLuka Perkov 		rc = kwboot_tty_send(tty, msg, 8);
279*d131ad68SLuka Perkov 		if (rc) {
280*d131ad68SLuka Perkov 			usleep(KWBOOT_MSG_REQ_DELAY * 1000);
281*d131ad68SLuka Perkov 			continue;
282*d131ad68SLuka Perkov 		}
283*d131ad68SLuka Perkov 
284*d131ad68SLuka Perkov 		rc = kwboot_tty_recv(tty, &c, 1, KWBOOT_MSG_RSP_TIMEO);
285*d131ad68SLuka Perkov 
286*d131ad68SLuka Perkov 		kwboot_spinner();
287*d131ad68SLuka Perkov 
288*d131ad68SLuka Perkov 	} while (rc || c != NAK);
289*d131ad68SLuka Perkov 
290*d131ad68SLuka Perkov 	kwboot_printv("\n");
291*d131ad68SLuka Perkov 
292*d131ad68SLuka Perkov 	return rc;
293*d131ad68SLuka Perkov }
294*d131ad68SLuka Perkov 
295*d131ad68SLuka Perkov static int
296*d131ad68SLuka Perkov kwboot_xm_makeblock(struct kwboot_block *block, const void *data,
297*d131ad68SLuka Perkov 		    size_t size, int pnum)
298*d131ad68SLuka Perkov {
299*d131ad68SLuka Perkov 	const size_t blksz = sizeof(block->data);
300*d131ad68SLuka Perkov 	size_t n;
301*d131ad68SLuka Perkov 	int i;
302*d131ad68SLuka Perkov 
303*d131ad68SLuka Perkov 	block->pnum = pnum;
304*d131ad68SLuka Perkov 	block->_pnum = ~block->pnum;
305*d131ad68SLuka Perkov 
306*d131ad68SLuka Perkov 	n = size < blksz ? size : blksz;
307*d131ad68SLuka Perkov 	memcpy(&block->data[0], data, n);
308*d131ad68SLuka Perkov 	memset(&block->data[n], 0, blksz - n);
309*d131ad68SLuka Perkov 
310*d131ad68SLuka Perkov 	block->csum = 0;
311*d131ad68SLuka Perkov 	for (i = 0; i < n; i++)
312*d131ad68SLuka Perkov 		block->csum += block->data[i];
313*d131ad68SLuka Perkov 
314*d131ad68SLuka Perkov 	return n;
315*d131ad68SLuka Perkov }
316*d131ad68SLuka Perkov 
317*d131ad68SLuka Perkov static int
318*d131ad68SLuka Perkov kwboot_xm_sendblock(int fd, struct kwboot_block *block)
319*d131ad68SLuka Perkov {
320*d131ad68SLuka Perkov 	int rc, retries;
321*d131ad68SLuka Perkov 	char c;
322*d131ad68SLuka Perkov 
323*d131ad68SLuka Perkov 	retries = 16;
324*d131ad68SLuka Perkov 	do {
325*d131ad68SLuka Perkov 		rc = kwboot_tty_send(fd, block, sizeof(*block));
326*d131ad68SLuka Perkov 		if (rc)
327*d131ad68SLuka Perkov 			break;
328*d131ad68SLuka Perkov 
329*d131ad68SLuka Perkov 		rc = kwboot_tty_recv(fd, &c, 1, KWBOOT_BLK_RSP_TIMEO);
330*d131ad68SLuka Perkov 		if (rc)
331*d131ad68SLuka Perkov 			break;
332*d131ad68SLuka Perkov 
333*d131ad68SLuka Perkov 		if (c != ACK)
334*d131ad68SLuka Perkov 			kwboot_progress(-1, '+');
335*d131ad68SLuka Perkov 
336*d131ad68SLuka Perkov 	} while (c == NAK && retries-- > 0);
337*d131ad68SLuka Perkov 
338*d131ad68SLuka Perkov 	rc = -1;
339*d131ad68SLuka Perkov 
340*d131ad68SLuka Perkov 	switch (c) {
341*d131ad68SLuka Perkov 	case ACK:
342*d131ad68SLuka Perkov 		rc = 0;
343*d131ad68SLuka Perkov 		break;
344*d131ad68SLuka Perkov 	case NAK:
345*d131ad68SLuka Perkov 		errno = EBADMSG;
346*d131ad68SLuka Perkov 		break;
347*d131ad68SLuka Perkov 	case CAN:
348*d131ad68SLuka Perkov 		errno = ECANCELED;
349*d131ad68SLuka Perkov 		break;
350*d131ad68SLuka Perkov 	default:
351*d131ad68SLuka Perkov 		errno = EPROTO;
352*d131ad68SLuka Perkov 		break;
353*d131ad68SLuka Perkov 	}
354*d131ad68SLuka Perkov 
355*d131ad68SLuka Perkov 	return rc;
356*d131ad68SLuka Perkov }
357*d131ad68SLuka Perkov 
358*d131ad68SLuka Perkov static int
359*d131ad68SLuka Perkov kwboot_xmodem(int tty, const void *_data, size_t size)
360*d131ad68SLuka Perkov {
361*d131ad68SLuka Perkov 	const uint8_t *data = _data;
362*d131ad68SLuka Perkov 	int rc, pnum, N, err;
363*d131ad68SLuka Perkov 
364*d131ad68SLuka Perkov 	pnum = 1;
365*d131ad68SLuka Perkov 	N = 0;
366*d131ad68SLuka Perkov 
367*d131ad68SLuka Perkov 	kwboot_printv("Sending boot image...\n");
368*d131ad68SLuka Perkov 
369*d131ad68SLuka Perkov 	do {
370*d131ad68SLuka Perkov 		struct kwboot_block block;
371*d131ad68SLuka Perkov 		int n;
372*d131ad68SLuka Perkov 
373*d131ad68SLuka Perkov 		n = kwboot_xm_makeblock(&block,
374*d131ad68SLuka Perkov 					data + N, size - N,
375*d131ad68SLuka Perkov 					pnum++);
376*d131ad68SLuka Perkov 		if (n < 0)
377*d131ad68SLuka Perkov 			goto can;
378*d131ad68SLuka Perkov 
379*d131ad68SLuka Perkov 		if (!n)
380*d131ad68SLuka Perkov 			break;
381*d131ad68SLuka Perkov 
382*d131ad68SLuka Perkov 		rc = kwboot_xm_sendblock(tty, &block);
383*d131ad68SLuka Perkov 		if (rc)
384*d131ad68SLuka Perkov 			goto out;
385*d131ad68SLuka Perkov 
386*d131ad68SLuka Perkov 		N += n;
387*d131ad68SLuka Perkov 		kwboot_progress(N * 100 / size, '.');
388*d131ad68SLuka Perkov 	} while (1);
389*d131ad68SLuka Perkov 
390*d131ad68SLuka Perkov 	rc = kwboot_tty_send_char(tty, EOT);
391*d131ad68SLuka Perkov 
392*d131ad68SLuka Perkov out:
393*d131ad68SLuka Perkov 	return rc;
394*d131ad68SLuka Perkov 
395*d131ad68SLuka Perkov can:
396*d131ad68SLuka Perkov 	err = errno;
397*d131ad68SLuka Perkov 	kwboot_tty_send_char(tty, CAN);
398*d131ad68SLuka Perkov 	errno = err;
399*d131ad68SLuka Perkov 	goto out;
400*d131ad68SLuka Perkov }
401*d131ad68SLuka Perkov 
402*d131ad68SLuka Perkov static int
403*d131ad68SLuka Perkov kwboot_term_pipe(int in, int out, char *quit, int *s)
404*d131ad68SLuka Perkov {
405*d131ad68SLuka Perkov 	ssize_t nin, nout;
406*d131ad68SLuka Perkov 	char _buf[128], *buf = _buf;
407*d131ad68SLuka Perkov 
408*d131ad68SLuka Perkov 	nin = read(in, buf, sizeof(buf));
409*d131ad68SLuka Perkov 	if (nin < 0)
410*d131ad68SLuka Perkov 		return -1;
411*d131ad68SLuka Perkov 
412*d131ad68SLuka Perkov 	if (quit) {
413*d131ad68SLuka Perkov 		int i;
414*d131ad68SLuka Perkov 
415*d131ad68SLuka Perkov 		for (i = 0; i < nin; i++) {
416*d131ad68SLuka Perkov 			if (*buf == quit[*s]) {
417*d131ad68SLuka Perkov 				(*s)++;
418*d131ad68SLuka Perkov 				if (!quit[*s])
419*d131ad68SLuka Perkov 					return 0;
420*d131ad68SLuka Perkov 				buf++;
421*d131ad68SLuka Perkov 				nin--;
422*d131ad68SLuka Perkov 			} else
423*d131ad68SLuka Perkov 				while (*s > 0) {
424*d131ad68SLuka Perkov 					nout = write(out, quit, *s);
425*d131ad68SLuka Perkov 					if (nout <= 0)
426*d131ad68SLuka Perkov 						return -1;
427*d131ad68SLuka Perkov 					(*s) -= nout;
428*d131ad68SLuka Perkov 				}
429*d131ad68SLuka Perkov 		}
430*d131ad68SLuka Perkov 	}
431*d131ad68SLuka Perkov 
432*d131ad68SLuka Perkov 	while (nin > 0) {
433*d131ad68SLuka Perkov 		nout = write(out, buf, nin);
434*d131ad68SLuka Perkov 		if (nout <= 0)
435*d131ad68SLuka Perkov 			return -1;
436*d131ad68SLuka Perkov 		nin -= nout;
437*d131ad68SLuka Perkov 	}
438*d131ad68SLuka Perkov 
439*d131ad68SLuka Perkov 	return 0;
440*d131ad68SLuka Perkov }
441*d131ad68SLuka Perkov 
442*d131ad68SLuka Perkov static int
443*d131ad68SLuka Perkov kwboot_terminal(int tty)
444*d131ad68SLuka Perkov {
445*d131ad68SLuka Perkov 	int rc, in, s;
446*d131ad68SLuka Perkov 	char *quit = "\34c";
447*d131ad68SLuka Perkov 	struct termios otio, tio;
448*d131ad68SLuka Perkov 
449*d131ad68SLuka Perkov 	rc = -1;
450*d131ad68SLuka Perkov 
451*d131ad68SLuka Perkov 	in = STDIN_FILENO;
452*d131ad68SLuka Perkov 	if (isatty(in)) {
453*d131ad68SLuka Perkov 		rc = tcgetattr(in, &otio);
454*d131ad68SLuka Perkov 		if (!rc) {
455*d131ad68SLuka Perkov 			tio = otio;
456*d131ad68SLuka Perkov 			cfmakeraw(&tio);
457*d131ad68SLuka Perkov 			rc = tcsetattr(in, TCSANOW, &tio);
458*d131ad68SLuka Perkov 		}
459*d131ad68SLuka Perkov 		if (rc) {
460*d131ad68SLuka Perkov 			perror("tcsetattr");
461*d131ad68SLuka Perkov 			goto out;
462*d131ad68SLuka Perkov 		}
463*d131ad68SLuka Perkov 
464*d131ad68SLuka Perkov 		kwboot_printv("[Type Ctrl-%c + %c to quit]\r\n",
465*d131ad68SLuka Perkov 			      quit[0]|0100, quit[1]);
466*d131ad68SLuka Perkov 	} else
467*d131ad68SLuka Perkov 		in = -1;
468*d131ad68SLuka Perkov 
469*d131ad68SLuka Perkov 	rc = 0;
470*d131ad68SLuka Perkov 	s = 0;
471*d131ad68SLuka Perkov 
472*d131ad68SLuka Perkov 	do {
473*d131ad68SLuka Perkov 		fd_set rfds;
474*d131ad68SLuka Perkov 		int nfds = 0;
475*d131ad68SLuka Perkov 
476*d131ad68SLuka Perkov 		FD_SET(tty, &rfds);
477*d131ad68SLuka Perkov 		nfds = nfds < tty ? tty : nfds;
478*d131ad68SLuka Perkov 
479*d131ad68SLuka Perkov 		if (in >= 0) {
480*d131ad68SLuka Perkov 			FD_SET(in, &rfds);
481*d131ad68SLuka Perkov 			nfds = nfds < in ? in : nfds;
482*d131ad68SLuka Perkov 		}
483*d131ad68SLuka Perkov 
484*d131ad68SLuka Perkov 		nfds = select(nfds + 1, &rfds, NULL, NULL, NULL);
485*d131ad68SLuka Perkov 		if (nfds < 0)
486*d131ad68SLuka Perkov 			break;
487*d131ad68SLuka Perkov 
488*d131ad68SLuka Perkov 		if (FD_ISSET(tty, &rfds)) {
489*d131ad68SLuka Perkov 			rc = kwboot_term_pipe(tty, STDOUT_FILENO, NULL, NULL);
490*d131ad68SLuka Perkov 			if (rc)
491*d131ad68SLuka Perkov 				break;
492*d131ad68SLuka Perkov 		}
493*d131ad68SLuka Perkov 
494*d131ad68SLuka Perkov 		if (FD_ISSET(in, &rfds)) {
495*d131ad68SLuka Perkov 			rc = kwboot_term_pipe(in, tty, quit, &s);
496*d131ad68SLuka Perkov 			if (rc)
497*d131ad68SLuka Perkov 				break;
498*d131ad68SLuka Perkov 		}
499*d131ad68SLuka Perkov 	} while (quit[s] != 0);
500*d131ad68SLuka Perkov 
501*d131ad68SLuka Perkov 	tcsetattr(in, TCSANOW, &otio);
502*d131ad68SLuka Perkov out:
503*d131ad68SLuka Perkov 	return rc;
504*d131ad68SLuka Perkov }
505*d131ad68SLuka Perkov 
506*d131ad68SLuka Perkov static void *
507*d131ad68SLuka Perkov kwboot_mmap_image(const char *path, size_t *size, int prot)
508*d131ad68SLuka Perkov {
509*d131ad68SLuka Perkov 	int rc, fd, flags;
510*d131ad68SLuka Perkov 	struct stat st;
511*d131ad68SLuka Perkov 	void *img;
512*d131ad68SLuka Perkov 
513*d131ad68SLuka Perkov 	rc = -1;
514*d131ad68SLuka Perkov 	fd = -1;
515*d131ad68SLuka Perkov 	img = NULL;
516*d131ad68SLuka Perkov 
517*d131ad68SLuka Perkov 	fd = open(path, O_RDONLY);
518*d131ad68SLuka Perkov 	if (fd < 0)
519*d131ad68SLuka Perkov 		goto out;
520*d131ad68SLuka Perkov 
521*d131ad68SLuka Perkov 	rc = fstat(fd, &st);
522*d131ad68SLuka Perkov 	if (rc)
523*d131ad68SLuka Perkov 		goto out;
524*d131ad68SLuka Perkov 
525*d131ad68SLuka Perkov 	flags = (prot & PROT_WRITE) ? MAP_PRIVATE : MAP_SHARED;
526*d131ad68SLuka Perkov 
527*d131ad68SLuka Perkov 	img = mmap(NULL, st.st_size, prot, flags, fd, 0);
528*d131ad68SLuka Perkov 	if (img == MAP_FAILED) {
529*d131ad68SLuka Perkov 		img = NULL;
530*d131ad68SLuka Perkov 		goto out;
531*d131ad68SLuka Perkov 	}
532*d131ad68SLuka Perkov 
533*d131ad68SLuka Perkov 	rc = 0;
534*d131ad68SLuka Perkov 	*size = st.st_size;
535*d131ad68SLuka Perkov out:
536*d131ad68SLuka Perkov 	if (rc && img) {
537*d131ad68SLuka Perkov 		munmap(img, st.st_size);
538*d131ad68SLuka Perkov 		img = NULL;
539*d131ad68SLuka Perkov 	}
540*d131ad68SLuka Perkov 	if (fd >= 0)
541*d131ad68SLuka Perkov 		close(fd);
542*d131ad68SLuka Perkov 
543*d131ad68SLuka Perkov 	return img;
544*d131ad68SLuka Perkov }
545*d131ad68SLuka Perkov 
546*d131ad68SLuka Perkov static uint8_t
547*d131ad68SLuka Perkov kwboot_img_csum8(void *_data, size_t size)
548*d131ad68SLuka Perkov {
549*d131ad68SLuka Perkov 	uint8_t *data = _data, csum;
550*d131ad68SLuka Perkov 
551*d131ad68SLuka Perkov 	for (csum = 0; size-- > 0; data++)
552*d131ad68SLuka Perkov 		csum += *data;
553*d131ad68SLuka Perkov 
554*d131ad68SLuka Perkov 	return csum;
555*d131ad68SLuka Perkov }
556*d131ad68SLuka Perkov 
557*d131ad68SLuka Perkov static int
558*d131ad68SLuka Perkov kwboot_img_patch_hdr(void *img, size_t size)
559*d131ad68SLuka Perkov {
560*d131ad68SLuka Perkov 	int rc;
561*d131ad68SLuka Perkov 	bhr_t *hdr;
562*d131ad68SLuka Perkov 	uint8_t csum;
563*d131ad68SLuka Perkov 	const size_t hdrsz = sizeof(*hdr);
564*d131ad68SLuka Perkov 
565*d131ad68SLuka Perkov 	rc = -1;
566*d131ad68SLuka Perkov 	hdr = img;
567*d131ad68SLuka Perkov 
568*d131ad68SLuka Perkov 	if (size < hdrsz) {
569*d131ad68SLuka Perkov 		errno = EINVAL;
570*d131ad68SLuka Perkov 		goto out;
571*d131ad68SLuka Perkov 	}
572*d131ad68SLuka Perkov 
573*d131ad68SLuka Perkov 	csum = kwboot_img_csum8(hdr, hdrsz) - hdr->checkSum;
574*d131ad68SLuka Perkov 	if (csum != hdr->checkSum) {
575*d131ad68SLuka Perkov 		errno = EINVAL;
576*d131ad68SLuka Perkov 		goto out;
577*d131ad68SLuka Perkov 	}
578*d131ad68SLuka Perkov 
579*d131ad68SLuka Perkov 	if (hdr->blockid == IBR_HDR_UART_ID) {
580*d131ad68SLuka Perkov 		rc = 0;
581*d131ad68SLuka Perkov 		goto out;
582*d131ad68SLuka Perkov 	}
583*d131ad68SLuka Perkov 
584*d131ad68SLuka Perkov 	hdr->blockid = IBR_HDR_UART_ID;
585*d131ad68SLuka Perkov 
586*d131ad68SLuka Perkov 	hdr->nandeccmode = IBR_HDR_ECC_DISABLED;
587*d131ad68SLuka Perkov 	hdr->nandpagesize = 0;
588*d131ad68SLuka Perkov 
589*d131ad68SLuka Perkov 	hdr->srcaddr = hdr->ext
590*d131ad68SLuka Perkov 		? sizeof(struct kwb_header)
591*d131ad68SLuka Perkov 		: sizeof(*hdr);
592*d131ad68SLuka Perkov 
593*d131ad68SLuka Perkov 	hdr->checkSum = kwboot_img_csum8(hdr, hdrsz) - csum;
594*d131ad68SLuka Perkov 
595*d131ad68SLuka Perkov 	rc = 0;
596*d131ad68SLuka Perkov out:
597*d131ad68SLuka Perkov 	return rc;
598*d131ad68SLuka Perkov }
599*d131ad68SLuka Perkov 
600*d131ad68SLuka Perkov static void
601*d131ad68SLuka Perkov kwboot_usage(FILE *stream, char *progname)
602*d131ad68SLuka Perkov {
603*d131ad68SLuka Perkov 	fprintf(stream,
604*d131ad68SLuka Perkov 		"Usage: %s -b <image> [ -p ] [ -t ] "
605*d131ad68SLuka Perkov 		"[-B <baud> ] <TTY>\n", progname);
606*d131ad68SLuka Perkov 	fprintf(stream, "\n");
607*d131ad68SLuka Perkov 	fprintf(stream, "  -b <image>: boot <image>\n");
608*d131ad68SLuka Perkov 	fprintf(stream, "  -p: patch <image> to type 0x69 (uart boot)\n");
609*d131ad68SLuka Perkov 	fprintf(stream, "\n");
610*d131ad68SLuka Perkov 	fprintf(stream, "  -t: mini terminal\n");
611*d131ad68SLuka Perkov 	fprintf(stream, "\n");
612*d131ad68SLuka Perkov 	fprintf(stream, "  -B <baud>: set baud rate\n");
613*d131ad68SLuka Perkov 	fprintf(stream, "\n");
614*d131ad68SLuka Perkov }
615*d131ad68SLuka Perkov 
616*d131ad68SLuka Perkov int
617*d131ad68SLuka Perkov main(int argc, char **argv)
618*d131ad68SLuka Perkov {
619*d131ad68SLuka Perkov 	const char *ttypath, *imgpath;
620*d131ad68SLuka Perkov 	int rv, rc, tty, term, prot, patch;
621*d131ad68SLuka Perkov 	void *bootmsg;
622*d131ad68SLuka Perkov 	void *img;
623*d131ad68SLuka Perkov 	size_t size;
624*d131ad68SLuka Perkov 	speed_t speed;
625*d131ad68SLuka Perkov 
626*d131ad68SLuka Perkov 	rv = 1;
627*d131ad68SLuka Perkov 	tty = -1;
628*d131ad68SLuka Perkov 	bootmsg = NULL;
629*d131ad68SLuka Perkov 	imgpath = NULL;
630*d131ad68SLuka Perkov 	img = NULL;
631*d131ad68SLuka Perkov 	term = 0;
632*d131ad68SLuka Perkov 	patch = 0;
633*d131ad68SLuka Perkov 	size = 0;
634*d131ad68SLuka Perkov 	speed = B115200;
635*d131ad68SLuka Perkov 
636*d131ad68SLuka Perkov 	kwboot_verbose = isatty(STDOUT_FILENO);
637*d131ad68SLuka Perkov 
638*d131ad68SLuka Perkov 	do {
639*d131ad68SLuka Perkov 		int c = getopt(argc, argv, "hb:ptB:");
640*d131ad68SLuka Perkov 		if (c < 0)
641*d131ad68SLuka Perkov 			break;
642*d131ad68SLuka Perkov 
643*d131ad68SLuka Perkov 		switch (c) {
644*d131ad68SLuka Perkov 		case 'b':
645*d131ad68SLuka Perkov 			bootmsg = kwboot_msg_boot;
646*d131ad68SLuka Perkov 			imgpath = optarg;
647*d131ad68SLuka Perkov 			break;
648*d131ad68SLuka Perkov 
649*d131ad68SLuka Perkov 		case 'p':
650*d131ad68SLuka Perkov 			patch = 1;
651*d131ad68SLuka Perkov 			break;
652*d131ad68SLuka Perkov 
653*d131ad68SLuka Perkov 		case 't':
654*d131ad68SLuka Perkov 			term = 1;
655*d131ad68SLuka Perkov 			break;
656*d131ad68SLuka Perkov 
657*d131ad68SLuka Perkov 		case 'B':
658*d131ad68SLuka Perkov 			speed = kwboot_tty_speed(atoi(optarg));
659*d131ad68SLuka Perkov 			if (speed == -1)
660*d131ad68SLuka Perkov 				goto usage;
661*d131ad68SLuka Perkov 			break;
662*d131ad68SLuka Perkov 
663*d131ad68SLuka Perkov 		case 'h':
664*d131ad68SLuka Perkov 			rv = 0;
665*d131ad68SLuka Perkov 		default:
666*d131ad68SLuka Perkov 			goto usage;
667*d131ad68SLuka Perkov 		}
668*d131ad68SLuka Perkov 	} while (1);
669*d131ad68SLuka Perkov 
670*d131ad68SLuka Perkov 	if (!bootmsg && !term)
671*d131ad68SLuka Perkov 		goto usage;
672*d131ad68SLuka Perkov 
673*d131ad68SLuka Perkov 	if (patch && !imgpath)
674*d131ad68SLuka Perkov 		goto usage;
675*d131ad68SLuka Perkov 
676*d131ad68SLuka Perkov 	if (argc - optind < 1)
677*d131ad68SLuka Perkov 		goto usage;
678*d131ad68SLuka Perkov 
679*d131ad68SLuka Perkov 	ttypath = argv[optind++];
680*d131ad68SLuka Perkov 
681*d131ad68SLuka Perkov 	tty = kwboot_open_tty(ttypath, speed);
682*d131ad68SLuka Perkov 	if (tty < 0) {
683*d131ad68SLuka Perkov 		perror(ttypath);
684*d131ad68SLuka Perkov 		goto out;
685*d131ad68SLuka Perkov 	}
686*d131ad68SLuka Perkov 
687*d131ad68SLuka Perkov 	if (imgpath) {
688*d131ad68SLuka Perkov 		prot = PROT_READ | (patch ? PROT_WRITE : 0);
689*d131ad68SLuka Perkov 
690*d131ad68SLuka Perkov 		img = kwboot_mmap_image(imgpath, &size, prot);
691*d131ad68SLuka Perkov 		if (!img) {
692*d131ad68SLuka Perkov 			perror(imgpath);
693*d131ad68SLuka Perkov 			goto out;
694*d131ad68SLuka Perkov 		}
695*d131ad68SLuka Perkov 	}
696*d131ad68SLuka Perkov 
697*d131ad68SLuka Perkov 	if (patch) {
698*d131ad68SLuka Perkov 		rc = kwboot_img_patch_hdr(img, size);
699*d131ad68SLuka Perkov 		if (rc) {
700*d131ad68SLuka Perkov 			fprintf(stderr, "%s: Invalid image.\n", imgpath);
701*d131ad68SLuka Perkov 			goto out;
702*d131ad68SLuka Perkov 		}
703*d131ad68SLuka Perkov 	}
704*d131ad68SLuka Perkov 
705*d131ad68SLuka Perkov 	if (bootmsg) {
706*d131ad68SLuka Perkov 		rc = kwboot_bootmsg(tty, bootmsg);
707*d131ad68SLuka Perkov 		if (rc) {
708*d131ad68SLuka Perkov 			perror("bootmsg");
709*d131ad68SLuka Perkov 			goto out;
710*d131ad68SLuka Perkov 		}
711*d131ad68SLuka Perkov 	}
712*d131ad68SLuka Perkov 
713*d131ad68SLuka Perkov 	if (img) {
714*d131ad68SLuka Perkov 		rc = kwboot_xmodem(tty, img, size);
715*d131ad68SLuka Perkov 		if (rc) {
716*d131ad68SLuka Perkov 			perror("xmodem");
717*d131ad68SLuka Perkov 			goto out;
718*d131ad68SLuka Perkov 		}
719*d131ad68SLuka Perkov 	}
720*d131ad68SLuka Perkov 
721*d131ad68SLuka Perkov 	if (term) {
722*d131ad68SLuka Perkov 		rc = kwboot_terminal(tty);
723*d131ad68SLuka Perkov 		if (rc && !(errno == EINTR)) {
724*d131ad68SLuka Perkov 			perror("terminal");
725*d131ad68SLuka Perkov 			goto out;
726*d131ad68SLuka Perkov 		}
727*d131ad68SLuka Perkov 	}
728*d131ad68SLuka Perkov 
729*d131ad68SLuka Perkov 	rv = 0;
730*d131ad68SLuka Perkov out:
731*d131ad68SLuka Perkov 	if (tty >= 0)
732*d131ad68SLuka Perkov 		close(tty);
733*d131ad68SLuka Perkov 
734*d131ad68SLuka Perkov 	if (img)
735*d131ad68SLuka Perkov 		munmap(img, size);
736*d131ad68SLuka Perkov 
737*d131ad68SLuka Perkov 	return rv;
738*d131ad68SLuka Perkov 
739*d131ad68SLuka Perkov usage:
740*d131ad68SLuka Perkov 	kwboot_usage(rv ? stderr : stdout, basename(argv[0]));
741*d131ad68SLuka Perkov 	goto out;
742*d131ad68SLuka Perkov }
743