2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2000,2001,2002,2004 Free Software Foundation, Inc.
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.
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.
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.
20 /* Based on "src/main.c" in etherboot-4.5.8. */
21 /**************************************************************************
22 ETHERBOOT - BOOTP/TFTP Bootstrap Program
24 Author: Martin Renters
27 **************************************************************************/
29 /* #define TFTP_DEBUG 1 */
34 #include <etherboot.h>
38 static unsigned short iport
= 2000;
39 static unsigned short oport
;
40 static unsigned short block
, prevblock
;
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
;
49 /* Fill the buffer by receiving the data via the TFTP protocol. */
54 grub_printf ("buf_fill (%d)\n", abort
);
57 while (! buf_eof
&& (buf_read
+ packetsize
<= FSYS_BUFLEN
))
63 timeout
= rfc2131_sleep_interval (block
? TFTP_REXMT
: TIMEOUT
, retry
);
65 timeout
= rfc2131_sleep_interval (TIMEOUT
, retry
);
68 if (! await_reply (AWAIT_TFTP
, iport
, NULL
, timeout
))
73 if (! block
&& retry
++ < MAX_TFTP_RETRIES
)
75 /* Maybe initial request was lost. */
77 grub_printf ("Maybe initial request was lost.\n");
79 if (! udp_transmit (arptable
[ARP_SERVER
].ipaddr
.s_addr
,
80 ++iport
, TFTP_PORT
, len
, &tp
))
87 if (block
&& ((retry
+= TFTP_REXMT
) < TFTP_TIMEOUT
))
89 /* We resend our last ack. */
91 grub_printf ("<REXMT>\n");
93 udp_transmit (arptable
[ARP_SERVER
].ipaddr
.s_addr
,
95 TFTP_MIN_PACKET
, &tp
);
103 tr
= (struct tftp_t
*) &nic
.packet
[ETH_HLEN
];
104 if (tr
->opcode
== ntohs (TFTP_ERROR
))
106 grub_printf ("TFTP error %d (%s)\n",
107 ntohs (tr
->u
.err
.errcode
),
112 if (tr
->opcode
== ntohs (TFTP_OACK
))
114 char *p
= tr
->u
.oack
.data
, *e
;
117 grub_printf ("OACK ");
119 /* Shouldn't happen. */
123 grub_printf ("%s:%d: warning: PREVBLOCK != 0 (0x%x)\n",
124 __FILE__
, __LINE__
, prevblock
);
128 len
= ntohs (tr
->udp
.len
) - sizeof (struct udphdr
) - 2;
129 if (len
> TFTP_MAX_PACKET
)
133 while (*p
!= '\000' && p
< e
)
135 if (! grub_strcmp ("blksize", p
))
138 if ((packetsize
= getdec (&p
)) < TFTP_DEFAULTSIZE_PACKET
)
141 grub_printf ("blksize = %d\n", packetsize
);
144 else if (! grub_strcmp ("tsize", p
))
147 if ((filemax
= getdec (&p
)) < 0)
153 grub_printf ("tsize = %d\n", filemax
);
160 grub_printf ("NOAK\n");
162 tp
.opcode
= htons (TFTP_ERROR
);
163 tp
.u
.err
.errcode
= 8;
164 len
= (grub_sprintf ((char *) tp
.u
.err
.errmsg
,
166 + sizeof (tp
.ip
) + sizeof (tp
.udp
)
167 + sizeof (tp
.opcode
) + sizeof (tp
.u
.err
.errcode
)
169 udp_transmit (arptable
[ARP_SERVER
].ipaddr
.s_addr
,
170 iport
, ntohs (tr
->udp
.src
),
185 /* This ensures that the packet does not get processed as
187 block
= tp
.u
.ack
.block
= 0;
189 else if (tr
->opcode
== ntohs (TFTP_DATA
))
192 grub_printf ("DATA ");
194 len
= ntohs (tr
->udp
.len
) - sizeof (struct udphdr
) - 4;
196 /* Shouldn't happen. */
197 if (len
> packetsize
)
200 grub_printf ("%s:%d: warning: LEN > PACKETSIZE (0x%x > 0x%x)\n",
201 __FILE__
, __LINE__
, len
, packetsize
);
205 block
= ntohs (tp
.u
.ack
.block
= tr
->u
.data
.block
);
208 /* Neither TFTP_OACK nor TFTP_DATA. */
211 if ((block
|| bcounter
) && (block
!= prevblock
+ (unsigned short) 1))
212 /* Block order should be continuous */
213 tp
.u
.ack
.block
= htons (block
= prevblock
);
215 /* Should be continuous. */
216 tp
.opcode
= abort
? htons (TFTP_ERROR
) : htons (TFTP_ACK
);
217 oport
= ntohs (tr
->udp
.src
);
220 grub_printf ("ACK\n");
223 udp_transmit (arptable
[ARP_SERVER
].ipaddr
.s_addr
, iport
,
224 oport
, TFTP_MIN_PACKET
, &tp
);
232 /* Retransmission or OACK. */
233 if ((unsigned short) (block
- prevblock
) != 1)
238 /* Is it the right place to zero the timer? */
241 /* In GRUB, this variable doesn't play any important role at all,
242 but use it for consistency with Etherboot. */
245 /* Copy the downloaded data to the buffer. */
246 grub_memmove (buf
+ buf_read
, tr
->u
.data
.download
, len
);
250 if (len
< packetsize
)
257 /* Send the RRQ whose length is LEN. */
261 /* Initialize some variables. */
265 packetsize
= TFTP_DEFAULTSIZE_PACKET
;
268 buf
= (char *) FSYS_BUF
;
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);
282 grub_printf ("send_rrq ()\n");
287 for (i
= 0, p
= (char *) &tp
; i
< len
; i
++)
288 if (p
[i
] >= ' ' && p
[i
] <= '~')
291 grub_printf ("\\%x", (unsigned) p
[i
]);
296 /* Send the packet. */
297 return udp_transmit (arptable
[ARP_SERVER
].ipaddr
.s_addr
, ++iport
,
298 TFTP_PORT
, len
, &tp
);
301 /* Mount the network drive. If the drive is ready, return one, otherwise
306 /* Check if the current drive is the network drive. */
307 if (current_drive
!= NETWORK_DRIVE
)
310 /* If the drive is not initialized yet, abort. */
317 /* Read up to SIZE bytes, returned in ADDR. */
319 tftp_read (char *addr
, int size
)
321 /* How many bytes is read? */
325 grub_printf ("tftp_read (0x%x, %d)\n", (int) addr
, size
);
328 if (filepos
< saved_filepos
)
330 /* Uggh.. FILEPOS has been moved backwards. So reopen the file. */
333 grub_memmove ((char *) &tp
, (char *) &saved_tp
, saved_len
);
338 grub_printf ("opcode = 0x%x, rrq = ", (unsigned long) tp
.opcode
);
339 for (i
= 0; i
< TFTP_DEFAULTSIZE_PACKET
; i
++)
341 if (tp
.u
.rrq
[i
] >= ' ' && tp
.u
.rrq
[i
] <= '~')
342 grub_putchar (tp
.u
.rrq
[i
]);
359 int amt
= buf_read
+ saved_filepos
- filepos
;
361 /* If the length that can be copied from the buffer is over the
362 requested size, cut it down. */
368 /* Copy the buffer to the supplied memory space. */
369 grub_memmove (addr
, buf
+ filepos
- saved_filepos
, amt
);
375 /* If the size of the empty space becomes small, move the unused
377 if (filepos
- saved_filepos
> FSYS_BUFLEN
/ 2)
379 grub_memmove (buf
, buf
+ FSYS_BUFLEN
/ 2, FSYS_BUFLEN
/ 2);
380 buf_read
-= FSYS_BUFLEN
/ 2;
381 saved_filepos
+= FSYS_BUFLEN
/ 2;
386 /* Skip the whole buffer. */
387 saved_filepos
+= buf_read
;
392 if (size
> 0 && ! buf_fill (0))
399 if (size
> 0 && buf_read
== 0)
409 /* Check if the file DIRNAME really exists. Get the size and save it in
412 tftp_dir (char *dirname
)
417 grub_printf ("tftp_dir (%s)\n", dirname
);
420 /* In TFTP, there is no way to know what files exist. */
421 if (print_possibilities
)
424 /* Don't know the size yet. */
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
);
451 errnum
= ERR_FILE_NOT_FOUND
;
457 /* The server doesn't support the "tsize" option, so we must read
460 /* Zero the size of the file. */
464 /* Add the length of the downloaded data. */
466 /* Reset the offset. Just discard the contents of the buffer. */
477 /* Maybe a few amounts of data remains. */
480 /* Retry the open instruction. */
487 /* Close the file. */
492 grub_printf ("tftp_close ()\n");