Remove ref to removed memdebug code
[people/oremanj/gpxe.git] / src / util / fireserve.c
1 /*
2  * Copyright (C) 2009 Joshua Oreman <oremanj@rwcr.net>
3  *
4  * `fireserve' is the client side of gPXE's ad-hoc FireWire
5  * image-loading protocol. It's useful for developing network card
6  * drivers, as it provides most of the convenience of network
7  * chainloading without actually using a network. It probably doesn't
8  * have much use for the typical end-user.
9  *
10  * When you compile GDB with fwload:// URL support
11  * (DOWNLOAD_PROTO_FWLOAD), attempting to open such a URL will cause a
12  * message to be printed listing the memory address of an internal
13  * link structure. gPXE will then wait for as long as is necessary for
14  * you to start up fireserve, passing that address as an argument so
15  * that it can establish a connection with gPXE. By default it will
16  * serve files within its current directory, so if you run it from
17  * util/ you might do fwload://host/../bin/gpxe.lkrn. (The string used
18  * for "host" is arbitrary, but you need something there if your file
19  * path contains slashes.) Pass -A for absolute paths
20  * (fwload://host/tftpboot/gpxe.lkrn) or -C <dir> to make paths
21  * relative to a directory other than the current one.
22  *
23  * You can load multiple files from one fireserve session. I'm not
24  * sure this will ever be used, but it seemed sensible to implement.
25  *
26  * This program is free software; you can redistribute it and/or
27  * modify it under the terms of the GNU General Public License as
28  * published by the Free Software Foundation; either version 2 of the
29  * License, or any later version.
30  *
31  * This program is distributed in the hope that it will be useful, but
32  * WITHOUT ANY WARRANTY; without even the implied warranty of
33  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
34  * General Public License for more details.
35  *
36  * You should have received a copy of the GNU General Public License
37  * along with this program; if not, write to the Free Software
38  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
39  */
40
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <stddef.h>
44 #include <string.h>
45 #include <signal.h>
46 #include <unistd.h>
47 #include <errno.h>
48 #include <poll.h>
49 #include <fcntl.h>
50 #include <sys/types.h>
51 #include <sys/stat.h>
52
53 #include "fwtools.h"
54
55 #define USERSPACE
56 #include <gpxe/fwtrans.h>
57
58 #define FW_DEFAULT_POLLDELAY    20
59
60 static struct fwtrans_connection fw_link;
61
62 static raw1394handle_t  fw_handle;
63 static nodeid_t         fw_target;
64 static u32              fw_addrbase;
65
66 static int verbose;
67
68 #define dbg(level, fmt...) do { if (verbose >= level) printf(fmt); } while(0)
69
70 /* Write fw_link.elem locally to fwload_link.elem remotely. */
71 #define FW_WRITE_LINKELEM(elem) raw1394_write(fw_handle, fw_target, \
72     fw_addrbase + offsetof(struct fwtrans_connection, elem), 4, \
73     (quadlet_t *)((u8 *)&fw_link + offsetof(struct fwtrans_connection, elem)))
74
75 /* Writes from fw_link the parts we might change. */
76 static void fw_write_link()
77 {
78         FW_WRITE_LINKELEM(response);
79         FW_WRITE_LINKELEM(idx_put);
80         dbg(3, "-- write response=%08x idx_put=%d\n", fw_link.response,
81             fw_link.idx_put);
82 }
83
84 /* Read fwload_link.elem remotely into fw_link.elem locally. */
85 #define FW_READ_LINKELEM(elem) raw1394_read(fw_handle, fw_target, \
86     fw_addrbase + offsetof(struct fwtrans_connection, elem), 4, \
87     (quadlet_t *)((u8 *)&fw_link + offsetof(struct fwtrans_connection, elem)))
88
89 /* Reads into fw_link the parts the remote host might change. */
90 static void fw_read_link()
91 {
92         FW_READ_LINKELEM(request);
93         FW_READ_LINKELEM(idx_get);
94         dbg(3, "-- read  request=%08x idx_get=%d\n", fw_link.request,
95             fw_link.idx_get);
96 }
97
98 /* Reads everything from the remote host's structure into our
99    fw_link. */
100 static void fw_read_link_initial()
101 {
102         quadlet_t *mylink = (quadlet_t *)&fw_link;
103         int i;
104
105         for (i = 0; i < sizeof(fw_link); i += 4) {
106                 raw1394_read(fw_handle, fw_target, fw_addrbase + i, 4, mylink);
107                 dbg(3, "read  [addr+%03x] -> %08x\n", i, *mylink);
108                 mylink++;
109         }
110 }
111
112 static int fw_write_avail()
113 {
114         return (fw_link.buffer_size - 1) -
115                 ((fw_link.idx_put - fw_link.idx_get) & (fw_link.buffer_size - 1));
116 }
117
118 u8 read_buffer[1024];
119
120 /* returns -1 for error on our side, 0 to finish with this file, 1 to
121    keep going, and adds the number of new bytes sent to *sent_bytes */
122 static int pump_link (int filedes, int *sent_bytes) 
123 {
124         int avail, len;
125
126         /* NAK set: error, give up */
127         if (fw_link.request & FWTRANS_NAK) {
128                 fprintf(stderr, "fireserve: Remote gave NAK, "
129                         "giving up on this file\n");
130                 return 0;
131         }
132
133         /* SYN set: they haven't acknowledged our setup yet */
134         if (fw_link.request & FWTRANS_SYN)
135                 return 1;
136
137         /* FIN set: finishing up, acknowledge it */
138         if (fw_link.request & FWTRANS_FIN) {
139                 fw_link.response = 0;
140                 return 0;
141         }
142         
143         /* Remote doesn't set ACK in the protocol, so anything else is
144            the normal read/write loop. */
145
146         fw_link.response &= ~FWTRANS_ACK;
147
148         avail = fw_write_avail();
149         dbg(2, "get=%d put=%d avail=%d\n", fw_link.idx_get, fw_link.idx_put, avail);
150
151         if (avail == 0)
152                 return 1;
153
154         len = read(filedes, read_buffer, avail);
155
156         if (len > 0) {
157                 fw_write_ring(fw_handle, fw_target, fw_link.buffer_addr,
158                               &fw_link.idx_put, fw_link.buffer_size,
159                               read_buffer, len);
160                 if (sent_bytes)
161                         *sent_bytes += len;
162         } else if (len < 0)
163                 return -1;
164         else
165                 fw_link.response |= FWTRANS_FIN;
166 }
167
168 static int fw_set_port(int port) 
169 {
170         do {
171                 struct raw1394_portinfo ports[8];
172                 int nports;
173                 nports = raw1394_get_port_info(fw_handle, ports, 8);
174                 if (port >= nports) {
175                         fprintf(stderr, "fireserve: specified port out of range\n");
176                         return -1;
177                 }
178                 if (raw1394_set_port(fw_handle, port) == 0) {
179                         dbg(1, "Opened %s port %d\n", ports[port].name, port);
180                         return 0;
181                 }
182         } while (errno == ESTALE);
183
184         perror("fireserve: setting port");
185         return -1;
186 }
187
188 static int fw_connect()
189 {
190         /* Look for the magic structure at all non-host nodes. */
191         int i, nodes, local;
192         nodes = raw1394_get_nodecount(fw_handle);
193         local = NODENR(raw1394_get_local_id(fw_handle));
194
195         dbg(1, "Connecting to gPXE...\n");
196
197         for (i = 0; i < nodes; i++) {
198                 if (i == local) {
199                         dbg(1, "  Skipping local node %d\n", local);
200                         continue;
201                 }
202
203                 fw_target = NODEID(i);
204                 fw_read_link_initial();
205                 if (fw_link.magic != FWTRANS_MAGIC) {
206                         dbg(1, "  Node %d is not gPXE\n", i);
207                 } else {
208                         printf("Connected to gPXE on node %d.\n", i);
209                         return 0;
210                 }
211         }
212
213         fprintf(stderr, "fireserve: could not find a valid gPXE node at that address\n");
214         return -1;
215 }
216
217 static void usage(int exitcode)
218 {
219         fprintf(stderr,
220 "Usage: fireserve [-A | -C dir] [-d delay] [-f fwport] [-v [-v [-v]]] address\n"
221 "\n"
222 "  Serves up images to gPXE over FireWire.\n"
223 "\n"
224 "    -A          Treat incoming paths as absolute.\n"
225 "    -C dir      Change to `dir' and serve files from there. If not specified,\n"
226 "                files will be served from the current directory.\n"
227 "    -d delay    Wait `delay' milliseconds between polls of the target\n"
228 "                for new data. 0 ms is acceptable but will use a lot of\n"
229 "                CPU time. (default 20 ms)\n"
230 "    -f fwport   Connect using the specified FireWire port (default 0).\n"
231 "    -v          Increase verbosity; may be specified multiple times.\n"
232 "\n"
233 "  The `address' argument is required to initiate the connection to\n"
234 "  gPXE. It should be eight hexadecimal digits without a preceding `0x',\n"
235 "  in the same form that gPXE prints it.\n"
236 "\n");
237
238         if ((fw_handle = raw1394_new_handle()) != NULL) {
239                 fw_print_status(fw_handle);
240                 raw1394_destroy_handle(fw_handle);
241         } else {
242                 fprintf(stderr, "Unable to get FireWire status. Ensure that "
243                         "raw1394 is loaded and you have\n"
244                         "appropriate permissions on /dev/raw1394.\n");
245         }
246         exit(exitcode);
247 }
248
249 int main(int argc, char **argv)
250 {
251         int abspath = 0;
252         int fwport = 0;
253         int sent_bytes = 0;
254         int polldelay = FW_DEFAULT_POLLDELAY;
255         struct pollfd fds[] = {
256                 /* 1394 */ { .fd = 0, .events = POLLIN | POLLPRI, .revents = 0 },
257         };
258         int opt;
259         u8 buf[256];
260         int filedes = -1;
261         struct stat st;
262
263         if (argc >= 2 && !strcmp(argv[1], "--help"))
264                 usage(0);
265         
266         while ((opt = getopt(argc, argv, "AC:d:f:vh")) != -1) {
267                 switch (opt) {
268                 case 'A':       /* Treat requested paths as absolute */
269                         abspath = 1;
270                         break;
271                 case 'C':       /* Serve files from given directory */
272                         if (chdir(optarg) < 0) {
273                                 perror("fireserve: chdir");
274                                 return 1;
275                         }
276                         break;
277                 case 'd':       /* Poll delay in ms */
278                         if (!isdigit(*optarg))
279                                 usage(1);
280                         polldelay = atoi(optarg);
281                         break;
282                 case 'f':       /* FireWire port to connect on */
283                         if (!isdigit(*optarg))
284                                 usage(1);
285                         fwport = atoi(optarg);
286                         break;
287                 case 'v':       /* Increase verbosity */
288                         verbose++;
289                         break;
290                 case 'h':       /* Help */
291                         usage(0);
292                 default:        /* Something unrecognized */
293                         usage(1);
294                 }
295         }
296
297         argc -= optind;
298         argv += optind;
299
300         fw_handle = raw1394_new_handle();
301         if (!fw_handle) {
302                 perror("fireserve: error initializing lib1394");
303                 fprintf(stderr, "You may have to modprobe raw1394 and/or give "
304                         "yourself appropriate permissions on /dev/raw1394.\n");
305                 return 2;
306         }
307
308         fw_print_status(fw_handle);
309         printf("\n");
310
311         if (!argc || !sscanf(argv[0], "%08X", &fw_addrbase)) {
312                 fprintf(stderr, "You must specify a valid link address. "
313                         "Try `firebug --help' for more information.\n");
314                 raw1394_destroy_handle(fw_handle);
315                 return 1;
316         }
317
318         if (fw_set_port(fwport) != 0)
319                 return 2;
320         
321         if (fw_connect() != 0)
322                 return 2;
323
324         fds[0].fd = raw1394_get_fd(fw_handle);
325         
326         while (poll(fds, 1, polldelay) >= 0) {
327                 if (fds[0].revents & POLLIN) {
328                         raw1394_loop_iterate(fw_handle);
329                 }
330
331                 fw_read_link();
332
333                 if (filedes < 0 && (fw_link.request & FWTRANS_SYN) &&
334                     !(fw_link.response & FWTRANS_NAK)) {
335                         int fnlen = FWTRANS_SIZE(fw_link.request) + 1;
336                         char *namep = buf + 1;
337                         quadlet_t *fptr;
338                         int i;
339                         
340                         if (fnlen + 1 > sizeof(buf)) {
341                                 fprintf(stderr, "fireserve: filename too long (%d)\n", fnlen);
342                                 fw_link.response = FWTRANS_NAK;
343                                 continue;
344                         }
345
346                         raw1394_read(fw_handle, fw_target, fw_link.buffer_addr,
347                                      fnlen, (quadlet_t *)namep);
348                         namep[fnlen] = 0;
349
350                         /* fwload://host/bin/gpxe.lkrn => ./bin/gpxe.lkrn */
351                         if (*namep == '/' && !abspath)
352                                 *--namep = '.';
353
354                         if (filedes >= 0) close(filedes);
355                         filedes = open(namep, O_RDONLY);
356                         if (filedes < 0) {
357                                 perror(namep);
358                                 fw_link.response = FWTRANS_NAK;
359                         } else if (fstat(filedes, &st) < 0) {
360                                 perror("fstat");
361                                 fw_link.response = FWTRANS_NAK;                         
362                         } else {
363                                 dbg(1, "Serving %s (%d bytes)...\n",
364                                     namep, st.st_size);
365                                 fw_link.response = FWTRANS_ACK | st.st_size;
366                         }
367                         sent_bytes = 0;
368                 }
369
370                 if (filedes >= 0) {
371                         int rc = pump_link(filedes, &sent_bytes);
372                         if (rc < 0) {
373                                 perror("read");
374                                 fw_link.response = FWTRANS_NAK;
375                         }
376                         if (rc <= 0) {
377                                 close(filedes);
378                                 filedes = -1;
379                                 dbg(1, "Done, closing file\n");
380                                 fw_link.response = 0;
381                         }
382                 }
383
384                 if (filedes >= 0 && verbose >= 1)
385                         fprintf(stderr, "\r% 8d/% 8d                  \r",
386                                 sent_bytes, st.st_size);
387
388                 fw_write_link();
389         }
390
391         raw1394_destroy_handle(fw_handle);
392 }