
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,