software/bios: move romboot after serialboot and netboot
[litex.git] / tools / flterm.c
1 /*
2 * MiSoC
3 * Copyright (C) 2007, 2008, 2009, 2010, 2011 Sebastien Bourdeauducq
4 * Copyright (C) 2011 Michael Walle
5 * Copyright (C) 2004 MontaVista Software, Inc
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, version 3 of the License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #define _GNU_SOURCE
21
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <sys/ioctl.h>
25 #include <sys/time.h>
26 #include <string.h>
27 #include <ctype.h>
28 #include <termios.h>
29 #include <fcntl.h>
30 #include <unistd.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <poll.h>
34 #include <fcntl.h>
35 #include <getopt.h>
36 #include <sfl.h>
37
38 #ifdef __linux__
39 #include <linux/serial.h>
40 #endif
41
42 #define DEFAULT_KERNELADR (0x40000000)
43 #define DEFAULT_CMDLINEADR (0x41000000)
44 #define DEFAULT_INITRDADR (0x41002000)
45
46 #define GDBBUFLEN 1000
47
48 unsigned int crc16_table[256] = {
49 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
50 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
51 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
52 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
53 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
54 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
55 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
56 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
57 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
58 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
59 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
60 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
61 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
62 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
63 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
64 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
65 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
66 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
67 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
68 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
69 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
70 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
71 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
72 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
73 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
74 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
75 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
76 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
77 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
78 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
79 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
80 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
81 };
82
83 static int debug = 0;
84
85 static unsigned short crc16(const void *_buffer, int len)
86 {
87 const unsigned char *buffer = (const unsigned char *)_buffer;
88 unsigned short crc;
89
90 crc = 0;
91 while(len-- > 0)
92 crc = crc16_table[((crc >> 8) ^ (*buffer++)) & 0xFF] ^ (crc << 8);
93
94 return crc;
95 }
96
97 static int write_exact(int fd, const char *data, unsigned int length)
98 {
99 int r;
100
101 while(length > 0) {
102 r = write(fd, data, length);
103 if(r <= 0) return 0;
104 length -= r;
105 data += r;
106 }
107 return 1;
108 }
109
110 /* length, cmd and payload must be filled in */
111 static int send_frame(int serialfd, struct sfl_frame *frame)
112 {
113 unsigned short int crc;
114 int retry;
115 char reply;
116
117 crc = crc16(&frame->cmd, frame->length+1);
118 frame->crc[0] = (crc & 0xff00) >> 8;
119 frame->crc[1] = (crc & 0x00ff);
120
121 retry = 0;
122 do {
123 if(!write_exact(serialfd, (char *)frame, frame->length+4)) {
124 perror("[FLTERM] Unable to write to serial port.");
125 return 0;
126 }
127 /* Get the reply from the device */
128 read(serialfd, &reply, 1); /* TODO: timeout */
129 switch(reply) {
130 case SFL_ACK_SUCCESS:
131 retry = 0;
132 break;
133 case SFL_ACK_CRCERROR:
134 retry = 1;
135 break;
136 default:
137 fprintf(stderr, "[FLTERM] Got unknown reply '%c' from the device, aborting.\n", reply);
138 return 0;
139 }
140 } while(retry);
141 return 1;
142 }
143
144 static int upload_fd(int serialfd, const char *name, int firmwarefd, unsigned int load_address)
145 {
146 struct sfl_frame frame;
147 int readbytes;
148 int length;
149 int position;
150 unsigned int current_address;
151 struct timeval t0;
152 struct timeval t1;
153 int millisecs;
154
155 length = lseek(firmwarefd, 0, SEEK_END);
156 lseek(firmwarefd, 0, SEEK_SET);
157
158 printf("[FLTERM] Uploading %s (%d bytes)...\n", name, length);
159
160 gettimeofday(&t0, NULL);
161
162 current_address = load_address;
163 position = 0;
164 while(1) {
165 printf("%d%%\r", 100*position/length);
166 fflush(stdout);
167
168 readbytes = read(firmwarefd, &frame.payload[4], sizeof(frame.payload) - 4);
169 if(readbytes < 0) {
170 perror("[FLTERM] Unable to read image.");
171 return -1;
172 }
173 if(readbytes == 0) break;
174
175 frame.length = readbytes+4;
176 frame.cmd = SFL_CMD_LOAD;
177 frame.payload[0] = (current_address & 0xff000000) >> 24;
178 frame.payload[1] = (current_address & 0x00ff0000) >> 16;
179 frame.payload[2] = (current_address & 0x0000ff00) >> 8;
180 frame.payload[3] = (current_address & 0x000000ff);
181
182 if(!send_frame(serialfd, &frame)) return -1;
183
184 current_address += readbytes;
185 position += readbytes;
186 }
187
188 gettimeofday(&t1, NULL);
189
190 millisecs = (t1.tv_sec - t0.tv_sec)*1000 + (t1.tv_usec - t0.tv_usec)/1000;
191
192 printf("[FLTERM] Upload complete (%.1fKB/s).\n", 1000.0*(double)length/((double)millisecs*1024.0));
193 return length;
194 }
195
196 static const char sfl_magic_req[SFL_MAGIC_LEN] = SFL_MAGIC_REQ;
197 static const char sfl_magic_ack[SFL_MAGIC_LEN] = SFL_MAGIC_ACK;
198
199 static void answer_magic(int serialfd,
200 const char *kernel_image, unsigned int kernel_address,
201 const char *cmdline, unsigned int cmdline_address,
202 const char *initrd_image, unsigned int initrd_address)
203 {
204 int kernelfd, initrdfd;
205 struct sfl_frame frame;
206
207 printf("[FLTERM] Received firmware download request from the device.\n");
208
209 kernelfd = open(kernel_image, O_RDONLY);
210 if(kernelfd == -1) {
211 perror("[FLTERM] Unable to open kernel image (request ignored).");
212 return;
213 }
214 initrdfd = -1;
215 if(initrd_image != NULL) {
216 initrdfd = open(initrd_image, O_RDONLY);
217 if(initrdfd == -1) {
218 perror("[FLTERM] Unable to open initrd image (request ignored).");
219 close(kernelfd);
220 return;
221 }
222 }
223
224 write_exact(serialfd, sfl_magic_ack, SFL_MAGIC_LEN);
225
226 upload_fd(serialfd, "kernel", kernelfd, kernel_address);
227 if(cmdline != NULL) {
228 int len;
229
230 printf("[FLTERM] Setting kernel command line: '%s'.\n", cmdline);
231
232 len = strlen(cmdline)+1;
233 if(len > (254-4)) {
234 fprintf(stderr, "[FLTERM] Kernel command line too long, load aborted.\n");
235 close(initrdfd);
236 close(kernelfd);
237 return;
238 }
239 frame.length = len+4;
240 frame.cmd = SFL_CMD_LOAD;
241 frame.payload[0] = (cmdline_address & 0xff000000) >> 24;
242 frame.payload[1] = (cmdline_address & 0x00ff0000) >> 16;
243 frame.payload[2] = (cmdline_address & 0x0000ff00) >> 8;
244 frame.payload[3] = (cmdline_address & 0x000000ff);
245 strcpy((char *)&frame.payload[4], cmdline);
246 send_frame(serialfd, &frame);
247
248 frame.length = 4;
249 frame.cmd = SFL_CMD_CMDLINE;
250 frame.payload[0] = (cmdline_address & 0xff000000) >> 24;
251 frame.payload[1] = (cmdline_address & 0x00ff0000) >> 16;
252 frame.payload[2] = (cmdline_address & 0x0000ff00) >> 8;
253 frame.payload[3] = (cmdline_address & 0x000000ff);
254 send_frame(serialfd, &frame);
255 }
256 if(initrdfd != -1) {
257 int len;
258
259 len = upload_fd(serialfd, "initrd", initrdfd, initrd_address);
260 if(len <= 0) return;
261
262 frame.length = 4;
263 frame.cmd = SFL_CMD_INITRDSTART;
264 frame.payload[0] = (initrd_address & 0xff000000) >> 24;
265 frame.payload[1] = (initrd_address & 0x00ff0000) >> 16;
266 frame.payload[2] = (initrd_address & 0x0000ff00) >> 8;
267 frame.payload[3] = (initrd_address & 0x000000ff);
268 send_frame(serialfd, &frame);
269
270 initrd_address += len-1;
271
272 frame.length = 4;
273 frame.cmd = SFL_CMD_INITRDEND;
274 frame.payload[0] = (initrd_address & 0xff000000) >> 24;
275 frame.payload[1] = (initrd_address & 0x00ff0000) >> 16;
276 frame.payload[2] = (initrd_address & 0x0000ff00) >> 8;
277 frame.payload[3] = (initrd_address & 0x000000ff);
278 send_frame(serialfd, &frame);
279 }
280
281 /* Send the jump command */
282 printf("[FLTERM] Booting the device.\n");
283 frame.length = 4;
284 frame.cmd = SFL_CMD_JUMP;
285 frame.payload[0] = (kernel_address & 0xff000000) >> 24;
286 frame.payload[1] = (kernel_address & 0x00ff0000) >> 16;
287 frame.payload[2] = (kernel_address & 0x0000ff00) >> 8;
288 frame.payload[3] = (kernel_address & 0x000000ff);
289 if(!send_frame(serialfd, &frame)) return;
290
291 printf("[FLTERM] Done.\n");
292
293 close(initrdfd);
294 close(kernelfd);
295 }
296
297 static int hex(unsigned char c)
298 {
299 if(c >= 'a' && c <= 'f') {
300 return c - 'a' + 10;
301 }
302 if(c >= '0' && c <= '9') {
303 return c - '0';
304 }
305 if(c >= 'A' && c <= 'F') {
306 return c - 'A' + 10;
307 }
308 return 0;
309 }
310
311 /*
312 * This is taken from kdmx2.
313 * Author: Tom Rini <trini@mvista.com>
314 */
315 static void gdb_process_packet(int infd, int outfd, int altfd)
316 {
317 /* gdb packet handling */
318 char gdbbuf[GDBBUFLEN + 1];
319 int pos = 0;
320 unsigned char runcksum = 0;
321 unsigned char recvcksum = 0;
322 struct pollfd fds;
323 char c;
324 int seen_hash = 0;
325
326 fds.fd = infd;
327 fds.events = POLLIN;
328
329 memset(gdbbuf, 0, sizeof(gdbbuf));
330 gdbbuf[0] = '$';
331 pos++;
332
333 while (1) {
334 fds.revents = 0;
335 if(poll(&fds, 1, 100) == 0) {
336 /* timeout */
337 if(altfd != -1) {
338 write(altfd, gdbbuf, pos);
339 }
340 break;
341 }
342 if(pos == GDBBUFLEN) {
343 if(altfd != -1) {
344 write(altfd, gdbbuf, pos);
345 }
346 break;
347 }
348 read(infd, &c, 1);
349 gdbbuf[pos++] = c;
350 if(c == '#') {
351 seen_hash = 1;
352 } else if(seen_hash == 0) {
353 runcksum += c;
354 } else if(seen_hash == 1) {
355 recvcksum = hex(c) << 4;
356 seen_hash = 2;
357 } else if(seen_hash == 2) {
358 recvcksum |= hex(c);
359 seen_hash = 3;
360 }
361
362 if(seen_hash == 3) {
363 /* we're done */
364 runcksum %= 256;
365 if(recvcksum == runcksum) {
366 if(debug) {
367 fprintf(stderr, "[GDB %s]\n", gdbbuf);
368 }
369 write(outfd, gdbbuf, pos);
370 } else {
371 if(altfd != -1) {
372 write(altfd, gdbbuf, pos);
373 }
374 }
375 seen_hash = 0;
376 break;
377 }
378 }
379 }
380
381 static void do_terminal(char *serial_port,
382 int baud, int gdb_passthrough,
383 const char *kernel_image, unsigned int kernel_address,
384 const char *cmdline, unsigned int cmdline_address,
385 const char *initrd_image, unsigned int initrd_address,
386 char *log_path)
387 {
388 int serialfd;
389 int gdbfd = -1;
390 FILE *logfd = NULL;
391 struct termios my_termios;
392 char c;
393 int recognized;
394 struct pollfd fds[3];
395 int flags;
396 int rsp_pending = 0;
397 int c_cflag;
398 int custom_divisor;
399
400 /* Open and configure the serial port */
401 if(log_path != NULL) {
402 logfd = fopen(log_path, "a+");
403 if(logfd == NULL) {
404 perror("Unable to open log file");
405 return;
406 }
407 }
408
409 serialfd = open(serial_port, O_RDWR|O_NOCTTY);
410 if(serialfd == -1) {
411 perror("Unable to open serial port");
412 return;
413 }
414
415 custom_divisor = 0;
416 switch(baud) {
417 case 9600: c_cflag = B9600; break;
418 case 19200: c_cflag = B19200; break;
419 case 38400: c_cflag = B38400; break;
420 case 57600: c_cflag = B57600; break;
421 case 115200: c_cflag = B115200; break;
422 case 230400: c_cflag = B230400; break;
423 default:
424 c_cflag = B38400;
425 custom_divisor = 1;
426 break;
427 }
428
429 #ifdef __linux__
430 if(custom_divisor) {
431 struct serial_struct serial_info;
432 ioctl(serialfd, TIOCGSERIAL, &serial_info);
433 serial_info.custom_divisor = serial_info.baud_base / baud;
434 serial_info.flags &= ~ASYNC_SPD_MASK;
435 serial_info.flags |= ASYNC_SPD_CUST;
436 ioctl(serialfd, TIOCSSERIAL, &serial_info);
437 }
438 #else
439 if(custom_divisor) {
440 fprintf(stderr, "[FLTERM] baudrate not supported\n");
441 return;
442 }
443 #endif
444
445 /* Thanks to Julien Schmitt (GTKTerm) for figuring out the correct parameters
446 * to put into that weird struct.
447 */
448 tcgetattr(serialfd, &my_termios);
449 my_termios.c_cflag = c_cflag;
450 my_termios.c_cflag |= CS8;
451 my_termios.c_cflag |= CREAD;
452 my_termios.c_iflag = IGNPAR | IGNBRK;
453 my_termios.c_cflag |= CLOCAL;
454 my_termios.c_oflag = 0;
455 my_termios.c_lflag = 0;
456 my_termios.c_cc[VTIME] = 0;
457 my_termios.c_cc[VMIN] = 1;
458 tcsetattr(serialfd, TCSANOW, &my_termios);
459 tcflush(serialfd, TCOFLUSH);
460 tcflush(serialfd, TCIFLUSH);
461
462 /* Prepare the fdset for poll() */
463 fds[0].fd = 0;
464 fds[0].events = POLLIN;
465 fds[1].fd = serialfd;
466 fds[1].events = POLLIN;
467
468 recognized = 0;
469 flags = fcntl(serialfd, F_GETFL, 0);
470 while(1) {
471 if(gdbfd == -1 && gdb_passthrough) {
472 gdbfd = open("/dev/ptmx", O_RDWR);
473 if(grantpt(gdbfd) != 0) {
474 perror("grantpt()");
475 return;
476 }
477 if(unlockpt(gdbfd) != 0) {
478 perror("unlockpt()");
479 return;
480 }
481 printf("[GDB passthrough] use %s as GDB remote device\n",
482 ptsname(gdbfd));
483 fds[2].fd = gdbfd;
484 fds[2].events = POLLIN;
485 }
486
487 fds[0].revents = 0;
488 fds[1].revents = 0;
489 fds[2].revents = 0;
490
491 /* poll() behaves strangely when the serial port descriptor is in
492 * blocking mode. So work around this.
493 */
494 fcntl(serialfd, F_SETFL, flags|O_NONBLOCK);
495 if(poll(&fds[0], (gdbfd == -1) ? 2 : 3, -1) < 0) break;
496 fcntl(serialfd, F_SETFL, flags);
497
498 if(fds[0].revents & POLLIN) {
499 if(read(0, &c, 1) <= 0) break;
500 if(write(serialfd, &c, 1) <= 0) break;
501 }
502
503 if(fds[2].revents & POLLIN) {
504 rsp_pending = 1;
505 if(read(gdbfd, &c, 1) <= 0) break;
506 if(c == '\03') {
507 /* convert ETX to breaks */
508 if(debug) {
509 fprintf(stderr, "[GDB BREAK]\n");
510 }
511 tcsendbreak(serialfd, 0);
512 } else if(c == '$') {
513 gdb_process_packet(gdbfd, serialfd, -1);
514 } else if(c == '+' || c == '-') {
515 write(serialfd, &c, 1);
516 } else {
517 fprintf(stderr, "Internal error (line %d)", __LINE__);
518 exit(1);
519 }
520 }
521
522 if(fds[2].revents & POLLHUP) {
523 /* close and reopen new pair */
524 close(gdbfd);
525 gdbfd = -1;
526 continue;
527 }
528
529 if(fds[1].revents & POLLIN) {
530 if(read(serialfd, &c, 1) <= 0) break;
531
532 if(logfd && c && isascii(c)) {
533 fwrite(&c, sizeof(c), 1, logfd);
534 if(c == '\n') fflush(logfd);
535 }
536
537 if(gdbfd != -1 && rsp_pending && (c == '+' || c == '-')) {
538 rsp_pending = 0;
539 write(gdbfd, &c, 1);
540 } else if(gdbfd != -1 && c == '$') {
541 gdb_process_packet(serialfd, gdbfd, 0);
542 } else {
543 /* write to terminal */
544 write(0, &c, 1);
545
546 if(kernel_image != NULL) {
547 if(c == sfl_magic_req[recognized]) {
548 recognized++;
549 if(recognized == SFL_MAGIC_LEN) {
550 /* We've got the magic string ! */
551 recognized = 0;
552 answer_magic(serialfd,
553 kernel_image, kernel_address,
554 cmdline, cmdline_address,
555 initrd_image, initrd_address);
556 }
557 } else {
558 if(c == sfl_magic_req[0]) recognized = 1; else recognized = 0;
559 }
560 }
561 }
562 }
563 }
564
565 close(serialfd);
566
567 if(gdbfd != -1) close(gdbfd);
568 if(logfd) fclose(logfd);
569 }
570
571 enum {
572 OPTION_PORT,
573 OPTION_GDB_PASSTHROUGH,
574 OPTION_SPEED,
575 OPTION_DEBUG,
576 OPTION_KERNEL,
577 OPTION_KERNELADR,
578 OPTION_CMDLINE,
579 OPTION_CMDLINEADR,
580 OPTION_INITRD,
581 OPTION_INITRDADR,
582 OPTION_LOG,
583 OPTION_HELP,
584 };
585
586 static const struct option options[] = {
587 {
588 .name = "port",
589 .has_arg = 1,
590 .val = OPTION_PORT
591 },
592 {
593 .name = "gdb-passthrough",
594 .has_arg = 0,
595 .val = OPTION_GDB_PASSTHROUGH
596 },
597 {
598 .name = "debug",
599 .has_arg = 0,
600 .val = OPTION_DEBUG
601 },
602 {
603 .name = "speed",
604 .has_arg = 1,
605 .val = OPTION_SPEED
606 },
607 {
608 .name = "kernel",
609 .has_arg = 1,
610 .val = OPTION_KERNEL
611 },
612 {
613 .name = "kernel-adr",
614 .has_arg = 1,
615 .val = OPTION_KERNELADR
616 },
617 {
618 .name = "cmdline",
619 .has_arg = 1,
620 .val = OPTION_CMDLINE
621 },
622 {
623 .name = "cmdline-adr",
624 .has_arg = 1,
625 .val = OPTION_CMDLINEADR
626 },
627 {
628 .name = "initrd",
629 .has_arg = 1,
630 .val = OPTION_INITRD
631 },
632 {
633 .name = "initrd-adr",
634 .has_arg = 1,
635 .val = OPTION_INITRDADR
636 },
637 {
638 .name = "log",
639 .has_arg = 1,
640 .val = OPTION_LOG
641 },
642 {
643 .name = "help",
644 .has_arg = 0,
645 .val = OPTION_HELP
646 },
647 {
648 .name = NULL
649 }
650 };
651
652 static void print_usage()
653 {
654 fprintf(stderr, "Serial boot program for MiSoC - v. 2.4\n");
655 fprintf(stderr, "Copyright (C) 2007, 2008, 2009, 2010, 2011 Sebastien Bourdeauducq\n");
656 fprintf(stderr, "Copyright (C) 2011 Michael Walle\n");
657 fprintf(stderr, "Copyright (C) 2004 MontaVista Software, Inc\n\n");
658
659 fprintf(stderr, "This program is free software: you can redistribute it and/or modify\n");
660 fprintf(stderr, "it under the terms of the GNU General Public License as published by\n");
661 fprintf(stderr, "the Free Software Foundation, version 3 of the License.\n\n");
662
663 fprintf(stderr, "Usage: flterm --port <port>\n");
664 fprintf(stderr, " [--speed <speed>] [--gdb-passthrough] [--debug]\n");
665 fprintf(stderr, " [--kernel <kernel_image> [--kernel-adr <address>]]\n");
666 fprintf(stderr, " [--cmdline <cmdline> [--cmdline-adr <address>]]\n");
667 fprintf(stderr, " [--initrd <initrd_image> [--initrd-adr <address>]]\n");
668 fprintf(stderr, " [--log <log_file>]\n\n");
669 fprintf(stderr, "Default load addresses:\n");
670 fprintf(stderr, " kernel: 0x%08x\n", DEFAULT_KERNELADR);
671 fprintf(stderr, " cmdline: 0x%08x\n", DEFAULT_CMDLINEADR);
672 fprintf(stderr, " initrd: 0x%08x\n", DEFAULT_INITRDADR);
673 }
674
675 int main(int argc, char *argv[])
676 {
677 int opt;
678 char *serial_port;
679 int baud;
680 int gdb_passthrough;
681 char *kernel_image;
682 unsigned int kernel_address;
683 char *cmdline;
684 unsigned int cmdline_address;
685 char *initrd_image;
686 unsigned int initrd_address;
687 char *endptr;
688 char *log_path;
689 struct termios otty, ntty;
690
691 /* Fetch command line arguments */
692 serial_port = NULL;
693 baud = 115200;
694 gdb_passthrough = 0;
695 kernel_image = NULL;
696 kernel_address = DEFAULT_KERNELADR;
697 cmdline = NULL;
698 cmdline_address = DEFAULT_CMDLINEADR;
699 initrd_image = NULL;
700 initrd_address = DEFAULT_INITRDADR;
701 log_path = NULL;
702 while((opt = getopt_long(argc, argv, "", options, NULL)) != -1) {
703 if(opt == '?') {
704 print_usage();
705 return 1;
706 }
707 switch(opt) {
708 case OPTION_PORT:
709 free(serial_port);
710 serial_port = strdup(optarg);
711 break;
712 case OPTION_SPEED:
713 baud = strtoul(optarg, &endptr, 0);
714 if(*endptr != 0) {
715 fprintf(stderr, "[FLTERM] Couldn't parse baudrate\n");
716 return 1;
717 }
718 break;
719 case OPTION_DEBUG:
720 debug = 1;
721 break;
722 case OPTION_GDB_PASSTHROUGH:
723 gdb_passthrough = 1;
724 break;
725 case OPTION_KERNEL:
726 free(kernel_image);
727 kernel_image = strdup(optarg);
728 break;
729 case OPTION_KERNELADR:
730 kernel_address = strtoul(optarg, &endptr, 0);
731 if(*endptr != 0) {
732 fprintf(stderr, "[FLTERM] Couldn't parse kernel address\n");
733 return 1;
734 }
735 break;
736 case OPTION_CMDLINE:
737 free(cmdline);
738 cmdline = strdup(optarg);
739 break;
740 case OPTION_CMDLINEADR:
741 cmdline_address = strtoul(optarg, &endptr, 0);
742 if(*endptr != 0) {
743 fprintf(stderr, "[FLTERM] Couldn't parse cmdline address\n");
744 return 1;
745 }
746 break;
747 case OPTION_INITRD:
748 free(initrd_image);
749 initrd_image = strdup(optarg);
750 break;
751 case OPTION_INITRDADR:
752 initrd_address = strtoul(optarg, &endptr, 0);
753 if(*endptr != 0) {
754 fprintf(stderr, "[FLTERM] Couldn't parse initrd address\n");
755 return 1;
756 }
757 break;
758 case OPTION_LOG:
759 free(log_path);
760 log_path = strdup(optarg);
761 break;
762 case OPTION_HELP:
763 print_usage();
764 return 0;
765 }
766 }
767
768 if(serial_port == NULL) {
769 fprintf(stderr, "[FLTERM] No port given\n");
770 return 1;
771 }
772
773 /* Banner */
774 printf("[FLTERM] Starting...\n");
775
776 /* Set up stdin/out */
777 tcgetattr(0, &otty);
778 ntty = otty;
779 ntty.c_lflag &= ~(ECHO | ICANON);
780 tcsetattr(0, TCSANOW, &ntty);
781
782 /* Do the bulk of the work */
783 do_terminal(serial_port, baud, gdb_passthrough,
784 kernel_image, kernel_address,
785 cmdline, cmdline_address,
786 initrd_image, initrd_address,
787 log_path);
788
789 /* Restore stdin/out into their previous state */
790 tcsetattr(0, TCSANOW, &otty);
791
792 return 0;
793 }