2008-12-12 Alex Smith <alex@alex-smith.me.uk>
[grub2/phcoder.git] / fs / i386 / pc / pxe.c
blobf52d61188a69bdb6f69ebed4191a5ae5c523a2f4
1 /* pxe.c - Driver to provide access to the pxe filesystem */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2008 Free Software Foundation, Inc.
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * GRUB 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.
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
20 #include <grub/dl.h>
21 #include <grub/fs.h>
22 #include <grub/mm.h>
23 #include <grub/disk.h>
24 #include <grub/file.h>
25 #include <grub/misc.h>
26 #include <grub/bufio.h>
28 #include <grub/machine/pxe.h>
29 #include <grub/machine/memory.h>
31 #define SEGMENT(x) ((x) >> 4)
32 #define OFFSET(x) ((x) & 0xF)
33 #define SEGOFS(x) ((SEGMENT(x) << 16) + OFFSET(x))
34 #define LINEAR(x) (void *) (((x >> 16) <<4) + (x & 0xFFFF))
36 struct grub_pxenv *grub_pxe_pxenv;
37 grub_uint32_t grub_pxe_your_ip;
38 grub_uint32_t grub_pxe_server_ip;
39 grub_uint32_t grub_pxe_gateway_ip;
40 int grub_pxe_blksize = GRUB_PXE_MIN_BLKSIZE;
42 static grub_file_t curr_file = 0;
44 struct grub_pxe_data
46 grub_uint32_t packet_number;
47 grub_uint32_t block_size;
48 char filename[0];
51 static int
52 grub_pxe_iterate (int (*hook) (const char *name))
54 if (hook ("pxe"))
55 return 1;
56 return 0;
59 static grub_err_t
60 grub_pxe_open (const char *name, grub_disk_t disk)
62 if (grub_strcmp (name, "pxe"))
63 return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a pxe disk");
65 disk->total_sectors = 0;
66 disk->id = (unsigned long) "pxe";
68 disk->has_partitions = 0;
69 disk->data = 0;
71 return GRUB_ERR_NONE;
74 static void
75 grub_pxe_close (grub_disk_t disk __attribute((unused)))
79 static grub_err_t
80 grub_pxe_read (grub_disk_t disk __attribute((unused)),
81 grub_disk_addr_t sector __attribute((unused)),
82 grub_size_t size __attribute((unused)),
83 char *buf __attribute((unused)))
85 return GRUB_ERR_OUT_OF_RANGE;
88 static grub_err_t
89 grub_pxe_write (grub_disk_t disk __attribute((unused)),
90 grub_disk_addr_t sector __attribute((unused)),
91 grub_size_t size __attribute((unused)),
92 const char *buf __attribute((unused)))
94 return GRUB_ERR_OUT_OF_RANGE;
97 static struct grub_disk_dev grub_pxe_dev =
99 .name = "pxe",
100 .id = GRUB_DISK_DEVICE_PXE_ID,
101 .iterate = grub_pxe_iterate,
102 .open = grub_pxe_open,
103 .close = grub_pxe_close,
104 .read = grub_pxe_read,
105 .write = grub_pxe_write,
106 .next = 0
109 static grub_err_t
110 grub_pxefs_dir (grub_device_t device __attribute((unused)),
111 const char *path __attribute((unused)),
112 int (*hook) (const char *filename, int dir) __attribute((unused)))
114 return GRUB_ERR_NONE;
117 static grub_err_t
118 grub_pxefs_open (struct grub_file *file, const char *name)
120 union
122 struct grub_pxenv_tftp_get_fsize c1;
123 struct grub_pxenv_tftp_open c2;
124 } c;
125 struct grub_pxe_data *data;
126 grub_file_t file_int, bufio;
128 if (curr_file != 0)
130 grub_pxe_call (GRUB_PXENV_TFTP_CLOSE, &c.c2);
131 curr_file = 0;
134 c.c1.server_ip = grub_pxe_server_ip;
135 c.c1.gateway_ip = grub_pxe_gateway_ip;
136 grub_strcpy (c.c1.filename, name);
137 grub_pxe_call (GRUB_PXENV_TFTP_GET_FSIZE, &c.c1);
138 if (c.c1.status)
139 return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
141 file->size = c.c1.file_size;
143 c.c2.tftp_port = grub_cpu_to_be16 (GRUB_PXE_TFTP_PORT);
144 c.c2.packet_size = grub_pxe_blksize;
145 grub_pxe_call (GRUB_PXENV_TFTP_OPEN, &c.c2);
146 if (c.c2.status)
147 return grub_error (GRUB_ERR_BAD_FS, "open fails");
149 data = grub_malloc (sizeof (struct grub_pxe_data) + grub_strlen (name) + 1);
150 if (! data)
151 return grub_errno;
153 data->packet_number = 0;
154 data->block_size = grub_pxe_blksize;
155 grub_strcpy (data->filename, name);
157 file_int = grub_malloc (sizeof (*file_int));
158 if (! file_int)
160 grub_free (data);
161 return grub_errno;
164 file->data = data;
165 grub_memcpy (file_int, file, sizeof (struct grub_file));
166 curr_file = file_int;
168 bufio = grub_bufio_open (file_int, data->block_size);
169 if (! bufio)
171 grub_free (file_int);
172 grub_free (data);
173 return grub_errno;
176 grub_memcpy (file, bufio, sizeof (struct grub_file));
178 return GRUB_ERR_NONE;
181 static grub_ssize_t
182 grub_pxefs_read (grub_file_t file, char *buf, grub_size_t len)
184 struct grub_pxenv_tftp_read c;
185 struct grub_pxe_data *data;
186 grub_uint32_t pn, r;
188 data = file->data;
190 pn = grub_divmod64 (file->offset, data->block_size, &r);
191 if (r)
192 return grub_error (GRUB_ERR_BAD_FS,
193 "read access must be aligned to packet size");
195 if ((curr_file != file) || (data->packet_number > pn))
197 struct grub_pxenv_tftp_open o;
199 if (curr_file != 0)
200 grub_pxe_call (GRUB_PXENV_TFTP_CLOSE, &o);
202 o.server_ip = grub_pxe_server_ip;
203 o.gateway_ip = grub_pxe_gateway_ip;
204 grub_strcpy (o.filename, data->filename);
205 o.tftp_port = grub_cpu_to_be16 (GRUB_PXE_TFTP_PORT);
206 o.packet_size = data->block_size;
207 grub_pxe_call (GRUB_PXENV_TFTP_OPEN, &o);
208 if (o.status)
209 return grub_error (GRUB_ERR_BAD_FS, "open fails");
210 data->packet_number = 0;
211 curr_file = file;
214 c.buffer = SEGOFS (GRUB_MEMORY_MACHINE_SCRATCH_ADDR);
215 while (pn >= data->packet_number)
217 c.buffer_size = grub_pxe_blksize;
218 grub_pxe_call (GRUB_PXENV_TFTP_READ, &c);
219 if (c.status)
221 grub_error (GRUB_ERR_BAD_FS, "read fails");
222 return -1;
224 data->packet_number++;
227 grub_memcpy (buf, (char *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR, len);
229 return len;
232 static grub_err_t
233 grub_pxefs_close (grub_file_t file)
235 struct grub_pxenv_tftp_close c;
237 if (curr_file == file)
239 grub_pxe_call (GRUB_PXENV_TFTP_CLOSE, &c);
240 curr_file = 0;
243 grub_free (file->data);
245 return GRUB_ERR_NONE;
248 static grub_err_t
249 grub_pxefs_label (grub_device_t device __attribute ((unused)),
250 char **label __attribute ((unused)))
252 *label = 0;
253 return GRUB_ERR_NONE;
256 static struct grub_fs grub_pxefs_fs =
258 .name = "pxefs",
259 .dir = grub_pxefs_dir,
260 .open = grub_pxefs_open,
261 .read = grub_pxefs_read,
262 .close = grub_pxefs_close,
263 .label = grub_pxefs_label,
264 .next = 0
267 static void
268 grub_pxe_detect (void)
270 struct grub_pxenv *pxenv;
271 struct grub_pxenv_get_cached_info ci;
272 struct grub_pxenv_boot_player *bp;
274 pxenv = grub_pxe_scan ();
275 if (! pxenv)
276 return;
278 ci.packet_type = GRUB_PXENV_PACKET_TYPE_DHCP_ACK;
279 ci.buffer = 0;
280 ci.buffer_size = 0;
281 grub_pxe_call (GRUB_PXENV_GET_CACHED_INFO, &ci);
282 if (ci.status)
283 return;
285 bp = LINEAR (ci.buffer);
287 grub_pxe_your_ip = bp->your_ip;
288 grub_pxe_server_ip = bp->server_ip;
289 grub_pxe_gateway_ip = bp->gateway_ip;
291 grub_pxe_pxenv = pxenv;
294 void
295 grub_pxe_unload (void)
297 if (grub_pxe_pxenv)
299 grub_fs_unregister (&grub_pxefs_fs);
300 grub_disk_dev_unregister (&grub_pxe_dev);
302 grub_pxe_pxenv = 0;
306 GRUB_MOD_INIT(pxe)
308 (void) mod; /* To stop warning. */
310 grub_pxe_detect ();
311 if (grub_pxe_pxenv)
313 grub_disk_dev_register (&grub_pxe_dev);
314 grub_fs_register (&grub_pxefs_fs);
318 GRUB_MOD_FINI(pxe)
320 grub_pxe_unload ();