1 /*
2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2000,2001,2002,2004 Free Software Foundation, Inc.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 /* Based on "src/main.c" in etherboot-4.5.8. */
21 /**************************************************************************
22 ETHERBOOT - BOOTP/TFTP Bootstrap Program
23
24 Author: Martin Renters
25 Date: Dec/93
26
27 **************************************************************************/
28
29 /* #define TFTP_DEBUG 1 */
30
31 #include <filesys.h>
32
33 #define GRUB 1
34 #include <etherboot.h>
35 #include <nic.h>
36
37 static int retry;
38 static unsigned short iport = 2000;
39 static unsigned short oport;
40 static unsigned short block, prevblock;
41 static int bcounter;
42 static struct tftp_t tp, saved_tp;
43 static int packetsize;
44 static int buf_eof, buf_read;
45 static int saved_filepos;
46 static unsigned short len, saved_len;
47 static char *buf;
48
49 /* Fill the buffer by receiving the data via the TFTP protocol. */
50 static int
buf_fill(int abort)51 buf_fill (int abort)
52 {
53 #ifdef TFTP_DEBUG
54 grub_printf ("buf_fill (%d)\n", abort);
55 #endif
56
57 while (! buf_eof && (buf_read + packetsize <= FSYS_BUFLEN))
58 {
59 struct tftp_t *tr;
60 long timeout;
61
62 #ifdef CONGESTED
63 timeout = rfc2131_sleep_interval (block ? TFTP_REXMT : TIMEOUT, retry);
64 #else
65 timeout = rfc2131_sleep_interval (TIMEOUT, retry);
66 #endif
67
68 if (! await_reply (AWAIT_TFTP, iport, NULL, timeout))
69 {
70 if (ip_abort)
71 return 0;
72
73 if (! block && retry++ < MAX_TFTP_RETRIES)
74 {
75 /* Maybe initial request was lost. */
76 #ifdef TFTP_DEBUG
77 grub_printf ("Maybe initial request was lost.\n");
78 #endif
79 if (! udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr,
80 ++iport, TFTP_PORT, len, &tp))
81 return 0;
82
83 continue;
84 }
85
86 #ifdef CONGESTED
87 if (block && ((retry += TFTP_REXMT) < TFTP_TIMEOUT))
88 {
89 /* We resend our last ack. */
90 # ifdef TFTP_DEBUG
91 grub_printf ("<REXMT>\n");
92 # endif
93 udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr,
94 iport, oport,
95 TFTP_MIN_PACKET, &tp);
96 continue;
97 }
98 #endif
99 /* Timeout. */
100 return 0;
101 }
102
103 tr = (struct tftp_t *) &nic.packet[ETH_HLEN];
104 if (tr->opcode == ntohs (TFTP_ERROR))
105 {
106 grub_printf ("TFTP error %d (%s)\n",
107 ntohs (tr->u.err.errcode),
108 tr->u.err.errmsg);
109 return 0;
110 }
111
112 if (tr->opcode == ntohs (TFTP_OACK))
113 {
114 char *p = tr->u.oack.data, *e;
115
116 #ifdef TFTP_DEBUG
117 grub_printf ("OACK ");
118 #endif
119 /* Shouldn't happen. */
120 if (prevblock)
121 {
122 /* Ignore it. */
123 grub_printf ("%s:%d: warning: PREVBLOCK != 0 (0x%x)\n",
124 __FILE__, __LINE__, prevblock);
125 continue;
126 }
127
128 len = ntohs (tr->udp.len) - sizeof (struct udphdr) - 2;
129 if (len > TFTP_MAX_PACKET)
130 goto noak;
131
132 e = p + len;
133 while (*p != '\000' && p < e)
134 {
135 if (! grub_strcmp ("blksize", p))
136 {
137 p += 8;
138 if ((packetsize = getdec (&p)) < TFTP_DEFAULTSIZE_PACKET)
139 goto noak;
140 #ifdef TFTP_DEBUG
141 grub_printf ("blksize = %d\n", packetsize);
142 #endif
143 }
144 else if (! grub_strcmp ("tsize", p))
145 {
146 p += 6;
147 if ((filemax = getdec (&p)) < 0)
148 {
149 filemax = -1;
150 goto noak;
151 }
152 #ifdef TFTP_DEBUG
153 grub_printf ("tsize = %d\n", filemax);
154 #endif
155 }
156 else
157 {
158 noak:
159 #ifdef TFTP_DEBUG
160 grub_printf ("NOAK\n");
161 #endif
162 tp.opcode = htons (TFTP_ERROR);
163 tp.u.err.errcode = 8;
164 len = (grub_sprintf ((char *) tp.u.err.errmsg,
165 "RFC1782 error")
166 + sizeof (tp.ip) + sizeof (tp.udp)
167 + sizeof (tp.opcode) + sizeof (tp.u.err.errcode)
168 + 1);
169 udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr,
170 iport, ntohs (tr->udp.src),
171 len, &tp);
172 return 0;
173 }
174
175 while (p < e && *p)
176 p++;
177
178 if (p < e)
179 p++;
180 }
181
182 if (p > e)
183 goto noak;
184
185 /* This ensures that the packet does not get processed as
186 data! */
187 block = tp.u.ack.block = 0;
188 }
189 else if (tr->opcode == ntohs (TFTP_DATA))
190 {
191 #ifdef TFTP_DEBUG
192 grub_printf ("DATA ");
193 #endif
194 len = ntohs (tr->udp.len) - sizeof (struct udphdr) - 4;
195
196 /* Shouldn't happen. */
197 if (len > packetsize)
198 {
199 /* Ignore it. */
200 grub_printf ("%s:%d: warning: LEN > PACKETSIZE (0x%x > 0x%x)\n",
201 __FILE__, __LINE__, len, packetsize);
202 continue;
203 }
204
205 block = ntohs (tp.u.ack.block = tr->u.data.block);
206 }
207 else
208 /* Neither TFTP_OACK nor TFTP_DATA. */
209 break;
210
211 if ((block || bcounter) && (block != prevblock + (unsigned short) 1))
212 /* Block order should be continuous */
213 tp.u.ack.block = htons (block = prevblock);
214
215 /* Should be continuous. */
216 tp.opcode = abort ? htons (TFTP_ERROR) : htons (TFTP_ACK);
217 oport = ntohs (tr->udp.src);
218
219 #ifdef TFTP_DEBUG
220 grub_printf ("ACK\n");
221 #endif
222 /* Ack. */
223 udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr, iport,
224 oport, TFTP_MIN_PACKET, &tp);
225
226 if (abort)
227 {
228 buf_eof = 1;
229 break;
230 }
231
232 /* Retransmission or OACK. */
233 if ((unsigned short) (block - prevblock) != 1)
234 /* Don't process. */
235 continue;
236
237 prevblock = block;
238 /* Is it the right place to zero the timer? */
239 retry = 0;
240
241 /* In GRUB, this variable doesn't play any important role at all,
242 but use it for consistency with Etherboot. */
243 bcounter++;
244
245 /* Copy the downloaded data to the buffer. */
246 grub_memmove (buf + buf_read, tr->u.data.download, len);
247 buf_read += len;
248
249 /* End of data. */
250 if (len < packetsize)
251 buf_eof = 1;
252 }
253
254 return 1;
255 }
256
257 /* Send the RRQ whose length is LEN. */
258 static int
send_rrq(void)259 send_rrq (void)
260 {
261 /* Initialize some variables. */
262 retry = 0;
263 block = 0;
264 prevblock = 0;
265 packetsize = TFTP_DEFAULTSIZE_PACKET;
266 bcounter = 0;
267
268 buf = (char *) FSYS_BUF;
269 buf_eof = 0;
270 buf_read = 0;
271 saved_filepos = 0;
272
273 /* Clear out the Rx queue first. It contains nothing of interest,
274 * except possibly ARP requests from the DHCP/TFTP server. We use
275 * polling throughout Etherboot, so some time may have passed since we
276 * last polled the receive queue, which may now be filled with
277 * broadcast packets. This will cause the reply to the packets we are
278 * about to send to be lost immediately. Not very clever. */
279 await_reply (AWAIT_QDRAIN, 0, NULL, 0);
280
281 #ifdef TFTP_DEBUG
282 grub_printf ("send_rrq ()\n");
283 {
284 int i;
285 char *p;
286
287 for (i = 0, p = (char *) &tp; i < len; i++)
288 if (p[i] >= ' ' && p[i] <= '~')
289 grub_putchar (p[i]);
290 else
291 grub_printf ("\\%x", (unsigned) p[i]);
292
293 grub_putchar ('\n');
294 }
295 #endif
296 /* Send the packet. */
297 return udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr, ++iport,
298 TFTP_PORT, len, &tp);
299 }
300
301 /* Mount the network drive. If the drive is ready, return one, otherwise
302 return zero. */
303 int
tftp_mount(void)304 tftp_mount (void)
305 {
306 /* Check if the current drive is the network drive. */
307 if (current_drive != NETWORK_DRIVE)
308 return 0;
309
310 /* If the drive is not initialized yet, abort. */
311 if (! network_ready)
312 return 0;
313
314 return 1;
315 }
316
317 /* Read up to SIZE bytes, returned in ADDR. */
318 int
tftp_read(char * addr,int size)319 tftp_read (char *addr, int size)
320 {
321 /* How many bytes is read? */
322 int ret = 0;
323
324 #ifdef TFTP_DEBUG
325 grub_printf ("tftp_read (0x%x, %d)\n", (int) addr, size);
326 #endif
327
328 if (filepos < saved_filepos)
329 {
330 /* Uggh.. FILEPOS has been moved backwards. So reopen the file. */
331 buf_read = 0;
332 buf_fill (1);
333 grub_memmove ((char *) &tp, (char *) &saved_tp, saved_len);
334 len = saved_len;
335 #ifdef TFTP_DEBUG
336 {
337 int i;
338 grub_printf ("opcode = 0x%x, rrq = ", (unsigned long) tp.opcode);
339 for (i = 0; i < TFTP_DEFAULTSIZE_PACKET; i++)
340 {
341 if (tp.u.rrq[i] >= ' ' && tp.u.rrq[i] <= '~')
342 grub_putchar (tp.u.rrq[i]);
343 else
344 grub_putchar ('*');
345 }
346 grub_putchar ('\n');
347 }
348 #endif
349
350 if (! send_rrq ())
351 {
352 errnum = ERR_WRITE;
353 return 0;
354 }
355 }
356
357 while (size > 0)
358 {
359 int amt = buf_read + saved_filepos - filepos;
360
361 /* If the length that can be copied from the buffer is over the
362 requested size, cut it down. */
363 if (amt > size)
364 amt = size;
365
366 if (amt > 0)
367 {
368 /* Copy the buffer to the supplied memory space. */
369 grub_memmove (addr, buf + filepos - saved_filepos, amt);
370 size -= amt;
371 addr += amt;
372 filepos += amt;
373 ret += amt;
374
375 /* If the size of the empty space becomes small, move the unused
376 data forwards. */
377 if (filepos - saved_filepos > FSYS_BUFLEN / 2)
378 {
379 grub_memmove (buf, buf + FSYS_BUFLEN / 2, FSYS_BUFLEN / 2);
380 buf_read -= FSYS_BUFLEN / 2;
381 saved_filepos += FSYS_BUFLEN / 2;
382 }
383 }
384 else
385 {
386 /* Skip the whole buffer. */
387 saved_filepos += buf_read;
388 buf_read = 0;
389 }
390
391 /* Read the data. */
392 if (size > 0 && ! buf_fill (0))
393 {
394 errnum = ERR_READ;
395 return 0;
396 }
397
398 /* Sanity check. */
399 if (size > 0 && buf_read == 0)
400 {
401 errnum = ERR_READ;
402 return 0;
403 }
404 }
405
406 return ret;
407 }
408
409 /* Check if the file DIRNAME really exists. Get the size and save it in
410 FILEMAX. */
411 int
tftp_dir(char * dirname)412 tftp_dir (char *dirname)
413 {
414 int ch;
415
416 #ifdef TFTP_DEBUG
417 grub_printf ("tftp_dir (%s)\n", dirname);
418 #endif
419
420 /* In TFTP, there is no way to know what files exist. */
421 if (print_possibilities)
422 return 1;
423
424 /* Don't know the size yet. */
425 filemax = -1;
426
427 reopen:
428 /* Construct the TFTP request packet. */
429 tp.opcode = htons (TFTP_RRQ);
430 /* Terminate the filename. */
431 ch = nul_terminate (dirname);
432 /* Make the request string (octet, blksize and tsize). */
433 len = (grub_sprintf ((char *) tp.u.rrq,
434 "%s%coctet%cblksize%c%d%ctsize%c0",
435 dirname, 0, 0, 0, TFTP_MAX_PACKET, 0, 0)
436 + sizeof (tp.ip) + sizeof (tp.udp) + sizeof (tp.opcode) + 1);
437 /* Restore the original DIRNAME. */
438 dirname[grub_strlen (dirname)] = ch;
439 /* Save the TFTP packet so that we can reopen the file later. */
440 grub_memmove ((char *) &saved_tp, (char *) &tp, len);
441 saved_len = len;
442 if (! send_rrq ())
443 {
444 errnum = ERR_WRITE;
445 return 0;
446 }
447
448 /* Read the data. */
449 if (! buf_fill (0))
450 {
451 errnum = ERR_FILE_NOT_FOUND;
452 return 0;
453 }
454
455 if (filemax == -1)
456 {
457 /* The server doesn't support the "tsize" option, so we must read
458 the file twice... */
459
460 /* Zero the size of the file. */
461 filemax = 0;
462 do
463 {
464 /* Add the length of the downloaded data. */
465 filemax += buf_read;
466 /* Reset the offset. Just discard the contents of the buffer. */
467 buf_read = 0;
468 /* Read the data. */
469 if (! buf_fill (0))
470 {
471 errnum = ERR_READ;
472 return 0;
473 }
474 }
475 while (! buf_eof);
476
477 /* Maybe a few amounts of data remains. */
478 filemax += buf_read;
479
480 /* Retry the open instruction. */
481 goto reopen;
482 }
483
484 return 1;
485 }
486
487 /* Close the file. */
488 void
tftp_close(void)489 tftp_close (void)
490 {
491 #ifdef TFTP_DEBUG
492 grub_printf ("tftp_close ()\n");
493 #endif
494
495 buf_read = 0;
496 buf_fill (1);
497 }
498