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