Add tools
authorSebastien Bourdeauducq <sebastien@milkymist.org>
Sun, 5 Feb 2012 18:14:24 +0000 (19:14 +0100)
committerSebastien Bourdeauducq <sebastien@milkymist.org>
Sun, 5 Feb 2012 18:14:24 +0000 (19:14 +0100)
tools/Makefile [new file with mode: 0644]
tools/bin2hex.c [new file with mode: 0644]
tools/flterm.c [new file with mode: 0644]
tools/mkmmimg.c [new file with mode: 0644]
tools/sfl.h [new file with mode: 0644]

diff --git a/tools/Makefile b/tools/Makefile
new file mode 100644 (file)
index 0000000..0ed7497
--- /dev/null
@@ -0,0 +1,16 @@
+TARGETS=bin2hex mkmmimg flterm
+CC=clang
+
+all: $(TARGETS)
+
+%: %.c
+       $(CC) -O2 -Wall -I. -s -o $@ $<
+
+install: mkmmimg flterm
+       cp mkmmimg /usr/bin
+       cp flterm /usr/bin
+
+.PHONY: clean install
+
+clean:
+       rm -f $(TARGETS)
diff --git a/tools/bin2hex.c b/tools/bin2hex.c
new file mode 100644 (file)
index 0000000..e1ec2f1
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Milkymist SoC
+ * Copyright (C) 2007, 2008, 2009, 2010 Sebastien Bourdeauducq
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+/* WARNING: This tool is little endian in 16-bit mode 
+ * and big endian in 32-bit mode. 
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+int main(int argc, char *argv[])
+{
+       int i;
+       int pad;
+       FILE *fdi, *fdo;
+       unsigned char w[4];
+       int mode16;
+       
+       if((argc != 4) && (argc != 5)) {
+               fprintf(stderr, "Usage: bin2hex <infile> <outfile> <size> [16]\n");
+               return 1;
+       }
+       pad = atoi(argv[3]);
+       if(pad <= 0) {
+               fprintf(stderr, "Incorrect size\n");
+               return 1;
+       }
+       fdi = fopen(argv[1], "rb");
+       if(!fdi) {
+               perror("Unable to open input file");
+               return 1;
+       }
+       fdo = fopen(argv[2], "w");
+       if(!fdo) {
+               perror("Unable to open output file");
+               fclose(fdi);
+               return 1;
+       }
+       mode16 = (argc == 5) && (strcmp(argv[4], "16") == 0);
+       if(mode16) {
+               while(1) {
+                       if(fread(w, 2, 1, fdi) <= 0) break;
+                       fprintf(fdo, "%02hhx%02hhx\n", w[1], w[0]);
+                       pad--;
+               }
+       } else {
+               while(1) {
+                       if(fread(w, 4, 1, fdi) <= 0) break;
+                       fprintf(fdo, "%02hhx%02hhx%02hhx%02hhx\n", w[0], w[1], w[2], w[3]);
+                       pad--;
+               }
+       }
+       fclose(fdi);
+       if(pad<0)
+               fprintf(stderr, "Warning: Input binary is larger than specified size\n");
+       if(mode16) {
+               for(i=0;i<pad;i++)
+                       fprintf(fdo, "0000\n");
+       } else {
+               for(i=0;i<pad;i++)
+                       fprintf(fdo, "00000000\n");
+       }
+       if(fclose(fdo) != 0) {
+               perror("Unable to close output file");
+               return 1;
+       }
+       return 0;
+}
diff --git a/tools/flterm.c b/tools/flterm.c
new file mode 100644 (file)
index 0000000..6c4f28e
--- /dev/null
@@ -0,0 +1,735 @@
+/*
+ * Milkymist SoC
+ * Copyright (C) 2007, 2008, 2009, 2010, 2011 Sebastien Bourdeauducq
+ * Copyright (C) 2011 Michael Walle
+ * Copyright (C) 2004 MontaVista Software, Inc
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define _GNU_SOURCE
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <string.h>
+#include <ctype.h>
+#include <termios.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <poll.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <sfl.h>
+
+#define DEFAULT_KERNELADR      (0x40000000)
+#define DEFAULT_CMDLINEADR     (0x41000000)
+#define DEFAULT_INITRDADR      (0x41002000)
+
+#define GDBBUFLEN 1000
+
+unsigned int crc16_table[256] = {
+       0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
+       0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
+       0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
+       0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
+       0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
+       0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
+       0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
+       0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
+       0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
+       0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
+       0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
+       0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
+       0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
+       0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
+       0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
+       0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
+       0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
+       0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
+       0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
+       0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
+       0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
+       0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
+       0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
+       0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
+       0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
+       0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
+       0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
+       0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
+       0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
+       0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
+       0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
+       0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
+};
+
+static int debug = 0;
+
+static unsigned short crc16(const void *_buffer, int len)
+{
+       const unsigned char *buffer = (const unsigned char *)_buffer;
+       unsigned short crc;
+       
+       crc = 0;
+       while(len-- > 0)
+           crc = crc16_table[((crc >> 8) ^ (*buffer++)) & 0xFF] ^ (crc << 8);
+       
+       return crc;
+}
+
+static int write_exact(int fd, const char *data, unsigned int length)
+{
+       int r;
+       
+       while(length > 0) {
+               r = write(fd, data, length);
+               if(r <= 0) return 0;
+               length -= r;
+               data += r;
+       }
+       return 1;
+}
+
+/* length, cmd and payload must be filled in */
+static int send_frame(int serialfd, struct sfl_frame *frame)
+{
+       unsigned short int crc;
+       int retry;
+       char reply;
+       
+       crc = crc16(&frame->cmd, frame->length+1);
+       frame->crc[0] = (crc & 0xff00) >> 8;
+       frame->crc[1] = (crc & 0x00ff);
+       
+       retry = 0;
+       do {
+               if(!write_exact(serialfd, (char *)frame, frame->length+4)) {
+                       perror("[FLTERM] Unable to write to serial port.");
+                       return 0;
+               }
+               /* Get the reply from the device */
+               read(serialfd, &reply, 1); /* TODO: timeout */
+               switch(reply) {
+                       case SFL_ACK_SUCCESS:
+                               retry = 0;
+                               break;
+                       case SFL_ACK_CRCERROR:
+                               retry = 1;
+                               break;
+                       default:
+                               fprintf(stderr, "[FLTERM] Got unknown reply '%c' from the device, aborting.\n", reply);
+                               return 0;
+               }
+       } while(retry);
+       return 1;
+}
+
+static int upload_fd(int serialfd, const char *name, int firmwarefd, unsigned int load_address)
+{
+       struct sfl_frame frame;
+       int readbytes;
+       int length;
+       int position;
+       unsigned int current_address;
+       struct timeval t0;
+       struct timeval t1;
+       int millisecs;
+       
+       length = lseek(firmwarefd, 0, SEEK_END);
+       lseek(firmwarefd, 0, SEEK_SET);
+       
+       printf("[FLTERM] Uploading %s (%d bytes)...\n", name, length);
+       
+       gettimeofday(&t0, NULL);
+       
+       current_address = load_address;
+       position = 0;
+       while(1) {
+               printf("%d%%\r", 100*position/length);
+               fflush(stdout);
+       
+               readbytes = read(firmwarefd, &frame.payload[4], sizeof(frame.payload) - 4);
+               if(readbytes < 0) {
+                       perror("[FLTERM] Unable to read image.");
+                       return -1;
+               }
+               if(readbytes == 0) break;
+               
+               frame.length = readbytes+4;
+               frame.cmd = SFL_CMD_LOAD;
+               frame.payload[0] = (current_address & 0xff000000) >> 24;
+               frame.payload[1] = (current_address & 0x00ff0000) >> 16;
+               frame.payload[2] = (current_address & 0x0000ff00) >> 8;
+               frame.payload[3] = (current_address & 0x000000ff);
+               
+               if(!send_frame(serialfd, &frame)) return -1;
+               
+               current_address += readbytes;
+               position += readbytes;
+       }
+       
+       gettimeofday(&t1, NULL);
+       
+       millisecs = (t1.tv_sec - t0.tv_sec)*1000 + (t1.tv_usec - t0.tv_usec)/1000;
+       
+       printf("[FLTERM] Upload complete (%.1fKB/s).\n", 1000.0*(double)length/((double)millisecs*1024.0));
+       return length;
+}
+
+static const char sfl_magic_req[SFL_MAGIC_LEN] = SFL_MAGIC_REQ;
+static const char sfl_magic_ack[SFL_MAGIC_LEN] = SFL_MAGIC_ACK;
+
+static void answer_magic(int serialfd,
+       const char *kernel_image, unsigned int kernel_address,
+       const char *cmdline, unsigned int cmdline_address,
+       const char *initrd_image, unsigned int initrd_address)
+{
+       int kernelfd, initrdfd;
+       struct sfl_frame frame;
+       
+       printf("[FLTERM] Received firmware download request from the device.\n");
+       
+       kernelfd = open(kernel_image, O_RDONLY);
+       if(kernelfd == -1) {
+               perror("[FLTERM] Unable to open kernel image (request ignored).");
+               return;
+       }
+       initrdfd = -1;
+       if(initrd_image != NULL) {
+               initrdfd = open(initrd_image, O_RDONLY);
+               if(initrdfd == -1) {
+                       perror("[FLTERM] Unable to open initrd image (request ignored).");
+                       close(kernelfd);
+                       return;
+               }
+       }
+
+       write_exact(serialfd, sfl_magic_ack, SFL_MAGIC_LEN);
+       
+       upload_fd(serialfd, "kernel", kernelfd, kernel_address);
+       if(cmdline != NULL) {
+               int len;
+
+               printf("[FLTERM] Setting kernel command line: '%s'.\n", cmdline);
+
+               len = strlen(cmdline)+1;
+               if(len > (254-4)) {
+                       fprintf(stderr, "[FLTERM] Kernel command line too long, load aborted.\n");
+                       close(initrdfd);
+                       close(kernelfd);
+                       return;
+               }
+               frame.length = len+4;
+               frame.cmd = SFL_CMD_LOAD;
+               frame.payload[0] = (cmdline_address & 0xff000000) >> 24;
+               frame.payload[1] = (cmdline_address & 0x00ff0000) >> 16;
+               frame.payload[2] = (cmdline_address & 0x0000ff00) >> 8;
+               frame.payload[3] = (cmdline_address & 0x000000ff);
+               strcpy((char *)&frame.payload[4], cmdline);
+               send_frame(serialfd, &frame);
+
+               frame.length = 4;
+               frame.cmd = SFL_CMD_CMDLINE;
+               frame.payload[0] = (cmdline_address & 0xff000000) >> 24;
+               frame.payload[1] = (cmdline_address & 0x00ff0000) >> 16;
+               frame.payload[2] = (cmdline_address & 0x0000ff00) >> 8;
+               frame.payload[3] = (cmdline_address & 0x000000ff);
+               send_frame(serialfd, &frame);
+       }
+       if(initrdfd != -1) {
+               int len;
+               
+               len = upload_fd(serialfd, "initrd", initrdfd, initrd_address);
+               if(len <= 0) return;
+               
+               frame.length = 4;
+               frame.cmd = SFL_CMD_INITRDSTART;
+               frame.payload[0] = (initrd_address & 0xff000000) >> 24;
+               frame.payload[1] = (initrd_address & 0x00ff0000) >> 16;
+               frame.payload[2] = (initrd_address & 0x0000ff00) >> 8;
+               frame.payload[3] = (initrd_address & 0x000000ff);
+               send_frame(serialfd, &frame);
+
+               initrd_address += len-1;
+
+               frame.length = 4;
+               frame.cmd = SFL_CMD_INITRDEND;
+               frame.payload[0] = (initrd_address & 0xff000000) >> 24;
+               frame.payload[1] = (initrd_address & 0x00ff0000) >> 16;
+               frame.payload[2] = (initrd_address & 0x0000ff00) >> 8;
+               frame.payload[3] = (initrd_address & 0x000000ff);
+               send_frame(serialfd, &frame);
+       }
+
+       /* Send the jump command */
+       printf("[FLTERM] Booting the device.\n");
+       frame.length = 4;
+       frame.cmd = SFL_CMD_JUMP;
+       frame.payload[0] = (kernel_address & 0xff000000) >> 24;
+       frame.payload[1] = (kernel_address & 0x00ff0000) >> 16;
+       frame.payload[2] = (kernel_address & 0x0000ff00) >> 8;
+       frame.payload[3] = (kernel_address & 0x000000ff);
+       if(!send_frame(serialfd, &frame)) return;
+
+       printf("[FLTERM] Done.\n");
+
+       close(initrdfd);
+       close(kernelfd);
+}
+
+static int hex(unsigned char c)
+{
+       if(c >= 'a' && c <= 'f') {
+               return c - 'a' + 10;
+       }
+       if(c >= '0' && c <= '9') {
+               return c - '0';
+       }
+       if(c >= 'A' && c <= 'F') {
+               return c - 'A' + 10;
+       }
+       return 0;
+}
+
+/*
+ * This is taken from kdmx2.
+ * Author: Tom Rini <trini@mvista.com>
+ */
+static void gdb_process_packet(int infd, int outfd, int altfd)
+{
+       /* gdb packet handling */
+       char gdbbuf[GDBBUFLEN + 1];
+       int pos = 0;
+       unsigned char runcksum = 0;
+       unsigned char recvcksum = 0;
+       struct pollfd fds;
+       char c;
+       int seen_hash = 0;
+
+       fds.fd = infd;
+       fds.events = POLLIN;
+
+       memset(gdbbuf, 0, sizeof(gdbbuf));
+       gdbbuf[0] = '$';
+       pos++;
+
+       while (1) {
+               fds.revents = 0;
+               if(poll(&fds, 1, 100) == 0) {
+                       /* timeout */
+                       if(altfd != -1) {
+                               write(altfd, gdbbuf, pos);
+                       }
+                       break;
+               }
+               if(pos == GDBBUFLEN) {
+                       if(altfd != -1) {
+                               write(altfd, gdbbuf, pos);
+                       }
+                       break;
+               }
+               read(infd, &c, 1);
+               gdbbuf[pos++] = c;
+               if(c == '#') {
+                       seen_hash = 1;
+               } else if(seen_hash == 0) {
+                       runcksum += c;
+               } else if(seen_hash == 1) {
+                       recvcksum = hex(c) << 4;
+                       seen_hash = 2;
+               } else if(seen_hash == 2) {
+                       recvcksum |= hex(c);
+                       seen_hash = 3;
+               }
+
+               if(seen_hash == 3) {
+                       /* we're done */
+                       runcksum %= 256;
+                       if(recvcksum == runcksum) {
+                               if(debug) {
+                                       fprintf(stderr, "[GDB %s]\n", gdbbuf);
+                               }
+                               write(outfd, gdbbuf, pos);
+                       } else {
+                               if(altfd != -1) {
+                                       write(altfd, gdbbuf, pos);
+                               }
+                       }
+                       seen_hash = 0;
+                       break;
+               }
+       }
+}
+
+static void do_terminal(char *serial_port,
+       int doublerate, int gdb_passthrough,
+       const char *kernel_image, unsigned int kernel_address,
+       const char *cmdline, unsigned int cmdline_address,
+       const char *initrd_image, unsigned int initrd_address,
+       char *log_path)
+{
+       int serialfd;
+       int gdbfd = -1;
+       FILE *logfd = NULL;
+       struct termios my_termios;
+       char c;
+       int recognized;
+       struct pollfd fds[3];
+       int flags;
+       int rsp_pending = 0;
+       
+       /* Open and configure the serial port */
+       if(log_path != NULL) {
+               logfd = fopen(log_path, "a+");
+               if(logfd == NULL) {
+                       perror("Unable to open log file");
+                       return;
+               }
+       }
+
+       serialfd = open(serial_port, O_RDWR|O_NOCTTY);
+       if(serialfd == -1) {
+               perror("Unable to open serial port");
+               return;
+       }
+
+       /* Thanks to Julien Schmitt (GTKTerm) for figuring out the correct parameters
+        * to put into that weird struct.
+        */
+       tcgetattr(serialfd, &my_termios);
+       my_termios.c_cflag = doublerate ? B230400 : B115200;
+       my_termios.c_cflag |= CS8;
+       my_termios.c_cflag |= CREAD;
+       my_termios.c_iflag = IGNPAR | IGNBRK;
+       my_termios.c_cflag |= CLOCAL;
+       my_termios.c_oflag = 0;
+       my_termios.c_lflag = 0;
+       my_termios.c_cc[VTIME] = 0;
+       my_termios.c_cc[VMIN] = 1;
+       tcsetattr(serialfd, TCSANOW, &my_termios);
+       tcflush(serialfd, TCOFLUSH);
+       tcflush(serialfd, TCIFLUSH);
+
+       /* Prepare the fdset for poll() */
+       fds[0].fd = 0;
+       fds[0].events = POLLIN;
+       fds[1].fd = serialfd;
+       fds[1].events = POLLIN;
+
+       recognized = 0;
+       flags = fcntl(serialfd, F_GETFL, 0);
+       while(1) {
+               if(gdbfd == -1 && gdb_passthrough) {
+                       gdbfd = open("/dev/ptmx", O_RDWR);
+                       if(grantpt(gdbfd) != 0) {
+                               perror("grantpt()");
+                               return;
+                       }
+                       if(unlockpt(gdbfd) != 0) {
+                               perror("unlockpt()");
+                               return;
+                       }
+                       printf("[GDB passthrough] use %s as GDB remote device\n",
+                                       ptsname(gdbfd));
+                       fds[2].fd = gdbfd;
+                       fds[2].events = POLLIN;
+               }
+
+               fds[0].revents = 0;
+               fds[1].revents = 0;
+               fds[2].revents = 0;
+
+               /* poll() behaves strangely when the serial port descriptor is in
+                * blocking mode. So work around this.
+                */
+               fcntl(serialfd, F_SETFL, flags|O_NONBLOCK);
+               if(poll(&fds[0], (gdbfd == -1) ? 2 : 3, -1) < 0) break;
+               fcntl(serialfd, F_SETFL, flags);
+
+               if(fds[0].revents & POLLIN) {
+                       if(read(0, &c, 1) <= 0) break;
+                       if(write(serialfd, &c, 1) <= 0) break;
+               }
+
+               if(fds[2].revents & POLLIN) {
+                       rsp_pending = 1;
+                       if(read(gdbfd, &c, 1) <= 0) break;
+                       if(c == '\03') {
+                               /* convert ETX to breaks */
+                               if(debug) {
+                                       fprintf(stderr, "[GDB BREAK]\n");
+                               }
+                               tcsendbreak(serialfd, 0);
+                       } else if(c == '$') {
+                               gdb_process_packet(gdbfd, serialfd, -1);
+                       } else if(c == '+' || c == '-') {
+                               write(serialfd, &c, 1);
+                       } else {
+                               fprintf(stderr, "Internal error (line %d)", __LINE__);
+                               exit(1);
+                       }
+               }
+
+               if(fds[2].revents & POLLHUP) {
+                       /* close and reopen new pair */
+                       close(gdbfd);
+                       gdbfd = -1;
+                       continue;
+               }
+
+               if(fds[1].revents & POLLIN) {
+                       if(read(serialfd, &c, 1) <= 0) break;
+
+                       if(logfd && c && isascii(c)) {
+                               fwrite(&c, sizeof(c), 1, logfd);
+                               if(c == '\n') fflush(logfd);
+                       }
+
+                       if(gdbfd != -1 && rsp_pending && (c == '+' || c == '-')) {
+                               rsp_pending = 0;
+                               write(gdbfd, &c, 1);
+                       } else if(gdbfd != -1 && c == '$') {
+                               gdb_process_packet(serialfd, gdbfd, 0);
+                       } else {
+                               /* write to terminal */
+                               write(0, &c, 1);
+                       
+                               if(kernel_image != NULL) {
+                                       if(c == sfl_magic_req[recognized]) {
+                                               recognized++;
+                                               if(recognized == SFL_MAGIC_LEN) {
+                                                       /* We've got the magic string ! */
+                                                       recognized = 0;
+                                                       answer_magic(serialfd,
+                                                               kernel_image, kernel_address,
+                                                               cmdline, cmdline_address,
+                                                               initrd_image, initrd_address);
+                                               }
+                                       } else {
+                                               if(c == sfl_magic_req[0]) recognized = 1; else recognized = 0;
+                                       }
+                               }
+                       }
+               }
+       }
+       
+       close(serialfd);
+
+       if(gdbfd != -1) close(gdbfd);
+       if(logfd) fclose(logfd);
+}
+
+enum {
+       OPTION_PORT,
+       OPTION_GDB_PASSTHROUGH,
+       OPTION_DOUBLERATE,
+       OPTION_DEBUG,
+       OPTION_KERNEL,
+       OPTION_KERNELADR,
+       OPTION_CMDLINE,
+       OPTION_CMDLINEADR,
+       OPTION_INITRD,
+       OPTION_INITRDADR,
+       OPTION_LOG
+};
+
+static const struct option options[] = {
+       {
+               .name = "port",
+               .has_arg = 1,
+               .val = OPTION_PORT
+       },
+       {
+               .name = "gdb-passthrough",
+               .has_arg = 0,
+               .val = OPTION_GDB_PASSTHROUGH
+       },
+       {
+               .name = "debug",
+               .has_arg = 0,
+               .val = OPTION_DEBUG
+       },
+       {
+               .name = "double-rate",
+               .has_arg = 0,
+               .val = OPTION_DOUBLERATE
+       },
+       {
+               .name = "kernel",
+               .has_arg = 1,
+               .val = OPTION_KERNEL
+       },
+       {
+               .name = "kernel-adr",
+               .has_arg = 1,
+               .val = OPTION_KERNELADR
+       },
+       {
+               .name = "cmdline",
+               .has_arg = 1,
+               .val = OPTION_CMDLINE
+       },
+       {
+               .name = "cmdline-adr",
+               .has_arg = 1,
+               .val = OPTION_CMDLINEADR
+       },
+       {
+               .name = "initrd",
+               .has_arg = 1,
+               .val = OPTION_INITRD
+       },
+       {
+               .name = "initrd-adr",
+               .has_arg = 1,
+               .val = OPTION_INITRDADR
+       },
+       {
+               .name = "log",
+               .has_arg = 1,
+               .val = OPTION_LOG
+       },
+       {
+               .name = NULL
+       }
+};
+
+static void print_usage()
+{
+       fprintf(stderr, "Serial boot program for Milkymist SoC - v. 2.2\n");
+       fprintf(stderr, "Copyright (C) 2007, 2008, 2009, 2010, 2011 Sebastien Bourdeauducq\n");
+       fprintf(stderr, "Copyright (C) 2011 Michael Walle\n");
+       fprintf(stderr, "Copyright (C) 2004 MontaVista Software, Inc\n\n");
+
+       fprintf(stderr, "This program is free software: you can redistribute it and/or modify\n");
+       fprintf(stderr, "it under the terms of the GNU General Public License as published by\n");
+       fprintf(stderr, "the Free Software Foundation, version 3 of the License.\n\n");
+
+       fprintf(stderr, "Usage: flterm --port <port>\n");
+       fprintf(stderr, "              [--double-rate] [--gdb-passthrough] [--debug]\n");
+       fprintf(stderr, "              [--kernel <kernel_image> [--kernel-adr <address>]]\n");
+       fprintf(stderr, "              [--cmdline <cmdline> [--cmdline-adr <address>]]\n");
+       fprintf(stderr, "              [--initrd <initrd_image> [--initrd-adr <address>]]\n");
+       fprintf(stderr, "              [--log <log_file>]\n\n");
+       fprintf(stderr, "Default load addresses:\n");
+       fprintf(stderr, "  kernel:  0x%08x\n", DEFAULT_KERNELADR);
+       fprintf(stderr, "  cmdline: 0x%08x\n", DEFAULT_CMDLINEADR);
+       fprintf(stderr, "  initrd:  0x%08x\n", DEFAULT_INITRDADR);
+}
+
+int main(int argc, char *argv[])
+{
+       int opt;
+       char *serial_port;
+       int doublerate;
+       int gdb_passthrough;
+       char *kernel_image;
+       unsigned int kernel_address;
+       char *cmdline;
+       unsigned int cmdline_address;
+       char *initrd_image;
+       unsigned int initrd_address;
+       char *endptr;
+       char *log_path;
+       struct termios otty, ntty;
+       
+       /* Fetch command line arguments */
+       serial_port = NULL;
+       doublerate = 0;
+       gdb_passthrough = 0;
+       kernel_image = NULL;
+       kernel_address = DEFAULT_KERNELADR;
+       cmdline = NULL;
+       cmdline_address = DEFAULT_CMDLINEADR;
+       initrd_image = NULL;
+       initrd_address = DEFAULT_INITRDADR;
+       log_path = NULL;
+       while((opt = getopt_long(argc, argv, "", options, NULL)) != -1) {
+               if(opt == '?') {
+                       print_usage();
+                       return 1;
+               }
+               switch(opt) {
+                       case OPTION_PORT:
+                               free(serial_port);
+                               serial_port = strdup(optarg);
+                               break;
+                       case OPTION_DOUBLERATE:
+                               doublerate = 1;
+                               break;
+                       case OPTION_DEBUG:
+                               debug = 1;
+                               break;
+                       case OPTION_GDB_PASSTHROUGH:
+                               gdb_passthrough = 1;
+                               break;
+                       case OPTION_KERNEL:
+                               free(kernel_image);
+                               kernel_image = strdup(optarg);
+                               break;
+                       case OPTION_KERNELADR:
+                               kernel_address = strtoul(optarg, &endptr, 0);
+                               if(*endptr != 0) kernel_address = 0;
+                               break;
+                       case OPTION_CMDLINE:
+                               free(cmdline);
+                               cmdline = strdup(optarg);
+                               break;
+                       case OPTION_CMDLINEADR:
+                               cmdline_address = strtoul(optarg, &endptr, 0);
+                               if(*endptr != 0) cmdline_address = 0;
+                               break;
+                       case OPTION_INITRD:
+                               free(initrd_image);
+                               initrd_image = strdup(optarg);
+                               break;
+                       case OPTION_INITRDADR:
+                               initrd_address = strtoul(optarg, &endptr, 0);
+                               if(*endptr != 0) initrd_address = 0;
+                               break;
+                       case OPTION_LOG:
+                               free(log_path);
+                               log_path = strdup(optarg);
+                               break;
+               }
+       }
+
+       if(serial_port == NULL) {
+               print_usage();
+               return 1;
+       }
+
+       /* Banner */
+       printf("[FLTERM] Starting...\n");
+       
+       /* Set up stdin/out */
+       tcgetattr(0, &otty);
+       ntty = otty;
+       ntty.c_lflag &= ~(ECHO | ICANON);
+       tcsetattr(0, TCSANOW, &ntty);
+
+       /* Do the bulk of the work */
+       do_terminal(serial_port, doublerate, gdb_passthrough,
+               kernel_image, kernel_address,
+               cmdline, cmdline_address,
+               initrd_image, initrd_address,
+               log_path);
+       
+       /* Restore stdin/out into their previous state */
+       tcsetattr(0, TCSANOW, &otty);
+       
+       return 0;
+}
diff --git a/tools/mkmmimg.c b/tools/mkmmimg.c
new file mode 100644 (file)
index 0000000..31ff401
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * CRC32 computation tool and Milkymist image file writer
+ * (c) 2009, 2010, Sebastien Bourdeauducq
+ * Released under GNU GPL v3
+ * This file is part of Milkymist.
+ */
+
+/* crc32.c -- compute the CRC-32 of a data stream
+ * Copyright (C) 1995-1998 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+const unsigned int crc_table[256] = {
+       0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+       0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+       0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+       0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+       0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
+       0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+       0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
+       0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+       0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+       0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
+       0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
+       0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+       0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
+       0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
+       0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
+       0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+       0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
+       0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+       0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
+       0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+       0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+       0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
+       0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
+       0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+       0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
+       0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
+       0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
+       0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+       0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
+       0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+       0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
+       0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+       0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+       0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
+       0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
+       0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+       0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
+       0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
+       0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+       0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+       0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
+       0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+       0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
+       0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+       0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+       0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
+       0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
+       0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+       0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
+       0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
+       0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+       0x2d02ef8dL
+};
+
+#define DO1(buf) crc = crc_table[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8);
+#define DO2(buf)  DO1(buf); DO1(buf);
+#define DO4(buf)  DO2(buf); DO2(buf);
+#define DO8(buf)  DO4(buf); DO4(buf);
+
+static unsigned int crc32(const unsigned char *buffer, unsigned int len)
+{
+       unsigned int crc;
+       crc = 0;
+       crc = crc ^ 0xffffffffL;
+       while(len >= 8) {
+               DO8(buffer);
+               len -= 8;
+       }
+       if(len) do {
+               DO1(buffer);
+       } while(--len);
+       return crc ^ 0xffffffffL;
+}
+
+int main(int argc, char *argv[])
+{
+       int fd, fd_out;
+       unsigned char *buffer;
+       unsigned int length;
+       unsigned int crc;
+       
+       if(((argc != 2) && (argc != 3) && (argc != 4))
+               || ((argc > 2) && (strcmp(argv[2], "write") && (strcmp(argv[2], "writelzma"))))) {
+               fprintf(stderr, "Usage: mkmmimg <filename> [write[lzma]] [dest]\n");
+               return 1;
+       }
+       
+       fd = open(argv[1], O_RDONLY);
+       if(fd == -1) {
+               perror("open");
+               return 1;
+       }
+       
+       length = lseek(fd, 0, SEEK_END);
+       lseek(fd, 0, SEEK_SET);
+       
+       buffer = mmap(NULL, length, PROT_READ, MAP_SHARED, fd, 0);
+       if(!buffer) {
+               perror("mmap");
+               close(fd);
+               return 1;
+       }
+       crc = crc32(buffer, length);
+       printf("CRC32 (%s): %08x\n", argv[1], crc);
+
+       if(argc == 3) {
+               /* Write the CRC32 in big endian at the end of the file */
+               char b[4];
+
+               fd_out = open(argv[1], O_WRONLY|O_APPEND);
+               if(fd_out == -1) {
+                       perror("open");
+                       return 1;
+               }
+               b[0] = (crc & 0xff000000) >> 24;
+               b[1] = (crc & 0x00ff0000) >> 16;
+               b[2] = (crc & 0x0000ff00) >> 8;
+               b[3] = (crc & 0x000000ff) >> 0;
+               write(fd_out, &b[0], 4);
+               close(fd_out);
+       }
+
+       if(argc == 4) {
+               /* Write a new file prepended with the size and CRC */
+               char b[4];
+
+               fd_out = open(argv[3], O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
+               if(fd_out == -1) {
+                       perror("open");
+                       return 1;
+               }
+               b[0] = (length & 0xff000000) >> 24;
+               b[1] = (length & 0x00ff0000) >> 16;
+               b[2] = (length & 0x0000ff00) >> 8;
+               b[3] = (length & 0x000000ff) >> 0;
+               if(strcmp(argv[2], "writelzma") == 0)
+                       b[0] |= 0x80;
+               write(fd_out, &b[0], 4);
+               b[0] = (crc & 0xff000000) >> 24;
+               b[1] = (crc & 0x00ff0000) >> 16;
+               b[2] = (crc & 0x0000ff00) >> 8;
+               b[3] = (crc & 0x000000ff) >> 0;
+               write(fd_out, &b[0], 4);
+               write(fd_out, buffer, length);
+               close(fd_out);
+       }
+
+       munmap(buffer, length);
+       close(fd);
+
+       return 0;
+}
diff --git a/tools/sfl.h b/tools/sfl.h
new file mode 100644 (file)
index 0000000..90a205f
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Milkymist SoC
+ * Copyright (C) 2007, 2008, 2009 Sebastien Bourdeauducq
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SFL_H
+#define __SFL_H
+
+#define SFL_MAGIC_LEN 14
+#define SFL_MAGIC_REQ "sL5DdSMmkekro\n"
+#define SFL_MAGIC_ACK "z6IHG7cYDID6o\n"
+
+struct sfl_frame {
+       unsigned char length;
+       unsigned char crc[2];
+       unsigned char cmd;
+       unsigned char payload[255];
+} __attribute__((packed));
+
+/* General commands */
+#define SFL_CMD_ABORT          0x00
+#define SFL_CMD_LOAD           0x01
+#define SFL_CMD_JUMP           0x02
+
+/* Linux-specific commands */
+#define SFL_CMD_CMDLINE                0x03
+#define SFL_CMD_INITRDSTART    0x04
+#define SFL_CMD_INITRDEND      0x05
+
+/* Replies */
+#define SFL_ACK_SUCCESS                'K'
+#define SFL_ACK_CRCERROR       'C'
+#define SFL_ACK_UNKNOWN                'U'
+#define SFL_ACK_ERROR          'E'
+
+#endif /* __SFL_H */