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