diff mbox series

[v4,05/14] net-lwip: add DHCP support and dhcp commmand

Message ID 1455663bf6d4f8537d3919b755ef5148b65975c5.1718638104.git.jerome.forissier@linaro.org
State Changes Requested
Delegated to: Tom Rini
Headers show
Series Introduce the lwIP network stack | expand

Commit Message

Jerome Forissier June 17, 2024, 3:32 p.m. UTC
Add what it takes to enable NETDEVICES with NET_LWIP and enable DHCP as
well as the dhcp command. CMD_TFTPBOOT is selected by BOOTMETH_EFI due
to this code having an implicit dependency on do_tftpb().

Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org>
---
 Makefile                |   6 +
 boot/Kconfig            |   3 +-
 cmd/Kconfig             |  26 ++++
 cmd/Makefile            |   4 +
 cmd/net-lwip.c          |  13 ++
 common/board_r.c        |   4 +-
 drivers/net/Kconfig     |   2 +-
 include/net-lwip.h      |   2 +
 net-lwip/Makefile       |  15 +++
 net-lwip/dhcp.c         |  99 +++++++++++++++
 net-lwip/eth_internal.h |  35 ++++++
 net-lwip/net-lwip.c     | 270 ++++++++++++++++++++++++++++++++++++++++
 net-lwip/tftp.c         |  11 ++
 13 files changed, 486 insertions(+), 4 deletions(-)
 create mode 100644 cmd/net-lwip.c
 create mode 100644 net-lwip/Makefile
 create mode 100644 net-lwip/dhcp.c
 create mode 100644 net-lwip/eth_internal.h
 create mode 100644 net-lwip/net-lwip.c
 create mode 100644 net-lwip/tftp.c

Comments

