Merge branch 'master' of git://git.denx.de/u-boot-usb
[oweals/u-boot.git] / net / nfs.c
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 /* NOTE 4: NFSv3 support added by Guillaume GARDET, 2016-June-20.
26  * NFSv2 is still used by default. But if server does not support NFSv2, then
27  * NFSv3 is used, if available on NFS server. */
28
29 #include <common.h>
30 #include <command.h>
31 #include <flash.h>
32 #include <image.h>
33 #include <net.h>
34 #include <malloc.h>
35 #include <mapmem.h>
36 #include "nfs.h"
37 #include "bootp.h"
38 #include <time.h>
39
40 #define HASHES_PER_LINE 65      /* Number of "loading" hashes per line  */
41 #define NFS_RETRY_COUNT 30
42 #ifndef CONFIG_NFS_TIMEOUT
43 # define NFS_TIMEOUT 2000UL
44 #else
45 # define NFS_TIMEOUT CONFIG_NFS_TIMEOUT
46 #endif
47
48 #define NFS_RPC_ERR     1
49 #define NFS_RPC_DROP    124
50
51 static int fs_mounted;
52 static unsigned long rpc_id;
53 static int nfs_offset = -1;
54 static int nfs_len;
55 static ulong nfs_timeout = NFS_TIMEOUT;
56
57 static char dirfh[NFS_FHSIZE];  /* NFSv2 / NFSv3 file handle of directory */
58 static char filefh[NFS3_FHSIZE]; /* NFSv2 / NFSv3 file handle */
59 static int filefh3_length;      /* (variable) length of filefh when NFSv3 */
60
61 static enum net_loop_state nfs_download_state;
62 static struct in_addr nfs_server_ip;
63 static int nfs_server_mount_port;
64 static int nfs_server_port;
65 static int nfs_our_port;
66 static int nfs_timeout_count;
67 static int nfs_state;
68 #define STATE_PRCLOOKUP_PROG_MOUNT_REQ  1
69 #define STATE_PRCLOOKUP_PROG_NFS_REQ    2
70 #define STATE_MOUNT_REQ                 3
71 #define STATE_UMOUNT_REQ                4
72 #define STATE_LOOKUP_REQ                5
73 #define STATE_READ_REQ                  6
74 #define STATE_READLINK_REQ              7
75
76 static char *nfs_filename;
77 static char *nfs_path;
78 static char nfs_path_buff[2048];
79
80 #define NFSV2_FLAG 1
81 #define NFSV3_FLAG 1 << 1
82 static char supported_nfs_versions = NFSV2_FLAG | NFSV3_FLAG;
83
84 static inline int store_block(uchar *src, unsigned offset, unsigned len)
85 {
86         ulong newsize = offset + len;
87 #ifdef CONFIG_SYS_DIRECT_FLASH_NFS
88         int i, rc = 0;
89
90         for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) {
91                 /* start address in flash? */
92                 if (image_load_addr + offset >= flash_info[i].start[0]) {
93                         rc = 1;
94                         break;
95                 }
96         }
97
98         if (rc) { /* Flash is destination for this packet */
99                 rc = flash_write((uchar *)src, (ulong)image_load_addr + offset,
100                                  len);
101                 if (rc) {
102                         flash_perror(rc);
103                         return -1;
104                 }
105         } else
106 #endif /* CONFIG_SYS_DIRECT_FLASH_NFS */
107         {
108                 void *ptr = map_sysmem(image_load_addr + offset, len);
109
110                 memcpy(ptr, src, len);
111                 unmap_sysmem(ptr);
112         }
113
114         if (net_boot_file_size < (offset + len))
115                 net_boot_file_size = newsize;
116         return 0;
117 }
118
119 static char *basename(char *path)
120 {
121         char *fname;
122
123         fname = path + strlen(path) - 1;
124         while (fname >= path) {
125                 if (*fname == '/') {
126                         fname++;
127                         break;
128                 }
129                 fname--;
130         }
131         return fname;
132 }
133
134 static char *dirname(char *path)
135 {
136         char *fname;
137
138         fname = basename(path);
139         --fname;
140         *fname = '\0';
141         return path;
142 }
143
144 /**************************************************************************
145 RPC_ADD_CREDENTIALS - Add RPC authentication/verifier entries
146 **************************************************************************/
147 static uint32_t *rpc_add_credentials(uint32_t *p)
148 {
149         /* Here's the executive summary on authentication requirements of the
150          * various NFS server implementations:  Linux accepts both AUTH_NONE
151          * and AUTH_UNIX authentication (also accepts an empty hostname field
152          * in the AUTH_UNIX scheme).  *BSD refuses AUTH_NONE, but accepts
153          * AUTH_UNIX (also accepts an empty hostname field in the AUTH_UNIX
154          * scheme).  To be safe, use AUTH_UNIX and pass the hostname if we have
155          * it (if the BOOTP/DHCP reply didn't give one, just use an empty
156          * hostname).  */
157
158         /* Provide an AUTH_UNIX credential.  */
159         *p++ = htonl(1);                /* AUTH_UNIX */
160         *p++ = htonl(20);               /* auth length */
161         *p++ = 0;                       /* stamp */
162         *p++ = 0;                       /* hostname string */
163         *p++ = 0;                       /* uid */
164         *p++ = 0;                       /* gid */
165         *p++ = 0;                       /* auxiliary gid list */
166
167         /* Provide an AUTH_NONE verifier.  */
168         *p++ = 0;                       /* AUTH_NONE */
169         *p++ = 0;                       /* auth length */
170
171         return p;
172 }
173
174 /**************************************************************************
175 RPC_LOOKUP - Lookup RPC Port numbers
176 **************************************************************************/
177 static void rpc_req(int rpc_prog, int rpc_proc, uint32_t *data, int datalen)
178 {
179         struct rpc_t rpc_pkt;
180         unsigned long id;
181         uint32_t *p;
182         int pktlen;
183         int sport;
184
185         id = ++rpc_id;
186         rpc_pkt.u.call.id = htonl(id);
187         rpc_pkt.u.call.type = htonl(MSG_CALL);
188         rpc_pkt.u.call.rpcvers = htonl(2);      /* use RPC version 2 */
189         rpc_pkt.u.call.prog = htonl(rpc_prog);
190         switch (rpc_prog) {
191         case PROG_NFS:
192                 if (supported_nfs_versions & NFSV2_FLAG)
193                         rpc_pkt.u.call.vers = htonl(2); /* NFS v2 */
194                 else /* NFSV3_FLAG */
195                         rpc_pkt.u.call.vers = htonl(3); /* NFS v3 */
196                 break;
197         case PROG_PORTMAP:
198         case PROG_MOUNT:
199         default:
200                 rpc_pkt.u.call.vers = htonl(2); /* portmapper is version 2 */
201         }
202         rpc_pkt.u.call.proc = htonl(rpc_proc);
203         p = rpc_pkt.u.call.data;
204
205         if (datalen)
206                 memcpy(p, data, datalen * sizeof(uint32_t));
207
208         pktlen = (char *)p + datalen * sizeof(uint32_t) - (char *)&rpc_pkt;
209
210         memcpy((char *)net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE,
211                &rpc_pkt.u.data[0], pktlen);
212
213         if (rpc_prog == PROG_PORTMAP)
214                 sport = SUNRPC_PORT;
215         else if (rpc_prog == PROG_MOUNT)
216                 sport = nfs_server_mount_port;
217         else
218                 sport = nfs_server_port;
219
220         net_send_udp_packet(net_server_ethaddr, nfs_server_ip, sport,
221                             nfs_our_port, pktlen);
222 }
223
224 /**************************************************************************
225 RPC_LOOKUP - Lookup RPC Port numbers
226 **************************************************************************/
227 static void rpc_lookup_req(int prog, int ver)
228 {
229         uint32_t data[16];
230
231         data[0] = 0; data[1] = 0;       /* auth credential */
232         data[2] = 0; data[3] = 0;       /* auth verifier */
233         data[4] = htonl(prog);
234         data[5] = htonl(ver);
235         data[6] = htonl(17);    /* IP_UDP */
236         data[7] = 0;
237         rpc_req(PROG_PORTMAP, PORTMAP_GETPORT, data, 8);
238 }
239
240 /**************************************************************************
241 NFS_MOUNT - Mount an NFS Filesystem
242 **************************************************************************/
243 static void nfs_mount_req(char *path)
244 {
245         uint32_t data[1024];
246         uint32_t *p;
247         int len;
248         int pathlen;
249
250         pathlen = strlen(path);
251
252         p = &(data[0]);
253         p = rpc_add_credentials(p);
254
255         *p++ = htonl(pathlen);
256         if (pathlen & 3)
257                 *(p + pathlen / 4) = 0;
258         memcpy(p, path, pathlen);
259         p += (pathlen + 3) / 4;
260
261         len = (uint32_t *)p - (uint32_t *)&(data[0]);
262
263         rpc_req(PROG_MOUNT, MOUNT_ADDENTRY, data, len);
264 }
265
266 /**************************************************************************
267 NFS_UMOUNTALL - Unmount all our NFS Filesystems on the Server
268 **************************************************************************/
269 static void nfs_umountall_req(void)
270 {
271         uint32_t data[1024];
272         uint32_t *p;
273         int len;
274
275         if ((nfs_server_mount_port == -1) || (!fs_mounted))
276                 /* Nothing mounted, nothing to umount */
277                 return;
278
279         p = &(data[0]);
280         p = rpc_add_credentials(p);
281
282         len = (uint32_t *)p - (uint32_t *)&(data[0]);
283
284         rpc_req(PROG_MOUNT, MOUNT_UMOUNTALL, data, len);
285 }
286
287 /***************************************************************************
288  * NFS_READLINK (AH 2003-07-14)
289  * This procedure is called when read of the first block fails -
290  * this probably happens when it's a directory or a symlink
291  * In case of successful readlink(), the dirname is manipulated,
292  * so that inside the nfs() function a recursion can be done.
293  **************************************************************************/
294 static void nfs_readlink_req(void)
295 {
296         uint32_t data[1024];
297         uint32_t *p;
298         int len;
299
300         p = &(data[0]);
301         p = rpc_add_credentials(p);
302
303         if (supported_nfs_versions & NFSV2_FLAG) {
304                 memcpy(p, filefh, NFS_FHSIZE);
305                 p += (NFS_FHSIZE / 4);
306         } else { /* NFSV3_FLAG */
307                 *p++ = htonl(filefh3_length);
308                 memcpy(p, filefh, filefh3_length);
309                 p += (filefh3_length / 4);
310         }
311
312         len = (uint32_t *)p - (uint32_t *)&(data[0]);
313
314         rpc_req(PROG_NFS, NFS_READLINK, data, len);
315 }
316
317 /**************************************************************************
318 NFS_LOOKUP - Lookup Pathname
319 **************************************************************************/
320 static void nfs_lookup_req(char *fname)
321 {
322         uint32_t data[1024];
323         uint32_t *p;
324         int len;
325         int fnamelen;
326
327         fnamelen = strlen(fname);
328
329         p = &(data[0]);
330         p = rpc_add_credentials(p);
331
332         if (supported_nfs_versions & NFSV2_FLAG) {
333                 memcpy(p, dirfh, NFS_FHSIZE);
334                 p += (NFS_FHSIZE / 4);
335                 *p++ = htonl(fnamelen);
336                 if (fnamelen & 3)
337                         *(p + fnamelen / 4) = 0;
338                 memcpy(p, fname, fnamelen);
339                 p += (fnamelen + 3) / 4;
340
341                 len = (uint32_t *)p - (uint32_t *)&(data[0]);
342
343                 rpc_req(PROG_NFS, NFS_LOOKUP, data, len);
344         } else {  /* NFSV3_FLAG */
345                 *p++ = htonl(NFS_FHSIZE);       /* Dir handle length */
346                 memcpy(p, dirfh, NFS_FHSIZE);
347                 p += (NFS_FHSIZE / 4);
348                 *p++ = htonl(fnamelen);
349                 if (fnamelen & 3)
350                         *(p + fnamelen / 4) = 0;
351                 memcpy(p, fname, fnamelen);
352                 p += (fnamelen + 3) / 4;
353
354                 len = (uint32_t *)p - (uint32_t *)&(data[0]);
355
356                 rpc_req(PROG_NFS, NFS3PROC_LOOKUP, data, len);
357         }
358 }
359
360 /**************************************************************************
361 NFS_READ - Read File on NFS Server
362 **************************************************************************/
363 static void nfs_read_req(int offset, int readlen)
364 {
365         uint32_t data[1024];
366         uint32_t *p;
367         int len;
368
369         p = &(data[0]);
370         p = rpc_add_credentials(p);
371
372         if (supported_nfs_versions & NFSV2_FLAG) {
373                 memcpy(p, filefh, NFS_FHSIZE);
374                 p += (NFS_FHSIZE / 4);
375                 *p++ = htonl(offset);
376                 *p++ = htonl(readlen);
377                 *p++ = 0;
378         } else { /* NFSV3_FLAG */
379                 *p++ = htonl(filefh3_length);
380                 memcpy(p, filefh, filefh3_length);
381                 p += (filefh3_length / 4);
382                 *p++ = htonl(0); /* offset is 64-bit long, so fill with 0 */
383                 *p++ = htonl(offset);
384                 *p++ = htonl(readlen);
385                 *p++ = 0;
386         }
387
388         len = (uint32_t *)p - (uint32_t *)&(data[0]);
389
390         rpc_req(PROG_NFS, NFS_READ, data, len);
391 }
392
393 /**************************************************************************
394 RPC request dispatcher
395 **************************************************************************/
396 static void nfs_send(void)
397 {
398         debug("%s\n", __func__);
399
400         switch (nfs_state) {
401         case STATE_PRCLOOKUP_PROG_MOUNT_REQ:
402                 if (supported_nfs_versions & NFSV2_FLAG)
403                         rpc_lookup_req(PROG_MOUNT, 1);
404                 else  /* NFSV3_FLAG */
405                         rpc_lookup_req(PROG_MOUNT, 3);
406                 break;
407         case STATE_PRCLOOKUP_PROG_NFS_REQ:
408                 if (supported_nfs_versions & NFSV2_FLAG)
409                         rpc_lookup_req(PROG_NFS, 2);
410                 else  /* NFSV3_FLAG */
411                         rpc_lookup_req(PROG_NFS, 3);
412                 break;
413         case STATE_MOUNT_REQ:
414                 nfs_mount_req(nfs_path);
415                 break;
416         case STATE_UMOUNT_REQ:
417                 nfs_umountall_req();
418                 break;
419         case STATE_LOOKUP_REQ:
420                 nfs_lookup_req(nfs_filename);
421                 break;
422         case STATE_READ_REQ:
423                 nfs_read_req(nfs_offset, nfs_len);
424                 break;
425         case STATE_READLINK_REQ:
426                 nfs_readlink_req();
427                 break;
428         }
429 }
430
431 /**************************************************************************
432 Handlers for the reply from server
433 **************************************************************************/
434
435 static int rpc_lookup_reply(int prog, uchar *pkt, unsigned len)
436 {
437         struct rpc_t rpc_pkt;
438
439         memcpy(&rpc_pkt.u.data[0], pkt, len);
440
441         debug("%s\n", __func__);
442
443         if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
444                 return -NFS_RPC_ERR;
445         else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
446                 return -NFS_RPC_DROP;
447
448         if (rpc_pkt.u.reply.rstatus  ||
449             rpc_pkt.u.reply.verifier ||
450             rpc_pkt.u.reply.astatus)
451                 return -1;
452
453         switch (prog) {
454         case PROG_MOUNT:
455                 nfs_server_mount_port = ntohl(rpc_pkt.u.reply.data[0]);
456                 break;
457         case PROG_NFS:
458                 nfs_server_port = ntohl(rpc_pkt.u.reply.data[0]);
459                 break;
460         }
461
462         return 0;
463 }
464
465 static int nfs_mount_reply(uchar *pkt, unsigned len)
466 {
467         struct rpc_t rpc_pkt;
468
469         debug("%s\n", __func__);
470
471         memcpy(&rpc_pkt.u.data[0], pkt, len);
472
473         if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
474                 return -NFS_RPC_ERR;
475         else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
476                 return -NFS_RPC_DROP;
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         fs_mounted = 1;
485         /*  NFSv2 and NFSv3 use same structure */
486         memcpy(dirfh, rpc_pkt.u.reply.data + 1, NFS_FHSIZE);
487
488         return 0;
489 }
490
491 static int nfs_umountall_reply(uchar *pkt, unsigned len)
492 {
493         struct rpc_t rpc_pkt;
494
495         debug("%s\n", __func__);
496
497         memcpy(&rpc_pkt.u.data[0], pkt, len);
498
499         if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
500                 return -NFS_RPC_ERR;
501         else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
502                 return -NFS_RPC_DROP;
503
504         if (rpc_pkt.u.reply.rstatus  ||
505             rpc_pkt.u.reply.verifier ||
506             rpc_pkt.u.reply.astatus)
507                 return -1;
508
509         fs_mounted = 0;
510         memset(dirfh, 0, sizeof(dirfh));
511
512         return 0;
513 }
514
515 static int nfs_lookup_reply(uchar *pkt, unsigned len)
516 {
517         struct rpc_t rpc_pkt;
518
519         debug("%s\n", __func__);
520
521         memcpy(&rpc_pkt.u.data[0], pkt, len);
522
523         if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
524                 return -NFS_RPC_ERR;
525         else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
526                 return -NFS_RPC_DROP;
527
528         if (rpc_pkt.u.reply.rstatus  ||
529             rpc_pkt.u.reply.verifier ||
530             rpc_pkt.u.reply.astatus  ||
531             rpc_pkt.u.reply.data[0]) {
532                 switch (ntohl(rpc_pkt.u.reply.astatus)) {
533                 case NFS_RPC_SUCCESS: /* Not an error */
534                         break;
535                 case NFS_RPC_PROG_MISMATCH:
536                         /* Remote can't support NFS version */
537                         switch (ntohl(rpc_pkt.u.reply.data[0])) {
538                         /* Minimal supported NFS version */
539                         case 3:
540                                 debug("*** Warning: NFS version not supported: Requested: V%d, accepted: min V%d - max V%d\n",
541                                       (supported_nfs_versions & NFSV2_FLAG) ?
542                                                 2 : 3,
543                                       ntohl(rpc_pkt.u.reply.data[0]),
544                                       ntohl(rpc_pkt.u.reply.data[1]));
545                                 debug("Will retry with NFSv3\n");
546                                 /* Clear NFSV2_FLAG from supported versions */
547                                 supported_nfs_versions &= ~NFSV2_FLAG;
548                                 return -NFS_RPC_PROG_MISMATCH;
549                         case 4:
550                         default:
551                                 puts("*** ERROR: NFS version not supported");
552                                 debug(": Requested: V%d, accepted: min V%d - max V%d\n",
553                                       (supported_nfs_versions & NFSV2_FLAG) ?
554                                                 2 : 3,
555                                       ntohl(rpc_pkt.u.reply.data[0]),
556                                       ntohl(rpc_pkt.u.reply.data[1]));
557                                 puts("\n");
558                         }
559                         break;
560                 case NFS_RPC_PROG_UNAVAIL:
561                 case NFS_RPC_PROC_UNAVAIL:
562                 case NFS_RPC_GARBAGE_ARGS:
563                 case NFS_RPC_SYSTEM_ERR:
564                 default: /* Unknown error on 'accept state' flag */
565                         debug("*** ERROR: accept state error (%d)\n",
566                               ntohl(rpc_pkt.u.reply.astatus));
567                         break;
568                 }
569                 return -1;
570         }
571
572         if (supported_nfs_versions & NFSV2_FLAG) {
573                 if (((uchar *)&(rpc_pkt.u.reply.data[0]) - (uchar *)(&rpc_pkt) + NFS_FHSIZE) > len)
574                         return -NFS_RPC_DROP;
575                 memcpy(filefh, rpc_pkt.u.reply.data + 1, NFS_FHSIZE);
576         } else {  /* NFSV3_FLAG */
577                 filefh3_length = ntohl(rpc_pkt.u.reply.data[1]);
578                 if (filefh3_length > NFS3_FHSIZE)
579                         filefh3_length  = NFS3_FHSIZE;
580                 if (((uchar *)&(rpc_pkt.u.reply.data[0]) - (uchar *)(&rpc_pkt) + filefh3_length) > len)
581                         return -NFS_RPC_DROP;
582                 memcpy(filefh, rpc_pkt.u.reply.data + 2, filefh3_length);
583         }
584
585         return 0;
586 }
587
588 static int nfs3_get_attributes_offset(uint32_t *data)
589 {
590         if (data[1]) {
591                 /* 'attributes_follow' flag is TRUE,
592                  * so we have attributes on 21 dwords */
593                 /* Skip unused values :
594                         type;   32 bits value,
595                         mode;   32 bits value,
596                         nlink;  32 bits value,
597                         uid;    32 bits value,
598                         gid;    32 bits value,
599                         size;   64 bits value,
600                         used;   64 bits value,
601                         rdev;   64 bits value,
602                         fsid;   64 bits value,
603                         fileid; 64 bits value,
604                         atime;  64 bits value,
605                         mtime;  64 bits value,
606                         ctime;  64 bits value,
607                 */
608                 return 22;
609         } else {
610                 /* 'attributes_follow' flag is FALSE,
611                  * so we don't have any attributes */
612                 return 1;
613         }
614 }
615
616 static int nfs_readlink_reply(uchar *pkt, unsigned len)
617 {
618         struct rpc_t rpc_pkt;
619         int rlen;
620         int nfsv3_data_offset = 0;
621
622         debug("%s\n", __func__);
623
624         memcpy((unsigned char *)&rpc_pkt, pkt, len);
625
626         if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
627                 return -NFS_RPC_ERR;
628         else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
629                 return -NFS_RPC_DROP;
630
631         if (rpc_pkt.u.reply.rstatus  ||
632             rpc_pkt.u.reply.verifier ||
633             rpc_pkt.u.reply.astatus  ||
634             rpc_pkt.u.reply.data[0])
635                 return -1;
636
637         if (!(supported_nfs_versions & NFSV2_FLAG)) { /* NFSV3_FLAG */
638                 nfsv3_data_offset =
639                         nfs3_get_attributes_offset(rpc_pkt.u.reply.data);
640         }
641
642         /* new path length */
643         rlen = ntohl(rpc_pkt.u.reply.data[1 + nfsv3_data_offset]);
644
645         if (((uchar *)&(rpc_pkt.u.reply.data[0]) - (uchar *)(&rpc_pkt) + rlen) > len)
646                 return -NFS_RPC_DROP;
647
648         if (*((char *)&(rpc_pkt.u.reply.data[2 + nfsv3_data_offset])) != '/') {
649                 int pathlen;
650
651                 strcat(nfs_path, "/");
652                 pathlen = strlen(nfs_path);
653                 memcpy(nfs_path + pathlen,
654                        (uchar *)&(rpc_pkt.u.reply.data[2 + nfsv3_data_offset]),
655                        rlen);
656                 nfs_path[pathlen + rlen] = 0;
657         } else {
658                 memcpy(nfs_path,
659                        (uchar *)&(rpc_pkt.u.reply.data[2 + nfsv3_data_offset]),
660                        rlen);
661                 nfs_path[rlen] = 0;
662         }
663         return 0;
664 }
665
666 static int nfs_read_reply(uchar *pkt, unsigned len)
667 {
668         struct rpc_t rpc_pkt;
669         int rlen;
670         uchar *data_ptr;
671
672         debug("%s\n", __func__);
673
674         memcpy(&rpc_pkt.u.data[0], pkt, sizeof(rpc_pkt.u.reply));
675
676         if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
677                 return -NFS_RPC_ERR;
678         else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
679                 return -NFS_RPC_DROP;
680
681         if (rpc_pkt.u.reply.rstatus  ||
682             rpc_pkt.u.reply.verifier ||
683             rpc_pkt.u.reply.astatus  ||
684             rpc_pkt.u.reply.data[0]) {
685                 if (rpc_pkt.u.reply.rstatus)
686                         return -9999;
687                 if (rpc_pkt.u.reply.astatus)
688                         return -9999;
689                 return -ntohl(rpc_pkt.u.reply.data[0]);
690         }
691
692         if ((nfs_offset != 0) && !((nfs_offset) %
693                         (NFS_READ_SIZE / 2 * 10 * HASHES_PER_LINE)))
694                 puts("\n\t ");
695         if (!(nfs_offset % ((NFS_READ_SIZE / 2) * 10)))
696                 putc('#');
697
698         if (supported_nfs_versions & NFSV2_FLAG) {
699                 rlen = ntohl(rpc_pkt.u.reply.data[18]);
700                 data_ptr = (uchar *)&(rpc_pkt.u.reply.data[19]);
701         } else {  /* NFSV3_FLAG */
702                 int nfsv3_data_offset =
703                         nfs3_get_attributes_offset(rpc_pkt.u.reply.data);
704
705                 /* count value */
706                 rlen = ntohl(rpc_pkt.u.reply.data[1 + nfsv3_data_offset]);
707                 /* Skip unused values :
708                         EOF:            32 bits value,
709                         data_size:      32 bits value,
710                 */
711                 data_ptr = (uchar *)
712                         &(rpc_pkt.u.reply.data[4 + nfsv3_data_offset]);
713         }
714
715         if (((uchar *)&(rpc_pkt.u.reply.data[0]) - (uchar *)(&rpc_pkt) + rlen) > len)
716                         return -9999;
717
718         if (store_block(data_ptr, nfs_offset, rlen))
719                         return -9999;
720
721         return rlen;
722 }
723
724 /**************************************************************************
725 Interfaces of U-BOOT
726 **************************************************************************/
727 static void nfs_timeout_handler(void)
728 {
729         if (++nfs_timeout_count > NFS_RETRY_COUNT) {
730                 puts("\nRetry count exceeded; starting again\n");
731                 net_start_again();
732         } else {
733                 puts("T ");
734                 net_set_timeout_handler(nfs_timeout +
735                                         NFS_TIMEOUT * nfs_timeout_count,
736                                         nfs_timeout_handler);
737                 nfs_send();
738         }
739 }
740
741 static void nfs_handler(uchar *pkt, unsigned dest, struct in_addr sip,
742                         unsigned src, unsigned len)
743 {
744         int rlen;
745         int reply;
746
747         debug("%s\n", __func__);
748
749         if (len > sizeof(struct rpc_t))
750                 return;
751
752         if (dest != nfs_our_port)
753                 return;
754
755         switch (nfs_state) {
756         case STATE_PRCLOOKUP_PROG_MOUNT_REQ:
757                 if (rpc_lookup_reply(PROG_MOUNT, pkt, len) == -NFS_RPC_DROP)
758                         break;
759                 nfs_state = STATE_PRCLOOKUP_PROG_NFS_REQ;
760                 nfs_send();
761                 break;
762
763         case STATE_PRCLOOKUP_PROG_NFS_REQ:
764                 if (rpc_lookup_reply(PROG_NFS, pkt, len) == -NFS_RPC_DROP)
765                         break;
766                 nfs_state = STATE_MOUNT_REQ;
767                 nfs_send();
768                 break;
769
770         case STATE_MOUNT_REQ:
771                 reply = nfs_mount_reply(pkt, len);
772                 if (reply == -NFS_RPC_DROP) {
773                         break;
774                 } else if (reply == -NFS_RPC_ERR) {
775                         puts("*** ERROR: Cannot mount\n");
776                         /* just to be sure... */
777                         nfs_state = STATE_UMOUNT_REQ;
778                         nfs_send();
779                 } else {
780                         nfs_state = STATE_LOOKUP_REQ;
781                         nfs_send();
782                 }
783                 break;
784
785         case STATE_UMOUNT_REQ:
786                 reply = nfs_umountall_reply(pkt, len);
787                 if (reply == -NFS_RPC_DROP) {
788                         break;
789                 } else if (reply == -NFS_RPC_ERR) {
790                         debug("*** ERROR: Cannot umount\n");
791                         net_set_state(NETLOOP_FAIL);
792                 } else {
793                         puts("\ndone\n");
794                         net_set_state(nfs_download_state);
795                 }
796                 break;
797
798         case STATE_LOOKUP_REQ:
799                 reply = nfs_lookup_reply(pkt, len);
800                 if (reply == -NFS_RPC_DROP) {
801                         break;
802                 } else if (reply == -NFS_RPC_ERR) {
803                         puts("*** ERROR: File lookup fail\n");
804                         nfs_state = STATE_UMOUNT_REQ;
805                         nfs_send();
806                 } else if (reply == -NFS_RPC_PROG_MISMATCH &&
807                            supported_nfs_versions != 0) {
808                         /* umount */
809                         nfs_state = STATE_UMOUNT_REQ;
810                         nfs_send();
811                         /* And retry with another supported version */
812                         nfs_state = STATE_PRCLOOKUP_PROG_MOUNT_REQ;
813                         nfs_send();
814                 } else {
815                         nfs_state = STATE_READ_REQ;
816                         nfs_offset = 0;
817                         nfs_len = NFS_READ_SIZE;
818                         nfs_send();
819                 }
820                 break;
821
822         case STATE_READLINK_REQ:
823                 reply = nfs_readlink_reply(pkt, len);
824                 if (reply == -NFS_RPC_DROP) {
825                         break;
826                 } else if (reply == -NFS_RPC_ERR) {
827                         puts("*** ERROR: Symlink fail\n");
828                         nfs_state = STATE_UMOUNT_REQ;
829                         nfs_send();
830                 } else {
831                         debug("Symlink --> %s\n", nfs_path);
832                         nfs_filename = basename(nfs_path);
833                         nfs_path     = dirname(nfs_path);
834
835                         nfs_state = STATE_MOUNT_REQ;
836                         nfs_send();
837                 }
838                 break;
839
840         case STATE_READ_REQ:
841                 rlen = nfs_read_reply(pkt, len);
842                 if (rlen == -NFS_RPC_DROP)
843                         break;
844                 net_set_timeout_handler(nfs_timeout, nfs_timeout_handler);
845                 if (rlen > 0) {
846                         nfs_offset += rlen;
847                         nfs_send();
848                 } else if ((rlen == -NFSERR_ISDIR) || (rlen == -NFSERR_INVAL)) {
849                         /* symbolic link */
850                         nfs_state = STATE_READLINK_REQ;
851                         nfs_send();
852                 } else {
853                         if (!rlen)
854                                 nfs_download_state = NETLOOP_SUCCESS;
855                         if (rlen < 0)
856                                 debug("NFS READ error (%d)\n", rlen);
857                         nfs_state = STATE_UMOUNT_REQ;
858                         nfs_send();
859                 }
860                 break;
861         }
862 }
863
864
865 void nfs_start(void)
866 {
867         debug("%s\n", __func__);
868         nfs_download_state = NETLOOP_FAIL;
869
870         nfs_server_ip = net_server_ip;
871         nfs_path = (char *)nfs_path_buff;
872
873         if (nfs_path == NULL) {
874                 net_set_state(NETLOOP_FAIL);
875                 printf("*** ERROR: Fail allocate memory\n");
876                 return;
877         }
878
879         if (!net_parse_bootfile(&nfs_server_ip, nfs_path,
880                                 sizeof(nfs_path_buff))) {
881                 sprintf(nfs_path, "/nfsroot/%02X%02X%02X%02X.img",
882                         net_ip.s_addr & 0xFF,
883                         (net_ip.s_addr >>  8) & 0xFF,
884                         (net_ip.s_addr >> 16) & 0xFF,
885                         (net_ip.s_addr >> 24) & 0xFF);
886
887                 printf("*** Warning: no boot file name; using '%s'\n",
888                        nfs_path);
889         }
890
891         nfs_filename = basename(nfs_path);
892         nfs_path     = dirname(nfs_path);
893
894         printf("Using %s device\n", eth_get_name());
895
896         printf("File transfer via NFS from server %pI4; our IP address is %pI4",
897                &nfs_server_ip, &net_ip);
898
899         /* Check if we need to send across this subnet */
900         if (net_gateway.s_addr && net_netmask.s_addr) {
901                 struct in_addr our_net;
902                 struct in_addr server_net;
903
904                 our_net.s_addr = net_ip.s_addr & net_netmask.s_addr;
905                 server_net.s_addr = nfs_server_ip.s_addr & net_netmask.s_addr;
906                 if (our_net.s_addr != server_net.s_addr)
907                         printf("; sending through gateway %pI4",
908                                &net_gateway);
909         }
910         printf("\nFilename '%s/%s'.", nfs_path, nfs_filename);
911
912         if (net_boot_file_expected_size_in_blocks) {
913                 printf(" Size is 0x%x Bytes = ",
914                        net_boot_file_expected_size_in_blocks << 9);
915                 print_size(net_boot_file_expected_size_in_blocks << 9, "");
916         }
917         printf("\nLoad address: 0x%lx\nLoading: *\b", image_load_addr);
918
919         net_set_timeout_handler(nfs_timeout, nfs_timeout_handler);
920         net_set_udp_handler(nfs_handler);
921
922         nfs_timeout_count = 0;
923         nfs_state = STATE_PRCLOOKUP_PROG_MOUNT_REQ;
924
925         /*nfs_our_port = 4096 + (get_ticks() % 3072);*/
926         /*FIX ME !!!*/
927         nfs_our_port = 1000;
928
929         /* zero out server ether in case the server ip has changed */
930         memset(net_server_ethaddr, 0, 6);
931
932         nfs_send();
933 }