1cbd8a35cSwdenk /*
2cbd8a35cSwdenk * NFS support driver - based on etherboot and U-BOOT's tftp.c
3cbd8a35cSwdenk *
4cbd8a35cSwdenk * Masami Komiya <mkomiya@sonare.it> 2004
5cbd8a35cSwdenk *
6cbd8a35cSwdenk */
7cbd8a35cSwdenk
8cbd8a35cSwdenk /* NOTE: the NFS code is heavily inspired by the NetBSD netboot code (read:
9cbd8a35cSwdenk * large portions are copied verbatim) as distributed in OSKit 0.97. A few
10cbd8a35cSwdenk * changes were necessary to adapt the code to Etherboot and to fix several
11cbd8a35cSwdenk * inconsistencies. Also the RPC message preparation is done "by hand" to
12cbd8a35cSwdenk * avoid adding netsprintf() which I find hard to understand and use. */
13cbd8a35cSwdenk
14cbd8a35cSwdenk /* NOTE 2: Etherboot does not care about things beyond the kernel image, so
15cbd8a35cSwdenk * it loads the kernel image off the boot server (ARP_SERVER) and does not
16cbd8a35cSwdenk * access the client root disk (root-path in dhcpd.conf), which would use
17cbd8a35cSwdenk * ARP_ROOTSERVER. The root disk is something the operating system we are
18cbd8a35cSwdenk * about to load needs to use. This is different from the OSKit 0.97 logic. */
19cbd8a35cSwdenk
20cbd8a35cSwdenk /* NOTE 3: Symlink handling introduced by Anselm M Hoffmeister, 2003-July-14
21cbd8a35cSwdenk * If a symlink is encountered, it is followed as far as possible (recursion
22cbd8a35cSwdenk * possible, maximum 16 steps). There is no clearing of ".."'s inside the
23cbd8a35cSwdenk * path, so please DON'T DO THAT. thx. */
24cbd8a35cSwdenk
25b0baca98SGuillaume GARDET /* NOTE 4: NFSv3 support added by Guillaume GARDET, 2016-June-20.
26b0baca98SGuillaume GARDET * NFSv2 is still used by default. But if server does not support NFSv2, then
27b0baca98SGuillaume GARDET * NFSv3 is used, if available on NFS server. */
28b0baca98SGuillaume GARDET
29cbd8a35cSwdenk #include <common.h>
30cbd8a35cSwdenk #include <command.h>
31cbd8a35cSwdenk #include <net.h>
32cbd8a35cSwdenk #include <malloc.h>
3355d5fd9aSJoe Hershberger #include <mapmem.h>
34cbd8a35cSwdenk #include "nfs.h"
35cbd8a35cSwdenk #include "bootp.h"
36cbd8a35cSwdenk
37cbd8a35cSwdenk #define HASHES_PER_LINE 65 /* Number of "loading" hashes per line */
38fe891ecfSHiroshi Ito #define NFS_RETRY_COUNT 30
3948a3e999STetsuyuki Kobayashi #ifndef CONFIG_NFS_TIMEOUT
4049f3bdbbSBartlomiej Sieka # define NFS_TIMEOUT 2000UL
4148a3e999STetsuyuki Kobayashi #else
4248a3e999STetsuyuki Kobayashi # define NFS_TIMEOUT CONFIG_NFS_TIMEOUT
4348a3e999STetsuyuki Kobayashi #endif
44cbd8a35cSwdenk
45fa84fa70SMatthias Brugger #define NFS_RPC_ERR 1
46fa84fa70SMatthias Brugger #define NFS_RPC_DROP 124
47fa84fa70SMatthias Brugger
48c9f6c91bSJoe Hershberger static int fs_mounted;
49c9f6c91bSJoe Hershberger static unsigned long rpc_id;
50cbd8a35cSwdenk static int nfs_offset = -1;
51cbd8a35cSwdenk static int nfs_len;
52fa84fa70SMatthias Brugger static ulong nfs_timeout = NFS_TIMEOUT;
53cbd8a35cSwdenk
54b0baca98SGuillaume GARDET static char dirfh[NFS_FHSIZE]; /* NFSv2 / NFSv3 file handle of directory */
555280c769SJoe Hershberger static char filefh[NFS3_FHSIZE]; /* NFSv2 / NFSv3 file handle */
565280c769SJoe Hershberger static int filefh3_length; /* (variable) length of filefh when NFSv3 */
57cbd8a35cSwdenk
5822f6e99dSJoe Hershberger static enum net_loop_state nfs_download_state;
59049a95a7SJoe Hershberger static struct in_addr nfs_server_ip;
6068c76a3aSJoe Hershberger static int nfs_server_mount_port;
6168c76a3aSJoe Hershberger static int nfs_server_port;
6268c76a3aSJoe Hershberger static int nfs_our_port;
6368c76a3aSJoe Hershberger static int nfs_timeout_count;
6468c76a3aSJoe Hershberger static int nfs_state;
65cbd8a35cSwdenk #define STATE_PRCLOOKUP_PROG_MOUNT_REQ 1
66cbd8a35cSwdenk #define STATE_PRCLOOKUP_PROG_NFS_REQ 2
67cbd8a35cSwdenk #define STATE_MOUNT_REQ 3
68cbd8a35cSwdenk #define STATE_UMOUNT_REQ 4
69cbd8a35cSwdenk #define STATE_LOOKUP_REQ 5
70cbd8a35cSwdenk #define STATE_READ_REQ 6
71cbd8a35cSwdenk #define STATE_READLINK_REQ 7
72cbd8a35cSwdenk
73cbd8a35cSwdenk static char *nfs_filename;
74cbd8a35cSwdenk static char *nfs_path;
75cbd8a35cSwdenk static char nfs_path_buff[2048];
76cbd8a35cSwdenk
77b0baca98SGuillaume GARDET #define NFSV2_FLAG 1
78b0baca98SGuillaume GARDET #define NFSV3_FLAG 1 << 1
79b0baca98SGuillaume GARDET static char supported_nfs_versions = NFSV2_FLAG | NFSV3_FLAG;
80b0baca98SGuillaume GARDET
store_block(uchar * src,unsigned offset,unsigned len)8168c76a3aSJoe Hershberger static inline int store_block(uchar *src, unsigned offset, unsigned len)
82cbd8a35cSwdenk {
83a084f7daSwdenk ulong newsize = offset + len;
846d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_SYS_DIRECT_FLASH_NFS
85cbd8a35cSwdenk int i, rc = 0;
86cbd8a35cSwdenk
876d0f6bcfSJean-Christophe PLAGNIOL-VILLARD for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) {
88cbd8a35cSwdenk /* start address in flash? */
89cbd8a35cSwdenk if (load_addr + offset >= flash_info[i].start[0]) {
90cbd8a35cSwdenk rc = 1;
91cbd8a35cSwdenk break;
92cbd8a35cSwdenk }
93cbd8a35cSwdenk }
94cbd8a35cSwdenk
95cbd8a35cSwdenk if (rc) { /* Flash is destination for this packet */
96cbd8a35cSwdenk rc = flash_write((uchar *)src, (ulong)(load_addr+offset), len);
97cbd8a35cSwdenk if (rc) {
98cbd8a35cSwdenk flash_perror(rc);
9923a7a32dSWolfgang Denk return -1;
100cbd8a35cSwdenk }
101cbd8a35cSwdenk } else
1026d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #endif /* CONFIG_SYS_DIRECT_FLASH_NFS */
103cbd8a35cSwdenk {
10455d5fd9aSJoe Hershberger void *ptr = map_sysmem(load_addr + offset, len);
10555d5fd9aSJoe Hershberger
10655d5fd9aSJoe Hershberger memcpy(ptr, src, len);
10755d5fd9aSJoe Hershberger unmap_sysmem(ptr);
108cbd8a35cSwdenk }
109a084f7daSwdenk
1101411157dSJoe Hershberger if (net_boot_file_size < (offset + len))
1111411157dSJoe Hershberger net_boot_file_size = newsize;
11223a7a32dSWolfgang Denk return 0;
113cbd8a35cSwdenk }
114cbd8a35cSwdenk
basename(char * path)11568c76a3aSJoe Hershberger static char *basename(char *path)
116cbd8a35cSwdenk {
117cbd8a35cSwdenk char *fname;
118cbd8a35cSwdenk
119cbd8a35cSwdenk fname = path + strlen(path) - 1;
120cbd8a35cSwdenk while (fname >= path) {
121cbd8a35cSwdenk if (*fname == '/') {
122cbd8a35cSwdenk fname++;
123cbd8a35cSwdenk break;
124cbd8a35cSwdenk }
125cbd8a35cSwdenk fname--;
126cbd8a35cSwdenk }
127cbd8a35cSwdenk return fname;
128cbd8a35cSwdenk }
129cbd8a35cSwdenk
dirname(char * path)13068c76a3aSJoe Hershberger static char *dirname(char *path)
131cbd8a35cSwdenk {
132cbd8a35cSwdenk char *fname;
133cbd8a35cSwdenk
134cbd8a35cSwdenk fname = basename(path);
135cbd8a35cSwdenk --fname;
136cbd8a35cSwdenk *fname = '\0';
137cbd8a35cSwdenk return path;
138cbd8a35cSwdenk }
139cbd8a35cSwdenk
140cbd8a35cSwdenk /**************************************************************************
141cbd8a35cSwdenk RPC_ADD_CREDENTIALS - Add RPC authentication/verifier entries
142cbd8a35cSwdenk **************************************************************************/
rpc_add_credentials(uint32_t * p)143e4ead4a2SRalf Hubert static uint32_t *rpc_add_credentials(uint32_t *p)
144cbd8a35cSwdenk {
145cbd8a35cSwdenk /* Here's the executive summary on authentication requirements of the
146cbd8a35cSwdenk * various NFS server implementations: Linux accepts both AUTH_NONE
147cbd8a35cSwdenk * and AUTH_UNIX authentication (also accepts an empty hostname field
148cbd8a35cSwdenk * in the AUTH_UNIX scheme). *BSD refuses AUTH_NONE, but accepts
149cbd8a35cSwdenk * AUTH_UNIX (also accepts an empty hostname field in the AUTH_UNIX
150cbd8a35cSwdenk * scheme). To be safe, use AUTH_UNIX and pass the hostname if we have
151cbd8a35cSwdenk * it (if the BOOTP/DHCP reply didn't give one, just use an empty
152cbd8a35cSwdenk * hostname). */
153cbd8a35cSwdenk
154cbd8a35cSwdenk /* Provide an AUTH_UNIX credential. */
155cbd8a35cSwdenk *p++ = htonl(1); /* AUTH_UNIX */
1561ff65d44SJoe Hershberger *p++ = htonl(20); /* auth length */
1571ff65d44SJoe Hershberger *p++ = 0; /* stamp */
1581ff65d44SJoe Hershberger *p++ = 0; /* hostname string */
159cbd8a35cSwdenk *p++ = 0; /* uid */
160cbd8a35cSwdenk *p++ = 0; /* gid */
161cbd8a35cSwdenk *p++ = 0; /* auxiliary gid list */
162cbd8a35cSwdenk
163cbd8a35cSwdenk /* Provide an AUTH_NONE verifier. */
164cbd8a35cSwdenk *p++ = 0; /* AUTH_NONE */
165cbd8a35cSwdenk *p++ = 0; /* auth length */
166cbd8a35cSwdenk
167cbd8a35cSwdenk return p;
168cbd8a35cSwdenk }
169cbd8a35cSwdenk
170cbd8a35cSwdenk /**************************************************************************
171cbd8a35cSwdenk RPC_LOOKUP - Lookup RPC Port numbers
172cbd8a35cSwdenk **************************************************************************/
rpc_req(int rpc_prog,int rpc_proc,uint32_t * data,int datalen)173a73588feSJoe Hershberger static void rpc_req(int rpc_prog, int rpc_proc, uint32_t *data, int datalen)
174cbd8a35cSwdenk {
175a73588feSJoe Hershberger struct rpc_t rpc_pkt;
176cbd8a35cSwdenk unsigned long id;
177a73588feSJoe Hershberger uint32_t *p;
178cbd8a35cSwdenk int pktlen;
179cbd8a35cSwdenk int sport;
180cbd8a35cSwdenk
181c3f9d493Swdenk id = ++rpc_id;
182a73588feSJoe Hershberger rpc_pkt.u.call.id = htonl(id);
183a73588feSJoe Hershberger rpc_pkt.u.call.type = htonl(MSG_CALL);
184a73588feSJoe Hershberger rpc_pkt.u.call.rpcvers = htonl(2); /* use RPC version 2 */
185a73588feSJoe Hershberger rpc_pkt.u.call.prog = htonl(rpc_prog);
186b0baca98SGuillaume GARDET switch (rpc_prog) {
187b0baca98SGuillaume GARDET case PROG_NFS:
188b0baca98SGuillaume GARDET if (supported_nfs_versions & NFSV2_FLAG)
189a73588feSJoe Hershberger rpc_pkt.u.call.vers = htonl(2); /* NFS v2 */
190b0baca98SGuillaume GARDET else /* NFSV3_FLAG */
191a73588feSJoe Hershberger rpc_pkt.u.call.vers = htonl(3); /* NFS v3 */
192b0baca98SGuillaume GARDET break;
193b0baca98SGuillaume GARDET case PROG_PORTMAP:
194b0baca98SGuillaume GARDET case PROG_MOUNT:
195b0baca98SGuillaume GARDET default:
196a73588feSJoe Hershberger rpc_pkt.u.call.vers = htonl(2); /* portmapper is version 2 */
197b0baca98SGuillaume GARDET }
198a73588feSJoe Hershberger rpc_pkt.u.call.proc = htonl(rpc_proc);
199a73588feSJoe Hershberger p = (uint32_t *)&(rpc_pkt.u.call.data);
200cbd8a35cSwdenk
201a73588feSJoe Hershberger if (datalen)
202a73588feSJoe Hershberger memcpy((char *)p, (char *)data, datalen*sizeof(uint32_t));
203a73588feSJoe Hershberger
204a73588feSJoe Hershberger pktlen = (char *)p + datalen * sizeof(uint32_t) - (char *)&rpc_pkt;
205a73588feSJoe Hershberger
206a73588feSJoe Hershberger memcpy((char *)net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE,
207a73588feSJoe Hershberger &rpc_pkt.u.data[0], pktlen);
208cbd8a35cSwdenk
209cbd8a35cSwdenk if (rpc_prog == PROG_PORTMAP)
210cbd8a35cSwdenk sport = SUNRPC_PORT;
211cbd8a35cSwdenk else if (rpc_prog == PROG_MOUNT)
21268c76a3aSJoe Hershberger sport = nfs_server_mount_port;
213cbd8a35cSwdenk else
21468c76a3aSJoe Hershberger sport = nfs_server_port;
215cbd8a35cSwdenk
2161203fcceSJoe Hershberger net_send_udp_packet(net_server_ethaddr, nfs_server_ip, sport,
21768c76a3aSJoe Hershberger nfs_our_port, pktlen);
218cbd8a35cSwdenk }
219cbd8a35cSwdenk
220cbd8a35cSwdenk /**************************************************************************
221cbd8a35cSwdenk RPC_LOOKUP - Lookup RPC Port numbers
222cbd8a35cSwdenk **************************************************************************/
rpc_lookup_req(int prog,int ver)22368c76a3aSJoe Hershberger static void rpc_lookup_req(int prog, int ver)
224cbd8a35cSwdenk {
225a73588feSJoe Hershberger uint32_t data[16];
226cbd8a35cSwdenk
227cbd8a35cSwdenk data[0] = 0; data[1] = 0; /* auth credential */
228cbd8a35cSwdenk data[2] = 0; data[3] = 0; /* auth verifier */
229cbd8a35cSwdenk data[4] = htonl(prog);
230cbd8a35cSwdenk data[5] = htonl(ver);
231cbd8a35cSwdenk data[6] = htonl(17); /* IP_UDP */
232cbd8a35cSwdenk data[7] = 0;
233a73588feSJoe Hershberger rpc_req(PROG_PORTMAP, PORTMAP_GETPORT, data, 8);
234cbd8a35cSwdenk }
235cbd8a35cSwdenk
236cbd8a35cSwdenk /**************************************************************************
237cbd8a35cSwdenk NFS_MOUNT - Mount an NFS Filesystem
238cbd8a35cSwdenk **************************************************************************/
nfs_mount_req(char * path)23968c76a3aSJoe Hershberger static void nfs_mount_req(char *path)
240cbd8a35cSwdenk {
241a73588feSJoe Hershberger uint32_t data[1024];
242cbd8a35cSwdenk uint32_t *p;
243cbd8a35cSwdenk int len;
244cbd8a35cSwdenk int pathlen;
245cbd8a35cSwdenk
246cbd8a35cSwdenk pathlen = strlen(path);
247cbd8a35cSwdenk
248a73588feSJoe Hershberger p = &(data[0]);
249e4ead4a2SRalf Hubert p = rpc_add_credentials(p);
250cbd8a35cSwdenk
251cbd8a35cSwdenk *p++ = htonl(pathlen);
252c9f6c91bSJoe Hershberger if (pathlen & 3)
253c9f6c91bSJoe Hershberger *(p + pathlen / 4) = 0;
254cbd8a35cSwdenk memcpy(p, path, pathlen);
255cbd8a35cSwdenk p += (pathlen + 3) / 4;
256cbd8a35cSwdenk
257a73588feSJoe Hershberger len = (uint32_t *)p - (uint32_t *)&(data[0]);
258cbd8a35cSwdenk
259a73588feSJoe Hershberger rpc_req(PROG_MOUNT, MOUNT_ADDENTRY, data, len);
260cbd8a35cSwdenk }
261cbd8a35cSwdenk
262cbd8a35cSwdenk /**************************************************************************
263cbd8a35cSwdenk NFS_UMOUNTALL - Unmount all our NFS Filesystems on the Server
264cbd8a35cSwdenk **************************************************************************/
nfs_umountall_req(void)26568c76a3aSJoe Hershberger static void nfs_umountall_req(void)
266cbd8a35cSwdenk {
267a73588feSJoe Hershberger uint32_t data[1024];
268cbd8a35cSwdenk uint32_t *p;
269cbd8a35cSwdenk int len;
270cbd8a35cSwdenk
27168c76a3aSJoe Hershberger if ((nfs_server_mount_port == -1) || (!fs_mounted))
272cbd8a35cSwdenk /* Nothing mounted, nothing to umount */
273cbd8a35cSwdenk return;
274cbd8a35cSwdenk
275a73588feSJoe Hershberger p = &(data[0]);
276e4ead4a2SRalf Hubert p = rpc_add_credentials(p);
277cbd8a35cSwdenk
278a73588feSJoe Hershberger len = (uint32_t *)p - (uint32_t *)&(data[0]);
279cbd8a35cSwdenk
280a73588feSJoe Hershberger rpc_req(PROG_MOUNT, MOUNT_UMOUNTALL, data, len);
281cbd8a35cSwdenk }
282cbd8a35cSwdenk
283cbd8a35cSwdenk /***************************************************************************
284cbd8a35cSwdenk * NFS_READLINK (AH 2003-07-14)
285cbd8a35cSwdenk * This procedure is called when read of the first block fails -
286cbd8a35cSwdenk * this probably happens when it's a directory or a symlink
287cbd8a35cSwdenk * In case of successful readlink(), the dirname is manipulated,
288cbd8a35cSwdenk * so that inside the nfs() function a recursion can be done.
289cbd8a35cSwdenk **************************************************************************/
nfs_readlink_req(void)29068c76a3aSJoe Hershberger static void nfs_readlink_req(void)
291cbd8a35cSwdenk {
292a73588feSJoe Hershberger uint32_t data[1024];
293cbd8a35cSwdenk uint32_t *p;
294cbd8a35cSwdenk int len;
295cbd8a35cSwdenk
296a73588feSJoe Hershberger p = &(data[0]);
297e4ead4a2SRalf Hubert p = rpc_add_credentials(p);
298cbd8a35cSwdenk
299b0baca98SGuillaume GARDET if (supported_nfs_versions & NFSV2_FLAG) {
300cbd8a35cSwdenk memcpy(p, filefh, NFS_FHSIZE);
301cbd8a35cSwdenk p += (NFS_FHSIZE / 4);
302b0baca98SGuillaume GARDET } else { /* NFSV3_FLAG */
303b0baca98SGuillaume GARDET *p++ = htonl(filefh3_length);
3045280c769SJoe Hershberger memcpy(p, filefh, filefh3_length);
305b0baca98SGuillaume GARDET p += (filefh3_length / 4);
306b0baca98SGuillaume GARDET }
307cbd8a35cSwdenk
308a73588feSJoe Hershberger len = (uint32_t *)p - (uint32_t *)&(data[0]);
309cbd8a35cSwdenk
310a73588feSJoe Hershberger rpc_req(PROG_NFS, NFS_READLINK, data, len);
311cbd8a35cSwdenk }
312cbd8a35cSwdenk
313cbd8a35cSwdenk /**************************************************************************
314cbd8a35cSwdenk NFS_LOOKUP - Lookup Pathname
315cbd8a35cSwdenk **************************************************************************/
nfs_lookup_req(char * fname)31668c76a3aSJoe Hershberger static void nfs_lookup_req(char *fname)
317cbd8a35cSwdenk {
318a73588feSJoe Hershberger uint32_t data[1024];
319cbd8a35cSwdenk uint32_t *p;
320cbd8a35cSwdenk int len;
321cbd8a35cSwdenk int fnamelen;
322cbd8a35cSwdenk
323cbd8a35cSwdenk fnamelen = strlen(fname);
324cbd8a35cSwdenk
325a73588feSJoe Hershberger p = &(data[0]);
326e4ead4a2SRalf Hubert p = rpc_add_credentials(p);
327cbd8a35cSwdenk
328b0baca98SGuillaume GARDET if (supported_nfs_versions & NFSV2_FLAG) {
329cbd8a35cSwdenk memcpy(p, dirfh, NFS_FHSIZE);
330cbd8a35cSwdenk p += (NFS_FHSIZE / 4);
331cbd8a35cSwdenk *p++ = htonl(fnamelen);
332c9f6c91bSJoe Hershberger if (fnamelen & 3)
333c9f6c91bSJoe Hershberger *(p + fnamelen / 4) = 0;
334cbd8a35cSwdenk memcpy(p, fname, fnamelen);
335cbd8a35cSwdenk p += (fnamelen + 3) / 4;
336cbd8a35cSwdenk
337a73588feSJoe Hershberger len = (uint32_t *)p - (uint32_t *)&(data[0]);
338cbd8a35cSwdenk
339a73588feSJoe Hershberger rpc_req(PROG_NFS, NFS_LOOKUP, data, len);
340b0baca98SGuillaume GARDET } else { /* NFSV3_FLAG */
341b0baca98SGuillaume GARDET *p++ = htonl(NFS_FHSIZE); /* Dir handle length */
342b0baca98SGuillaume GARDET memcpy(p, dirfh, NFS_FHSIZE);
343b0baca98SGuillaume GARDET p += (NFS_FHSIZE / 4);
344b0baca98SGuillaume GARDET *p++ = htonl(fnamelen);
345b0baca98SGuillaume GARDET if (fnamelen & 3)
346b0baca98SGuillaume GARDET *(p + fnamelen / 4) = 0;
347b0baca98SGuillaume GARDET memcpy(p, fname, fnamelen);
348b0baca98SGuillaume GARDET p += (fnamelen + 3) / 4;
349b0baca98SGuillaume GARDET
350a73588feSJoe Hershberger len = (uint32_t *)p - (uint32_t *)&(data[0]);
351b0baca98SGuillaume GARDET
352a73588feSJoe Hershberger rpc_req(PROG_NFS, NFS3PROC_LOOKUP, data, len);
353b0baca98SGuillaume GARDET }
354cbd8a35cSwdenk }
355cbd8a35cSwdenk
356cbd8a35cSwdenk /**************************************************************************
357cbd8a35cSwdenk NFS_READ - Read File on NFS Server
358cbd8a35cSwdenk **************************************************************************/
nfs_read_req(int offset,int readlen)35968c76a3aSJoe Hershberger static void nfs_read_req(int offset, int readlen)
360cbd8a35cSwdenk {
361a73588feSJoe Hershberger uint32_t data[1024];
362cbd8a35cSwdenk uint32_t *p;
363cbd8a35cSwdenk int len;
364cbd8a35cSwdenk
365a73588feSJoe Hershberger p = &(data[0]);
366e4ead4a2SRalf Hubert p = rpc_add_credentials(p);
367cbd8a35cSwdenk
368b0baca98SGuillaume GARDET if (supported_nfs_versions & NFSV2_FLAG) {
369cbd8a35cSwdenk memcpy(p, filefh, NFS_FHSIZE);
370cbd8a35cSwdenk p += (NFS_FHSIZE / 4);
371cbd8a35cSwdenk *p++ = htonl(offset);
372cbd8a35cSwdenk *p++ = htonl(readlen);
373cbd8a35cSwdenk *p++ = 0;
374b0baca98SGuillaume GARDET } else { /* NFSV3_FLAG */
375b0baca98SGuillaume GARDET *p++ = htonl(filefh3_length);
3765280c769SJoe Hershberger memcpy(p, filefh, filefh3_length);
377b0baca98SGuillaume GARDET p += (filefh3_length / 4);
378b0baca98SGuillaume GARDET *p++ = htonl(0); /* offset is 64-bit long, so fill with 0 */
379b0baca98SGuillaume GARDET *p++ = htonl(offset);
380b0baca98SGuillaume GARDET *p++ = htonl(readlen);
381b0baca98SGuillaume GARDET *p++ = 0;
382b0baca98SGuillaume GARDET }
383cbd8a35cSwdenk
384a73588feSJoe Hershberger len = (uint32_t *)p - (uint32_t *)&(data[0]);
385cbd8a35cSwdenk
386a73588feSJoe Hershberger rpc_req(PROG_NFS, NFS_READ, data, len);
387cbd8a35cSwdenk }
388cbd8a35cSwdenk
389cbd8a35cSwdenk /**************************************************************************
390cbd8a35cSwdenk RPC request dispatcher
391cbd8a35cSwdenk **************************************************************************/
nfs_send(void)39268c76a3aSJoe Hershberger static void nfs_send(void)
393cbd8a35cSwdenk {
3940ebf04c6SRobin Getz debug("%s\n", __func__);
395cbd8a35cSwdenk
39668c76a3aSJoe Hershberger switch (nfs_state) {
397cbd8a35cSwdenk case STATE_PRCLOOKUP_PROG_MOUNT_REQ:
398b0baca98SGuillaume GARDET if (supported_nfs_versions & NFSV2_FLAG)
399cbd8a35cSwdenk rpc_lookup_req(PROG_MOUNT, 1);
400b0baca98SGuillaume GARDET else /* NFSV3_FLAG */
401b0baca98SGuillaume GARDET rpc_lookup_req(PROG_MOUNT, 3);
402cbd8a35cSwdenk break;
403cbd8a35cSwdenk case STATE_PRCLOOKUP_PROG_NFS_REQ:
404b0baca98SGuillaume GARDET if (supported_nfs_versions & NFSV2_FLAG)
405cbd8a35cSwdenk rpc_lookup_req(PROG_NFS, 2);
406b0baca98SGuillaume GARDET else /* NFSV3_FLAG */
407b0baca98SGuillaume GARDET rpc_lookup_req(PROG_NFS, 3);
408cbd8a35cSwdenk break;
409cbd8a35cSwdenk case STATE_MOUNT_REQ:
410cbd8a35cSwdenk nfs_mount_req(nfs_path);
411cbd8a35cSwdenk break;
412cbd8a35cSwdenk case STATE_UMOUNT_REQ:
413cbd8a35cSwdenk nfs_umountall_req();
414cbd8a35cSwdenk break;
415cbd8a35cSwdenk case STATE_LOOKUP_REQ:
416cbd8a35cSwdenk nfs_lookup_req(nfs_filename);
417cbd8a35cSwdenk break;
418cbd8a35cSwdenk case STATE_READ_REQ:
419cbd8a35cSwdenk nfs_read_req(nfs_offset, nfs_len);
420cbd8a35cSwdenk break;
421cbd8a35cSwdenk case STATE_READLINK_REQ:
422cbd8a35cSwdenk nfs_readlink_req();
423cbd8a35cSwdenk break;
424cbd8a35cSwdenk }
425cbd8a35cSwdenk }
426cbd8a35cSwdenk
427cbd8a35cSwdenk /**************************************************************************
428cbd8a35cSwdenk Handlers for the reply from server
429cbd8a35cSwdenk **************************************************************************/
430cbd8a35cSwdenk
rpc_lookup_reply(int prog,uchar * pkt,unsigned len)43168c76a3aSJoe Hershberger static int rpc_lookup_reply(int prog, uchar *pkt, unsigned len)
432cbd8a35cSwdenk {
433cbd8a35cSwdenk struct rpc_t rpc_pkt;
434cbd8a35cSwdenk
4350517cc45SJoe Hershberger memcpy(&rpc_pkt.u.data[0], pkt, len);
436cbd8a35cSwdenk
4370ebf04c6SRobin Getz debug("%s\n", __func__);
438cbd8a35cSwdenk
439fa84fa70SMatthias Brugger if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
440fa84fa70SMatthias Brugger return -NFS_RPC_ERR;
441fa84fa70SMatthias Brugger else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
442fa84fa70SMatthias Brugger return -NFS_RPC_DROP;
443c3f9d493Swdenk
444cbd8a35cSwdenk if (rpc_pkt.u.reply.rstatus ||
445cbd8a35cSwdenk rpc_pkt.u.reply.verifier ||
446c9f6c91bSJoe Hershberger rpc_pkt.u.reply.astatus)
447c3f9d493Swdenk return -1;
448cbd8a35cSwdenk
449cbd8a35cSwdenk switch (prog) {
450cbd8a35cSwdenk case PROG_MOUNT:
45168c76a3aSJoe Hershberger nfs_server_mount_port = ntohl(rpc_pkt.u.reply.data[0]);
452cbd8a35cSwdenk break;
453cbd8a35cSwdenk case PROG_NFS:
45468c76a3aSJoe Hershberger nfs_server_port = ntohl(rpc_pkt.u.reply.data[0]);
455cbd8a35cSwdenk break;
456cbd8a35cSwdenk }
457cbd8a35cSwdenk
458cbd8a35cSwdenk return 0;
459cbd8a35cSwdenk }
460cbd8a35cSwdenk
nfs_mount_reply(uchar * pkt,unsigned len)46168c76a3aSJoe Hershberger static int nfs_mount_reply(uchar *pkt, unsigned len)
462cbd8a35cSwdenk {
463cbd8a35cSwdenk struct rpc_t rpc_pkt;
464cbd8a35cSwdenk
4650ebf04c6SRobin Getz debug("%s\n", __func__);
466cbd8a35cSwdenk
4670517cc45SJoe Hershberger memcpy(&rpc_pkt.u.data[0], pkt, len);
468cbd8a35cSwdenk
469fa84fa70SMatthias Brugger if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
470fa84fa70SMatthias Brugger return -NFS_RPC_ERR;
471fa84fa70SMatthias Brugger else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
472fa84fa70SMatthias Brugger return -NFS_RPC_DROP;
473c3f9d493Swdenk
474cbd8a35cSwdenk if (rpc_pkt.u.reply.rstatus ||
475cbd8a35cSwdenk rpc_pkt.u.reply.verifier ||
476cbd8a35cSwdenk rpc_pkt.u.reply.astatus ||
477c9f6c91bSJoe Hershberger rpc_pkt.u.reply.data[0])
478cbd8a35cSwdenk return -1;
479cbd8a35cSwdenk
480cbd8a35cSwdenk fs_mounted = 1;
481b0baca98SGuillaume GARDET /* NFSv2 and NFSv3 use same structure */
482cbd8a35cSwdenk memcpy(dirfh, rpc_pkt.u.reply.data + 1, NFS_FHSIZE);
483cbd8a35cSwdenk
484cbd8a35cSwdenk return 0;
485cbd8a35cSwdenk }
486cbd8a35cSwdenk
nfs_umountall_reply(uchar * pkt,unsigned len)48768c76a3aSJoe Hershberger static int nfs_umountall_reply(uchar *pkt, unsigned len)
488cbd8a35cSwdenk {
489cbd8a35cSwdenk struct rpc_t rpc_pkt;
490cbd8a35cSwdenk
4910ebf04c6SRobin Getz debug("%s\n", __func__);
492cbd8a35cSwdenk
4930517cc45SJoe Hershberger memcpy(&rpc_pkt.u.data[0], pkt, len);
494cbd8a35cSwdenk
495fa84fa70SMatthias Brugger if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
496fa84fa70SMatthias Brugger return -NFS_RPC_ERR;
497fa84fa70SMatthias Brugger else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
498fa84fa70SMatthias Brugger return -NFS_RPC_DROP;
499c3f9d493Swdenk
500cbd8a35cSwdenk if (rpc_pkt.u.reply.rstatus ||
501cbd8a35cSwdenk rpc_pkt.u.reply.verifier ||
502c9f6c91bSJoe Hershberger rpc_pkt.u.reply.astatus)
503cbd8a35cSwdenk return -1;
504cbd8a35cSwdenk
505cbd8a35cSwdenk fs_mounted = 0;
506cbd8a35cSwdenk memset(dirfh, 0, sizeof(dirfh));
507cbd8a35cSwdenk
508cbd8a35cSwdenk return 0;
509cbd8a35cSwdenk }
510cbd8a35cSwdenk
nfs_lookup_reply(uchar * pkt,unsigned len)51168c76a3aSJoe Hershberger static int nfs_lookup_reply(uchar *pkt, unsigned len)
512cbd8a35cSwdenk {
513cbd8a35cSwdenk struct rpc_t rpc_pkt;
514cbd8a35cSwdenk
5150ebf04c6SRobin Getz debug("%s\n", __func__);
516cbd8a35cSwdenk
5170517cc45SJoe Hershberger memcpy(&rpc_pkt.u.data[0], pkt, len);
518cbd8a35cSwdenk
519fa84fa70SMatthias Brugger if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
520fa84fa70SMatthias Brugger return -NFS_RPC_ERR;
521fa84fa70SMatthias Brugger else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
522fa84fa70SMatthias Brugger return -NFS_RPC_DROP;
523c3f9d493Swdenk
524cbd8a35cSwdenk if (rpc_pkt.u.reply.rstatus ||
525cbd8a35cSwdenk rpc_pkt.u.reply.verifier ||
526cbd8a35cSwdenk rpc_pkt.u.reply.astatus ||
52769fd0d41SGuillaume GARDET rpc_pkt.u.reply.data[0]) {
52869fd0d41SGuillaume GARDET switch (ntohl(rpc_pkt.u.reply.astatus)) {
529b0baca98SGuillaume GARDET case NFS_RPC_SUCCESS: /* Not an error */
53069fd0d41SGuillaume GARDET break;
531b0baca98SGuillaume GARDET case NFS_RPC_PROG_MISMATCH:
532b0baca98SGuillaume GARDET /* Remote can't support NFS version */
533b0baca98SGuillaume GARDET switch (ntohl(rpc_pkt.u.reply.data[0])) {
534b0baca98SGuillaume GARDET /* Minimal supported NFS version */
535b0baca98SGuillaume GARDET case 3:
536faecf84aSJoe Hershberger debug("*** Warning: NFS version not supported: Requested: V%d, accepted: min V%d - max V%d\n",
537347a9015SJoe Hershberger (supported_nfs_versions & NFSV2_FLAG) ?
538347a9015SJoe Hershberger 2 : 3,
53969fd0d41SGuillaume GARDET ntohl(rpc_pkt.u.reply.data[0]),
54069fd0d41SGuillaume GARDET ntohl(rpc_pkt.u.reply.data[1]));
541b0baca98SGuillaume GARDET debug("Will retry with NFSv3\n");
542b0baca98SGuillaume GARDET /* Clear NFSV2_FLAG from supported versions */
543b0baca98SGuillaume GARDET supported_nfs_versions &= ~NFSV2_FLAG;
544b0baca98SGuillaume GARDET return -NFS_RPC_PROG_MISMATCH;
545b0baca98SGuillaume GARDET case 4:
546b0baca98SGuillaume GARDET default:
547d89ff2dfSJoe Hershberger puts("*** ERROR: NFS version not supported");
548d89ff2dfSJoe Hershberger debug(": Requested: V%d, accepted: min V%d - max V%d\n",
549347a9015SJoe Hershberger (supported_nfs_versions & NFSV2_FLAG) ?
550347a9015SJoe Hershberger 2 : 3,
551b0baca98SGuillaume GARDET ntohl(rpc_pkt.u.reply.data[0]),
552b0baca98SGuillaume GARDET ntohl(rpc_pkt.u.reply.data[1]));
553d89ff2dfSJoe Hershberger puts("\n");
554b0baca98SGuillaume GARDET }
55569fd0d41SGuillaume GARDET break;
556b0baca98SGuillaume GARDET case NFS_RPC_PROG_UNAVAIL:
557b0baca98SGuillaume GARDET case NFS_RPC_PROC_UNAVAIL:
558b0baca98SGuillaume GARDET case NFS_RPC_GARBAGE_ARGS:
559b0baca98SGuillaume GARDET case NFS_RPC_SYSTEM_ERR:
56069fd0d41SGuillaume GARDET default: /* Unknown error on 'accept state' flag */
561d89ff2dfSJoe Hershberger debug("*** ERROR: accept state error (%d)\n",
56269fd0d41SGuillaume GARDET ntohl(rpc_pkt.u.reply.astatus));
56369fd0d41SGuillaume GARDET break;
56469fd0d41SGuillaume GARDET }
565cbd8a35cSwdenk return -1;
56669fd0d41SGuillaume GARDET }
567cbd8a35cSwdenk
568b0baca98SGuillaume GARDET if (supported_nfs_versions & NFSV2_FLAG) {
569cbd8a35cSwdenk memcpy(filefh, rpc_pkt.u.reply.data + 1, NFS_FHSIZE);
570b0baca98SGuillaume GARDET } else { /* NFSV3_FLAG */
571b0baca98SGuillaume GARDET filefh3_length = ntohl(rpc_pkt.u.reply.data[1]);
572b0baca98SGuillaume GARDET if (filefh3_length > NFS3_FHSIZE)
573b0baca98SGuillaume GARDET filefh3_length = NFS3_FHSIZE;
5745280c769SJoe Hershberger memcpy(filefh, rpc_pkt.u.reply.data + 2, filefh3_length);
575b0baca98SGuillaume GARDET }
576cbd8a35cSwdenk
577cbd8a35cSwdenk return 0;
578cbd8a35cSwdenk }
579cbd8a35cSwdenk
nfs3_get_attributes_offset(uint32_t * data)580051ed9afSJoe Hershberger static int nfs3_get_attributes_offset(uint32_t *data)
581cbd8a35cSwdenk {
582051ed9afSJoe Hershberger if (ntohl(data[1]) != 0) {
583b0baca98SGuillaume GARDET /* 'attributes_follow' flag is TRUE,
584c629c45fSJoe Hershberger * so we have attributes on 21 dwords */
585b0baca98SGuillaume GARDET /* Skip unused values :
586b0baca98SGuillaume GARDET type; 32 bits value,
587b0baca98SGuillaume GARDET mode; 32 bits value,
588b0baca98SGuillaume GARDET nlink; 32 bits value,
589b0baca98SGuillaume GARDET uid; 32 bits value,
590b0baca98SGuillaume GARDET gid; 32 bits value,
591b0baca98SGuillaume GARDET size; 64 bits value,
592b0baca98SGuillaume GARDET used; 64 bits value,
593b0baca98SGuillaume GARDET rdev; 64 bits value,
594b0baca98SGuillaume GARDET fsid; 64 bits value,
595b0baca98SGuillaume GARDET fileid; 64 bits value,
596b0baca98SGuillaume GARDET atime; 64 bits value,
597b0baca98SGuillaume GARDET mtime; 64 bits value,
598b0baca98SGuillaume GARDET ctime; 64 bits value,
599b0baca98SGuillaume GARDET */
600051ed9afSJoe Hershberger return 22;
601b0baca98SGuillaume GARDET } else {
602b0baca98SGuillaume GARDET /* 'attributes_follow' flag is FALSE,
603b0baca98SGuillaume GARDET * so we don't have any attributes */
604051ed9afSJoe Hershberger return 1;
605051ed9afSJoe Hershberger }
606051ed9afSJoe Hershberger }
607051ed9afSJoe Hershberger
nfs_readlink_reply(uchar * pkt,unsigned len)608051ed9afSJoe Hershberger static int nfs_readlink_reply(uchar *pkt, unsigned len)
609051ed9afSJoe Hershberger {
610051ed9afSJoe Hershberger struct rpc_t rpc_pkt;
611051ed9afSJoe Hershberger int rlen;
612051ed9afSJoe Hershberger int nfsv3_data_offset = 0;
613051ed9afSJoe Hershberger
614051ed9afSJoe Hershberger debug("%s\n", __func__);
615051ed9afSJoe Hershberger
616051ed9afSJoe Hershberger memcpy((unsigned char *)&rpc_pkt, pkt, len);
617051ed9afSJoe Hershberger
618051ed9afSJoe Hershberger if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
619051ed9afSJoe Hershberger return -NFS_RPC_ERR;
620051ed9afSJoe Hershberger else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
621051ed9afSJoe Hershberger return -NFS_RPC_DROP;
622051ed9afSJoe Hershberger
623051ed9afSJoe Hershberger if (rpc_pkt.u.reply.rstatus ||
624051ed9afSJoe Hershberger rpc_pkt.u.reply.verifier ||
625051ed9afSJoe Hershberger rpc_pkt.u.reply.astatus ||
626051ed9afSJoe Hershberger rpc_pkt.u.reply.data[0])
627051ed9afSJoe Hershberger return -1;
628051ed9afSJoe Hershberger
629051ed9afSJoe Hershberger if (!(supported_nfs_versions & NFSV2_FLAG)) { /* NFSV3_FLAG */
630051ed9afSJoe Hershberger nfsv3_data_offset =
631051ed9afSJoe Hershberger nfs3_get_attributes_offset(rpc_pkt.u.reply.data);
632b0baca98SGuillaume GARDET }
633b0baca98SGuillaume GARDET
634b0baca98SGuillaume GARDET /* new path length */
635b0baca98SGuillaume GARDET rlen = ntohl(rpc_pkt.u.reply.data[1 + nfsv3_data_offset]);
636b0baca98SGuillaume GARDET
637b0baca98SGuillaume GARDET if (*((char *)&(rpc_pkt.u.reply.data[2 + nfsv3_data_offset])) != '/') {
638b0baca98SGuillaume GARDET int pathlen;
639051ed9afSJoe Hershberger
640b0baca98SGuillaume GARDET strcat(nfs_path, "/");
641b0baca98SGuillaume GARDET pathlen = strlen(nfs_path);
642b0baca98SGuillaume GARDET memcpy(nfs_path + pathlen,
643b0baca98SGuillaume GARDET (uchar *)&(rpc_pkt.u.reply.data[2 + nfsv3_data_offset]),
644b0baca98SGuillaume GARDET rlen);
645b0baca98SGuillaume GARDET nfs_path[pathlen + rlen] = 0;
646b0baca98SGuillaume GARDET } else {
647b0baca98SGuillaume GARDET memcpy(nfs_path,
648b0baca98SGuillaume GARDET (uchar *)&(rpc_pkt.u.reply.data[2 + nfsv3_data_offset]),
649b0baca98SGuillaume GARDET rlen);
650b0baca98SGuillaume GARDET nfs_path[rlen] = 0;
651b0baca98SGuillaume GARDET }
652cbd8a35cSwdenk return 0;
653cbd8a35cSwdenk }
654cbd8a35cSwdenk
nfs_read_reply(uchar * pkt,unsigned len)65568c76a3aSJoe Hershberger static int nfs_read_reply(uchar *pkt, unsigned len)
656cbd8a35cSwdenk {
657cbd8a35cSwdenk struct rpc_t rpc_pkt;
658cbd8a35cSwdenk int rlen;
659b0baca98SGuillaume GARDET uchar *data_ptr;
660cbd8a35cSwdenk
6610ebf04c6SRobin Getz debug("%s\n", __func__);
662cbd8a35cSwdenk
6630517cc45SJoe Hershberger memcpy(&rpc_pkt.u.data[0], pkt, sizeof(rpc_pkt.u.reply));
664cbd8a35cSwdenk
665fa84fa70SMatthias Brugger if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
666fa84fa70SMatthias Brugger return -NFS_RPC_ERR;
667fa84fa70SMatthias Brugger else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
668fa84fa70SMatthias Brugger return -NFS_RPC_DROP;
669c3f9d493Swdenk
670cbd8a35cSwdenk if (rpc_pkt.u.reply.rstatus ||
671cbd8a35cSwdenk rpc_pkt.u.reply.verifier ||
672cbd8a35cSwdenk rpc_pkt.u.reply.astatus ||
673cbd8a35cSwdenk rpc_pkt.u.reply.data[0]) {
674c9f6c91bSJoe Hershberger if (rpc_pkt.u.reply.rstatus)
675cbd8a35cSwdenk return -9999;
676c9f6c91bSJoe Hershberger if (rpc_pkt.u.reply.astatus)
677cbd8a35cSwdenk return -9999;
678c9f6c91bSJoe Hershberger return -ntohl(rpc_pkt.u.reply.data[0]);
679cbd8a35cSwdenk }
680cbd8a35cSwdenk
681c9f6c91bSJoe Hershberger if ((nfs_offset != 0) && !((nfs_offset) %
682c9f6c91bSJoe Hershberger (NFS_READ_SIZE / 2 * 10 * HASHES_PER_LINE)))
683cbd8a35cSwdenk puts("\n\t ");
684c9f6c91bSJoe Hershberger if (!(nfs_offset % ((NFS_READ_SIZE / 2) * 10)))
685cbd8a35cSwdenk putc('#');
686cbd8a35cSwdenk
687b0baca98SGuillaume GARDET if (supported_nfs_versions & NFSV2_FLAG) {
688cbd8a35cSwdenk rlen = ntohl(rpc_pkt.u.reply.data[18]);
689b0baca98SGuillaume GARDET data_ptr = (uchar *)&(rpc_pkt.u.reply.data[19]);
690b0baca98SGuillaume GARDET } else { /* NFSV3_FLAG */
691051ed9afSJoe Hershberger int nfsv3_data_offset =
692051ed9afSJoe Hershberger nfs3_get_attributes_offset(rpc_pkt.u.reply.data);
693051ed9afSJoe Hershberger
694051ed9afSJoe Hershberger /* count value */
695051ed9afSJoe Hershberger rlen = ntohl(rpc_pkt.u.reply.data[1 + nfsv3_data_offset]);
696b0baca98SGuillaume GARDET /* Skip unused values :
697b0baca98SGuillaume GARDET EOF: 32 bits value,
698b0baca98SGuillaume GARDET data_size: 32 bits value,
699b0baca98SGuillaume GARDET */
700051ed9afSJoe Hershberger data_ptr = (uchar *)
701051ed9afSJoe Hershberger &(rpc_pkt.u.reply.data[4 + nfsv3_data_offset]);
702b0baca98SGuillaume GARDET }
703b0baca98SGuillaume GARDET
704b0baca98SGuillaume GARDET if (store_block(data_ptr, nfs_offset, rlen))
70523a7a32dSWolfgang Denk return -9999;
706cbd8a35cSwdenk
707cbd8a35cSwdenk return rlen;
708cbd8a35cSwdenk }
709cbd8a35cSwdenk
710cbd8a35cSwdenk /**************************************************************************
711cbd8a35cSwdenk Interfaces of U-BOOT
712cbd8a35cSwdenk **************************************************************************/
nfs_timeout_handler(void)71368c76a3aSJoe Hershberger static void nfs_timeout_handler(void)
714a5725fabSwdenk {
71568c76a3aSJoe Hershberger if (++nfs_timeout_count > NFS_RETRY_COUNT) {
716aabb8cb0SEvan Samanas puts("\nRetry count exceeded; starting again\n");
717bc0571fcSJoe Hershberger net_start_again();
718aabb8cb0SEvan Samanas } else {
719aabb8cb0SEvan Samanas puts("T ");
720bc0571fcSJoe Hershberger net_set_timeout_handler(nfs_timeout +
721bc0571fcSJoe Hershberger NFS_TIMEOUT * nfs_timeout_count,
72268c76a3aSJoe Hershberger nfs_timeout_handler);
72368c76a3aSJoe Hershberger nfs_send();
724fe891ecfSHiroshi Ito }
725a5725fabSwdenk }
726a5725fabSwdenk
nfs_handler(uchar * pkt,unsigned dest,struct in_addr sip,unsigned src,unsigned len)727049a95a7SJoe Hershberger static void nfs_handler(uchar *pkt, unsigned dest, struct in_addr sip,
728049a95a7SJoe Hershberger unsigned src, unsigned len)
729cbd8a35cSwdenk {
730cbd8a35cSwdenk int rlen;
731fa84fa70SMatthias Brugger int reply;
732cbd8a35cSwdenk
7330ebf04c6SRobin Getz debug("%s\n", __func__);
734cbd8a35cSwdenk
73568c76a3aSJoe Hershberger if (dest != nfs_our_port)
736c9f6c91bSJoe Hershberger return;
737cbd8a35cSwdenk
73868c76a3aSJoe Hershberger switch (nfs_state) {
739cbd8a35cSwdenk case STATE_PRCLOOKUP_PROG_MOUNT_REQ:
740fa84fa70SMatthias Brugger if (rpc_lookup_reply(PROG_MOUNT, pkt, len) == -NFS_RPC_DROP)
741fa84fa70SMatthias Brugger break;
74268c76a3aSJoe Hershberger nfs_state = STATE_PRCLOOKUP_PROG_NFS_REQ;
74368c76a3aSJoe Hershberger nfs_send();
744cbd8a35cSwdenk break;
745cbd8a35cSwdenk
746cbd8a35cSwdenk case STATE_PRCLOOKUP_PROG_NFS_REQ:
747fa84fa70SMatthias Brugger if (rpc_lookup_reply(PROG_NFS, pkt, len) == -NFS_RPC_DROP)
748fa84fa70SMatthias Brugger break;
74968c76a3aSJoe Hershberger nfs_state = STATE_MOUNT_REQ;
75068c76a3aSJoe Hershberger nfs_send();
751cbd8a35cSwdenk break;
752cbd8a35cSwdenk
753cbd8a35cSwdenk case STATE_MOUNT_REQ:
754fa84fa70SMatthias Brugger reply = nfs_mount_reply(pkt, len);
75568c76a3aSJoe Hershberger if (reply == -NFS_RPC_DROP) {
756fa84fa70SMatthias Brugger break;
75768c76a3aSJoe Hershberger } else if (reply == -NFS_RPC_ERR) {
758cbd8a35cSwdenk puts("*** ERROR: Cannot mount\n");
759cbd8a35cSwdenk /* just to be sure... */
76068c76a3aSJoe Hershberger nfs_state = STATE_UMOUNT_REQ;
76168c76a3aSJoe Hershberger nfs_send();
762cbd8a35cSwdenk } else {
76368c76a3aSJoe Hershberger nfs_state = STATE_LOOKUP_REQ;
76468c76a3aSJoe Hershberger nfs_send();
765cbd8a35cSwdenk }
766cbd8a35cSwdenk break;
767cbd8a35cSwdenk
768cbd8a35cSwdenk case STATE_UMOUNT_REQ:
769fa84fa70SMatthias Brugger reply = nfs_umountall_reply(pkt, len);
77068c76a3aSJoe Hershberger if (reply == -NFS_RPC_DROP) {
771fa84fa70SMatthias Brugger break;
77268c76a3aSJoe Hershberger } else if (reply == -NFS_RPC_ERR) {
773d89ff2dfSJoe Hershberger debug("*** ERROR: Cannot umount\n");
77422f6e99dSJoe Hershberger net_set_state(NETLOOP_FAIL);
775cbd8a35cSwdenk } else {
776a084f7daSwdenk puts("\ndone\n");
77722f6e99dSJoe Hershberger net_set_state(nfs_download_state);
778cbd8a35cSwdenk }
779cbd8a35cSwdenk break;
780cbd8a35cSwdenk
781cbd8a35cSwdenk case STATE_LOOKUP_REQ:
782fa84fa70SMatthias Brugger reply = nfs_lookup_reply(pkt, len);
78368c76a3aSJoe Hershberger if (reply == -NFS_RPC_DROP) {
784fa84fa70SMatthias Brugger break;
78568c76a3aSJoe Hershberger } else if (reply == -NFS_RPC_ERR) {
786cbd8a35cSwdenk puts("*** ERROR: File lookup fail\n");
78768c76a3aSJoe Hershberger nfs_state = STATE_UMOUNT_REQ;
78868c76a3aSJoe Hershberger nfs_send();
789347a9015SJoe Hershberger } else if (reply == -NFS_RPC_PROG_MISMATCH &&
790347a9015SJoe Hershberger supported_nfs_versions != 0) {
791b0baca98SGuillaume GARDET /* umount */
792b0baca98SGuillaume GARDET nfs_state = STATE_UMOUNT_REQ;
793b0baca98SGuillaume GARDET nfs_send();
794b0baca98SGuillaume GARDET /* And retry with another supported version */
795b0baca98SGuillaume GARDET nfs_state = STATE_PRCLOOKUP_PROG_MOUNT_REQ;
796b0baca98SGuillaume GARDET nfs_send();
797cbd8a35cSwdenk } else {
79868c76a3aSJoe Hershberger nfs_state = STATE_READ_REQ;
799cbd8a35cSwdenk nfs_offset = 0;
800cbd8a35cSwdenk nfs_len = NFS_READ_SIZE;
80168c76a3aSJoe Hershberger nfs_send();
802cbd8a35cSwdenk }
803cbd8a35cSwdenk break;
804cbd8a35cSwdenk
805cbd8a35cSwdenk case STATE_READLINK_REQ:
806fa84fa70SMatthias Brugger reply = nfs_readlink_reply(pkt, len);
80768c76a3aSJoe Hershberger if (reply == -NFS_RPC_DROP) {
808fa84fa70SMatthias Brugger break;
80968c76a3aSJoe Hershberger } else if (reply == -NFS_RPC_ERR) {
810cbd8a35cSwdenk puts("*** ERROR: Symlink fail\n");
81168c76a3aSJoe Hershberger nfs_state = STATE_UMOUNT_REQ;
81268c76a3aSJoe Hershberger nfs_send();
813cbd8a35cSwdenk } else {
8140ebf04c6SRobin Getz debug("Symlink --> %s\n", nfs_path);
815cbd8a35cSwdenk nfs_filename = basename(nfs_path);
816cbd8a35cSwdenk nfs_path = dirname(nfs_path);
817cbd8a35cSwdenk
81868c76a3aSJoe Hershberger nfs_state = STATE_MOUNT_REQ;
81968c76a3aSJoe Hershberger nfs_send();
820cbd8a35cSwdenk }
821cbd8a35cSwdenk break;
822cbd8a35cSwdenk
823cbd8a35cSwdenk case STATE_READ_REQ:
824cbd8a35cSwdenk rlen = nfs_read_reply(pkt, len);
825d48d40a0SVasily Khoruzhick if (rlen == -NFS_RPC_DROP)
826d48d40a0SVasily Khoruzhick break;
827bc0571fcSJoe Hershberger net_set_timeout_handler(nfs_timeout, nfs_timeout_handler);
828cbd8a35cSwdenk if (rlen > 0) {
829cbd8a35cSwdenk nfs_offset += rlen;
83068c76a3aSJoe Hershberger nfs_send();
831c9f6c91bSJoe Hershberger } else if ((rlen == -NFSERR_ISDIR) || (rlen == -NFSERR_INVAL)) {
832cbd8a35cSwdenk /* symbolic link */
83368c76a3aSJoe Hershberger nfs_state = STATE_READLINK_REQ;
83468c76a3aSJoe Hershberger nfs_send();
835cbd8a35cSwdenk } else {
836c9f6c91bSJoe Hershberger if (!rlen)
83722f6e99dSJoe Hershberger nfs_download_state = NETLOOP_SUCCESS;
838b0baca98SGuillaume GARDET if (rlen < 0)
839d89ff2dfSJoe Hershberger debug("NFS READ error (%d)\n", rlen);
84068c76a3aSJoe Hershberger nfs_state = STATE_UMOUNT_REQ;
84168c76a3aSJoe Hershberger nfs_send();
842cbd8a35cSwdenk }
843cbd8a35cSwdenk break;
844cbd8a35cSwdenk }
845cbd8a35cSwdenk }
846cbd8a35cSwdenk
847cbd8a35cSwdenk
nfs_start(void)84868c76a3aSJoe Hershberger void nfs_start(void)
849cbd8a35cSwdenk {
8500ebf04c6SRobin Getz debug("%s\n", __func__);
85122f6e99dSJoe Hershberger nfs_download_state = NETLOOP_FAIL;
852cbd8a35cSwdenk
853049a95a7SJoe Hershberger nfs_server_ip = net_server_ip;
854cbd8a35cSwdenk nfs_path = (char *)nfs_path_buff;
855cbd8a35cSwdenk
856cbd8a35cSwdenk if (nfs_path == NULL) {
85722f6e99dSJoe Hershberger net_set_state(NETLOOP_FAIL);
858faecf84aSJoe Hershberger printf("*** ERROR: Fail allocate memory\n");
859cbd8a35cSwdenk return;
860cbd8a35cSwdenk }
861cbd8a35cSwdenk
862*6ab12830SJoe Hershberger if (!net_parse_bootfile(&nfs_server_ip, nfs_path,
863*6ab12830SJoe Hershberger sizeof(nfs_path_buff))) {
864f8b26c7aSJoe Hershberger sprintf(nfs_path, "/nfsroot/%02X%02X%02X%02X.img",
865049a95a7SJoe Hershberger net_ip.s_addr & 0xFF,
866049a95a7SJoe Hershberger (net_ip.s_addr >> 8) & 0xFF,
867049a95a7SJoe Hershberger (net_ip.s_addr >> 16) & 0xFF,
868049a95a7SJoe Hershberger (net_ip.s_addr >> 24) & 0xFF);
869cbd8a35cSwdenk
870faecf84aSJoe Hershberger printf("*** Warning: no boot file name; using '%s'\n",
871cbd8a35cSwdenk nfs_path);
872cbd8a35cSwdenk }
873cbd8a35cSwdenk
874cbd8a35cSwdenk nfs_filename = basename(nfs_path);
875cbd8a35cSwdenk nfs_path = dirname(nfs_path);
876cbd8a35cSwdenk
877faecf84aSJoe Hershberger printf("Using %s device\n", eth_get_name());
878cbd8a35cSwdenk
879faecf84aSJoe Hershberger printf("File transfer via NFS from server %pI4; our IP address is %pI4",
880049a95a7SJoe Hershberger &nfs_server_ip, &net_ip);
881cbd8a35cSwdenk
882cbd8a35cSwdenk /* Check if we need to send across this subnet */
883049a95a7SJoe Hershberger if (net_gateway.s_addr && net_netmask.s_addr) {
884049a95a7SJoe Hershberger struct in_addr our_net;
885049a95a7SJoe Hershberger struct in_addr server_net;
886cbd8a35cSwdenk
887049a95a7SJoe Hershberger our_net.s_addr = net_ip.s_addr & net_netmask.s_addr;
888347e32b0SJoe Hershberger server_net.s_addr = nfs_server_ip.s_addr & net_netmask.s_addr;
889049a95a7SJoe Hershberger if (our_net.s_addr != server_net.s_addr)
890faecf84aSJoe Hershberger printf("; sending through gateway %pI4",
891049a95a7SJoe Hershberger &net_gateway);
892cbd8a35cSwdenk }
893faecf84aSJoe Hershberger printf("\nFilename '%s/%s'.", nfs_path, nfs_filename);
894cbd8a35cSwdenk
8951411157dSJoe Hershberger if (net_boot_file_expected_size_in_blocks) {
896faecf84aSJoe Hershberger printf(" Size is 0x%x Bytes = ",
8971411157dSJoe Hershberger net_boot_file_expected_size_in_blocks << 9);
8981411157dSJoe Hershberger print_size(net_boot_file_expected_size_in_blocks << 9, "");
899cbd8a35cSwdenk }
900faecf84aSJoe Hershberger printf("\nLoad address: 0x%lx\nLoading: *\b", load_addr);
901cbd8a35cSwdenk
902bc0571fcSJoe Hershberger net_set_timeout_handler(nfs_timeout, nfs_timeout_handler);
903049a95a7SJoe Hershberger net_set_udp_handler(nfs_handler);
904cbd8a35cSwdenk
90568c76a3aSJoe Hershberger nfs_timeout_count = 0;
90668c76a3aSJoe Hershberger nfs_state = STATE_PRCLOOKUP_PROG_MOUNT_REQ;
907cbd8a35cSwdenk
90868c76a3aSJoe Hershberger /*nfs_our_port = 4096 + (get_ticks() % 3072);*/
909cbd8a35cSwdenk /*FIX ME !!!*/
91068c76a3aSJoe Hershberger nfs_our_port = 1000;
911cbd8a35cSwdenk
912cbd8a35cSwdenk /* zero out server ether in case the server ip has changed */
9130adb5b76SJoe Hershberger memset(net_server_ethaddr, 0, 6);
914cbd8a35cSwdenk
91568c76a3aSJoe Hershberger nfs_send();
916cbd8a35cSwdenk }
917