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