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 ----------------- 2 files changed, 17 insertions(+), 17 deletions(-) (limited to 'include') 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 -- 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 'include') 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 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 'include') 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