Oops, forgot a file.
[people/oremanj/gpxe.git] / src / core / fwload.c
1 /*
2  * Copyright (C) 2009 Joshua Oreman <oremanj@rwcr.net>
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 /**
20  * @file
21  *
22  * Implements an ad-hoc protocol (host end util/fireserve.c) for
23  * chainloading gPXE over FireWire, to make testing faster.
24  */
25
26 #include <stdint.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <errno.h>
31 #include <gpxe/uri.h>
32 #include <gpxe/iobuf.h>
33 #include <gpxe/xfer.h>
34 #include <gpxe/process.h>
35 #include <gpxe/open.h>
36 #include <gpxe/malloc.h>
37 #include <gpxe/fwtrans.h>
38 #include <gpxe/uaccess.h>
39
40 #define FWLOAD_BUFSIZE 1024
41
42 static struct fwtrans_connection fwload_link;
43
44 /* We only support one FW connection at a time */
45 static int fwload_in_use;
46
47 struct fwload_state {
48         /* Reference count */
49         struct refcnt refcnt;
50
51         /* xfer_interface we're using */
52         struct xfer_interface xfer;
53
54         /* Link-pumping process */
55         struct process process;
56
57         /* Length we're supposed to get in total */
58         size_t file_length;
59
60         /* Length we got so far */
61         size_t rx_length;
62 };
63
64 static void fwload_done(struct fwload_state *state, int rc)
65 {
66         struct fwtrans_connection *l = &fwload_link;
67         l->response = l->idx_get = l->idx_put = 0;
68         l->request = FWTRANS_NAK;
69
70         fwload_in_use--;
71
72         if (rc == 0 && state->rx_length != state->file_length) {
73                 DBG("FW %p incorrect length %zd, should be %zd\n",
74                     state, state->rx_length, state->file_length);
75                 rc = -EIO;
76         }
77
78
79         process_del(&state->process);
80         xfer_nullify(&state->xfer);
81         xfer_close(&state->xfer, rc);
82 }
83
84 static void fwload_close(struct xfer_interface *xfer, int rc)
85 {
86         struct fwload_state *state = container_of(xfer, struct fwload_state, xfer);
87         fwload_done(state, rc);
88 }
89
90 static void fwload_free(struct refcnt *refcnt) 
91 {
92         struct fwload_state *state = container_of(refcnt, struct fwload_state, refcnt);
93         free(state);
94 }
95
96 /*
97  * State: If (request & SYN) we're waiting for an open.
98  * If (request & FIN) we're waiting for a close.
99  * Otherwise, read.
100  */
101
102 static void fwload_pump(struct process *proc) 
103 {
104         struct fwload_state *state = container_of(proc, struct fwload_state, process);
105         struct fwtrans_connection *l = &fwload_link;
106
107         if (l->response & FWTRANS_NAK) {
108                 DBG("FW %p host sent NAK\n", state);
109                 fwload_done(state, -EIO);
110                 return;
111         }
112
113         if (l->request & FWTRANS_SYN) {
114                 if (l->response & FWTRANS_ACK) {
115                         state->file_length = FWTRANS_SIZE(l->response);
116                         l->request &= ~FWTRANS_SYN;
117                 }
118                 return;
119         }
120
121         if (l->request & FWTRANS_FIN) {
122                 if (l->response == 0)
123                         fwload_done(state, 0);
124                 return;
125         }
126
127         /* Normal read loop */
128
129         /* Save `put', which is volatile, so as not to get confused if
130            it gets changed while we work. */
131         u32 put = l->idx_put;
132         if (l->idx_get > put) {
133                 /* The buffer is wrapped; send here-to-end
134                    first. */
135                 xfer_deliver_raw(&state->xfer, l->buffer + l->idx_get,
136                                  l->buffer_size - l->idx_get);
137                 state->rx_length += l->buffer_size - l->idx_get;
138                 l->idx_get = 0;
139         }
140         if (l->idx_get < put) {
141                 /* Send either the second part of a wrapped buffer or
142                    the entirety of an unwrapped. */
143                 xfer_deliver_raw(&state->xfer, l->buffer + l->idx_get,
144                                  put - l->idx_get);
145                 state->rx_length += put - l->idx_get;
146                 l->idx_get = put;
147         }
148
149         if (l->response & FWTRANS_FIN)
150                 l->request |= FWTRANS_FIN;
151 }
152
153 static struct xfer_interface_operations fwload_xfer_ops = {
154         .close          = fwload_close,
155         .vredirect      = ignore_xfer_vredirect,
156         .window         = unlimited_xfer_window,
157         .alloc_iob      = default_xfer_alloc_iob,
158         .deliver_iob    = xfer_deliver_as_raw,
159         .deliver_raw    = ignore_xfer_deliver_raw,
160 };
161
162 static int fwload_open(struct xfer_interface *xfer, struct uri *uri) 
163 {
164         struct fwload_state *state;
165         struct fwtrans_connection *l = &fwload_link;
166         const char *reqfile;
167
168         if (fwload_in_use)
169                 return -EBUSY;
170
171         /* Set up structures */
172         state = zalloc(sizeof(*state));
173         if (!state)
174                 return -ENOMEM;
175
176         /* In case we've done this before... */
177         free_dma(l->buffer, l->buffer_size);
178
179         l->buffer_size = FWLOAD_BUFSIZE;
180         l->buffer = malloc_dma(l->buffer_size, 16);
181         if (!l->buffer) {
182                 free(state);
183                 return -ENOMEM;
184         }
185         
186         l->buffer_addr = virt_to_phys(l->buffer);
187         l->response = 0;
188         l->idx_get = l->idx_put = 0;
189         l->magic = FWTRANS_MAGIC;
190
191         state->rx_length = 0;
192
193         printf("fwload: link structure at %08lX\n", virt_to_phys(l));
194
195         if (uri->path)
196                 reqfile = uri->path;
197         else if (uri->host)
198                 reqfile = uri->host;
199         else
200                 reqfile = NULL;
201
202         if (!reqfile) {
203                 l->request = 0;
204         } else {
205                 strcpy((char *)l->buffer, reqfile);
206                 l->request = strlen(reqfile);
207         }
208
209         state->refcnt.free = fwload_free;
210         xfer_init(&state->xfer, &fwload_xfer_ops, &state->refcnt);
211         process_init(&state->process, fwload_pump, &state->refcnt);
212
213         xfer_plug_plug(&state->xfer, xfer);
214         ref_put(&state->refcnt);
215
216         l->request |= FWTRANS_SYN;
217         fwload_in_use++;
218
219         return 0;
220 }
221
222 struct uri_opener fwload_uri_opener __uri_opener = {
223         .scheme = "fwload",
224         .open   = fwload_open,
225 };