From 492ff73de699a40d9eec22b9afe631061b8baef0 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Fri, 5 Dec 2025 09:46:48 +0000 Subject: net:lwip: Add debug line to net-lwip When debugging the LWIP NFS implementation this debug line helped to show the cause of an error. This could be useful to someone in the future. Signed-off-by: Andrew Goodbody Reviewed-by: Jerome Forissier --- net/lwip/net-lwip.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/lwip/net-lwip.c b/net/lwip/net-lwip.c index 8741f65fe12..f70857204b2 100644 --- a/net/lwip/net-lwip.c +++ b/net/lwip/net-lwip.c @@ -297,6 +297,7 @@ static struct pbuf *alloc_pbuf_and_copy(uchar *data, int len) /* We allocate a pbuf chain of pbufs from the pool. */ p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); if (!p) { + debug("Failed to allocate pbuf !!!!!\n"); LINK_STATS_INC(link.memerr); LINK_STATS_INC(link.drop); return NULL; -- cgit v1.3.1 From 60c228c07734a6f3d670c89d2db2e3addd8a73a8 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Fri, 12 Dec 2025 11:32:24 +0000 Subject: net: move net_state to net-common Move the net_state variable into common code so that it can be used by either the legacy network code or lwIP. This is needed for porting across the NFS support code for use with lwIP. Signed-off-by: Andrew Goodbody Reviewed-by: Jerome Forissier --- include/net-common.h | 17 +++++++++++++++++ include/net-legacy.h | 17 ----------------- net/net-common.c | 3 +++ net/net.c | 2 -- 4 files changed, 20 insertions(+), 19 deletions(-) (limited to 'net') diff --git a/include/net-common.h b/include/net-common.h index f5cff3e7c0c..d7a0f7dff7e 100644 --- a/include/net-common.h +++ b/include/net-common.h @@ -13,6 +13,7 @@ #include #define DEBUG_NET_PKT_TRACE 0 /* Trace all packet data */ +#define DEBUG_INT_STATE 0 /* Internal network state changes */ /* * The number of receive packet buffers, and the required packet buffer @@ -114,6 +115,22 @@ struct ip_udp_hdr { #define RINGSZ 4 #define RINGSZ_LOG2 2 +/* Network loop state */ +enum net_loop_state { + NETLOOP_CONTINUE, + NETLOOP_RESTART, + NETLOOP_SUCCESS, + NETLOOP_FAIL +}; + +extern enum net_loop_state net_state; + +static inline void net_set_state(enum net_loop_state state) +{ + debug_cond(DEBUG_INT_STATE, "--- NetState set to %d\n", state); + net_state = state; +} + extern int net_restart_wrap; /* Tried all network devices */ extern uchar *net_rx_packets[PKTBUFSRX]; /* Receive packets */ extern const u8 net_bcast_ethaddr[ARP_HLEN]; /* Ethernet broadcast address */ diff --git a/include/net-legacy.h b/include/net-legacy.h index 9564e97d238..0a10121b0cf 100644 --- a/include/net-legacy.h +++ b/include/net-legacy.h @@ -25,7 +25,6 @@ struct udevice; #define DEBUG_LL_STATE 0 /* Link local state machine changes */ #define DEBUG_DEV_PKT 0 /* Packets or info directed to the device */ #define DEBUG_NET_PKT 0 /* Packets on info on the network at large */ -#define DEBUG_INT_STATE 0 /* Internal network state changes */ /* ARP hardware address length */ #define ARP_HLEN 6 @@ -369,22 +368,6 @@ bool arp_is_waiting(void); /* Waiting for ARP reply? */ void net_set_icmp_handler(rxhand_icmp_f *f); /* Set ICMP RX handler */ void net_set_timeout_handler(ulong t, thand_f *f);/* Set timeout handler */ -/* Network loop state */ -enum net_loop_state { - NETLOOP_CONTINUE, - NETLOOP_RESTART, - NETLOOP_SUCCESS, - NETLOOP_FAIL -}; - -extern enum net_loop_state net_state; - -static inline void net_set_state(enum net_loop_state state) -{ - debug_cond(DEBUG_INT_STATE, "--- NetState set to %d\n", state); - net_state = state; -} - /* * net_get_async_tx_pkt_buf - Get a packet buffer that is not in use for * sending an asynchronous reply diff --git a/net/net-common.c b/net/net-common.c index c68e19fc03e..ec1e179f7d9 100644 --- a/net/net-common.c +++ b/net/net-common.c @@ -6,6 +6,9 @@ #include #include +/* Network loop state */ +enum net_loop_state net_state; + void copy_filename(char *dst, const char *src, int size) { if (src && *src && (*src == '"')) { diff --git a/net/net.c b/net/net.c index 44ce0ba4011..096f01427ba 100644 --- a/net/net.c +++ b/net/net.c @@ -157,8 +157,6 @@ const u8 net_null_ethaddr[6]; #if defined(CONFIG_API) || defined(CONFIG_EFI_LOADER) void (*push_packet)(void *, int len) = 0; #endif -/* Network loop state */ -enum net_loop_state net_state; /* Tried all network devices */ int net_restart_wrap; /* Network loop restarted */ -- cgit v1.3.1 From 3938ae6457193cd3b0abb8b74957a388a6b3e639 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Fri, 12 Dec 2025 11:32:25 +0000 Subject: net: Move some variables to net-common files Make some variables available to be used by either the legacy network code or lwIP by moving them into the net-common files. This also allowed removing a small number of duplicated variables from the lwIP code. Signed-off-by: Andrew Goodbody Reviewed-by: Jerome Forissier --- include/net-common.h | 12 ++++++++++-- include/net-legacy.h | 5 ----- net/lwip/net-lwip.c | 5 ----- net/net-common.c | 9 +++++++++ net/net.c | 10 ---------- 5 files changed, 19 insertions(+), 22 deletions(-) (limited to 'net') diff --git a/include/net-common.h b/include/net-common.h index d7a0f7dff7e..f293b21bc0b 100644 --- a/include/net-common.h +++ b/include/net-common.h @@ -132,13 +132,21 @@ static inline void net_set_state(enum net_loop_state state) } extern int net_restart_wrap; /* Tried all network devices */ -extern uchar *net_rx_packets[PKTBUFSRX]; /* Receive packets */ +extern uchar *net_rx_packets[PKTBUFSRX]; /* Receive packets */ extern const u8 net_bcast_ethaddr[ARP_HLEN]; /* Ethernet broadcast address */ -extern char net_boot_file_name[1024];/* Boot File name */ extern struct in_addr net_ip; /* Our IP addr (0 = unknown) */ /* Indicates whether the pxe path prefix / config file was specified in dhcp option */ extern char *pxelinux_configfile; +/* Our IP addr (0 = unknown) */ +extern struct in_addr net_ip; +/* Boot File name */ +extern char net_boot_file_name[1024]; +/* The actual transferred size of the bootfile (in bytes) */ +extern u32 net_boot_file_size; +/* Boot file size in blocks as reported by the DHCP server */ +extern u32 net_boot_file_expected_size_in_blocks; + /** * compute_ip_checksum() - Compute IP checksum * diff --git a/include/net-legacy.h b/include/net-legacy.h index 0a10121b0cf..d489c2480cd 100644 --- a/include/net-legacy.h +++ b/include/net-legacy.h @@ -289,7 +289,6 @@ extern u8 net_ethaddr[ARP_HLEN]; /* Our ethernet address */ extern u8 net_server_ethaddr[ARP_HLEN]; /* Boot server enet address */ extern struct in_addr net_server_ip; /* Server IP addr (0 = unknown) */ extern uchar *net_tx_packet; /* THE transmit packet */ -extern uchar *net_rx_packets[PKTBUFSRX]; /* Receive packets */ extern uchar *net_rx_packet; /* Current receive packet */ extern int net_rx_packet_len; /* Current rx packet length */ extern const u8 net_null_ethaddr[ARP_HLEN]; @@ -309,10 +308,6 @@ enum proto_t { /* Indicates whether the file name was specified on the command line */ extern bool net_boot_file_name_explicit; -/* The actual transferred size of the bootfile (in bytes) */ -extern u32 net_boot_file_size; -/* Boot file size in blocks as reported by the DHCP server */ -extern u32 net_boot_file_expected_size_in_blocks; #if defined(CONFIG_DNS) extern char *net_dns_resolve; /* The host to resolve */ diff --git a/net/lwip/net-lwip.c b/net/lwip/net-lwip.c index f70857204b2..c325e923d20 100644 --- a/net/lwip/net-lwip.c +++ b/net/lwip/net-lwip.c @@ -33,13 +33,8 @@ static int net_restarted; int net_restart_wrap; static uchar net_pkt_buf[(PKTBUFSRX) * PKTSIZE_ALIGN + PKTALIGN] __aligned(PKTALIGN); -uchar *net_rx_packets[PKTBUFSRX]; -uchar *net_rx_packet; const u8 net_bcast_ethaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; char *pxelinux_configfile; -/* Our IP addr (0 = unknown) */ -struct in_addr net_ip; -char net_boot_file_name[1024]; static err_t net_lwip_tx(struct netif *netif, struct pbuf *p) { diff --git a/net/net-common.c b/net/net-common.c index ec1e179f7d9..f21a80357f6 100644 --- a/net/net-common.c +++ b/net/net-common.c @@ -8,6 +8,15 @@ /* Network loop state */ enum net_loop_state net_state; +/* Our IP addr (0 = unknown) */ +struct in_addr net_ip; +/* Boot File name */ +char net_boot_file_name[1024]; +/* The actual transferred size of the bootfile (in bytes) */ +u32 net_boot_file_size; +/* Boot file size in blocks as reported by the DHCP server */ +u32 net_boot_file_expected_size_in_blocks; +uchar *net_rx_packets[PKTBUFSRX]; void copy_filename(char *dst, const char *src, int size) { diff --git a/net/net.c b/net/net.c index 096f01427ba..8a8160e633f 100644 --- a/net/net.c +++ b/net/net.c @@ -141,8 +141,6 @@ char *pxelinux_configfile; u8 net_ethaddr[6]; /* Boot server enet address */ u8 net_server_ethaddr[6]; -/* Our IP addr (0 = unknown) */ -struct in_addr net_ip; /* Server IP addr (0 = unknown) */ struct in_addr net_server_ip; /* Current receive packet */ @@ -170,18 +168,10 @@ ushort net_our_vlan = 0xFFFF; /* ditto */ ushort net_native_vlan = 0xFFFF; -/* Boot File name */ -char net_boot_file_name[1024]; /* Indicates whether the file name was specified on the command line */ bool net_boot_file_name_explicit; -/* The actual transferred size of the bootfile (in bytes) */ -u32 net_boot_file_size; -/* Boot file size in blocks as reported by the DHCP server */ -u32 net_boot_file_expected_size_in_blocks; static uchar net_pkt_buf[(PKTBUFSRX+1) * PKTSIZE_ALIGN + PKTALIGN]; -/* Receive packets */ -uchar *net_rx_packets[PKTBUFSRX]; /* Current UDP RX packet handler */ static rxhand_f *udp_packet_handler; /* Current ARP RX packet handler */ -- cgit v1.3.1 From 046f553fe872317707055dd308a5f0e4baec910a Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Fri, 12 Dec 2025 11:32:26 +0000 Subject: net: nfs: Add licence header Add the same GPL2+ licence header to the NFS code as appears on other NFS related files. Acked-by: Jerome Forissier Signed-off-by: Andrew Goodbody --- net/nfs.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/nfs.c b/net/nfs.c index 537d4c62de2..7bae7c78642 100644 --- a/net/nfs.c +++ b/net/nfs.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * NFS support driver - based on etherboot and U-BOOT's tftp.c * -- cgit v1.3.1 From 3bc1197e3d7c54bcbd2dfb481cb4a9ae47f3e8d3 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Fri, 12 Dec 2025 11:32:27 +0000 Subject: net: nfs: Move most NFS code to common files Move most of the NFS code into common files so that it can be used by an lwIP port of NFS. Acked-by: Jerome Forissier Signed-off-by: Andrew Goodbody --- net/Makefile | 1 + net/nfs-common.c | 861 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ net/nfs-common.h | 123 ++++++++ net/nfs.c | 848 +----------------------------------------------------- net/nfs.h | 59 ---- 5 files changed, 998 insertions(+), 894 deletions(-) create mode 100644 net/nfs-common.c create mode 100644 net/nfs-common.h (limited to 'net') diff --git a/net/Makefile b/net/Makefile index 468820186cf..3a32bc8b0e7 100644 --- a/net/Makefile +++ b/net/Makefile @@ -43,6 +43,7 @@ obj-$(CONFIG_$(PHASE_)DM_ETH) += eth-uclass.o obj-$(CONFIG_$(PHASE_)BOOTDEV_ETH) += eth_bootdev.o obj-$(CONFIG_DM_MDIO) += mdio-uclass.o obj-$(CONFIG_DM_MDIO_MUX) += mdio-mux-uclass.o +obj-$(CONFIG_CMD_NFS) += nfs-common.o obj-$(CONFIG_$(PHASE_)DM_ETH) += eth_common.o obj-y += net-common.o endif diff --git a/net/nfs-common.c b/net/nfs-common.c new file mode 100644 index 00000000000..a6a70677bd2 --- /dev/null +++ b/net/nfs-common.c @@ -0,0 +1,861 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * NFS support driver - based on etherboot and U-BOOT's tftp.c + * + * Masami Komiya 2004 + * + */ + +/* NOTE: the NFS code is heavily inspired by the NetBSD netboot code (read: + * large portions are copied verbatim) as distributed in OSKit 0.97. A few + * changes were necessary to adapt the code to Etherboot and to fix several + * inconsistencies. Also the RPC message preparation is done "by hand" to + * avoid adding netsprintf() which I find hard to understand and use. + */ + +/* NOTE 2: Etherboot does not care about things beyond the kernel image, so + * it loads the kernel image off the boot server (ARP_SERVER) and does not + * access the client root disk (root-path in dhcpd.conf), which would use + * ARP_ROOTSERVER. The root disk is something the operating system we are + * about to load needs to use. This is different from the OSKit 0.97 logic. + */ + +/* NOTE 3: Symlink handling introduced by Anselm M Hoffmeister, 2003-July-14 + * If a symlink is encountered, it is followed as far as possible (recursion + * possible, maximum 16 steps). There is no clearing of ".."'s inside the + * path, so please DON'T DO THAT. thx. + */ + +/* NOTE 4: NFSv3 support added by Guillaume GARDET, 2016-June-20. + * NFSv2 is still used by default. But if server does not support NFSv2, then + * NFSv3 is used, if available on NFS server. + */ + +/* NOTE 5: NFSv1 support added by Christian Gmeiner, Thomas Rienoessl, + * September 27, 2018. As of now, NFSv3 is the default choice. If the server + * does not support NFSv3, we fall back to versions 2 or 1. + */ + +#ifdef CONFIG_SYS_DIRECT_FLASH_NFS +#include +#endif +#include +#include +#include +#include +#include "nfs.h" +#include "nfs-common.h" + +static int fs_mounted; +static char dirfh[NFS3_FHSIZE]; /* NFSv2 / NFSv3 file handle of directory */ +static unsigned int dirfh3_length; /* (variable) length of dirfh when NFSv3 */ +static char filefh[NFS3_FHSIZE]; /* NFSv2 / NFSv3 file handle */ +static unsigned int filefh3_length; /* (variable) length of filefh when NFSv3 */ + +enum net_loop_state nfs_download_state; +char *nfs_filename; +char *nfs_path; +char nfs_path_buff[2048]; +struct in_addr nfs_server_ip; +int nfs_server_mount_port; +int nfs_server_port; +int nfs_our_port; +int nfs_state; +int nfs_timeout_count; +unsigned long rpc_id; +int nfs_offset = -1; +int nfs_len; + +const ulong nfs_timeout = CONFIG_NFS_TIMEOUT; + +enum nfs_version choosen_nfs_version = NFS_V3; + +static inline int store_block(uchar *src, unsigned int offset, unsigned int len) +{ + ulong newsize = offset + len; +#ifdef CONFIG_SYS_DIRECT_FLASH_NFS + int i, rc = 0; + + for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) { + /* start address in flash? */ + if (image_load_addr + offset >= flash_info[i].start[0]) { + rc = 1; + break; + } + } + + if (rc) { /* Flash is destination for this packet */ + rc = flash_write((uchar *)src, (ulong)image_load_addr + offset, + len); + if (rc) { + flash_perror(rc); + return -1; + } + } else +#endif /* CONFIG_SYS_DIRECT_FLASH_NFS */ + { + void *ptr = map_sysmem(image_load_addr + offset, len); + + memcpy(ptr, src, len); + unmap_sysmem(ptr); + } + + if (net_boot_file_size < (offset + len)) + net_boot_file_size = newsize; + return 0; +} + +char *nfs_basename(char *path) +{ + char *fname; + + fname = path + strlen(path) - 1; + while (fname >= path) { + if (*fname == '/') { + fname++; + break; + } + fname--; + } + return fname; +} + +char *nfs_dirname(char *path) +{ + char *fname; + + fname = nfs_basename(path); + --fname; + *fname = '\0'; + return path; +} + +/************************************************************************** + * RPC_ADD_CREDENTIALS - Add RPC authentication/verifier entries + ************************************************************************** + */ +static uint32_t *rpc_add_credentials(uint32_t *p) +{ + /* Here's the executive summary on authentication requirements of the + * various NFS server implementations: Linux accepts both AUTH_NONE + * and AUTH_UNIX authentication (also accepts an empty hostname field + * in the AUTH_UNIX scheme). *BSD refuses AUTH_NONE, but accepts + * AUTH_UNIX (also accepts an empty hostname field in the AUTH_UNIX + * scheme). To be safe, use AUTH_UNIX and pass the hostname if we have + * it (if the BOOTP/DHCP reply didn't give one, just use an empty + * hostname). + */ + + /* Provide an AUTH_UNIX credential. */ + *p++ = htonl(1); /* AUTH_UNIX */ + *p++ = htonl(20); /* auth length */ + *p++ = 0; /* stamp */ + *p++ = 0; /* hostname string */ + *p++ = 0; /* uid */ + *p++ = 0; /* gid */ + *p++ = 0; /* auxiliary gid list */ + + /* Provide an AUTH_NONE verifier. */ + *p++ = 0; /* AUTH_NONE */ + *p++ = 0; /* auth length */ + + return p; +} + +void rpc_req_common(int rpc_prog, int rpc_proc, uint32_t *data, int datalen, + uchar *txbuff, int *pktlen, int *sport) +{ + struct rpc_t rpc_pkt; + unsigned long id; + u32 *p; + + id = ++rpc_id; + rpc_pkt.u.call.id = htonl(id); + rpc_pkt.u.call.type = htonl(MSG_CALL); + rpc_pkt.u.call.rpcvers = htonl(2); /* use RPC version 2 */ + rpc_pkt.u.call.prog = htonl(rpc_prog); + switch (rpc_prog) { + case PROG_NFS: + switch (choosen_nfs_version) { + case NFS_V1: + case NFS_V2: + rpc_pkt.u.call.vers = htonl(2); + break; + + case NFS_V3: + rpc_pkt.u.call.vers = htonl(3); + break; + + case NFS_UNKOWN: + /* nothing to do */ + break; + } + break; + case PROG_MOUNT: + switch (choosen_nfs_version) { + case NFS_V1: + rpc_pkt.u.call.vers = htonl(1); + break; + + case NFS_V2: + rpc_pkt.u.call.vers = htonl(2); + break; + + case NFS_V3: + rpc_pkt.u.call.vers = htonl(3); + break; + + case NFS_UNKOWN: + /* nothing to do */ + break; + } + break; + case PROG_PORTMAP: + default: + rpc_pkt.u.call.vers = htonl(2); /* portmapper is version 2 */ + } + rpc_pkt.u.call.proc = htonl(rpc_proc); + p = rpc_pkt.u.call.data; + + if (datalen) + memcpy(p, data, datalen * sizeof(uint32_t)); + + *pktlen = (char *)p + datalen * sizeof(uint32_t) - (char *)&rpc_pkt; + + memcpy(txbuff, &rpc_pkt.u.data[0], *pktlen); + + if (rpc_prog == PROG_PORTMAP) + *sport = SUNRPC_PORT; + else if (rpc_prog == PROG_MOUNT) + *sport = nfs_server_mount_port; + else + *sport = nfs_server_port; +} + +/************************************************************************** + * RPC_LOOKUP - Lookup RPC Port numbers + ************************************************************************** + */ +static void rpc_lookup_req(int prog, int ver) +{ + u32 data[16]; + + data[0] = 0; data[1] = 0; /* auth credential */ + data[2] = 0; data[3] = 0; /* auth verifier */ + data[4] = htonl(prog); + data[5] = htonl(ver); + data[6] = htonl(17); /* IP_UDP */ + data[7] = 0; + rpc_req(PROG_PORTMAP, PORTMAP_GETPORT, data, 8); +} + +/************************************************************************** + * NFS_MOUNT - Mount an NFS Filesystem + ************************************************************************** + */ +static void nfs_mount_req(char *path) +{ + u32 data[1024]; + u32 *p; + int len; + int pathlen; + + pathlen = strlen(path); + + p = &data[0]; + p = rpc_add_credentials(p); + + *p++ = htonl(pathlen); + if (pathlen & 3) + *(p + pathlen / 4) = 0; + memcpy(p, path, pathlen); + p += (pathlen + 3) / 4; + + len = (uint32_t *)p - (uint32_t *)&data[0]; + + rpc_req(PROG_MOUNT, MOUNT_ADDENTRY, data, len); +} + +/************************************************************************** + * NFS_UMOUNTALL - Unmount all our NFS Filesystems on the Server + ************************************************************************** + */ +static void nfs_umountall_req(void) +{ + u32 data[1024]; + u32 *p; + int len; + + if ((nfs_server_mount_port == -1) || !fs_mounted) + /* Nothing mounted, nothing to umount */ + return; + + p = &data[0]; + p = rpc_add_credentials(p); + + len = (uint32_t *)p - (uint32_t *)&data[0]; + + rpc_req(PROG_MOUNT, MOUNT_UMOUNTALL, data, len); +} + +/*************************************************************************** + * NFS_READLINK (AH 2003-07-14) + * This procedure is called when read of the first block fails - + * this probably happens when it's a directory or a symlink + * In case of successful readlink(), the dirname is manipulated, + * so that inside the nfs() function a recursion can be done. + *************************************************************************** + */ +static void nfs_readlink_req(void) +{ + u32 data[1024]; + u32 *p; + int len; + + p = &data[0]; + p = rpc_add_credentials(p); + + if (choosen_nfs_version != NFS_V3) { + memcpy(p, filefh, NFS_FHSIZE); + p += (NFS_FHSIZE / 4); + } else { /* NFS_V3 */ + *p++ = htonl(filefh3_length); + memcpy(p, filefh, filefh3_length); + p += (filefh3_length / 4); + } + + len = (uint32_t *)p - (uint32_t *)&data[0]; + + rpc_req(PROG_NFS, NFS_READLINK, data, len); +} + +/************************************************************************** + * NFS_LOOKUP - Lookup Pathname + ************************************************************************** + */ +static void nfs_lookup_req(char *fname) +{ + u32 data[1024]; + u32 *p; + int len; + int fnamelen; + + fnamelen = strlen(fname); + + p = &data[0]; + p = rpc_add_credentials(p); + + if (choosen_nfs_version != NFS_V3) { + memcpy(p, dirfh, NFS_FHSIZE); + p += (NFS_FHSIZE / 4); + *p++ = htonl(fnamelen); + if (fnamelen & 3) + *(p + fnamelen / 4) = 0; + memcpy(p, fname, fnamelen); + p += (fnamelen + 3) / 4; + + len = (uint32_t *)p - (uint32_t *)&data[0]; + + rpc_req(PROG_NFS, NFS_LOOKUP, data, len); + } else { /* NFS_V3 */ + *p++ = htonl(dirfh3_length); /* Dir handle length */ + memcpy(p, dirfh, dirfh3_length); + p += (dirfh3_length / 4); + *p++ = htonl(fnamelen); + if (fnamelen & 3) + *(p + fnamelen / 4) = 0; + memcpy(p, fname, fnamelen); + p += (fnamelen + 3) / 4; + + len = (uint32_t *)p - (uint32_t *)&data[0]; + + rpc_req(PROG_NFS, NFS3PROC_LOOKUP, data, len); + } +} + +/************************************************************************** + * NFS_READ - Read File on NFS Server + ************************************************************************** + */ +static void nfs_read_req(int offset, int readlen) +{ + u32 data[1024]; + u32 *p; + int len; + + p = &data[0]; + p = rpc_add_credentials(p); + + if (choosen_nfs_version != NFS_V3) { + memcpy(p, filefh, NFS_FHSIZE); + p += (NFS_FHSIZE / 4); + *p++ = htonl(offset); + *p++ = htonl(readlen); + *p++ = 0; + } else { /* NFS_V3 */ + *p++ = htonl(filefh3_length); + memcpy(p, filefh, filefh3_length); + p += (filefh3_length / 4); + *p++ = htonl(0); /* offset is 64-bit long, so fill with 0 */ + *p++ = htonl(offset); + *p++ = htonl(readlen); + *p++ = 0; + } + + len = (uint32_t *)p - (uint32_t *)&data[0]; + + rpc_req(PROG_NFS, NFS_READ, data, len); +} + +/************************************************************************** + * RPC request dispatcher + ************************************************************************** + */ +void nfs_send(void) +{ + switch (nfs_state) { + case STATE_PRCLOOKUP_PROG_MOUNT_REQ: + if (choosen_nfs_version != NFS_V3) + rpc_lookup_req(PROG_MOUNT, 1); + else /* NFS_V3 */ + rpc_lookup_req(PROG_MOUNT, 3); + break; + case STATE_PRCLOOKUP_PROG_NFS_REQ: + if (choosen_nfs_version != NFS_V3) + rpc_lookup_req(PROG_NFS, 2); + else /* NFS_V3 */ + rpc_lookup_req(PROG_NFS, 3); + break; + case STATE_MOUNT_REQ: + nfs_mount_req(nfs_path); + break; + case STATE_UMOUNT_REQ: + nfs_umountall_req(); + break; + case STATE_LOOKUP_REQ: + nfs_lookup_req(nfs_filename); + break; + case STATE_READ_REQ: + nfs_read_req(nfs_offset, nfs_len); + break; + case STATE_READLINK_REQ: + nfs_readlink_req(); + break; + } +} + +/************************************************************************** + * Handlers for the reply from server + ************************************************************************** + */ + +static int rpc_handle_error(struct rpc_t *rpc_pkt) +{ + if (rpc_pkt->u.reply.rstatus || + rpc_pkt->u.reply.verifier || + rpc_pkt->u.reply.astatus || + rpc_pkt->u.reply.data[0]) { + switch (ntohl(rpc_pkt->u.reply.astatus)) { + case NFS_RPC_SUCCESS: /* Not an error */ + break; + case NFS_RPC_PROG_MISMATCH: { + /* Remote can't support NFS version */ + const int min = ntohl(rpc_pkt->u.reply.data[0]); + const int max = ntohl(rpc_pkt->u.reply.data[1]); + + if (max < NFS_V1 || max > NFS_V3 || min > NFS_V3) { + puts("*** ERROR: NFS version not supported"); + debug(": Requested: V%d, accepted: min V%d - max V%d\n", + choosen_nfs_version, + ntohl(rpc_pkt->u.reply.data[0]), + ntohl(rpc_pkt->u.reply.data[1])); + puts("\n"); + choosen_nfs_version = NFS_UNKOWN; + break; + } + + debug("*** Warning: NFS version not supported: Requested: V%d, accepted: min V%d - max V%d\n", + choosen_nfs_version, + ntohl(rpc_pkt->u.reply.data[0]), + ntohl(rpc_pkt->u.reply.data[1])); + debug("Will retry with NFSv%d\n", min); + choosen_nfs_version = min; + return -NFS_RPC_PROG_MISMATCH; + } + case NFS_RPC_PROG_UNAVAIL: + case NFS_RPC_PROC_UNAVAIL: + case NFS_RPC_GARBAGE_ARGS: + case NFS_RPC_SYSTEM_ERR: + default: /* Unknown error on 'accept state' flag */ + debug("*** ERROR: accept state error (%d)\n", + ntohl(rpc_pkt->u.reply.astatus)); + break; + } + return -1; + } + + return 0; +} + +static int rpc_lookup_reply(int prog, uchar *pkt, unsigned int len) +{ + struct rpc_t rpc_pkt; + + memcpy(&rpc_pkt.u.data[0], pkt, len); + + if (ntohl(rpc_pkt.u.reply.id) > rpc_id) + return -NFS_RPC_ERR; + else if (ntohl(rpc_pkt.u.reply.id) < rpc_id) + return -NFS_RPC_DROP; + + if (rpc_pkt.u.reply.rstatus || + rpc_pkt.u.reply.verifier || + rpc_pkt.u.reply.astatus) + return -1; + + switch (prog) { + case PROG_MOUNT: + nfs_server_mount_port = ntohl(rpc_pkt.u.reply.data[0]); + break; + case PROG_NFS: + nfs_server_port = ntohl(rpc_pkt.u.reply.data[0]); + break; + } + + return 0; +} + +static int nfs_mount_reply(uchar *pkt, unsigned int len) +{ + struct rpc_t rpc_pkt; + int ret; + + memcpy(&rpc_pkt.u.data[0], pkt, len); + + if (ntohl(rpc_pkt.u.reply.id) > rpc_id) + return -NFS_RPC_ERR; + else if (ntohl(rpc_pkt.u.reply.id) < rpc_id) + return -NFS_RPC_DROP; + + ret = rpc_handle_error(&rpc_pkt); + if (ret) + return ret; + + fs_mounted = 1; + /* NFSv2 and NFSv3 use same structure */ + if (choosen_nfs_version != NFS_V3) { + memcpy(dirfh, rpc_pkt.u.reply.data + 1, NFS_FHSIZE); + } else { + dirfh3_length = ntohl(rpc_pkt.u.reply.data[1]); + if (dirfh3_length > NFS3_FHSIZE) + dirfh3_length = NFS3_FHSIZE; + memcpy(dirfh, rpc_pkt.u.reply.data + 2, dirfh3_length); + } + + return 0; +} + +static int nfs_umountall_reply(uchar *pkt, unsigned int len) +{ + struct rpc_t rpc_pkt; + + memcpy(&rpc_pkt.u.data[0], pkt, len); + + if (ntohl(rpc_pkt.u.reply.id) > rpc_id) + return -NFS_RPC_ERR; + else if (ntohl(rpc_pkt.u.reply.id) < rpc_id) + return -NFS_RPC_DROP; + + if (rpc_pkt.u.reply.rstatus || + rpc_pkt.u.reply.verifier || + rpc_pkt.u.reply.astatus) + return -1; + + fs_mounted = 0; + memset(dirfh, 0, sizeof(dirfh)); + + return 0; +} + +static int nfs_lookup_reply(uchar *pkt, unsigned int len) +{ + struct rpc_t rpc_pkt; + int ret; + + memcpy(&rpc_pkt.u.data[0], pkt, len); + + if (ntohl(rpc_pkt.u.reply.id) > rpc_id) + return -NFS_RPC_ERR; + else if (ntohl(rpc_pkt.u.reply.id) < rpc_id) + return -NFS_RPC_DROP; + + ret = rpc_handle_error(&rpc_pkt); + if (ret) + return ret; + + if (choosen_nfs_version != NFS_V3) { + if (((uchar *)&rpc_pkt.u.reply.data[0] - (uchar *)&rpc_pkt + NFS_FHSIZE) > len) + return -NFS_RPC_DROP; + memcpy(filefh, rpc_pkt.u.reply.data + 1, NFS_FHSIZE); + } else { /* NFS_V3 */ + filefh3_length = ntohl(rpc_pkt.u.reply.data[1]); + if (filefh3_length > NFS3_FHSIZE) + filefh3_length = NFS3_FHSIZE; + memcpy(filefh, rpc_pkt.u.reply.data + 2, filefh3_length); + } + + return 0; +} + +static int nfs3_get_attributes_offset(uint32_t *data) +{ + if (data[1]) { + /* 'attributes_follow' flag is TRUE, + * so we have attributes on 21 dwords + */ + /* Skip unused values : + * type; 32 bits value, + * mode; 32 bits value, + * nlink; 32 bits value, + * uid; 32 bits value, + * gid; 32 bits value, + * size; 64 bits value, + * used; 64 bits value, + * rdev; 64 bits value, + * fsid; 64 bits value, + * fileid; 64 bits value, + * atime; 64 bits value, + * mtime; 64 bits value, + * ctime; 64 bits value, + */ + return 22; + } + + /* 'attributes_follow' flag is FALSE, + * so we don't have any attributes + */ + return 1; +} + +static int nfs_readlink_reply(uchar *pkt, unsigned int len) +{ + struct rpc_t rpc_pkt; + int rlen; + int nfsv3_data_offset = 0; + + memcpy((unsigned char *)&rpc_pkt, pkt, len); + + if (ntohl(rpc_pkt.u.reply.id) > rpc_id) + return -NFS_RPC_ERR; + else if (ntohl(rpc_pkt.u.reply.id) < rpc_id) + return -NFS_RPC_DROP; + + if (rpc_pkt.u.reply.rstatus || + rpc_pkt.u.reply.verifier || + rpc_pkt.u.reply.astatus || + rpc_pkt.u.reply.data[0]) + return -1; + + if (choosen_nfs_version == NFS_V3) { + nfsv3_data_offset = + nfs3_get_attributes_offset(rpc_pkt.u.reply.data); + } + + /* new path length */ + rlen = ntohl(rpc_pkt.u.reply.data[1 + nfsv3_data_offset]); + + if (((uchar *)&rpc_pkt.u.reply.data[0] - (uchar *)&rpc_pkt + rlen) > len) + return -NFS_RPC_DROP; + + if (*((char *)&rpc_pkt.u.reply.data[2 + nfsv3_data_offset]) != '/') { + int pathlen; + + strcat(nfs_path, "/"); + pathlen = strlen(nfs_path); + memcpy(nfs_path + pathlen, + (uchar *)&rpc_pkt.u.reply.data[2 + nfsv3_data_offset], + rlen); + nfs_path[pathlen + rlen] = 0; + } else { + memcpy(nfs_path, + (uchar *)&rpc_pkt.u.reply.data[2 + nfsv3_data_offset], + rlen); + nfs_path[rlen] = 0; + } + return 0; +} + +static int nfs_read_reply(uchar *pkt, unsigned int len) +{ + struct rpc_t rpc_pkt; + int rlen; + uchar *data_ptr; + + memcpy(&rpc_pkt.u.data[0], pkt, sizeof(rpc_pkt.u.reply)); + + if (ntohl(rpc_pkt.u.reply.id) > rpc_id) + return -NFS_RPC_ERR; + else if (ntohl(rpc_pkt.u.reply.id) < rpc_id) + return -NFS_RPC_DROP; + + if (rpc_pkt.u.reply.rstatus || + rpc_pkt.u.reply.verifier || + rpc_pkt.u.reply.astatus || + rpc_pkt.u.reply.data[0]) { + if (rpc_pkt.u.reply.rstatus) + return -9999; + if (rpc_pkt.u.reply.astatus) + return -9999; + return -ntohl(rpc_pkt.u.reply.data[0]); + } + + if (nfs_offset != 0 && !((nfs_offset) % + (NFS_READ_SIZE / 2 * 10 * HASHES_PER_LINE))) + puts("\n\t "); + if (!(nfs_offset % ((NFS_READ_SIZE / 2) * 10))) + putc('#'); + + if (choosen_nfs_version != NFS_V3) { + rlen = ntohl(rpc_pkt.u.reply.data[18]); + data_ptr = (uchar *)&rpc_pkt.u.reply.data[19]; + } else { /* NFS_V3 */ + int nfsv3_data_offset = + nfs3_get_attributes_offset(rpc_pkt.u.reply.data); + + /* count value */ + rlen = ntohl(rpc_pkt.u.reply.data[1 + nfsv3_data_offset]); + /* Skip unused values : + * EOF: 32 bits value, + * data_size: 32 bits value, + */ + data_ptr = (uchar *) + &rpc_pkt.u.reply.data[4 + nfsv3_data_offset]; + } + + if (((uchar *)&rpc_pkt.u.reply.data[0] - (uchar *)&rpc_pkt + rlen) > len) + return -9999; + + if (store_block(data_ptr, nfs_offset, rlen)) + return -9999; + + return rlen; +} + +void nfs_pkt_recv(uchar *pkt, unsigned int len) +{ + int rlen; + int reply; + + switch (nfs_state) { + case STATE_PRCLOOKUP_PROG_MOUNT_REQ: + if (rpc_lookup_reply(PROG_MOUNT, pkt, len) == -NFS_RPC_DROP) + break; + nfs_state = STATE_PRCLOOKUP_PROG_NFS_REQ; + nfs_send(); + break; + + case STATE_PRCLOOKUP_PROG_NFS_REQ: + if (rpc_lookup_reply(PROG_NFS, pkt, len) == -NFS_RPC_DROP) + break; + nfs_state = STATE_MOUNT_REQ; + nfs_send(); + break; + + case STATE_MOUNT_REQ: + reply = nfs_mount_reply(pkt, len); + if (reply == -NFS_RPC_DROP) { + break; + } else if (reply == -NFS_RPC_ERR) { + puts("*** ERROR: Cannot mount\n"); + /* just to be sure... */ + nfs_state = STATE_UMOUNT_REQ; + nfs_send(); + } else if (reply == -NFS_RPC_PROG_MISMATCH && + choosen_nfs_version != NFS_UNKOWN) { + nfs_state = STATE_MOUNT_REQ; + nfs_send(); + } else { + nfs_state = STATE_LOOKUP_REQ; + nfs_send(); + } + break; + + case STATE_UMOUNT_REQ: + reply = nfs_umountall_reply(pkt, len); + if (reply == -NFS_RPC_DROP) { + break; + } else if (reply == -NFS_RPC_ERR) { + debug("*** ERROR: Cannot umount\n"); + net_set_state(NETLOOP_FAIL); + } else { + puts("\ndone\n"); + net_set_state(nfs_download_state); + } + break; + + case STATE_LOOKUP_REQ: + reply = nfs_lookup_reply(pkt, len); + if (reply == -NFS_RPC_DROP) { + break; + } else if (reply == -NFS_RPC_ERR) { + puts("*** ERROR: File lookup fail\n"); + nfs_state = STATE_UMOUNT_REQ; + nfs_send(); + } else if (reply == -NFS_RPC_PROG_MISMATCH && + choosen_nfs_version != NFS_UNKOWN) { + /* umount */ + nfs_state = STATE_UMOUNT_REQ; + nfs_send(); + /* And retry with another supported version */ + nfs_state = STATE_PRCLOOKUP_PROG_MOUNT_REQ; + nfs_send(); + } else { + nfs_state = STATE_READ_REQ; + nfs_offset = 0; + nfs_len = NFS_READ_SIZE; + nfs_send(); + } + break; + + case STATE_READLINK_REQ: + reply = nfs_readlink_reply(pkt, len); + if (reply == -NFS_RPC_DROP) { + break; + } else if (reply == -NFS_RPC_ERR) { + puts("*** ERROR: Symlink fail\n"); + nfs_state = STATE_UMOUNT_REQ; + nfs_send(); + } else { + debug("Symlink --> %s\n", nfs_path); + nfs_filename = nfs_basename(nfs_path); + nfs_path = nfs_dirname(nfs_path); + + nfs_state = STATE_MOUNT_REQ; + nfs_send(); + } + break; + + case STATE_READ_REQ: + rlen = nfs_read_reply(pkt, len); + if (rlen == -NFS_RPC_DROP) + break; + nfs_refresh_timeout(); + if (rlen > 0) { + nfs_offset += rlen; + nfs_send(); + } else if ((rlen == -NFSERR_ISDIR) || (rlen == -NFSERR_INVAL)) { + /* symbolic link */ + nfs_state = STATE_READLINK_REQ; + nfs_send(); + } else { + if (!rlen) + nfs_download_state = NETLOOP_SUCCESS; + if (rlen < 0) + debug("NFS READ error (%d)\n", rlen); + nfs_state = STATE_UMOUNT_REQ; + nfs_send(); + } + break; + } +} + diff --git a/net/nfs-common.h b/net/nfs-common.h new file mode 100644 index 00000000000..a19b98b7b1f --- /dev/null +++ b/net/nfs-common.h @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Masami Komiya 2004 + */ + +#ifndef __NFS_COMMON_H__ +#define __NFS_COMMON_H__ + +#define SUNRPC_PORT 111 + +#define PROG_PORTMAP 100000 +#define PROG_NFS 100003 +#define PROG_MOUNT 100005 + +#define MSG_CALL 0 +#define MSG_REPLY 1 + +#define HASHES_PER_LINE 65 /* Number of "loading" hashes per line */ +#define NFS_RETRY_COUNT 30 + +#define NFS_RPC_ERR 1 +#define NFS_RPC_DROP 124 + +#define NFSERR_PERM 1 +#define NFSERR_NOENT 2 +#define NFSERR_ACCES 13 +#define NFSERR_ISDIR 21 +#define NFSERR_INVAL 22 + +/* Values for Accept State flag on RPC answers (See: rfc1831) */ +enum rpc_accept_stat { + NFS_RPC_SUCCESS = 0, /* RPC executed successfully */ + NFS_RPC_PROG_UNAVAIL = 1, /* remote hasn't exported program */ + NFS_RPC_PROG_MISMATCH = 2, /* remote can't support version # */ + NFS_RPC_PROC_UNAVAIL = 3, /* program can't support procedure */ + NFS_RPC_GARBAGE_ARGS = 4, /* procedure can't decode params */ + NFS_RPC_SYSTEM_ERR = 5 /* errors like memory allocation failure */ +}; + +enum nfs_version { + NFS_UNKOWN = 0, + NFS_V1 = 1, + NFS_V2 = 2, + NFS_V3 = 3, +}; + +extern enum net_loop_state nfs_download_state; +extern char *nfs_filename; +extern char *nfs_path; +extern char nfs_path_buff[2048]; +extern struct in_addr nfs_server_ip; +extern int nfs_server_mount_port; +extern int nfs_server_port; +extern int nfs_our_port; +extern int nfs_timeout_count; +extern unsigned long rpc_id; +extern int nfs_offset; +extern int nfs_len; + +extern const ulong nfs_timeout; + +extern int nfs_state; +#define STATE_PRCLOOKUP_PROG_MOUNT_REQ 1 +#define STATE_PRCLOOKUP_PROG_NFS_REQ 2 +#define STATE_MOUNT_REQ 3 +#define STATE_UMOUNT_REQ 4 +#define STATE_LOOKUP_REQ 5 +#define STATE_READ_REQ 6 +#define STATE_READLINK_REQ 7 + +/* + * Block size used for NFS read accesses. A RPC reply packet (including all + * headers) must fit within a single Ethernet frame to avoid fragmentation. + * However, if CONFIG_IP_DEFRAG is set, a bigger value could be used. In any + * case, most NFS servers are optimized for a power of 2. + */ +#define NFS_READ_SIZE 1024 /* biggest power of two that fits Ether frame */ +#define NFS_MAX_ATTRS 26 + +struct rpc_t { + union { + u8 data[NFS_READ_SIZE + (6 + NFS_MAX_ATTRS) * + sizeof(uint32_t)]; + struct { + u32 id; + u32 type; + u32 rpcvers; + u32 prog; + u32 vers; + u32 proc; + u32 data[1]; + } call; + struct { + u32 id; + u32 type; + u32 rstatus; + u32 verifier; + u32 v2; + u32 astatus; + u32 data[NFS_READ_SIZE / sizeof(u32) + + NFS_MAX_ATTRS]; + } reply; + } u; +}; + +char *nfs_basename(char *path); +char *nfs_dirname(char *path); +void rpc_req_common(int rpc_prog, int rpc_proc, uint32_t *data, int datalen, + uchar *txbuff, int *pktlen, int *sport); +void nfs_send(void); +void nfs_pkt_recv(uchar *pkt, unsigned int len); + +extern enum nfs_version choosen_nfs_version; + +/* + * Implementation specific functions called from common code + */ +void rpc_req(int rpc_prog, int rpc_proc, uint32_t *data, int datalen); +void nfs_refresh_timeout(void); + +/**********************************************************************/ + +#endif /* __NFS_COMMON_H__ */ diff --git a/net/nfs.c b/net/nfs.c index 7bae7c78642..f5edc9b043a 100644 --- a/net/nfs.c +++ b/net/nfs.c @@ -31,745 +31,31 @@ * September 27, 2018. As of now, NFSv3 is the default choice. If the server * does not support NFSv3, we fall back to versions 2 or 1. */ -#include #include -#ifdef CONFIG_SYS_DIRECT_FLASH_NFS -#include -#endif #include #include #include #include -#include #include "nfs.h" -#include "bootp.h" +#include "nfs-common.h" #include -#define HASHES_PER_LINE 65 /* Number of "loading" hashes per line */ -#define NFS_RETRY_COUNT 30 - -#define NFS_RPC_ERR 1 -#define NFS_RPC_DROP 124 - -static int fs_mounted; -static unsigned long rpc_id; -static int nfs_offset = -1; -static int nfs_len; -static const ulong nfs_timeout = CONFIG_NFS_TIMEOUT; - -static char dirfh[NFS3_FHSIZE]; /* NFSv2 / NFSv3 file handle of directory */ -static unsigned int dirfh3_length; /* (variable) length of dirfh when NFSv3 */ -static char filefh[NFS3_FHSIZE]; /* NFSv2 / NFSv3 file handle */ -static unsigned int filefh3_length; /* (variable) length of filefh when NFSv3 */ - -static enum net_loop_state nfs_download_state; -static struct in_addr nfs_server_ip; -static int nfs_server_mount_port; -static int nfs_server_port; -static int nfs_our_port; -static int nfs_timeout_count; -static int nfs_state; -#define STATE_PRCLOOKUP_PROG_MOUNT_REQ 1 -#define STATE_PRCLOOKUP_PROG_NFS_REQ 2 -#define STATE_MOUNT_REQ 3 -#define STATE_UMOUNT_REQ 4 -#define STATE_LOOKUP_REQ 5 -#define STATE_READ_REQ 6 -#define STATE_READLINK_REQ 7 - -static char *nfs_filename; -static char *nfs_path; -static char nfs_path_buff[2048]; - -enum nfs_version { - NFS_UNKOWN = 0, - NFS_V1 = 1, - NFS_V2 = 2, - NFS_V3 = 3, -}; - -static enum nfs_version choosen_nfs_version = NFS_V3; -static inline int store_block(uchar *src, unsigned offset, unsigned len) -{ - ulong newsize = offset + len; -#ifdef CONFIG_SYS_DIRECT_FLASH_NFS - int i, rc = 0; - - for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) { - /* start address in flash? */ - if (image_load_addr + offset >= flash_info[i].start[0]) { - rc = 1; - break; - } - } - - if (rc) { /* Flash is destination for this packet */ - rc = flash_write((uchar *)src, (ulong)image_load_addr + offset, - len); - if (rc) { - flash_perror(rc); - return -1; - } - } else -#endif /* CONFIG_SYS_DIRECT_FLASH_NFS */ - { - void *ptr = map_sysmem(image_load_addr + offset, len); - - memcpy(ptr, src, len); - unmap_sysmem(ptr); - } - - if (net_boot_file_size < (offset + len)) - net_boot_file_size = newsize; - return 0; -} - -static char *basename(char *path) -{ - char *fname; - - fname = path + strlen(path) - 1; - while (fname >= path) { - if (*fname == '/') { - fname++; - break; - } - fname--; - } - return fname; -} - -static char *dirname(char *path) -{ - char *fname; - - fname = basename(path); - --fname; - *fname = '\0'; - return path; -} - -/************************************************************************** -RPC_ADD_CREDENTIALS - Add RPC authentication/verifier entries -**************************************************************************/ -static uint32_t *rpc_add_credentials(uint32_t *p) -{ - /* Here's the executive summary on authentication requirements of the - * various NFS server implementations: Linux accepts both AUTH_NONE - * and AUTH_UNIX authentication (also accepts an empty hostname field - * in the AUTH_UNIX scheme). *BSD refuses AUTH_NONE, but accepts - * AUTH_UNIX (also accepts an empty hostname field in the AUTH_UNIX - * scheme). To be safe, use AUTH_UNIX and pass the hostname if we have - * it (if the BOOTP/DHCP reply didn't give one, just use an empty - * hostname). */ - - /* Provide an AUTH_UNIX credential. */ - *p++ = htonl(1); /* AUTH_UNIX */ - *p++ = htonl(20); /* auth length */ - *p++ = 0; /* stamp */ - *p++ = 0; /* hostname string */ - *p++ = 0; /* uid */ - *p++ = 0; /* gid */ - *p++ = 0; /* auxiliary gid list */ - - /* Provide an AUTH_NONE verifier. */ - *p++ = 0; /* AUTH_NONE */ - *p++ = 0; /* auth length */ - - return p; -} - /************************************************************************** RPC_LOOKUP - Lookup RPC Port numbers **************************************************************************/ -static void rpc_req(int rpc_prog, int rpc_proc, uint32_t *data, int datalen) +void rpc_req(int rpc_prog, int rpc_proc, uint32_t *data, int datalen) { - struct rpc_t rpc_pkt; - unsigned long id; - uint32_t *p; int pktlen; int sport; - id = ++rpc_id; - rpc_pkt.u.call.id = htonl(id); - rpc_pkt.u.call.type = htonl(MSG_CALL); - rpc_pkt.u.call.rpcvers = htonl(2); /* use RPC version 2 */ - rpc_pkt.u.call.prog = htonl(rpc_prog); - switch (rpc_prog) { - case PROG_NFS: - switch (choosen_nfs_version) { - case NFS_V1: - case NFS_V2: - rpc_pkt.u.call.vers = htonl(2); - break; - - case NFS_V3: - rpc_pkt.u.call.vers = htonl(3); - break; - - case NFS_UNKOWN: - /* nothing to do */ - break; - } - break; - case PROG_MOUNT: - switch (choosen_nfs_version) { - case NFS_V1: - rpc_pkt.u.call.vers = htonl(1); - break; - - case NFS_V2: - rpc_pkt.u.call.vers = htonl(2); - break; - - case NFS_V3: - rpc_pkt.u.call.vers = htonl(3); - break; - - case NFS_UNKOWN: - /* nothing to do */ - break; - } - break; - case PROG_PORTMAP: - default: - rpc_pkt.u.call.vers = htonl(2); /* portmapper is version 2 */ - } - rpc_pkt.u.call.proc = htonl(rpc_proc); - p = rpc_pkt.u.call.data; - - if (datalen) - memcpy(p, data, datalen * sizeof(uint32_t)); - - pktlen = (char *)p + datalen * sizeof(uint32_t) - (char *)&rpc_pkt; - - memcpy((char *)net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE, - &rpc_pkt.u.data[0], pktlen); - - if (rpc_prog == PROG_PORTMAP) - sport = SUNRPC_PORT; - else if (rpc_prog == PROG_MOUNT) - sport = nfs_server_mount_port; - else - sport = nfs_server_port; + rpc_req_common(rpc_prog, rpc_proc, data, datalen, + (char *)net_tx_packet + net_eth_hdr_size() + + IP_UDP_HDR_SIZE, &pktlen, &sport); net_send_udp_packet(net_server_ethaddr, nfs_server_ip, sport, nfs_our_port, pktlen); } -/************************************************************************** -RPC_LOOKUP - Lookup RPC Port numbers -**************************************************************************/ -static void rpc_lookup_req(int prog, int ver) -{ - uint32_t data[16]; - - data[0] = 0; data[1] = 0; /* auth credential */ - data[2] = 0; data[3] = 0; /* auth verifier */ - data[4] = htonl(prog); - data[5] = htonl(ver); - data[6] = htonl(17); /* IP_UDP */ - data[7] = 0; - rpc_req(PROG_PORTMAP, PORTMAP_GETPORT, data, 8); -} - -/************************************************************************** -NFS_MOUNT - Mount an NFS Filesystem -**************************************************************************/ -static void nfs_mount_req(char *path) -{ - uint32_t data[1024]; - uint32_t *p; - int len; - int pathlen; - - pathlen = strlen(path); - - p = &(data[0]); - p = rpc_add_credentials(p); - - *p++ = htonl(pathlen); - if (pathlen & 3) - *(p + pathlen / 4) = 0; - memcpy(p, path, pathlen); - p += (pathlen + 3) / 4; - - len = (uint32_t *)p - (uint32_t *)&(data[0]); - - rpc_req(PROG_MOUNT, MOUNT_ADDENTRY, data, len); -} - -/************************************************************************** -NFS_UMOUNTALL - Unmount all our NFS Filesystems on the Server -**************************************************************************/ -static void nfs_umountall_req(void) -{ - uint32_t data[1024]; - uint32_t *p; - int len; - - if ((nfs_server_mount_port == -1) || (!fs_mounted)) - /* Nothing mounted, nothing to umount */ - return; - - p = &(data[0]); - p = rpc_add_credentials(p); - - len = (uint32_t *)p - (uint32_t *)&(data[0]); - - rpc_req(PROG_MOUNT, MOUNT_UMOUNTALL, data, len); -} - -/*************************************************************************** - * NFS_READLINK (AH 2003-07-14) - * This procedure is called when read of the first block fails - - * this probably happens when it's a directory or a symlink - * In case of successful readlink(), the dirname is manipulated, - * so that inside the nfs() function a recursion can be done. - **************************************************************************/ -static void nfs_readlink_req(void) -{ - uint32_t data[1024]; - uint32_t *p; - int len; - - p = &(data[0]); - p = rpc_add_credentials(p); - - if (choosen_nfs_version != NFS_V3) { - memcpy(p, filefh, NFS_FHSIZE); - p += (NFS_FHSIZE / 4); - } else { /* NFS_V3 */ - *p++ = htonl(filefh3_length); - memcpy(p, filefh, filefh3_length); - p += (filefh3_length / 4); - } - - len = (uint32_t *)p - (uint32_t *)&(data[0]); - - rpc_req(PROG_NFS, NFS_READLINK, data, len); -} - -/************************************************************************** -NFS_LOOKUP - Lookup Pathname -**************************************************************************/ -static void nfs_lookup_req(char *fname) -{ - uint32_t data[1024]; - uint32_t *p; - int len; - int fnamelen; - - fnamelen = strlen(fname); - - p = &(data[0]); - p = rpc_add_credentials(p); - - if (choosen_nfs_version != NFS_V3) { - memcpy(p, dirfh, NFS_FHSIZE); - p += (NFS_FHSIZE / 4); - *p++ = htonl(fnamelen); - if (fnamelen & 3) - *(p + fnamelen / 4) = 0; - memcpy(p, fname, fnamelen); - p += (fnamelen + 3) / 4; - - len = (uint32_t *)p - (uint32_t *)&(data[0]); - - rpc_req(PROG_NFS, NFS_LOOKUP, data, len); - } else { /* NFS_V3 */ - *p++ = htonl(dirfh3_length); /* Dir handle length */ - memcpy(p, dirfh, dirfh3_length); - p += (dirfh3_length / 4); - *p++ = htonl(fnamelen); - if (fnamelen & 3) - *(p + fnamelen / 4) = 0; - memcpy(p, fname, fnamelen); - p += (fnamelen + 3) / 4; - - len = (uint32_t *)p - (uint32_t *)&(data[0]); - - rpc_req(PROG_NFS, NFS3PROC_LOOKUP, data, len); - } -} - -/************************************************************************** -NFS_READ - Read File on NFS Server -**************************************************************************/ -static void nfs_read_req(int offset, int readlen) -{ - uint32_t data[1024]; - uint32_t *p; - int len; - - p = &(data[0]); - p = rpc_add_credentials(p); - - if (choosen_nfs_version != NFS_V3) { - memcpy(p, filefh, NFS_FHSIZE); - p += (NFS_FHSIZE / 4); - *p++ = htonl(offset); - *p++ = htonl(readlen); - *p++ = 0; - } else { /* NFS_V3 */ - *p++ = htonl(filefh3_length); - memcpy(p, filefh, filefh3_length); - p += (filefh3_length / 4); - *p++ = htonl(0); /* offset is 64-bit long, so fill with 0 */ - *p++ = htonl(offset); - *p++ = htonl(readlen); - *p++ = 0; - } - - len = (uint32_t *)p - (uint32_t *)&(data[0]); - - rpc_req(PROG_NFS, NFS_READ, data, len); -} - -/************************************************************************** -RPC request dispatcher -**************************************************************************/ -static void nfs_send(void) -{ - debug("%s\n", __func__); - - switch (nfs_state) { - case STATE_PRCLOOKUP_PROG_MOUNT_REQ: - if (choosen_nfs_version != NFS_V3) - rpc_lookup_req(PROG_MOUNT, 1); - else /* NFS_V3 */ - rpc_lookup_req(PROG_MOUNT, 3); - break; - case STATE_PRCLOOKUP_PROG_NFS_REQ: - if (choosen_nfs_version != NFS_V3) - rpc_lookup_req(PROG_NFS, 2); - else /* NFS_V3 */ - rpc_lookup_req(PROG_NFS, 3); - break; - case STATE_MOUNT_REQ: - nfs_mount_req(nfs_path); - break; - case STATE_UMOUNT_REQ: - nfs_umountall_req(); - break; - case STATE_LOOKUP_REQ: - nfs_lookup_req(nfs_filename); - break; - case STATE_READ_REQ: - nfs_read_req(nfs_offset, nfs_len); - break; - case STATE_READLINK_REQ: - nfs_readlink_req(); - break; - } -} - -/************************************************************************** -Handlers for the reply from server -**************************************************************************/ - -static int rpc_handle_error(struct rpc_t *rpc_pkt) -{ - if (rpc_pkt->u.reply.rstatus || - rpc_pkt->u.reply.verifier || - rpc_pkt->u.reply.astatus || - rpc_pkt->u.reply.data[0]) { - switch (ntohl(rpc_pkt->u.reply.astatus)) { - case NFS_RPC_SUCCESS: /* Not an error */ - break; - case NFS_RPC_PROG_MISMATCH: { - /* Remote can't support NFS version */ - const int min = ntohl(rpc_pkt->u.reply.data[0]); - const int max = ntohl(rpc_pkt->u.reply.data[1]); - - if (max < NFS_V1 || max > NFS_V3 || min > NFS_V3) { - puts("*** ERROR: NFS version not supported"); - debug(": Requested: V%d, accepted: min V%d - max V%d\n", - choosen_nfs_version, - ntohl(rpc_pkt->u.reply.data[0]), - ntohl(rpc_pkt->u.reply.data[1])); - puts("\n"); - choosen_nfs_version = NFS_UNKOWN; - break; - } - - debug("*** Warning: NFS version not supported: Requested: V%d, accepted: min V%d - max V%d\n", - choosen_nfs_version, - ntohl(rpc_pkt->u.reply.data[0]), - ntohl(rpc_pkt->u.reply.data[1])); - debug("Will retry with NFSv%d\n", min); - choosen_nfs_version = min; - return -NFS_RPC_PROG_MISMATCH; - } - case NFS_RPC_PROG_UNAVAIL: - case NFS_RPC_PROC_UNAVAIL: - case NFS_RPC_GARBAGE_ARGS: - case NFS_RPC_SYSTEM_ERR: - default: /* Unknown error on 'accept state' flag */ - debug("*** ERROR: accept state error (%d)\n", - ntohl(rpc_pkt->u.reply.astatus)); - break; - } - return -1; - } - - return 0; -} - -static int rpc_lookup_reply(int prog, uchar *pkt, unsigned len) -{ - struct rpc_t rpc_pkt; - - memcpy(&rpc_pkt.u.data[0], pkt, len); - - debug("%s\n", __func__); - - if (ntohl(rpc_pkt.u.reply.id) > rpc_id) - return -NFS_RPC_ERR; - else if (ntohl(rpc_pkt.u.reply.id) < rpc_id) - return -NFS_RPC_DROP; - - if (rpc_pkt.u.reply.rstatus || - rpc_pkt.u.reply.verifier || - rpc_pkt.u.reply.astatus) - return -1; - - switch (prog) { - case PROG_MOUNT: - nfs_server_mount_port = ntohl(rpc_pkt.u.reply.data[0]); - break; - case PROG_NFS: - nfs_server_port = ntohl(rpc_pkt.u.reply.data[0]); - break; - } - - return 0; -} - -static int nfs_mount_reply(uchar *pkt, unsigned len) -{ - struct rpc_t rpc_pkt; - int ret; - - debug("%s\n", __func__); - - memcpy(&rpc_pkt.u.data[0], pkt, len); - - if (ntohl(rpc_pkt.u.reply.id) > rpc_id) - return -NFS_RPC_ERR; - else if (ntohl(rpc_pkt.u.reply.id) < rpc_id) - return -NFS_RPC_DROP; - - ret = rpc_handle_error(&rpc_pkt); - if (ret) - return ret; - - fs_mounted = 1; - /* NFSv2 and NFSv3 use same structure */ - if (choosen_nfs_version != NFS_V3) { - memcpy(dirfh, rpc_pkt.u.reply.data + 1, NFS_FHSIZE); - } else { - dirfh3_length = ntohl(rpc_pkt.u.reply.data[1]); - if (dirfh3_length > NFS3_FHSIZE) - dirfh3_length = NFS3_FHSIZE; - memcpy(dirfh, rpc_pkt.u.reply.data + 2, dirfh3_length); - } - - return 0; -} - -static int nfs_umountall_reply(uchar *pkt, unsigned len) -{ - struct rpc_t rpc_pkt; - - debug("%s\n", __func__); - - memcpy(&rpc_pkt.u.data[0], pkt, len); - - if (ntohl(rpc_pkt.u.reply.id) > rpc_id) - return -NFS_RPC_ERR; - else if (ntohl(rpc_pkt.u.reply.id) < rpc_id) - return -NFS_RPC_DROP; - - if (rpc_pkt.u.reply.rstatus || - rpc_pkt.u.reply.verifier || - rpc_pkt.u.reply.astatus) - return -1; - - fs_mounted = 0; - memset(dirfh, 0, sizeof(dirfh)); - - return 0; -} - -static int nfs_lookup_reply(uchar *pkt, unsigned len) -{ - struct rpc_t rpc_pkt; - int ret; - - debug("%s\n", __func__); - - memcpy(&rpc_pkt.u.data[0], pkt, len); - - if (ntohl(rpc_pkt.u.reply.id) > rpc_id) - return -NFS_RPC_ERR; - else if (ntohl(rpc_pkt.u.reply.id) < rpc_id) - return -NFS_RPC_DROP; - - ret = rpc_handle_error(&rpc_pkt); - if (ret) - return ret; - - if (choosen_nfs_version != NFS_V3) { - if (((uchar *)&(rpc_pkt.u.reply.data[0]) - (uchar *)(&rpc_pkt) + NFS_FHSIZE) > len) - return -NFS_RPC_DROP; - memcpy(filefh, rpc_pkt.u.reply.data + 1, NFS_FHSIZE); - } else { /* NFS_V3 */ - filefh3_length = ntohl(rpc_pkt.u.reply.data[1]); - if (filefh3_length > NFS3_FHSIZE) - filefh3_length = NFS3_FHSIZE; - memcpy(filefh, rpc_pkt.u.reply.data + 2, filefh3_length); - } - - return 0; -} - -static int nfs3_get_attributes_offset(uint32_t *data) -{ - if (data[1]) { - /* 'attributes_follow' flag is TRUE, - * so we have attributes on 21 dwords */ - /* Skip unused values : - type; 32 bits value, - mode; 32 bits value, - nlink; 32 bits value, - uid; 32 bits value, - gid; 32 bits value, - size; 64 bits value, - used; 64 bits value, - rdev; 64 bits value, - fsid; 64 bits value, - fileid; 64 bits value, - atime; 64 bits value, - mtime; 64 bits value, - ctime; 64 bits value, - */ - return 22; - } else { - /* 'attributes_follow' flag is FALSE, - * so we don't have any attributes */ - return 1; - } -} - -static int nfs_readlink_reply(uchar *pkt, unsigned len) -{ - struct rpc_t rpc_pkt; - int rlen; - int nfsv3_data_offset = 0; - - debug("%s\n", __func__); - - memcpy((unsigned char *)&rpc_pkt, pkt, len); - - if (ntohl(rpc_pkt.u.reply.id) > rpc_id) - return -NFS_RPC_ERR; - else if (ntohl(rpc_pkt.u.reply.id) < rpc_id) - return -NFS_RPC_DROP; - - if (rpc_pkt.u.reply.rstatus || - rpc_pkt.u.reply.verifier || - rpc_pkt.u.reply.astatus || - rpc_pkt.u.reply.data[0]) - return -1; - - if (choosen_nfs_version == NFS_V3) { - nfsv3_data_offset = - nfs3_get_attributes_offset(rpc_pkt.u.reply.data); - } - - /* new path length */ - rlen = ntohl(rpc_pkt.u.reply.data[1 + nfsv3_data_offset]); - - if (((uchar *)&(rpc_pkt.u.reply.data[0]) - (uchar *)(&rpc_pkt) + rlen) > len) - return -NFS_RPC_DROP; - - if (*((char *)&(rpc_pkt.u.reply.data[2 + nfsv3_data_offset])) != '/') { - int pathlen; - - strcat(nfs_path, "/"); - pathlen = strlen(nfs_path); - memcpy(nfs_path + pathlen, - (uchar *)&(rpc_pkt.u.reply.data[2 + nfsv3_data_offset]), - rlen); - nfs_path[pathlen + rlen] = 0; - } else { - memcpy(nfs_path, - (uchar *)&(rpc_pkt.u.reply.data[2 + nfsv3_data_offset]), - rlen); - nfs_path[rlen] = 0; - } - return 0; -} - -static int nfs_read_reply(uchar *pkt, unsigned len) -{ - struct rpc_t rpc_pkt; - int rlen; - uchar *data_ptr; - - debug("%s\n", __func__); - - memcpy(&rpc_pkt.u.data[0], pkt, sizeof(rpc_pkt.u.reply)); - - if (ntohl(rpc_pkt.u.reply.id) > rpc_id) - return -NFS_RPC_ERR; - else if (ntohl(rpc_pkt.u.reply.id) < rpc_id) - return -NFS_RPC_DROP; - - if (rpc_pkt.u.reply.rstatus || - rpc_pkt.u.reply.verifier || - rpc_pkt.u.reply.astatus || - rpc_pkt.u.reply.data[0]) { - if (rpc_pkt.u.reply.rstatus) - return -9999; - if (rpc_pkt.u.reply.astatus) - return -9999; - return -ntohl(rpc_pkt.u.reply.data[0]); - } - - if ((nfs_offset != 0) && !((nfs_offset) % - (NFS_READ_SIZE / 2 * 10 * HASHES_PER_LINE))) - puts("\n\t "); - if (!(nfs_offset % ((NFS_READ_SIZE / 2) * 10))) - putc('#'); - - if (choosen_nfs_version != NFS_V3) { - rlen = ntohl(rpc_pkt.u.reply.data[18]); - data_ptr = (uchar *)&(rpc_pkt.u.reply.data[19]); - } else { /* NFS_V3 */ - int nfsv3_data_offset = - nfs3_get_attributes_offset(rpc_pkt.u.reply.data); - - /* count value */ - rlen = ntohl(rpc_pkt.u.reply.data[1 + nfsv3_data_offset]); - /* Skip unused values : - EOF: 32 bits value, - data_size: 32 bits value, - */ - data_ptr = (uchar *) - &(rpc_pkt.u.reply.data[4 + nfsv3_data_offset]); - } - - if (((uchar *)&(rpc_pkt.u.reply.data[0]) - (uchar *)(&rpc_pkt) + rlen) > len) - return -9999; - - if (store_block(data_ptr, nfs_offset, rlen)) - return -9999; - - return rlen; -} - /************************************************************************** Interfaces of U-BOOT **************************************************************************/ @@ -787,12 +73,14 @@ static void nfs_timeout_handler(void) } } +void nfs_refresh_timeout(void) +{ + net_set_timeout_handler(nfs_timeout, nfs_timeout_handler); +} + static void nfs_handler(uchar *pkt, unsigned dest, struct in_addr sip, unsigned src, unsigned len) { - int rlen; - int reply; - debug("%s\n", __func__); if (len > sizeof(struct rpc_t)) @@ -801,117 +89,7 @@ static void nfs_handler(uchar *pkt, unsigned dest, struct in_addr sip, if (dest != nfs_our_port) return; - switch (nfs_state) { - case STATE_PRCLOOKUP_PROG_MOUNT_REQ: - if (rpc_lookup_reply(PROG_MOUNT, pkt, len) == -NFS_RPC_DROP) - break; - nfs_state = STATE_PRCLOOKUP_PROG_NFS_REQ; - nfs_send(); - break; - - case STATE_PRCLOOKUP_PROG_NFS_REQ: - if (rpc_lookup_reply(PROG_NFS, pkt, len) == -NFS_RPC_DROP) - break; - nfs_state = STATE_MOUNT_REQ; - nfs_send(); - break; - - case STATE_MOUNT_REQ: - reply = nfs_mount_reply(pkt, len); - if (reply == -NFS_RPC_DROP) { - break; - } else if (reply == -NFS_RPC_ERR) { - puts("*** ERROR: Cannot mount\n"); - /* just to be sure... */ - nfs_state = STATE_UMOUNT_REQ; - nfs_send(); - } else if (reply == -NFS_RPC_PROG_MISMATCH && - choosen_nfs_version != NFS_UNKOWN) { - nfs_state = STATE_MOUNT_REQ; - nfs_send(); - } else { - nfs_state = STATE_LOOKUP_REQ; - nfs_send(); - } - break; - - case STATE_UMOUNT_REQ: - reply = nfs_umountall_reply(pkt, len); - if (reply == -NFS_RPC_DROP) { - break; - } else if (reply == -NFS_RPC_ERR) { - debug("*** ERROR: Cannot umount\n"); - net_set_state(NETLOOP_FAIL); - } else { - puts("\ndone\n"); - net_set_state(nfs_download_state); - } - break; - - case STATE_LOOKUP_REQ: - reply = nfs_lookup_reply(pkt, len); - if (reply == -NFS_RPC_DROP) { - break; - } else if (reply == -NFS_RPC_ERR) { - puts("*** ERROR: File lookup fail\n"); - nfs_state = STATE_UMOUNT_REQ; - nfs_send(); - } else if (reply == -NFS_RPC_PROG_MISMATCH && - choosen_nfs_version != NFS_UNKOWN) { - /* umount */ - nfs_state = STATE_UMOUNT_REQ; - nfs_send(); - /* And retry with another supported version */ - nfs_state = STATE_PRCLOOKUP_PROG_MOUNT_REQ; - nfs_send(); - } else { - nfs_state = STATE_READ_REQ; - nfs_offset = 0; - nfs_len = NFS_READ_SIZE; - nfs_send(); - } - break; - - case STATE_READLINK_REQ: - reply = nfs_readlink_reply(pkt, len); - if (reply == -NFS_RPC_DROP) { - break; - } else if (reply == -NFS_RPC_ERR) { - puts("*** ERROR: Symlink fail\n"); - nfs_state = STATE_UMOUNT_REQ; - nfs_send(); - } else { - debug("Symlink --> %s\n", nfs_path); - nfs_filename = basename(nfs_path); - nfs_path = dirname(nfs_path); - - nfs_state = STATE_MOUNT_REQ; - nfs_send(); - } - break; - - case STATE_READ_REQ: - rlen = nfs_read_reply(pkt, len); - if (rlen == -NFS_RPC_DROP) - break; - net_set_timeout_handler(nfs_timeout, nfs_timeout_handler); - if (rlen > 0) { - nfs_offset += rlen; - nfs_send(); - } else if ((rlen == -NFSERR_ISDIR) || (rlen == -NFSERR_INVAL)) { - /* symbolic link */ - nfs_state = STATE_READLINK_REQ; - nfs_send(); - } else { - if (!rlen) - nfs_download_state = NETLOOP_SUCCESS; - if (rlen < 0) - debug("NFS READ error (%d)\n", rlen); - nfs_state = STATE_UMOUNT_REQ; - nfs_send(); - } - break; - } + nfs_pkt_recv(pkt, len); } void nfs_start(void) @@ -940,8 +118,8 @@ void nfs_start(void) nfs_path); } - nfs_filename = basename(nfs_path); - nfs_path = dirname(nfs_path); + nfs_filename = nfs_basename(nfs_path); + nfs_path = nfs_dirname(nfs_path); printf("Using %s device\n", eth_get_name()); diff --git a/net/nfs.h b/net/nfs.h index 6bf1cb76bd5..0dd180ce222 100644 --- a/net/nfs.h +++ b/net/nfs.h @@ -6,15 +6,6 @@ #ifndef __NFS_H__ #define __NFS_H__ -#define SUNRPC_PORT 111 - -#define PROG_PORTMAP 100000 -#define PROG_NFS 100003 -#define PROG_MOUNT 100005 - -#define MSG_CALL 0 -#define MSG_REPLY 1 - #define PORTMAP_GETPORT 3 #define MOUNT_ADDENTRY 1 @@ -29,56 +20,6 @@ #define NFS_FHSIZE 32 #define NFS3_FHSIZE 64 -#define NFSERR_PERM 1 -#define NFSERR_NOENT 2 -#define NFSERR_ACCES 13 -#define NFSERR_ISDIR 21 -#define NFSERR_INVAL 22 - -/* - * Block size used for NFS read accesses. A RPC reply packet (including all - * headers) must fit within a single Ethernet frame to avoid fragmentation. - * However, if CONFIG_IP_DEFRAG is set, a bigger value could be used. In any - * case, most NFS servers are optimized for a power of 2. - */ -#define NFS_READ_SIZE 1024 /* biggest power of two that fits Ether frame */ -#define NFS_MAX_ATTRS 26 - -/* Values for Accept State flag on RPC answers (See: rfc1831) */ -enum rpc_accept_stat { - NFS_RPC_SUCCESS = 0, /* RPC executed successfully */ - NFS_RPC_PROG_UNAVAIL = 1, /* remote hasn't exported program */ - NFS_RPC_PROG_MISMATCH = 2, /* remote can't support version # */ - NFS_RPC_PROC_UNAVAIL = 3, /* program can't support procedure */ - NFS_RPC_GARBAGE_ARGS = 4, /* procedure can't decode params */ - NFS_RPC_SYSTEM_ERR = 5 /* errors like memory allocation failure */ -}; - -struct rpc_t { - union { - uint8_t data[NFS_READ_SIZE + (6 + NFS_MAX_ATTRS) * - sizeof(uint32_t)]; - struct { - uint32_t id; - uint32_t type; - uint32_t rpcvers; - uint32_t prog; - uint32_t vers; - uint32_t proc; - uint32_t data[1]; - } call; - struct { - uint32_t id; - uint32_t type; - uint32_t rstatus; - uint32_t verifier; - uint32_t v2; - uint32_t astatus; - uint32_t data[NFS_READ_SIZE / sizeof(uint32_t) + - NFS_MAX_ATTRS]; - } reply; - } u; -}; void nfs_start(void); /* Begin NFS */ /**********************************************************************/ -- cgit v1.3.1 From 230cf3bc277622081b27e33469f5f1f59fc48180 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Fri, 12 Dec 2025 11:32:28 +0000 Subject: net: lwip: nfs: Port the NFS code to work with lwIP After the preparatory patches moved most of the NFS code into common files we now add the code to enable NFS support with lwIP. Signed-off-by: Andrew Goodbody Acked-by: Jerome Forissier --- cmd/Kconfig | 28 +++--- cmd/lwip/Makefile | 1 + cmd/lwip/nfs.c | 11 +++ include/net-lwip.h | 1 + net/lwip/Makefile | 1 + net/lwip/nfs.c | 286 +++++++++++++++++++++++++++++++++++++++++++++++++++++ net/nfs-common.c | 4 +- 7 files changed, 317 insertions(+), 15 deletions(-) create mode 100644 cmd/lwip/nfs.c create mode 100644 net/lwip/nfs.c (limited to 'net') diff --git a/cmd/Kconfig b/cmd/Kconfig index b71ac554c0b..595ac49da41 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -2116,20 +2116,6 @@ config CMD_RARP help Boot image via network using RARP/TFTP protocol -config CMD_NFS - bool "nfs" - help - Boot image via network using NFS protocol. - -config NFS_TIMEOUT - int "Timeout in milliseconds for NFS mounts" - depends on CMD_NFS - default 2000 - help - Timeout in milliseconds used in NFS protocol. If you encounter - "ERROR: Cannot umount" in nfs command, try longer timeout such as - 10000. - config SYS_DISABLE_AUTOLOAD bool "Disable automatically loading files over the network" depends on CMD_BOOTP || CMD_DHCP || CMD_NFS || CMD_RARP @@ -2224,6 +2210,20 @@ config CMD_MDIO The MDIO interface is orthogonal to the MII interface and extends it by adding access to more registers through indirect addressing. +config CMD_NFS + bool "nfs" + help + Boot image via network using NFS protocol. + +config NFS_TIMEOUT + int "Timeout in milliseconds for NFS mounts" + depends on CMD_NFS + default 2000 + help + Timeout in milliseconds used in NFS protocol. If you encounter + "ERROR: Cannot umount" in nfs command, try longer timeout such as + 10000. + config CMD_PING bool "ping" select PROT_RAW_LWIP if NET_LWIP diff --git a/cmd/lwip/Makefile b/cmd/lwip/Makefile index a7f8976af3f..90df1f5511c 100644 --- a/cmd/lwip/Makefile +++ b/cmd/lwip/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_CMD_DHCP) += dhcp.o obj-$(CONFIG_CMD_DNS) += dns.o +obj-$(CONFIG_CMD_NFS) += nfs.o obj-$(CONFIG_CMD_PING) += ping.o obj-$(CONFIG_CMD_SNTP) += sntp.o obj-$(CONFIG_CMD_TFTPBOOT) += tftp.o diff --git a/cmd/lwip/nfs.c b/cmd/lwip/nfs.c new file mode 100644 index 00000000000..f22db582fdb --- /dev/null +++ b/cmd/lwip/nfs.c @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright (C) 2025 Linaro Ltd. */ + +#include +#include + +U_BOOT_CMD(nfs, 3, 1, do_nfs, + "boot image via network using NFS protocol", + "[loadAddress] [[hostIPaddr:]bootfilename]" + ); + diff --git a/include/net-lwip.h b/include/net-lwip.h index c910def5719..20cb0992cce 100644 --- a/include/net-lwip.h +++ b/include/net-lwip.h @@ -51,6 +51,7 @@ int net_lwip_dns_resolve(char *name_or_ip, ip_addr_t *ip); bool wget_validate_uri(char *uri); int do_dns(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]); +int do_nfs(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]); int do_wget(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]); #endif /* __NET_LWIP_H__ */ diff --git a/net/lwip/Makefile b/net/lwip/Makefile index 1b48ae4d508..5291cbb2a1c 100644 --- a/net/lwip/Makefile +++ b/net/lwip/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_$(PHASE_)DM_ETH) += net-lwip.o obj-$(CONFIG_CMD_DHCP) += dhcp.o obj-$(CONFIG_DNS) += dns.o obj-$(CONFIG_LWIP_ICMP_SHOW_UNREACH) += icmp_unreach.o +obj-$(CONFIG_CMD_NFS) += nfs.o obj-$(CONFIG_CMD_TFTPBOOT) += tftp.o obj-$(CONFIG_WGET) += wget.o diff --git a/net/lwip/nfs.c b/net/lwip/nfs.c new file mode 100644 index 00000000000..27579af8f1f --- /dev/null +++ b/net/lwip/nfs.c @@ -0,0 +1,286 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright (C) 2025 Linaro Ltd. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../nfs-common.h" +#include + +static ulong timer_start; + +static struct nfs_ctx { + ip_addr_t nfs_server; + struct udp_pcb *pcb; +} sess_ctx; + +/************************************************************************** + * RPC_LOOKUP - Lookup RPC Port numbers + ************************************************************************** + */ +void rpc_req(int rpc_prog, int rpc_proc, uint32_t *data, int datalen) +{ + struct pbuf *pb; + int pktlen; + int sport; + err_t err; + + pb = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct rpc_t), PBUF_RAM); + if (!pb) { + debug("Failed to allocate pbuf to build RPC packet\n"); + return; + } + + rpc_req_common(rpc_prog, rpc_proc, data, datalen, + pb->payload, &pktlen, &sport); + + pbuf_realloc(pb, (u16_t)pktlen); + + err = udp_sendto(sess_ctx.pcb, pb, &sess_ctx.nfs_server, sport); + debug_cond((err != ERR_OK), "Failed to send UDP packet err = %d\n", err); + pbuf_free(pb); +} + +void nfs_refresh_timeout(void) +{ + timer_start = get_timer(0); +} + +static void nfs_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, + const ip_addr_t *addr, u16_t port) +{ + struct nfs_ctx *ctx = arg; + int plen; + struct rpc_t rpc_pkt; + + if (addr->addr != ctx->nfs_server.addr) + goto exitfree; + + if (p->tot_len > sizeof(struct rpc_t)) + goto exitfree; + + plen = pbuf_copy_partial(p, &rpc_pkt.u.data[0], sizeof(rpc_pkt), 0); + nfs_pkt_recv(&rpc_pkt.u.data[0], plen); + +exitfree: + pbuf_free(p); +} + +static int nfs_udp_init(struct nfs_ctx *ctx) +{ + ctx->pcb = udp_new(); + if (!ctx->pcb) + return -ENOMEM; + + ctx->pcb->local_port = nfs_our_port; + udp_recv(ctx->pcb, nfs_recv, ctx); + + return 0; +} + +static int nfs_timeout_check(void) +{ + if (get_timer(timer_start) < nfs_timeout) + return 0; + if (++nfs_timeout_count < NFS_RETRY_COUNT) { + puts("T "); + timer_start = get_timer(0); + nfs_send(); + return 0; + } + + return 1; +} + +static int nfs_loop(struct udevice *udev, ulong addr, char *fname, + ip_addr_t srvip) +{ + struct netif *netif; + int ret; + + nfs_download_state = NETLOOP_FAIL; + net_set_state(NETLOOP_FAIL); + + if (!fname || addr == 0) + return -1; + + netif = net_lwip_new_netif(udev); + if (!netif) + return -1; + + nfs_filename = nfs_basename(fname); + nfs_path = nfs_dirname(fname); + + printf("Using %s device\n", eth_get_name()); + + printf("File transfer via NFS from server %s; our IP address is %s\n", + ip4addr_ntoa(&srvip), env_get("ipaddr")); + + printf("\nFilename '%s/%s'.", nfs_path, nfs_filename); + + if (net_boot_file_expected_size_in_blocks) { + printf(" Size is 0x%x Bytes = ", + net_boot_file_expected_size_in_blocks << 9); + print_size(net_boot_file_expected_size_in_blocks << 9, ""); + } + printf("\nLoad address: 0x%lx\nLoading: *\b", addr); + image_load_addr = addr; + + nfs_timeout_count = 0; + nfs_state = STATE_PRCLOOKUP_PROG_MOUNT_REQ; + + ret = nfs_udp_init(&sess_ctx); + if (ret < 0) { + net_lwip_remove_netif(netif); + debug("Failed to init network interface, aborting for error = %d\n", ret); + return ret; + } + + net_set_state(NETLOOP_CONTINUE); + + sess_ctx.nfs_server.addr = srvip.addr; + + nfs_send(); + + timer_start = get_timer(0); + do { + net_lwip_rx(udev, netif); + if (net_state != NETLOOP_CONTINUE) + break; + if (ctrlc()) { + printf("\nAbort\n"); + break; + } + + if (nfs_timeout_check()) + break; + } while (true); + debug("%s: Loop exit at %lu\n", __func__, get_timer(0)); + + net_lwip_remove_netif(netif); + + if (net_state == NETLOOP_SUCCESS) { + ret = 0; + if (net_boot_file_size > 0) { + printf("Bytes transferred = %u (%x hex)\n", + net_boot_file_size, net_boot_file_size); + env_set_hex("filesize", net_boot_file_size); + env_set_hex("fileaddr", image_load_addr); + } + + } else { + debug("%s: NFS loop failed\n", __func__); + ret = -1; + } + + return ret; +} + +int do_nfs(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + int ret = CMD_RET_SUCCESS; + char *arg = NULL; + char *words[2] = { }; + char *fname = NULL; + char *server_ip = NULL; + char *end; + ip_addr_t srvip; + ulong laddr; + ulong addr; + int i; + + laddr = env_get_ulong("loadaddr", 16, image_load_addr); + + switch (argc) { + case 1: + fname = env_get("bootfile"); + break; + case 2: + /* + * Only one arg - accept two forms: + * Just load address, or just boot file name. The latter + * form must be written in a format which can not be + * mis-interpreted as a valid number. + */ + addr = hextoul(argv[1], &end); + if (end == (argv[1] + strlen(argv[1]))) { + laddr = addr; + fname = env_get("bootfile"); + } else { + arg = strdup(argv[1]); + } + break; + case 3: + laddr = hextoul(argv[1], NULL); + arg = strdup(argv[2]); + break; + default: + ret = CMD_RET_USAGE; + goto out; + } + + if (!arg) + arg = net_boot_file_name; + + if (*arg) { + /* Parse [ip:]fname */ + i = 0; + while ((*(words + i) = strsep(&arg, ":"))) + i++; + + switch (i) { + case 2: + server_ip = words[0]; + fname = words[1]; + break; + case 1: + fname = words[0]; + break; + default: + break; + } + } + + if (!server_ip) + server_ip = env_get("serverip"); + if (!server_ip) { + log_err("*** ERROR: 'serverip' not set\n"); + ret = CMD_RET_FAILURE; + goto out; + } + + if (!ipaddr_aton(server_ip, &srvip)) { + log_err("error: ipaddr_aton\n"); + ret = CMD_RET_FAILURE; + goto out; + } + + if (!fname) { + log_err("error: no file name\n"); + ret = CMD_RET_FAILURE; + goto out; + } + + if (!laddr) { + log_err("error: no load address\n"); + ret = CMD_RET_FAILURE; + goto out; + } + + if (net_lwip_eth_start() < 0) { + ret = CMD_RET_FAILURE; + goto out; + } + + if (nfs_loop(eth_get_dev(), laddr, fname, srvip) < 0) + ret = CMD_RET_FAILURE; +out: + if (arg != net_boot_file_name) + free(arg); + return ret; +} diff --git a/net/nfs-common.c b/net/nfs-common.c index a6a70677bd2..4fbde67a760 100644 --- a/net/nfs-common.c +++ b/net/nfs-common.c @@ -286,9 +286,11 @@ static void nfs_umountall_req(void) u32 *p; int len; - if ((nfs_server_mount_port == -1) || !fs_mounted) + if ((nfs_server_mount_port == -1) || !fs_mounted) { /* Nothing mounted, nothing to umount */ + net_set_state(NETLOOP_FAIL); return; + } p = &data[0]; p = rpc_add_credentials(p); -- cgit v1.3.1 From c832cd3b49a51e34c3672201881f3da64df45031 Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Thu, 25 Dec 2025 09:37:21 -0600 Subject: net: tftpput: Rework to exclude code from xPL phases Given how the support for CONFIG_CMD_TFTPPUT is woven through the support for the tftp protocol we currently end up including "put" support in xPL phases, if enabled. This in turn can lead to size overflow on those platforms as xPL tends to be constrained. To resolve this, use "CMD_TFTPPUT" in the code to check for both CONFIG_CMD_TFTPPUT being true and not being in an xPL build phase. Signed-off-by: Tom Rini Reviewed-by: Jerome Forissier --- net/tftp.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) (limited to 'net') diff --git a/net/tftp.c b/net/tftp.c index 3b0f4cd2006..78ec44159c1 100644 --- a/net/tftp.c +++ b/net/tftp.c @@ -22,6 +22,15 @@ DECLARE_GLOBAL_DATA_PTR; +/* + * We cannot use the 'tftpput' command in xPL phases. Given how the + * support is integrated in the code, this is how we disable that support + * in xPL. + */ +#if defined(CONFIG_CMD_TFTPPUT) && !defined(CONFIG_XPL_BUILD) +#define CMD_TFTPPUT +#endif + /* Well known TFTP port # */ #define WELL_KNOWN_PORT 69 /* Millisecs to timeout for lost pkt */ @@ -95,7 +104,7 @@ static ushort tftp_windowsize; static ushort tftp_next_ack; /* Last nack block we send */ static ushort tftp_last_nack; -#ifdef CONFIG_CMD_TFTPPUT +#ifdef CMD_TFTPPUT /* 1 if writing, else 0 */ static int tftp_put_active; /* 1 if we have sent the last block */ @@ -183,13 +192,13 @@ static void new_transfer(void) tftp_prev_block = 0; tftp_block_wrap = 0; tftp_block_wrap_offset = 0; -#ifdef CONFIG_CMD_TFTPPUT +#ifdef CMD_TFTPPUT tftp_put_final_block_sent = 0; #endif led_activity_blink(); } -#ifdef CONFIG_CMD_TFTPPUT +#ifdef CMD_TFTPPUT /** * Load the next block from memory to be sent over tftp. * @@ -329,7 +338,7 @@ static void tftp_send(void) case STATE_SEND_WRQ: xp = pkt; s = (ushort *)pkt; -#ifdef CONFIG_CMD_TFTPPUT +#ifdef CMD_TFTPPUT *s++ = htons(tftp_state == STATE_SEND_RRQ ? TFTP_RRQ : TFTP_WRQ); #else @@ -372,7 +381,7 @@ static void tftp_send(void) s[0] = htons(TFTP_ACK); s[1] = htons(tftp_cur_block); pkt = (uchar *)(s + 2); -#ifdef CONFIG_CMD_TFTPPUT +#ifdef CMD_TFTPPUT if (tftp_put_active) { int toload = tftp_block_size; int loaded = load_block(tftp_cur_block, pkt, toload); @@ -437,7 +446,7 @@ static void tftp_send(void) net_set_state(NETLOOP_FAIL); } -#ifdef CONFIG_CMD_TFTPPUT +#ifdef CMD_TFTPPUT static void icmp_handler(unsigned type, unsigned code, unsigned dest, struct in_addr sip, unsigned src, uchar *pkt, unsigned len) @@ -476,7 +485,7 @@ static void tftp_handler(uchar *pkt, unsigned dest, struct in_addr sip, break; case TFTP_ACK: -#ifdef CONFIG_CMD_TFTPPUT +#ifdef CMD_TFTPPUT if (tftp_put_active) { timeout_count = 0; if (tftp_put_final_block_sent) { @@ -578,7 +587,7 @@ static void tftp_handler(uchar *pkt, unsigned dest, struct in_addr sip, tftp_next_ack = tftp_windowsize; -#ifdef CONFIG_CMD_TFTPPUT +#ifdef CMD_TFTPPUT if (tftp_put_active && tftp_state == STATE_OACK) { /* Get ready to send the first block */ tftp_state = STATE_DATA; @@ -854,7 +863,7 @@ void tftp_start(enum proto_t protocol) tftp_block_size_option = TFTP_MTU_BLOCKSIZE6; } else { printf("TFTP %s server %pI4; our IP address is %pI4", -#ifdef CONFIG_CMD_TFTPPUT +#ifdef CMD_TFTPPUT protocol == TFTPPUT ? "to" : "from", #else "from", @@ -888,7 +897,7 @@ void tftp_start(enum proto_t protocol) } putc('\n'); -#ifdef CONFIG_CMD_TFTPPUT +#ifdef CMD_TFTPPUT tftp_put_active = (protocol == TFTPPUT); if (tftp_put_active) { printf("Save address: 0x%lx\n", image_save_addr); @@ -911,7 +920,7 @@ void tftp_start(enum proto_t protocol) net_set_timeout_handler(timeout_ms, tftp_timeout_handler); net_set_udp_handler(tftp_handler); -#ifdef CONFIG_CMD_TFTPPUT +#ifdef CMD_TFTPPUT net_set_icmp_handler(icmp_handler); #endif tftp_remote_port = WELL_KNOWN_PORT; -- cgit v1.3.1