summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/Kconfig81
-rw-r--r--net/Makefile20
-rw-r--r--net/eth-uclass.c38
-rw-r--r--net/lwip/Kconfig49
-rw-r--r--net/lwip/Makefile8
-rw-r--r--net/lwip/dhcp.c139
-rw-r--r--net/lwip/dns.c127
-rw-r--r--net/lwip/eth_internal.h35
-rw-r--r--net/lwip/net-lwip.c305
-rw-r--r--net/lwip/ping.c177
-rw-r--r--net/lwip/tftp.c290
-rw-r--r--net/lwip/wget.c357
-rw-r--r--net/net-common.c13
-rw-r--r--net/net.c12
14 files changed, 1581 insertions, 70 deletions
diff --git a/net/Kconfig b/net/Kconfig
index 7cb80b880a9..76ab7d91eeb 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -2,11 +2,6 @@
# Network configuration
#
-menuconfig NET
- bool "Networking support"
- default y
- imply NETDEVICES
-
if NET
config ARP_TIMEOUT
@@ -26,15 +21,6 @@ config PROT_UDP
Enable a generic udp framework that allows defining a custom
handler for udp protocol.
-config BOOTDEV_ETH
- bool "Enable bootdev for ethernet"
- depends on BOOTSTD
- default y
- help
- Provide a bootdev for ethernet so that is it possible to boot
- an operationg system over the network, using the PXE (Preboot
- Execution Environment) protocol.
-
config BOOTP_SEND_HOSTNAME
bool "Send hostname to DNS server"
help
@@ -45,15 +31,6 @@ config BOOTP_SEND_HOSTNAME
of the "hostname" environment variable is passed as
option 12 to the DHCP server.
-config NET_RANDOM_ETHADDR
- bool "Random ethaddr if unset"
- help
- Selecting this will allow the Ethernet interface to function even
- when the ethaddr variable for that interface is unset. In this case,
- a random MAC address in the locally administered address space is
- generated. It will be saved to the appropriate environment variable,
- too.
-
config NETCONSOLE
bool "NetConsole support"
help
@@ -83,16 +60,6 @@ config SYS_FAULT_ECHO_LINK_DOWN
this option is active, then CONFIG_SYS_FAULT_MII_ADDR also needs to
be configured.
-config TFTP_BLOCKSIZE
- int "TFTP block size"
- default 1468
- help
- Default TFTP block size.
- The MTU is typically 1500 for ethernet, so a TFTP block of
- 1468 (MTU minus eth.hdrs) provides a good throughput with
- almost-MTU block sizes.
- You can also activate CONFIG_IP_DEFRAG to set a larger block.
-
config TFTP_PORT
bool "Set TFTP UDP source/destination ports via the environment"
help
@@ -255,11 +222,45 @@ config IPV6
endif # if NET
-config SYS_RX_ETH_BUFFER
- int "Number of receive packet buffers"
- default 4
+source "net/lwip/Kconfig"
+
+if NET || NET_LWIP
+
+config BOOTDEV_ETH
+ bool "Enable bootdev for ethernet"
+ depends on BOOTSTD
+ default y
+ help
+ Provide a bootdev for ethernet so that is it possible to boot
+ an operating system over the network, using the PXE (Preboot
+ Execution Environment) protocol.
+
+config NET_RANDOM_ETHADDR
+ bool "Random ethaddr if unset"
+ help
+ Selecting this will allow the Ethernet interface to function even
+ when the ethaddr variable for that interface is unset. In this case,
+ a random MAC address in the locally administered address space is
+ generated. It will be saved to the appropriate environment variable,
+ too.
+
+config TFTP_BLOCKSIZE
+ int "TFTP block size"
+ default 1468
help
- Defines the number of Ethernet receive buffers. On some Ethernet
- controllers it is recommended to set this value to 8 or even higher,
- since all buffers can be full shortly after enabling the interface on
- high Ethernet traffic.
+ Default TFTP block size.
+ The MTU is typically 1500 for ethernet, so a TFTP block of
+ 1468 (MTU minus eth.hdrs) provides a good throughput with
+ almost-MTU block sizes.
+ You can also activate CONFIG_IP_DEFRAG to set a larger block.
+
+endif # if NET || NET_LWIP
+
+config SYS_RX_ETH_BUFFER
+ int "Number of receive packet buffers"
+ default 4
+ help
+ Defines the number of Ethernet receive buffers. On some Ethernet
+ controllers it is recommended to set this value to 8 or even higher,
+ since all buffers can be full shortly after enabling the interface on
+ high Ethernet traffic.
diff --git a/net/Makefile b/net/Makefile
index a7075c36a04..209377aeb26 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -5,16 +5,13 @@
#ccflags-y += -DDEBUG
+ifeq ($(CONFIG_NET),y)
+
obj-$(CONFIG_NET) += arp.o
obj-$(CONFIG_CMD_BOOTP) += bootp.o
obj-$(CONFIG_CMD_CDP) += cdp.o
obj-$(CONFIG_CMD_DNS) += dns.o
obj-$(CONFIG_DM_DSA) += dsa-uclass.o
-obj-$(CONFIG_$(XPL_)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_$(XPL_)DM_ETH) += eth_common.o
obj-$(CONFIG_CMD_LINK_LOCAL) += link_local.o
obj-$(CONFIG_IPV6) += ndisc.o
obj-$(CONFIG_$(XPL_)DM_ETH) += net.o
@@ -38,3 +35,16 @@ obj-$(CONFIG_CMD_WGET) += wget.o
# sprintf(buf, index ? "foo%d" : "foo", index)
# and this is intentional usage.
CFLAGS_eth_common.o += -Wno-format-extra-args
+
+endif
+
+ifeq ($(filter y,$(CONFIG_NET) $(CONFIG_NET_LWIP)),y)
+obj-$(CONFIG_$(XPL_)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_$(XPL_)DM_ETH) += eth_common.o
+obj-y += net-common.o
+endif
+
+obj-$(CONFIG_NET_LWIP) += lwip/
diff --git a/net/eth-uclass.c b/net/eth-uclass.c
index e34d7af0229..5555f82f23e 100644
--- a/net/eth-uclass.c
+++ b/net/eth-uclass.c
@@ -284,6 +284,27 @@ static int on_ethaddr(const char *name, const char *value, enum env_op op,
}
U_BOOT_ENV_CALLBACK(ethaddr, on_ethaddr);
+int eth_start_udev(struct udevice *dev)
+{
+ struct eth_device_priv *priv = dev_get_uclass_priv(dev);
+ int ret;
+
+ if (priv->running)
+ return 0;
+
+ if (!device_active(dev))
+ return -EINVAL;
+
+ ret = eth_get_ops(dev)->start(dev);
+ if (ret < 0)
+ return ret;
+
+ priv->state = ETH_STATE_ACTIVE;
+ priv->running = true;
+
+ return 0;
+}
+
int eth_init(void)
{
struct udevice *current = NULL;
@@ -328,20 +349,11 @@ int eth_init(void)
if (current) {
debug("Trying %s\n", current->name);
- if (device_active(current)) {
- ret = eth_get_ops(current)->start(current);
- if (ret >= 0) {
- struct eth_device_priv *priv =
- dev_get_uclass_priv(current);
-
- priv->state = ETH_STATE_ACTIVE;
- priv->running = true;
- ret = 0;
- goto end;
- }
- } else {
+ ret = eth_start_udev(current);
+ if (ret < 0)
ret = eth_errno;
- }
+ else
+ break;
debug("FAIL\n");
} else {
diff --git a/net/lwip/Kconfig b/net/lwip/Kconfig
new file mode 100644
index 00000000000..8a67de4cf33
--- /dev/null
+++ b/net/lwip/Kconfig
@@ -0,0 +1,49 @@
+#
+# Network configuration (with lwIP stack)
+#
+
+if NET_LWIP
+
+config LWIP_DEBUG
+ bool "Enable debug traces in the lwIP library"
+
+config LWIP_ASSERT
+ bool "Enable assertions in the lwIP library"
+
+config PROT_DHCP_LWIP
+ bool
+ select PROT_UDP_LWIP
+
+config PROT_DNS_LWIP
+ bool
+ select PROT_UDP_LWIP
+
+config PROT_RAW_LWIP
+ bool
+
+config PROT_TCP_LWIP
+ bool
+
+config PROT_TCP_SACK_LWIP
+ bool "TCP SACK support"
+ depends on PROT_TCP_LWIP
+ default y
+ help
+ TCP protocol with selective acknowledgements. Improves
+ file transfer speed in wget.
+
+config PROT_UDP_LWIP
+ bool
+
+config LWIP_TCP_WND
+ int "Value of TCP_WND"
+ default 8000 if ARCH_QEMU
+ default 3000000
+ help
+ Default value for TCP_WND in the lwIP configuration
+ Lower values result in slower wget transfer speeds in
+ general, especially when the latency on the network is high,
+ but QEMU with "-net user" needs no more than a few KB or the
+ transfer will stall and eventually time out.
+
+endif # NET_LWIP
diff --git a/net/lwip/Makefile b/net/lwip/Makefile
new file mode 100644
index 00000000000..f2558f8763a
--- /dev/null
+++ b/net/lwip/Makefile
@@ -0,0 +1,8 @@
+ccflags-y += -I$(srctree)/lib/lwip/lwip/src/include -I$(srctree)/lib/lwip/u-boot
+
+obj-$(CONFIG_$(SPL_)DM_ETH) += net-lwip.o
+obj-$(CONFIG_CMD_DHCP) += dhcp.o
+obj-$(CONFIG_CMD_DNS) += dns.o
+obj-$(CONFIG_CMD_PING) += ping.o
+obj-$(CONFIG_CMD_TFTPBOOT) += tftp.o
+obj-$(CONFIG_CMD_WGET) += wget.o
diff --git a/net/lwip/dhcp.c b/net/lwip/dhcp.c
new file mode 100644
index 00000000000..23b56226921
--- /dev/null
+++ b/net/lwip/dhcp.c
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (C) 2024 Linaro Ltd. */
+
+#include <command.h>
+#include <console.h>
+#include <dm/device.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <lwip/dhcp.h>
+#include <lwip/dns.h>
+#include <lwip/timeouts.h>
+#include <net.h>
+#include <time.h>
+
+#define DHCP_TIMEOUT_MS 10000
+
+#ifdef CONFIG_CMD_TFTPBOOT
+/* Boot file obtained from DHCP (if present) */
+static char boot_file_name[DHCP_BOOT_FILE_LEN];
+#endif
+
+static void call_lwip_dhcp_fine_tmr(void *ctx)
+{
+ dhcp_fine_tmr();
+ sys_timeout(10, call_lwip_dhcp_fine_tmr, NULL);
+}
+
+static int dhcp_loop(struct udevice *udev)
+{
+ char *ipstr = "ipaddr\0\0";
+ char *maskstr = "netmask\0\0";
+ char *gwstr = "gatewayip\0\0";
+ unsigned long start;
+ struct netif *netif;
+ struct dhcp *dhcp;
+ bool bound;
+ int idx;
+
+ idx = dev_seq(udev);
+ if (idx < 0 || idx > 99) {
+ log_err("unexpected idx %d\n", idx);
+ return CMD_RET_FAILURE;
+ }
+
+ netif = net_lwip_new_netif_noip(udev);
+ if (!netif)
+ return CMD_RET_FAILURE;
+
+ start = get_timer(0);
+
+ if (dhcp_start(netif))
+ return CMD_RET_FAILURE;
+
+ call_lwip_dhcp_fine_tmr(NULL);
+
+ /* Wait for DHCP to complete */
+ do {
+ net_lwip_rx(udev, netif);
+ sys_check_timeouts();
+ bound = dhcp_supplied_address(netif);
+ if (bound)
+ break;
+ if (ctrlc()) {
+ printf("Abort\n");
+ break;
+ }
+ mdelay(1);
+ } while (get_timer(start) < DHCP_TIMEOUT_MS);
+
+ sys_untimeout(call_lwip_dhcp_fine_tmr, NULL);
+
+ if (!bound) {
+ net_lwip_remove_netif(netif);
+ return CMD_RET_FAILURE;
+ }
+
+ dhcp = netif_dhcp_data(netif);
+
+ env_set("bootfile", dhcp->boot_file_name);
+
+ if (idx > 0) {
+ sprintf(ipstr, "ipaddr%d", idx);
+ sprintf(maskstr, "netmask%d", idx);
+ sprintf(gwstr, "gatewayip%d", idx);
+ } else {
+ net_ip.s_addr = dhcp->offered_ip_addr.addr;
+ }
+
+ env_set(ipstr, ip4addr_ntoa(&dhcp->offered_ip_addr));
+ env_set(maskstr, ip4addr_ntoa(&dhcp->offered_sn_mask));
+ env_set("serverip", ip4addr_ntoa(&dhcp->server_ip_addr));
+ if (dhcp->offered_gw_addr.addr != 0)
+ env_set(gwstr, ip4addr_ntoa(&dhcp->offered_gw_addr));
+
+#ifdef CONFIG_PROT_DNS_LWIP
+ env_set("dnsip", ip4addr_ntoa(dns_getserver(0)));
+ env_set("dnsip2", ip4addr_ntoa(dns_getserver(1)));
+#endif
+#ifdef CONFIG_CMD_TFTPBOOT
+ if (dhcp->boot_file_name[0] != '\0')
+ strncpy(boot_file_name, dhcp->boot_file_name,
+ sizeof(boot_file_name));
+#endif
+
+ printf("DHCP client bound to address %pI4 (%lu ms)\n",
+ &dhcp->offered_ip_addr, get_timer(start));
+
+ net_lwip_remove_netif(netif);
+ return CMD_RET_SUCCESS;
+}
+
+int do_dhcp(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ eth_set_current();
+
+ return dhcp_loop(eth_get_dev());
+}
+
+int dhcp_run(ulong addr, const char *fname, bool autoload)
+{
+ char *dhcp_argv[] = {"dhcp", NULL, };
+#ifdef CONFIG_CMD_TFTPBOOT
+ char *tftp_argv[] = {"tftpboot", boot_file_name, NULL, };
+#endif
+ struct cmd_tbl cmdtp = {}; /* dummy */
+
+ if (autoload) {
+#ifdef CONFIG_CMD_TFTPBOOT
+ /* Assume DHCP was already performed */
+ if (boot_file_name[0])
+ return do_tftpb(&cmdtp, 0, 2, tftp_argv);
+ return 0;
+#else
+ return -EOPNOTSUPP;
+#endif
+ }
+
+ return do_dhcp(&cmdtp, 0, 1, dhcp_argv);
+}
diff --git a/net/lwip/dns.c b/net/lwip/dns.c
new file mode 100644
index 00000000000..4b937feaee1
--- /dev/null
+++ b/net/lwip/dns.c
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (C) 2024 Linaro Ltd. */
+
+#include <command.h>
+#include <console.h>
+#include <lwip/dns.h>
+#include <lwip/timeouts.h>
+#include <net.h>
+#include <time.h>
+
+#define DNS_RESEND_MS 1000
+#define DNS_TIMEOUT_MS 10000
+
+struct dns_cb_arg {
+ ip_addr_t host_ipaddr;
+ const char *var;
+ bool done;
+};
+
+static void do_dns_tmr(void *arg)
+{
+ dns_tmr();
+}
+
+static void dns_cb(const char *name, const ip_addr_t *ipaddr, void *arg)
+{
+ struct dns_cb_arg *dns_cb_arg = arg;
+ char *ipstr = ip4addr_ntoa(ipaddr);
+
+ dns_cb_arg->done = true;
+
+ if (!ipaddr) {
+ printf("DNS: host not found\n");
+ dns_cb_arg->host_ipaddr.addr = 0;
+ return;
+ }
+
+ if (dns_cb_arg->var)
+ env_set(dns_cb_arg->var, ipstr);
+
+ printf("%s\n", ipstr);
+}
+
+static int dns_loop(struct udevice *udev, const char *name, const char *var)
+{
+ struct dns_cb_arg dns_cb_arg = { };
+ bool has_server = false;
+ struct netif *netif;
+ ip_addr_t ipaddr;
+ ip_addr_t ns;
+ ulong start;
+ char *nsenv;
+ int ret;
+
+ dns_cb_arg.var = var;
+
+ netif = net_lwip_new_netif(udev);
+ if (!netif)
+ return -1;
+
+ dns_init();
+
+ nsenv = env_get("dnsip");
+ if (nsenv && ipaddr_aton(nsenv, &ns)) {
+ dns_setserver(0, &ns);
+ has_server = true;
+ }
+
+ nsenv = env_get("dnsip2");
+ if (nsenv && ipaddr_aton(nsenv, &ns)) {
+ dns_setserver(1, &ns);
+ has_server = true;
+ }
+
+ if (!has_server) {
+ log_err("No valid name server (dnsip/dnsip2)\n");
+ net_lwip_remove_netif(netif);
+ return CMD_RET_FAILURE;
+ }
+
+ dns_cb_arg.done = false;
+
+ ret = dns_gethostbyname(name, &ipaddr, dns_cb, &dns_cb_arg);
+
+ if (ret == ERR_OK) {
+ dns_cb(name, &ipaddr, &dns_cb_arg);
+ } else if (ret == ERR_INPROGRESS) {
+ start = get_timer(0);
+ sys_timeout(DNS_RESEND_MS, do_dns_tmr, NULL);
+ do {
+ net_lwip_rx(udev, netif);
+ if (dns_cb_arg.done)
+ break;
+ sys_check_timeouts();
+ if (ctrlc()) {
+ printf("\nAbort\n");
+ break;
+ }
+ } while (get_timer(start) < DNS_TIMEOUT_MS);
+ sys_untimeout(do_dns_tmr, NULL);
+ }
+
+ net_lwip_remove_netif(netif);
+
+ if (dns_cb_arg.done && dns_cb_arg.host_ipaddr.addr != 0)
+ return CMD_RET_SUCCESS;
+
+ return CMD_RET_FAILURE;
+}
+
+int do_dns(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ char *name;
+ char *var = NULL;
+
+ if (argc == 1 || argc > 3)
+ return CMD_RET_USAGE;
+
+ name = argv[1];
+
+ if (argc == 3)
+ var = argv[2];
+
+ eth_set_current();
+
+ return dns_loop(eth_get_dev(), name, var);
+}
diff --git a/net/lwip/eth_internal.h b/net/lwip/eth_internal.h
new file mode 100644
index 00000000000..0b829a8d388
--- /dev/null
+++ b/net/lwip/eth_internal.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * (C) Copyright 2001-2015
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ * Joe Hershberger, National Instruments
+ */
+
+#ifndef __ETH_INTERNAL_H
+#define __ETH_INTERNAL_H
+
+/* Do init that is common to driver model and legacy networking */
+void eth_common_init(void);
+
+/**
+ * eth_env_set_enetaddr_by_index() - set the MAC address environment variable
+ *
+ * This sets up an environment variable with the given MAC address (@enetaddr).
+ * The environment variable to be set is defined by <@base_name><@index>addr.
+ * If @index is 0 it is omitted. For common Ethernet this means ethaddr,
+ * eth1addr, etc.
+ *
+ * @base_name: Base name for variable, typically "eth"
+ * @index: Index of interface being updated (>=0)
+ * @enetaddr: Pointer to MAC address to put into the variable
+ * Return: 0 if OK, other value on error
+ */
+int eth_env_set_enetaddr_by_index(const char *base_name, int index,
+ uchar *enetaddr);
+
+int eth_mac_skip(int index);
+void eth_current_changed(void);
+void eth_set_dev(struct udevice *dev);
+void eth_set_current_to_next(void);
+
+#endif
diff --git a/net/lwip/net-lwip.c b/net/lwip/net-lwip.c
new file mode 100644
index 00000000000..5c2bb2e0361
--- /dev/null
+++ b/net/lwip/net-lwip.c
@@ -0,0 +1,305 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* Copyright (C) 2024 Linaro Ltd. */
+
+#include <command.h>
+#include <dm/device.h>
+#include <dm/uclass.h>
+#include <lwip/ip4_addr.h>
+#include <lwip/err.h>
+#include <lwip/netif.h>
+#include <lwip/pbuf.h>
+#include <lwip/etharp.h>
+#include <lwip/init.h>
+#include <lwip/prot/etharp.h>
+#include <net.h>
+
+/* xx:xx:xx:xx:xx:xx\0 */
+#define MAC_ADDR_STRLEN 18
+
+#if defined(CONFIG_API) || defined(CONFIG_EFI_LOADER)
+void (*push_packet)(void *, int len) = 0;
+#endif
+int net_restart_wrap;
+static uchar net_pkt_buf[(PKTBUFSRX) * PKTSIZE_ALIGN + 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 linkoutput(struct netif *netif, struct pbuf *p)
+{
+ struct udevice *udev = netif->state;
+ void *pp = NULL;
+ int err;
+
+ if ((unsigned long)p->payload % PKTALIGN) {
+ /*
+ * Some net drivers have strict alignment requirements and may
+ * fail or output invalid data if the packet is not aligned.
+ */
+ pp = memalign(PKTALIGN, p->len);
+ if (!pp)
+ return ERR_ABRT;
+ memcpy(pp, p->payload, p->len);
+ }
+
+ err = eth_get_ops(udev)->send(udev, pp ? pp : p->payload, p->len);
+ free(pp);
+ if (err) {
+ log_err("send error %d\n", err);
+ return ERR_ABRT;
+ }
+
+ return ERR_OK;
+}
+
+static err_t net_lwip_if_init(struct netif *netif)
+{
+ netif->output = etharp_output;
+ netif->linkoutput = linkoutput;
+ netif->mtu = 1500;
+ netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
+
+ return ERR_OK;
+}
+
+static void eth_init_rings(void)
+{
+ int i;
+
+ for (i = 0; i < PKTBUFSRX; i++)
+ net_rx_packets[i] = net_pkt_buf + i * PKTSIZE_ALIGN;
+}
+
+struct netif *net_lwip_get_netif(void)
+{
+ struct netif *netif, *found = NULL;
+
+ NETIF_FOREACH(netif) {
+ if (!found)
+ found = netif;
+ else
+ printf("Error: more than one netif in lwIP\n");
+ }
+ return found;
+}
+
+static int get_udev_ipv4_info(struct udevice *dev, ip4_addr_t *ip,
+ ip4_addr_t *mask, ip4_addr_t *gw)
+{
+ char *ipstr = "ipaddr\0\0";
+ char *maskstr = "netmask\0\0";
+ char *gwstr = "gatewayip\0\0";
+ int idx = dev_seq(dev);
+ char *env;
+
+ if (idx < 0 || idx > 99) {
+ log_err("unexpected idx %d\n", idx);
+ return -1;
+ }
+
+ if (idx) {
+ sprintf(ipstr, "ipaddr%d", idx);
+ sprintf(maskstr, "netmask%d", idx);
+ sprintf(gwstr, "gatewayip%d", idx);
+ }
+
+ ip4_addr_set_zero(ip);
+ ip4_addr_set_zero(mask);
+ ip4_addr_set_zero(gw);
+
+ env = env_get(ipstr);
+ if (env)
+ ip4addr_aton(env, ip);
+
+ env = env_get(maskstr);
+ if (env)
+ ip4addr_aton(env, mask);
+
+ env = env_get(gwstr);
+ if (env)
+ ip4addr_aton(env, gw);
+
+ return 0;
+}
+
+static struct netif *new_netif(struct udevice *udev, bool with_ip)
+{
+ unsigned char enetaddr[ARP_HLEN];
+ char hwstr[MAC_ADDR_STRLEN];
+ ip4_addr_t ip, mask, gw;
+ struct netif *netif;
+ int ret = 0;
+ static bool first_call = true;
+
+ if (!udev)
+ return NULL;
+
+ if (first_call) {
+ eth_init_rings();
+ /* Pick a valid active device, if any */
+ eth_init();
+ lwip_init();
+ first_call = false;
+ }
+
+ if (eth_start_udev(udev) < 0) {
+ log_err("Could not start %s\n", udev->name);
+ return NULL;
+ }
+
+ netif_remove(net_lwip_get_netif());
+
+ ip4_addr_set_zero(&ip);
+ ip4_addr_set_zero(&mask);
+ ip4_addr_set_zero(&gw);
+
+ if (with_ip)
+ if (get_udev_ipv4_info(udev, &ip, &mask, &gw) < 0)
+ return NULL;
+
+ eth_env_get_enetaddr_by_index("eth", dev_seq(udev), enetaddr);
+ ret = snprintf(hwstr, MAC_ADDR_STRLEN, "%pM", enetaddr);
+ if (ret < 0 || ret >= MAC_ADDR_STRLEN)
+ return NULL;
+
+ netif = calloc(1, sizeof(struct netif));
+ if (!netif)
+ return NULL;
+
+ netif->name[0] = 'e';
+ netif->name[1] = 't';
+
+ string_to_enetaddr(hwstr, netif->hwaddr);
+ netif->hwaddr_len = ETHARP_HWADDR_LEN;
+ debug("adding lwIP netif for %s with hwaddr:%s ip:%s ", udev->name,
+ hwstr, ip4addr_ntoa(&ip));
+ debug("mask:%s ", ip4addr_ntoa(&mask));
+ debug("gw:%s\n", ip4addr_ntoa(&gw));
+
+ if (!netif_add(netif, &ip, &mask, &gw, udev, net_lwip_if_init,
+ netif_input)) {
+ printf("error: netif_add() failed\n");
+ free(netif);
+ return NULL;
+ }
+
+ netif_set_up(netif);
+ netif_set_link_up(netif);
+ /* Routing: use this interface to reach the default gateway */
+ netif_set_default(netif);
+
+ return netif;
+}
+
+struct netif *net_lwip_new_netif(struct udevice *udev)
+{
+ return new_netif(udev, true);
+}
+
+struct netif *net_lwip_new_netif_noip(struct udevice *udev)
+{
+
+ return new_netif(udev, false);
+}
+
+void net_lwip_remove_netif(struct netif *netif)
+{
+ netif_remove(netif);
+ free(netif);
+}
+
+int net_init(void)
+{
+ eth_set_current();
+
+ net_lwip_new_netif(eth_get_dev());
+
+ return 0;
+}
+
+static struct pbuf *alloc_pbuf_and_copy(uchar *data, int len)
+{
+ struct pbuf *p, *q;
+
+ /* We allocate a pbuf chain of pbufs from the pool. */
+ p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
+ if (!p) {
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.drop);
+ return NULL;
+ }
+
+ for (q = p; q != NULL; q = q->next) {
+ memcpy(q->payload, data, q->len);
+ data += q->len;
+ }
+
+ LINK_STATS_INC(link.recv);
+
+ return p;
+}
+
+int net_lwip_rx(struct udevice *udev, struct netif *netif)
+{
+ struct pbuf *pbuf;
+ uchar *packet;
+ int flags;
+ int len;
+ int i;
+
+ if (!eth_is_active(udev))
+ return -EINVAL;
+
+ flags = ETH_RECV_CHECK_DEVICE;
+ for (i = 0; i < ETH_PACKETS_BATCH_RECV; i++) {
+ len = eth_get_ops(udev)->recv(udev, flags, &packet);
+ flags = 0;
+
+ if (len > 0) {
+ pbuf = alloc_pbuf_and_copy(packet, len);
+ if (pbuf)
+ netif->input(pbuf, netif);
+ }
+ if (len >= 0 && eth_get_ops(udev)->free_pkt)
+ eth_get_ops(udev)->free_pkt(udev, packet, len);
+ if (len <= 0)
+ break;
+ }
+ if (len == -EAGAIN)
+ len = 0;
+
+ return len;
+}
+
+void net_process_received_packet(uchar *in_packet, int len)
+{
+#if defined(CONFIG_API) || defined(CONFIG_EFI_LOADER)
+ if (push_packet)
+ (*push_packet)(in_packet, len);
+#endif
+}
+
+int net_loop(enum proto_t protocol)
+{
+ char *argv[1];
+
+ switch (protocol) {
+ case TFTPGET:
+ argv[0] = "tftpboot";
+ return do_tftpb(NULL, 0, 1, argv);
+ default:
+ return -EINVAL;
+ }
+
+ return -EINVAL;
+}
+
+u32_t sys_now(void)
+{
+ return get_timer(0);
+}
diff --git a/net/lwip/ping.c b/net/lwip/ping.c
new file mode 100644
index 00000000000..8dafa25959f
--- /dev/null
+++ b/net/lwip/ping.c
@@ -0,0 +1,177 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (C) 2024 Linaro Ltd. */
+
+#include <command.h>
+#include <console.h>
+#include <dm/device.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <lwip/icmp.h>
+#include <lwip/inet_chksum.h>
+#include <lwip/raw.h>
+#include <lwip/timeouts.h>
+#include <net.h>
+#include <time.h>
+
+#define PING_DELAY_MS 1000
+#define PING_COUNT 5
+/* Ping identifier - must fit on a u16_t */
+#define PING_ID 0xAFAF
+
+struct ping_ctx {
+ ip_addr_t target;
+ struct raw_pcb *pcb;
+ struct icmp_echo_hdr *iecho;
+ uint16_t seq_num;
+ bool alive;
+};
+
+static u8_t ping_recv(void *arg, struct raw_pcb *pcb, struct pbuf *p,
+ const ip_addr_t *addr)
+{
+ struct ping_ctx *ctx = arg;
+ struct icmp_echo_hdr *iecho = ctx->iecho;
+
+ if (addr->addr != ctx->target.addr)
+ return 0;
+
+ if ((p->tot_len >= (IP_HLEN + sizeof(struct icmp_echo_hdr))) &&
+ pbuf_remove_header(p, IP_HLEN) == 0) {
+ iecho = (struct icmp_echo_hdr *)p->payload;
+
+ if ((iecho->id == PING_ID) &&
+ (iecho->seqno == lwip_htons(ctx->seq_num))) {
+ ctx->alive = true;
+ printf("host %s is alive\n", ipaddr_ntoa(addr));
+ pbuf_free(p);
+ return 1; /* eat the packet */
+ }
+ /* not eaten, restore original packet */
+ pbuf_add_header(p, IP_HLEN);
+ }
+
+ return 0; /* don't eat the packet */
+}
+
+static int ping_raw_init(struct ping_ctx *ctx)
+{
+ ctx->pcb = raw_new(IP_PROTO_ICMP);
+ if (!ctx->pcb)
+ return -ENOMEM;
+
+ raw_recv(ctx->pcb, ping_recv, ctx);
+ raw_bind(ctx->pcb, IP_ADDR_ANY);
+
+ return 0;
+}
+
+static void ping_raw_stop(struct ping_ctx *ctx)
+{
+ if (ctx->pcb)
+ raw_remove(ctx->pcb);
+}
+
+static void ping_prepare_echo(struct ping_ctx *ctx)
+{
+ struct icmp_echo_hdr *iecho = ctx->iecho;
+
+ ICMPH_TYPE_SET(iecho, ICMP_ECHO);
+ ICMPH_CODE_SET(iecho, 0);
+ iecho->chksum = 0;
+ iecho->id = PING_ID;
+ iecho->seqno = lwip_htons(ctx->seq_num);
+
+ iecho->chksum = inet_chksum(iecho, sizeof(*iecho));
+}
+
+static void ping_send_icmp(struct ping_ctx *ctx)
+{
+ struct pbuf *p;
+ size_t ping_size = sizeof(struct icmp_echo_hdr);
+
+ p = pbuf_alloc(PBUF_IP, (u16_t)ping_size, PBUF_RAM);
+ if (!p)
+ return;
+
+ if ((p->len == p->tot_len) && !p->next) {
+ ctx->iecho = (struct icmp_echo_hdr *)p->payload;
+ ping_prepare_echo(ctx);
+ raw_sendto(ctx->pcb, p, &ctx->target);
+ }
+
+ pbuf_free(p);
+}
+
+static void ping_send(void *arg)
+{
+ struct ping_ctx *ctx = arg;
+
+ ctx->seq_num++;
+ if (ctx->seq_num <= PING_COUNT) {
+ ping_send_icmp(ctx);
+ sys_timeout(PING_DELAY_MS, ping_send, ctx);
+ }
+}
+
+static int ping_loop(struct udevice *udev, const ip_addr_t* addr)
+{
+ struct ping_ctx ctx = {};
+ struct netif *netif;
+ int ret;
+
+ netif = net_lwip_new_netif(udev);
+ if (!netif)
+ return CMD_RET_FAILURE;
+
+ printf("Using %s device\n", udev->name);
+
+ ret = ping_raw_init(&ctx);
+ if (ret < 0) {
+ net_lwip_remove_netif(netif);
+ return ret;
+ }
+
+ ctx.target = *addr;
+
+ ping_send(&ctx);
+
+ do {
+ sys_check_timeouts();
+ net_lwip_rx(udev, netif);
+ if (ctx.alive)
+ break;
+ if (ctrlc()) {
+ printf("\nAbort\n");
+ break;
+ }
+ } while (ctx.seq_num <= PING_COUNT);
+
+ sys_untimeout(ping_send, &ctx);
+ ping_raw_stop(&ctx);
+
+ net_lwip_remove_netif(netif);
+
+ if (ctx.alive)
+ return 0;
+
+ printf("ping failed; host %s is not alive\n", ipaddr_ntoa(addr));
+ return -1;
+}
+
+int do_ping(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ ip_addr_t addr;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ if (!ipaddr_aton(argv[1], &addr))
+ return CMD_RET_USAGE;
+
+ eth_set_current();
+
+ if (ping_loop(eth_get_dev(), &addr) < 0)
+ return CMD_RET_FAILURE;
+
+ return CMD_RET_SUCCESS;
+}
diff --git a/net/lwip/tftp.c b/net/lwip/tftp.c
new file mode 100644
index 00000000000..f4d0a6aa19a
--- /dev/null
+++ b/net/lwip/tftp.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (C) 2024 Linaro Ltd. */
+
+#include <command.h>
+#include <console.h>
+#include <display_options.h>
+#include <dm/device.h>
+#include <efi_loader.h>
+#include <image.h>
+#include <linux/delay.h>
+#include <lwip/apps/tftp_client.h>
+#include <lwip/timeouts.h>
+#include <mapmem.h>
+#include <net.h>
+#include <time.h>
+
+#define PROGRESS_PRINT_STEP_BYTES (10 * 1024)
+
+enum done_state {
+ NOT_DONE = 0,
+ SUCCESS,
+ FAILURE,
+ ABORTED
+};
+
+struct tftp_ctx {
+ ulong daddr;
+ ulong size;
+ ulong block_count;
+ ulong start_time;
+ enum done_state done;
+};
+
+static void *tftp_open(const char *fname, const char *mode, u8_t is_write)
+{
+ return NULL;
+}
+
+static void tftp_close(void *handle)
+{
+ struct tftp_ctx *ctx = handle;
+ ulong elapsed;
+
+ if (ctx->done == FAILURE || ctx->done == ABORTED) {
+ /* Closing after an error or Ctrl-C */
+ return;
+ }
+ ctx->done = SUCCESS;
+
+ elapsed = get_timer(ctx->start_time);
+ if (elapsed > 0) {
+ puts("\n\t "); /* Line up with "Loading: " */
+ print_size(ctx->size / elapsed * 1000, "/s");
+ }
+ puts("\ndone\n");
+ printf("Bytes transferred = %lu (%lx hex)\n", ctx->size, ctx->size);
+
+ if (env_set_hex("filesize", ctx->size)) {
+ log_err("filesize not updated\n");
+ return;
+ }
+}
+
+static int tftp_read(void *handle, void *buf, int bytes)
+{
+ return 0;
+}
+
+static int tftp_write(void *handle, struct pbuf *p)
+{
+ struct tftp_ctx *ctx = handle;
+ struct pbuf *q;
+
+ for (q = p; q != NULL; q = q->next) {
+ memcpy((void *)ctx->daddr, q->payload, q->len);
+ ctx->daddr += q->len;
+ ctx->size += q->len;
+ ctx->block_count++;
+ if (ctx->block_count % 10 == 0) {
+ putc('#');
+ if (ctx->block_count % (65 * 10) == 0)
+ puts("\n\t ");
+ }
+ }
+
+ return 0;
+}
+
+static void tftp_error(void *handle, int err, const char *msg, int size)
+{
+ struct tftp_ctx *ctx = handle;
+ char message[100];
+
+ ctx->done = FAILURE;
+ memset(message, 0, sizeof(message));
+ memcpy(message, msg, LWIP_MIN(sizeof(message) - 1, (size_t)size));
+
+ printf("\nTFTP error: %d (%s)\n", err, message);
+}
+
+static const struct tftp_context tftp_context = {
+ tftp_open,
+ tftp_close,
+ tftp_read,
+ tftp_write,
+ tftp_error
+};
+
+static int tftp_loop(struct udevice *udev, ulong addr, char *fname,
+ ip_addr_t srvip, uint16_t srvport)
+{
+ struct netif *netif;
+ struct tftp_ctx ctx;
+ err_t err;
+
+ if (!fname || addr == 0)
+ return -1;
+
+ if (!srvport)
+ srvport = TFTP_PORT;
+
+ netif = net_lwip_new_netif(udev);
+ if (!netif)
+ return -1;
+
+ ctx.done = NOT_DONE;
+ ctx.size = 0;
+ ctx.block_count = 0;
+ ctx.daddr = addr;
+
+ printf("Using %s device\n", udev->name);
+ printf("TFTP from server %s; our IP address is %s\n",
+ ip4addr_ntoa(&srvip), env_get("ipaddr"));
+ printf("Filename '%s'.\n", fname);
+ printf("Load address: 0x%lx\n", ctx.daddr);
+ printf("Loading: ");
+
+ err = tftp_init_client(&tftp_context);
+ if (!(err == ERR_OK || err == ERR_USE))
+ log_err("tftp_init_client err: %d\n", err);
+
+ tftp_client_set_blksize(CONFIG_TFTP_BLOCKSIZE);
+
+ ctx.start_time = get_timer(0);
+ err = tftp_get(&ctx, &srvip, srvport, fname, TFTP_MODE_OCTET);
+ /* might return different errors, like routing problems */
+ if (err != ERR_OK) {
+ printf("tftp_get() error %d\n", err);
+ net_lwip_remove_netif(netif);
+ return -1;
+ }
+
+ while (!ctx.done) {
+ net_lwip_rx(udev, netif);
+ sys_check_timeouts();
+ if (ctrlc()) {
+ printf("\nAbort\n");
+ ctx.done = ABORTED;
+ break;
+ }
+ }
+
+ tftp_cleanup();
+
+ net_lwip_remove_netif(netif);
+
+ if (ctx.done == SUCCESS) {
+ if (env_set_hex("fileaddr", addr)) {
+ log_err("fileaddr not updated\n");
+ return -1;
+ }
+ efi_set_bootdev("Net", "", fname, map_sysmem(addr, 0),
+ ctx.size);
+ return 0;
+ }
+
+ return -1;
+}
+
+int do_tftpb(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ int ret = CMD_RET_SUCCESS;
+ char *arg = NULL;
+ char *words[3] = { };
+ char *fname = NULL;
+ char *server_ip = NULL;
+ char *server_port = NULL;
+ char *end;
+ ip_addr_t srvip;
+ uint16_t port = TFTP_PORT;
+ 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:[port:]]fname */
+ i = 0;
+ while ((*(words + i) = strsep(&arg,":")))
+ i++;
+
+ switch (i) {
+ case 3:
+ server_ip = words[0];
+ server_port = words[1];
+ fname = words[2];
+ break;
+ 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("tftpserverip");
+ if (!server_ip)
+ server_ip = env_get("serverip");
+ if (!server_ip) {
+ log_err("error: tftpserverip/serverip has to be set\n");
+ ret = CMD_RET_FAILURE;
+ goto out;
+ }
+
+ if (server_port)
+ port = dectoul(server_port, NULL);
+
+ 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;
+ }
+
+ eth_set_current();
+
+ if (tftp_loop(eth_get_dev(), laddr, fname, srvip, port) < 0)
+ ret = CMD_RET_FAILURE;
+out:
+ free(arg);
+ return ret;
+}
diff --git a/net/lwip/wget.c b/net/lwip/wget.c
new file mode 100644
index 00000000000..b495ebd1aa9
--- /dev/null
+++ b/net/lwip/wget.c
@@ -0,0 +1,357 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (C) 2024 Linaro Ltd. */
+
+#include <command.h>
+#include <console.h>
+#include <display_options.h>
+#include <efi_loader.h>
+#include <image.h>
+#include <lwip/apps/http_client.h>
+#include <lwip/timeouts.h>
+#include <mapmem.h>
+#include <net.h>
+#include <time.h>
+
+#define SERVER_NAME_SIZE 200
+#define HTTP_PORT_DEFAULT 80
+#define PROGRESS_PRINT_STEP_BYTES (100 * 1024)
+
+enum done_state {
+ NOT_DONE = 0,
+ SUCCESS = 1,
+ FAILURE = 2
+};
+
+struct wget_ctx {
+ char *path;
+ ulong daddr;
+ ulong saved_daddr;
+ ulong size;
+ ulong prevsize;
+ ulong start_time;
+ enum done_state done;
+};
+
+static int parse_url(char *url, char *host, u16 *port, char **path)
+{
+ char *p, *pp;
+ long lport;
+
+ p = strstr(url, "http://");
+ if (!p) {
+ log_err("only http:// is supported\n");
+ return -EINVAL;
+ }
+
+ p += strlen("http://");
+
+ /* Parse hostname */
+ pp = strchr(p, ':');
+ if (!pp)
+ pp = strchr(p, '/');
+ if (!pp)
+ return -EINVAL;
+
+ if (p + SERVER_NAME_SIZE <= pp)
+ return -EINVAL;
+
+ memcpy(host, p, pp - p);
+ host[pp - p] = '\0';
+
+ if (*pp == ':') {
+ /* Parse port number */
+ p = pp + 1;
+ lport = simple_strtol(p, &pp, 10);
+ if (pp && *pp != '/')
+ return -EINVAL;
+ if (lport > 65535)
+ return -EINVAL;
+ *port = (u16)lport;
+ } else {
+ *port = HTTP_PORT_DEFAULT;
+ }
+ if (*pp != '/')
+ return -EINVAL;
+ *path = pp;
+
+ return 0;
+}
+
+/*
+ * Legacy syntax support
+ * Convert [<server_name_or_ip>:]filename into a URL if needed
+ */
+static int parse_legacy_arg(char *arg, char *nurl, size_t rem)
+{
+ char *p = nurl;
+ size_t n;
+ char *col = strchr(arg, ':');
+ char *env;
+ char *server;
+ char *path;
+
+ if (strstr(arg, "http") == arg) {
+ n = snprintf(nurl, rem, "%s", arg);
+ if (n < 0 || n > rem)
+ return -1;
+ return 0;
+ }
+
+ n = snprintf(p, rem, "%s", "http://");
+ if (n < 0 || n > rem)
+ return -1;
+ p += n;
+ rem -= n;
+
+ if (col) {
+ n = col - arg;
+ server = arg;
+ path = col + 1;
+ } else {
+ env = env_get("httpserverip");
+ if (!env)
+ env = env_get("serverip");
+ if (!env) {
+ log_err("error: httpserver/serverip has to be set\n");
+ return -1;
+ }
+ n = strlen(env);
+ server = env;
+ path = arg;
+ }
+
+ if (rem < n)
+ return -1;
+ strncpy(p, server, n);
+ p += n;
+ rem -= n;
+ if (rem < 1)
+ return -1;
+ *p = '/';
+ p++;
+ rem--;
+ n = strlen(path);
+ if (rem < n)
+ return -1;
+ strncpy(p, path, n);
+ p += n;
+ rem -= n;
+ if (rem < 1)
+ return -1;
+ *p = '\0';
+
+ return 0;
+}
+
+static err_t httpc_recv_cb(void *arg, struct altcp_pcb *pcb, struct pbuf *pbuf,
+ err_t err)
+{
+ struct wget_ctx *ctx = arg;
+ struct pbuf *buf;
+
+ if (!pbuf)
+ return ERR_BUF;
+
+ if (!ctx->start_time)
+ ctx->start_time = get_timer(0);
+
+ for (buf = pbuf; buf; buf = buf->next) {
+ memcpy((void *)ctx->daddr, buf->payload, buf->len);
+ ctx->daddr += buf->len;
+ ctx->size += buf->len;
+ if (ctx->size - ctx->prevsize > PROGRESS_PRINT_STEP_BYTES) {
+ printf("#");
+ ctx->prevsize = ctx->size;
+ }
+ }
+
+ altcp_recved(pcb, pbuf->tot_len);
+ pbuf_free(pbuf);
+ return ERR_OK;
+}
+
+static void httpc_result_cb(void *arg, httpc_result_t httpc_result,
+ u32_t rx_content_len, u32_t srv_res, err_t err)
+{
+ struct wget_ctx *ctx = arg;
+ ulong elapsed;
+
+ if (httpc_result != HTTPC_RESULT_OK) {
+ log_err("\nHTTP client error %d\n", httpc_result);
+ ctx->done = FAILURE;
+ return;
+ }
+ if (srv_res != 200) {
+ log_err("\nHTTP server error %d\n", srv_res);
+ ctx->done = FAILURE;
+ return;
+ }
+
+ elapsed = get_timer(ctx->start_time);
+ if (!elapsed)
+ elapsed = 1;
+ if (rx_content_len > PROGRESS_PRINT_STEP_BYTES)
+ printf("\n");
+ printf("%u bytes transferred in %lu ms (", rx_content_len, elapsed);
+ print_size(rx_content_len / elapsed * 1000, "/s)\n");
+ printf("Bytes transferred = %lu (%lx hex)\n", ctx->size, ctx->size);
+ efi_set_bootdev("Net", "", ctx->path, map_sysmem(ctx->saved_daddr, 0),
+ rx_content_len);
+ if (env_set_hex("filesize", rx_content_len) ||
+ env_set_hex("fileaddr", ctx->saved_daddr)) {
+ log_err("Could not set filesize or fileaddr\n");
+ ctx->done = FAILURE;
+ return;
+ }
+
+ ctx->done = SUCCESS;
+}
+
+static int wget_loop(struct udevice *udev, ulong dst_addr, char *uri)
+{
+ char server_name[SERVER_NAME_SIZE];
+ httpc_connection_t conn;
+ httpc_state_t *state;
+ struct netif *netif;
+ struct wget_ctx ctx;
+ char *path;
+ u16 port;
+
+ ctx.daddr = dst_addr;
+ ctx.saved_daddr = dst_addr;
+ ctx.done = NOT_DONE;
+ ctx.size = 0;
+ ctx.prevsize = 0;
+ ctx.start_time = 0;
+
+ if (parse_url(uri, server_name, &port, &path))
+ return CMD_RET_USAGE;
+
+ netif = net_lwip_new_netif(udev);
+ if (!netif)
+ return -1;
+
+ memset(&conn, 0, sizeof(conn));
+ conn.result_fn = httpc_result_cb;
+ ctx.path = path;
+ if (httpc_get_file_dns(server_name, port, path, &conn, httpc_recv_cb,
+ &ctx, &state)) {
+ net_lwip_remove_netif(netif);
+ return CMD_RET_FAILURE;
+ }
+
+ while (!ctx.done) {
+ net_lwip_rx(udev, netif);
+ sys_check_timeouts();
+ if (ctrlc())
+ break;
+ }
+
+ net_lwip_remove_netif(netif);
+
+ if (ctx.done == SUCCESS)
+ return 0;
+
+ return -1;
+}
+
+int wget_with_dns(ulong dst_addr, char *uri)
+{
+ eth_set_current();
+
+ return wget_loop(eth_get_dev(), dst_addr, uri);
+}
+
+int do_wget(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
+{
+ char *end;
+ char *url;
+ ulong dst_addr;
+ char nurl[1024];
+
+ if (argc < 2 || argc > 3)
+ return CMD_RET_USAGE;
+
+ dst_addr = hextoul(argv[1], &end);
+ if (end == (argv[1] + strlen(argv[1]))) {
+ if (argc < 3)
+ return CMD_RET_USAGE;
+ url = argv[2];
+ } else {
+ dst_addr = image_load_addr;
+ url = argv[1];
+ }
+
+ if (parse_legacy_arg(url, nurl, sizeof(nurl)))
+ return CMD_RET_FAILURE;
+
+ if (wget_with_dns(dst_addr, nurl))
+ return CMD_RET_FAILURE;
+
+ return CMD_RET_SUCCESS;
+}
+
+/**
+ * wget_validate_uri() - validate the uri for wget
+ *
+ * @uri: uri string
+ *
+ * This function follows the current U-Boot wget implementation.
+ * scheme: only "http:" is supported
+ * authority:
+ * - user information: not supported
+ * - host: supported
+ * - port: not supported(always use the default port)
+ *
+ * Uri is expected to be correctly percent encoded.
+ * This is the minimum check, control codes(0x1-0x19, 0x7F, except '\0')
+ * and space character(0x20) are not allowed.
+ *
+ * TODO: stricter uri conformance check
+ *
+ * Return: true on success, false on failure
+ */
+bool wget_validate_uri(char *uri)
+{
+ char c;
+ bool ret = true;
+ char *str_copy, *s, *authority;
+
+ for (c = 0x1; c < 0x21; c++) {
+ if (strchr(uri, c)) {
+ log_err("invalid character is used\n");
+ return false;
+ }
+ }
+ if (strchr(uri, 0x7f)) {
+ log_err("invalid character is used\n");
+ return false;
+ }
+
+ if (strncmp(uri, "http://", 7)) {
+ log_err("only http:// is supported\n");
+ return false;
+ }
+ str_copy = strdup(uri);
+ if (!str_copy)
+ return false;
+
+ s = str_copy + strlen("http://");
+ authority = strsep(&s, "/");
+ if (!s) {
+ log_err("invalid uri, no file path\n");
+ ret = false;
+ goto out;
+ }
+ s = strchr(authority, '@');
+ if (s) {
+ log_err("user information is not supported\n");
+ ret = false;
+ goto out;
+ }
+
+out:
+ free(str_copy);
+
+ return ret;
+}
diff --git a/net/net-common.c b/net/net-common.c
new file mode 100644
index 00000000000..a7f767d5e9c
--- /dev/null
+++ b/net/net-common.c
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0
+
+void copy_filename(char *dst, const char *src, int size)
+{
+ if (src && *src && (*src == '"')) {
+ ++src;
+ --size;
+ }
+
+ while ((--size > 0) && src && *src && (*src != '"'))
+ *dst++ = *src++;
+ *dst = '\0';
+}
diff --git a/net/net.c b/net/net.c
index 64bcf69d83f..f47e9fbe33a 100644
--- a/net/net.c
+++ b/net/net.c
@@ -1693,18 +1693,6 @@ void net_set_udp_header(uchar *pkt, struct in_addr dest, int dport, int sport,
ip->udp_xsum = 0;
}
-void copy_filename(char *dst, const char *src, int size)
-{
- if (src && *src && (*src == '"')) {
- ++src;
- --size;
- }
-
- while ((--size > 0) && src && *src && (*src != '"'))
- *dst++ = *src++;
- *dst = '\0';
-}
-
int is_serverip_in_cmd(void)
{
return !!strchr(net_boot_file_name, ':');