Heinrich Schuchardt June 17, 2024, 4:54 p.m. UTC | #1
On 17.06.24 17:32, Jerome Forissier wrote:
> Add what it takes to enable NETDEVICES with NET_LWIP and enable DHCP as
> well as the dhcp command. CMD_TFTPBOOT is selected by BOOTMETH_EFI due
> to this code having an implicit dependency on do_tftpb().
>
> Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org>
> ---
>   Makefile                |   6 +
>   boot/Kconfig            |   3 +-
>   cmd/Kconfig             |  26 ++++
>   cmd/Makefile            |   4 +
>   cmd/net-lwip.c          |  13 ++
>   common/board_r.c        |   4 +-
>   drivers/net/Kconfig     |   2 +-
>   include/net-lwip.h      |   2 +
>   net-lwip/Makefile       |  15 +++
>   net-lwip/dhcp.c         |  99 +++++++++++++++
>   net-lwip/eth_internal.h |  35 ++++++
>   net-lwip/net-lwip.c     | 270 ++++++++++++++++++++++++++++++++++++++++
>   net-lwip/tftp.c         |  11 ++
>   13 files changed, 486 insertions(+), 4 deletions(-)
>   create mode 100644 cmd/net-lwip.c
>   create mode 100644 net-lwip/Makefile
>   create mode 100644 net-lwip/dhcp.c
>   create mode 100644 net-lwip/eth_internal.h
>   create mode 100644 net-lwip/net-lwip.c
>   create mode 100644 net-lwip/tftp.c
>
> diff --git a/Makefile b/Makefile
> index 0fe1623c550..92a0ab770bb 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -862,6 +862,7 @@ libs-y += env/
>   libs-y += lib/
>   libs-y += fs/
>   libs-$(CONFIG_NET) += net/
> +libs-$(CONFIG_NET_LWIP) += net-lwip/
>   libs-y += disk/
>   libs-y += drivers/
>   libs-$(CONFIG_SYS_FSL_DDR) += drivers/ddr/fsl/
> @@ -2132,6 +2133,11 @@ etags:
>   cscope:
>   		$(FIND) $(FINDFLAGS) $(TAG_SUBDIRS) -name '*.[chS]' -print > \
>   						cscope.files
> +ifdef CONFIG_NET_LWIP
> +		echo net/eth-uclass.c net/eth_common.c net/eth_bootdev.c \
> +		     net/mdio-uclass.c net/mdio-mux-uclass.c >> \
> +						cscope.files
> +endif
>   		@find $(TAG_SUBDIRS) -name '*.[chS]' -type l -print | \
>   			grep -xvf - cscope.files > cscope.files.no-symlinks; \
>   		mv cscope.files.no-symlinks cscope.files
> diff --git a/boot/Kconfig b/boot/Kconfig
> index 6f3096c15a6..004e69dd92a 100644
> --- a/boot/Kconfig
> +++ b/boot/Kconfig
> @@ -378,7 +378,7 @@ config BOOT_DEFAULTS_CMDS
>   	select CMD_FAT
>   	select CMD_FS_GENERIC
>   	select CMD_PART if PARTITIONS
> -	select CMD_DHCP if CMD_NET
> +	select CMD_DHCP if CMD_NET || CMD_NET_LWIP
>   	select CMD_PING if CMD_NET
>   	select CMD_PXE if CMD_NET
>   	select CMD_BOOTI if ARM64
> @@ -540,6 +540,7 @@ config BOOTMETH_EXTLINUX_PXE
>   config BOOTMETH_EFILOADER
>   	bool "Bootdev support for EFI boot"
>   	depends on EFI_BINARY_EXEC
> +	select CMD_TFTPBOOT if CMD_NET_LWIP
>   	default y
>   	help
>   	  Enables support for EFI boot using bootdevs. This makes the
> diff --git a/cmd/Kconfig b/cmd/Kconfig
> index b026439c773..1bfa528e945 100644
> --- a/cmd/Kconfig
> +++ b/cmd/Kconfig
> @@ -2084,6 +2084,32 @@ config CMD_WOL
>
>   endif
>
> +if NET_LWIP
> +
> +menuconfig CMD_NET_LWIP
> +	bool "Network commands (lwIP)"
> +	default y
> +
> +if CMD_NET_LWIP
> +
> +config CMD_DHCP
> +	bool "dhcp"
> +	select PROT_DHCP_LWIP
> +	help
> +	  Boot image via network using DHCP/TFTP protocol
> +
> +config CMD_TFTPBOOT
> +	bool "tftp"
> +	select PROT_UDP_LWIP
> +	default n
> +	help
> +	  tftpboot - load file via network using TFTP protocol
> +	  Currently a placeholder (not implemented)
> +
> +endif
> +
> +endif
> +
>   menu "Misc commands"
>
>   config CMD_2048
> diff --git a/cmd/Makefile b/cmd/Makefile
> index 87133cc27a8..535b6838ca5 100644
> --- a/cmd/Makefile
> +++ b/cmd/Makefile
> @@ -128,6 +128,10 @@ endif
>   obj-$(CONFIG_CMD_MUX) += mux.o
>   obj-$(CONFIG_CMD_NAND) += nand.o
>   obj-$(CONFIG_CMD_NET) += net.o
> +obj-$(CONFIG_CMD_NET_LWIP) += net-lwip.o
> +ifdef CONFIG_CMD_NET_LWIP
> +CFLAGS_net-lwip.o := -I$(srctree)/lib/lwip/lwip/src/include -I$(srctree)/lib/lwip/u-boot
> +endif
>   obj-$(CONFIG_ENV_SUPPORT) += nvedit.o
>   obj-$(CONFIG_CMD_NVEDIT_EFI) += nvedit_efi.o
>   obj-$(CONFIG_CMD_ONENAND) += onenand.o
> diff --git a/cmd/net-lwip.c b/cmd/net-lwip.c
> new file mode 100644
> index 00000000000..82edb5fd2e6
> --- /dev/null
> +++ b/cmd/net-lwip.c
> @@ -0,0 +1,13 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/* Copyright (C) 2024 Linaro Ltd. */
> +
> +#include <command.h>
> +#include <net.h>
> +
> +#if defined(CONFIG_CMD_DHCP)
> +U_BOOT_CMD(
> +        dhcp,   3,      1,      do_dhcp,
> +        "boot image via network using DHCP/TFTP protocol",
> +        "[loadAddress] [[hostIPaddr:]bootfilename]"
> +);
> +#endif
> diff --git a/common/board_r.c b/common/board_r.c
> index da0b80f24ff..6548eb8fdd5 100644
> --- a/common/board_r.c
> +++ b/common/board_r.c
> @@ -472,7 +472,7 @@ static int initr_status_led(void)
>   }
>   #endif
>
> -#ifdef CONFIG_CMD_NET
> +#if defined(CONFIG_CMD_NET) || defined(CONFIG_CMD_NET_LWIP)
>   static int initr_net(void)
>   {
>   	puts("Net:   ");
> @@ -727,7 +727,7 @@ static init_fnc_t init_sequence_r[] = {
>   #ifdef CONFIG_PCI_ENDPOINT
>   	pci_ep_init,
>   #endif
> -#ifdef CONFIG_CMD_NET
> +#if defined(CONFIG_CMD_NET) || defined(CONFIG_CMD_NET_LWIP)
>   	INIT_FUNC_WATCHDOG_RESET
>   	initr_net,
>   #endif
> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
> index efc55e45ca8..640c4218518 100644
> --- a/drivers/net/Kconfig
> +++ b/drivers/net/Kconfig
> @@ -97,7 +97,7 @@ config DSA_SANDBOX
>
>   menuconfig NETDEVICES
>   	bool "Network device support"
> -	depends on NET
> +	depends on NET || NET_LWIP
>   	select DM_ETH
>   	help
>   	  You must select Y to enable any network device support
> diff --git a/include/net-lwip.h b/include/net-lwip.h
> index f5c743b8d11..46cf6875f7e 100644
> --- a/include/net-lwip.h
> +++ b/include/net-lwip.h
> @@ -11,4 +11,6 @@ struct netif *net_lwip_new_netif_noip(void);
>   void net_lwip_remove_netif(struct netif *netif);
>   struct netif *net_lwip_get_netif(void);
>
> +int do_dhcp(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
> new file mode 100644
> index 00000000000..a56c32bfa74
> --- /dev/null
> +++ b/net-lwip/Makefile
> @@ -0,0 +1,15 @@
> +ccflags-y += -I$(srctree)/lib/lwip/lwip/src/include -I$(srctree)/lib/lwip/u-boot
> +
> +obj-$(CONFIG_$(SPL_TPL_)BOOTDEV_ETH) += ../net/eth_bootdev.o
> +obj-$(CONFIG_DM_MDIO)  += ../net/mdio-uclass.o
> +obj-$(CONFIG_DM_MDIO_MUX) += ../net/mdio-mux-uclass.o
> +obj-$(CONFIG_$(SPL_)DM_ETH) += ../net/eth_common.o
> +obj-$(CONFIG_$(SPL_)DM_ETH) += ../net/eth-uclass.o
> +obj-$(CONFIG_$(SPL_)DM_ETH) += net-lwip.o
> +obj-$(CONFIG_CMD_DHCP) += dhcp.o
> +obj-$(CONFIG_CMD_TFTPBOOT) += tftp.o
> +
> +# Disable this warning as it is triggered by:
> +# sprintf(buf, index ? "foo%d" : "foo", index)
> +# and this is intentional usage.
> +CFLAGS_eth_common.o += -Wno-format-extra-args
> diff --git a/net-lwip/dhcp.c b/net-lwip/dhcp.c
> new file mode 100644
> index 00000000000..38ea565508f
> --- /dev/null
> +++ b/net-lwip/dhcp.c
> @@ -0,0 +1,99 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/* Copyright (C) 2024 Linaro Ltd. */
> +
> +#include <command.h>
> +#include <console.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);
> +}
> +
> +int do_dhcp(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
> +{
> +	unsigned long start;
> +	struct netif *netif;
> +	struct dhcp *dhcp;
> +	bool bound;
> +
> +	netif = net_lwip_new_netif_noip();
> +	if (!netif)
> +		return CMD_RET_FAILURE;
> +
> +	start = get_timer(0);
> +	dhcp_start(netif);
> +	call_lwip_dhcp_fine_tmr(NULL);
> +
> +	/* Wait for DHCP to complete */
> +	do {
> +		eth_rx();
> +		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 (dhcp->offered_gw_addr.addr != 0)
> +		env_set("gatewayip", ip4addr_ntoa(&dhcp->offered_gw_addr));
> +	env_set("ipaddr", ip4addr_ntoa(&dhcp->offered_ip_addr));
> +	env_set("netmask", ip4addr_ntoa(&dhcp->offered_sn_mask));
> +	env_set("serverip", ip4addr_ntoa(&dhcp->server_ip_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 dhcp_run(ulong addr, const char *fname, bool autoload)
> +{
> +	char *dhcp_argv[] = {"dhcp", NULL, };
> +	struct cmd_tbl cmdtp = {};	/* dummy */
> +
> +	if (autoload) {
> +		/* Will be supported when TFTP is added */
> +		return -EOPNOTSUPP;
> +	}
> +
> +	return do_dhcp(&cmdtp, 0, 1, dhcp_argv);
> +}
> 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..39e7e51e542
> --- /dev/null
> +++ b/net-lwip/net-lwip.c
> @@ -0,0 +1,270 @@
> +// 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/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+1) * PKTSIZE_ALIGN + PKTALIGN];
> +uchar *net_rx_packets[PKTBUFSRX];
> +uchar *net_rx_packet;
> +uchar *net_tx_packet;
> +
> +static err_t low_level_output(struct netif *netif, struct pbuf *p)
> +{
> +        int err;
> +
> +	/*
> +	 * lwIP is alwys configured to use one device, the active one, so
> +	 * there is no need to use the netif parameter.
> +	 */
> +
> +        /* switch dev to active state */
> +        eth_init_state_only();
> +
> +        err = eth_send(p->payload, p->len);
> +        if (err) {
> +                log_err("eth_send error %d\n", err);
> +                return ERR_ABRT;
> +        }
> +        return ERR_OK;
> +}
> +
> +static err_t net_lwip_if_init(struct netif *netif)
> +{
> +#if LWIP_IPV4
> +	netif->output = etharp_output;
> +#endif
> +	netif->linkoutput = low_level_output;
> +	netif->mtu = 1500;
> +	netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
> +
> +	return ERR_OK;
> +}
> +
> +static void eth_init_rings(void)
> +{
> +        static bool called;
> +	int i;
> +
> +        if (called)
> +		return;
> +	called = true;
> +
> +	net_tx_packet = &net_pkt_buf[0] + (PKTALIGN - 1);
> +	net_tx_packet -= (ulong)net_tx_packet % PKTALIGN;
> +	for (i = 0; i < PKTBUFSRX; i++)
> +		net_rx_packets[i] = net_tx_packet + (i + 1) * 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[9] = { 'i', 'p', 'a' , 'd', 'd', 'r', };
> +	char maskstr[10] = { 'n', 'e', 't', 'm', 'a', 's', 'k', };
> +	char gwstr[12] = { 'g', 'a', 't', 'e', 'w', 'a', 'y', 'i', 'p', };
> +	char *env;
> +	int ret;
> +
> +	if (dev_seq(dev) > 0) {
> +		ret = snprintf(ipstr, sizeof(ipstr), "ipaddr%d", dev_seq(dev));
> +		if (ret < 0 || ret >= sizeof(ipstr))
> +			return -1;
> +		snprintf(maskstr, sizeof(maskstr), "netmask%d", dev_seq(dev));
> +		if (ret < 0 || ret >= sizeof(maskstr))
> +			return -1;
> +		snprintf(gwstr, sizeof(gwstr), "gw%d", dev_seq(dev));
> +		if (ret < 0 || ret >= sizeof(gwstr))
> +			return -1;
> +	}
> +
> +	ip4_addr_set_zero(ip);
> +	ip4_addr_set_zero(mask);
> +	ip4_addr_set_zero(gw);
> +
> +	env = env_get(ipstr);
> +	if (env)
> +		ipaddr_aton(env, ip);
> +
> +	env = env_get(maskstr);
> +	if (env)
> +		ipaddr_aton(env, mask);
> +
> +	env = env_get(gwstr);
> +	if (env)
> +		ipaddr_aton(env, gw);
> +
> +	return 0;
> +}
> +
> +static struct netif *new_netif(bool with_ip)
> +{
> +	unsigned char enetaddr[ARP_HLEN];
> +	char hwstr[MAC_ADDR_STRLEN];
> +	ip4_addr_t ip, mask, gw;
> +	struct udevice *dev;
> +	struct netif *netif;

This does not fit into the driver model.

In the EFI subsystem we want to implement network protocols like the
EFI_DHCP4_PROTOCOL.

Please, carve out functions to which we can pass a UCLASS_ETH udevice to
execute DHCP.

> +	int ret;
> +	static bool first_call = true;
> +
> +	eth_init_rings();
> +
> +	if (first_call) {
> +		if (eth_init()) {
> +			printf("eth_init() error\n");
> +			return NULL;
> +		}
> +		first_call = false;
> +	}
> +
> +	netif_remove(net_lwip_get_netif());
> +
> +	eth_set_current();
> +
> +	dev = eth_get_dev();
> +	if (!dev)
> +		return NULL;
> +
> +	ip4_addr_set_zero(&ip);
> +	ip4_addr_set_zero(&mask);
> +	ip4_addr_set_zero(&gw);
> +
> +	if (with_ip)
> +		if (get_udev_ipv4_info(dev, &ip, &mask, &gw) < 0)
> +			return NULL;
> +
> +	eth_env_get_enetaddr_by_index("eth", dev_seq(dev), 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;
> +
> +	if (!netif_add(netif, &ip, &mask, &gw, netif, 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;
> +}
> +
> +/* Configure lwIP to use the currently active network device */
> +struct netif *net_lwip_new_netif()
> +{
> +	return new_netif(true);
> +}
> +
> +struct netif *net_lwip_new_netif_noip()
> +{
> +
> +	return new_netif(false);
> +}
> +
> +void net_lwip_remove_netif(struct netif *netif)
> +{
> +	netif_remove(netif);
> +	free(netif);
> +}
> +
> +int net_init(void)
> +{
> +	net_lwip_new_netif();
> +
> +	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;
> +}
> +
> +void net_process_received_packet(uchar *in_packet, int len)

Library functions should take a udevice as an argument. Please, do not
use the concept of "active device" in these library functions.

The command line interface may implement such a concept for backwards
compatibility.

Best regards

Heinrich

> +{
> +	struct netif *netif;
> +	struct pbuf *pbuf;
> +
> +	if (len < ETHER_HDR_SIZE)
> +		return;
> +
> +#if defined(CONFIG_API) || defined(CONFIG_EFI_LOADER)
> +	if (push_packet) {
> +		(*push_packet)(in_packet, len);
> +		return;
> +	}
> +#endif
> +
> +	netif = net_lwip_get_netif();
> +	if (!netif)
> +		return;
> +
> +	pbuf = alloc_pbuf_and_copy(in_packet, len);
> +	if (!pbuf)
> +		return;
> +
> +	netif->input(pbuf, netif);
> +}
> +
> +u32_t sys_now(void)
> +{
> +	return get_timer(0);
> +}
> diff --git a/net-lwip/tftp.c b/net-lwip/tftp.c
> new file mode 100644
> index 00000000000..1fa246f55d9
> --- /dev/null
> +++ b/net-lwip/tftp.c
> @@ -0,0 +1,11 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/* Copyright (C) 2024 Linaro Ltd. */
> +
> +#include <command.h>
> +#include <net-lwip.h>
> +
> +int do_tftpb(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
> +{
> +	/* Not implemented */
> +	return CMD_RET_FAILURE;
> +}
Jerome Forissier June 19, 2024, 9:37 a.m. UTC | #2
On 6/17/24 18:54, Heinrich Schuchardt wrote:
> On 17.06.24 17:32, Jerome Forissier wrote:
>> Add what it takes to enable NETDEVICES with NET_LWIP and enable DHCP as
>> well as the dhcp command. CMD_TFTPBOOT is selected by BOOTMETH_EFI due
>> to this code having an implicit dependency on do_tftpb().
>>
>> Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org>
>> ---
>>   Makefile                |   6 +
>>   boot/Kconfig            |   3 +-
>>   cmd/Kconfig             |  26 ++++
>>   cmd/Makefile            |   4 +
>>   cmd/net-lwip.c          |  13 ++
>>   common/board_r.c        |   4 +-
>>   drivers/net/Kconfig     |   2 +-
>>   include/net-lwip.h      |   2 +
>>   net-lwip/Makefile       |  15 +++
>>   net-lwip/dhcp.c         |  99 +++++++++++++++
>>   net-lwip/eth_internal.h |  35 ++++++
>>   net-lwip/net-lwip.c     | 270 ++++++++++++++++++++++++++++++++++++++++
>>   net-lwip/tftp.c         |  11 ++
>>   13 files changed, 486 insertions(+), 4 deletions(-)
>>   create mode 100644 cmd/net-lwip.c
>>   create mode 100644 net-lwip/Makefile
>>   create mode 100644 net-lwip/dhcp.c
>>   create mode 100644 net-lwip/eth_internal.h
>>   create mode 100644 net-lwip/net-lwip.c
>>   create mode 100644 net-lwip/tftp.c
>>
>> diff --git a/Makefile b/Makefile
>> index 0fe1623c550..92a0ab770bb 100644
>> --- a/Makefile
>> +++ b/Makefile
>> @@ -862,6 +862,7 @@ libs-y += env/
>>   libs-y += lib/
>>   libs-y += fs/
>>   libs-$(CONFIG_NET) += net/
>> +libs-$(CONFIG_NET_LWIP) += net-lwip/
>>   libs-y += disk/
>>   libs-y += drivers/
>>   libs-$(CONFIG_SYS_FSL_DDR) += drivers/ddr/fsl/
>> @@ -2132,6 +2133,11 @@ etags:
>>   cscope:
>>           $(FIND) $(FINDFLAGS) $(TAG_SUBDIRS) -name '*.[chS]' -print > \
>>                           cscope.files
>> +ifdef CONFIG_NET_LWIP
>> +        echo net/eth-uclass.c net/eth_common.c net/eth_bootdev.c \
>> +             net/mdio-uclass.c net/mdio-mux-uclass.c >> \
>> +                        cscope.files
>> +endif
>>           @find $(TAG_SUBDIRS) -name '*.[chS]' -type l -print | \
>>               grep -xvf - cscope.files > cscope.files.no-symlinks; \
>>           mv cscope.files.no-symlinks cscope.files
>> diff --git a/boot/Kconfig b/boot/Kconfig
>> index 6f3096c15a6..004e69dd92a 100644
>> --- a/boot/Kconfig
>> +++ b/boot/Kconfig
>> @@ -378,7 +378,7 @@ config BOOT_DEFAULTS_CMDS
>>       select CMD_FAT
>>       select CMD_FS_GENERIC
>>       select CMD_PART if PARTITIONS
>> -    select CMD_DHCP if CMD_NET
>> +    select CMD_DHCP if CMD_NET || CMD_NET_LWIP
>>       select CMD_PING if CMD_NET
>>       select CMD_PXE if CMD_NET
>>       select CMD_BOOTI if ARM64
>> @@ -540,6 +540,7 @@ config BOOTMETH_EXTLINUX_PXE
>>   config BOOTMETH_EFILOADER
>>       bool "Bootdev support for EFI boot"
>>       depends on EFI_BINARY_EXEC
>> +    select CMD_TFTPBOOT if CMD_NET_LWIP
>>       default y
>>       help
>>         Enables support for EFI boot using bootdevs. This makes the
>> diff --git a/cmd/Kconfig b/cmd/Kconfig
>> index b026439c773..1bfa528e945 100644
>> --- a/cmd/Kconfig
>> +++ b/cmd/Kconfig
>> @@ -2084,6 +2084,32 @@ config CMD_WOL
>>
>>   endif
>>
>> +if NET_LWIP
>> +
>> +menuconfig CMD_NET_LWIP
>> +    bool "Network commands (lwIP)"
>> +    default y
>> +
>> +if CMD_NET_LWIP
>> +
>> +config CMD_DHCP
>> +    bool "dhcp"
>> +    select PROT_DHCP_LWIP
>> +    help
>> +      Boot image via network using DHCP/TFTP protocol
>> +
>> +config CMD_TFTPBOOT
>> +    bool "tftp"
>> +    select PROT_UDP_LWIP
>> +    default n
>> +    help
>> +      tftpboot - load file via network using TFTP protocol
>> +      Currently a placeholder (not implemented)
>> +
>> +endif
>> +
>> +endif
>> +
>>   menu "Misc commands"
>>
>>   config CMD_2048
>> diff --git a/cmd/Makefile b/cmd/Makefile
>> index 87133cc27a8..535b6838ca5 100644
>> --- a/cmd/Makefile
>> +++ b/cmd/Makefile
>> @@ -128,6 +128,10 @@ endif
>>   obj-$(CONFIG_CMD_MUX) += mux.o
>>   obj-$(CONFIG_CMD_NAND) += nand.o
>>   obj-$(CONFIG_CMD_NET) += net.o
>> +obj-$(CONFIG_CMD_NET_LWIP) += net-lwip.o
>> +ifdef CONFIG_CMD_NET_LWIP
>> +CFLAGS_net-lwip.o := -I$(srctree)/lib/lwip/lwip/src/include -I$(srctree)/lib/lwip/u-boot
>> +endif
>>   obj-$(CONFIG_ENV_SUPPORT) += nvedit.o
>>   obj-$(CONFIG_CMD_NVEDIT_EFI) += nvedit_efi.o
>>   obj-$(CONFIG_CMD_ONENAND) += onenand.o
>> diff --git a/cmd/net-lwip.c b/cmd/net-lwip.c
>> new file mode 100644
>> index 00000000000..82edb5fd2e6
>> --- /dev/null
>> +++ b/cmd/net-lwip.c
>> @@ -0,0 +1,13 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/* Copyright (C) 2024 Linaro Ltd. */
>> +
>> +#include <command.h>
>> +#include <net.h>
>> +
>> +#if defined(CONFIG_CMD_DHCP)
>> +U_BOOT_CMD(
>> +        dhcp,   3,      1,      do_dhcp,
>> +        "boot image via network using DHCP/TFTP protocol",
>> +        "[loadAddress] [[hostIPaddr:]bootfilename]"
>> +);
>> +#endif
>> diff --git a/common/board_r.c b/common/board_r.c
>> index da0b80f24ff..6548eb8fdd5 100644
>> --- a/common/board_r.c
>> +++ b/common/board_r.c
>> @@ -472,7 +472,7 @@ static int initr_status_led(void)
>>   }
>>   #endif
>>
>> -#ifdef CONFIG_CMD_NET
>> +#if defined(CONFIG_CMD_NET) || defined(CONFIG_CMD_NET_LWIP)
>>   static int initr_net(void)
>>   {
>>       puts("Net:   ");
>> @@ -727,7 +727,7 @@ static init_fnc_t init_sequence_r[] = {
>>   #ifdef CONFIG_PCI_ENDPOINT
>>       pci_ep_init,
>>   #endif
>> -#ifdef CONFIG_CMD_NET
>> +#if defined(CONFIG_CMD_NET) || defined(CONFIG_CMD_NET_LWIP)
>>       INIT_FUNC_WATCHDOG_RESET
>>       initr_net,
>>   #endif
>> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
>> index efc55e45ca8..640c4218518 100644
>> --- a/drivers/net/Kconfig
>> +++ b/drivers/net/Kconfig
>> @@ -97,7 +97,7 @@ config DSA_SANDBOX
>>
>>   menuconfig NETDEVICES
>>       bool "Network device support"
>> -    depends on NET
>> +    depends on NET || NET_LWIP
>>       select DM_ETH
>>       help
>>         You must select Y to enable any network device support
>> diff --git a/include/net-lwip.h b/include/net-lwip.h
>> index f5c743b8d11..46cf6875f7e 100644
>> --- a/include/net-lwip.h
>> +++ b/include/net-lwip.h
>> @@ -11,4 +11,6 @@ struct netif *net_lwip_new_netif_noip(void);
>>   void net_lwip_remove_netif(struct netif *netif);
>>   struct netif *net_lwip_get_netif(void);
>>
>> +int do_dhcp(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
>> new file mode 100644
>> index 00000000000..a56c32bfa74
>> --- /dev/null
>> +++ b/net-lwip/Makefile
>> @@ -0,0 +1,15 @@
>> +ccflags-y += -I$(srctree)/lib/lwip/lwip/src/include -I$(srctree)/lib/lwip/u-boot
>> +
>> +obj-$(CONFIG_$(SPL_TPL_)BOOTDEV_ETH) += ../net/eth_bootdev.o
>> +obj-$(CONFIG_DM_MDIO)  += ../net/mdio-uclass.o
>> +obj-$(CONFIG_DM_MDIO_MUX) += ../net/mdio-mux-uclass.o
>> +obj-$(CONFIG_$(SPL_)DM_ETH) += ../net/eth_common.o
>> +obj-$(CONFIG_$(SPL_)DM_ETH) += ../net/eth-uclass.o
>> +obj-$(CONFIG_$(SPL_)DM_ETH) += net-lwip.o
>> +obj-$(CONFIG_CMD_DHCP) += dhcp.o
>> +obj-$(CONFIG_CMD_TFTPBOOT) += tftp.o
>> +
>> +# Disable this warning as it is triggered by:
>> +# sprintf(buf, index ? "foo%d" : "foo", index)
>> +# and this is intentional usage.
>> +CFLAGS_eth_common.o += -Wno-format-extra-args
>> diff --git a/net-lwip/dhcp.c b/net-lwip/dhcp.c
>> new file mode 100644
>> index 00000000000..38ea565508f
>> --- /dev/null
>> +++ b/net-lwip/dhcp.c
>> @@ -0,0 +1,99 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/* Copyright (C) 2024 Linaro Ltd. */
>> +
>> +#include <command.h>
>> +#include <console.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);
>> +}
>> +
>> +int do_dhcp(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
>> +{
>> +    unsigned long start;
>> +    struct netif *netif;
>> +    struct dhcp *dhcp;
>> +    bool bound;
>> +
>> +    netif = net_lwip_new_netif_noip();
>> +    if (!netif)
>> +        return CMD_RET_FAILURE;
>> +
>> +    start = get_timer(0);
>> +    dhcp_start(netif);
>> +    call_lwip_dhcp_fine_tmr(NULL);
>> +
>> +    /* Wait for DHCP to complete */
>> +    do {
>> +        eth_rx();
>> +        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 (dhcp->offered_gw_addr.addr != 0)
>> +        env_set("gatewayip", ip4addr_ntoa(&dhcp->offered_gw_addr));
>> +    env_set("ipaddr", ip4addr_ntoa(&dhcp->offered_ip_addr));
>> +    env_set("netmask", ip4addr_ntoa(&dhcp->offered_sn_mask));
>> +    env_set("serverip", ip4addr_ntoa(&dhcp->server_ip_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 dhcp_run(ulong addr, const char *fname, bool autoload)
>> +{
>> +    char *dhcp_argv[] = {"dhcp", NULL, };
>> +    struct cmd_tbl cmdtp = {};    /* dummy */
>> +
>> +    if (autoload) {
>> +        /* Will be supported when TFTP is added */
>> +        return -EOPNOTSUPP;
>> +    }
>> +
>> +    return do_dhcp(&cmdtp, 0, 1, dhcp_argv);
>> +}
>> 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..39e7e51e542
>> --- /dev/null
>> +++ b/net-lwip/net-lwip.c
>> @@ -0,0 +1,270 @@
>> +// 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/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+1) * PKTSIZE_ALIGN + PKTALIGN];
>> +uchar *net_rx_packets[PKTBUFSRX];
>> +uchar *net_rx_packet;
>> +uchar *net_tx_packet;
>> +
>> +static err_t low_level_output(struct netif *netif, struct pbuf *p)
>> +{
>> +        int err;
>> +
>> +    /*
>> +     * lwIP is alwys configured to use one device, the active one, so
>> +     * there is no need to use the netif parameter.
>> +     */
>> +
>> +        /* switch dev to active state */
>> +        eth_init_state_only();
>> +
>> +        err = eth_send(p->payload, p->len);
>> +        if (err) {
>> +                log_err("eth_send error %d\n", err);
>> +                return ERR_ABRT;
>> +        }
>> +        return ERR_OK;
>> +}
>> +
>> +static err_t net_lwip_if_init(struct netif *netif)
>> +{
>> +#if LWIP_IPV4
>> +    netif->output = etharp_output;
>> +#endif
>> +    netif->linkoutput = low_level_output;
>> +    netif->mtu = 1500;
>> +    netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
>> +
>> +    return ERR_OK;
>> +}
>> +
>> +static void eth_init_rings(void)
>> +{
>> +        static bool called;
>> +    int i;
>> +
>> +        if (called)
>> +        return;
>> +    called = true;
>> +
>> +    net_tx_packet = &net_pkt_buf[0] + (PKTALIGN - 1);
>> +    net_tx_packet -= (ulong)net_tx_packet % PKTALIGN;
>> +    for (i = 0; i < PKTBUFSRX; i++)
>> +        net_rx_packets[i] = net_tx_packet + (i + 1) * 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[9] = { 'i', 'p', 'a' , 'd', 'd', 'r', };
>> +    char maskstr[10] = { 'n', 'e', 't', 'm', 'a', 's', 'k', };
>> +    char gwstr[12] = { 'g', 'a', 't', 'e', 'w', 'a', 'y', 'i', 'p', };
>> +    char *env;
>> +    int ret;
>> +
>> +    if (dev_seq(dev) > 0) {
>> +        ret = snprintf(ipstr, sizeof(ipstr), "ipaddr%d", dev_seq(dev));
>> +        if (ret < 0 || ret >= sizeof(ipstr))
>> +            return -1;
>> +        snprintf(maskstr, sizeof(maskstr), "netmask%d", dev_seq(dev));
>> +        if (ret < 0 || ret >= sizeof(maskstr))
>> +            return -1;
>> +        snprintf(gwstr, sizeof(gwstr), "gw%d", dev_seq(dev));
>> +        if (ret < 0 || ret >= sizeof(gwstr))
>> +            return -1;
>> +    }
>> +
>> +    ip4_addr_set_zero(ip);
>> +    ip4_addr_set_zero(mask);
>> +    ip4_addr_set_zero(gw);
>> +
>> +    env = env_get(ipstr);
>> +    if (env)
>> +        ipaddr_aton(env, ip);
>> +
>> +    env = env_get(maskstr);
>> +    if (env)
>> +        ipaddr_aton(env, mask);
>> +
>> +    env = env_get(gwstr);
>> +    if (env)
>> +        ipaddr_aton(env, gw);
>> +
>> +    return 0;
>> +}
>> +
>> +static struct netif *new_netif(bool with_ip)
>> +{
>> +    unsigned char enetaddr[ARP_HLEN];
>> +    char hwstr[MAC_ADDR_STRLEN];
>> +    ip4_addr_t ip, mask, gw;
>> +    struct udevice *dev;
>> +    struct netif *netif;
> 
> This does not fit into the driver model.
> 
> In the EFI subsystem we want to implement network protocols like the
> EFI_DHCP4_PROTOCOL.
> 
> Please, carve out functions to which we can pass a UCLASS_ETH udevice to
> execute DHCP.

v6 will have:

static struct netif *new_netif(struct udevice *udev, bool with_ip)
struct netif *net_lwip_new_netif(struct udevice *udev)
struct netif *net_lwip_new_netif_noip(struct udevice *udev)

static int dhcp_loop(struct udevice *udev)

> 
>> +    int ret;
>> +    static bool first_call = true;
>> +
>> +    eth_init_rings();
>> +
>> +    if (first_call) {
>> +        if (eth_init()) {
>> +            printf("eth_init() error\n");
>> +            return NULL;
>> +        }
>> +        first_call = false;
>> +    }
>> +
>> +    netif_remove(net_lwip_get_netif());
>> +
>> +    eth_set_current();
>> +
>> +    dev = eth_get_dev();
>> +    if (!dev)
>> +        return NULL;
>> +
>> +    ip4_addr_set_zero(&ip);
>> +    ip4_addr_set_zero(&mask);
>> +    ip4_addr_set_zero(&gw);
>> +
>> +    if (with_ip)
>> +        if (get_udev_ipv4_info(dev, &ip, &mask, &gw) < 0)
>> +            return NULL;
>> +
>> +    eth_env_get_enetaddr_by_index("eth", dev_seq(dev), 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;
>> +
>> +    if (!netif_add(netif, &ip, &mask, &gw, netif, 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;
>> +}
>> +
>> +/* Configure lwIP to use the currently active network device */
>> +struct netif *net_lwip_new_netif()
>> +{
>> +    return new_netif(true);
>> +}
>> +
>> +struct netif *net_lwip_new_netif_noip()
>> +{
>> +
>> +    return new_netif(false);
>> +}
>> +
>> +void net_lwip_remove_netif(struct netif *netif)
>> +{
>> +    netif_remove(netif);
>> +    free(netif);
>> +}
>> +
>> +int net_init(void)
>> +{
>> +    net_lwip_new_netif();
>> +
>> +    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;
>> +}
>> +
>> +void net_process_received_packet(uchar *in_packet, int len)
> 
> Library functions should take a udevice as an argument. Please, do not
> use the concept of "active device" in these library functions.

OK, I have unified the naming in v6 and all functions will take a udevice:

static int dhcp_loop(struct udevice *udev)
static int dns_loop(struct udevice *udev, const char *name, const char *var)
static int ping_loop(struct udevice *udev, const ip_addr_t* addr)
static int tftp_loop(struct udevice *udev, ulong addr, char *fname,
		     ip_addr_t srvip)
static int wget_loop(struct udevice *udev, ulong dst_addr, char *uri)

> 
> The command line interface may implement such a concept for backwards
> compatibility.

Sure.

Thanks,
Ilias Apalodimas June 19, 2024, 10:07 a.m. UTC | #3
[...]


> >> +
> >> +static struct netif *new_netif(bool with_ip)
> >> +{
> >> +    unsigned char enetaddr[ARP_HLEN];
> >> +    char hwstr[MAC_ADDR_STRLEN];
> >> +    ip4_addr_t ip, mask, gw;
> >> +    struct udevice *dev;
> >> +    struct netif *netif;
> >
> > This does not fit into the driver model.
> >
> > In the EFI subsystem we want to implement network protocols like the
> > EFI_DHCP4_PROTOCOL.
> >
> > Please, carve out functions to which we can pass a UCLASS_ETH udevice to
> > execute DHCP.
>
> v6 will have:
>
> static struct netif *new_netif(struct udevice *udev, bool with_ip)
> struct netif *net_lwip_new_netif(struct udevice *udev)
> struct netif *net_lwip_new_netif_noip(struct udevice *udev)

Heinrich, any other EFI protocols you have in mind that would require
similar tweaking? e.g the EFI_TCP4 and HTTP protocols?


>
> static int dhcp_loop(struct udevice *udev)
>
> >
> >> +    int ret;
> >> +    static bool first_call = true;
> >> +
> >> +    eth_init_rings();
> >> +
> >> +    if (first_call) {
> >> +        if (eth_init()) {
> >> +            printf("eth_init() error\n");
> >> +            return NULL;
> >> +        }
> >> +        first_call = false;
> >> +    }
> >> +
> >> +    netif_remove(net_lwip_get_netif());
> >> +
> >> +    eth_set_current();
> >> +
> >> +    dev = eth_get_dev();
> >> +    if (!dev)
> >> +        return NULL;
> >> +
> >> +    ip4_addr_set_zero(&ip);
> >> +    ip4_addr_set_zero(&mask);
> >> +    ip4_addr_set_zero(&gw);
> >> +
> >> +    if (with_ip)
> >> +        if (get_udev_ipv4_info(dev, &ip, &mask, &gw) < 0)
> >> +            return NULL;
> >> +
> >> +    eth_env_get_enetaddr_by_index("eth", dev_seq(dev), 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;
> >> +
> >> +    if (!netif_add(netif, &ip, &mask, &gw, netif, 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;
> >> +}
> >> +
> >> +/* Configure lwIP to use the currently active network device */
> >> +struct netif *net_lwip_new_netif()
> >> +{
> >> +    return new_netif(true);
> >> +}
> >> +
> >> +struct netif *net_lwip_new_netif_noip()
> >> +{
> >> +
> >> +    return new_netif(false);
> >> +}
> >> +
> >> +void net_lwip_remove_netif(struct netif *netif)
> >> +{
> >> +    netif_remove(netif);
> >> +    free(netif);
> >> +}
> >> +
> >> +int net_init(void)
> >> +{
> >> +    net_lwip_new_netif();
> >> +
> >> +    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;
> >> +}
> >> +
> >> +void net_process_received_packet(uchar *in_packet, int len)
> >
> > Library functions should take a udevice as an argument. Please, do not
> > use the concept of "active device" in these library functions.
>
> OK, I have unified the naming in v6 and all functions will take a udevice:
>
> static int dhcp_loop(struct udevice *udev)
> static int dns_loop(struct udevice *udev, const char *name, const char *var)
> static int ping_loop(struct udevice *udev, const ip_addr_t* addr)
> static int tftp_loop(struct udevice *udev, ulong addr, char *fname,
>                      ip_addr_t srvip)
> static int wget_loop(struct udevice *udev, ulong dst_addr, char *uri)
>
> >
> > The command line interface may implement such a concept for backwards
> > compatibility.
>
> Sure.
>
> Thanks,
> --
> Jerome
>
> >
> > Best regards
> >
> > Heinrich
> >
> >> +{
> >> +    struct netif *netif;
> >> +    struct pbuf *pbuf;
> >> +
> >> +    if (len < ETHER_HDR_SIZE)
> >> +        return;
> >> +
> >> +#if defined(CONFIG_API) || defined(CONFIG_EFI_LOADER)
> >> +    if (push_packet) {
> >> +        (*push_packet)(in_packet, len);
> >> +        return;
> >> +    }
> >> +#endif
> >> +
> >> +    netif = net_lwip_get_netif();
> >> +    if (!netif)
> >> +        return;
> >> +
> >> +    pbuf = alloc_pbuf_and_copy(in_packet, len);
> >> +    if (!pbuf)
> >> +        return;
> >> +
> >> +    netif->input(pbuf, netif);
> >> +}
> >> +
> >> +u32_t sys_now(void)
> >> +{
> >> +    return get_timer(0);
> >> +}
> >> diff --git a/net-lwip/tftp.c b/net-lwip/tftp.c
> >> new file mode 100644
> >> index 00000000000..1fa246f55d9
> >> --- /dev/null
> >> +++ b/net-lwip/tftp.c
> >> @@ -0,0 +1,11 @@
> >> +// SPDX-License-Identifier: GPL-2.0+
> >> +/* Copyright (C) 2024 Linaro Ltd. */
> >> +
> >> +#include <command.h>
> >> +#include <net-lwip.h>
> >> +
> >> +int do_tftpb(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
> >> +{
> >> +    /* Not implemented */
> >> +    return CMD_RET_FAILURE;
> >> +}
> >
diff mbox series

Patch

diff --git a/Makefile b/Makefile
index 0fe1623c550..92a0ab770bb 100644
--- a/Makefile
+++ b/Makefile
@@ -862,6 +862,7 @@  libs-y += env/
 libs-y += lib/
 libs-y += fs/
 libs-$(CONFIG_NET) += net/
+libs-$(CONFIG_NET_LWIP) += net-lwip/
 libs-y += disk/
 libs-y += drivers/
 libs-$(CONFIG_SYS_FSL_DDR) += drivers/ddr/fsl/
@@ -2132,6 +2133,11 @@  etags:
 cscope:
 		$(FIND) $(FINDFLAGS) $(TAG_SUBDIRS) -name '*.[chS]' -print > \
 						cscope.files
+ifdef CONFIG_NET_LWIP
+		echo net/eth-uclass.c net/eth_common.c net/eth_bootdev.c \
+		     net/mdio-uclass.c net/mdio-mux-uclass.c >> \
+						cscope.files
+endif
 		@find $(TAG_SUBDIRS) -name '*.[chS]' -type l -print | \
 			grep -xvf - cscope.files > cscope.files.no-symlinks; \
 		mv cscope.files.no-symlinks cscope.files
diff --git a/boot/Kconfig b/boot/Kconfig
index 6f3096c15a6..004e69dd92a 100644
--- a/boot/Kconfig
+++ b/boot/Kconfig
@@ -378,7 +378,7 @@  config BOOT_DEFAULTS_CMDS
 	select CMD_FAT
 	select CMD_FS_GENERIC
 	select CMD_PART if PARTITIONS
-	select CMD_DHCP if CMD_NET
+	select CMD_DHCP if CMD_NET || CMD_NET_LWIP
 	select CMD_PING if CMD_NET
 	select CMD_PXE if CMD_NET
 	select CMD_BOOTI if ARM64
@@ -540,6 +540,7 @@  config BOOTMETH_EXTLINUX_PXE
 config BOOTMETH_EFILOADER
 	bool "Bootdev support for EFI boot"
 	depends on EFI_BINARY_EXEC
+	select CMD_TFTPBOOT if CMD_NET_LWIP
 	default y
 	help
 	  Enables support for EFI boot using bootdevs. This makes the
diff --git a/cmd/Kconfig b/cmd/Kconfig
index b026439c773..1bfa528e945 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -2084,6 +2084,32 @@  config CMD_WOL
 
 endif
 
+if NET_LWIP
+
+menuconfig CMD_NET_LWIP
+	bool "Network commands (lwIP)"
+	default y
+
+if CMD_NET_LWIP
+
+config CMD_DHCP
+	bool "dhcp"
+	select PROT_DHCP_LWIP
+	help
+	  Boot image via network using DHCP/TFTP protocol
+
+config CMD_TFTPBOOT
+	bool "tftp"
+	select PROT_UDP_LWIP
+	default n
+	help
+	  tftpboot - load file via network using TFTP protocol
+	  Currently a placeholder (not implemented)
+
+endif
+
+endif
+
 menu "Misc commands"
 
 config CMD_2048
diff --git a/cmd/Makefile b/cmd/Makefile
index 87133cc27a8..535b6838ca5 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -128,6 +128,10 @@  endif
 obj-$(CONFIG_CMD_MUX) += mux.o
 obj-$(CONFIG_CMD_NAND) += nand.o
 obj-$(CONFIG_CMD_NET) += net.o
+obj-$(CONFIG_CMD_NET_LWIP) += net-lwip.o
+ifdef CONFIG_CMD_NET_LWIP
+CFLAGS_net-lwip.o := -I$(srctree)/lib/lwip/lwip/src/include -I$(srctree)/lib/lwip/u-boot
+endif
 obj-$(CONFIG_ENV_SUPPORT) += nvedit.o
 obj-$(CONFIG_CMD_NVEDIT_EFI) += nvedit_efi.o
 obj-$(CONFIG_CMD_ONENAND) += onenand.o
diff --git a/cmd/net-lwip.c b/cmd/net-lwip.c
new file mode 100644
index 00000000000..82edb5fd2e6
--- /dev/null
+++ b/cmd/net-lwip.c
@@ -0,0 +1,13 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (C) 2024 Linaro Ltd. */
+
+#include <command.h>
+#include <net.h>
+
+#if defined(CONFIG_CMD_DHCP)
+U_BOOT_CMD(
+        dhcp,   3,      1,      do_dhcp,
+        "boot image via network using DHCP/TFTP protocol",
+        "[loadAddress] [[hostIPaddr:]bootfilename]"
+);
+#endif
diff --git a/common/board_r.c b/common/board_r.c
index da0b80f24ff..6548eb8fdd5 100644
--- a/common/board_r.c
+++ b/common/board_r.c
@@ -472,7 +472,7 @@  static int initr_status_led(void)
 }
 #endif
 
-#ifdef CONFIG_CMD_NET
+#if defined(CONFIG_CMD_NET) || defined(CONFIG_CMD_NET_LWIP)
 static int initr_net(void)
 {
 	puts("Net:   ");
@@ -727,7 +727,7 @@  static init_fnc_t init_sequence_r[] = {
 #ifdef CONFIG_PCI_ENDPOINT
 	pci_ep_init,
 #endif
-#ifdef CONFIG_CMD_NET
+#if defined(CONFIG_CMD_NET) || defined(CONFIG_CMD_NET_LWIP)
 	INIT_FUNC_WATCHDOG_RESET
 	initr_net,
 #endif
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index efc55e45ca8..640c4218518 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -97,7 +97,7 @@  config DSA_SANDBOX
 
 menuconfig NETDEVICES
 	bool "Network device support"
-	depends on NET
+	depends on NET || NET_LWIP
 	select DM_ETH
 	help
 	  You must select Y to enable any network device support
diff --git a/include/net-lwip.h b/include/net-lwip.h
index f5c743b8d11..46cf6875f7e 100644
--- a/include/net-lwip.h
+++ b/include/net-lwip.h
@@ -11,4 +11,6 @@  struct netif *net_lwip_new_netif_noip(void);
 void net_lwip_remove_netif(struct netif *netif);
 struct netif *net_lwip_get_netif(void);
 
+int do_dhcp(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
new file mode 100644
index 00000000000..a56c32bfa74
--- /dev/null
+++ b/net-lwip/Makefile
@@ -0,0 +1,15 @@ 
+ccflags-y += -I$(srctree)/lib/lwip/lwip/src/include -I$(srctree)/lib/lwip/u-boot
+
+obj-$(CONFIG_$(SPL_TPL_)BOOTDEV_ETH) += ../net/eth_bootdev.o
+obj-$(CONFIG_DM_MDIO)  += ../net/mdio-uclass.o
+obj-$(CONFIG_DM_MDIO_MUX) += ../net/mdio-mux-uclass.o
+obj-$(CONFIG_$(SPL_)DM_ETH) += ../net/eth_common.o
+obj-$(CONFIG_$(SPL_)DM_ETH) += ../net/eth-uclass.o
+obj-$(CONFIG_$(SPL_)DM_ETH) += net-lwip.o
+obj-$(CONFIG_CMD_DHCP) += dhcp.o
+obj-$(CONFIG_CMD_TFTPBOOT) += tftp.o
+
+# Disable this warning as it is triggered by:
+# sprintf(buf, index ? "foo%d" : "foo", index)
+# and this is intentional usage.
+CFLAGS_eth_common.o += -Wno-format-extra-args
diff --git a/net-lwip/dhcp.c b/net-lwip/dhcp.c
new file mode 100644
index 00000000000..38ea565508f
--- /dev/null
+++ b/net-lwip/dhcp.c
@@ -0,0 +1,99 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (C) 2024 Linaro Ltd. */
+
+#include <command.h>
+#include <console.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);
+}
+
+int do_dhcp(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+	unsigned long start;
+	struct netif *netif;
+	struct dhcp *dhcp;
+	bool bound;
+
+	netif = net_lwip_new_netif_noip();
+	if (!netif)
+		return CMD_RET_FAILURE;
+
+	start = get_timer(0);
+	dhcp_start(netif);
+	call_lwip_dhcp_fine_tmr(NULL);
+
+	/* Wait for DHCP to complete */
+	do {
+		eth_rx();
+		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 (dhcp->offered_gw_addr.addr != 0)
+		env_set("gatewayip", ip4addr_ntoa(&dhcp->offered_gw_addr));
+	env_set("ipaddr", ip4addr_ntoa(&dhcp->offered_ip_addr));
+	env_set("netmask", ip4addr_ntoa(&dhcp->offered_sn_mask));
+	env_set("serverip", ip4addr_ntoa(&dhcp->server_ip_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 dhcp_run(ulong addr, const char *fname, bool autoload)
+{
+	char *dhcp_argv[] = {"dhcp", NULL, };
+	struct cmd_tbl cmdtp = {};	/* dummy */
+
+	if (autoload) {
+		/* Will be supported when TFTP is added */
+		return -EOPNOTSUPP;
+	}
+
+	return do_dhcp(&cmdtp, 0, 1, dhcp_argv);
+}
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..39e7e51e542
--- /dev/null
+++ b/net-lwip/net-lwip.c
@@ -0,0 +1,270 @@ 
+// 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/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+1) * PKTSIZE_ALIGN + PKTALIGN];
+uchar *net_rx_packets[PKTBUFSRX];
+uchar *net_rx_packet;
+uchar *net_tx_packet;
+
+static err_t low_level_output(struct netif *netif, struct pbuf *p)
+{
+        int err;
+
+	/*
+	 * lwIP is alwys configured to use one device, the active one, so
+	 * there is no need to use the netif parameter.
+	 */
+
+        /* switch dev to active state */
+        eth_init_state_only();
+
+        err = eth_send(p->payload, p->len);
+        if (err) {
+                log_err("eth_send error %d\n", err);
+                return ERR_ABRT;
+        }
+        return ERR_OK;
+}
+
+static err_t net_lwip_if_init(struct netif *netif)
+{
+#if LWIP_IPV4
+	netif->output = etharp_output;
+#endif
+	netif->linkoutput = low_level_output;
+	netif->mtu = 1500;
+	netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
+
+	return ERR_OK;
+}
+
+static void eth_init_rings(void)
+{
+        static bool called;
+	int i;
+
+        if (called)
+		return;
+	called = true;
+
+	net_tx_packet = &net_pkt_buf[0] + (PKTALIGN - 1);
+	net_tx_packet -= (ulong)net_tx_packet % PKTALIGN;
+	for (i = 0; i < PKTBUFSRX; i++)
+		net_rx_packets[i] = net_tx_packet + (i + 1) * 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[9] = { 'i', 'p', 'a' , 'd', 'd', 'r', };
+	char maskstr[10] = { 'n', 'e', 't', 'm', 'a', 's', 'k', };
+	char gwstr[12] = { 'g', 'a', 't', 'e', 'w', 'a', 'y', 'i', 'p', };
+	char *env;
+	int ret;
+
+	if (dev_seq(dev) > 0) {
+		ret = snprintf(ipstr, sizeof(ipstr), "ipaddr%d", dev_seq(dev));
+		if (ret < 0 || ret >= sizeof(ipstr))
+			return -1;
+		snprintf(maskstr, sizeof(maskstr), "netmask%d", dev_seq(dev));
+		if (ret < 0 || ret >= sizeof(maskstr))
+			return -1;
+		snprintf(gwstr, sizeof(gwstr), "gw%d", dev_seq(dev));
+		if (ret < 0 || ret >= sizeof(gwstr))
+			return -1;
+	}
+
+	ip4_addr_set_zero(ip);
+	ip4_addr_set_zero(mask);
+	ip4_addr_set_zero(gw);
+
+	env = env_get(ipstr);
+	if (env)
+		ipaddr_aton(env, ip);
+
+	env = env_get(maskstr);
+	if (env)
+		ipaddr_aton(env, mask);
+
+	env = env_get(gwstr);
+	if (env)
+		ipaddr_aton(env, gw);
+
+	return 0;
+}
+
+static struct netif *new_netif(bool with_ip)
+{
+	unsigned char enetaddr[ARP_HLEN];
+	char hwstr[MAC_ADDR_STRLEN];
+	ip4_addr_t ip, mask, gw;
+	struct udevice *dev;
+	struct netif *netif;
+	int ret;
+	static bool first_call = true;
+
+	eth_init_rings();
+
+	if (first_call) {
+		if (eth_init()) {
+			printf("eth_init() error\n");
+			return NULL;
+		}
+		first_call = false;
+	}
+
+	netif_remove(net_lwip_get_netif());
+
+	eth_set_current();
+
+	dev = eth_get_dev();
+	if (!dev)
+		return NULL;
+
+	ip4_addr_set_zero(&ip);
+	ip4_addr_set_zero(&mask);
+	ip4_addr_set_zero(&gw);
+
+	if (with_ip)
+		if (get_udev_ipv4_info(dev, &ip, &mask, &gw) < 0)
+			return NULL;
+
+	eth_env_get_enetaddr_by_index("eth", dev_seq(dev), 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;
+
+	if (!netif_add(netif, &ip, &mask, &gw, netif, 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;
+}
+
+/* Configure lwIP to use the currently active network device */
+struct netif *net_lwip_new_netif()
+{
+	return new_netif(true);
+}
+
+struct netif *net_lwip_new_netif_noip()
+{
+
+	return new_netif(false);
+}
+
+void net_lwip_remove_netif(struct netif *netif)
+{
+	netif_remove(netif);
+	free(netif);
+}
+
+int net_init(void)
+{
+	net_lwip_new_netif();
+
+	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;
+}
+
+void net_process_received_packet(uchar *in_packet, int len)
+{
+	struct netif *netif;
+	struct pbuf *pbuf;
+
+	if (len < ETHER_HDR_SIZE)
+		return;
+
+#if defined(CONFIG_API) || defined(CONFIG_EFI_LOADER)
+	if (push_packet) {
+		(*push_packet)(in_packet, len);
+		return;
+	}
+#endif
+
+	netif = net_lwip_get_netif();
+	if (!netif)
+		return;
+
+	pbuf = alloc_pbuf_and_copy(in_packet, len);
+	if (!pbuf)
+		return;
+
+	netif->input(pbuf, netif);
+}
+
+u32_t sys_now(void)
+{
+	return get_timer(0);
+}
diff --git a/net-lwip/tftp.c b/net-lwip/tftp.c
new file mode 100644
index 00000000000..1fa246f55d9
--- /dev/null
+++ b/net-lwip/tftp.c
@@ -0,0 +1,11 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (C) 2024 Linaro Ltd. */
+
+#include <command.h>
+#include <net-lwip.h>
+
+int do_tftpb(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+	/* Not implemented */
+	return CMD_RET_FAILURE;
+}