• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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