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