Add protocol for loading images over FireWire. Mainly for development.
authorJoshua Oreman <oremanj@xenon.get-linux.org>
Mon, 13 Apr 2009 04:19:32 +0000 (21:19 -0700)
committerJoshua Oreman <oremanj@xenon.get-linux.org>
Mon, 13 Apr 2009 04:39:04 +0000 (21:39 -0700)
src/Makefile
src/Makefile.housekeeping
src/config/general.h
src/core/config.c
src/util/fireserve.c [new file with mode: 0644]

index 202beb0..4d1e89d 100644 (file)
@@ -42,6 +42,7 @@ ELF2EFI64     := ./util/elf2efi64
 EFIROM         := ./util/efirom
 ICCFIX         := ./util/iccfix
 FIREBUG                := ./util/firebug
+FIRESERVE      := ./util/fireserve
 DOXYGEN                := doxygen
 BINUTILS_DIR   := /usr
 BFD_DIR                := $(BINUTILS_DIR)
index 06a0e21..2a41593 100644 (file)
@@ -838,6 +838,14 @@ $(FIREBUG) : util/firebug.c util/fwtools.c $(MAKEDEPS)
        $(Q)$(HOST_CC) -idirafter include -O2 -o $@ $< util/fwtools.c -lraw1394
 CLEANUP += $(FIREBUG)
 
+###############################################################################
+#
+# The FireWire image server
+#
+$(FIRESERVE) : util/fireserve.c util/fwtools.c $(MAKEDEPS)
+       $(QM)$(ECHO) "  [HOSTCC] $@"
+       $(Q)$(HOST_CC) -idirafter include -O2 -o $@ $< util/fwtools.c -lraw1394
+
 ###############################################################################
 #
 # Auto-incrementing build serial number.  Append "bs" to your list of
index 5449954..ff45cec 100644 (file)
@@ -52,6 +52,7 @@
 #undef DOWNLOAD_PROTO_TFTM     /* Multicast Trivial File Transfer Protocol */
 #undef DOWNLOAD_PROTO_SLAM     /* Scalable Local Area Multicast */
 #undef DOWNLOAD_PROTO_FSP      /* FSP? */
