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 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 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 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 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 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 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