1 /* 2 * NFS support driver - based on etherboot and U-BOOT's tftp.c 3 * 4 * Masami Komiya <mkomiya@sonare.it> 2004 5 * 6 */ 7 8 /* NOTE: the NFS code is heavily inspired by the NetBSD netboot code (read: 9 * large portions are copied verbatim) as distributed in OSKit 0.97. A few 10 * changes were necessary to adapt the code to Etherboot and to fix several 11 * inconsistencies. Also the RPC message preparation is done "by hand" to 12 * avoid adding netsprintf() which I find hard to understand and use. */ 13 14 /* NOTE 2: Etherboot does not care about things beyond the kernel image, so 15 * it loads the kernel image off the boot server (ARP_SERVER) and does not 16 * access the client root disk (root-path in dhcpd.conf), which would use 17 * ARP_ROOTSERVER. The root disk is something the operating system we are 18 * about to load needs to use. This is different from the OSKit 0.97 logic. */ 19 20 /* NOTE 3: Symlink handling introduced by Anselm M Hoffmeister, 2003-July-14 21 * If a symlink is encountered, it is followed as far as possible (recursion 22 * possible, maximum 16 steps). There is no clearing of ".."'s inside the 23 * path, so please DON'T DO THAT. thx. */ 24 25 #include <common.h> 26 #include <command.h> 27 #include <net.h> 28 #include <malloc.h> 29 #include "nfs.h" 30 #include "bootp.h" 31 32 #define HASHES_PER_LINE 65 /* Number of "loading" hashes per line */ 33 #define NFS_RETRY_COUNT 30 34 #ifndef CONFIG_NFS_TIMEOUT 35 # define NFS_TIMEOUT 2000UL 36 #else 37 # define NFS_TIMEOUT CONFIG_NFS_TIMEOUT 38 #endif 39 40 static int fs_mounted; 41 static unsigned long rpc_id; 42 static int nfs_offset = -1; 43 static int nfs_len; 44 45 static char dirfh[NFS_FHSIZE]; /* file handle of directory */ 46 static char filefh[NFS_FHSIZE]; /* file handle of kernel image */ 47 48 static enum net_loop_state nfs_download_state; 49 static IPaddr_t NfsServerIP; 50 static int NfsSrvMountPort; 51 static int NfsSrvNfsPort; 52 static int NfsOurPort; 53 static int NfsTimeoutCount; 54 static int NfsState; 55 #define STATE_PRCLOOKUP_PROG_MOUNT_REQ 1 56 #define STATE_PRCLOOKUP_PROG_NFS_REQ 2 57 #define STATE_MOUNT_REQ 3 58 #define STATE_UMOUNT_REQ 4 59 #define STATE_LOOKUP_REQ 5 60 #define STATE_READ_REQ 6 61 #define STATE_READLINK_REQ 7 62 63 static char default_filename[64]; 64 static char *nfs_filename; 65 static char *nfs_path; 66 static char nfs_path_buff[2048]; 67 68 static inline int 69 store_block(uchar *src, unsigned offset, unsigned len) 70 { 71 ulong newsize = offset + len; 72 #ifdef CONFIG_SYS_DIRECT_FLASH_NFS 73 int i, rc = 0; 74 75 for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) { 76 /* start address in flash? */ 77 if (load_addr + offset >= flash_info[i].start[0]) { 78 rc = 1; 79 break; 80 } 81 } 82 83 if (rc) { /* Flash is destination for this packet */ 84 rc = flash_write((uchar *)src, (ulong)(load_addr+offset), len); 85 if (rc) { 86 flash_perror(rc); 87 return -1; 88 } 89 } else 90 #endif /* CONFIG_SYS_DIRECT_FLASH_NFS */ 91 { 92 (void)memcpy((void *)(load_addr + offset), src, len); 93 } 94 95 if (NetBootFileXferSize < (offset+len)) 96 NetBootFileXferSize = newsize; 97 return 0; 98 } 99 100 static char* 101 basename(char *path) 102 { 103 char *fname; 104 105 fname = path + strlen(path) - 1; 106 while (fname >= path) { 107 if (*fname == '/') { 108 fname++; 109 break; 110 } 111 fname--; 112 } 113 return fname; 114 } 115 116 static char* 117 dirname(char *path) 118 { 119 char *fname; 120 121 fname = basename(path); 122 --fname; 123 *fname = '\0'; 124 return path; 125 } 126 127 /************************************************************************** 128 RPC_ADD_CREDENTIALS - Add RPC authentication/verifier entries 129 **************************************************************************/ 130 static long *rpc_add_credentials(long *p) 131 { 132 int hl; 133 int hostnamelen; 134 char hostname[256]; 135 136 strcpy(hostname, ""); 137 hostnamelen = strlen(hostname); 138 139 /* Here's the executive summary on authentication requirements of the 140 * various NFS server implementations: Linux accepts both AUTH_NONE 141 * and AUTH_UNIX authentication (also accepts an empty hostname field 142 * in the AUTH_UNIX scheme). *BSD refuses AUTH_NONE, but accepts 143 * AUTH_UNIX (also accepts an empty hostname field in the AUTH_UNIX 144 * scheme). To be safe, use AUTH_UNIX and pass the hostname if we have 145 * it (if the BOOTP/DHCP reply didn't give one, just use an empty 146 * hostname). */ 147 148 hl = (hostnamelen + 3) & ~3; 149 150 /* Provide an AUTH_UNIX credential. */ 151 *p++ = htonl(1); /* AUTH_UNIX */ 152 *p++ = htonl(hl+20); /* auth length */ 153 *p++ = htonl(0); /* stamp */ 154 *p++ = htonl(hostnamelen); /* hostname string */ 155 if (hostnamelen & 3) 156 *(p + hostnamelen / 4) = 0; /* add zero padding */ 157 memcpy(p, hostname, hostnamelen); 158 p += hl / 4; 159 *p++ = 0; /* uid */ 160 *p++ = 0; /* gid */ 161 *p++ = 0; /* auxiliary gid list */ 162 163 /* Provide an AUTH_NONE verifier. */ 164 *p++ = 0; /* AUTH_NONE */ 165 *p++ = 0; /* auth length */ 166 167 return p; 168 } 169 170 /************************************************************************** 171 RPC_LOOKUP - Lookup RPC Port numbers 172 **************************************************************************/ 173 static void 174 rpc_req(int rpc_prog, int rpc_proc, uint32_t *data, int datalen) 175 { 176 struct rpc_t pkt; 177 unsigned long id; 178 uint32_t *p; 179 int pktlen; 180 int sport; 181 182 id = ++rpc_id; 183 pkt.u.call.id = htonl(id); 184 pkt.u.call.type = htonl(MSG_CALL); 185 pkt.u.call.rpcvers = htonl(2); /* use RPC version 2 */ 186 pkt.u.call.prog = htonl(rpc_prog); 187 pkt.u.call.vers = htonl(2); /* portmapper is version 2 */ 188 pkt.u.call.proc = htonl(rpc_proc); 189 p = (uint32_t *)&(pkt.u.call.data); 190 191 if (datalen) 192 memcpy((char *)p, (char *)data, datalen*sizeof(uint32_t)); 193 194 pktlen = (char *)p + datalen*sizeof(uint32_t) - (char *)&pkt; 195 196 memcpy((char *)NetTxPacket + NetEthHdrSize() + IP_UDP_HDR_SIZE, 197 (char *)&pkt, pktlen); 198 199 if (rpc_prog == PROG_PORTMAP) 200 sport = SUNRPC_PORT; 201 else if (rpc_prog == PROG_MOUNT) 202 sport = NfsSrvMountPort; 203 else 204 sport = NfsSrvNfsPort; 205 206 NetSendUDPPacket(NetServerEther, NfsServerIP, sport, NfsOurPort, 207 pktlen); 208 } 209 210 /************************************************************************** 211 RPC_LOOKUP - Lookup RPC Port numbers 212 **************************************************************************/ 213 static void 214 rpc_lookup_req(int prog, int ver) 215 { 216 uint32_t data[16]; 217 218 data[0] = 0; data[1] = 0; /* auth credential */ 219 data[2] = 0; data[3] = 0; /* auth verifier */ 220 data[4] = htonl(prog); 221 data[5] = htonl(ver); 222 data[6] = htonl(17); /* IP_UDP */ 223 data[7] = 0; 224 225 rpc_req(PROG_PORTMAP, PORTMAP_GETPORT, data, 8); 226 } 227 228 /************************************************************************** 229 NFS_MOUNT - Mount an NFS Filesystem 230 **************************************************************************/ 231 static void 232 nfs_mount_req(char *path) 233 { 234 uint32_t data[1024]; 235 uint32_t *p; 236 int len; 237 int pathlen; 238 239 pathlen = strlen(path); 240 241 p = &(data[0]); 242 p = (uint32_t *)rpc_add_credentials((long *)p); 243 244 *p++ = htonl(pathlen); 245 if (pathlen & 3) 246 *(p + pathlen / 4) = 0; 247 memcpy(p, path, pathlen); 248 p += (pathlen + 3) / 4; 249 250 len = (uint32_t *)p - (uint32_t *)&(data[0]); 251 252 rpc_req(PROG_MOUNT, MOUNT_ADDENTRY, data, len); 253 } 254 255 /************************************************************************** 256 NFS_UMOUNTALL - Unmount all our NFS Filesystems on the Server 257 **************************************************************************/ 258 static void 259 nfs_umountall_req(void) 260 { 261 uint32_t data[1024]; 262 uint32_t *p; 263 int len; 264 265 if ((NfsSrvMountPort == -1) || (!fs_mounted)) 266 /* Nothing mounted, nothing to umount */ 267 return; 268 269 p = &(data[0]); 270 p = (uint32_t *)rpc_add_credentials((long *)p); 271 272 len = (uint32_t *)p - (uint32_t *)&(data[0]); 273 274 rpc_req(PROG_MOUNT, MOUNT_UMOUNTALL, data, len); 275 } 276 277 /*************************************************************************** 278 * NFS_READLINK (AH 2003-07-14) 279 * This procedure is called when read of the first block fails - 280 * this probably happens when it's a directory or a symlink 281 * In case of successful readlink(), the dirname is manipulated, 282 * so that inside the nfs() function a recursion can be done. 283 **************************************************************************/ 284 static void 285 nfs_readlink_req(void) 286 { 287 uint32_t data[1024]; 288 uint32_t *p; 289 int len; 290 291 p = &(data[0]); 292 p = (uint32_t *)rpc_add_credentials((long *)p); 293 294 memcpy(p, filefh, NFS_FHSIZE); 295 p += (NFS_FHSIZE / 4); 296 297 len = (uint32_t *)p - (uint32_t *)&(data[0]); 298 299 rpc_req(PROG_NFS, NFS_READLINK, data, len); 300 } 301 302 /************************************************************************** 303 NFS_LOOKUP - Lookup Pathname 304 **************************************************************************/ 305 static void 306 nfs_lookup_req(char *fname) 307 { 308 uint32_t data[1024]; 309 uint32_t *p; 310 int len; 311 int fnamelen; 312 313 fnamelen = strlen(fname); 314 315 p = &(data[0]); 316 p = (uint32_t *)rpc_add_credentials((long *)p); 317 318 memcpy(p, dirfh, NFS_FHSIZE); 319 p += (NFS_FHSIZE / 4); 320 *p++ = htonl(fnamelen); 321 if (fnamelen & 3) 322 *(p + fnamelen / 4) = 0; 323 memcpy(p, fname, fnamelen); 324 p += (fnamelen + 3) / 4; 325 326 len = (uint32_t *)p - (uint32_t *)&(data[0]); 327 328 rpc_req(PROG_NFS, NFS_LOOKUP, data, len); 329 } 330 331 /************************************************************************** 332 NFS_READ - Read File on NFS Server 333 **************************************************************************/ 334 static void 335 nfs_read_req(int offset, int readlen) 336 { 337 uint32_t data[1024]; 338 uint32_t *p; 339 int len; 340 341 p = &(data[0]); 342 p = (uint32_t *)rpc_add_credentials((long *)p); 343 344 memcpy(p, filefh, NFS_FHSIZE); 345 p += (NFS_FHSIZE / 4); 346 *p++ = htonl(offset); 347 *p++ = htonl(readlen); 348 *p++ = 0; 349 350 len = (uint32_t *)p - (uint32_t *)&(data[0]); 351 352 rpc_req(PROG_NFS, NFS_READ, data, len); 353 } 354 355 /************************************************************************** 356 RPC request dispatcher 357 **************************************************************************/ 358 359 static void 360 NfsSend(void) 361 { 362 debug("%s\n", __func__); 363 364 switch (NfsState) { 365 case STATE_PRCLOOKUP_PROG_MOUNT_REQ: 366 rpc_lookup_req(PROG_MOUNT, 1); 367 break; 368 case STATE_PRCLOOKUP_PROG_NFS_REQ: 369 rpc_lookup_req(PROG_NFS, 2); 370 break; 371 case STATE_MOUNT_REQ: 372 nfs_mount_req(nfs_path); 373 break; 374 case STATE_UMOUNT_REQ: 375 nfs_umountall_req(); 376 break; 377 case STATE_LOOKUP_REQ: 378 nfs_lookup_req(nfs_filename); 379 break; 380 case STATE_READ_REQ: 381 nfs_read_req(nfs_offset, nfs_len); 382 break; 383 case STATE_READLINK_REQ: 384 nfs_readlink_req(); 385 break; 386 } 387 } 388 389 /************************************************************************** 390 Handlers for the reply from server 391 **************************************************************************/ 392 393 static int 394 rpc_lookup_reply(int prog, uchar *pkt, unsigned len) 395 { 396 struct rpc_t rpc_pkt; 397 398 memcpy((unsigned char *)&rpc_pkt, pkt, len); 399 400 debug("%s\n", __func__); 401 402 if (ntohl(rpc_pkt.u.reply.id) != rpc_id) 403 return -1; 404 405 if (rpc_pkt.u.reply.rstatus || 406 rpc_pkt.u.reply.verifier || 407 rpc_pkt.u.reply.astatus) 408 return -1; 409 410 switch (prog) { 411 case PROG_MOUNT: 412 NfsSrvMountPort = ntohl(rpc_pkt.u.reply.data[0]); 413 break; 414 case PROG_NFS: 415 NfsSrvNfsPort = ntohl(rpc_pkt.u.reply.data[0]); 416 break; 417 } 418 419 return 0; 420 } 421 422 static int 423 nfs_mount_reply(uchar *pkt, unsigned len) 424 { 425 struct rpc_t rpc_pkt; 426 427 debug("%s\n", __func__); 428 429 memcpy((unsigned char *)&rpc_pkt, pkt, len); 430 431 if (ntohl(rpc_pkt.u.reply.id) != rpc_id) 432 return -1; 433 434 if (rpc_pkt.u.reply.rstatus || 435 rpc_pkt.u.reply.verifier || 436 rpc_pkt.u.reply.astatus || 437 rpc_pkt.u.reply.data[0]) 438 return -1; 439 440 fs_mounted = 1; 441 memcpy(dirfh, rpc_pkt.u.reply.data + 1, NFS_FHSIZE); 442 443 return 0; 444 } 445 446 static int 447 nfs_umountall_reply(uchar *pkt, unsigned len) 448 { 449 struct rpc_t rpc_pkt; 450 451 debug("%s\n", __func__); 452 453 memcpy((unsigned char *)&rpc_pkt, pkt, len); 454 455 if (ntohl(rpc_pkt.u.reply.id) != rpc_id) 456 return -1; 457 458 if (rpc_pkt.u.reply.rstatus || 459 rpc_pkt.u.reply.verifier || 460 rpc_pkt.u.reply.astatus) 461 return -1; 462 463 fs_mounted = 0; 464 memset(dirfh, 0, sizeof(dirfh)); 465 466 return 0; 467 } 468 469 static int 470 nfs_lookup_reply(uchar *pkt, unsigned len) 471 { 472 struct rpc_t rpc_pkt; 473 474 debug("%s\n", __func__); 475 476 memcpy((unsigned char *)&rpc_pkt, pkt, len); 477 478 if (ntohl(rpc_pkt.u.reply.id) != rpc_id) 479 return -1; 480 481 if (rpc_pkt.u.reply.rstatus || 482 rpc_pkt.u.reply.verifier || 483 rpc_pkt.u.reply.astatus || 484 rpc_pkt.u.reply.data[0]) 485 return -1; 486 487 memcpy(filefh, rpc_pkt.u.reply.data + 1, NFS_FHSIZE); 488 489 return 0; 490 } 491 492 static int 493 nfs_readlink_reply(uchar *pkt, unsigned len) 494 { 495 struct rpc_t rpc_pkt; 496 int rlen; 497 498 debug("%s\n", __func__); 499 500 memcpy((unsigned char *)&rpc_pkt, pkt, len); 501 502 if (ntohl(rpc_pkt.u.reply.id) != rpc_id) 503 return -1; 504 505 if (rpc_pkt.u.reply.rstatus || 506 rpc_pkt.u.reply.verifier || 507 rpc_pkt.u.reply.astatus || 508 rpc_pkt.u.reply.data[0]) 509 return -1; 510 511 rlen = ntohl(rpc_pkt.u.reply.data[1]); /* new path length */ 512 513 if (*((char *)&(rpc_pkt.u.reply.data[2])) != '/') { 514 int pathlen; 515 strcat(nfs_path, "/"); 516 pathlen = strlen(nfs_path); 517 memcpy(nfs_path + pathlen, (uchar *)&(rpc_pkt.u.reply.data[2]), 518 rlen); 519 nfs_path[pathlen + rlen] = 0; 520 } else { 521 memcpy(nfs_path, (uchar *)&(rpc_pkt.u.reply.data[2]), rlen); 522 nfs_path[rlen] = 0; 523 } 524 return 0; 525 } 526 527 static int 528 nfs_read_reply(uchar *pkt, unsigned len) 529 { 530 struct rpc_t rpc_pkt; 531 int rlen; 532 533 debug("%s\n", __func__); 534 535 memcpy((uchar *)&rpc_pkt, pkt, sizeof(rpc_pkt.u.reply)); 536 537 if (ntohl(rpc_pkt.u.reply.id) != rpc_id) 538 return -1; 539 540 if (rpc_pkt.u.reply.rstatus || 541 rpc_pkt.u.reply.verifier || 542 rpc_pkt.u.reply.astatus || 543 rpc_pkt.u.reply.data[0]) { 544 if (rpc_pkt.u.reply.rstatus) 545 return -9999; 546 if (rpc_pkt.u.reply.astatus) 547 return -9999; 548 return -ntohl(rpc_pkt.u.reply.data[0]); 549 } 550 551 if ((nfs_offset != 0) && !((nfs_offset) % 552 (NFS_READ_SIZE / 2 * 10 * HASHES_PER_LINE))) 553 puts("\n\t "); 554 if (!(nfs_offset % ((NFS_READ_SIZE / 2) * 10))) 555 putc('#'); 556 557 rlen = ntohl(rpc_pkt.u.reply.data[18]); 558 if (store_block((uchar *)pkt + sizeof(rpc_pkt.u.reply), 559 nfs_offset, rlen)) 560 return -9999; 561 562 return rlen; 563 } 564 565 /************************************************************************** 566 Interfaces of U-BOOT 567 **************************************************************************/ 568 569 static void 570 NfsTimeout(void) 571 { 572 if (++NfsTimeoutCount > NFS_RETRY_COUNT) { 573 puts("\nRetry count exceeded; starting again\n"); 574 NetStartAgain(); 575 } else { 576 puts("T "); 577 NetSetTimeout(NFS_TIMEOUT, NfsTimeout); 578 NfsSend(); 579 } 580 } 581 582 static void 583 NfsHandler(uchar *pkt, unsigned dest, IPaddr_t sip, unsigned src, unsigned len) 584 { 585 int rlen; 586 587 debug("%s\n", __func__); 588 589 if (dest != NfsOurPort) 590 return; 591 592 switch (NfsState) { 593 case STATE_PRCLOOKUP_PROG_MOUNT_REQ: 594 rpc_lookup_reply(PROG_MOUNT, pkt, len); 595 NfsState = STATE_PRCLOOKUP_PROG_NFS_REQ; 596 NfsSend(); 597 break; 598 599 case STATE_PRCLOOKUP_PROG_NFS_REQ: 600 rpc_lookup_reply(PROG_NFS, pkt, len); 601 NfsState = STATE_MOUNT_REQ; 602 NfsSend(); 603 break; 604 605 case STATE_MOUNT_REQ: 606 if (nfs_mount_reply(pkt, len)) { 607 puts("*** ERROR: Cannot mount\n"); 608 /* just to be sure... */ 609 NfsState = STATE_UMOUNT_REQ; 610 NfsSend(); 611 } else { 612 NfsState = STATE_LOOKUP_REQ; 613 NfsSend(); 614 } 615 break; 616 617 case STATE_UMOUNT_REQ: 618 if (nfs_umountall_reply(pkt, len)) { 619 puts("*** ERROR: Cannot umount\n"); 620 net_set_state(NETLOOP_FAIL); 621 } else { 622 puts("\ndone\n"); 623 net_set_state(nfs_download_state); 624 } 625 break; 626 627 case STATE_LOOKUP_REQ: 628 if (nfs_lookup_reply(pkt, len)) { 629 puts("*** ERROR: File lookup fail\n"); 630 NfsState = STATE_UMOUNT_REQ; 631 NfsSend(); 632 } else { 633 NfsState = STATE_READ_REQ; 634 nfs_offset = 0; 635 nfs_len = NFS_READ_SIZE; 636 NfsSend(); 637 } 638 break; 639 640 case STATE_READLINK_REQ: 641 if (nfs_readlink_reply(pkt, len)) { 642 puts("*** ERROR: Symlink fail\n"); 643 NfsState = STATE_UMOUNT_REQ; 644 NfsSend(); 645 } else { 646 debug("Symlink --> %s\n", nfs_path); 647 nfs_filename = basename(nfs_path); 648 nfs_path = dirname(nfs_path); 649 650 NfsState = STATE_MOUNT_REQ; 651 NfsSend(); 652 } 653 break; 654 655 case STATE_READ_REQ: 656 rlen = nfs_read_reply(pkt, len); 657 NetSetTimeout(NFS_TIMEOUT, NfsTimeout); 658 if (rlen > 0) { 659 nfs_offset += rlen; 660 NfsSend(); 661 } else if ((rlen == -NFSERR_ISDIR) || (rlen == -NFSERR_INVAL)) { 662 /* symbolic link */ 663 NfsState = STATE_READLINK_REQ; 664 NfsSend(); 665 } else { 666 if (!rlen) 667 nfs_download_state = NETLOOP_SUCCESS; 668 NfsState = STATE_UMOUNT_REQ; 669 NfsSend(); 670 } 671 break; 672 } 673 } 674 675 676 void 677 NfsStart(void) 678 { 679 debug("%s\n", __func__); 680 nfs_download_state = NETLOOP_FAIL; 681 682 NfsServerIP = NetServerIP; 683 nfs_path = (char *)nfs_path_buff; 684 685 if (nfs_path == NULL) { 686 net_set_state(NETLOOP_FAIL); 687 puts("*** ERROR: Fail allocate memory\n"); 688 return; 689 } 690 691 if (BootFile[0] == '\0') { 692 sprintf(default_filename, "/nfsroot/%02X%02X%02X%02X.img", 693 NetOurIP & 0xFF, 694 (NetOurIP >> 8) & 0xFF, 695 (NetOurIP >> 16) & 0xFF, 696 (NetOurIP >> 24) & 0xFF); 697 strcpy(nfs_path, default_filename); 698 699 printf("*** Warning: no boot file name; using '%s'\n", 700 nfs_path); 701 } else { 702 char *p = BootFile; 703 704 p = strchr(p, ':'); 705 706 if (p != NULL) { 707 NfsServerIP = string_to_ip(BootFile); 708 ++p; 709 strcpy(nfs_path, p); 710 } else { 711 strcpy(nfs_path, BootFile); 712 } 713 } 714 715 nfs_filename = basename(nfs_path); 716 nfs_path = dirname(nfs_path); 717 718 printf("Using %s device\n", eth_get_name()); 719 720 printf("File transfer via NFS from server %pI4" 721 "; our IP address is %pI4", &NfsServerIP, &NetOurIP); 722 723 /* Check if we need to send across this subnet */ 724 if (NetOurGatewayIP && NetOurSubnetMask) { 725 IPaddr_t OurNet = NetOurIP & NetOurSubnetMask; 726 IPaddr_t ServerNet = NetServerIP & NetOurSubnetMask; 727 728 if (OurNet != ServerNet) 729 printf("; sending through gateway %pI4", 730 &NetOurGatewayIP); 731 } 732 printf("\nFilename '%s/%s'.", nfs_path, nfs_filename); 733 734 if (NetBootFileSize) { 735 printf(" Size is 0x%x Bytes = ", NetBootFileSize<<9); 736 print_size(NetBootFileSize<<9, ""); 737 } 738 printf("\nLoad address: 0x%lx\n" 739 "Loading: *\b", load_addr); 740 741 NetSetTimeout(NFS_TIMEOUT, NfsTimeout); 742 net_set_udp_handler(NfsHandler); 743 744 NfsTimeoutCount = 0; 745 NfsState = STATE_PRCLOOKUP_PROG_MOUNT_REQ; 746 747 /*NfsOurPort = 4096 + (get_ticks() % 3072);*/ 748 /*FIX ME !!!*/ 749 NfsOurPort = 1000; 750 751 /* zero out server ether in case the server ip has changed */ 752 memset(NetServerEther, 0, 6); 753 754 NfsSend(); 755 } 756