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