+#undef DOWNLOAD_PROTO_FWLOAD   /* Load over FireWire (for development) */
 
 /*
  * SAN boot protocols
index 8f4f0a2..6a22d1d 100644 (file)
@@ -96,6 +96,10 @@ REQUIRE_OBJECT ( tftm );
 #ifdef DOWNLOAD_PROTO_SLAM
 REQUIRE_OBJECT ( slam );
 #endif
+#ifdef DOWNLOAD_PROTO_FWLOAD
+REQUIRE_OBJECT ( fwload );
+REQUIRE_OBJECT ( ohci1394dbg );
+#endif
 
 /*
  * Drag in all requested SAN boot protocols
diff --git a/src/util/fireserve.c b/src/util/fireserve.c
new file mode 100644 (file)
index 0000000..2b4fad0
--- /dev/null
@@ -0,0 +1,395 @@
+/*
+ * Copyright (C) 2009 Joshua Oreman <oremanj@rwcr.net>
+ *
+ * `fireserve' is the client side of gPXE's ad-hoc FireWire
+ * image-loading protocol. It's useful for developing network card
+ * drivers, as it provides most of the convenience of network
+ * chainloading without actually using a network. It probably doesn't
+ * have much use for the typical end-user.
+ *
+ * When you compile GDB with fwload:// URL support
+ * (DOWNLOAD_PROTO_FWLOAD), attempting to open such a URL will cause a
+ * message to be printed listing the memory address of an internal
+ * link structure. gPXE will then wait for as long as is necessary for
+ * you to start up fireserve, passing that address as an argument so
+ * that it can establish a connection with gPXE. By default it will
+ * serve files within its current directory, so if you run it from
+ * util/ you might do fwload://host/../bin/gpxe.lkrn. (The string used
+ * for "host" is arbitrary, but you need something there if your file
+ * path contains slashes.) Pass -A for absolute paths
+ * (fwload://host/tftpboot/gpxe.lkrn) or -C <dir> to make paths
+ * relative to a directory other than the current one.
+ *
+ * You can load multiple files from one fireserve session. I'm not
+ * sure this will ever be used, but it seemed sensible to implement.
+ *
+ * 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; either version 2 of the
+ * License, or any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <errno.h>
+#include <poll.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "fwtools.h"
+
+#define USERSPACE
+#include <gpxe/fwtrans.h>
+
+#define FW_DEFAULT_POLLDELAY   20
+
+static struct fwtrans_connection fw_link;
+
+static raw1394handle_t fw_handle;
+static nodeid_t                fw_target;
+static u32             fw_addrbase;
+
+static int verbose;
+
+#define dbg(level, fmt...) do { if (verbose >= level) printf(fmt); } while(0)
+
+/* Write fw_link.elem locally to fwload_link.elem remotely. */
+#define FW_WRITE_LINKELEM(elem) raw1394_write(fw_handle, fw_target, \
+    fw_addrbase + offsetof(struct fwtrans_connection, elem), 4, \
+    (quadlet_t *)((u8 *)&fw_link + offsetof(struct fwtrans_connection, elem)))
+
+/* Writes from fw_link the parts we might change. */
+static void fw_write_link()
+{
+       FW_WRITE_LINKELEM(response);
+       FW_WRITE_LINKELEM(idx_put);
+       dbg(3, "-- write response=%08x idx_put=%d\n", fw_link.response,
+           fw_link.idx_put);
+}
+
+/* Read fwload_link.elem remotely into fw_link.elem locally. */
+#define FW_READ_LINKELEM(elem) raw1394_read(fw_handle, fw_target, \
+    fw_addrbase + offsetof(struct fwtrans_connection, elem), 4, \
+    (quadlet_t *)((u8 *)&fw_link + offsetof(struct fwtrans_connection, elem)))
+
+/* Reads into fw_link the parts the remote host might change. */
+static void fw_read_link()
+{
+       FW_READ_LINKELEM(request);
+       FW_READ_LINKELEM(idx_get);
+       dbg(3, "-- read  request=%08x idx_get=%d\n", fw_link.request,
+           fw_link.idx_get);
+}
+
+/* Reads everything from the remote host's structure into our
+   fw_link. */
+static void fw_read_link_initial()
+{
+       quadlet_t *mylink = (quadlet_t *)&fw_link;
+       int i;
+
+       for (i = 0; i < sizeof(fw_link); i += 4) {
+               raw1394_read(fw_handle, fw_target, fw_addrbase + i, 4, mylink);
+               dbg(3, "read  [addr+%03x] -> %08x\n", i, *mylink);
+               mylink++;
+       }
+}
+
+static int fw_write_avail()
+{
+       return (fw_link.buffer_size - 1) -
+               ((fw_link.idx_put - fw_link.idx_get) & (fw_link.buffer_size - 1));
+}
+
+u8 read_buffer[1024];
+
+/* returns -1 for error on our side, 0 to finish with this file, 1 to
+   keep going, and adds the number of new bytes sent to *sent_bytes */
+static int pump_link (int filedes, int *sent_bytes) 
+{
+       int avail, len;
+
+       /* NAK set: error, give up */
+       if (fw_link.request & FWTRANS_NAK) {
+               fprintf(stderr, "fireserve: Remote gave NAK, "
+                       "giving up on this file\n");
+               return 0;
+       }
+
+       /* SYN set: they haven't acknowledged our setup yet */
+       if (fw_link.request & FWTRANS_SYN)
+               return 1;
+
+       /* FIN set: finishing up, acknowledge it */
+       if (fw_link.request & FWTRANS_FIN) {
+               fw_link.response = 0;
+               return 0;
+       }
+       
+       /* Remote doesn't set ACK in the protocol, so anything else is
+          the normal read/write loop. */
+
+       fw_link.response &= ~FWTRANS_ACK;
+
+       avail = fw_write_avail();
+       dbg(2, "get=%d put=%d avail=%d\n", fw_link.idx_get, fw_link.idx_put, avail);
+
+       if (avail == 0)
+               return 1;
+
+       len = read(filedes, read_buffer, avail);
+
+       if (len > 0) {
+               fw_write_ring(fw_handle, fw_target, fw_link.buffer_addr,
+                             &fw_link.idx_put, fw_link.buffer_size,
+                             read_buffer, len);
+               if (sent_bytes)
+                       *sent_bytes += len;
+       } else if (len < 0)
+               return -1;
+       else
+               fw_link.response |= FWTRANS_FIN;
+}
+
+static int fw_set_port(int port) 
+{
+       do {
+               struct raw1394_portinfo ports[8];
+               int nports;
+               nports = raw1394_get_port_info(fw_handle, ports, 8);
+               if (port >= nports) {
+                       fprintf(stderr, "fireserve: specified port out of range\n");
+                       return -1;
+               }
+               if (raw1394_set_port(fw_handle, port) == 0) {
+                       dbg(1, "Opened %s port %d\n", ports[port].name, port);
+                       return 0;
+               }
+       } while (errno == ESTALE);
+
+       perror("fireserve: setting port");
+       return -1;
+}
+
+static int fw_connect()
+{
+       /* Look for the magic structure at all non-host nodes. */
+       int i, nodes, local;
+       nodes = raw1394_get_nodecount(fw_handle);
+       local = NODENR(raw1394_get_local_id(fw_handle));
+
+       dbg(1, "Connecting to gPXE...\n");
+
+       for (i = 0; i < nodes; i++) {
+               if (i == local) {
+                       dbg(1, "  Skipping local node %d\n", local);
+                       continue;
+               }
+
+               fw_target = NODEID(i);
+               fw_read_link_initial();
+               if (fw_link.magic != FWTRANS_MAGIC) {
+                       dbg(1, "  Node %d is not gPXE\n", i);
+               } else {
+                       printf("Connected to gPXE on node %d.\n", i);
+                       return 0;
+               }
+       }
+
+       fprintf(stderr, "fireserve: could not find a valid gPXE node at that address\n");
+       return -1;
+}
+
+static void usage(int exitcode)
+{
+       fprintf(stderr,
+"Usage: fireserve [-A | -C dir] [-d delay] [-f fwport] [-v [-v [-v]]] address\n"
+"\n"
+"  Serves up images to gPXE over FireWire.\n"
+"\n"
+"    -A          Treat incoming paths as absolute.\n"
+"    -C dir      Change to `dir' and serve files from there. If not specified,\n"
+"                files will be served from the current directory.\n"
+"    -d delay    Wait `delay' milliseconds between polls of the target\n"
+"                for new data. 0 ms is acceptable but will use a lot of\n"
+"                CPU time. (default 20 ms)\n"
+"    -f fwport   Connect using the specified FireWire port (default 0).\n"
+"    -v          Increase verbosity; may be specified multiple times.\n"
+"\n"
+"  The `address' argument is required to initiate the connection to\n"
+"  gPXE. It should be eight hexadecimal digits without a preceding `0x',\n"
+"  in the same form that gPXE prints it.\n"
+"\n");
+
+       if ((fw_handle = raw1394_new_handle()) != NULL) {
+               fw_print_status(fw_handle);
+               raw1394_destroy_handle(fw_handle);
+       } else {
+               fprintf(stderr, "Unable to get FireWire status. Ensure that "
+                       "raw1394 is loaded and you have\n"
+                       "appropriate permissions on /dev/raw1394.\n");
+       }
+       exit(exitcode);
+}
+
+int main(int argc, char **argv)
+{
+       int abspath = 0;
+       int fwport = 0;
+       int sent_bytes = 0;
+       int polldelay = FW_DEFAULT_POLLDELAY;
+       struct pollfd fds[] = {
+               /* 1394 */ { .fd = 0, .events = POLLIN | POLLPRI, .revents = 0 },
+       };
+       int opt;
+       u8 buf[256];
+       int filedes = -1;
+       struct stat st;
+
+       if (argc >= 2 && !strcmp(argv[1], "--help"))
+               usage(0);
+       
+       while ((opt = getopt(argc, argv, "AC:d:f:vh")) != -1) {
+               switch (opt) {
+               case 'A':       /* Treat requested paths as absolute */
+                       abspath = 1;
+                       break;
+               case 'C':       /* Serve files from given directory */
+                       if (chdir(optarg) < 0) {
+                               perror("fireserve: chdir");
+                               return 1;
+                       }
+                       break;
+               case 'd':       /* Poll delay in ms */
+                       if (!isdigit(*optarg))
+                               usage(1);
+                       polldelay = atoi(optarg);
+                       break;
+               case 'f':       /* FireWire port to connect on */
+                       if (!isdigit(*optarg))
+                               usage(1);
+                       fwport = atoi(optarg);
+                       break;
+               case 'v':       /* Increase verbosity */
+                       verbose++;
+                       break;
+               case 'h':       /* Help */
+                       usage(0);
+               default:        /* Something unrecognized */
+                       usage(1);
+               }
+       }
+
+       if (verbose >= 3)
+               fw_set_memdebug(1);
+
+       argc -= optind;
+       argv += optind;
+
+       fw_handle = raw1394_new_handle();
+       if (!fw_handle) {
+               perror("fireserve: error initializing lib1394");
+               fprintf(stderr, "You may have to modprobe raw1394 and/or give "
+                       "yourself appropriate permissions on /dev/raw1394.\n");
+               return 2;
+       }
+
+       fw_print_status(fw_handle);
+       printf("\n");
+
+       if (!argc || !sscanf(argv[0], "%08X", &fw_addrbase)) {
+               fprintf(stderr, "You must specify a valid link address. "
+                       "Try `firebug --help' for more information.\n");
+               raw1394_destroy_handle(fw_handle);
+               return 1;
+       }
+
+       if (fw_set_port(fwport) != 0)
+               return 2;
+       
+       if (fw_connect() != 0)
+               return 2;
+
+       fds[0].fd = raw1394_get_fd(fw_handle);
+       
+       while (poll(fds, 1, polldelay) >= 0) {
+               if (fds[0].revents & POLLIN) {
+                       raw1394_loop_iterate(fw_handle);
+               }
+
+               fw_read_link();
+
+               if (filedes < 0 && (fw_link.request & FWTRANS_SYN) &&
+                   !(fw_link.response & FWTRANS_NAK)) {
+                       int fnlen = FWTRANS_SIZE(fw_link.request) + 1;
+                       char *namep = buf + 1;
+                       quadlet_t *fptr;
+                       int i;
+                       
+                       if (fnlen + 1 > sizeof(buf)) {
+                               fprintf(stderr, "fireserve: filename too long (%d)\n", fnlen);
+                               fw_link.response = FWTRANS_NAK;
+                               continue;
+                       }
+
+                       raw1394_read(fw_handle, fw_target, fw_link.buffer_addr,
+                                    fnlen, (quadlet_t *)namep);
+                       namep[fnlen] = 0;
+
+                       /* fwload://host/bin/gpxe.lkrn => ./bin/gpxe.lkrn */
+                       if (*namep == '/' && !abspath)
+                               *--namep = '.';
+
+                       if (filedes >= 0) close(filedes);
+                       filedes = open(namep, O_RDONLY);
+                       if (filedes < 0) {
+                               perror(namep);
+                               fw_link.response = FWTRANS_NAK;
+                       } else if (fstat(filedes, &st) < 0) {
+                               perror("fstat");
+                               fw_link.response = FWTRANS_NAK;                         
+                       } else {
+                               dbg(1, "Serving %s (%d bytes)...\n",
+                                   namep, st.st_size);
+                               fw_link.response = FWTRANS_ACK | st.st_size;
+                       }
+                       sent_bytes = 0;
+               }
+
+               if (filedes >= 0) {
+                       int rc = pump_link(filedes, &sent_bytes);
+                       if (rc < 0) {
+                               perror("read");
+                               fw_link.response = FWTRANS_NAK;
+                       }
+                       if (rc <= 0) {
+                               close(filedes);
+                               filedes = -1;
+                               dbg(1, "Done, closing file\n");
+                               fw_link.response = 0;
+                       }
+               }
+
+               if (filedes >= 0 && verbose >= 1)
+                       fprintf(stderr, "\r% 8d/% 8d                  \r",
+                               sent_bytes, st.st_size);
+
+               fw_write_link();
+       }
+
+       raw1394_destroy_handle(fw_handle);
+}