aboutsummaryrefslogtreecommitdiffstats
path: root/roms/u-boot/net
diff options
context:
space:
mode:
authorAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
committerAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
commitaf1a266670d040d2f4083ff309d732d648afba2a (patch)
tree2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/u-boot/net
parente02cda008591317b1625707ff8e115a4841aa889 (diff)
Add submodule dependency filesHEADmaster
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/u-boot/net')
-rw-r--r--roms/u-boot/net/Kconfig91
-rw-r--r--roms/u-boot/net/Makefile36
-rw-r--r--roms/u-boot/net/arp.c245
-rw-r--r--roms/u-boot/net/arp.h32
-rw-r--r--roms/u-boot/net/bootp.c1137
-rw-r--r--roms/u-boot/net/bootp.h93
-rw-r--r--roms/u-boot/net/cdp.c363
-rw-r--r--roms/u-boot/net/cdp.h22
-rw-r--r--roms/u-boot/net/dns.c219
-rw-r--r--roms/u-boot/net/dns.h35
-rw-r--r--roms/u-boot/net/dsa-uclass.c463
-rw-r--r--roms/u-boot/net/eth-uclass.c618
-rw-r--r--roms/u-boot/net/eth_common.c140
-rw-r--r--roms/u-boot/net/eth_internal.h39
-rw-r--r--roms/u-boot/net/eth_legacy.c424
-rw-r--r--roms/u-boot/net/fastboot.c322
-rw-r--r--roms/u-boot/net/link_local.c349
-rw-r--r--roms/u-boot/net/link_local.h24
-rw-r--r--roms/u-boot/net/mdio-mux-uclass.c233
-rw-r--r--roms/u-boot/net/mdio-uclass.c230
-rw-r--r--roms/u-boot/net/net.c1602
-rw-r--r--roms/u-boot/net/net_rand.h60
-rw-r--r--roms/u-boot/net/nfs.c934
-rw-r--r--roms/u-boot/net/nfs.h87
-rw-r--r--roms/u-boot/net/pcap.c156
-rw-r--r--roms/u-boot/net/ping.c119
-rw-r--r--roms/u-boot/net/ping.h32
-rw-r--r--roms/u-boot/net/rarp.c99
-rw-r--r--roms/u-boot/net/rarp.h28
-rw-r--r--roms/u-boot/net/sntp.c124
-rw-r--r--roms/u-boot/net/tftp.c931
-rw-r--r--roms/u-boot/net/udp.c46
-rw-r--r--roms/u-boot/net/wol.c96
-rw-r--r--roms/u-boot/net/wol.h65
34 files changed, 9494 insertions, 0 deletions
diff --git a/roms/u-boot/net/Kconfig b/roms/u-boot/net/Kconfig
new file mode 100644
index 000000000..c4b4dae06
--- /dev/null
+++ b/roms/u-boot/net/Kconfig
@@ -0,0 +1,91 @@
+#
+# Network configuration
+#
+
+menuconfig NET
+ bool "Networking support"
+ default y
+
+if NET
+
+config PROT_UDP
+ bool "Enable generic udp framework"
+ help
+ Enable a generic udp framework that allows defining a custom
+ handler for udp protocol.
+
+config BOOTP_SEND_HOSTNAME
+ bool "Send hostname to DNS server"
+ help
+ Some DHCP servers are capable to do a dynamic update of a
+ DNS server. To do this, they need the hostname of the DHCP
+ requester.
+ If CONFIG_BOOTP_SEND_HOSTNAME is defined, the content
+ 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.
+ A new MAC address will be generated on every boot and it will
+ not be added to the environment.
+
+config NETCONSOLE
+ bool "NetConsole support"
+ help
+ Support the 'nc' input/output device for networked console.
+ See README.NetConsole for details.
+
+config IP_DEFRAG
+ bool "Support IP datagram reassembly"
+ default n
+ help
+ Selecting this will enable IP datagram reassembly according
+ to the algorithm in RFC815.
+
+config NET_MAXDEFRAG
+ int "Size of buffer used for IP datagram reassembly"
+ depends on IP_DEFRAG
+ default 16384
+ range 1024 65536
+ help
+ This defines the size of the statically allocated buffer
+ used for reassembly, and thus an upper bound for the size of
+ IP datagrams that can be received.
+
+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_WINDOWSIZE
+ int "TFTP window size"
+ default 1
+ help
+ Default TFTP window size.
+ RFC7440 defines an optional window size of transmits,
+ before an ack response is required.
+ The default TFTP implementation implies a window size of 1.
+
+config SERVERIP_FROM_PROXYDHCP
+ bool "Get serverip value from Proxy DHCP response"
+ help
+ Allows bootfile config to be fetched from Proxy DHCP server
+ while IP is obtained from main DHCP server.
+
+config SERVERIP_FROM_PROXYDHCP_DELAY_MS
+ int "# of additional milliseconds to wait for ProxyDHCP response"
+ default 100
+ help
+ Amount of additional time to wait for ProxyDHCP response after
+ receiving response from main DHCP server. Has no effect if
+ SERVERIP_FROM_PROXYDHCP is false.
+
+endif # if NET
diff --git a/roms/u-boot/net/Makefile b/roms/u-boot/net/Makefile
new file mode 100644
index 000000000..fb3eba840
--- /dev/null
+++ b/roms/u-boot/net/Makefile
@@ -0,0 +1,36 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2000-2006
+# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+
+#ccflags-y += -DDEBUG
+
+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
+ifdef CONFIG_DM_ETH
+obj-$(CONFIG_NET) += eth-uclass.o
+else
+obj-$(CONFIG_NET) += eth_legacy.o
+endif
+obj-$(CONFIG_DM_MDIO) += mdio-uclass.o
+obj-$(CONFIG_DM_MDIO_MUX) += mdio-mux-uclass.o
+obj-$(CONFIG_NET) += eth_common.o
+obj-$(CONFIG_CMD_LINK_LOCAL) += link_local.o
+obj-$(CONFIG_NET) += net.o
+obj-$(CONFIG_CMD_NFS) += nfs.o
+obj-$(CONFIG_CMD_PING) += ping.o
+obj-$(CONFIG_CMD_PCAP) += pcap.o
+obj-$(CONFIG_CMD_RARP) += rarp.o
+obj-$(CONFIG_CMD_SNTP) += sntp.o
+obj-$(CONFIG_CMD_TFTPBOOT) += tftp.o
+obj-$(CONFIG_UDP_FUNCTION_FASTBOOT) += fastboot.o
+obj-$(CONFIG_CMD_WOL) += wol.o
+obj-$(CONFIG_PROT_UDP) += udp.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/roms/u-boot/net/arp.c b/roms/u-boot/net/arp.c
new file mode 100644
index 000000000..1d06ed257
--- /dev/null
+++ b/roms/u-boot/net/arp.c
@@ -0,0 +1,245 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copied from Linux Monitor (LiMon) - Networking.
+ *
+ * Copyright 1994 - 2000 Neil Russell.
+ * (See License)
+ * Copyright 2000 Roland Borde
+ * Copyright 2000 Paolo Scaffardi
+ * Copyright 2000-2002 Wolfgang Denk, wd@denx.de
+ */
+
+#include <common.h>
+#include <env.h>
+#include <log.h>
+#include <net.h>
+#include <linux/delay.h>
+
+#include "arp.h"
+
+#ifndef CONFIG_ARP_TIMEOUT
+/* Milliseconds before trying ARP again */
+# define ARP_TIMEOUT 5000UL
+#else
+# define ARP_TIMEOUT CONFIG_ARP_TIMEOUT
+#endif
+
+
+#ifndef CONFIG_NET_RETRY_COUNT
+# define ARP_TIMEOUT_COUNT 5 /* # of timeouts before giving up */
+#else
+# define ARP_TIMEOUT_COUNT CONFIG_NET_RETRY_COUNT
+#endif
+
+struct in_addr net_arp_wait_packet_ip;
+static struct in_addr net_arp_wait_reply_ip;
+/* MAC address of waiting packet's destination */
+uchar *arp_wait_packet_ethaddr;
+int arp_wait_tx_packet_size;
+ulong arp_wait_timer_start;
+int arp_wait_try;
+uchar *arp_tx_packet; /* THE ARP transmit packet */
+static uchar arp_tx_packet_buf[PKTSIZE_ALIGN + PKTALIGN];
+
+void arp_init(void)
+{
+ /* XXX problem with bss workaround */
+ arp_wait_packet_ethaddr = NULL;
+ net_arp_wait_packet_ip.s_addr = 0;
+ net_arp_wait_reply_ip.s_addr = 0;
+ arp_wait_tx_packet_size = 0;
+ arp_tx_packet = &arp_tx_packet_buf[0] + (PKTALIGN - 1);
+ arp_tx_packet -= (ulong)arp_tx_packet % PKTALIGN;
+}
+
+void arp_raw_request(struct in_addr source_ip, const uchar *target_ethaddr,
+ struct in_addr target_ip)
+{
+ uchar *pkt;
+ struct arp_hdr *arp;
+ int eth_hdr_size;
+
+ debug_cond(DEBUG_DEV_PKT, "ARP broadcast %d\n", arp_wait_try);
+
+ pkt = arp_tx_packet;
+
+ eth_hdr_size = net_set_ether(pkt, net_bcast_ethaddr, PROT_ARP);
+ pkt += eth_hdr_size;
+
+ arp = (struct arp_hdr *)pkt;
+
+ arp->ar_hrd = htons(ARP_ETHER);
+ arp->ar_pro = htons(PROT_IP);
+ arp->ar_hln = ARP_HLEN;
+ arp->ar_pln = ARP_PLEN;
+ arp->ar_op = htons(ARPOP_REQUEST);
+
+ memcpy(&arp->ar_sha, net_ethaddr, ARP_HLEN); /* source ET addr */
+ net_write_ip(&arp->ar_spa, source_ip); /* source IP addr */
+ memcpy(&arp->ar_tha, target_ethaddr, ARP_HLEN); /* target ET addr */
+ net_write_ip(&arp->ar_tpa, target_ip); /* target IP addr */
+
+ net_send_packet(arp_tx_packet, eth_hdr_size + ARP_HDR_SIZE);
+}
+
+void arp_request(void)
+{
+ if ((net_arp_wait_packet_ip.s_addr & net_netmask.s_addr) !=
+ (net_ip.s_addr & net_netmask.s_addr)) {
+ if (net_gateway.s_addr == 0) {
+ puts("## Warning: gatewayip needed but not set\n");
+ net_arp_wait_reply_ip = net_arp_wait_packet_ip;
+ } else {
+ net_arp_wait_reply_ip = net_gateway;
+ }
+ } else {
+ net_arp_wait_reply_ip = net_arp_wait_packet_ip;
+ }
+
+ arp_raw_request(net_ip, net_null_ethaddr, net_arp_wait_reply_ip);
+}
+
+int arp_timeout_check(void)
+{
+ ulong t;
+
+ if (!arp_is_waiting())
+ return 0;
+
+ t = get_timer(0);
+
+ /* check for arp timeout */
+ if ((t - arp_wait_timer_start) > ARP_TIMEOUT) {
+ arp_wait_try++;
+
+ if (arp_wait_try >= ARP_TIMEOUT_COUNT) {
+ puts("\nARP Retry count exceeded; starting again\n");
+ arp_wait_try = 0;
+ net_set_state(NETLOOP_FAIL);
+ } else {
+ arp_wait_timer_start = t;
+ arp_request();
+ }
+ }
+ return 1;
+}
+
+void arp_receive(struct ethernet_hdr *et, struct ip_udp_hdr *ip, int len)
+{
+ struct arp_hdr *arp;
+ struct in_addr reply_ip_addr;
+ int eth_hdr_size;
+ uchar *tx_packet;
+
+ /*
+ * We have to deal with two types of ARP packets:
+ * - REQUEST packets will be answered by sending our
+ * IP address - if we know it.
+ * - REPLY packates are expected only after we asked
+ * for the TFTP server's or the gateway's ethernet
+ * address; so if we receive such a packet, we set
+ * the server ethernet address
+ */
+ debug_cond(DEBUG_NET_PKT, "Got ARP\n");
+
+ arp = (struct arp_hdr *)ip;
+ if (len < ARP_HDR_SIZE) {
+ printf("bad length %d < %d\n", len, ARP_HDR_SIZE);
+ return;
+ }
+ if (ntohs(arp->ar_hrd) != ARP_ETHER)
+ return;
+ if (ntohs(arp->ar_pro) != PROT_IP)
+ return;
+ if (arp->ar_hln != ARP_HLEN)
+ return;
+ if (arp->ar_pln != ARP_PLEN)
+ return;
+
+ if (net_ip.s_addr == 0)
+ return;
+
+ if (net_read_ip(&arp->ar_tpa).s_addr != net_ip.s_addr)
+ return;
+
+ switch (ntohs(arp->ar_op)) {
+ case ARPOP_REQUEST:
+ /* reply with our IP address */
+ debug_cond(DEBUG_DEV_PKT, "Got ARP REQUEST, return our IP\n");
+ eth_hdr_size = net_update_ether(et, et->et_src, PROT_ARP);
+ arp->ar_op = htons(ARPOP_REPLY);
+ memcpy(&arp->ar_tha, &arp->ar_sha, ARP_HLEN);
+ net_copy_ip(&arp->ar_tpa, &arp->ar_spa);
+ memcpy(&arp->ar_sha, net_ethaddr, ARP_HLEN);
+ net_copy_ip(&arp->ar_spa, &net_ip);
+
+#ifdef CONFIG_CMD_LINK_LOCAL
+ /*
+ * Work-around for brain-damaged Cisco equipment with
+ * arp-proxy enabled.
+ *
+ * If the requesting IP is not on our subnet, wait 5ms to
+ * reply to ARP request so that our reply will overwrite
+ * the arp-proxy's instead of the other way around.
+ */
+ if ((net_read_ip(&arp->ar_tpa).s_addr & net_netmask.s_addr) !=
+ (net_read_ip(&arp->ar_spa).s_addr & net_netmask.s_addr))
+ udelay(5000);
+#endif
+ tx_packet = net_get_async_tx_pkt_buf();
+ memcpy(tx_packet, et, eth_hdr_size + ARP_HDR_SIZE);
+ net_send_packet(tx_packet, eth_hdr_size + ARP_HDR_SIZE);
+ return;
+
+ case ARPOP_REPLY: /* arp reply */
+ /* are we waiting for a reply? */
+ if (!arp_is_waiting())
+ break;
+
+#ifdef CONFIG_KEEP_SERVERADDR
+ if (net_server_ip.s_addr == net_arp_wait_packet_ip.s_addr) {
+ char buf[20];
+ sprintf(buf, "%pM", &arp->ar_sha);
+ env_set("serveraddr", buf);
+ }
+#endif
+
+ reply_ip_addr = net_read_ip(&arp->ar_spa);
+
+ /* matched waiting packet's address */
+ if (reply_ip_addr.s_addr == net_arp_wait_reply_ip.s_addr) {
+ debug_cond(DEBUG_DEV_PKT,
+ "Got ARP REPLY, set eth addr (%pM)\n",
+ arp->ar_data);
+
+ /* save address for later use */
+ if (arp_wait_packet_ethaddr != NULL)
+ memcpy(arp_wait_packet_ethaddr,
+ &arp->ar_sha, ARP_HLEN);
+
+ net_get_arp_handler()((uchar *)arp, 0, reply_ip_addr,
+ 0, len);
+
+ /* set the mac address in the waiting packet's header
+ and transmit it */
+ memcpy(((struct ethernet_hdr *)net_tx_packet)->et_dest,
+ &arp->ar_sha, ARP_HLEN);
+ net_send_packet(net_tx_packet, arp_wait_tx_packet_size);
+
+ /* no arp request pending now */
+ net_arp_wait_packet_ip.s_addr = 0;
+ arp_wait_tx_packet_size = 0;
+ arp_wait_packet_ethaddr = NULL;
+ }
+ return;
+ default:
+ debug("Unexpected ARP opcode 0x%x\n",
+ ntohs(arp->ar_op));
+ return;
+ }
+}
+
+bool arp_is_waiting(void)
+{
+ return !!net_arp_wait_packet_ip.s_addr;
+}
diff --git a/roms/u-boot/net/arp.h b/roms/u-boot/net/arp.h
new file mode 100644
index 000000000..25b3c00d5
--- /dev/null
+++ b/roms/u-boot/net/arp.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copied from Linux Monitor (LiMon) - Networking.
+ *
+ * Copyright 1994 - 2000 Neil Russell.
+ * (See License)
+ * Copyright 2000 Roland Borde
+ * Copyright 2000 Paolo Scaffardi
+ * Copyright 2000-2002 Wolfgang Denk, wd@denx.de
+ */
+
+#ifndef __ARP_H__
+#define __ARP_H__
+
+#include <common.h>
+
+extern struct in_addr net_arp_wait_packet_ip;
+/* MAC address of waiting packet's destination */
+extern uchar *arp_wait_packet_ethaddr;
+extern int arp_wait_tx_packet_size;
+extern ulong arp_wait_timer_start;
+extern int arp_wait_try;
+extern uchar *arp_tx_packet;
+
+void arp_init(void);
+void arp_request(void);
+void arp_raw_request(struct in_addr source_ip, const uchar *targetEther,
+ struct in_addr target_ip);
+int arp_timeout_check(void);
+void arp_receive(struct ethernet_hdr *et, struct ip_udp_hdr *ip, int len);
+
+#endif /* __ARP_H__ */
diff --git a/roms/u-boot/net/bootp.c b/roms/u-boot/net/bootp.c
new file mode 100644
index 000000000..163af41e9
--- /dev/null
+++ b/roms/u-boot/net/bootp.c
@@ -0,0 +1,1137 @@
+/*
+ * Based on LiMon - BOOTP.
+ *
+ * Copyright 1994, 1995, 2000 Neil Russell.
+ * (See License)
+ * Copyright 2000 Roland Borde
+ * Copyright 2000 Paolo Scaffardi
+ * Copyright 2000-2004 Wolfgang Denk, wd@denx.de
+ */
+
+#include <common.h>
+#include <bootstage.h>
+#include <command.h>
+#include <env.h>
+#include <efi_loader.h>
+#include <log.h>
+#include <net.h>
+#include <rand.h>
+#include <uuid.h>
+#include <linux/delay.h>
+#include <net/tftp.h>
+#include "bootp.h"
+#ifdef CONFIG_LED_STATUS
+#include <status_led.h>
+#endif
+#ifdef CONFIG_BOOTP_RANDOM_DELAY
+#include "net_rand.h"
+#endif
+
+#define BOOTP_VENDOR_MAGIC 0x63825363 /* RFC1048 Magic Cookie */
+
+/*
+ * The timeout for the initial BOOTP/DHCP request used to be described by a
+ * counter of fixed-length timeout periods. TIMEOUT_COUNT represents
+ * that counter
+ *
+ * Now that the timeout periods are variable (exponential backoff and retry)
+ * we convert the timeout count to the absolute time it would have take to
+ * execute that many retries, and keep sending retry packets until that time
+ * is reached.
+ */
+#ifndef CONFIG_NET_RETRY_COUNT
+# define TIMEOUT_COUNT 5 /* # of timeouts before giving up */
+#else
+# define TIMEOUT_COUNT (CONFIG_NET_RETRY_COUNT)
+#endif
+#define TIMEOUT_MS ((3 + (TIMEOUT_COUNT * 5)) * 1000)
+
+#define PORT_BOOTPS 67 /* BOOTP server UDP port */
+#define PORT_BOOTPC 68 /* BOOTP client UDP port */
+
+#ifndef CONFIG_DHCP_MIN_EXT_LEN /* minimal length of extension list */
+#define CONFIG_DHCP_MIN_EXT_LEN 64
+#endif
+
+#ifndef CONFIG_BOOTP_ID_CACHE_SIZE
+#define CONFIG_BOOTP_ID_CACHE_SIZE 4
+#endif
+
+u32 bootp_ids[CONFIG_BOOTP_ID_CACHE_SIZE];
+unsigned int bootp_num_ids;
+int bootp_try;
+ulong bootp_start;
+ulong bootp_timeout;
+char net_nis_domain[32] = {0,}; /* Our NIS domain */
+char net_hostname[32] = {0,}; /* Our hostname */
+char net_root_path[64] = {0,}; /* Our bootpath */
+
+static ulong time_taken_max;
+
+#if defined(CONFIG_CMD_DHCP)
+static dhcp_state_t dhcp_state = INIT;
+static u32 dhcp_leasetime;
+static struct in_addr dhcp_server_ip;
+static u8 dhcp_option_overload;
+#define OVERLOAD_FILE 1
+#define OVERLOAD_SNAME 2
+static void dhcp_handler(uchar *pkt, unsigned dest, struct in_addr sip,
+ unsigned src, unsigned len);
+
+/* For Debug */
+#if 0
+static char *dhcpmsg2str(int type)
+{
+ switch (type) {
+ case 1: return "DHCPDISCOVER"; break;
+ case 2: return "DHCPOFFER"; break;
+ case 3: return "DHCPREQUEST"; break;
+ case 4: return "DHCPDECLINE"; break;
+ case 5: return "DHCPACK"; break;
+ case 6: return "DHCPNACK"; break;
+ case 7: return "DHCPRELEASE"; break;
+ default: return "UNKNOWN/INVALID MSG TYPE"; break;
+ }
+}
+#endif
+#endif
+
+static void bootp_add_id(ulong id)
+{
+ if (bootp_num_ids >= ARRAY_SIZE(bootp_ids)) {
+ size_t size = sizeof(bootp_ids) - sizeof(id);
+
+ memmove(bootp_ids, &bootp_ids[1], size);
+ bootp_ids[bootp_num_ids - 1] = id;
+ } else {
+ bootp_ids[bootp_num_ids] = id;
+ bootp_num_ids++;
+ }
+}
+
+static bool bootp_match_id(ulong id)
+{
+ unsigned int i;
+
+ for (i = 0; i < bootp_num_ids; i++)
+ if (bootp_ids[i] == id)
+ return true;
+
+ return false;
+}
+
+static int check_reply_packet(uchar *pkt, unsigned dest, unsigned src,
+ unsigned len)
+{
+ struct bootp_hdr *bp = (struct bootp_hdr *)pkt;
+ int retval = 0;
+
+ if (dest != PORT_BOOTPC || src != PORT_BOOTPS)
+ retval = -1;
+ else if (len < sizeof(struct bootp_hdr) - OPT_FIELD_SIZE)
+ retval = -2;
+ else if (bp->bp_op != OP_BOOTREPLY)
+ retval = -3;
+ else if (bp->bp_htype != HWT_ETHER)
+ retval = -4;
+ else if (bp->bp_hlen != HWL_ETHER)
+ retval = -5;
+ else if (!bootp_match_id(net_read_u32(&bp->bp_id)))
+ retval = -6;
+ else if (memcmp(bp->bp_chaddr, net_ethaddr, HWL_ETHER) != 0)
+ retval = -7;
+
+ debug("Filtering pkt = %d\n", retval);
+
+ return retval;
+}
+
+static void store_bootp_params(struct bootp_hdr *bp)
+{
+#if !defined(CONFIG_BOOTP_SERVERIP)
+ struct in_addr tmp_ip;
+ bool overwrite_serverip = true;
+
+#if defined(CONFIG_BOOTP_PREFER_SERVERIP)
+ overwrite_serverip = false;
+#endif
+
+ net_copy_ip(&tmp_ip, &bp->bp_siaddr);
+ if (tmp_ip.s_addr != 0 && (overwrite_serverip || !net_server_ip.s_addr))
+ net_copy_ip(&net_server_ip, &bp->bp_siaddr);
+ memcpy(net_server_ethaddr,
+ ((struct ethernet_hdr *)net_rx_packet)->et_src, 6);
+ if (
+#if defined(CONFIG_CMD_DHCP)
+ !(dhcp_option_overload & OVERLOAD_FILE) &&
+#endif
+ (strlen(bp->bp_file) > 0) &&
+ !net_boot_file_name_explicit) {
+ copy_filename(net_boot_file_name, bp->bp_file,
+ sizeof(net_boot_file_name));
+ }
+
+ debug("net_boot_file_name: %s\n", net_boot_file_name);
+
+ /* Propagate to environment:
+ * don't delete exising entry when BOOTP / DHCP reply does
+ * not contain a new value
+ */
+ if (*net_boot_file_name)
+ env_set("bootfile", net_boot_file_name);
+#endif
+}
+
+/*
+ * Copy parameters of interest from BOOTP_REPLY/DHCP_OFFER packet
+ */
+static void store_net_params(struct bootp_hdr *bp)
+{
+#if !defined(CONFIG_SERVERIP_FROM_PROXYDHCP)
+ store_bootp_params(bp);
+#endif
+ net_copy_ip(&net_ip, &bp->bp_yiaddr);
+}
+
+static int truncate_sz(const char *name, int maxlen, int curlen)
+{
+ if (curlen >= maxlen) {
+ printf("*** WARNING: %s is too long (%d - max: %d)"
+ " - truncated\n", name, curlen, maxlen);
+ curlen = maxlen - 1;
+ }
+ return curlen;
+}
+
+#if !defined(CONFIG_CMD_DHCP)
+
+static void bootp_process_vendor_field(u8 *ext)
+{
+ int size = *(ext + 1);
+
+ debug("[BOOTP] Processing extension %d... (%d bytes)\n", *ext,
+ *(ext + 1));
+
+ net_boot_file_expected_size_in_blocks = 0;
+
+ switch (*ext) {
+ /* Fixed length fields */
+ case 1: /* Subnet mask */
+ if (net_netmask.s_addr == 0)
+ net_copy_ip(&net_netmask, (struct in_addr *)(ext + 2));
+ break;
+ case 2: /* Time offset - Not yet supported */
+ break;
+ /* Variable length fields */
+ case 3: /* Gateways list */
+ if (net_gateway.s_addr == 0)
+ net_copy_ip(&net_gateway, (struct in_addr *)(ext + 2));
+ break;
+ case 4: /* Time server - Not yet supported */
+ break;
+ case 5: /* IEN-116 name server - Not yet supported */
+ break;
+ case 6:
+ if (net_dns_server.s_addr == 0)
+ net_copy_ip(&net_dns_server,
+ (struct in_addr *)(ext + 2));
+#if defined(CONFIG_BOOTP_DNS2)
+ if ((net_dns_server2.s_addr == 0) && (size > 4))
+ net_copy_ip(&net_dns_server2,
+ (struct in_addr *)(ext + 2 + 4));
+#endif
+ break;
+ case 7: /* Log server - Not yet supported */
+ break;
+ case 8: /* Cookie/Quote server - Not yet supported */
+ break;
+ case 9: /* LPR server - Not yet supported */
+ break;
+ case 10: /* Impress server - Not yet supported */
+ break;
+ case 11: /* RPL server - Not yet supported */
+ break;
+ case 12: /* Host name */
+ if (net_hostname[0] == 0) {
+ size = truncate_sz("Host Name",
+ sizeof(net_hostname), size);
+ memcpy(&net_hostname, ext + 2, size);
+ net_hostname[size] = 0;
+ }
+ break;
+ case 13: /* Boot file size */
+ if (size == 2)
+ net_boot_file_expected_size_in_blocks =
+ ntohs(*(ushort *)(ext + 2));
+ else if (size == 4)
+ net_boot_file_expected_size_in_blocks =
+ ntohl(*(ulong *)(ext + 2));
+ break;
+ case 14: /* Merit dump file - Not yet supported */
+ break;
+ case 15: /* Domain name - Not yet supported */
+ break;
+ case 16: /* Swap server - Not yet supported */
+ break;
+ case 17: /* Root path */
+ if (net_root_path[0] == 0) {
+ size = truncate_sz("Root Path",
+ sizeof(net_root_path), size);
+ memcpy(&net_root_path, ext + 2, size);
+ net_root_path[size] = 0;
+ }
+ break;
+ case 18: /* Extension path - Not yet supported */
+ /*
+ * This can be used to send the information of the
+ * vendor area in another file that the client can
+ * access via TFTP.
+ */
+ break;
+ /* IP host layer fields */
+ case 40: /* NIS Domain name */
+ if (net_nis_domain[0] == 0) {
+ size = truncate_sz("NIS Domain Name",
+ sizeof(net_nis_domain), size);
+ memcpy(&net_nis_domain, ext + 2, size);
+ net_nis_domain[size] = 0;
+ }
+ break;
+#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_NTPSERVER)
+ case 42: /* NTP server IP */
+ net_copy_ip(&net_ntp_server, (struct in_addr *)(ext + 2));
+ break;
+#endif
+ /* Application layer fields */
+ case 43: /* Vendor specific info - Not yet supported */
+ /*
+ * Binary information to exchange specific
+ * product information.
+ */
+ break;
+ /* Reserved (custom) fields (128..254) */
+ }
+}
+
+static void bootp_process_vendor(u8 *ext, int size)
+{
+ u8 *end = ext + size;
+
+ debug("[BOOTP] Checking extension (%d bytes)...\n", size);
+
+ while ((ext < end) && (*ext != 0xff)) {
+ if (*ext == 0) {
+ ext++;
+ } else {
+ u8 *opt = ext;
+
+ ext += ext[1] + 2;
+ if (ext <= end)
+ bootp_process_vendor_field(opt);
+ }
+ }
+
+ debug("[BOOTP] Received fields:\n");
+ if (net_netmask.s_addr)
+ debug("net_netmask : %pI4\n", &net_netmask);
+
+ if (net_gateway.s_addr)
+ debug("net_gateway : %pI4", &net_gateway);
+
+ if (net_boot_file_expected_size_in_blocks)
+ debug("net_boot_file_expected_size_in_blocks : %d\n",
+ net_boot_file_expected_size_in_blocks);
+
+ if (net_hostname[0])
+ debug("net_hostname : %s\n", net_hostname);
+
+ if (net_root_path[0])
+ debug("net_root_path : %s\n", net_root_path);
+
+ if (net_nis_domain[0])
+ debug("net_nis_domain : %s\n", net_nis_domain);
+
+#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_NTPSERVER)
+ if (net_ntp_server.s_addr)
+ debug("net_ntp_server : %pI4\n", &net_ntp_server);
+#endif
+}
+
+/*
+ * Handle a BOOTP received packet.
+ */
+static void bootp_handler(uchar *pkt, unsigned dest, struct in_addr sip,
+ unsigned src, unsigned len)
+{
+ struct bootp_hdr *bp;
+
+ debug("got BOOTP packet (src=%d, dst=%d, len=%d want_len=%zu)\n",
+ src, dest, len, sizeof(struct bootp_hdr));
+
+ bp = (struct bootp_hdr *)pkt;
+
+ /* Filter out pkts we don't want */
+ if (check_reply_packet(pkt, dest, src, len))
+ return;
+
+ /*
+ * Got a good BOOTP reply. Copy the data into our variables.
+ */
+#if defined(CONFIG_LED_STATUS) && defined(CONFIG_LED_STATUS_BOOT_ENABLE)
+ status_led_set(CONFIG_LED_STATUS_BOOT, CONFIG_LED_STATUS_OFF);
+#endif
+
+ store_net_params(bp); /* Store net parameters from reply */
+
+ /* Retrieve extended information (we must parse the vendor area) */
+ if (net_read_u32((u32 *)&bp->bp_vend[0]) == htonl(BOOTP_VENDOR_MAGIC))
+ bootp_process_vendor((uchar *)&bp->bp_vend[4], len);
+
+ net_set_timeout_handler(0, (thand_f *)0);
+ bootstage_mark_name(BOOTSTAGE_ID_BOOTP_STOP, "bootp_stop");
+
+ debug("Got good BOOTP\n");
+
+ net_auto_load();
+}
+#endif
+
+/*
+ * Timeout on BOOTP/DHCP request.
+ */
+static void bootp_timeout_handler(void)
+{
+ ulong time_taken = get_timer(bootp_start);
+
+ if (time_taken >= time_taken_max) {
+#ifdef CONFIG_BOOTP_MAY_FAIL
+ char *ethrotate;
+
+ ethrotate = env_get("ethrotate");
+ if ((ethrotate && strcmp(ethrotate, "no") == 0) ||
+ net_restart_wrap) {
+ puts("\nRetry time exceeded\n");
+ net_set_state(NETLOOP_FAIL);
+ } else
+#endif
+ {
+ puts("\nRetry time exceeded; starting again\n");
+ net_start_again();
+ }
+ } else {
+ bootp_timeout *= 2;
+ if (bootp_timeout > 2000)
+ bootp_timeout = 2000;
+ net_set_timeout_handler(bootp_timeout, bootp_timeout_handler);
+ bootp_request();
+ }
+}
+
+#define put_vci(e, str) \
+ do { \
+ size_t vci_strlen = strlen(str); \
+ *e++ = 60; /* Vendor Class Identifier */ \
+ *e++ = vci_strlen; \
+ memcpy(e, str, vci_strlen); \
+ e += vci_strlen; \
+ } while (0)
+
+static u8 *add_vci(u8 *e)
+{
+ char *vci = NULL;
+ char *env_vci = env_get("bootp_vci");
+
+#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_NET_VCI_STRING)
+ vci = CONFIG_SPL_NET_VCI_STRING;
+#elif defined(CONFIG_BOOTP_VCI_STRING)
+ vci = CONFIG_BOOTP_VCI_STRING;
+#endif
+
+ if (env_vci)
+ vci = env_vci;
+
+ if (vci)
+ put_vci(e, vci);
+
+ return e;
+}
+
+/*
+ * Initialize BOOTP extension fields in the request.
+ */
+#if defined(CONFIG_CMD_DHCP)
+static int dhcp_extended(u8 *e, int message_type, struct in_addr server_ip,
+ struct in_addr requested_ip)
+{
+ u8 *start = e;
+ u8 *cnt;
+#ifdef CONFIG_LIB_UUID
+ char *uuid;
+#endif
+ int clientarch = -1;
+
+#if defined(CONFIG_BOOTP_VENDOREX)
+ u8 *x;
+#endif
+#if defined(CONFIG_BOOTP_SEND_HOSTNAME)
+ char *hostname;
+#endif
+
+ *e++ = 99; /* RFC1048 Magic Cookie */
+ *e++ = 130;
+ *e++ = 83;
+ *e++ = 99;
+
+ *e++ = 53; /* DHCP Message Type */
+ *e++ = 1;
+ *e++ = message_type;
+
+ *e++ = 57; /* Maximum DHCP Message Size */
+ *e++ = 2;
+ *e++ = (576 - 312 + OPT_FIELD_SIZE) >> 8;
+ *e++ = (576 - 312 + OPT_FIELD_SIZE) & 0xff;
+
+ if (server_ip.s_addr) {
+ int tmp = ntohl(server_ip.s_addr);
+
+ *e++ = 54; /* ServerID */
+ *e++ = 4;
+ *e++ = tmp >> 24;
+ *e++ = tmp >> 16;
+ *e++ = tmp >> 8;
+ *e++ = tmp & 0xff;
+ }
+
+ if (requested_ip.s_addr) {
+ int tmp = ntohl(requested_ip.s_addr);
+
+ *e++ = 50; /* Requested IP */
+ *e++ = 4;
+ *e++ = tmp >> 24;
+ *e++ = tmp >> 16;
+ *e++ = tmp >> 8;
+ *e++ = tmp & 0xff;
+ }
+#if defined(CONFIG_BOOTP_SEND_HOSTNAME)
+ hostname = env_get("hostname");
+ if (hostname) {
+ int hostnamelen = strlen(hostname);
+
+ *e++ = 12; /* Hostname */
+ *e++ = hostnamelen;
+ memcpy(e, hostname, hostnamelen);
+ e += hostnamelen;
+ }
+#endif
+
+#ifdef CONFIG_BOOTP_PXE_CLIENTARCH
+ clientarch = CONFIG_BOOTP_PXE_CLIENTARCH;
+#endif
+
+ if (env_get("bootp_arch"))
+ clientarch = env_get_ulong("bootp_arch", 16, clientarch);
+
+ if (clientarch > 0) {
+ *e++ = 93; /* Client System Architecture */
+ *e++ = 2;
+ *e++ = (clientarch >> 8) & 0xff;
+ *e++ = clientarch & 0xff;
+ }
+
+ *e++ = 94; /* Client Network Interface Identifier */
+ *e++ = 3;
+ *e++ = 1; /* type field for UNDI */
+ *e++ = 0; /* major revision */
+ *e++ = 0; /* minor revision */
+
+#ifdef CONFIG_LIB_UUID
+ uuid = env_get("pxeuuid");
+
+ if (uuid) {
+ if (uuid_str_valid(uuid)) {
+ *e++ = 97; /* Client Machine Identifier */
+ *e++ = 17;
+ *e++ = 0; /* type 0 - UUID */
+
+ uuid_str_to_bin(uuid, e, UUID_STR_FORMAT_STD);
+ e += 16;
+ } else {
+ printf("Invalid pxeuuid: %s\n", uuid);
+ }
+ }
+#endif
+
+ e = add_vci(e);
+
+#if defined(CONFIG_BOOTP_VENDOREX)
+ x = dhcp_vendorex_prep(e);
+ if (x)
+ return x - start;
+#endif
+
+ *e++ = 55; /* Parameter Request List */
+ cnt = e++; /* Pointer to count of requested items */
+ *cnt = 0;
+#if defined(CONFIG_BOOTP_SUBNETMASK)
+ *e++ = 1; /* Subnet Mask */
+ *cnt += 1;
+#endif
+#if defined(CONFIG_BOOTP_TIMEOFFSET)
+ *e++ = 2;
+ *cnt += 1;
+#endif
+#if defined(CONFIG_BOOTP_GATEWAY)
+ *e++ = 3; /* Router Option */
+ *cnt += 1;
+#endif
+#if defined(CONFIG_BOOTP_DNS)
+ *e++ = 6; /* DNS Server(s) */
+ *cnt += 1;
+#endif
+#if defined(CONFIG_BOOTP_HOSTNAME)
+ *e++ = 12; /* Hostname */
+ *cnt += 1;
+#endif
+#if defined(CONFIG_BOOTP_BOOTFILESIZE)
+ *e++ = 13; /* Boot File Size */
+ *cnt += 1;
+#endif
+#if defined(CONFIG_BOOTP_BOOTPATH)
+ *e++ = 17; /* Boot path */
+ *cnt += 1;
+#endif
+#if defined(CONFIG_BOOTP_NISDOMAIN)
+ *e++ = 40; /* NIS Domain name request */
+ *cnt += 1;
+#endif
+#if defined(CONFIG_BOOTP_NTPSERVER)
+ *e++ = 42;
+ *cnt += 1;
+#endif
+ /* no options, so back up to avoid sending an empty request list */
+ if (*cnt == 0)
+ e -= 2;
+
+ *e++ = 255; /* End of the list */
+
+ /* Pad to minimal length */
+#ifdef CONFIG_DHCP_MIN_EXT_LEN
+ while ((e - start) < CONFIG_DHCP_MIN_EXT_LEN)
+ *e++ = 0;
+#endif
+
+ return e - start;
+}
+
+#else
+/*
+ * Warning: no field size check - change CONFIG_BOOTP_* at your own risk!
+ */
+static int bootp_extended(u8 *e)
+{
+ u8 *start = e;
+
+ *e++ = 99; /* RFC1048 Magic Cookie */
+ *e++ = 130;
+ *e++ = 83;
+ *e++ = 99;
+
+#if defined(CONFIG_CMD_DHCP)
+ *e++ = 53; /* DHCP Message Type */
+ *e++ = 1;
+ *e++ = DHCP_DISCOVER;
+
+ *e++ = 57; /* Maximum DHCP Message Size */
+ *e++ = 2;
+ *e++ = (576 - 312 + OPT_FIELD_SIZE) >> 16;
+ *e++ = (576 - 312 + OPT_FIELD_SIZE) & 0xff;
+#endif
+
+ add_vci(e);
+
+#if defined(CONFIG_BOOTP_SUBNETMASK)
+ *e++ = 1; /* Subnet mask request */
+ *e++ = 4;
+ e += 4;
+#endif
+
+#if defined(CONFIG_BOOTP_GATEWAY)
+ *e++ = 3; /* Default gateway request */
+ *e++ = 4;
+ e += 4;
+#endif
+
+#if defined(CONFIG_BOOTP_DNS)
+ *e++ = 6; /* Domain Name Server */
+ *e++ = 4;
+ e += 4;
+#endif
+
+#if defined(CONFIG_BOOTP_HOSTNAME)
+ *e++ = 12; /* Host name request */
+ *e++ = 32;
+ e += 32;
+#endif
+
+#if defined(CONFIG_BOOTP_BOOTFILESIZE)
+ *e++ = 13; /* Boot file size */
+ *e++ = 2;
+ e += 2;
+#endif
+
+#if defined(CONFIG_BOOTP_BOOTPATH)
+ *e++ = 17; /* Boot path */
+ *e++ = 32;
+ e += 32;
+#endif
+
+#if defined(CONFIG_BOOTP_NISDOMAIN)
+ *e++ = 40; /* NIS Domain name request */
+ *e++ = 32;
+ e += 32;
+#endif
+#if defined(CONFIG_BOOTP_NTPSERVER)
+ *e++ = 42;
+ *e++ = 4;
+ e += 4;
+#endif
+
+ *e++ = 255; /* End of the list */
+
+ /*
+ * If nothing in list, remove it altogether. Some DHCP servers get
+ * upset by this minor faux pas and do not respond at all.
+ */
+ if (e == start + 3) {
+ printf("*** Warning: no DHCP options requested\n");
+ e -= 3;
+ }
+
+ return e - start;
+}
+#endif
+
+void bootp_reset(void)
+{
+ bootp_num_ids = 0;
+ bootp_try = 0;
+ bootp_start = get_timer(0);
+ bootp_timeout = 250;
+}
+
+void bootp_request(void)
+{
+ uchar *pkt, *iphdr;
+ struct bootp_hdr *bp;
+ int extlen, pktlen, iplen;
+ int eth_hdr_size;
+#ifdef CONFIG_BOOTP_RANDOM_DELAY
+ ulong rand_ms;
+#endif
+ u32 bootp_id;
+ struct in_addr zero_ip;
+ struct in_addr bcast_ip;
+ char *ep; /* Environment pointer */
+
+ bootstage_mark_name(BOOTSTAGE_ID_BOOTP_START, "bootp_start");
+#if defined(CONFIG_CMD_DHCP)
+ dhcp_state = INIT;
+#endif
+
+ ep = env_get("bootpretryperiod");
+ if (ep != NULL)
+ time_taken_max = simple_strtoul(ep, NULL, 10);
+ else
+ time_taken_max = TIMEOUT_MS;
+
+#ifdef CONFIG_BOOTP_RANDOM_DELAY /* Random BOOTP delay */
+ if (bootp_try == 0)
+ srand_mac();
+
+ if (bootp_try <= 2) /* Start with max 1024 * 1ms */
+ rand_ms = rand() >> (22 - bootp_try);
+ else /* After 3rd BOOTP request max 8192 * 1ms */
+ rand_ms = rand() >> 19;
+
+ printf("Random delay: %ld ms...\n", rand_ms);
+ mdelay(rand_ms);
+
+#endif /* CONFIG_BOOTP_RANDOM_DELAY */
+
+ printf("BOOTP broadcast %d\n", ++bootp_try);
+ pkt = net_tx_packet;
+ memset((void *)pkt, 0, PKTSIZE);
+
+ eth_hdr_size = net_set_ether(pkt, net_bcast_ethaddr, PROT_IP);
+ pkt += eth_hdr_size;
+
+ /*
+ * Next line results in incorrect packet size being transmitted,
+ * resulting in errors in some DHCP servers, reporting missing bytes.
+ * Size must be set in packet header after extension length has been
+ * determined.
+ * C. Hallinan, DS4.COM, Inc.
+ */
+ /* net_set_udp_header(pkt, 0xFFFFFFFFL, PORT_BOOTPS, PORT_BOOTPC,
+ sizeof (struct bootp_hdr)); */
+ iphdr = pkt; /* We need this later for net_set_udp_header() */
+ pkt += IP_UDP_HDR_SIZE;
+
+ bp = (struct bootp_hdr *)pkt;
+ bp->bp_op = OP_BOOTREQUEST;
+ bp->bp_htype = HWT_ETHER;
+ bp->bp_hlen = HWL_ETHER;
+ bp->bp_hops = 0;
+ /*
+ * according to RFC1542, should be 0 on first request, secs since
+ * first request otherwise
+ */
+ bp->bp_secs = htons(get_timer(bootp_start) / 1000);
+ zero_ip.s_addr = 0;
+ net_write_ip(&bp->bp_ciaddr, zero_ip);
+ net_write_ip(&bp->bp_yiaddr, zero_ip);
+ net_write_ip(&bp->bp_siaddr, zero_ip);
+ net_write_ip(&bp->bp_giaddr, zero_ip);
+ memcpy(bp->bp_chaddr, net_ethaddr, 6);
+ copy_filename(bp->bp_file, net_boot_file_name, sizeof(bp->bp_file));
+
+ /* Request additional information from the BOOTP/DHCP server */
+#if defined(CONFIG_CMD_DHCP)
+ extlen = dhcp_extended((u8 *)bp->bp_vend, DHCP_DISCOVER, zero_ip,
+ zero_ip);
+#else
+ extlen = bootp_extended((u8 *)bp->bp_vend);
+#endif
+
+ /*
+ * Bootp ID is the lower 4 bytes of our ethernet address
+ * plus the current time in ms.
+ */
+ bootp_id = ((u32)net_ethaddr[2] << 24)
+ | ((u32)net_ethaddr[3] << 16)
+ | ((u32)net_ethaddr[4] << 8)
+ | (u32)net_ethaddr[5];
+ bootp_id += get_timer(0);
+ bootp_id = htonl(bootp_id);
+ bootp_add_id(bootp_id);
+ net_copy_u32(&bp->bp_id, &bootp_id);
+
+ /*
+ * Calculate proper packet lengths taking into account the
+ * variable size of the options field
+ */
+ iplen = BOOTP_HDR_SIZE - OPT_FIELD_SIZE + extlen;
+ pktlen = eth_hdr_size + IP_UDP_HDR_SIZE + iplen;
+ bcast_ip.s_addr = 0xFFFFFFFFL;
+ net_set_udp_header(iphdr, bcast_ip, PORT_BOOTPS, PORT_BOOTPC, iplen);
+ net_set_timeout_handler(bootp_timeout, bootp_timeout_handler);
+
+#if defined(CONFIG_CMD_DHCP)
+ dhcp_state = SELECTING;
+ net_set_udp_handler(dhcp_handler);
+#else
+ net_set_udp_handler(bootp_handler);
+#endif
+ net_send_packet(net_tx_packet, pktlen);
+}
+
+#if defined(CONFIG_CMD_DHCP)
+static void dhcp_process_options(uchar *popt, uchar *end)
+{
+ int oplen, size;
+#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_TIMEOFFSET)
+ int *to_ptr;
+#endif
+
+ while (popt < end && *popt != 0xff) {
+ oplen = *(popt + 1);
+ switch (*popt) {
+ case 0:
+ oplen = -1; /* Pad omits len byte */
+ break;
+ case 1:
+ net_copy_ip(&net_netmask, (popt + 2));
+ break;
+#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_TIMEOFFSET)
+ case 2: /* Time offset */
+ to_ptr = &net_ntp_time_offset;
+ net_copy_u32((u32 *)to_ptr, (u32 *)(popt + 2));
+ net_ntp_time_offset = ntohl(net_ntp_time_offset);
+ break;
+#endif
+ case 3:
+ net_copy_ip(&net_gateway, (popt + 2));
+ break;
+ case 6:
+ net_copy_ip(&net_dns_server, (popt + 2));
+#if defined(CONFIG_BOOTP_DNS2)
+ if (*(popt + 1) > 4)
+ net_copy_ip(&net_dns_server2, (popt + 2 + 4));
+#endif
+ break;
+ case 12:
+ size = truncate_sz("Host Name",
+ sizeof(net_hostname), oplen);
+ memcpy(&net_hostname, popt + 2, size);
+ net_hostname[size] = 0;
+ break;
+ case 15: /* Ignore Domain Name Option */
+ break;
+ case 17:
+ size = truncate_sz("Root Path",
+ sizeof(net_root_path), oplen);
+ memcpy(&net_root_path, popt + 2, size);
+ net_root_path[size] = 0;
+ break;
+ case 28: /* Ignore Broadcast Address Option */
+ break;
+#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_NTPSERVER)
+ case 42: /* NTP server IP */
+ net_copy_ip(&net_ntp_server, (popt + 2));
+ break;
+#endif
+ case 51:
+ net_copy_u32(&dhcp_leasetime, (u32 *)(popt + 2));
+ break;
+ case 52:
+ dhcp_option_overload = popt[2];
+ break;
+ case 53: /* Ignore Message Type Option */
+ break;
+ case 54:
+ net_copy_ip(&dhcp_server_ip, (popt + 2));
+ break;
+ case 58: /* Ignore Renewal Time Option */
+ break;
+ case 59: /* Ignore Rebinding Time Option */
+ break;
+ case 66: /* Ignore TFTP server name */
+ break;
+ case 67: /* Bootfile option */
+ if (!net_boot_file_name_explicit) {
+ size = truncate_sz("Bootfile",
+ sizeof(net_boot_file_name),
+ oplen);
+ memcpy(&net_boot_file_name, popt + 2, size);
+ net_boot_file_name[size] = 0;
+ }
+ break;
+ default:
+#if defined(CONFIG_BOOTP_VENDOREX)
+ if (dhcp_vendorex_proc(popt))
+ break;
+#endif
+ printf("*** Unhandled DHCP Option in OFFER/ACK:"
+ " %d\n", *popt);
+ break;
+ }
+ popt += oplen + 2; /* Process next option */
+ }
+}
+
+static void dhcp_packet_process_options(struct bootp_hdr *bp)
+{
+ uchar *popt = (uchar *)&bp->bp_vend[4];
+ uchar *end = popt + BOOTP_HDR_SIZE;
+
+ if (net_read_u32((u32 *)&bp->bp_vend[0]) != htonl(BOOTP_VENDOR_MAGIC))
+ return;
+
+ dhcp_option_overload = 0;
+
+ /*
+ * The 'options' field MUST be interpreted first, 'file' next,
+ * 'sname' last.
+ */
+ dhcp_process_options(popt, end);
+
+ if (dhcp_option_overload & OVERLOAD_FILE) {
+ popt = (uchar *)bp->bp_file;
+ end = popt + sizeof(bp->bp_file);
+ dhcp_process_options(popt, end);
+ }
+
+ if (dhcp_option_overload & OVERLOAD_SNAME) {
+ popt = (uchar *)bp->bp_sname;
+ end = popt + sizeof(bp->bp_sname);
+ dhcp_process_options(popt, end);
+ }
+}
+
+static int dhcp_message_type(unsigned char *popt)
+{
+ if (net_read_u32((u32 *)popt) != htonl(BOOTP_VENDOR_MAGIC))
+ return -1;
+
+ popt += 4;
+ while (*popt != 0xff) {
+ if (*popt == 53) /* DHCP Message Type */
+ return *(popt + 2);
+ if (*popt == 0) {
+ /* Pad */
+ popt += 1;
+ } else {
+ /* Scan through all options */
+ popt += *(popt + 1) + 2;
+ }
+ }
+ return -1;
+}
+
+static void dhcp_send_request_packet(struct bootp_hdr *bp_offer)
+{
+ uchar *pkt, *iphdr;
+ struct bootp_hdr *bp;
+ int pktlen, iplen, extlen;
+ int eth_hdr_size;
+ struct in_addr offered_ip;
+ struct in_addr zero_ip;
+ struct in_addr bcast_ip;
+
+ debug("dhcp_send_request_packet: Sending DHCPREQUEST\n");
+ pkt = net_tx_packet;
+ memset((void *)pkt, 0, PKTSIZE);
+
+ eth_hdr_size = net_set_ether(pkt, net_bcast_ethaddr, PROT_IP);
+ pkt += eth_hdr_size;
+
+ iphdr = pkt; /* We'll need this later to set proper pkt size */
+ pkt += IP_UDP_HDR_SIZE;
+
+ bp = (struct bootp_hdr *)pkt;
+ bp->bp_op = OP_BOOTREQUEST;
+ bp->bp_htype = HWT_ETHER;
+ bp->bp_hlen = HWL_ETHER;
+ bp->bp_hops = 0;
+ bp->bp_secs = htons(get_timer(bootp_start) / 1000);
+ /* Do not set the client IP, your IP, or server IP yet, since it
+ * hasn't been ACK'ed by the server yet */
+
+ /*
+ * RFC3046 requires Relay Agents to discard packets with
+ * nonzero and offered giaddr
+ */
+ zero_ip.s_addr = 0;
+ net_write_ip(&bp->bp_giaddr, zero_ip);
+
+ memcpy(bp->bp_chaddr, net_ethaddr, 6);
+ copy_filename(bp->bp_file, net_boot_file_name, sizeof(bp->bp_file));
+
+ /*
+ * ID is the id of the OFFER packet
+ */
+
+ net_copy_u32(&bp->bp_id, &bp_offer->bp_id);
+
+ /*
+ * Copy options from OFFER packet if present
+ */
+
+ /* Copy offered IP into the parameters request list */
+ net_copy_ip(&offered_ip, &bp_offer->bp_yiaddr);
+ extlen = dhcp_extended((u8 *)bp->bp_vend, DHCP_REQUEST,
+ dhcp_server_ip, offered_ip);
+
+ iplen = BOOTP_HDR_SIZE - OPT_FIELD_SIZE + extlen;
+ pktlen = eth_hdr_size + IP_UDP_HDR_SIZE + iplen;
+ bcast_ip.s_addr = 0xFFFFFFFFL;
+ net_set_udp_header(iphdr, bcast_ip, PORT_BOOTPS, PORT_BOOTPC, iplen);
+
+#ifdef CONFIG_BOOTP_DHCP_REQUEST_DELAY
+ udelay(CONFIG_BOOTP_DHCP_REQUEST_DELAY);
+#endif /* CONFIG_BOOTP_DHCP_REQUEST_DELAY */
+ debug("Transmitting DHCPREQUEST packet: len = %d\n", pktlen);
+ net_send_packet(net_tx_packet, pktlen);
+}
+
+/*
+ * Handle DHCP received packets.
+ */
+static void dhcp_handler(uchar *pkt, unsigned dest, struct in_addr sip,
+ unsigned src, unsigned len)
+{
+ struct bootp_hdr *bp = (struct bootp_hdr *)pkt;
+
+ debug("DHCPHandler: got packet: (src=%d, dst=%d, len=%d) state: %d\n",
+ src, dest, len, dhcp_state);
+
+ /* Filter out pkts we don't want */
+ if (check_reply_packet(pkt, dest, src, len))
+ return;
+
+ debug("DHCPHandler: got DHCP packet: (src=%d, dst=%d, len=%d) state: "
+ "%d\n", src, dest, len, dhcp_state);
+
+ if (net_read_ip(&bp->bp_yiaddr).s_addr == 0) {
+#if defined(CONFIG_SERVERIP_FROM_PROXYDHCP)
+ store_bootp_params(bp);
+#endif
+ return;
+ }
+
+ switch (dhcp_state) {
+ case SELECTING:
+ /*
+ * Wait an appropriate time for any potential DHCPOFFER packets
+ * to arrive. Then select one, and generate DHCPREQUEST
+ * response. If filename is in format we recognize, assume it
+ * is a valid OFFER from a server we want.
+ */
+ debug("DHCP: state=SELECTING bp_file: \"%s\"\n", bp->bp_file);
+#ifdef CONFIG_SYS_BOOTFILE_PREFIX
+ if (strncmp(bp->bp_file,
+ CONFIG_SYS_BOOTFILE_PREFIX,
+ strlen(CONFIG_SYS_BOOTFILE_PREFIX)) == 0) {
+#endif /* CONFIG_SYS_BOOTFILE_PREFIX */
+ dhcp_packet_process_options(bp);
+ efi_net_set_dhcp_ack(pkt, len);
+
+#if defined(CONFIG_SERVERIP_FROM_PROXYDHCP)
+ if (!net_server_ip.s_addr)
+ udelay(CONFIG_SERVERIP_FROM_PROXYDHCP_DELAY_MS *
+ 1000);
+#endif /* CONFIG_SERVERIP_FROM_PROXYDHCP */
+
+ debug("TRANSITIONING TO REQUESTING STATE\n");
+ dhcp_state = REQUESTING;
+
+ net_set_timeout_handler(5000, bootp_timeout_handler);
+ dhcp_send_request_packet(bp);
+#ifdef CONFIG_SYS_BOOTFILE_PREFIX
+ }
+#endif /* CONFIG_SYS_BOOTFILE_PREFIX */
+
+ return;
+ break;
+ case REQUESTING:
+ debug("DHCP State: REQUESTING\n");
+
+ if (dhcp_message_type((u8 *)bp->bp_vend) == DHCP_ACK) {
+ dhcp_packet_process_options(bp);
+ /* Store net params from reply */
+ store_net_params(bp);
+ dhcp_state = BOUND;
+ printf("DHCP client bound to address %pI4 (%lu ms)\n",
+ &net_ip, get_timer(bootp_start));
+ net_set_timeout_handler(0, (thand_f *)0);
+ bootstage_mark_name(BOOTSTAGE_ID_BOOTP_STOP,
+ "bootp_stop");
+
+ net_auto_load();
+ return;
+ }
+ break;
+ case BOUND:
+ /* DHCP client bound to address */
+ break;
+ default:
+ puts("DHCP: INVALID STATE\n");
+ break;
+ }
+}
+
+void dhcp_request(void)
+{
+ bootp_request();
+}
+#endif /* CONFIG_CMD_DHCP */
diff --git a/roms/u-boot/net/bootp.h b/roms/u-boot/net/bootp.h
new file mode 100644
index 000000000..567340ec5
--- /dev/null
+++ b/roms/u-boot/net/bootp.h
@@ -0,0 +1,93 @@
+/*
+ * Copied from LiMon - BOOTP.
+ *
+ * Copyright 1994, 1995, 2000 Neil Russell.
+ * (See License)
+ * Copyright 2000 Paolo Scaffardi
+ */
+
+#ifndef __BOOTP_H__
+#define __BOOTP_H__
+
+#ifndef __NET_H__
+#include <net.h>
+#endif /* __NET_H__ */
+
+/**********************************************************************/
+
+/*
+ * BOOTP header.
+ */
+#if defined(CONFIG_CMD_DHCP)
+/* Minimum DHCP Options size per RFC2131 - results in 576 byte pkt */
+#define OPT_FIELD_SIZE 312
+#if defined(CONFIG_BOOTP_VENDOREX)
+extern u8 *dhcp_vendorex_prep(u8 *e); /*rtn new e after add own opts. */
+extern u8 *dhcp_vendorex_proc(u8 *e); /*rtn next e if mine,else NULL */
+#endif
+#else
+#define OPT_FIELD_SIZE 64
+#endif
+
+struct bootp_hdr {
+ u8 bp_op; /* Operation */
+# define OP_BOOTREQUEST 1
+# define OP_BOOTREPLY 2
+ u8 bp_htype; /* Hardware type */
+# define HWT_ETHER 1
+ u8 bp_hlen; /* Hardware address length */
+# define HWL_ETHER 6
+ u8 bp_hops; /* Hop count (gateway thing) */
+ u32 bp_id; /* Transaction ID */
+ u16 bp_secs; /* Seconds since boot */
+ u16 bp_spare1; /* Alignment */
+ struct in_addr bp_ciaddr; /* Client IP address */
+ struct in_addr bp_yiaddr; /* Your (client) IP address */
+ struct in_addr bp_siaddr; /* Server IP address */
+ struct in_addr bp_giaddr; /* Gateway IP address */
+ u8 bp_chaddr[16]; /* Client hardware address */
+ char bp_sname[64]; /* Server host name */
+ char bp_file[128]; /* Boot file name */
+ char bp_vend[OPT_FIELD_SIZE]; /* Vendor information */
+} __attribute__((packed));
+
+#define BOOTP_HDR_SIZE sizeof(struct bootp_hdr)
+
+/**********************************************************************/
+/*
+ * Global functions and variables.
+ */
+
+/* bootp.c */
+extern u32 bootp_id; /* ID of cur BOOTP request */
+extern int bootp_try;
+
+
+/* Send a BOOTP request */
+void bootp_reset(void);
+void bootp_request(void);
+
+/****************** DHCP Support *********************/
+void dhcp_request(void);
+
+/* DHCP States */
+typedef enum { INIT,
+ INIT_REBOOT,
+ REBOOTING,
+ SELECTING,
+ REQUESTING,
+ REBINDING,
+ BOUND,
+ RENEWING } dhcp_state_t;
+
+#define DHCP_DISCOVER 1
+#define DHCP_OFFER 2
+#define DHCP_REQUEST 3
+#define DHCP_DECLINE 4
+#define DHCP_ACK 5
+#define DHCP_NAK 6
+#define DHCP_RELEASE 7
+
+/**********************************************************************/
+
+#endif /* __BOOTP_H__ */
diff --git a/roms/u-boot/net/cdp.c b/roms/u-boot/net/cdp.c
new file mode 100644
index 000000000..fac020468
--- /dev/null
+++ b/roms/u-boot/net/cdp.c
@@ -0,0 +1,363 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copied from Linux Monitor (LiMon) - Networking.
+ *
+ * Copyright 1994 - 2000 Neil Russell.
+ * (See License)
+ * Copyright 2000 Roland Borde
+ * Copyright 2000 Paolo Scaffardi
+ * Copyright 2000-2002 Wolfgang Denk, wd@denx.de
+ */
+
+#include <common.h>
+#include <net.h>
+#if defined(CONFIG_CDP_VERSION)
+#include <timestamp.h>
+#endif
+
+#include "cdp.h"
+
+/* Ethernet bcast address */
+const u8 net_cdp_ethaddr[6] = { 0x01, 0x00, 0x0c, 0xcc, 0xcc, 0xcc };
+
+#define CDP_DEVICE_ID_TLV 0x0001
+#define CDP_ADDRESS_TLV 0x0002
+#define CDP_PORT_ID_TLV 0x0003
+#define CDP_CAPABILITIES_TLV 0x0004
+#define CDP_VERSION_TLV 0x0005
+#define CDP_PLATFORM_TLV 0x0006
+#define CDP_NATIVE_VLAN_TLV 0x000a
+#define CDP_APPLIANCE_VLAN_TLV 0x000e
+#define CDP_TRIGGER_TLV 0x000f
+#define CDP_POWER_CONSUMPTION_TLV 0x0010
+#define CDP_SYSNAME_TLV 0x0014
+#define CDP_SYSOBJECT_TLV 0x0015
+#define CDP_MANAGEMENT_ADDRESS_TLV 0x0016
+
+#define CDP_TIMEOUT 250UL /* one packet every 250ms */
+
+static int cdp_seq;
+static int cdp_ok;
+
+ushort cdp_native_vlan;
+ushort cdp_appliance_vlan;
+
+static const uchar cdp_snap_hdr[8] = {
+ 0xAA, 0xAA, 0x03, 0x00, 0x00, 0x0C, 0x20, 0x00 };
+
+static ushort cdp_compute_csum(const uchar *buff, ushort len)
+{
+ ushort csum;
+ int odd;
+ ulong result = 0;
+ ushort leftover;
+ ushort *p;
+
+ if (len > 0) {
+ odd = 1 & (ulong)buff;
+ if (odd) {
+ result = *buff << 8;
+ len--;
+ buff++;
+ }
+ while (len > 1) {
+ p = (ushort *)buff;
+ result += *p++;
+ buff = (uchar *)p;
+ if (result & 0x80000000)
+ result = (result & 0xFFFF) + (result >> 16);
+ len -= 2;
+ }
+ if (len) {
+ leftover = (signed short)(*(const signed char *)buff);
+ /*
+ * CISCO SUCKS big time! (and blows too):
+ * CDP uses the IP checksum algorithm with a twist;
+ * for the last byte it *sign* extends and sums.
+ */
+ result = (result & 0xffff0000) |
+ ((result + leftover) & 0x0000ffff);
+ }
+ while (result >> 16)
+ result = (result & 0xFFFF) + (result >> 16);
+
+ if (odd)
+ result = ((result >> 8) & 0xff) |
+ ((result & 0xff) << 8);
+ }
+
+ /* add up 16-bit and 17-bit words for 17+c bits */
+ result = (result & 0xffff) + (result >> 16);
+ /* add up 16-bit and 2-bit for 16+c bit */
+ result = (result & 0xffff) + (result >> 16);
+ /* add up carry.. */
+ result = (result & 0xffff) + (result >> 16);
+
+ /* negate */
+ csum = ~(ushort)result;
+
+ /* run time endian detection */
+ if (csum != htons(csum)) /* little endian */
+ csum = htons(csum);
+
+ return csum;
+}
+
+static int cdp_send_trigger(void)
+{
+ uchar *pkt;
+ ushort *s;
+ ushort *cp;
+ struct ethernet_hdr *et;
+ int len;
+ ushort chksum;
+#if defined(CONFIG_CDP_DEVICE_ID) || defined(CONFIG_CDP_PORT_ID) || \
+ defined(CONFIG_CDP_VERSION) || defined(CONFIG_CDP_PLATFORM)
+ char buf[32];
+#endif
+
+ pkt = net_tx_packet;
+ et = (struct ethernet_hdr *)pkt;
+
+ /* NOTE: trigger sent not on any VLAN */
+
+ /* form ethernet header */
+ memcpy(et->et_dest, net_cdp_ethaddr, 6);
+ memcpy(et->et_src, net_ethaddr, 6);
+
+ pkt += ETHER_HDR_SIZE;
+
+ /* SNAP header */
+ memcpy((uchar *)pkt, cdp_snap_hdr, sizeof(cdp_snap_hdr));
+ pkt += sizeof(cdp_snap_hdr);
+
+ /* CDP header */
+ *pkt++ = 0x02; /* CDP version 2 */
+ *pkt++ = 180; /* TTL */
+ s = (ushort *)pkt;
+ cp = s;
+ /* checksum (0 for later calculation) */
+ *s++ = htons(0);
+
+ /* CDP fields */
+#ifdef CONFIG_CDP_DEVICE_ID
+ *s++ = htons(CDP_DEVICE_ID_TLV);
+ *s++ = htons(CONFIG_CDP_DEVICE_ID);
+ sprintf(buf, CONFIG_CDP_DEVICE_ID_PREFIX "%pm", net_ethaddr);
+ memcpy((uchar *)s, buf, 16);
+ s += 16 / 2;
+#endif
+
+#ifdef CONFIG_CDP_PORT_ID
+ *s++ = htons(CDP_PORT_ID_TLV);
+ memset(buf, 0, sizeof(buf));
+ sprintf(buf, CONFIG_CDP_PORT_ID, eth_get_dev_index());
+ len = strlen(buf);
+ if (len & 1) /* make it even */
+ len++;
+ *s++ = htons(len + 4);
+ memcpy((uchar *)s, buf, len);
+ s += len / 2;
+#endif
+
+#ifdef CONFIG_CDP_CAPABILITIES
+ *s++ = htons(CDP_CAPABILITIES_TLV);
+ *s++ = htons(8);
+ *(ulong *)s = htonl(CONFIG_CDP_CAPABILITIES);
+ s += 2;
+#endif
+
+#ifdef CONFIG_CDP_VERSION
+ *s++ = htons(CDP_VERSION_TLV);
+ memset(buf, 0, sizeof(buf));
+ strcpy(buf, CONFIG_CDP_VERSION);
+ len = strlen(buf);
+ if (len & 1) /* make it even */
+ len++;
+ *s++ = htons(len + 4);
+ memcpy((uchar *)s, buf, len);
+ s += len / 2;
+#endif
+
+#ifdef CONFIG_CDP_PLATFORM
+ *s++ = htons(CDP_PLATFORM_TLV);
+ memset(buf, 0, sizeof(buf));
+ strcpy(buf, CONFIG_CDP_PLATFORM);
+ len = strlen(buf);
+ if (len & 1) /* make it even */
+ len++;
+ *s++ = htons(len + 4);
+ memcpy((uchar *)s, buf, len);
+ s += len / 2;
+#endif
+
+#ifdef CONFIG_CDP_TRIGGER
+ *s++ = htons(CDP_TRIGGER_TLV);
+ *s++ = htons(8);
+ *(ulong *)s = htonl(CONFIG_CDP_TRIGGER);
+ s += 2;
+#endif
+
+#ifdef CONFIG_CDP_POWER_CONSUMPTION
+ *s++ = htons(CDP_POWER_CONSUMPTION_TLV);
+ *s++ = htons(6);
+ *s++ = htons(CONFIG_CDP_POWER_CONSUMPTION);
+#endif
+
+ /* length of ethernet packet */
+ len = (uchar *)s - ((uchar *)net_tx_packet + ETHER_HDR_SIZE);
+ et->et_protlen = htons(len);
+
+ len = ETHER_HDR_SIZE + sizeof(cdp_snap_hdr);
+ chksum = cdp_compute_csum((uchar *)net_tx_packet + len,
+ (uchar *)s - (net_tx_packet + len));
+ if (chksum == 0)
+ chksum = 0xFFFF;
+ *cp = htons(chksum);
+
+ net_send_packet(net_tx_packet, (uchar *)s - net_tx_packet);
+ return 0;
+}
+
+static void cdp_timeout_handler(void)
+{
+ cdp_seq++;
+
+ if (cdp_seq < 3) {
+ net_set_timeout_handler(CDP_TIMEOUT, cdp_timeout_handler);
+ cdp_send_trigger();
+ return;
+ }
+
+ /* if not OK try again */
+ if (!cdp_ok)
+ net_start_again();
+ else
+ net_set_state(NETLOOP_SUCCESS);
+}
+
+void cdp_receive(const uchar *pkt, unsigned len)
+{
+ const uchar *t;
+ const ushort *ss;
+ ushort type, tlen;
+ ushort vlan, nvlan;
+
+ /* minimum size? */
+ if (len < sizeof(cdp_snap_hdr) + 4)
+ goto pkt_short;
+
+ /* check for valid CDP SNAP header */
+ if (memcmp(pkt, cdp_snap_hdr, sizeof(cdp_snap_hdr)) != 0)
+ return;
+
+ pkt += sizeof(cdp_snap_hdr);
+ len -= sizeof(cdp_snap_hdr);
+
+ /* Version of CDP protocol must be >= 2 and TTL != 0 */
+ if (pkt[0] < 0x02 || pkt[1] == 0)
+ return;
+
+ /*
+ * if version is greater than 0x02 maybe we'll have a problem;
+ * output a warning
+ */
+ if (pkt[0] != 0x02)
+ printf("**WARNING: CDP packet received with a protocol version "
+ "%d > 2\n", pkt[0] & 0xff);
+
+ if (cdp_compute_csum(pkt, len) != 0)
+ return;
+
+ pkt += 4;
+ len -= 4;
+
+ vlan = htons(-1);
+ nvlan = htons(-1);
+ while (len > 0) {
+ if (len < 4)
+ goto pkt_short;
+
+ ss = (const ushort *)pkt;
+ type = ntohs(ss[0]);
+ tlen = ntohs(ss[1]);
+ if (tlen > len)
+ goto pkt_short;
+
+ pkt += tlen;
+ len -= tlen;
+
+ ss += 2; /* point ss to the data of the TLV */
+ tlen -= 4;
+
+ switch (type) {
+ case CDP_DEVICE_ID_TLV:
+ break;
+ case CDP_ADDRESS_TLV:
+ break;
+ case CDP_PORT_ID_TLV:
+ break;
+ case CDP_CAPABILITIES_TLV:
+ break;
+ case CDP_VERSION_TLV:
+ break;
+ case CDP_PLATFORM_TLV:
+ break;
+ case CDP_NATIVE_VLAN_TLV:
+ nvlan = *ss;
+ break;
+ case CDP_APPLIANCE_VLAN_TLV:
+ t = (const uchar *)ss;
+ while (tlen > 0) {
+ if (tlen < 3)
+ goto pkt_short;
+
+ ss = (const ushort *)(t + 1);
+
+#ifdef CONFIG_CDP_APPLIANCE_VLAN_TYPE
+ if (t[0] == CONFIG_CDP_APPLIANCE_VLAN_TYPE)
+ vlan = *ss;
+#else
+ /* XXX will this work; dunno */
+ vlan = ntohs(*ss);
+#endif
+ t += 3; tlen -= 3;
+ }
+ break;
+ case CDP_TRIGGER_TLV:
+ break;
+ case CDP_POWER_CONSUMPTION_TLV:
+ break;
+ case CDP_SYSNAME_TLV:
+ break;
+ case CDP_SYSOBJECT_TLV:
+ break;
+ case CDP_MANAGEMENT_ADDRESS_TLV:
+ break;
+ }
+ }
+
+ cdp_appliance_vlan = vlan;
+ cdp_native_vlan = nvlan;
+
+ cdp_ok = 1;
+ return;
+
+pkt_short:
+ printf("** CDP packet is too short\n");
+ return;
+}
+
+void cdp_start(void)
+{
+ printf("Using %s device\n", eth_get_name());
+ cdp_seq = 0;
+ cdp_ok = 0;
+
+ cdp_native_vlan = htons(-1);
+ cdp_appliance_vlan = htons(-1);
+
+ net_set_timeout_handler(CDP_TIMEOUT, cdp_timeout_handler);
+
+ cdp_send_trigger();
+}
diff --git a/roms/u-boot/net/cdp.h b/roms/u-boot/net/cdp.h
new file mode 100644
index 000000000..16ccbf4b5
--- /dev/null
+++ b/roms/u-boot/net/cdp.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copied from Linux Monitor (LiMon) - Networking.
+ *
+ * Copyright 1994 - 2000 Neil Russell.
+ * (See License)
+ * Copyright 2000 Roland Borde
+ * Copyright 2000 Paolo Scaffardi
+ * Copyright 2000-2002 Wolfgang Denk, wd@denx.de
+ */
+
+#if defined(CONFIG_CMD_CDP)
+
+#ifndef __CDP_H__
+#define __CDP_H__
+
+void cdp_start(void);
+/* Process a received CDP packet */
+void cdp_receive(const uchar *pkt, unsigned len);
+
+#endif /* __CDP_H__ */
+#endif
diff --git a/roms/u-boot/net/dns.c b/roms/u-boot/net/dns.c
new file mode 100644
index 000000000..5b1fe5b01
--- /dev/null
+++ b/roms/u-boot/net/dns.c
@@ -0,0 +1,219 @@
+/*
+ * DNS support driver
+ *
+ * Copyright (c) 2008 Pieter Voorthuijsen <pieter.voorthuijsen@prodrive.nl>
+ * Copyright (c) 2009 Robin Getz <rgetz@blackfin.uclinux.org>
+ *
+ * This is a simple DNS implementation for U-Boot. It will use the first IP
+ * in the DNS response as net_server_ip. This can then be used for any other
+ * network related activities.
+ *
+ * The packet handling is partly based on TADNS, original copyrights
+ * follow below.
+ *
+ */
+
+/*
+ * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
+ *
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Sergey Lyubka wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.
+ */
+
+#include <common.h>
+#include <command.h>
+#include <env.h>
+#include <log.h>
+#include <net.h>
+#include <asm/unaligned.h>
+
+#include "dns.h"
+
+char *net_dns_resolve; /* The host to resolve */
+char *net_dns_env_var; /* The envvar to store the answer in */
+
+static int dns_our_port;
+
+/*
+ * make port a little random (1024-17407)
+ * This keeps the math somewhat trivial to compute, and seems to work with
+ * all supported protocols/clients/servers
+ */
+static unsigned int random_port(void)
+{
+ return 1024 + (get_timer(0) % 0x4000);
+}
+
+static void dns_send(void)
+{
+ struct header *header;
+ int n, name_len;
+ uchar *p, *pkt;
+ const char *s;
+ const char *name;
+ enum dns_query_type qtype = DNS_A_RECORD;
+
+ name = net_dns_resolve;
+ pkt = (uchar *)(net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE);
+ p = pkt;
+
+ /* Prepare DNS packet header */
+ header = (struct header *)pkt;
+ header->tid = 1;
+ header->flags = htons(0x100); /* standard query */
+ header->nqueries = htons(1); /* Just one query */
+ header->nanswers = 0;
+ header->nauth = 0;
+ header->nother = 0;
+
+ /* Encode DNS name */
+ name_len = strlen(name);
+ p = (uchar *)&header->data; /* For encoding host name into packet */
+
+ do {
+ s = strchr(name, '.');
+ if (!s)
+ s = name + name_len;
+
+ n = s - name; /* Chunk length */
+ *p++ = n; /* Copy length */
+ memcpy(p, name, n); /* Copy chunk */
+ p += n;
+
+ if (*s == '.')
+ n++;
+
+ name += n;
+ name_len -= n;
+ } while (*s != '\0');
+
+ *p++ = 0; /* Mark end of host name */
+ *p++ = 0; /* Some servers require double null */
+ *p++ = (unsigned char) qtype; /* Query Type */
+
+ *p++ = 0;
+ *p++ = 1; /* Class: inet, 0x0001 */
+
+ n = p - pkt; /* Total packet length */
+ debug("Packet size %d\n", n);
+
+ dns_our_port = random_port();
+
+ net_send_udp_packet(net_server_ethaddr, net_dns_server,
+ DNS_SERVICE_PORT, dns_our_port, n);
+ debug("DNS packet sent\n");
+}
+
+static void dns_timeout_handler(void)
+{
+ puts("Timeout\n");
+ net_set_state(NETLOOP_FAIL);
+}
+
+static void dns_handler(uchar *pkt, unsigned dest, struct in_addr sip,
+ unsigned src, unsigned len)
+{
+ struct header *header;
+ const unsigned char *p, *e, *s;
+ u16 type, i;
+ int found, stop, dlen;
+ char ip_str[22];
+ struct in_addr ip_addr;
+
+
+ debug("%s\n", __func__);
+ if (dest != dns_our_port)
+ return;
+
+ for (i = 0; i < len; i += 4)
+ debug("0x%p - 0x%.2x 0x%.2x 0x%.2x 0x%.2x\n",
+ pkt+i, pkt[i], pkt[i+1], pkt[i+2], pkt[i+3]);
+
+ /* We sent one query. We want to have a single answer: */
+ header = (struct header *)pkt;
+ if (ntohs(header->nqueries) != 1)
+ return;
+
+ /* Received 0 answers */
+ if (header->nanswers == 0) {
+ puts("DNS: host not found\n");
+ net_set_state(NETLOOP_SUCCESS);
+ return;
+ }
+
+ /* Skip host name */
+ s = &header->data[0];
+ e = pkt + len;
+ for (p = s; p < e && *p != '\0'; p++)
+ continue;
+
+ /* We sent query class 1, query type 1 */
+ if (&p[5] > e || get_unaligned_be16(p+1) != DNS_A_RECORD) {
+ puts("DNS: response was not an A record\n");
+ net_set_state(NETLOOP_SUCCESS);
+ return;
+ }
+
+ /* Go to the first answer section */
+ p += 5;
+
+ /* Loop through the answers, we want A type answer */
+ for (found = stop = 0; !stop && &p[12] < e; ) {
+ /* Skip possible name in CNAME answer */
+ if (*p != 0xc0) {
+ while (*p && &p[12] < e)
+ p++;
+ p--;
+ }
+ debug("Name (Offset in header): %d\n", p[1]);
+
+ type = get_unaligned_be16(p+2);
+ debug("type = %d\n", type);
+ if (type == DNS_CNAME_RECORD) {
+ /* CNAME answer. shift to the next section */
+ debug("Found canonical name\n");
+ dlen = get_unaligned_be16(p+10);
+ debug("dlen = %d\n", dlen);
+ p += 12 + dlen;
+ } else if (type == DNS_A_RECORD) {
+ debug("Found A-record\n");
+ found = 1;
+ stop = 1;
+ } else {
+ debug("Unknown type\n");
+ stop = 1;
+ }
+ }
+
+ if (found && &p[12] < e) {
+ dlen = get_unaligned_be16(p+10);
+ p += 12;
+ memcpy(&ip_addr, p, 4);
+
+ if (p + dlen <= e) {
+ ip_to_string(ip_addr, ip_str);
+ printf("%s\n", ip_str);
+ if (net_dns_env_var)
+ env_set(net_dns_env_var, ip_str);
+ } else {
+ puts("server responded with invalid IP number\n");
+ }
+ }
+
+ net_set_state(NETLOOP_SUCCESS);
+}
+
+void dns_start(void)
+{
+ debug("%s\n", __func__);
+
+ net_set_timeout_handler(DNS_TIMEOUT, dns_timeout_handler);
+ net_set_udp_handler(dns_handler);
+
+ /* Clear a previous MAC address, the server IP might have changed. */
+ memset(net_server_ethaddr, 0, sizeof(net_server_ethaddr));
+
+ dns_send();
+}
diff --git a/roms/u-boot/net/dns.h b/roms/u-boot/net/dns.h
new file mode 100644
index 000000000..79ac76f59
--- /dev/null
+++ b/roms/u-boot/net/dns.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * (C) Masami Komiya <mkomiya@sonare.it> 2005
+ * Copyright 2009, Robin Getz <rgetz@blackfin.uclinux.org>
+ */
+
+#ifndef __DNS_H__
+#define __DNS_H__
+
+#define DNS_SERVICE_PORT 53
+#define DNS_TIMEOUT 10000UL
+
+/* http://en.wikipedia.org/wiki/List_of_DNS_record_types */
+enum dns_query_type {
+ DNS_A_RECORD = 0x01,
+ DNS_CNAME_RECORD = 0x05,
+ DNS_MX_RECORD = 0x0f,
+};
+
+/*
+ * DNS network packet
+ */
+struct header {
+ uint16_t tid; /* Transaction ID */
+ uint16_t flags; /* Flags */
+ uint16_t nqueries; /* Questions */
+ uint16_t nanswers; /* Answers */
+ uint16_t nauth; /* Authority PRs */
+ uint16_t nother; /* Other PRs */
+ unsigned char data[1]; /* Data, variable length */
+} __attribute__((packed));
+
+void dns_start(void); /* Begin DNS */
+
+#endif
diff --git a/roms/u-boot/net/dsa-uclass.c b/roms/u-boot/net/dsa-uclass.c
new file mode 100644
index 000000000..7ea1cb694
--- /dev/null
+++ b/roms/u-boot/net/dsa-uclass.c
@@ -0,0 +1,463 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2019-2021 NXP
+ */
+
+#include <net/dsa.h>
+#include <dm/lists.h>
+#include <dm/device_compat.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
+#include <linux/bitmap.h>
+#include <miiphy.h>
+
+#define DSA_PORT_CHILD_DRV_NAME "dsa-port"
+
+/* per-device internal state structure */
+struct dsa_priv {
+ struct phy_device *cpu_port_fixed_phy;
+ struct udevice *master_dev;
+ int num_ports;
+ u32 cpu_port;
+ int headroom;
+ int tailroom;
+};
+
+/* external API */
+int dsa_set_tagging(struct udevice *dev, ushort headroom, ushort tailroom)
+{
+ struct dsa_priv *priv;
+
+ if (!dev)
+ return -EINVAL;
+
+ if (headroom + tailroom > DSA_MAX_OVR)
+ return -EINVAL;
+
+ priv = dev_get_uclass_priv(dev);
+
+ if (headroom > 0)
+ priv->headroom = headroom;
+ if (tailroom > 0)
+ priv->tailroom = tailroom;
+
+ return 0;
+}
+
+/* returns the DSA master Ethernet device */
+struct udevice *dsa_get_master(struct udevice *dev)
+{
+ struct dsa_priv *priv;
+
+ if (!dev)
+ return NULL;
+
+ priv = dev_get_uclass_priv(dev);
+
+ return priv->master_dev;
+}
+
+/*
+ * Start the desired port, the CPU port and the master Eth interface.
+ * TODO: if cascaded we may need to _start ports in other switches too
+ */
+static int dsa_port_start(struct udevice *pdev)
+{
+ struct udevice *dev = dev_get_parent(pdev);
+ struct dsa_priv *priv = dev_get_uclass_priv(dev);
+ struct udevice *master = dsa_get_master(dev);
+ struct dsa_ops *ops = dsa_get_ops(dev);
+ int err;
+
+ if (ops->port_enable) {
+ struct dsa_port_pdata *port_pdata;
+
+ port_pdata = dev_get_parent_plat(pdev);
+ err = ops->port_enable(dev, port_pdata->index,
+ port_pdata->phy);
+ if (err)
+ return err;
+
+ err = ops->port_enable(dev, priv->cpu_port,
+ priv->cpu_port_fixed_phy);
+ if (err)
+ return err;
+ }
+
+ return eth_get_ops(master)->start(master);
+}
+
+/* Stop the desired port, the CPU port and the master Eth interface */
+static void dsa_port_stop(struct udevice *pdev)
+{
+ struct udevice *dev = dev_get_parent(pdev);
+ struct dsa_priv *priv = dev_get_uclass_priv(dev);
+ struct udevice *master = dsa_get_master(dev);
+ struct dsa_ops *ops = dsa_get_ops(dev);
+
+ if (ops->port_disable) {
+ struct dsa_port_pdata *port_pdata;
+
+ port_pdata = dev_get_parent_plat(pdev);
+ ops->port_disable(dev, port_pdata->index, port_pdata->phy);
+ ops->port_disable(dev, priv->cpu_port, NULL);
+ }
+
+ eth_get_ops(master)->stop(master);
+}
+
+/*
+ * Insert a DSA tag and call master Ethernet send on the resulting packet
+ * We copy the frame to a stack buffer where we have reserved headroom and
+ * tailroom space. Headroom and tailroom are set to 0.
+ */
+static int dsa_port_send(struct udevice *pdev, void *packet, int length)
+{
+ struct udevice *dev = dev_get_parent(pdev);
+ struct dsa_priv *priv = dev_get_uclass_priv(dev);
+ int head = priv->headroom, tail = priv->tailroom;
+ struct udevice *master = dsa_get_master(dev);
+ struct dsa_ops *ops = dsa_get_ops(dev);
+ uchar dsa_packet_tmp[PKTSIZE_ALIGN];
+ struct dsa_port_pdata *port_pdata;
+ int err;
+
+ if (length + head + tail > PKTSIZE_ALIGN)
+ return -EINVAL;
+
+ memset(dsa_packet_tmp, 0, head);
+ memset(dsa_packet_tmp + head + length, 0, tail);
+ memcpy(dsa_packet_tmp + head, packet, length);
+ length += head + tail;
+ /* copy back to preserve original buffer alignment */
+ memcpy(packet, dsa_packet_tmp, length);
+
+ port_pdata = dev_get_parent_plat(pdev);
+ err = ops->xmit(dev, port_pdata->index, packet, length);
+ if (err)
+ return err;
+
+ return eth_get_ops(master)->send(master, packet, length);
+}
+
+/* Receive a frame from master Ethernet, process it and pass it on */
+static int dsa_port_recv(struct udevice *pdev, int flags, uchar **packetp)
+{
+ struct udevice *dev = dev_get_parent(pdev);
+ struct dsa_priv *priv = dev_get_uclass_priv(dev);
+ int head = priv->headroom, tail = priv->tailroom;
+ struct udevice *master = dsa_get_master(dev);
+ struct dsa_ops *ops = dsa_get_ops(dev);
+ struct dsa_port_pdata *port_pdata;
+ int length, port_index, err;
+
+ length = eth_get_ops(master)->recv(master, flags, packetp);
+ if (length <= 0)
+ return length;
+
+ /*
+ * If we receive frames from a different port or frames that DSA driver
+ * doesn't like we discard them here.
+ * In case of discard we return with no frame and expect to be called
+ * again instead of looping here, so upper layer can deal with timeouts.
+ */
+ port_pdata = dev_get_parent_plat(pdev);
+ err = ops->rcv(dev, &port_index, *packetp, length);
+ if (err || port_index != port_pdata->index || (length <= head + tail)) {
+ if (eth_get_ops(master)->free_pkt)
+ eth_get_ops(master)->free_pkt(master, *packetp, length);
+ return -EAGAIN;
+ }
+
+ /*
+ * We move the pointer over headroom here to avoid a copy. If free_pkt
+ * gets called we move the pointer back before calling master free_pkt.
+ */
+ *packetp += head;
+
+ return length - head - tail;
+}
+
+static int dsa_port_free_pkt(struct udevice *pdev, uchar *packet, int length)
+{
+ struct udevice *dev = dev_get_parent(pdev);
+ struct udevice *master = dsa_get_master(dev);
+ struct dsa_priv *priv;
+
+ priv = dev_get_uclass_priv(dev);
+ if (eth_get_ops(master)->free_pkt) {
+ /* return the original pointer and length to master Eth */
+ packet -= priv->headroom;
+ length += priv->headroom - priv->tailroom;
+
+ return eth_get_ops(master)->free_pkt(master, packet, length);
+ }
+
+ return 0;
+}
+
+static int dsa_port_of_to_pdata(struct udevice *pdev)
+{
+ struct dsa_port_pdata *port_pdata;
+ struct dsa_pdata *dsa_pdata;
+ struct eth_pdata *eth_pdata;
+ struct udevice *dev;
+ const char *label;
+ u32 index;
+ int err;
+
+ if (!pdev)
+ return -ENODEV;
+
+ err = ofnode_read_u32(dev_ofnode(pdev), "reg", &index);
+ if (err)
+ return err;
+
+ dev = dev_get_parent(pdev);
+ dsa_pdata = dev_get_uclass_plat(dev);
+
+ port_pdata = dev_get_parent_plat(pdev);
+ port_pdata->index = index;
+
+ label = ofnode_read_string(dev_ofnode(pdev), "label");
+ if (label)
+ strncpy(port_pdata->name, label, DSA_PORT_NAME_LENGTH);
+
+ eth_pdata = dev_get_plat(pdev);
+ eth_pdata->priv_pdata = port_pdata;
+
+ dev_dbg(pdev, "port %d node %s\n", port_pdata->index,
+ ofnode_get_name(dev_ofnode(pdev)));
+
+ return 0;
+}
+
+static const struct eth_ops dsa_port_ops = {
+ .start = dsa_port_start,
+ .send = dsa_port_send,
+ .recv = dsa_port_recv,
+ .stop = dsa_port_stop,
+ .free_pkt = dsa_port_free_pkt,
+};
+
+static int dsa_port_probe(struct udevice *pdev)
+{
+ struct udevice *dev = dev_get_parent(pdev);
+ struct eth_pdata *eth_pdata, *master_pdata;
+ unsigned char env_enetaddr[ARP_HLEN];
+ struct dsa_port_pdata *port_pdata;
+ struct dsa_priv *dsa_priv;
+ struct udevice *master;
+ int ret;
+
+ port_pdata = dev_get_parent_plat(pdev);
+ dsa_priv = dev_get_uclass_priv(dev);
+
+ port_pdata->phy = dm_eth_phy_connect(pdev);
+ if (!port_pdata->phy)
+ return -ENODEV;
+
+ master = dsa_get_master(dev);
+ if (!master)
+ return -ENODEV;
+
+ /*
+ * Probe the master device. We depend on the master device for proper
+ * operation and we also need it for MAC inheritance below.
+ *
+ * TODO: we assume the master device is always there and doesn't get
+ * removed during runtime.
+ */
+ ret = device_probe(master);
+ if (ret)
+ return ret;
+
+ /*
+ * Inherit port's hwaddr from the DSA master, unless the port already
+ * has a unique MAC address specified in the environment.
+ */
+ eth_env_get_enetaddr_by_index("eth", dev_seq(pdev), env_enetaddr);
+ if (!is_zero_ethaddr(env_enetaddr))
+ return 0;
+
+ master_pdata = dev_get_plat(master);
+ eth_pdata = dev_get_plat(pdev);
+ memcpy(eth_pdata->enetaddr, master_pdata->enetaddr, ARP_HLEN);
+ eth_env_set_enetaddr_by_index("eth", dev_seq(pdev),
+ master_pdata->enetaddr);
+
+ return 0;
+}
+
+static int dsa_port_remove(struct udevice *pdev)
+{
+ struct udevice *dev = dev_get_parent(pdev);
+ struct dsa_port_pdata *port_pdata;
+ struct dsa_priv *dsa_priv;
+
+ port_pdata = dev_get_parent_plat(pdev);
+ dsa_priv = dev_get_uclass_priv(dev);
+
+ port_pdata->phy = NULL;
+
+ return 0;
+}
+
+U_BOOT_DRIVER(dsa_port) = {
+ .name = DSA_PORT_CHILD_DRV_NAME,
+ .id = UCLASS_ETH,
+ .ops = &dsa_port_ops,
+ .probe = dsa_port_probe,
+ .remove = dsa_port_remove,
+ .of_to_plat = dsa_port_of_to_pdata,
+ .plat_auto = sizeof(struct eth_pdata),
+};
+
+/*
+ * This function mostly deals with pulling information out of the device tree
+ * into the pdata structure.
+ * It goes through the list of switch ports, registers an eth device for each
+ * front panel port and identifies the cpu port connected to master eth device.
+ * TODO: support cascaded switches
+ */
+static int dsa_post_bind(struct udevice *dev)
+{
+ struct dsa_pdata *pdata = dev_get_uclass_plat(dev);
+ ofnode node = dev_ofnode(dev), pnode;
+ int i, err, first_err = 0;
+
+ if (!ofnode_valid(node))
+ return -ENODEV;
+
+ pdata->master_node = ofnode_null();
+
+ node = ofnode_find_subnode(node, "ports");
+ if (!ofnode_valid(node))
+ node = ofnode_find_subnode(node, "ethernet-ports");
+ if (!ofnode_valid(node)) {
+ dev_err(dev, "ports node is missing under DSA device!\n");
+ return -EINVAL;
+ }
+
+ pdata->num_ports = ofnode_get_child_count(node);
+ if (pdata->num_ports <= 0 || pdata->num_ports > DSA_MAX_PORTS) {
+ dev_err(dev, "invalid number of ports (%d)\n",
+ pdata->num_ports);
+ return -EINVAL;
+ }
+
+ /* look for the CPU port */
+ ofnode_for_each_subnode(pnode, node) {
+ u32 ethernet;
+
+ if (ofnode_read_u32(pnode, "ethernet", &ethernet))
+ continue;
+
+ pdata->master_node = ofnode_get_by_phandle(ethernet);
+ pdata->cpu_port_node = pnode;
+ break;
+ }
+
+ if (!ofnode_valid(pdata->master_node)) {
+ dev_err(dev, "master eth node missing!\n");
+ return -EINVAL;
+ }
+
+ if (ofnode_read_u32(pnode, "reg", &pdata->cpu_port)) {
+ dev_err(dev, "CPU port node not valid!\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "master node %s on port %d\n",
+ ofnode_get_name(pdata->master_node), pdata->cpu_port);
+
+ for (i = 0; i < pdata->num_ports; i++) {
+ char name[DSA_PORT_NAME_LENGTH];
+ struct udevice *pdev;
+
+ /*
+ * If this is the CPU port don't register it as an ETH device,
+ * we skip it on purpose since I/O to/from it from the CPU
+ * isn't useful.
+ */
+ if (i == pdata->cpu_port)
+ continue;
+
+ /*
+ * Set up default port names. If present, DT port labels
+ * will override the default port names.
+ */
+ snprintf(name, DSA_PORT_NAME_LENGTH, "%s@%d", dev->name, i);
+
+ ofnode_for_each_subnode(pnode, node) {
+ u32 reg;
+
+ if (ofnode_read_u32(pnode, "reg", &reg))
+ continue;
+
+ if (reg == i)
+ break;
+ }
+
+ /*
+ * skip registration if port id not found or if the port
+ * is explicitly disabled in DT
+ */
+ if (!ofnode_valid(pnode) || !ofnode_is_available(pnode))
+ continue;
+
+ err = device_bind_driver_to_node(dev, DSA_PORT_CHILD_DRV_NAME,
+ name, pnode, &pdev);
+ if (pdev) {
+ struct dsa_port_pdata *port_pdata;
+
+ port_pdata = dev_get_parent_plat(pdev);
+ strncpy(port_pdata->name, name, DSA_PORT_NAME_LENGTH);
+ pdev->name = port_pdata->name;
+ }
+
+ /* try to bind all ports but keep 1st error */
+ if (err && !first_err)
+ first_err = err;
+ }
+
+ if (first_err)
+ return first_err;
+
+ dev_dbg(dev, "DSA ports successfully bound\n");
+
+ return 0;
+}
+
+/**
+ * Initialize the uclass per device internal state structure (priv).
+ * TODO: pick up references to other switch devices here, if we're cascaded.
+ */
+static int dsa_pre_probe(struct udevice *dev)
+{
+ struct dsa_pdata *pdata = dev_get_uclass_plat(dev);
+ struct dsa_priv *priv = dev_get_uclass_priv(dev);
+
+ priv->num_ports = pdata->num_ports;
+ priv->cpu_port = pdata->cpu_port;
+ priv->cpu_port_fixed_phy = fixed_phy_create(pdata->cpu_port_node);
+ if (!priv->cpu_port_fixed_phy) {
+ dev_err(dev, "Failed to register fixed-link for CPU port\n");
+ return -ENODEV;
+ }
+
+ uclass_find_device_by_ofnode(UCLASS_ETH, pdata->master_node,
+ &priv->master_dev);
+ return 0;
+}
+
+UCLASS_DRIVER(dsa) = {
+ .id = UCLASS_DSA,
+ .name = "dsa",
+ .post_bind = dsa_post_bind,
+ .pre_probe = dsa_pre_probe,
+ .per_device_auto = sizeof(struct dsa_priv),
+ .per_device_plat_auto = sizeof(struct dsa_pdata),
+ .per_child_plat_auto = sizeof(struct dsa_port_pdata),
+ .flags = DM_UC_FLAG_SEQ_ALIAS,
+};
diff --git a/roms/u-boot/net/eth-uclass.c b/roms/u-boot/net/eth-uclass.c
new file mode 100644
index 000000000..5146bd666
--- /dev/null
+++ b/roms/u-boot/net/eth-uclass.c
@@ -0,0 +1,618 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2001-2015
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ * Joe Hershberger, National Instruments
+ */
+
+#include <common.h>
+#include <bootstage.h>
+#include <dm.h>
+#include <env.h>
+#include <log.h>
+#include <net.h>
+#include <asm/global_data.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
+#include <net/pcap.h>
+#include "eth_internal.h"
+#include <eth_phy.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/**
+ * struct eth_device_priv - private structure for each Ethernet device
+ *
+ * @state: The state of the Ethernet MAC driver (defined by enum eth_state_t)
+ */
+struct eth_device_priv {
+ enum eth_state_t state;
+ bool running;
+};
+
+/**
+ * struct eth_uclass_priv - The structure attached to the uclass itself
+ *
+ * @current: The Ethernet device that the network functions are using
+ */
+struct eth_uclass_priv {
+ struct udevice *current;
+};
+
+/* eth_errno - This stores the most recent failure code from DM functions */
+static int eth_errno;
+
+static struct eth_uclass_priv *eth_get_uclass_priv(void)
+{
+ struct uclass *uc;
+ int ret;
+
+ ret = uclass_get(UCLASS_ETH, &uc);
+ if (ret)
+ return NULL;
+
+ assert(uc);
+ return uclass_get_priv(uc);
+}
+
+void eth_set_current_to_next(void)
+{
+ struct eth_uclass_priv *uc_priv;
+
+ uc_priv = eth_get_uclass_priv();
+ if (uc_priv->current)
+ uclass_next_device(&uc_priv->current);
+ if (!uc_priv->current)
+ uclass_first_device(UCLASS_ETH, &uc_priv->current);
+}
+
+/*
+ * Typically this will simply return the active device.
+ * In the case where the most recent active device was unset, this will attempt
+ * to return the device with sequence id 0 (which can be configured by the
+ * device tree). If this fails, fall back to just getting the first device.
+ * The latter is non-deterministic and depends on the order of the probing.
+ * If that device doesn't exist or fails to probe, this function will return
+ * NULL.
+ */
+struct udevice *eth_get_dev(void)
+{
+ struct eth_uclass_priv *uc_priv;
+
+ uc_priv = eth_get_uclass_priv();
+ if (!uc_priv)
+ return NULL;
+
+ if (!uc_priv->current) {
+ eth_errno = uclass_get_device_by_seq(UCLASS_ETH, 0,
+ &uc_priv->current);
+ if (eth_errno)
+ eth_errno = uclass_first_device(UCLASS_ETH,
+ &uc_priv->current);
+ }
+ return uc_priv->current;
+}
+
+/*
+ * Typically this will just store a device pointer.
+ * In case it was not probed, we will attempt to do so.
+ * dev may be NULL to unset the active device.
+ */
+void eth_set_dev(struct udevice *dev)
+{
+ if (dev && !device_active(dev)) {
+ eth_errno = device_probe(dev);
+ if (eth_errno)
+ dev = NULL;
+ }
+
+ eth_get_uclass_priv()->current = dev;
+}
+
+/*
+ * Find the udevice that either has the name passed in as devname or has an
+ * alias named devname.
+ */
+struct udevice *eth_get_dev_by_name(const char *devname)
+{
+ int seq = -1;
+ char *endp = NULL;
+ const char *startp = NULL;
+ struct udevice *it;
+ struct uclass *uc;
+ int len = strlen("eth");
+ int ret;
+
+ /* Must be longer than 3 to be an alias */
+ if (!strncmp(devname, "eth", len) && strlen(devname) > len) {
+ startp = devname + len;
+ seq = simple_strtoul(startp, &endp, 10);
+ }
+
+ ret = uclass_get(UCLASS_ETH, &uc);
+ if (ret)
+ return NULL;
+
+ uclass_foreach_dev(it, uc) {
+ /*
+ * We don't care about errors from probe here. Either they won't
+ * match an alias or it will match a literal name and we'll pick
+ * up the error when we try to probe again in eth_set_dev().
+ */
+ if (device_probe(it))
+ continue;
+ /* Check for the name or the sequence number to match */
+ if (strcmp(it->name, devname) == 0 ||
+ (endp > startp && dev_seq(it) == seq))
+ return it;
+ }
+
+ return NULL;
+}
+
+unsigned char *eth_get_ethaddr(void)
+{
+ struct eth_pdata *pdata;
+
+ if (eth_get_dev()) {
+ pdata = dev_get_plat(eth_get_dev());
+ return pdata->enetaddr;
+ }
+
+ return NULL;
+}
+
+/* Set active state without calling start on the driver */
+int eth_init_state_only(void)
+{
+ struct udevice *current;
+ struct eth_device_priv *priv;
+
+ current = eth_get_dev();
+ if (!current || !device_active(current))
+ return -EINVAL;
+
+ priv = dev_get_uclass_priv(current);
+ priv->state = ETH_STATE_ACTIVE;
+
+ return 0;
+}
+
+/* Set passive state without calling stop on the driver */
+void eth_halt_state_only(void)
+{
+ struct udevice *current;
+ struct eth_device_priv *priv;
+
+ current = eth_get_dev();
+ if (!current || !device_active(current))
+ return;
+
+ priv = dev_get_uclass_priv(current);
+ priv->state = ETH_STATE_PASSIVE;
+}
+
+int eth_get_dev_index(void)
+{
+ if (eth_get_dev())
+ return dev_seq(eth_get_dev());
+ return -1;
+}
+
+static int eth_write_hwaddr(struct udevice *dev)
+{
+ struct eth_pdata *pdata;
+ int ret = 0;
+
+ if (!dev || !device_active(dev))
+ return -EINVAL;
+
+ /* seq is valid since the device is active */
+ if (eth_get_ops(dev)->write_hwaddr && !eth_mac_skip(dev_seq(dev))) {
+ pdata = dev_get_plat(dev);
+ if (!is_valid_ethaddr(pdata->enetaddr)) {
+ printf("\nError: %s address %pM illegal value\n",
+ dev->name, pdata->enetaddr);
+ return -EINVAL;
+ }
+
+ /*
+ * Drivers are allowed to decide not to implement this at
+ * run-time. E.g. Some devices may use it and some may not.
+ */
+ ret = eth_get_ops(dev)->write_hwaddr(dev);
+ if (ret == -ENOSYS)
+ ret = 0;
+ if (ret)
+ printf("\nWarning: %s failed to set MAC address\n",
+ dev->name);
+ }
+
+ return ret;
+}
+
+static int on_ethaddr(const char *name, const char *value, enum env_op op,
+ int flags)
+{
+ int index;
+ int retval;
+ struct udevice *dev;
+
+ /* look for an index after "eth" */
+ index = simple_strtoul(name + 3, NULL, 10);
+
+ retval = uclass_find_device_by_seq(UCLASS_ETH, index, &dev);
+ if (!retval) {
+ struct eth_pdata *pdata = dev_get_plat(dev);
+ switch (op) {
+ case env_op_create:
+ case env_op_overwrite:
+ string_to_enetaddr(value, pdata->enetaddr);
+ eth_write_hwaddr(dev);
+ break;
+ case env_op_delete:
+ memset(pdata->enetaddr, 0, ARP_HLEN);
+ }
+ }
+
+ return 0;
+}
+U_BOOT_ENV_CALLBACK(ethaddr, on_ethaddr);
+
+int eth_init(void)
+{
+ char *ethact = env_get("ethact");
+ char *ethrotate = env_get("ethrotate");
+ struct udevice *current = NULL;
+ struct udevice *old_current;
+ int ret = -ENODEV;
+
+ /*
+ * When 'ethrotate' variable is set to 'no' and 'ethact' variable
+ * is already set to an ethernet device, we should stick to 'ethact'.
+ */
+ if ((ethrotate != NULL) && (strcmp(ethrotate, "no") == 0)) {
+ if (ethact) {
+ current = eth_get_dev_by_name(ethact);
+ if (!current)
+ return -EINVAL;
+ }
+ }
+
+ if (!current) {
+ current = eth_get_dev();
+ if (!current) {
+ log_err("No ethernet found.\n");
+ return -ENODEV;
+ }
+ }
+
+ old_current = current;
+ do {
+ 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;
+ return 0;
+ }
+ } else {
+ ret = eth_errno;
+ }
+
+ debug("FAIL\n");
+ } else {
+ debug("PROBE FAIL\n");
+ }
+
+ /*
+ * If ethrotate is enabled, this will change "current",
+ * otherwise we will drop out of this while loop immediately
+ */
+ eth_try_another(0);
+ /* This will ensure the new "current" attempted to probe */
+ current = eth_get_dev();
+ } while (old_current != current);
+
+ return ret;
+}
+
+void eth_halt(void)
+{
+ struct udevice *current;
+ struct eth_device_priv *priv;
+
+ current = eth_get_dev();
+ if (!current)
+ return;
+
+ priv = dev_get_uclass_priv(current);
+ if (!priv || !priv->running)
+ return;
+
+ eth_get_ops(current)->stop(current);
+ priv->state = ETH_STATE_PASSIVE;
+ priv->running = false;
+}
+
+int eth_is_active(struct udevice *dev)
+{
+ struct eth_device_priv *priv;
+
+ if (!dev || !device_active(dev))
+ return 0;
+
+ priv = dev_get_uclass_priv(dev);
+ return priv->state == ETH_STATE_ACTIVE;
+}
+
+int eth_send(void *packet, int length)
+{
+ struct udevice *current;
+ int ret;
+
+ current = eth_get_dev();
+ if (!current)
+ return -ENODEV;
+
+ if (!eth_is_active(current))
+ return -EINVAL;
+
+ ret = eth_get_ops(current)->send(current, packet, length);
+ if (ret < 0) {
+ /* We cannot completely return the error at present */
+ debug("%s: send() returned error %d\n", __func__, ret);
+ }
+#if defined(CONFIG_CMD_PCAP)
+ if (ret >= 0)
+ pcap_post(packet, length, true);
+#endif
+ return ret;
+}
+
+int eth_rx(void)
+{
+ struct udevice *current;
+ uchar *packet;
+ int flags;
+ int ret;
+ int i;
+
+ current = eth_get_dev();
+ if (!current)
+ return -ENODEV;
+
+ if (!eth_is_active(current))
+ return -EINVAL;
+
+ /* Process up to 32 packets at one time */
+ flags = ETH_RECV_CHECK_DEVICE;
+ for (i = 0; i < ETH_PACKETS_BATCH_RECV; i++) {
+ ret = eth_get_ops(current)->recv(current, flags, &packet);
+ flags = 0;
+ if (ret > 0)
+ net_process_received_packet(packet, ret);
+ if (ret >= 0 && eth_get_ops(current)->free_pkt)
+ eth_get_ops(current)->free_pkt(current, packet, ret);
+ if (ret <= 0)
+ break;
+ }
+ if (ret == -EAGAIN)
+ ret = 0;
+ if (ret < 0) {
+ /* We cannot completely return the error at present */
+ debug("%s: recv() returned error %d\n", __func__, ret);
+ }
+ return ret;
+}
+
+int eth_initialize(void)
+{
+ int num_devices = 0;
+ struct udevice *dev;
+
+ eth_common_init();
+
+ /*
+ * Devices need to write the hwaddr even if not started so that Linux
+ * will have access to the hwaddr that u-boot stored for the device.
+ * This is accomplished by attempting to probe each device and calling
+ * their write_hwaddr() operation.
+ */
+ uclass_first_device_check(UCLASS_ETH, &dev);
+ if (!dev) {
+ log_err("No ethernet found.\n");
+ bootstage_error(BOOTSTAGE_ID_NET_ETH_START);
+ } else {
+ char *ethprime = env_get("ethprime");
+ struct udevice *prime_dev = NULL;
+
+ if (ethprime)
+ prime_dev = eth_get_dev_by_name(ethprime);
+ if (prime_dev) {
+ eth_set_dev(prime_dev);
+ eth_current_changed();
+ } else {
+ eth_set_dev(NULL);
+ }
+
+ bootstage_mark(BOOTSTAGE_ID_NET_ETH_INIT);
+ do {
+ if (device_active(dev)) {
+ if (num_devices)
+ printf(", ");
+
+ printf("eth%d: %s", dev_seq(dev), dev->name);
+
+ if (ethprime && dev == prime_dev)
+ printf(" [PRIME]");
+ }
+
+ eth_write_hwaddr(dev);
+
+ if (device_active(dev))
+ num_devices++;
+ uclass_next_device_check(&dev);
+ } while (dev);
+
+ if (!num_devices)
+ log_err("No ethernet found.\n");
+ putc('\n');
+ }
+
+ return num_devices;
+}
+
+static int eth_post_bind(struct udevice *dev)
+{
+ if (strchr(dev->name, ' ')) {
+ printf("\nError: eth device name \"%s\" has a space!\n",
+ dev->name);
+ return -EINVAL;
+ }
+
+#ifdef CONFIG_DM_ETH_PHY
+ eth_phy_binds_nodes(dev);
+#endif
+
+ return 0;
+}
+
+static int eth_pre_unbind(struct udevice *dev)
+{
+ /* Don't hang onto a pointer that is going away */
+ if (dev == eth_get_uclass_priv()->current)
+ eth_set_dev(NULL);
+
+ return 0;
+}
+
+static bool eth_dev_get_mac_address(struct udevice *dev, u8 mac[ARP_HLEN])
+{
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+ const uint8_t *p;
+
+ p = dev_read_u8_array_ptr(dev, "mac-address", ARP_HLEN);
+ if (!p)
+ p = dev_read_u8_array_ptr(dev, "local-mac-address", ARP_HLEN);
+
+ if (!p)
+ return false;
+
+ memcpy(mac, p, ARP_HLEN);
+
+ return true;
+#else
+ return false;
+#endif
+}
+
+static int eth_post_probe(struct udevice *dev)
+{
+ struct eth_device_priv *priv = dev_get_uclass_priv(dev);
+ struct eth_pdata *pdata = dev_get_plat(dev);
+ unsigned char env_enetaddr[ARP_HLEN];
+ char *source = "DT";
+
+#if defined(CONFIG_NEEDS_MANUAL_RELOC)
+ struct eth_ops *ops = eth_get_ops(dev);
+ static int reloc_done;
+
+ if (!reloc_done) {
+ if (ops->start)
+ ops->start += gd->reloc_off;
+ if (ops->send)
+ ops->send += gd->reloc_off;
+ if (ops->recv)
+ ops->recv += gd->reloc_off;
+ if (ops->free_pkt)
+ ops->free_pkt += gd->reloc_off;
+ if (ops->stop)
+ ops->stop += gd->reloc_off;
+ if (ops->mcast)
+ ops->mcast += gd->reloc_off;
+ if (ops->write_hwaddr)
+ ops->write_hwaddr += gd->reloc_off;
+ if (ops->read_rom_hwaddr)
+ ops->read_rom_hwaddr += gd->reloc_off;
+
+ reloc_done++;
+ }
+#endif
+
+ priv->state = ETH_STATE_INIT;
+ priv->running = false;
+
+ /* Check if the device has a valid MAC address in device tree */
+ if (!eth_dev_get_mac_address(dev, pdata->enetaddr) ||
+ !is_valid_ethaddr(pdata->enetaddr)) {
+ source = "ROM";
+ /* Check if the device has a MAC address in ROM */
+ if (eth_get_ops(dev)->read_rom_hwaddr)
+ eth_get_ops(dev)->read_rom_hwaddr(dev);
+ }
+
+ eth_env_get_enetaddr_by_index("eth", dev_seq(dev), env_enetaddr);
+ if (!is_zero_ethaddr(env_enetaddr)) {
+ if (!is_zero_ethaddr(pdata->enetaddr) &&
+ memcmp(pdata->enetaddr, env_enetaddr, ARP_HLEN)) {
+ printf("\nWarning: %s MAC addresses don't match:\n",
+ dev->name);
+ printf("Address in %s is\t\t%pM\n",
+ source, pdata->enetaddr);
+ printf("Address in environment is\t%pM\n",
+ env_enetaddr);
+ }
+
+ /* Override the ROM MAC address */
+ memcpy(pdata->enetaddr, env_enetaddr, ARP_HLEN);
+ } else if (is_valid_ethaddr(pdata->enetaddr)) {
+ eth_env_set_enetaddr_by_index("eth", dev_seq(dev),
+ pdata->enetaddr);
+ } else if (is_zero_ethaddr(pdata->enetaddr) ||
+ !is_valid_ethaddr(pdata->enetaddr)) {
+#ifdef CONFIG_NET_RANDOM_ETHADDR
+ net_random_ethaddr(pdata->enetaddr);
+ printf("\nWarning: %s (eth%d) using random MAC address - %pM\n",
+ dev->name, dev_seq(dev), pdata->enetaddr);
+#else
+ printf("\nError: %s address not set.\n",
+ dev->name);
+ return -EINVAL;
+#endif
+ }
+
+ eth_write_hwaddr(dev);
+
+ return 0;
+}
+
+static int eth_pre_remove(struct udevice *dev)
+{
+ struct eth_pdata *pdata = dev_get_plat(dev);
+
+ eth_get_ops(dev)->stop(dev);
+
+ /* clear the MAC address */
+ memset(pdata->enetaddr, 0, ARP_HLEN);
+
+ return 0;
+}
+
+UCLASS_DRIVER(ethernet) = {
+ .name = "ethernet",
+ .id = UCLASS_ETH,
+ .post_bind = eth_post_bind,
+ .pre_unbind = eth_pre_unbind,
+ .post_probe = eth_post_probe,
+ .pre_remove = eth_pre_remove,
+ .priv_auto = sizeof(struct eth_uclass_priv),
+ .per_device_auto = sizeof(struct eth_device_priv),
+ .flags = DM_UC_FLAG_SEQ_ALIAS,
+};
diff --git a/roms/u-boot/net/eth_common.c b/roms/u-boot/net/eth_common.c
new file mode 100644
index 000000000..58f899a02
--- /dev/null
+++ b/roms/u-boot/net/eth_common.c
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2001-2015
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ * Joe Hershberger, National Instruments
+ */
+
+#include <common.h>
+#include <bootstage.h>
+#include <dm.h>
+#include <env.h>
+#include <miiphy.h>
+#include <net.h>
+#include "eth_internal.h"
+
+int eth_env_get_enetaddr_by_index(const char *base_name, int index,
+ uchar *enetaddr)
+{
+ char enetvar[32];
+ sprintf(enetvar, index ? "%s%daddr" : "%saddr", base_name, index);
+ return eth_env_get_enetaddr(enetvar, enetaddr);
+}
+
+int eth_env_set_enetaddr_by_index(const char *base_name, int index,
+ uchar *enetaddr)
+{
+ char enetvar[32];
+ sprintf(enetvar, index ? "%s%daddr" : "%saddr", base_name, index);
+ return eth_env_set_enetaddr(enetvar, enetaddr);
+}
+
+void eth_common_init(void)
+{
+ bootstage_mark(BOOTSTAGE_ID_NET_ETH_START);
+#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) || defined(CONFIG_PHYLIB)
+ miiphy_init();
+#endif
+
+#ifdef CONFIG_PHYLIB
+ phy_init();
+#endif
+}
+
+int eth_mac_skip(int index)
+{
+ char enetvar[15];
+ char *skip_state;
+
+ sprintf(enetvar, index ? "eth%dmacskip" : "ethmacskip", index);
+ skip_state = env_get(enetvar);
+ return skip_state != NULL;
+}
+
+void eth_current_changed(void)
+{
+ char *act = env_get("ethact");
+ char *ethrotate;
+
+ /*
+ * The call to eth_get_dev() below has a side effect of rotating
+ * ethernet device if uc_priv->current == NULL. This is not what
+ * we want when 'ethrotate' variable is 'no'.
+ */
+ ethrotate = env_get("ethrotate");
+ if ((ethrotate != NULL) && (strcmp(ethrotate, "no") == 0))
+ return;
+
+ /* update current ethernet name */
+ if (eth_get_dev()) {
+ if (act == NULL || strcmp(act, eth_get_name()) != 0)
+ env_set("ethact", eth_get_name());
+ }
+ /*
+ * remove the variable completely if there is no active
+ * interface
+ */
+ else if (act != NULL)
+ env_set("ethact", NULL);
+}
+
+void eth_try_another(int first_restart)
+{
+ static void *first_failed;
+ char *ethrotate;
+
+ /*
+ * Do not rotate between network interfaces when
+ * 'ethrotate' variable is set to 'no'.
+ */
+ ethrotate = env_get("ethrotate");
+ if ((ethrotate != NULL) && (strcmp(ethrotate, "no") == 0))
+ return;
+
+ if (!eth_get_dev())
+ return;
+
+ if (first_restart)
+ first_failed = eth_get_dev();
+
+ eth_set_current_to_next();
+
+ eth_current_changed();
+
+ if (first_failed == eth_get_dev())
+ net_restart_wrap = 1;
+}
+
+void eth_set_current(void)
+{
+ static char *act;
+ static int env_changed_id;
+ int env_id;
+
+ env_id = env_get_id();
+ if ((act == NULL) || (env_changed_id != env_id)) {
+ act = env_get("ethact");
+ env_changed_id = env_id;
+ }
+
+ if (act == NULL) {
+ char *ethprime = env_get("ethprime");
+ void *dev = NULL;
+
+ if (ethprime)
+ dev = eth_get_dev_by_name(ethprime);
+ if (dev)
+ eth_set_dev(dev);
+ else
+ eth_set_dev(NULL);
+ } else {
+ eth_set_dev(eth_get_dev_by_name(act));
+ }
+
+ eth_current_changed();
+}
+
+const char *eth_get_name(void)
+{
+ return eth_get_dev() ? eth_get_dev()->name : "unknown";
+}
diff --git a/roms/u-boot/net/eth_internal.h b/roms/u-boot/net/eth_internal.h
new file mode 100644
index 000000000..faff0ef86
--- /dev/null
+++ b/roms/u-boot/net/eth_internal.h
@@ -0,0 +1,39 @@
+/* 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);
+#ifdef CONFIG_DM_ETH
+void eth_set_dev(struct udevice *dev);
+#else
+void eth_set_dev(struct eth_device *dev);
+#endif
+void eth_set_current_to_next(void);
+
+#endif
diff --git a/roms/u-boot/net/eth_legacy.c b/roms/u-boot/net/eth_legacy.c
new file mode 100644
index 000000000..96ed5a472
--- /dev/null
+++ b/roms/u-boot/net/eth_legacy.c
@@ -0,0 +1,424 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2001-2015
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ * Joe Hershberger, National Instruments
+ */
+
+#include <common.h>
+#include <bootstage.h>
+#include <command.h>
+#include <dm.h>
+#include <env.h>
+#include <log.h>
+#include <net.h>
+#include <phy.h>
+#include <asm/global_data.h>
+#include <linux/bug.h>
+#include <linux/errno.h>
+#include <net/pcap.h>
+#include "eth_internal.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/*
+ * CPU and board-specific Ethernet initializations. Aliased function
+ * signals caller to move on
+ */
+static int __def_eth_init(struct bd_info *bis)
+{
+ return -1;
+}
+int cpu_eth_init(struct bd_info *bis) __attribute__((weak, alias("__def_eth_init")));
+int board_eth_init(struct bd_info *bis) __attribute__((weak, alias("__def_eth_init")));
+
+#ifdef CONFIG_API
+static struct {
+ uchar data[PKTSIZE];
+ int length;
+} eth_rcv_bufs[PKTBUFSRX];
+
+static unsigned int eth_rcv_current, eth_rcv_last;
+#endif
+
+static struct eth_device *eth_devices;
+struct eth_device *eth_current;
+
+void eth_set_current_to_next(void)
+{
+ eth_current = eth_current->next;
+}
+
+void eth_set_dev(struct eth_device *dev)
+{
+ eth_current = dev;
+}
+
+struct eth_device *eth_get_dev_by_name(const char *devname)
+{
+ struct eth_device *dev, *target_dev;
+
+ BUG_ON(devname == NULL);
+
+ if (!eth_devices)
+ return NULL;
+
+ dev = eth_devices;
+ target_dev = NULL;
+ do {
+ if (strcmp(devname, dev->name) == 0) {
+ target_dev = dev;
+ break;
+ }
+ dev = dev->next;
+ } while (dev != eth_devices);
+
+ return target_dev;
+}
+
+struct eth_device *eth_get_dev_by_index(int index)
+{
+ struct eth_device *dev, *target_dev;
+
+ if (!eth_devices)
+ return NULL;
+
+ dev = eth_devices;
+ target_dev = NULL;
+ do {
+ if (dev->index == index) {
+ target_dev = dev;
+ break;
+ }
+ dev = dev->next;
+ } while (dev != eth_devices);
+
+ return target_dev;
+}
+
+int eth_get_dev_index(void)
+{
+ if (!eth_current)
+ return -1;
+
+ return eth_current->index;
+}
+
+static int on_ethaddr(const char *name, const char *value, enum env_op op,
+ int flags)
+{
+ int index;
+ struct eth_device *dev;
+
+ if (!eth_devices)
+ return 0;
+
+ /* look for an index after "eth" */
+ index = simple_strtoul(name + 3, NULL, 10);
+
+ dev = eth_devices;
+ do {
+ if (dev->index == index) {
+ switch (op) {
+ case env_op_create:
+ case env_op_overwrite:
+ string_to_enetaddr(value, dev->enetaddr);
+ eth_write_hwaddr(dev, "eth", dev->index);
+ break;
+ case env_op_delete:
+ memset(dev->enetaddr, 0, ARP_HLEN);
+ }
+ }
+ dev = dev->next;
+ } while (dev != eth_devices);
+
+ return 0;
+}
+U_BOOT_ENV_CALLBACK(ethaddr, on_ethaddr);
+
+int eth_write_hwaddr(struct eth_device *dev, const char *base_name,
+ int eth_number)
+{
+ unsigned char env_enetaddr[ARP_HLEN];
+ int ret = 0;
+
+ eth_env_get_enetaddr_by_index(base_name, eth_number, env_enetaddr);
+
+ if (!is_zero_ethaddr(env_enetaddr)) {
+ if (!is_zero_ethaddr(dev->enetaddr) &&
+ memcmp(dev->enetaddr, env_enetaddr, ARP_HLEN)) {
+ printf("\nWarning: %s MAC addresses don't match:\n",
+ dev->name);
+ printf("Address in SROM is %pM\n",
+ dev->enetaddr);
+ printf("Address in environment is %pM\n",
+ env_enetaddr);
+ }
+
+ memcpy(dev->enetaddr, env_enetaddr, ARP_HLEN);
+ } else if (is_valid_ethaddr(dev->enetaddr)) {
+ eth_env_set_enetaddr_by_index(base_name, eth_number,
+ dev->enetaddr);
+ } else if (is_zero_ethaddr(dev->enetaddr)) {
+#ifdef CONFIG_NET_RANDOM_ETHADDR
+ net_random_ethaddr(dev->enetaddr);
+ printf("\nWarning: %s (eth%d) using random MAC address - %pM\n",
+ dev->name, eth_number, dev->enetaddr);
+#else
+ printf("\nError: %s address not set.\n",
+ dev->name);
+ return -EINVAL;
+#endif
+ }
+
+ if (dev->write_hwaddr && !eth_mac_skip(eth_number)) {
+ if (!is_valid_ethaddr(dev->enetaddr)) {
+ printf("\nError: %s address %pM illegal value\n",
+ dev->name, dev->enetaddr);
+ return -EINVAL;
+ }
+
+ ret = dev->write_hwaddr(dev);
+ if (ret)
+ printf("\nWarning: %s failed to set MAC address\n",
+ dev->name);
+ }
+
+ return ret;
+}
+
+int eth_register(struct eth_device *dev)
+{
+ struct eth_device *d;
+ static int index;
+
+ assert(strlen(dev->name) < sizeof(dev->name));
+
+ if (!eth_devices) {
+ eth_devices = dev;
+ eth_current = dev;
+ eth_current_changed();
+ } else {
+ for (d = eth_devices; d->next != eth_devices; d = d->next)
+ ;
+ d->next = dev;
+ }
+
+ dev->state = ETH_STATE_INIT;
+ dev->next = eth_devices;
+ dev->index = index++;
+
+ return 0;
+}
+
+int eth_unregister(struct eth_device *dev)
+{
+ struct eth_device *cur;
+
+ /* No device */
+ if (!eth_devices)
+ return -ENODEV;
+
+ for (cur = eth_devices; cur->next != eth_devices && cur->next != dev;
+ cur = cur->next)
+ ;
+
+ /* Device not found */
+ if (cur->next != dev)
+ return -ENODEV;
+
+ cur->next = dev->next;
+
+ if (eth_devices == dev)
+ eth_devices = dev->next == eth_devices ? NULL : dev->next;
+
+ if (eth_current == dev) {
+ eth_current = eth_devices;
+ eth_current_changed();
+ }
+
+ return 0;
+}
+
+int eth_initialize(void)
+{
+ int num_devices = 0;
+
+ eth_devices = NULL;
+ eth_current = NULL;
+ eth_common_init();
+ /*
+ * If board-specific initialization exists, call it.
+ * If not, call a CPU-specific one
+ */
+ if (board_eth_init != __def_eth_init) {
+ if (board_eth_init(gd->bd) < 0)
+ printf("Board Net Initialization Failed\n");
+ } else if (cpu_eth_init != __def_eth_init) {
+ if (cpu_eth_init(gd->bd) < 0)
+ printf("CPU Net Initialization Failed\n");
+ } else {
+ printf("Net Initialization Skipped\n");
+ }
+
+ if (!eth_devices) {
+ log_err("No ethernet found.\n");
+ bootstage_error(BOOTSTAGE_ID_NET_ETH_START);
+ } else {
+ struct eth_device *dev = eth_devices;
+ char *ethprime = env_get("ethprime");
+
+ bootstage_mark(BOOTSTAGE_ID_NET_ETH_INIT);
+ do {
+ if (dev->index)
+ puts(", ");
+
+ printf("%s", dev->name);
+
+ if (ethprime && strcmp(dev->name, ethprime) == 0) {
+ eth_current = dev;
+ puts(" [PRIME]");
+ }
+
+ if (strchr(dev->name, ' '))
+ puts("\nWarning: eth device name has a space!"
+ "\n");
+
+ eth_write_hwaddr(dev, "eth", dev->index);
+
+ dev = dev->next;
+ num_devices++;
+ } while (dev != eth_devices);
+
+ eth_current_changed();
+ putc('\n');
+ }
+
+ return num_devices;
+}
+
+/* Multicast.
+ * mcast_addr: multicast ipaddr from which multicast Mac is made
+ * join: 1=join, 0=leave.
+ */
+int eth_mcast_join(struct in_addr mcast_ip, int join)
+{
+ u8 mcast_mac[ARP_HLEN];
+ if (!eth_current || !eth_current->mcast)
+ return -1;
+ mcast_mac[5] = htonl(mcast_ip.s_addr) & 0xff;
+ mcast_mac[4] = (htonl(mcast_ip.s_addr)>>8) & 0xff;
+ mcast_mac[3] = (htonl(mcast_ip.s_addr)>>16) & 0x7f;
+ mcast_mac[2] = 0x5e;
+ mcast_mac[1] = 0x0;
+ mcast_mac[0] = 0x1;
+ return eth_current->mcast(eth_current, mcast_mac, join);
+}
+
+int eth_init(void)
+{
+ struct eth_device *old_current;
+
+ if (!eth_current) {
+ log_err("No ethernet found.\n");
+ return -ENODEV;
+ }
+
+ old_current = eth_current;
+ do {
+ debug("Trying %s\n", eth_current->name);
+
+ if (eth_current->init(eth_current, gd->bd) >= 0) {
+ eth_current->state = ETH_STATE_ACTIVE;
+
+ return 0;
+ }
+ debug("FAIL\n");
+
+ eth_try_another(0);
+ } while (old_current != eth_current);
+
+ return -ETIMEDOUT;
+}
+
+void eth_halt(void)
+{
+ if (!eth_current)
+ return;
+
+ eth_current->halt(eth_current);
+
+ eth_current->state = ETH_STATE_PASSIVE;
+}
+
+int eth_is_active(struct eth_device *dev)
+{
+ return dev && dev->state == ETH_STATE_ACTIVE;
+}
+
+int eth_send(void *packet, int length)
+{
+ int ret;
+
+ if (!eth_current)
+ return -ENODEV;
+
+ ret = eth_current->send(eth_current, packet, length);
+#if defined(CONFIG_CMD_PCAP)
+ if (ret >= 0)
+ pcap_post(packet, length, true);
+#endif
+ return ret;
+}
+
+int eth_rx(void)
+{
+ if (!eth_current)
+ return -ENODEV;
+
+ return eth_current->recv(eth_current);
+}
+
+#ifdef CONFIG_API
+static void eth_save_packet(void *packet, int length)
+{
+ char *p = packet;
+ int i;
+
+ if ((eth_rcv_last+1) % PKTBUFSRX == eth_rcv_current)
+ return;
+
+ if (PKTSIZE < length)
+ return;
+
+ for (i = 0; i < length; i++)
+ eth_rcv_bufs[eth_rcv_last].data[i] = p[i];
+
+ eth_rcv_bufs[eth_rcv_last].length = length;
+ eth_rcv_last = (eth_rcv_last + 1) % PKTBUFSRX;
+}
+
+int eth_receive(void *packet, int length)
+{
+ char *p = packet;
+ void *pp = push_packet;
+ int i;
+
+ if (eth_rcv_current == eth_rcv_last) {
+ push_packet = eth_save_packet;
+ eth_rx();
+ push_packet = pp;
+
+ if (eth_rcv_current == eth_rcv_last)
+ return -1;
+ }
+
+ length = min(eth_rcv_bufs[eth_rcv_current].length, length);
+
+ for (i = 0; i < length; i++)
+ p[i] = eth_rcv_bufs[eth_rcv_current].data[i];
+
+ eth_rcv_current = (eth_rcv_current + 1) % PKTBUFSRX;
+ return length;
+}
+#endif /* CONFIG_API */
diff --git a/roms/u-boot/net/fastboot.c b/roms/u-boot/net/fastboot.c
new file mode 100644
index 000000000..7e7a601b9
--- /dev/null
+++ b/roms/u-boot/net/fastboot.c
@@ -0,0 +1,322 @@
+// SPDX-License-Identifier: BSD-2-Clause
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ */
+
+#include <common.h>
+#include <command.h>
+#include <fastboot.h>
+#include <net.h>
+#include <net/fastboot.h>
+
+/* Fastboot port # defined in spec */
+#define WELL_KNOWN_PORT 5554
+
+enum {
+ FASTBOOT_ERROR = 0,
+ FASTBOOT_QUERY = 1,
+ FASTBOOT_INIT = 2,
+ FASTBOOT_FASTBOOT = 3,
+};
+
+struct __packed fastboot_header {
+ uchar id;
+ uchar flags;
+ unsigned short seq;
+};
+
+#define PACKET_SIZE 1024
+#define DATA_SIZE (PACKET_SIZE - sizeof(struct fastboot_header))
+
+/* Sequence number sent for every packet */
+static unsigned short sequence_number = 1;
+static const unsigned short packet_size = PACKET_SIZE;
+static const unsigned short udp_version = 1;
+
+/* Keep track of last packet for resubmission */
+static uchar last_packet[PACKET_SIZE];
+static unsigned int last_packet_len;
+
+static struct in_addr fastboot_remote_ip;
+/* The UDP port at their end */
+static int fastboot_remote_port;
+/* The UDP port at our end */
+static int fastboot_our_port;
+
+static void boot_downloaded_image(void);
+
+#if CONFIG_IS_ENABLED(FASTBOOT_FLASH)
+/**
+ * fastboot_udp_send_info() - Send an INFO packet during long commands.
+ *
+ * @msg: String describing the reason for waiting
+ */
+static void fastboot_udp_send_info(const char *msg)
+{
+ uchar *packet;
+ uchar *packet_base;
+ int len = 0;
+ char response[FASTBOOT_RESPONSE_LEN] = {0};
+
+ struct fastboot_header response_header = {
+ .id = FASTBOOT_FASTBOOT,
+ .flags = 0,
+ .seq = htons(sequence_number)
+ };
+ ++sequence_number;
+ packet = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE;
+ packet_base = packet;
+
+ /* Write headers */
+ memcpy(packet, &response_header, sizeof(response_header));
+ packet += sizeof(response_header);
+ /* Write response */
+ fastboot_response("INFO", response, "%s", msg);
+ memcpy(packet, response, strlen(response));
+ packet += strlen(response);
+
+ len = packet - packet_base;
+
+ /* Save packet for retransmitting */
+ last_packet_len = len;
+ memcpy(last_packet, packet_base, last_packet_len);
+
+ net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip,
+ fastboot_remote_port, fastboot_our_port, len);
+}
+
+/**
+ * fastboot_timed_send_info() - Send INFO packet every 30 seconds
+ *
+ * @msg: String describing the reason for waiting
+ *
+ * Send an INFO packet during long commands based on timer. An INFO packet
+ * is sent if the time is 30 seconds after start. Else, noop.
+ */
+static void fastboot_timed_send_info(const char *msg)
+{
+ static ulong start;
+
+ /* Initialize timer */
+ if (start == 0)
+ start = get_timer(0);
+ ulong time = get_timer(start);
+ /* Send INFO packet to host every 30 seconds */
+ if (time >= 30000) {
+ start = get_timer(0);
+ fastboot_udp_send_info(msg);
+ }
+}
+#endif
+
+/**
+ * fastboot_send() - Sends a packet in response to received fastboot packet
+ *
+ * @header: Header for response packet
+ * @fastboot_data: Pointer to received fastboot data
+ * @fastboot_data_len: Length of received fastboot data
+ * @retransmit: Nonzero if sending last sent packet
+ */
+static void fastboot_send(struct fastboot_header header, char *fastboot_data,
+ unsigned int fastboot_data_len, uchar retransmit)
+{
+ uchar *packet;
+ uchar *packet_base;
+ int len = 0;
+ const char *error_msg = "An error occurred.";
+ short tmp;
+ struct fastboot_header response_header = header;
+ static char command[FASTBOOT_COMMAND_LEN];
+ static int cmd = -1;
+ static bool pending_command;
+ char response[FASTBOOT_RESPONSE_LEN] = {0};
+
+ /*
+ * We will always be sending some sort of packet, so
+ * cobble together the packet headers now.
+ */
+ packet = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE;
+ packet_base = packet;
+
+ /* Resend last packet */
+ if (retransmit) {
+ memcpy(packet, last_packet, last_packet_len);
+ net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip,
+ fastboot_remote_port, fastboot_our_port,
+ last_packet_len);
+ return;
+ }
+
+ response_header.seq = htons(response_header.seq);
+ memcpy(packet, &response_header, sizeof(response_header));
+ packet += sizeof(response_header);
+
+ switch (header.id) {
+ case FASTBOOT_QUERY:
+ tmp = htons(sequence_number);
+ memcpy(packet, &tmp, sizeof(tmp));
+ packet += sizeof(tmp);
+ break;
+ case FASTBOOT_INIT:
+ tmp = htons(udp_version);
+ memcpy(packet, &tmp, sizeof(tmp));
+ packet += sizeof(tmp);
+ tmp = htons(packet_size);
+ memcpy(packet, &tmp, sizeof(tmp));
+ packet += sizeof(tmp);
+ break;
+ case FASTBOOT_ERROR:
+ memcpy(packet, error_msg, strlen(error_msg));
+ packet += strlen(error_msg);
+ break;
+ case FASTBOOT_FASTBOOT:
+ if (cmd == FASTBOOT_COMMAND_DOWNLOAD) {
+ if (!fastboot_data_len && !fastboot_data_remaining()) {
+ fastboot_data_complete(response);
+ } else {
+ fastboot_data_download(fastboot_data,
+ fastboot_data_len,
+ response);
+ }
+ } else if (!pending_command) {
+ strlcpy(command, fastboot_data,
+ min((size_t)fastboot_data_len + 1,
+ sizeof(command)));
+ pending_command = true;
+ } else {
+ cmd = fastboot_handle_command(command, response);
+ pending_command = false;
+ }
+ /*
+ * Sent some INFO packets, need to update sequence number in
+ * header
+ */
+ if (header.seq != sequence_number) {
+ response_header.seq = htons(sequence_number);
+ memcpy(packet_base, &response_header,
+ sizeof(response_header));
+ }
+ /* Write response to packet */
+ memcpy(packet, response, strlen(response));
+ packet += strlen(response);
+ break;
+ default:
+ pr_err("ID %d not implemented.\n", header.id);
+ return;
+ }
+
+ len = packet - packet_base;
+
+ /* Save packet for retransmitting */
+ last_packet_len = len;
+ memcpy(last_packet, packet_base, last_packet_len);
+
+ net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip,
+ fastboot_remote_port, fastboot_our_port, len);
+
+ /* Continue boot process after sending response */
+ if (!strncmp("OKAY", response, 4)) {
+ switch (cmd) {
+ case FASTBOOT_COMMAND_BOOT:
+ boot_downloaded_image();
+ break;
+
+ case FASTBOOT_COMMAND_CONTINUE:
+ net_set_state(NETLOOP_SUCCESS);
+ break;
+
+ case FASTBOOT_COMMAND_REBOOT:
+ case FASTBOOT_COMMAND_REBOOT_BOOTLOADER:
+ case FASTBOOT_COMMAND_REBOOT_FASTBOOTD:
+ case FASTBOOT_COMMAND_REBOOT_RECOVERY:
+ do_reset(NULL, 0, 0, NULL);
+ break;
+ }
+ }
+
+ if (!strncmp("OKAY", response, 4) || !strncmp("FAIL", response, 4))
+ cmd = -1;
+}
+
+/**
+ * boot_downloaded_image() - Boots into downloaded image.
+ */
+static void boot_downloaded_image(void)
+{
+ fastboot_boot();
+ net_set_state(NETLOOP_SUCCESS);
+}
+
+/**
+ * fastboot_handler() - Incoming UDP packet handler.
+ *
+ * @packet: Pointer to incoming UDP packet
+ * @dport: Destination UDP port
+ * @sip: Source IP address
+ * @sport: Source UDP port
+ * @len: Packet length
+ */
+static void fastboot_handler(uchar *packet, unsigned int dport,
+ struct in_addr sip, unsigned int sport,
+ unsigned int len)
+{
+ struct fastboot_header header;
+ char fastboot_data[DATA_SIZE] = {0};
+ unsigned int fastboot_data_len = 0;
+
+ if (dport != fastboot_our_port)
+ return;
+
+ fastboot_remote_ip = sip;
+ fastboot_remote_port = sport;
+
+ if (len < sizeof(struct fastboot_header) || len > PACKET_SIZE)
+ return;
+ memcpy(&header, packet, sizeof(header));
+ header.flags = 0;
+ header.seq = ntohs(header.seq);
+ packet += sizeof(header);
+ len -= sizeof(header);
+
+ switch (header.id) {
+ case FASTBOOT_QUERY:
+ fastboot_send(header, fastboot_data, 0, 0);
+ break;
+ case FASTBOOT_INIT:
+ case FASTBOOT_FASTBOOT:
+ fastboot_data_len = len;
+ if (len > 0)
+ memcpy(fastboot_data, packet, len);
+ if (header.seq == sequence_number) {
+ fastboot_send(header, fastboot_data,
+ fastboot_data_len, 0);
+ sequence_number++;
+ } else if (header.seq == sequence_number - 1) {
+ /* Retransmit last sent packet */
+ fastboot_send(header, fastboot_data,
+ fastboot_data_len, 1);
+ }
+ break;
+ default:
+ pr_err("ID %d not implemented.\n", header.id);
+ header.id = FASTBOOT_ERROR;
+ fastboot_send(header, fastboot_data, 0, 0);
+ break;
+ }
+}
+
+void fastboot_start_server(void)
+{
+ printf("Using %s device\n", eth_get_name());
+ printf("Listening for fastboot command on %pI4\n", &net_ip);
+
+ fastboot_our_port = WELL_KNOWN_PORT;
+
+#if CONFIG_IS_ENABLED(FASTBOOT_FLASH)
+ fastboot_set_progress_callback(fastboot_timed_send_info);
+#endif
+ net_set_udp_handler(fastboot_handler);
+
+ /* zero out server ether in case the server ip has changed */
+ memset(net_server_ethaddr, 0, 6);
+}
diff --git a/roms/u-boot/net/link_local.c b/roms/u-boot/net/link_local.c
new file mode 100644
index 000000000..8aec3c799
--- /dev/null
+++ b/roms/u-boot/net/link_local.c
@@ -0,0 +1,349 @@
+/*
+ * RFC3927 ZeroConf IPv4 Link-Local addressing
+ * (see <http://www.zeroconf.org/>)
+ *
+ * Copied from BusyBox - networking/zcip.c
+ *
+ * Copyright (C) 2003 by Arthur van Hoff (avh@strangeberry.com)
+ * Copyright (C) 2004 by David Brownell
+ * Copyright (C) 2010 by Joe Hershberger
+ *
+ * Licensed under the GPL v2 or later
+ */
+
+#include <common.h>
+#include <env.h>
+#include <log.h>
+#include <net.h>
+#include <rand.h>
+#include "arp.h"
+#include "net_rand.h"
+
+/* We don't need more than 32 bits of the counter */
+#define MONOTONIC_MS() ((unsigned)get_timer(0) * (1000 / CONFIG_SYS_HZ))
+
+enum {
+/* 169.254.0.0 */
+ LINKLOCAL_ADDR = 0xa9fe0000,
+
+ IN_CLASSB_NET = 0xffff0000,
+ IN_CLASSB_HOST = 0x0000ffff,
+
+/* protocol timeout parameters, specified in seconds */
+ PROBE_WAIT = 1,
+ PROBE_MIN = 1,
+ PROBE_MAX = 2,
+ PROBE_NUM = 3,
+ MAX_CONFLICTS = 10,
+ RATE_LIMIT_INTERVAL = 60,
+ ANNOUNCE_WAIT = 2,
+ ANNOUNCE_NUM = 2,
+ ANNOUNCE_INTERVAL = 2,
+ DEFEND_INTERVAL = 10
+};
+
+/* States during the configuration process. */
+static enum ll_state_t {
+ PROBE = 0,
+ RATE_LIMIT_PROBE,
+ ANNOUNCE,
+ MONITOR,
+ DEFEND,
+ DISABLED
+} state = DISABLED;
+
+static struct in_addr ip;
+static int timeout_ms = -1;
+static unsigned deadline_ms;
+static unsigned conflicts;
+static unsigned nprobes;
+static unsigned nclaims;
+static int ready;
+static unsigned int seed;
+
+static void link_local_timeout(void);
+
+/**
+ * Pick a random link local IP address on 169.254/16, except that
+ * the first and last 256 addresses are reserved.
+ */
+static struct in_addr pick(void)
+{
+ unsigned tmp;
+ struct in_addr ip;
+
+ do {
+ tmp = rand_r(&seed) & IN_CLASSB_HOST;
+ } while (tmp > (IN_CLASSB_HOST - 0x0200));
+ ip.s_addr = htonl((LINKLOCAL_ADDR + 0x0100) + tmp);
+ return ip;
+}
+
+/**
+ * Return milliseconds of random delay, up to "secs" seconds.
+ */
+static inline unsigned random_delay_ms(unsigned secs)
+{
+ return rand_r(&seed) % (secs * 1000);
+}
+
+static void configure_wait(void)
+{
+ if (timeout_ms == -1)
+ return;
+
+ /* poll, being ready to adjust current timeout */
+ if (!timeout_ms)
+ timeout_ms = random_delay_ms(PROBE_WAIT);
+
+ /* set deadline_ms to the point in time when we timeout */
+ deadline_ms = MONOTONIC_MS() + timeout_ms;
+
+ debug_cond(DEBUG_DEV_PKT, "...wait %d %s nprobes=%u, nclaims=%u\n",
+ timeout_ms, eth_get_name(), nprobes, nclaims);
+
+ net_set_timeout_handler(timeout_ms, link_local_timeout);
+}
+
+void link_local_start(void)
+{
+ ip = env_get_ip("llipaddr");
+ if (ip.s_addr != 0 &&
+ (ntohl(ip.s_addr) & IN_CLASSB_NET) != LINKLOCAL_ADDR) {
+ puts("invalid link address");
+ net_set_state(NETLOOP_FAIL);
+ return;
+ }
+ net_netmask.s_addr = htonl(IN_CLASSB_NET);
+
+ seed = seed_mac();
+ if (ip.s_addr == 0)
+ ip = pick();
+
+ state = PROBE;
+ timeout_ms = 0;
+ conflicts = 0;
+ nprobes = 0;
+ nclaims = 0;
+ ready = 0;
+
+ configure_wait();
+}
+
+static void link_local_timeout(void)
+{
+ switch (state) {
+ case PROBE:
+ /* timeouts in the PROBE state mean no conflicting ARP packets
+ have been received, so we can progress through the states */
+ if (nprobes < PROBE_NUM) {
+ struct in_addr zero_ip = {.s_addr = 0};
+
+ nprobes++;
+ debug_cond(DEBUG_LL_STATE, "probe/%u %s@%pI4\n",
+ nprobes, eth_get_name(), &ip);
+ arp_raw_request(zero_ip, net_null_ethaddr, ip);
+ timeout_ms = PROBE_MIN * 1000;
+ timeout_ms += random_delay_ms(PROBE_MAX - PROBE_MIN);
+ } else {
+ /* Switch to announce state */
+ state = ANNOUNCE;
+ nclaims = 0;
+ debug_cond(DEBUG_LL_STATE, "announce/%u %s@%pI4\n",
+ nclaims, eth_get_name(), &ip);
+ arp_raw_request(ip, net_ethaddr, ip);
+ timeout_ms = ANNOUNCE_INTERVAL * 1000;
+ }
+ break;
+ case RATE_LIMIT_PROBE:
+ /* timeouts in the RATE_LIMIT_PROBE state mean no conflicting
+ ARP packets have been received, so we can move immediately
+ to the announce state */
+ state = ANNOUNCE;
+ nclaims = 0;
+ debug_cond(DEBUG_LL_STATE, "announce/%u %s@%pI4\n",
+ nclaims, eth_get_name(), &ip);
+ arp_raw_request(ip, net_ethaddr, ip);
+ timeout_ms = ANNOUNCE_INTERVAL * 1000;
+ break;
+ case ANNOUNCE:
+ /* timeouts in the ANNOUNCE state mean no conflicting ARP
+ packets have been received, so we can progress through
+ the states */
+ if (nclaims < ANNOUNCE_NUM) {
+ nclaims++;
+ debug_cond(DEBUG_LL_STATE, "announce/%u %s@%pI4\n",
+ nclaims, eth_get_name(), &ip);
+ arp_raw_request(ip, net_ethaddr, ip);
+ timeout_ms = ANNOUNCE_INTERVAL * 1000;
+ } else {
+ /* Switch to monitor state */
+ state = MONITOR;
+ printf("Successfully assigned %pI4\n", &ip);
+ net_copy_ip(&net_ip, &ip);
+ ready = 1;
+ conflicts = 0;
+ timeout_ms = -1;
+ /* Never timeout in the monitor state */
+ net_set_timeout_handler(0, NULL);
+
+ /* NOTE: all other exit paths should deconfig ... */
+ net_set_state(NETLOOP_SUCCESS);
+ return;
+ }
+ break;
+ case DEFEND:
+ /* We won! No ARP replies, so just go back to monitor */
+ state = MONITOR;
+ timeout_ms = -1;
+ conflicts = 0;
+ break;
+ default:
+ /* Invalid, should never happen. Restart the whole protocol */
+ state = PROBE;
+ ip = pick();
+ timeout_ms = 0;
+ nprobes = 0;
+ nclaims = 0;
+ break;
+ }
+ configure_wait();
+}
+
+void link_local_receive_arp(struct arp_hdr *arp, int len)
+{
+ int source_ip_conflict;
+ int target_ip_conflict;
+ struct in_addr null_ip = {.s_addr = 0};
+
+ if (state == DISABLED)
+ return;
+
+ /* We need to adjust the timeout in case we didn't receive a
+ conflicting packet. */
+ if (timeout_ms > 0) {
+ unsigned diff = deadline_ms - MONOTONIC_MS();
+ if ((int)(diff) < 0) {
+ /* Current time is greater than the expected timeout
+ time. This should never happen */
+ debug_cond(DEBUG_LL_STATE,
+ "missed an expected timeout\n");
+ timeout_ms = 0;
+ } else {
+ debug_cond(DEBUG_INT_STATE, "adjusting timeout\n");
+ timeout_ms = diff | 1; /* never 0 */
+ }
+ }
+#if 0
+ /* XXX Don't bother with ethernet link just yet */
+ if ((fds[0].revents & POLLIN) == 0) {
+ if (fds[0].revents & POLLERR) {
+ /*
+ * FIXME: links routinely go down;
+ */
+ bb_error_msg("iface %s is down", eth_get_name());
+ if (ready)
+ run(argv, "deconfig", &ip);
+ return EXIT_FAILURE;
+ }
+ continue;
+ }
+#endif
+
+ debug_cond(DEBUG_INT_STATE, "%s recv arp type=%d, op=%d,\n",
+ eth_get_name(), ntohs(arp->ar_pro),
+ ntohs(arp->ar_op));
+ debug_cond(DEBUG_INT_STATE, "\tsource=%pM %pI4\n",
+ &arp->ar_sha,
+ &arp->ar_spa);
+ debug_cond(DEBUG_INT_STATE, "\ttarget=%pM %pI4\n",
+ &arp->ar_tha,
+ &arp->ar_tpa);
+
+ if (arp->ar_op != htons(ARPOP_REQUEST) &&
+ arp->ar_op != htons(ARPOP_REPLY)) {
+ configure_wait();
+ return;
+ }
+
+ source_ip_conflict = 0;
+ target_ip_conflict = 0;
+
+ if (memcmp(&arp->ar_spa, &ip, ARP_PLEN) == 0 &&
+ memcmp(&arp->ar_sha, net_ethaddr, ARP_HLEN) != 0)
+ source_ip_conflict = 1;
+
+ /*
+ * According to RFC 3927, section 2.2.1:
+ * Check if packet is an ARP probe by checking for a null source IP
+ * then check that target IP is equal to ours and source hw addr
+ * is not equal to ours. This condition should cause a conflict only
+ * during probe.
+ */
+ if (arp->ar_op == htons(ARPOP_REQUEST) &&
+ memcmp(&arp->ar_spa, &null_ip, ARP_PLEN) == 0 &&
+ memcmp(&arp->ar_tpa, &ip, ARP_PLEN) == 0 &&
+ memcmp(&arp->ar_sha, net_ethaddr, ARP_HLEN) != 0) {
+ target_ip_conflict = 1;
+ }
+
+ debug_cond(DEBUG_NET_PKT,
+ "state = %d, source ip conflict = %d, target ip conflict = "
+ "%d\n", state, source_ip_conflict, target_ip_conflict);
+ switch (state) {
+ case PROBE:
+ case ANNOUNCE:
+ /* When probing or announcing, check for source IP conflicts
+ and other hosts doing ARP probes (target IP conflicts). */
+ if (source_ip_conflict || target_ip_conflict) {
+ conflicts++;
+ state = PROBE;
+ if (conflicts >= MAX_CONFLICTS) {
+ debug("%s ratelimit\n", eth_get_name());
+ timeout_ms = RATE_LIMIT_INTERVAL * 1000;
+ state = RATE_LIMIT_PROBE;
+ }
+
+ /* restart the whole protocol */
+ ip = pick();
+ timeout_ms = 0;
+ nprobes = 0;
+ nclaims = 0;
+ }
+ break;
+ case MONITOR:
+ /* If a conflict, we try to defend with a single ARP probe */
+ if (source_ip_conflict) {
+ debug("monitor conflict -- defending\n");
+ state = DEFEND;
+ timeout_ms = DEFEND_INTERVAL * 1000;
+ arp_raw_request(ip, net_ethaddr, ip);
+ }
+ break;
+ case DEFEND:
+ /* Well, we tried. Start over (on conflict) */
+ if (source_ip_conflict) {
+ state = PROBE;
+ debug("defend conflict -- starting over\n");
+ ready = 0;
+ net_ip.s_addr = 0;
+
+ /* restart the whole protocol */
+ ip = pick();
+ timeout_ms = 0;
+ nprobes = 0;
+ nclaims = 0;
+ }
+ break;
+ default:
+ /* Invalid, should never happen. Restart the whole protocol */
+ debug("invalid state -- starting over\n");
+ state = PROBE;
+ ip = pick();
+ timeout_ms = 0;
+ nprobes = 0;
+ nclaims = 0;
+ break;
+ }
+ configure_wait();
+}
diff --git a/roms/u-boot/net/link_local.h b/roms/u-boot/net/link_local.h
new file mode 100644
index 000000000..bb998164d
--- /dev/null
+++ b/roms/u-boot/net/link_local.h
@@ -0,0 +1,24 @@
+/*
+ * RFC3927 ZeroConf IPv4 Link-Local addressing
+ * (see <http://www.zeroconf.org/>)
+ *
+ * Copied from BusyBox - networking/zcip.c
+ *
+ * Copyright (C) 2003 by Arthur van Hoff (avh@strangeberry.com)
+ * Copyright (C) 2004 by David Brownell
+ *
+ * Licensed under the GPL v2 or later
+ */
+
+#if defined(CONFIG_CMD_LINK_LOCAL)
+
+#ifndef __LINK_LOCAL_H__
+#define __LINK_LOCAL_H__
+
+#include <common.h>
+
+void link_local_receive_arp(struct arp_hdr *arp, int len);
+void link_local_start(void);
+
+#endif /* __LINK_LOCAL_H__ */
+#endif
diff --git a/roms/u-boot/net/mdio-mux-uclass.c b/roms/u-boot/net/mdio-mux-uclass.c
new file mode 100644
index 000000000..780526c19
--- /dev/null
+++ b/roms/u-boot/net/mdio-mux-uclass.c
@@ -0,0 +1,233 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2019
+ * Alex Marginean, NXP
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <log.h>
+#include <miiphy.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
+#include <dm/lists.h>
+
+#define MDIO_MUX_CHILD_DRV_NAME "mdio-mux-bus-drv"
+
+/**
+ * struct mdio_mux_perdev_priv - Per-device class data for MDIO MUX DM
+ *
+ * @parent_mdio: Parent DM MDIO device, this is called for actual MDIO I/O after
+ * setting up the mux. Typically this is a real MDIO device,
+ * unless there are cascaded muxes.
+ * @selected: Current child bus selection. Defaults to -1
+ */
+struct mdio_mux_perdev_priv {
+ struct udevice *mdio_parent;
+ int selected;
+};
+
+/*
+ * This source file uses three types of devices, as follows:
+ * - mux is the hardware MDIO MUX which selects between the existing child MDIO
+ * buses, this is the device relevant for MDIO MUX class of drivers.
+ * - ch is a child MDIO bus, this is just a representation of a mux selection,
+ * not a real piece of hardware.
+ * - mdio_parent is the actual MDIO bus called to perform reads/writes after
+ * the MUX is configured. Typically this is a real MDIO device, unless there
+ * are cascaded muxes.
+ */
+
+/**
+ * struct mdio_mux_ch_data - Per-device data for child MDIOs
+ *
+ * @sel: Selection value used by the MDIO MUX to access this child MDIO bus
+ */
+struct mdio_mux_ch_data {
+ int sel;
+};
+
+static struct udevice *mmux_get_parent_mdio(struct udevice *mux)
+{
+ struct mdio_mux_perdev_priv *pdata = dev_get_uclass_priv(mux);
+
+ return pdata->mdio_parent;
+}
+
+static struct mdio_ops *mmux_get_mdio_parent_ops(struct udevice *mux)
+{
+ return mdio_get_ops(mmux_get_parent_mdio(mux));
+}
+
+/* call driver select function before performing MDIO r/w */
+static int mmux_change_sel(struct udevice *ch, bool sel)
+{
+ struct udevice *mux = ch->parent;
+ struct mdio_mux_perdev_priv *priv = dev_get_uclass_priv(mux);
+ struct mdio_mux_ops *ops = mdio_mux_get_ops(mux);
+ struct mdio_mux_ch_data *ch_data = dev_get_parent_plat(ch);
+ int err = 0;
+
+ if (sel) {
+ err = ops->select(mux, priv->selected, ch_data->sel);
+ if (err)
+ return err;
+
+ priv->selected = ch_data->sel;
+ } else {
+ if (ops->deselect) {
+ ops->deselect(mux, ch_data->sel);
+ priv->selected = MDIO_MUX_SELECT_NONE;
+ }
+ }
+
+ return 0;
+}
+
+/* Read wrapper, sets up the mux before issuing a read on parent MDIO bus */
+static int mmux_read(struct udevice *ch, int addr, int devad,
+ int reg)
+{
+ struct udevice *mux = ch->parent;
+ struct udevice *parent_mdio = mmux_get_parent_mdio(mux);
+ struct mdio_ops *parent_ops = mmux_get_mdio_parent_ops(mux);
+ int err;
+
+ err = mmux_change_sel(ch, true);
+ if (err)
+ return err;
+
+ err = parent_ops->read(parent_mdio, addr, devad, reg);
+ mmux_change_sel(ch, false);
+
+ return err;
+}
+
+/* Write wrapper, sets up the mux before issuing a write on parent MDIO bus */
+static int mmux_write(struct udevice *ch, int addr, int devad,
+ int reg, u16 val)
+{
+ struct udevice *mux = ch->parent;
+ struct udevice *parent_mdio = mmux_get_parent_mdio(mux);
+ struct mdio_ops *parent_ops = mmux_get_mdio_parent_ops(mux);
+ int err;
+
+ err = mmux_change_sel(ch, true);
+ if (err)
+ return err;
+
+ err = parent_ops->write(parent_mdio, addr, devad, reg, val);
+ mmux_change_sel(ch, false);
+
+ return err;
+}
+
+/* Reset wrapper, sets up the mux before issuing a reset on parent MDIO bus */
+static int mmux_reset(struct udevice *ch)
+{
+ struct udevice *mux = ch->parent;
+ struct udevice *parent_mdio = mmux_get_parent_mdio(mux);
+ struct mdio_ops *parent_ops = mmux_get_mdio_parent_ops(mux);
+ int err;
+
+ /* reset is optional, if it's not implemented just exit */
+ if (!parent_ops->reset)
+ return 0;
+
+ err = mmux_change_sel(ch, true);
+ if (err)
+ return err;
+
+ err = parent_ops->reset(parent_mdio);
+ mmux_change_sel(ch, false);
+
+ return err;
+}
+
+/* Picks up the mux selection value for each child */
+static int dm_mdio_mux_child_post_bind(struct udevice *ch)
+{
+ struct mdio_mux_ch_data *ch_data = dev_get_parent_plat(ch);
+
+ ch_data->sel = dev_read_u32_default(ch, "reg", MDIO_MUX_SELECT_NONE);
+
+ if (ch_data->sel == MDIO_MUX_SELECT_NONE)
+ return -EINVAL;
+
+ return 0;
+}
+
+/* Explicitly bind child MDIOs after binding the mux */
+static int dm_mdio_mux_post_bind(struct udevice *mux)
+{
+ ofnode ch_node;
+ int err, first_err = 0;
+
+ if (!dev_has_ofnode(mux)) {
+ debug("%s: no mux node found, no child MDIO busses set up\n",
+ __func__);
+ return 0;
+ }
+
+ /*
+ * we're going by Linux bindings so the child nodes do not have
+ * compatible strings. We're going through them here and binding to
+ * them.
+ */
+ dev_for_each_subnode(ch_node, mux) {
+ struct udevice *ch_dev;
+ const char *ch_name;
+
+ ch_name = ofnode_get_name(ch_node);
+
+ err = device_bind_driver_to_node(mux, MDIO_MUX_CHILD_DRV_NAME,
+ ch_name, ch_node, &ch_dev);
+ /* try to bind all, but keep 1st error */
+ if (err && !first_err)
+ first_err = err;
+ }
+
+ return first_err;
+}
+
+/* Get a reference to the parent MDIO bus, it should be bound by now */
+static int dm_mdio_mux_post_probe(struct udevice *mux)
+{
+ struct mdio_mux_perdev_priv *priv = dev_get_uclass_priv(mux);
+ int err;
+
+ priv->selected = MDIO_MUX_SELECT_NONE;
+
+ /* pick up mdio parent from device tree */
+ err = uclass_get_device_by_phandle(UCLASS_MDIO, mux, "mdio-parent-bus",
+ &priv->mdio_parent);
+ if (err) {
+ debug("%s: didn't find mdio-parent-bus\n", __func__);
+ return err;
+ }
+
+ return 0;
+}
+
+const struct mdio_ops mmux_child_mdio_ops = {
+ .read = mmux_read,
+ .write = mmux_write,
+ .reset = mmux_reset,
+};
+
+/* MDIO class driver used for MUX child MDIO buses */
+U_BOOT_DRIVER(mdio_mux_child) = {
+ .name = MDIO_MUX_CHILD_DRV_NAME,
+ .id = UCLASS_MDIO,
+ .ops = &mmux_child_mdio_ops,
+};
+
+UCLASS_DRIVER(mdio_mux) = {
+ .id = UCLASS_MDIO_MUX,
+ .name = "mdio-mux",
+ .child_post_bind = dm_mdio_mux_child_post_bind,
+ .post_bind = dm_mdio_mux_post_bind,
+ .post_probe = dm_mdio_mux_post_probe,
+ .per_device_auto = sizeof(struct mdio_mux_perdev_priv),
+ .per_child_plat_auto = sizeof(struct mdio_mux_ch_data),
+};
diff --git a/roms/u-boot/net/mdio-uclass.c b/roms/u-boot/net/mdio-uclass.c
new file mode 100644
index 000000000..1b687765b
--- /dev/null
+++ b/roms/u-boot/net/mdio-uclass.c
@@ -0,0 +1,230 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2019
+ * Alex Marginean, NXP
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <log.h>
+#include <malloc.h>
+#include <miiphy.h>
+#include <dm/device-internal.h>
+#include <dm/device_compat.h>
+#include <dm/of_extra.h>
+#include <dm/uclass-internal.h>
+#include <linux/compat.h>
+
+/* DT node properties for MAC-PHY interface */
+#define PHY_MODE_STR_CNT 2
+static const char *phy_mode_str[PHY_MODE_STR_CNT] = { "phy-mode",
+ "phy-connection-type" };
+/* DT node properties that reference a PHY node */
+#define PHY_HANDLE_STR_CNT 3
+const char *phy_handle_str[PHY_HANDLE_STR_CNT] = { "phy-handle",
+ "phy",
+ "phy-device" };
+
+void dm_mdio_probe_devices(void)
+{
+ struct udevice *it;
+ struct uclass *uc;
+
+ uclass_get(UCLASS_MDIO, &uc);
+ uclass_foreach_dev(it, uc) {
+ device_probe(it);
+ }
+}
+
+static int dm_mdio_post_bind(struct udevice *dev)
+{
+ const char *dt_name;
+
+ /* set a custom name for the MDIO device, if present in DT */
+ if (dev_has_ofnode(dev)) {
+ dt_name = dev_read_string(dev, "device-name");
+ if (dt_name) {
+ debug("renaming dev %s to %s\n", dev->name, dt_name);
+ device_set_name(dev, dt_name);
+ }
+ }
+
+ /*
+ * MDIO command doesn't like spaces in names, don't allow them to keep
+ * it happy
+ */
+ if (strchr(dev->name, ' ')) {
+ debug("\nError: MDIO device name \"%s\" has a space!\n",
+ dev->name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Following read/write/reset functions are registered with legacy MII code.
+ * These are called for PHY operations by upper layers and we further call the
+ * DM MDIO driver functions.
+ */
+static int mdio_read(struct mii_dev *mii_bus, int addr, int devad, int reg)
+{
+ struct udevice *dev = mii_bus->priv;
+
+ return mdio_get_ops(dev)->read(dev, addr, devad, reg);
+}
+
+static int mdio_write(struct mii_dev *mii_bus, int addr, int devad, int reg,
+ u16 val)
+{
+ struct udevice *dev = mii_bus->priv;
+
+ return mdio_get_ops(dev)->write(dev, addr, devad, reg, val);
+}
+
+static int mdio_reset(struct mii_dev *mii_bus)
+{
+ struct udevice *dev = mii_bus->priv;
+
+ if (mdio_get_ops(dev)->reset)
+ return mdio_get_ops(dev)->reset(dev);
+ else
+ return 0;
+}
+
+static int dm_mdio_post_probe(struct udevice *dev)
+{
+ struct mdio_perdev_priv *pdata = dev_get_uclass_priv(dev);
+
+ pdata->mii_bus = mdio_alloc();
+ pdata->mii_bus->read = mdio_read;
+ pdata->mii_bus->write = mdio_write;
+ pdata->mii_bus->reset = mdio_reset;
+ pdata->mii_bus->priv = dev;
+ strncpy(pdata->mii_bus->name, dev->name, MDIO_NAME_LEN - 1);
+
+ return mdio_register(pdata->mii_bus);
+}
+
+static int dm_mdio_pre_remove(struct udevice *dev)
+{
+ struct mdio_perdev_priv *pdata = dev_get_uclass_priv(dev);
+ struct mdio_ops *ops = mdio_get_ops(dev);
+
+ if (ops->reset)
+ ops->reset(dev);
+ mdio_unregister(pdata->mii_bus);
+ mdio_free(pdata->mii_bus);
+
+ return 0;
+}
+
+struct phy_device *dm_mdio_phy_connect(struct udevice *mdiodev, int phyaddr,
+ struct udevice *ethdev,
+ phy_interface_t interface)
+{
+ struct mdio_perdev_priv *pdata = dev_get_uclass_priv(mdiodev);
+
+ if (device_probe(mdiodev))
+ return NULL;
+
+ return phy_connect(pdata->mii_bus, phyaddr, ethdev, interface);
+}
+
+static struct phy_device *dm_eth_connect_phy_handle(struct udevice *ethdev,
+ phy_interface_t interface)
+{
+ u32 phy_addr;
+ struct udevice *mdiodev;
+ struct phy_device *phy;
+ struct ofnode_phandle_args phandle = {.node = ofnode_null()};
+ ofnode phynode;
+ int i;
+
+ if (CONFIG_IS_ENABLED(PHY_FIXED) &&
+ ofnode_phy_is_fixed_link(dev_ofnode(ethdev), &phynode)) {
+ phy = phy_connect(NULL, 0, ethdev, interface);
+ phandle.node = phynode;
+ goto out;
+ }
+
+ for (i = 0; i < PHY_HANDLE_STR_CNT; i++)
+ if (!dev_read_phandle_with_args(ethdev, phy_handle_str[i], NULL,
+ 0, 0, &phandle))
+ break;
+
+ if (!ofnode_valid(phandle.node)) {
+ dev_dbg(ethdev, "can't find PHY node\n");
+ return NULL;
+ }
+
+ /*
+ * reading 'reg' directly should be fine. This is a PHY node, the
+ * address is always size 1 and requires no translation
+ */
+ if (ofnode_read_u32(phandle.node, "reg", &phy_addr)) {
+ dev_dbg(ethdev, "missing reg property in phy node\n");
+ return NULL;
+ }
+
+ if (uclass_get_device_by_ofnode(UCLASS_MDIO,
+ ofnode_get_parent(phandle.node),
+ &mdiodev)) {
+ dev_dbg(ethdev, "can't find MDIO bus for node %s\n",
+ ofnode_get_name(ofnode_get_parent(phandle.node)));
+ return NULL;
+ }
+
+ phy = dm_mdio_phy_connect(mdiodev, phy_addr, ethdev, interface);
+
+out:
+ if (phy)
+ phy->node = phandle.node;
+
+ return phy;
+}
+
+/* Connect to a PHY linked in eth DT node */
+struct phy_device *dm_eth_phy_connect(struct udevice *ethdev)
+{
+ const char *if_str;
+ phy_interface_t interface;
+ struct phy_device *phy;
+ int i;
+
+ if (!dev_has_ofnode(ethdev)) {
+ debug("%s: supplied eth dev has no DT node!\n", ethdev->name);
+ return NULL;
+ }
+
+ interface = PHY_INTERFACE_MODE_NONE;
+ for (i = 0; i < PHY_MODE_STR_CNT; i++) {
+ if_str = dev_read_string(ethdev, phy_mode_str[i]);
+ if (if_str) {
+ interface = phy_get_interface_by_name(if_str);
+ break;
+ }
+ }
+ if (interface < 0)
+ interface = PHY_INTERFACE_MODE_NONE;
+ if (interface == PHY_INTERFACE_MODE_NONE)
+ dev_dbg(ethdev, "can't find interface mode, default to NONE\n");
+
+ phy = dm_eth_connect_phy_handle(ethdev, interface);
+
+ if (!phy)
+ return NULL;
+
+ phy->interface = interface;
+
+ return phy;
+}
+
+UCLASS_DRIVER(mdio) = {
+ .id = UCLASS_MDIO,
+ .name = "mdio",
+ .post_bind = dm_mdio_post_bind,
+ .post_probe = dm_mdio_post_probe,
+ .pre_remove = dm_mdio_pre_remove,
+ .per_device_auto = sizeof(struct mdio_perdev_priv),
+};
diff --git a/roms/u-boot/net/net.c b/roms/u-boot/net/net.c
new file mode 100644
index 000000000..b58f3062b
--- /dev/null
+++ b/roms/u-boot/net/net.c
@@ -0,0 +1,1602 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copied from Linux Monitor (LiMon) - Networking.
+ *
+ * Copyright 1994 - 2000 Neil Russell.
+ * (See License)
+ * Copyright 2000 Roland Borde
+ * Copyright 2000 Paolo Scaffardi
+ * Copyright 2000-2002 Wolfgang Denk, wd@denx.de
+ */
+
+/*
+ * General Desription:
+ *
+ * The user interface supports commands for BOOTP, RARP, and TFTP.
+ * Also, we support ARP internally. Depending on available data,
+ * these interact as follows:
+ *
+ * BOOTP:
+ *
+ * Prerequisites: - own ethernet address
+ * We want: - own IP address
+ * - TFTP server IP address
+ * - name of bootfile
+ * Next step: ARP
+ *
+ * LINK_LOCAL:
+ *
+ * Prerequisites: - own ethernet address
+ * We want: - own IP address
+ * Next step: ARP
+ *
+ * RARP:
+ *
+ * Prerequisites: - own ethernet address
+ * We want: - own IP address
+ * - TFTP server IP address
+ * Next step: ARP
+ *
+ * ARP:
+ *
+ * Prerequisites: - own ethernet address
+ * - own IP address
+ * - TFTP server IP address
+ * We want: - TFTP server ethernet address
+ * Next step: TFTP
+ *
+ * DHCP:
+ *
+ * Prerequisites: - own ethernet address
+ * We want: - IP, Netmask, ServerIP, Gateway IP
+ * - bootfilename, lease time
+ * Next step: - TFTP
+ *
+ * TFTP:
+ *
+ * Prerequisites: - own ethernet address
+ * - own IP address
+ * - TFTP server IP address
+ * - TFTP server ethernet address
+ * - name of bootfile (if unknown, we use a default name
+ * derived from our own IP address)
+ * We want: - load the boot file
+ * Next step: none
+ *
+ * NFS:
+ *
+ * Prerequisites: - own ethernet address
+ * - own IP address
+ * - name of bootfile (if unknown, we use a default name
+ * derived from our own IP address)
+ * We want: - load the boot file
+ * Next step: none
+ *
+ *
+ * WOL:
+ *
+ * Prerequisites: - own ethernet address
+ * We want: - magic packet or timeout
+ * Next step: none
+ */
+
+
+#include <common.h>
+#include <bootstage.h>
+#include <command.h>
+#include <console.h>
+#include <env.h>
+#include <env_internal.h>
+#include <errno.h>
+#include <image.h>
+#include <log.h>
+#include <net.h>
+#include <net/fastboot.h>
+#include <net/tftp.h>
+#if defined(CONFIG_CMD_PCAP)
+#include <net/pcap.h>
+#endif
+#include <net/udp.h>
+#if defined(CONFIG_LED_STATUS)
+#include <miiphy.h>
+#include <status_led.h>
+#endif
+#include <watchdog.h>
+#include <linux/compiler.h>
+#include "arp.h"
+#include "bootp.h"
+#include "cdp.h"
+#if defined(CONFIG_CMD_DNS)
+#include "dns.h"
+#endif
+#include "link_local.h"
+#include "nfs.h"
+#include "ping.h"
+#include "rarp.h"
+#if defined(CONFIG_CMD_WOL)
+#include "wol.h"
+#endif
+
+/** BOOTP EXTENTIONS **/
+
+/* Our subnet mask (0=unknown) */
+struct in_addr net_netmask;
+/* Our gateways IP address */
+struct in_addr net_gateway;
+/* Our DNS IP address */
+struct in_addr net_dns_server;
+#if defined(CONFIG_BOOTP_DNS2)
+/* Our 2nd DNS IP address */
+struct in_addr net_dns_server2;
+#endif
+
+/** END OF BOOTP EXTENTIONS **/
+
+/* Our ethernet address */
+u8 net_ethaddr[6];
+/* Boot server enet address */
+u8 net_server_ethaddr[6];
+/* Our IP addr (0 = unknown) */
+struct in_addr net_ip;
+/* Server IP addr (0 = unknown) */
+struct in_addr net_server_ip;
+/* Current receive packet */
+uchar *net_rx_packet;
+/* Current rx packet length */
+int net_rx_packet_len;
+/* IP packet ID */
+static unsigned net_ip_id;
+/* Ethernet bcast address */
+const u8 net_bcast_ethaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+const u8 net_null_ethaddr[6];
+#if defined(CONFIG_API) || defined(CONFIG_EFI_LOADER)
+void (*push_packet)(void *, int len) = 0;
+#endif
+/* Network loop state */
+enum net_loop_state net_state;
+/* Tried all network devices */
+int net_restart_wrap;
+/* Network loop restarted */
+static int net_restarted;
+/* At least one device configured */
+static int net_dev_exists;
+
+/* XXX in both little & big endian machines 0xFFFF == ntohs(-1) */
+/* default is without VLAN */
+ushort net_our_vlan = 0xFFFF;
+/* ditto */
+ushort net_native_vlan = 0xFFFF;
+
+/* Boot File name */
+char net_boot_file_name[1024];
+/* Indicates whether the file name was specified on the command line */
+bool net_boot_file_name_explicit;
+/* The actual transferred size of the bootfile (in bytes) */
+u32 net_boot_file_size;
+/* Boot file size in blocks as reported by the DHCP server */
+u32 net_boot_file_expected_size_in_blocks;
+
+static uchar net_pkt_buf[(PKTBUFSRX+1) * PKTSIZE_ALIGN + PKTALIGN];
+/* Receive packets */
+uchar *net_rx_packets[PKTBUFSRX];
+/* Current UDP RX packet handler */
+static rxhand_f *udp_packet_handler;
+/* Current ARP RX packet handler */
+static rxhand_f *arp_packet_handler;
+#ifdef CONFIG_CMD_TFTPPUT
+/* Current ICMP rx handler */
+static rxhand_icmp_f *packet_icmp_handler;
+#endif
+/* Current timeout handler */
+static thand_f *time_handler;
+/* Time base value */
+static ulong time_start;
+/* Current timeout value */
+static ulong time_delta;
+/* THE transmit packet */
+uchar *net_tx_packet;
+
+static int net_check_prereq(enum proto_t protocol);
+
+static int net_try_count;
+
+int __maybe_unused net_busy_flag;
+
+/**********************************************************************/
+
+static int on_ipaddr(const char *name, const char *value, enum env_op op,
+ int flags)
+{
+ if (flags & H_PROGRAMMATIC)
+ return 0;
+
+ net_ip = string_to_ip(value);
+
+ return 0;
+}
+U_BOOT_ENV_CALLBACK(ipaddr, on_ipaddr);
+
+static int on_gatewayip(const char *name, const char *value, enum env_op op,
+ int flags)
+{
+ if (flags & H_PROGRAMMATIC)
+ return 0;
+
+ net_gateway = string_to_ip(value);
+
+ return 0;
+}
+U_BOOT_ENV_CALLBACK(gatewayip, on_gatewayip);
+
+static int on_netmask(const char *name, const char *value, enum env_op op,
+ int flags)
+{
+ if (flags & H_PROGRAMMATIC)
+ return 0;
+
+ net_netmask = string_to_ip(value);
+
+ return 0;
+}
+U_BOOT_ENV_CALLBACK(netmask, on_netmask);
+
+static int on_serverip(const char *name, const char *value, enum env_op op,
+ int flags)
+{
+ if (flags & H_PROGRAMMATIC)
+ return 0;
+
+ net_server_ip = string_to_ip(value);
+
+ return 0;
+}
+U_BOOT_ENV_CALLBACK(serverip, on_serverip);
+
+static int on_nvlan(const char *name, const char *value, enum env_op op,
+ int flags)
+{
+ if (flags & H_PROGRAMMATIC)
+ return 0;
+
+ net_native_vlan = string_to_vlan(value);
+
+ return 0;
+}
+U_BOOT_ENV_CALLBACK(nvlan, on_nvlan);
+
+static int on_vlan(const char *name, const char *value, enum env_op op,
+ int flags)
+{
+ if (flags & H_PROGRAMMATIC)
+ return 0;
+
+ net_our_vlan = string_to_vlan(value);
+
+ return 0;
+}
+U_BOOT_ENV_CALLBACK(vlan, on_vlan);
+
+#if defined(CONFIG_CMD_DNS)
+static int on_dnsip(const char *name, const char *value, enum env_op op,
+ int flags)
+{
+ if (flags & H_PROGRAMMATIC)
+ return 0;
+
+ net_dns_server = string_to_ip(value);
+
+ return 0;
+}
+U_BOOT_ENV_CALLBACK(dnsip, on_dnsip);
+#endif
+
+/*
+ * Check if autoload is enabled. If so, use either NFS or TFTP to download
+ * the boot file.
+ */
+void net_auto_load(void)
+{
+#if defined(CONFIG_CMD_NFS) && !defined(CONFIG_SPL_BUILD)
+ const char *s = env_get("autoload");
+
+ if (s != NULL && strcmp(s, "NFS") == 0) {
+ if (net_check_prereq(NFS)) {
+/* We aren't expecting to get a serverip, so just accept the assigned IP */
+#ifdef CONFIG_BOOTP_SERVERIP
+ net_set_state(NETLOOP_SUCCESS);
+#else
+ printf("Cannot autoload with NFS\n");
+ net_set_state(NETLOOP_FAIL);
+#endif
+ return;
+ }
+ /*
+ * Use NFS to load the bootfile.
+ */
+ nfs_start();
+ return;
+ }
+#endif
+ if (env_get_yesno("autoload") == 0) {
+ /*
+ * Just use BOOTP/RARP to configure system;
+ * Do not use TFTP to load the bootfile.
+ */
+ net_set_state(NETLOOP_SUCCESS);
+ return;
+ }
+ if (net_check_prereq(TFTPGET)) {
+/* We aren't expecting to get a serverip, so just accept the assigned IP */
+#ifdef CONFIG_BOOTP_SERVERIP
+ net_set_state(NETLOOP_SUCCESS);
+#else
+ printf("Cannot autoload with TFTPGET\n");
+ net_set_state(NETLOOP_FAIL);
+#endif
+ return;
+ }
+ tftp_start(TFTPGET);
+}
+
+static int net_init_loop(void)
+{
+ if (eth_get_dev())
+ memcpy(net_ethaddr, eth_get_ethaddr(), 6);
+ else
+ /*
+ * Not ideal, but there's no way to get the actual error, and I
+ * don't feel like fixing all the users of eth_get_dev to deal
+ * with errors.
+ */
+ return -ENONET;
+
+ return 0;
+}
+
+static void net_clear_handlers(void)
+{
+ net_set_udp_handler(NULL);
+ net_set_arp_handler(NULL);
+ net_set_timeout_handler(0, NULL);
+}
+
+static void net_cleanup_loop(void)
+{
+ net_clear_handlers();
+}
+
+int net_init(void)
+{
+ static int first_call = 1;
+
+ if (first_call) {
+ /*
+ * Setup packet buffers, aligned correctly.
+ */
+ int i;
+
+ 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;
+ }
+ arp_init();
+ net_clear_handlers();
+
+ /* Only need to setup buffer pointers once. */
+ first_call = 0;
+ }
+
+ return net_init_loop();
+}
+
+/**********************************************************************/
+/*
+ * Main network processing loop.
+ */
+
+int net_loop(enum proto_t protocol)
+{
+ int ret = -EINVAL;
+ enum net_loop_state prev_net_state = net_state;
+
+#if defined(CONFIG_CMD_PING)
+ if (protocol != PING)
+ net_ping_ip.s_addr = 0;
+#endif
+ net_restarted = 0;
+ net_dev_exists = 0;
+ net_try_count = 1;
+ debug_cond(DEBUG_INT_STATE, "--- net_loop Entry\n");
+
+ bootstage_mark_name(BOOTSTAGE_ID_ETH_START, "eth_start");
+ net_init();
+ if (eth_is_on_demand_init()) {
+ eth_halt();
+ eth_set_current();
+ ret = eth_init();
+ if (ret < 0) {
+ eth_halt();
+ return ret;
+ }
+ } else {
+ eth_init_state_only();
+ }
+restart:
+#ifdef CONFIG_USB_KEYBOARD
+ net_busy_flag = 0;
+#endif
+ net_set_state(NETLOOP_CONTINUE);
+
+ /*
+ * Start the ball rolling with the given start function. From
+ * here on, this code is a state machine driven by received
+ * packets and timer events.
+ */
+ debug_cond(DEBUG_INT_STATE, "--- net_loop Init\n");
+ net_init_loop();
+
+ switch (net_check_prereq(protocol)) {
+ case 1:
+ /* network not configured */
+ eth_halt();
+ net_set_state(prev_net_state);
+ return -ENODEV;
+
+ case 2:
+ /* network device not configured */
+ break;
+
+ case 0:
+ net_dev_exists = 1;
+ net_boot_file_size = 0;
+ switch (protocol) {
+#ifdef CONFIG_CMD_TFTPBOOT
+ case TFTPGET:
+#ifdef CONFIG_CMD_TFTPPUT
+ case TFTPPUT:
+#endif
+ /* always use ARP to get server ethernet address */
+ tftp_start(protocol);
+ break;
+#endif
+#ifdef CONFIG_CMD_TFTPSRV
+ case TFTPSRV:
+ tftp_start_server();
+ break;
+#endif
+#ifdef CONFIG_UDP_FUNCTION_FASTBOOT
+ case FASTBOOT:
+ fastboot_start_server();
+ break;
+#endif
+#if defined(CONFIG_CMD_DHCP)
+ case DHCP:
+ bootp_reset();
+ net_ip.s_addr = 0;
+ dhcp_request(); /* Basically same as BOOTP */
+ break;
+#endif
+#if defined(CONFIG_CMD_BOOTP)
+ case BOOTP:
+ bootp_reset();
+ net_ip.s_addr = 0;
+ bootp_request();
+ break;
+#endif
+#if defined(CONFIG_CMD_RARP)
+ case RARP:
+ rarp_try = 0;
+ net_ip.s_addr = 0;
+ rarp_request();
+ break;
+#endif
+#if defined(CONFIG_CMD_PING)
+ case PING:
+ ping_start();
+ break;
+#endif
+#if defined(CONFIG_CMD_NFS) && !defined(CONFIG_SPL_BUILD)
+ case NFS:
+ nfs_start();
+ break;
+#endif
+#if defined(CONFIG_CMD_CDP)
+ case CDP:
+ cdp_start();
+ break;
+#endif
+#if defined(CONFIG_NETCONSOLE) && !defined(CONFIG_SPL_BUILD)
+ case NETCONS:
+ nc_start();
+ break;
+#endif
+#if defined(CONFIG_CMD_DNS)
+ case DNS:
+ dns_start();
+ break;
+#endif
+#if defined(CONFIG_CMD_LINK_LOCAL)
+ case LINKLOCAL:
+ link_local_start();
+ break;
+#endif
+#if defined(CONFIG_CMD_WOL)
+ case WOL:
+ wol_start();
+ break;
+#endif
+ default:
+ break;
+ }
+
+ if (IS_ENABLED(CONFIG_PROT_UDP) && protocol == UDP)
+ udp_start();
+
+ break;
+ }
+
+#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
+#if defined(CONFIG_SYS_FAULT_ECHO_LINK_DOWN) && \
+ defined(CONFIG_LED_STATUS) && \
+ defined(CONFIG_LED_STATUS_RED)
+ /*
+ * Echo the inverted link state to the fault LED.
+ */
+ if (miiphy_link(eth_get_dev()->name, CONFIG_SYS_FAULT_MII_ADDR))
+ status_led_set(CONFIG_LED_STATUS_RED, CONFIG_LED_STATUS_OFF);
+ else
+ status_led_set(CONFIG_LED_STATUS_RED, CONFIG_LED_STATUS_ON);
+#endif /* CONFIG_SYS_FAULT_ECHO_LINK_DOWN, ... */
+#endif /* CONFIG_MII, ... */
+#ifdef CONFIG_USB_KEYBOARD
+ net_busy_flag = 1;
+#endif
+
+ /*
+ * Main packet reception loop. Loop receiving packets until
+ * someone sets `net_state' to a state that terminates.
+ */
+ for (;;) {
+ WATCHDOG_RESET();
+ if (arp_timeout_check() > 0)
+ time_start = get_timer(0);
+
+ /*
+ * Check the ethernet for a new packet. The ethernet
+ * receive routine will process it.
+ * Most drivers return the most recent packet size, but not
+ * errors that may have happened.
+ */
+ eth_rx();
+
+ /*
+ * Abort if ctrl-c was pressed.
+ */
+ if (ctrlc()) {
+ /* cancel any ARP that may not have completed */
+ net_arp_wait_packet_ip.s_addr = 0;
+
+ net_cleanup_loop();
+ eth_halt();
+ /* Invalidate the last protocol */
+ eth_set_last_protocol(BOOTP);
+
+ puts("\nAbort\n");
+ /* include a debug print as well incase the debug
+ messages are directed to stderr */
+ debug_cond(DEBUG_INT_STATE, "--- net_loop Abort!\n");
+ ret = -EINTR;
+ goto done;
+ }
+
+ /*
+ * Check for a timeout, and run the timeout handler
+ * if we have one.
+ */
+ if (time_handler &&
+ ((get_timer(0) - time_start) > time_delta)) {
+ thand_f *x;
+
+#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
+#if defined(CONFIG_SYS_FAULT_ECHO_LINK_DOWN) && \
+ defined(CONFIG_LED_STATUS) && \
+ defined(CONFIG_LED_STATUS_RED)
+ /*
+ * Echo the inverted link state to the fault LED.
+ */
+ if (miiphy_link(eth_get_dev()->name,
+ CONFIG_SYS_FAULT_MII_ADDR))
+ status_led_set(CONFIG_LED_STATUS_RED,
+ CONFIG_LED_STATUS_OFF);
+ else
+ status_led_set(CONFIG_LED_STATUS_RED,
+ CONFIG_LED_STATUS_ON);
+#endif /* CONFIG_SYS_FAULT_ECHO_LINK_DOWN, ... */
+#endif /* CONFIG_MII, ... */
+ debug_cond(DEBUG_INT_STATE, "--- net_loop timeout\n");
+ x = time_handler;
+ time_handler = (thand_f *)0;
+ (*x)();
+ }
+
+ if (net_state == NETLOOP_FAIL)
+ ret = net_start_again();
+
+ switch (net_state) {
+ case NETLOOP_RESTART:
+ net_restarted = 1;
+ goto restart;
+
+ case NETLOOP_SUCCESS:
+ net_cleanup_loop();
+ if (net_boot_file_size > 0) {
+ printf("Bytes transferred = %d (%x hex)\n",
+ net_boot_file_size, net_boot_file_size);
+ env_set_hex("filesize", net_boot_file_size);
+ env_set_hex("fileaddr", image_load_addr);
+ }
+ if (protocol != NETCONS)
+ eth_halt();
+ else
+ eth_halt_state_only();
+
+ eth_set_last_protocol(protocol);
+
+ ret = net_boot_file_size;
+ debug_cond(DEBUG_INT_STATE, "--- net_loop Success!\n");
+ goto done;
+
+ case NETLOOP_FAIL:
+ net_cleanup_loop();
+ /* Invalidate the last protocol */
+ eth_set_last_protocol(BOOTP);
+ debug_cond(DEBUG_INT_STATE, "--- net_loop Fail!\n");
+ ret = -ENONET;
+ goto done;
+
+ case NETLOOP_CONTINUE:
+ continue;
+ }
+ }
+
+done:
+#ifdef CONFIG_USB_KEYBOARD
+ net_busy_flag = 0;
+#endif
+#ifdef CONFIG_CMD_TFTPPUT
+ /* Clear out the handlers */
+ net_set_udp_handler(NULL);
+ net_set_icmp_handler(NULL);
+#endif
+ net_set_state(prev_net_state);
+
+#if defined(CONFIG_CMD_PCAP)
+ if (pcap_active())
+ pcap_print_status();
+#endif
+ return ret;
+}
+
+/**********************************************************************/
+
+static void start_again_timeout_handler(void)
+{
+ net_set_state(NETLOOP_RESTART);
+}
+
+int net_start_again(void)
+{
+ char *nretry;
+ int retry_forever = 0;
+ unsigned long retrycnt = 0;
+ int ret;
+
+ nretry = env_get("netretry");
+ if (nretry) {
+ if (!strcmp(nretry, "yes"))
+ retry_forever = 1;
+ else if (!strcmp(nretry, "no"))
+ retrycnt = 0;
+ else if (!strcmp(nretry, "once"))
+ retrycnt = 1;
+ else
+ retrycnt = simple_strtoul(nretry, NULL, 0);
+ } else {
+ retrycnt = 0;
+ retry_forever = 0;
+ }
+
+ if ((!retry_forever) && (net_try_count > retrycnt)) {
+ eth_halt();
+ net_set_state(NETLOOP_FAIL);
+ /*
+ * We don't provide a way for the protocol to return an error,
+ * but this is almost always the reason.
+ */
+ return -ETIMEDOUT;
+ }
+
+ net_try_count++;
+
+ eth_halt();
+#if !defined(CONFIG_NET_DO_NOT_TRY_ANOTHER)
+ eth_try_another(!net_restarted);
+#endif
+ ret = eth_init();
+ if (net_restart_wrap) {
+ net_restart_wrap = 0;
+ if (net_dev_exists) {
+ net_set_timeout_handler(10000UL,
+ start_again_timeout_handler);
+ net_set_udp_handler(NULL);
+ } else {
+ net_set_state(NETLOOP_FAIL);
+ }
+ } else {
+ net_set_state(NETLOOP_RESTART);
+ }
+ return ret;
+}
+
+/**********************************************************************/
+/*
+ * Miscelaneous bits.
+ */
+
+static void dummy_handler(uchar *pkt, unsigned dport,
+ struct in_addr sip, unsigned sport,
+ unsigned len)
+{
+}
+
+rxhand_f *net_get_udp_handler(void)
+{
+ return udp_packet_handler;
+}
+
+void net_set_udp_handler(rxhand_f *f)
+{
+ debug_cond(DEBUG_INT_STATE, "--- net_loop UDP handler set (%p)\n", f);
+ if (f == NULL)
+ udp_packet_handler = dummy_handler;
+ else
+ udp_packet_handler = f;
+}
+
+rxhand_f *net_get_arp_handler(void)
+{
+ return arp_packet_handler;
+}
+
+void net_set_arp_handler(rxhand_f *f)
+{
+ debug_cond(DEBUG_INT_STATE, "--- net_loop ARP handler set (%p)\n", f);
+ if (f == NULL)
+ arp_packet_handler = dummy_handler;
+ else
+ arp_packet_handler = f;
+}
+
+#ifdef CONFIG_CMD_TFTPPUT
+void net_set_icmp_handler(rxhand_icmp_f *f)
+{
+ packet_icmp_handler = f;
+}
+#endif
+
+void net_set_timeout_handler(ulong iv, thand_f *f)
+{
+ if (iv == 0) {
+ debug_cond(DEBUG_INT_STATE,
+ "--- net_loop timeout handler cancelled\n");
+ time_handler = (thand_f *)0;
+ } else {
+ debug_cond(DEBUG_INT_STATE,
+ "--- net_loop timeout handler set (%p)\n", f);
+ time_handler = f;
+ time_start = get_timer(0);
+ time_delta = iv * CONFIG_SYS_HZ / 1000;
+ }
+}
+
+uchar *net_get_async_tx_pkt_buf(void)
+{
+ if (arp_is_waiting())
+ return arp_tx_packet; /* If we are waiting, we already sent */
+ else
+ return net_tx_packet;
+}
+
+int net_send_udp_packet(uchar *ether, struct in_addr dest, int dport, int sport,
+ int payload_len)
+{
+ return net_send_ip_packet(ether, dest, dport, sport, payload_len,
+ IPPROTO_UDP, 0, 0, 0);
+}
+
+int net_send_ip_packet(uchar *ether, struct in_addr dest, int dport, int sport,
+ int payload_len, int proto, u8 action, u32 tcp_seq_num,
+ u32 tcp_ack_num)
+{
+ uchar *pkt;
+ int eth_hdr_size;
+ int pkt_hdr_size;
+
+ /* make sure the net_tx_packet is initialized (net_init() was called) */
+ assert(net_tx_packet != NULL);
+ if (net_tx_packet == NULL)
+ return -1;
+
+ /* convert to new style broadcast */
+ if (dest.s_addr == 0)
+ dest.s_addr = 0xFFFFFFFF;
+
+ /* if broadcast, make the ether address a broadcast and don't do ARP */
+ if (dest.s_addr == 0xFFFFFFFF)
+ ether = (uchar *)net_bcast_ethaddr;
+
+ pkt = (uchar *)net_tx_packet;
+
+ eth_hdr_size = net_set_ether(pkt, ether, PROT_IP);
+
+ switch (proto) {
+ case IPPROTO_UDP:
+ net_set_udp_header(pkt + eth_hdr_size, dest, dport, sport,
+ payload_len);
+ pkt_hdr_size = eth_hdr_size + IP_UDP_HDR_SIZE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* if MAC address was not discovered yet, do an ARP request */
+ if (memcmp(ether, net_null_ethaddr, 6) == 0) {
+ debug_cond(DEBUG_DEV_PKT, "sending ARP for %pI4\n", &dest);
+
+ /* save the ip and eth addr for the packet to send after arp */
+ net_arp_wait_packet_ip = dest;
+ arp_wait_packet_ethaddr = ether;
+
+ /* size of the waiting packet */
+ arp_wait_tx_packet_size = pkt_hdr_size + payload_len;
+
+ /* and do the ARP request */
+ arp_wait_try = 1;
+ arp_wait_timer_start = get_timer(0);
+ arp_request();
+ return 1; /* waiting */
+ } else {
+ debug_cond(DEBUG_DEV_PKT, "sending UDP to %pI4/%pM\n",
+ &dest, ether);
+ net_send_packet(net_tx_packet, pkt_hdr_size + payload_len);
+ return 0; /* transmitted */
+ }
+}
+
+#ifdef CONFIG_IP_DEFRAG
+/*
+ * This function collects fragments in a single packet, according
+ * to the algorithm in RFC815. It returns NULL or the pointer to
+ * a complete packet, in static storage
+ */
+#define IP_PKTSIZE (CONFIG_NET_MAXDEFRAG)
+
+#define IP_MAXUDP (IP_PKTSIZE - IP_HDR_SIZE)
+
+/*
+ * this is the packet being assembled, either data or frag control.
+ * Fragments go by 8 bytes, so this union must be 8 bytes long
+ */
+struct hole {
+ /* first_byte is address of this structure */
+ u16 last_byte; /* last byte in this hole + 1 (begin of next hole) */
+ u16 next_hole; /* index of next (in 8-b blocks), 0 == none */
+ u16 prev_hole; /* index of prev, 0 == none */
+ u16 unused;
+};
+
+static struct ip_udp_hdr *__net_defragment(struct ip_udp_hdr *ip, int *lenp)
+{
+ static uchar pkt_buff[IP_PKTSIZE] __aligned(PKTALIGN);
+ static u16 first_hole, total_len;
+ struct hole *payload, *thisfrag, *h, *newh;
+ struct ip_udp_hdr *localip = (struct ip_udp_hdr *)pkt_buff;
+ uchar *indata = (uchar *)ip;
+ int offset8, start, len, done = 0;
+ u16 ip_off = ntohs(ip->ip_off);
+
+ /* payload starts after IP header, this fragment is in there */
+ payload = (struct hole *)(pkt_buff + IP_HDR_SIZE);
+ offset8 = (ip_off & IP_OFFS);
+ thisfrag = payload + offset8;
+ start = offset8 * 8;
+ len = ntohs(ip->ip_len) - IP_HDR_SIZE;
+
+ if (start + len > IP_MAXUDP) /* fragment extends too far */
+ return NULL;
+
+ if (!total_len || localip->ip_id != ip->ip_id) {
+ /* new (or different) packet, reset structs */
+ total_len = 0xffff;
+ payload[0].last_byte = ~0;
+ payload[0].next_hole = 0;
+ payload[0].prev_hole = 0;
+ first_hole = 0;
+ /* any IP header will work, copy the first we received */
+ memcpy(localip, ip, IP_HDR_SIZE);
+ }
+
+ /*
+ * What follows is the reassembly algorithm. We use the payload
+ * array as a linked list of hole descriptors, as each hole starts
+ * at a multiple of 8 bytes. However, last byte can be whatever value,
+ * so it is represented as byte count, not as 8-byte blocks.
+ */
+
+ h = payload + first_hole;
+ while (h->last_byte < start) {
+ if (!h->next_hole) {
+ /* no hole that far away */
+ return NULL;
+ }
+ h = payload + h->next_hole;
+ }
+
+ /* last fragment may be 1..7 bytes, the "+7" forces acceptance */
+ if (offset8 + ((len + 7) / 8) <= h - payload) {
+ /* no overlap with holes (dup fragment?) */
+ return NULL;
+ }
+
+ if (!(ip_off & IP_FLAGS_MFRAG)) {
+ /* no more fragmentss: truncate this (last) hole */
+ total_len = start + len;
+ h->last_byte = start + len;
+ }
+
+ /*
+ * There is some overlap: fix the hole list. This code doesn't
+ * deal with a fragment that overlaps with two different holes
+ * (thus being a superset of a previously-received fragment).
+ */
+
+ if ((h >= thisfrag) && (h->last_byte <= start + len)) {
+ /* complete overlap with hole: remove hole */
+ if (!h->prev_hole && !h->next_hole) {
+ /* last remaining hole */
+ done = 1;
+ } else if (!h->prev_hole) {
+ /* first hole */
+ first_hole = h->next_hole;
+ payload[h->next_hole].prev_hole = 0;
+ } else if (!h->next_hole) {
+ /* last hole */
+ payload[h->prev_hole].next_hole = 0;
+ } else {
+ /* in the middle of the list */
+ payload[h->next_hole].prev_hole = h->prev_hole;
+ payload[h->prev_hole].next_hole = h->next_hole;
+ }
+
+ } else if (h->last_byte <= start + len) {
+ /* overlaps with final part of the hole: shorten this hole */
+ h->last_byte = start;
+
+ } else if (h >= thisfrag) {
+ /* overlaps with initial part of the hole: move this hole */
+ newh = thisfrag + (len / 8);
+ *newh = *h;
+ h = newh;
+ if (h->next_hole)
+ payload[h->next_hole].prev_hole = (h - payload);
+ if (h->prev_hole)
+ payload[h->prev_hole].next_hole = (h - payload);
+ else
+ first_hole = (h - payload);
+
+ } else {
+ /* fragment sits in the middle: split the hole */
+ newh = thisfrag + (len / 8);
+ *newh = *h;
+ h->last_byte = start;
+ h->next_hole = (newh - payload);
+ newh->prev_hole = (h - payload);
+ if (newh->next_hole)
+ payload[newh->next_hole].prev_hole = (newh - payload);
+ }
+
+ /* finally copy this fragment and possibly return whole packet */
+ memcpy((uchar *)thisfrag, indata + IP_HDR_SIZE, len);
+ if (!done)
+ return NULL;
+
+ localip->ip_len = htons(total_len);
+ *lenp = total_len + IP_HDR_SIZE;
+ return localip;
+}
+
+static inline struct ip_udp_hdr *net_defragment(struct ip_udp_hdr *ip,
+ int *lenp)
+{
+ u16 ip_off = ntohs(ip->ip_off);
+ if (!(ip_off & (IP_OFFS | IP_FLAGS_MFRAG)))
+ return ip; /* not a fragment */
+ return __net_defragment(ip, lenp);
+}
+
+#else /* !CONFIG_IP_DEFRAG */
+
+static inline struct ip_udp_hdr *net_defragment(struct ip_udp_hdr *ip,
+ int *lenp)
+{
+ u16 ip_off = ntohs(ip->ip_off);
+ if (!(ip_off & (IP_OFFS | IP_FLAGS_MFRAG)))
+ return ip; /* not a fragment */
+ return NULL;
+}
+#endif
+
+/**
+ * Receive an ICMP packet. We deal with REDIRECT and PING here, and silently
+ * drop others.
+ *
+ * @parma ip IP packet containing the ICMP
+ */
+static void receive_icmp(struct ip_udp_hdr *ip, int len,
+ struct in_addr src_ip, struct ethernet_hdr *et)
+{
+ struct icmp_hdr *icmph = (struct icmp_hdr *)&ip->udp_src;
+
+ switch (icmph->type) {
+ case ICMP_REDIRECT:
+ if (icmph->code != ICMP_REDIR_HOST)
+ return;
+ printf(" ICMP Host Redirect to %pI4 ",
+ &icmph->un.gateway);
+ break;
+ default:
+#if defined(CONFIG_CMD_PING)
+ ping_receive(et, ip, len);
+#endif
+#ifdef CONFIG_CMD_TFTPPUT
+ if (packet_icmp_handler)
+ packet_icmp_handler(icmph->type, icmph->code,
+ ntohs(ip->udp_dst), src_ip,
+ ntohs(ip->udp_src), icmph->un.data,
+ ntohs(ip->udp_len));
+#endif
+ break;
+ }
+}
+
+void net_process_received_packet(uchar *in_packet, int len)
+{
+ struct ethernet_hdr *et;
+ struct ip_udp_hdr *ip;
+ struct in_addr dst_ip;
+ struct in_addr src_ip;
+ int eth_proto;
+#if defined(CONFIG_CMD_CDP)
+ int iscdp;
+#endif
+ ushort cti = 0, vlanid = VLAN_NONE, myvlanid, mynvlanid;
+
+ debug_cond(DEBUG_NET_PKT, "packet received\n");
+
+#if defined(CONFIG_CMD_PCAP)
+ pcap_post(in_packet, len, false);
+#endif
+ net_rx_packet = in_packet;
+ net_rx_packet_len = len;
+ et = (struct ethernet_hdr *)in_packet;
+
+ /* too small packet? */
+ if (len < ETHER_HDR_SIZE)
+ return;
+
+#if defined(CONFIG_API) || defined(CONFIG_EFI_LOADER)
+ if (push_packet) {
+ (*push_packet)(in_packet, len);
+ return;
+ }
+#endif
+
+#if defined(CONFIG_CMD_CDP)
+ /* keep track if packet is CDP */
+ iscdp = is_cdp_packet(et->et_dest);
+#endif
+
+ myvlanid = ntohs(net_our_vlan);
+ if (myvlanid == (ushort)-1)
+ myvlanid = VLAN_NONE;
+ mynvlanid = ntohs(net_native_vlan);
+ if (mynvlanid == (ushort)-1)
+ mynvlanid = VLAN_NONE;
+
+ eth_proto = ntohs(et->et_protlen);
+
+ if (eth_proto < 1514) {
+ struct e802_hdr *et802 = (struct e802_hdr *)et;
+ /*
+ * Got a 802.2 packet. Check the other protocol field.
+ * XXX VLAN over 802.2+SNAP not implemented!
+ */
+ eth_proto = ntohs(et802->et_prot);
+
+ ip = (struct ip_udp_hdr *)(in_packet + E802_HDR_SIZE);
+ len -= E802_HDR_SIZE;
+
+ } else if (eth_proto != PROT_VLAN) { /* normal packet */
+ ip = (struct ip_udp_hdr *)(in_packet + ETHER_HDR_SIZE);
+ len -= ETHER_HDR_SIZE;
+
+ } else { /* VLAN packet */
+ struct vlan_ethernet_hdr *vet =
+ (struct vlan_ethernet_hdr *)et;
+
+ debug_cond(DEBUG_NET_PKT, "VLAN packet received\n");
+
+ /* too small packet? */
+ if (len < VLAN_ETHER_HDR_SIZE)
+ return;
+
+ /* if no VLAN active */
+ if ((ntohs(net_our_vlan) & VLAN_IDMASK) == VLAN_NONE
+#if defined(CONFIG_CMD_CDP)
+ && iscdp == 0
+#endif
+ )
+ return;
+
+ cti = ntohs(vet->vet_tag);
+ vlanid = cti & VLAN_IDMASK;
+ eth_proto = ntohs(vet->vet_type);
+
+ ip = (struct ip_udp_hdr *)(in_packet + VLAN_ETHER_HDR_SIZE);
+ len -= VLAN_ETHER_HDR_SIZE;
+ }
+
+ debug_cond(DEBUG_NET_PKT, "Receive from protocol 0x%x\n", eth_proto);
+
+#if defined(CONFIG_CMD_CDP)
+ if (iscdp) {
+ cdp_receive((uchar *)ip, len);
+ return;
+ }
+#endif
+
+ if ((myvlanid & VLAN_IDMASK) != VLAN_NONE) {
+ if (vlanid == VLAN_NONE)
+ vlanid = (mynvlanid & VLAN_IDMASK);
+ /* not matched? */
+ if (vlanid != (myvlanid & VLAN_IDMASK))
+ return;
+ }
+
+ switch (eth_proto) {
+ case PROT_ARP:
+ arp_receive(et, ip, len);
+ break;
+
+#ifdef CONFIG_CMD_RARP
+ case PROT_RARP:
+ rarp_receive(ip, len);
+ break;
+#endif
+ case PROT_IP:
+ debug_cond(DEBUG_NET_PKT, "Got IP\n");
+ /* Before we start poking the header, make sure it is there */
+ if (len < IP_UDP_HDR_SIZE) {
+ debug("len bad %d < %lu\n", len,
+ (ulong)IP_UDP_HDR_SIZE);
+ return;
+ }
+ /* Check the packet length */
+ if (len < ntohs(ip->ip_len)) {
+ debug("len bad %d < %d\n", len, ntohs(ip->ip_len));
+ return;
+ }
+ len = ntohs(ip->ip_len);
+ debug_cond(DEBUG_NET_PKT, "len=%d, v=%02x\n",
+ len, ip->ip_hl_v & 0xff);
+
+ /* Can't deal with anything except IPv4 */
+ if ((ip->ip_hl_v & 0xf0) != 0x40)
+ return;
+ /* Can't deal with IP options (headers != 20 bytes) */
+ if ((ip->ip_hl_v & 0x0f) > 0x05)
+ return;
+ /* Check the Checksum of the header */
+ if (!ip_checksum_ok((uchar *)ip, IP_HDR_SIZE)) {
+ debug("checksum bad\n");
+ return;
+ }
+ /* If it is not for us, ignore it */
+ dst_ip = net_read_ip(&ip->ip_dst);
+ if (net_ip.s_addr && dst_ip.s_addr != net_ip.s_addr &&
+ dst_ip.s_addr != 0xFFFFFFFF) {
+ return;
+ }
+ /* Read source IP address for later use */
+ src_ip = net_read_ip(&ip->ip_src);
+ /*
+ * The function returns the unchanged packet if it's not
+ * a fragment, and either the complete packet or NULL if
+ * it is a fragment (if !CONFIG_IP_DEFRAG, it returns NULL)
+ */
+ ip = net_defragment(ip, &len);
+ if (!ip)
+ return;
+ /*
+ * watch for ICMP host redirects
+ *
+ * There is no real handler code (yet). We just watch
+ * for ICMP host redirect messages. In case anybody
+ * sees these messages: please contact me
+ * (wd@denx.de), or - even better - send me the
+ * necessary fixes :-)
+ *
+ * Note: in all cases where I have seen this so far
+ * it was a problem with the router configuration,
+ * for instance when a router was configured in the
+ * BOOTP reply, but the TFTP server was on the same
+ * subnet. So this is probably a warning that your
+ * configuration might be wrong. But I'm not really
+ * sure if there aren't any other situations.
+ *
+ * Simon Glass <sjg@chromium.org>: We get an ICMP when
+ * we send a tftp packet to a dead connection, or when
+ * there is no server at the other end.
+ */
+ if (ip->ip_p == IPPROTO_ICMP) {
+ receive_icmp(ip, len, src_ip, et);
+ return;
+ } else if (ip->ip_p != IPPROTO_UDP) { /* Only UDP packets */
+ return;
+ }
+
+ if (ntohs(ip->udp_len) < UDP_HDR_SIZE || ntohs(ip->udp_len) > ntohs(ip->ip_len))
+ return;
+
+ debug_cond(DEBUG_DEV_PKT,
+ "received UDP (to=%pI4, from=%pI4, len=%d)\n",
+ &dst_ip, &src_ip, len);
+
+#ifdef CONFIG_UDP_CHECKSUM
+ if (ip->udp_xsum != 0) {
+ ulong xsum;
+ u8 *sumptr;
+ ushort sumlen;
+
+ xsum = ip->ip_p;
+ xsum += (ntohs(ip->udp_len));
+ xsum += (ntohl(ip->ip_src.s_addr) >> 16) & 0x0000ffff;
+ xsum += (ntohl(ip->ip_src.s_addr) >> 0) & 0x0000ffff;
+ xsum += (ntohl(ip->ip_dst.s_addr) >> 16) & 0x0000ffff;
+ xsum += (ntohl(ip->ip_dst.s_addr) >> 0) & 0x0000ffff;
+
+ sumlen = ntohs(ip->udp_len);
+ sumptr = (u8 *)&ip->udp_src;
+
+ while (sumlen > 1) {
+ /* inlined ntohs() to avoid alignment errors */
+ xsum += (sumptr[0] << 8) + sumptr[1];
+ sumptr += 2;
+ sumlen -= 2;
+ }
+ if (sumlen > 0)
+ xsum += (sumptr[0] << 8) + sumptr[0];
+ while ((xsum >> 16) != 0) {
+ xsum = (xsum & 0x0000ffff) +
+ ((xsum >> 16) & 0x0000ffff);
+ }
+ if ((xsum != 0x00000000) && (xsum != 0x0000ffff)) {
+ printf(" UDP wrong checksum %08lx %08x\n",
+ xsum, ntohs(ip->udp_xsum));
+ return;
+ }
+ }
+#endif
+
+#if defined(CONFIG_NETCONSOLE) && !defined(CONFIG_SPL_BUILD)
+ nc_input_packet((uchar *)ip + IP_UDP_HDR_SIZE,
+ src_ip,
+ ntohs(ip->udp_dst),
+ ntohs(ip->udp_src),
+ ntohs(ip->udp_len) - UDP_HDR_SIZE);
+#endif
+ /*
+ * IP header OK. Pass the packet to the current handler.
+ */
+ (*udp_packet_handler)((uchar *)ip + IP_UDP_HDR_SIZE,
+ ntohs(ip->udp_dst),
+ src_ip,
+ ntohs(ip->udp_src),
+ ntohs(ip->udp_len) - UDP_HDR_SIZE);
+ break;
+#ifdef CONFIG_CMD_WOL
+ case PROT_WOL:
+ wol_receive(ip, len);
+ break;
+#endif
+ }
+}
+
+/**********************************************************************/
+
+static int net_check_prereq(enum proto_t protocol)
+{
+ switch (protocol) {
+ /* Fall through */
+#if defined(CONFIG_CMD_PING)
+ case PING:
+ if (net_ping_ip.s_addr == 0) {
+ puts("*** ERROR: ping address not given\n");
+ return 1;
+ }
+ goto common;
+#endif
+#if defined(CONFIG_CMD_DNS)
+ case DNS:
+ if (net_dns_server.s_addr == 0) {
+ puts("*** ERROR: DNS server address not given\n");
+ return 1;
+ }
+ goto common;
+#endif
+#if defined(CONFIG_PROT_UDP)
+ case UDP:
+ if (udp_prereq())
+ return 1;
+ goto common;
+#endif
+
+#if defined(CONFIG_CMD_NFS)
+ case NFS:
+#endif
+ /* Fall through */
+ case TFTPGET:
+ case TFTPPUT:
+ if (net_server_ip.s_addr == 0 && !is_serverip_in_cmd()) {
+ puts("*** ERROR: `serverip' not set\n");
+ return 1;
+ }
+#if defined(CONFIG_CMD_PING) || \
+ defined(CONFIG_CMD_DNS) || defined(CONFIG_PROT_UDP)
+common:
+#endif
+ /* Fall through */
+
+ case NETCONS:
+ case FASTBOOT:
+ case TFTPSRV:
+ if (net_ip.s_addr == 0) {
+ puts("*** ERROR: `ipaddr' not set\n");
+ return 1;
+ }
+ /* Fall through */
+
+#ifdef CONFIG_CMD_RARP
+ case RARP:
+#endif
+ case BOOTP:
+ case CDP:
+ case DHCP:
+ case LINKLOCAL:
+ if (memcmp(net_ethaddr, "\0\0\0\0\0\0", 6) == 0) {
+ int num = eth_get_dev_index();
+
+ switch (num) {
+ case -1:
+ puts("*** ERROR: No ethernet found.\n");
+ return 1;
+ case 0:
+ puts("*** ERROR: `ethaddr' not set\n");
+ break;
+ default:
+ printf("*** ERROR: `eth%daddr' not set\n",
+ num);
+ break;
+ }
+
+ net_start_again();
+ return 2;
+ }
+ /* Fall through */
+ default:
+ return 0;
+ }
+ return 0; /* OK */
+}
+/**********************************************************************/
+
+int
+net_eth_hdr_size(void)
+{
+ ushort myvlanid;
+
+ myvlanid = ntohs(net_our_vlan);
+ if (myvlanid == (ushort)-1)
+ myvlanid = VLAN_NONE;
+
+ return ((myvlanid & VLAN_IDMASK) == VLAN_NONE) ? ETHER_HDR_SIZE :
+ VLAN_ETHER_HDR_SIZE;
+}
+
+int net_set_ether(uchar *xet, const uchar *dest_ethaddr, uint prot)
+{
+ struct ethernet_hdr *et = (struct ethernet_hdr *)xet;
+ ushort myvlanid;
+
+ myvlanid = ntohs(net_our_vlan);
+ if (myvlanid == (ushort)-1)
+ myvlanid = VLAN_NONE;
+
+ memcpy(et->et_dest, dest_ethaddr, 6);
+ memcpy(et->et_src, net_ethaddr, 6);
+ if ((myvlanid & VLAN_IDMASK) == VLAN_NONE) {
+ et->et_protlen = htons(prot);
+ return ETHER_HDR_SIZE;
+ } else {
+ struct vlan_ethernet_hdr *vet =
+ (struct vlan_ethernet_hdr *)xet;
+
+ vet->vet_vlan_type = htons(PROT_VLAN);
+ vet->vet_tag = htons((0 << 5) | (myvlanid & VLAN_IDMASK));
+ vet->vet_type = htons(prot);
+ return VLAN_ETHER_HDR_SIZE;
+ }
+}
+
+int net_update_ether(struct ethernet_hdr *et, uchar *addr, uint prot)
+{
+ ushort protlen;
+
+ memcpy(et->et_dest, addr, 6);
+ memcpy(et->et_src, net_ethaddr, 6);
+ protlen = ntohs(et->et_protlen);
+ if (protlen == PROT_VLAN) {
+ struct vlan_ethernet_hdr *vet =
+ (struct vlan_ethernet_hdr *)et;
+ vet->vet_type = htons(prot);
+ return VLAN_ETHER_HDR_SIZE;
+ } else if (protlen > 1514) {
+ et->et_protlen = htons(prot);
+ return ETHER_HDR_SIZE;
+ } else {
+ /* 802.2 + SNAP */
+ struct e802_hdr *et802 = (struct e802_hdr *)et;
+ et802->et_prot = htons(prot);
+ return E802_HDR_SIZE;
+ }
+}
+
+void net_set_ip_header(uchar *pkt, struct in_addr dest, struct in_addr source,
+ u16 pkt_len, u8 proto)
+{
+ struct ip_udp_hdr *ip = (struct ip_udp_hdr *)pkt;
+
+ /*
+ * Construct an IP header.
+ */
+ /* IP_HDR_SIZE / 4 (not including UDP) */
+ ip->ip_hl_v = 0x45;
+ ip->ip_tos = 0;
+ ip->ip_len = htons(pkt_len);
+ ip->ip_p = proto;
+ ip->ip_id = htons(net_ip_id++);
+ ip->ip_off = htons(IP_FLAGS_DFRAG); /* Don't fragment */
+ ip->ip_ttl = 255;
+ ip->ip_sum = 0;
+ /* already in network byte order */
+ net_copy_ip((void *)&ip->ip_src, &source);
+ /* already in network byte order */
+ net_copy_ip((void *)&ip->ip_dst, &dest);
+
+ ip->ip_sum = compute_ip_checksum(ip, IP_HDR_SIZE);
+}
+
+void net_set_udp_header(uchar *pkt, struct in_addr dest, int dport, int sport,
+ int len)
+{
+ struct ip_udp_hdr *ip = (struct ip_udp_hdr *)pkt;
+
+ /*
+ * If the data is an odd number of bytes, zero the
+ * byte after the last byte so that the checksum
+ * will work.
+ */
+ if (len & 1)
+ pkt[IP_UDP_HDR_SIZE + len] = 0;
+
+ net_set_ip_header(pkt, dest, net_ip, IP_UDP_HDR_SIZE + len,
+ IPPROTO_UDP);
+
+ ip->udp_src = htons(sport);
+ ip->udp_dst = htons(dport);
+ ip->udp_len = htons(UDP_HDR_SIZE + len);
+ 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, ':');
+}
+
+int net_parse_bootfile(struct in_addr *ipaddr, char *filename, int max_len)
+{
+ char *colon;
+
+ if (net_boot_file_name[0] == '\0')
+ return 0;
+
+ colon = strchr(net_boot_file_name, ':');
+ if (colon) {
+ if (ipaddr)
+ *ipaddr = string_to_ip(net_boot_file_name);
+ strncpy(filename, colon + 1, max_len);
+ } else {
+ strncpy(filename, net_boot_file_name, max_len);
+ }
+ filename[max_len - 1] = '\0';
+
+ return 1;
+}
+
+void ip_to_string(struct in_addr x, char *s)
+{
+ x.s_addr = ntohl(x.s_addr);
+ sprintf(s, "%d.%d.%d.%d",
+ (int) ((x.s_addr >> 24) & 0xff),
+ (int) ((x.s_addr >> 16) & 0xff),
+ (int) ((x.s_addr >> 8) & 0xff),
+ (int) ((x.s_addr >> 0) & 0xff)
+ );
+}
+
+void vlan_to_string(ushort x, char *s)
+{
+ x = ntohs(x);
+
+ if (x == (ushort)-1)
+ x = VLAN_NONE;
+
+ if (x == VLAN_NONE)
+ strcpy(s, "none");
+ else
+ sprintf(s, "%d", x & VLAN_IDMASK);
+}
+
+ushort string_to_vlan(const char *s)
+{
+ ushort id;
+
+ if (s == NULL)
+ return htons(VLAN_NONE);
+
+ if (*s < '0' || *s > '9')
+ id = VLAN_NONE;
+ else
+ id = (ushort)simple_strtoul(s, NULL, 10);
+
+ return htons(id);
+}
+
+ushort env_get_vlan(char *var)
+{
+ return string_to_vlan(env_get(var));
+}
diff --git a/roms/u-boot/net/net_rand.h b/roms/u-boot/net/net_rand.h
new file mode 100644
index 000000000..6a52cda85
--- /dev/null
+++ b/roms/u-boot/net/net_rand.h
@@ -0,0 +1,60 @@
+/*
+ * Copied from LiMon - BOOTP.
+ *
+ * Copyright 1994, 1995, 2000 Neil Russell.
+ * (See License)
+ * Copyright 2000 Paolo Scaffardi
+ */
+
+#ifndef __NET_RAND_H__
+#define __NET_RAND_H__
+
+#include <common.h>
+#include <dm/uclass.h>
+#include <rng.h>
+
+/*
+ * Return a seed for the PRNG derived from the eth0 MAC address.
+ */
+static inline unsigned int seed_mac(void)
+{
+ unsigned char enetaddr[ARP_HLEN];
+ unsigned int seed;
+
+ /* get our mac */
+ memcpy(enetaddr, eth_get_ethaddr(), ARP_HLEN);
+
+ seed = enetaddr[5];
+ seed ^= enetaddr[4] << 8;
+ seed ^= enetaddr[3] << 16;
+ seed ^= enetaddr[2] << 24;
+ seed ^= enetaddr[1];
+ seed ^= enetaddr[0] << 8;
+
+ return seed;
+}
+
+/*
+ * Seed the random number generator using the eth0 MAC address.
+ */
+static inline void srand_mac(void)
+{
+ int ret;
+ struct udevice *devp;
+ u32 randv = 0;
+
+ if (IS_ENABLED(CONFIG_DM_RNG)) {
+ ret = uclass_get_device(UCLASS_RNG, 0, &devp);
+ if (ret) {
+ ret = dm_rng_read(devp, &randv, sizeof(randv));
+ if (ret < 0)
+ randv = 0;
+ }
+ }
+ if (randv)
+ srand(randv);
+ else
+ srand(seed_mac());
+}
+
+#endif /* __NET_RAND_H__ */
diff --git a/roms/u-boot/net/nfs.c b/roms/u-boot/net/nfs.c
new file mode 100644
index 000000000..70d0e08bd
--- /dev/null
+++ b/roms/u-boot/net/nfs.c
@@ -0,0 +1,934 @@
+/*
+ * NFS support driver - based on etherboot and U-BOOT's tftp.c
+ *
+ * Masami Komiya <mkomiya@sonare.it> 2004
+ *
+ */
+
+/* NOTE: the NFS code is heavily inspired by the NetBSD netboot code (read:
+ * large portions are copied verbatim) as distributed in OSKit 0.97. A few
+ * changes were necessary to adapt the code to Etherboot and to fix several
+ * inconsistencies. Also the RPC message preparation is done "by hand" to
+ * avoid adding netsprintf() which I find hard to understand and use. */
+
+/* NOTE 2: Etherboot does not care about things beyond the kernel image, so
+ * it loads the kernel image off the boot server (ARP_SERVER) and does not
+ * access the client root disk (root-path in dhcpd.conf), which would use
+ * ARP_ROOTSERVER. The root disk is something the operating system we are
+ * about to load needs to use. This is different from the OSKit 0.97 logic. */
+
+/* NOTE 3: Symlink handling introduced by Anselm M Hoffmeister, 2003-July-14
+ * If a symlink is encountered, it is followed as far as possible (recursion
+ * possible, maximum 16 steps). There is no clearing of ".."'s inside the
+ * path, so please DON'T DO THAT. thx. */
+
+/* NOTE 4: NFSv3 support added by Guillaume GARDET, 2016-June-20.
+ * NFSv2 is still used by default. But if server does not support NFSv2, then
+ * NFSv3 is used, if available on NFS server. */
+
+#include <common.h>
+#include <command.h>
+#include <flash.h>
+#include <image.h>
+#include <log.h>
+#include <net.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include "nfs.h"
+#include "bootp.h"
+#include <time.h>
+
+#define HASHES_PER_LINE 65 /* Number of "loading" hashes per line */
+#define NFS_RETRY_COUNT 30
+#ifndef CONFIG_NFS_TIMEOUT
+# define NFS_TIMEOUT 2000UL
+#else
+# define NFS_TIMEOUT CONFIG_NFS_TIMEOUT
+#endif
+
+#define NFS_RPC_ERR 1
+#define NFS_RPC_DROP 124
+
+static int fs_mounted;
+static unsigned long rpc_id;
+static int nfs_offset = -1;
+static int nfs_len;
+static ulong nfs_timeout = NFS_TIMEOUT;
+
+static char dirfh[NFS_FHSIZE]; /* NFSv2 / NFSv3 file handle of directory */
+static char filefh[NFS3_FHSIZE]; /* NFSv2 / NFSv3 file handle */
+static int filefh3_length; /* (variable) length of filefh when NFSv3 */
+
+static enum net_loop_state nfs_download_state;
+static struct in_addr nfs_server_ip;
+static int nfs_server_mount_port;
+static int nfs_server_port;
+static int nfs_our_port;
+static int nfs_timeout_count;
+static int nfs_state;
+#define STATE_PRCLOOKUP_PROG_MOUNT_REQ 1
+#define STATE_PRCLOOKUP_PROG_NFS_REQ 2
+#define STATE_MOUNT_REQ 3
+#define STATE_UMOUNT_REQ 4
+#define STATE_LOOKUP_REQ 5
+#define STATE_READ_REQ 6
+#define STATE_READLINK_REQ 7
+
+static char *nfs_filename;
+static char *nfs_path;
+static char nfs_path_buff[2048];
+
+#define NFSV2_FLAG 1
+#define NFSV3_FLAG 1 << 1
+static char supported_nfs_versions = NFSV2_FLAG | NFSV3_FLAG;
+
+static inline int store_block(uchar *src, unsigned offset, unsigned len)
+{
+ ulong newsize = offset + len;
+#ifdef CONFIG_SYS_DIRECT_FLASH_NFS
+ int i, rc = 0;
+
+ for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) {
+ /* start address in flash? */
+ if (image_load_addr + offset >= flash_info[i].start[0]) {
+ rc = 1;
+ break;
+ }
+ }
+
+ if (rc) { /* Flash is destination for this packet */
+ rc = flash_write((uchar *)src, (ulong)image_load_addr + offset,
+ len);
+ if (rc) {
+ flash_perror(rc);
+ return -1;
+ }
+ } else
+#endif /* CONFIG_SYS_DIRECT_FLASH_NFS */
+ {
+ void *ptr = map_sysmem(image_load_addr + offset, len);
+
+ memcpy(ptr, src, len);
+ unmap_sysmem(ptr);
+ }
+
+ if (net_boot_file_size < (offset + len))
+ net_boot_file_size = newsize;
+ return 0;
+}
+
+static char *basename(char *path)
+{
+ char *fname;
+
+ fname = path + strlen(path) - 1;
+ while (fname >= path) {
+ if (*fname == '/') {
+ fname++;
+ break;
+ }
+ fname--;
+ }
+ return fname;
+}
+
+static char *dirname(char *path)
+{
+ char *fname;
+
+ fname = basename(path);
+ --fname;
+ *fname = '\0';
+ return path;
+}
+
+/**************************************************************************
+RPC_ADD_CREDENTIALS - Add RPC authentication/verifier entries
+**************************************************************************/
+static uint32_t *rpc_add_credentials(uint32_t *p)
+{
+ /* Here's the executive summary on authentication requirements of the
+ * various NFS server implementations: Linux accepts both AUTH_NONE
+ * and AUTH_UNIX authentication (also accepts an empty hostname field
+ * in the AUTH_UNIX scheme). *BSD refuses AUTH_NONE, but accepts
+ * AUTH_UNIX (also accepts an empty hostname field in the AUTH_UNIX
+ * scheme). To be safe, use AUTH_UNIX and pass the hostname if we have
+ * it (if the BOOTP/DHCP reply didn't give one, just use an empty
+ * hostname). */
+
+ /* Provide an AUTH_UNIX credential. */
+ *p++ = htonl(1); /* AUTH_UNIX */
+ *p++ = htonl(20); /* auth length */
+ *p++ = 0; /* stamp */
+ *p++ = 0; /* hostname string */
+ *p++ = 0; /* uid */
+ *p++ = 0; /* gid */
+ *p++ = 0; /* auxiliary gid list */
+
+ /* Provide an AUTH_NONE verifier. */
+ *p++ = 0; /* AUTH_NONE */
+ *p++ = 0; /* auth length */
+
+ return p;
+}
+
+/**************************************************************************
+RPC_LOOKUP - Lookup RPC Port numbers
+**************************************************************************/
+static void rpc_req(int rpc_prog, int rpc_proc, uint32_t *data, int datalen)
+{
+ struct rpc_t rpc_pkt;
+ unsigned long id;
+ uint32_t *p;
+ int pktlen;
+ int sport;
+
+ id = ++rpc_id;
+ rpc_pkt.u.call.id = htonl(id);
+ rpc_pkt.u.call.type = htonl(MSG_CALL);
+ rpc_pkt.u.call.rpcvers = htonl(2); /* use RPC version 2 */
+ rpc_pkt.u.call.prog = htonl(rpc_prog);
+ switch (rpc_prog) {
+ case PROG_NFS:
+ if (supported_nfs_versions & NFSV2_FLAG)
+ rpc_pkt.u.call.vers = htonl(2); /* NFS v2 */
+ else /* NFSV3_FLAG */
+ rpc_pkt.u.call.vers = htonl(3); /* NFS v3 */
+ break;
+ case PROG_PORTMAP:
+ case PROG_MOUNT:
+ default:
+ rpc_pkt.u.call.vers = htonl(2); /* portmapper is version 2 */
+ }
+ rpc_pkt.u.call.proc = htonl(rpc_proc);
+ p = rpc_pkt.u.call.data;
+
+ if (datalen)
+ memcpy(p, data, datalen * sizeof(uint32_t));
+
+ pktlen = (char *)p + datalen * sizeof(uint32_t) - (char *)&rpc_pkt;
+
+ memcpy((char *)net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE,
+ &rpc_pkt.u.data[0], pktlen);
+
+ if (rpc_prog == PROG_PORTMAP)
+ sport = SUNRPC_PORT;
+ else if (rpc_prog == PROG_MOUNT)
+ sport = nfs_server_mount_port;
+ else
+ sport = nfs_server_port;
+
+ net_send_udp_packet(net_server_ethaddr, nfs_server_ip, sport,
+ nfs_our_port, pktlen);
+}
+
+/**************************************************************************
+RPC_LOOKUP - Lookup RPC Port numbers
+**************************************************************************/
+static void rpc_lookup_req(int prog, int ver)
+{
+ uint32_t data[16];
+
+ data[0] = 0; data[1] = 0; /* auth credential */
+ data[2] = 0; data[3] = 0; /* auth verifier */
+ data[4] = htonl(prog);
+ data[5] = htonl(ver);
+ data[6] = htonl(17); /* IP_UDP */
+ data[7] = 0;
+ rpc_req(PROG_PORTMAP, PORTMAP_GETPORT, data, 8);
+}
+
+/**************************************************************************
+NFS_MOUNT - Mount an NFS Filesystem
+**************************************************************************/
+static void nfs_mount_req(char *path)
+{
+ uint32_t data[1024];
+ uint32_t *p;
+ int len;
+ int pathlen;
+
+ pathlen = strlen(path);
+
+ p = &(data[0]);
+ p = rpc_add_credentials(p);
+
+ *p++ = htonl(pathlen);
+ if (pathlen & 3)
+ *(p + pathlen / 4) = 0;
+ memcpy(p, path, pathlen);
+ p += (pathlen + 3) / 4;
+
+ len = (uint32_t *)p - (uint32_t *)&(data[0]);
+
+ rpc_req(PROG_MOUNT, MOUNT_ADDENTRY, data, len);
+}
+
+/**************************************************************************
+NFS_UMOUNTALL - Unmount all our NFS Filesystems on the Server
+**************************************************************************/
+static void nfs_umountall_req(void)
+{
+ uint32_t data[1024];
+ uint32_t *p;
+ int len;
+
+ if ((nfs_server_mount_port == -1) || (!fs_mounted))
+ /* Nothing mounted, nothing to umount */
+ return;
+
+ p = &(data[0]);
+ p = rpc_add_credentials(p);
+
+ len = (uint32_t *)p - (uint32_t *)&(data[0]);
+
+ rpc_req(PROG_MOUNT, MOUNT_UMOUNTALL, data, len);
+}
+
+/***************************************************************************
+ * NFS_READLINK (AH 2003-07-14)
+ * This procedure is called when read of the first block fails -
+ * this probably happens when it's a directory or a symlink
+ * In case of successful readlink(), the dirname is manipulated,
+ * so that inside the nfs() function a recursion can be done.
+ **************************************************************************/
+static void nfs_readlink_req(void)
+{
+ uint32_t data[1024];
+ uint32_t *p;
+ int len;
+
+ p = &(data[0]);
+ p = rpc_add_credentials(p);
+
+ if (supported_nfs_versions & NFSV2_FLAG) {
+ memcpy(p, filefh, NFS_FHSIZE);
+ p += (NFS_FHSIZE / 4);
+ } else { /* NFSV3_FLAG */
+ *p++ = htonl(filefh3_length);
+ memcpy(p, filefh, filefh3_length);
+ p += (filefh3_length / 4);
+ }
+
+ len = (uint32_t *)p - (uint32_t *)&(data[0]);
+
+ rpc_req(PROG_NFS, NFS_READLINK, data, len);
+}
+
+/**************************************************************************
+NFS_LOOKUP - Lookup Pathname
+**************************************************************************/
+static void nfs_lookup_req(char *fname)
+{
+ uint32_t data[1024];
+ uint32_t *p;
+ int len;
+ int fnamelen;
+
+ fnamelen = strlen(fname);
+
+ p = &(data[0]);
+ p = rpc_add_credentials(p);
+
+ if (supported_nfs_versions & NFSV2_FLAG) {
+ memcpy(p, dirfh, NFS_FHSIZE);
+ p += (NFS_FHSIZE / 4);
+ *p++ = htonl(fnamelen);
+ if (fnamelen & 3)
+ *(p + fnamelen / 4) = 0;
+ memcpy(p, fname, fnamelen);
+ p += (fnamelen + 3) / 4;
+
+ len = (uint32_t *)p - (uint32_t *)&(data[0]);
+
+ rpc_req(PROG_NFS, NFS_LOOKUP, data, len);
+ } else { /* NFSV3_FLAG */
+ *p++ = htonl(NFS_FHSIZE); /* Dir handle length */
+ memcpy(p, dirfh, NFS_FHSIZE);
+ p += (NFS_FHSIZE / 4);
+ *p++ = htonl(fnamelen);
+ if (fnamelen & 3)
+ *(p + fnamelen / 4) = 0;
+ memcpy(p, fname, fnamelen);
+ p += (fnamelen + 3) / 4;
+
+ len = (uint32_t *)p - (uint32_t *)&(data[0]);
+
+ rpc_req(PROG_NFS, NFS3PROC_LOOKUP, data, len);
+ }
+}
+
+/**************************************************************************
+NFS_READ - Read File on NFS Server
+**************************************************************************/
+static void nfs_read_req(int offset, int readlen)
+{
+ uint32_t data[1024];
+ uint32_t *p;
+ int len;
+
+ p = &(data[0]);
+ p = rpc_add_credentials(p);
+
+ if (supported_nfs_versions & NFSV2_FLAG) {
+ memcpy(p, filefh, NFS_FHSIZE);
+ p += (NFS_FHSIZE / 4);
+ *p++ = htonl(offset);
+ *p++ = htonl(readlen);
+ *p++ = 0;
+ } else { /* NFSV3_FLAG */
+ *p++ = htonl(filefh3_length);
+ memcpy(p, filefh, filefh3_length);
+ p += (filefh3_length / 4);
+ *p++ = htonl(0); /* offset is 64-bit long, so fill with 0 */
+ *p++ = htonl(offset);
+ *p++ = htonl(readlen);
+ *p++ = 0;
+ }
+
+ len = (uint32_t *)p - (uint32_t *)&(data[0]);
+
+ rpc_req(PROG_NFS, NFS_READ, data, len);
+}
+
+/**************************************************************************
+RPC request dispatcher
+**************************************************************************/
+static void nfs_send(void)
+{
+ debug("%s\n", __func__);
+
+ switch (nfs_state) {
+ case STATE_PRCLOOKUP_PROG_MOUNT_REQ:
+ if (supported_nfs_versions & NFSV2_FLAG)
+ rpc_lookup_req(PROG_MOUNT, 1);
+ else /* NFSV3_FLAG */
+ rpc_lookup_req(PROG_MOUNT, 3);
+ break;
+ case STATE_PRCLOOKUP_PROG_NFS_REQ:
+ if (supported_nfs_versions & NFSV2_FLAG)
+ rpc_lookup_req(PROG_NFS, 2);
+ else /* NFSV3_FLAG */
+ rpc_lookup_req(PROG_NFS, 3);
+ break;
+ case STATE_MOUNT_REQ:
+ nfs_mount_req(nfs_path);
+ break;
+ case STATE_UMOUNT_REQ:
+ nfs_umountall_req();
+ break;
+ case STATE_LOOKUP_REQ:
+ nfs_lookup_req(nfs_filename);
+ break;
+ case STATE_READ_REQ:
+ nfs_read_req(nfs_offset, nfs_len);
+ break;
+ case STATE_READLINK_REQ:
+ nfs_readlink_req();
+ break;
+ }
+}
+
+/**************************************************************************
+Handlers for the reply from server
+**************************************************************************/
+
+static int rpc_lookup_reply(int prog, uchar *pkt, unsigned len)
+{
+ struct rpc_t rpc_pkt;
+
+ memcpy(&rpc_pkt.u.data[0], pkt, len);
+
+ debug("%s\n", __func__);
+
+ if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
+ return -NFS_RPC_ERR;
+ else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
+ return -NFS_RPC_DROP;
+
+ if (rpc_pkt.u.reply.rstatus ||
+ rpc_pkt.u.reply.verifier ||
+ rpc_pkt.u.reply.astatus)
+ return -1;
+
+ switch (prog) {
+ case PROG_MOUNT:
+ nfs_server_mount_port = ntohl(rpc_pkt.u.reply.data[0]);
+ break;
+ case PROG_NFS:
+ nfs_server_port = ntohl(rpc_pkt.u.reply.data[0]);
+ break;
+ }
+
+ return 0;
+}
+
+static int nfs_mount_reply(uchar *pkt, unsigned len)
+{
+ struct rpc_t rpc_pkt;
+
+ debug("%s\n", __func__);
+
+ memcpy(&rpc_pkt.u.data[0], pkt, len);
+
+ if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
+ return -NFS_RPC_ERR;
+ else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
+ return -NFS_RPC_DROP;
+
+ if (rpc_pkt.u.reply.rstatus ||
+ rpc_pkt.u.reply.verifier ||
+ rpc_pkt.u.reply.astatus ||
+ rpc_pkt.u.reply.data[0])
+ return -1;
+
+ fs_mounted = 1;
+ /* NFSv2 and NFSv3 use same structure */
+ memcpy(dirfh, rpc_pkt.u.reply.data + 1, NFS_FHSIZE);
+
+ return 0;
+}
+
+static int nfs_umountall_reply(uchar *pkt, unsigned len)
+{
+ struct rpc_t rpc_pkt;
+
+ debug("%s\n", __func__);
+
+ memcpy(&rpc_pkt.u.data[0], pkt, len);
+
+ if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
+ return -NFS_RPC_ERR;
+ else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
+ return -NFS_RPC_DROP;
+
+ if (rpc_pkt.u.reply.rstatus ||
+ rpc_pkt.u.reply.verifier ||
+ rpc_pkt.u.reply.astatus)
+ return -1;
+
+ fs_mounted = 0;
+ memset(dirfh, 0, sizeof(dirfh));
+
+ return 0;
+}
+
+static int nfs_lookup_reply(uchar *pkt, unsigned len)
+{
+ struct rpc_t rpc_pkt;
+
+ debug("%s\n", __func__);
+
+ memcpy(&rpc_pkt.u.data[0], pkt, len);
+
+ if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
+ return -NFS_RPC_ERR;
+ else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
+ return -NFS_RPC_DROP;
+
+ if (rpc_pkt.u.reply.rstatus ||
+ rpc_pkt.u.reply.verifier ||
+ rpc_pkt.u.reply.astatus ||
+ rpc_pkt.u.reply.data[0]) {
+ switch (ntohl(rpc_pkt.u.reply.astatus)) {
+ case NFS_RPC_SUCCESS: /* Not an error */
+ break;
+ case NFS_RPC_PROG_MISMATCH:
+ /* Remote can't support NFS version */
+ switch (ntohl(rpc_pkt.u.reply.data[0])) {
+ /* Minimal supported NFS version */
+ case 3:
+ debug("*** Warning: NFS version not supported: Requested: V%d, accepted: min V%d - max V%d\n",
+ (supported_nfs_versions & NFSV2_FLAG) ?
+ 2 : 3,
+ ntohl(rpc_pkt.u.reply.data[0]),
+ ntohl(rpc_pkt.u.reply.data[1]));
+ debug("Will retry with NFSv3\n");
+ /* Clear NFSV2_FLAG from supported versions */
+ supported_nfs_versions &= ~NFSV2_FLAG;
+ return -NFS_RPC_PROG_MISMATCH;
+ case 4:
+ default:
+ puts("*** ERROR: NFS version not supported");
+ debug(": Requested: V%d, accepted: min V%d - max V%d\n",
+ (supported_nfs_versions & NFSV2_FLAG) ?
+ 2 : 3,
+ ntohl(rpc_pkt.u.reply.data[0]),
+ ntohl(rpc_pkt.u.reply.data[1]));
+ puts("\n");
+ }
+ break;
+ case NFS_RPC_PROG_UNAVAIL:
+ case NFS_RPC_PROC_UNAVAIL:
+ case NFS_RPC_GARBAGE_ARGS:
+ case NFS_RPC_SYSTEM_ERR:
+ default: /* Unknown error on 'accept state' flag */
+ debug("*** ERROR: accept state error (%d)\n",
+ ntohl(rpc_pkt.u.reply.astatus));
+ break;
+ }
+ return -1;
+ }
+
+ if (supported_nfs_versions & NFSV2_FLAG) {
+ if (((uchar *)&(rpc_pkt.u.reply.data[0]) - (uchar *)(&rpc_pkt) + NFS_FHSIZE) > len)
+ return -NFS_RPC_DROP;
+ memcpy(filefh, rpc_pkt.u.reply.data + 1, NFS_FHSIZE);
+ } else { /* NFSV3_FLAG */
+ filefh3_length = ntohl(rpc_pkt.u.reply.data[1]);
+ if (filefh3_length > NFS3_FHSIZE)
+ filefh3_length = NFS3_FHSIZE;
+ if (((uchar *)&(rpc_pkt.u.reply.data[0]) - (uchar *)(&rpc_pkt) + filefh3_length) > len)
+ return -NFS_RPC_DROP;
+ memcpy(filefh, rpc_pkt.u.reply.data + 2, filefh3_length);
+ }
+
+ return 0;
+}
+
+static int nfs3_get_attributes_offset(uint32_t *data)
+{
+ if (data[1]) {
+ /* 'attributes_follow' flag is TRUE,
+ * so we have attributes on 21 dwords */
+ /* Skip unused values :
+ type; 32 bits value,
+ mode; 32 bits value,
+ nlink; 32 bits value,
+ uid; 32 bits value,
+ gid; 32 bits value,
+ size; 64 bits value,
+ used; 64 bits value,
+ rdev; 64 bits value,
+ fsid; 64 bits value,
+ fileid; 64 bits value,
+ atime; 64 bits value,
+ mtime; 64 bits value,
+ ctime; 64 bits value,
+ */
+ return 22;
+ } else {
+ /* 'attributes_follow' flag is FALSE,
+ * so we don't have any attributes */
+ return 1;
+ }
+}
+
+static int nfs_readlink_reply(uchar *pkt, unsigned len)
+{
+ struct rpc_t rpc_pkt;
+ int rlen;
+ int nfsv3_data_offset = 0;
+
+ debug("%s\n", __func__);
+
+ memcpy((unsigned char *)&rpc_pkt, pkt, len);
+
+ if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
+ return -NFS_RPC_ERR;
+ else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
+ return -NFS_RPC_DROP;
+
+ if (rpc_pkt.u.reply.rstatus ||
+ rpc_pkt.u.reply.verifier ||
+ rpc_pkt.u.reply.astatus ||
+ rpc_pkt.u.reply.data[0])
+ return -1;
+
+ if (!(supported_nfs_versions & NFSV2_FLAG)) { /* NFSV3_FLAG */
+ nfsv3_data_offset =
+ nfs3_get_attributes_offset(rpc_pkt.u.reply.data);
+ }
+
+ /* new path length */
+ rlen = ntohl(rpc_pkt.u.reply.data[1 + nfsv3_data_offset]);
+
+ if (((uchar *)&(rpc_pkt.u.reply.data[0]) - (uchar *)(&rpc_pkt) + rlen) > len)
+ return -NFS_RPC_DROP;
+
+ if (*((char *)&(rpc_pkt.u.reply.data[2 + nfsv3_data_offset])) != '/') {
+ int pathlen;
+
+ strcat(nfs_path, "/");
+ pathlen = strlen(nfs_path);
+ memcpy(nfs_path + pathlen,
+ (uchar *)&(rpc_pkt.u.reply.data[2 + nfsv3_data_offset]),
+ rlen);
+ nfs_path[pathlen + rlen] = 0;
+ } else {
+ memcpy(nfs_path,
+ (uchar *)&(rpc_pkt.u.reply.data[2 + nfsv3_data_offset]),
+ rlen);
+ nfs_path[rlen] = 0;
+ }
+ return 0;
+}
+
+static int nfs_read_reply(uchar *pkt, unsigned len)
+{
+ struct rpc_t rpc_pkt;
+ int rlen;
+ uchar *data_ptr;
+
+ debug("%s\n", __func__);
+
+ memcpy(&rpc_pkt.u.data[0], pkt, sizeof(rpc_pkt.u.reply));
+
+ if (ntohl(rpc_pkt.u.reply.id) > rpc_id)
+ return -NFS_RPC_ERR;
+ else if (ntohl(rpc_pkt.u.reply.id) < rpc_id)
+ return -NFS_RPC_DROP;
+
+ if (rpc_pkt.u.reply.rstatus ||
+ rpc_pkt.u.reply.verifier ||
+ rpc_pkt.u.reply.astatus ||
+ rpc_pkt.u.reply.data[0]) {
+ if (rpc_pkt.u.reply.rstatus)
+ return -9999;
+ if (rpc_pkt.u.reply.astatus)
+ return -9999;
+ return -ntohl(rpc_pkt.u.reply.data[0]);
+ }
+
+ if ((nfs_offset != 0) && !((nfs_offset) %
+ (NFS_READ_SIZE / 2 * 10 * HASHES_PER_LINE)))
+ puts("\n\t ");
+ if (!(nfs_offset % ((NFS_READ_SIZE / 2) * 10)))
+ putc('#');
+
+ if (supported_nfs_versions & NFSV2_FLAG) {
+ rlen = ntohl(rpc_pkt.u.reply.data[18]);
+ data_ptr = (uchar *)&(rpc_pkt.u.reply.data[19]);
+ } else { /* NFSV3_FLAG */
+ int nfsv3_data_offset =
+ nfs3_get_attributes_offset(rpc_pkt.u.reply.data);
+
+ /* count value */
+ rlen = ntohl(rpc_pkt.u.reply.data[1 + nfsv3_data_offset]);
+ /* Skip unused values :
+ EOF: 32 bits value,
+ data_size: 32 bits value,
+ */
+ data_ptr = (uchar *)
+ &(rpc_pkt.u.reply.data[4 + nfsv3_data_offset]);
+ }
+
+ if (((uchar *)&(rpc_pkt.u.reply.data[0]) - (uchar *)(&rpc_pkt) + rlen) > len)
+ return -9999;
+
+ if (store_block(data_ptr, nfs_offset, rlen))
+ return -9999;
+
+ return rlen;
+}
+
+/**************************************************************************
+Interfaces of U-BOOT
+**************************************************************************/
+static void nfs_timeout_handler(void)
+{
+ if (++nfs_timeout_count > NFS_RETRY_COUNT) {
+ puts("\nRetry count exceeded; starting again\n");
+ net_start_again();
+ } else {
+ puts("T ");
+ net_set_timeout_handler(nfs_timeout +
+ NFS_TIMEOUT * nfs_timeout_count,
+ nfs_timeout_handler);
+ nfs_send();
+ }
+}
+
+static void nfs_handler(uchar *pkt, unsigned dest, struct in_addr sip,
+ unsigned src, unsigned len)
+{
+ int rlen;
+ int reply;
+
+ debug("%s\n", __func__);
+
+ if (len > sizeof(struct rpc_t))
+ return;
+
+ if (dest != nfs_our_port)
+ return;
+
+ switch (nfs_state) {
+ case STATE_PRCLOOKUP_PROG_MOUNT_REQ:
+ if (rpc_lookup_reply(PROG_MOUNT, pkt, len) == -NFS_RPC_DROP)
+ break;
+ nfs_state = STATE_PRCLOOKUP_PROG_NFS_REQ;
+ nfs_send();
+ break;
+
+ case STATE_PRCLOOKUP_PROG_NFS_REQ:
+ if (rpc_lookup_reply(PROG_NFS, pkt, len) == -NFS_RPC_DROP)
+ break;
+ nfs_state = STATE_MOUNT_REQ;
+ nfs_send();
+ break;
+
+ case STATE_MOUNT_REQ:
+ reply = nfs_mount_reply(pkt, len);
+ if (reply == -NFS_RPC_DROP) {
+ break;
+ } else if (reply == -NFS_RPC_ERR) {
+ puts("*** ERROR: Cannot mount\n");
+ /* just to be sure... */
+ nfs_state = STATE_UMOUNT_REQ;
+ nfs_send();
+ } else {
+ nfs_state = STATE_LOOKUP_REQ;
+ nfs_send();
+ }
+ break;
+
+ case STATE_UMOUNT_REQ:
+ reply = nfs_umountall_reply(pkt, len);
+ if (reply == -NFS_RPC_DROP) {
+ break;
+ } else if (reply == -NFS_RPC_ERR) {
+ debug("*** ERROR: Cannot umount\n");
+ net_set_state(NETLOOP_FAIL);
+ } else {
+ puts("\ndone\n");
+ net_set_state(nfs_download_state);
+ }
+ break;
+
+ case STATE_LOOKUP_REQ:
+ reply = nfs_lookup_reply(pkt, len);
+ if (reply == -NFS_RPC_DROP) {
+ break;
+ } else if (reply == -NFS_RPC_ERR) {
+ puts("*** ERROR: File lookup fail\n");
+ nfs_state = STATE_UMOUNT_REQ;
+ nfs_send();
+ } else if (reply == -NFS_RPC_PROG_MISMATCH &&
+ supported_nfs_versions != 0) {
+ /* umount */
+ nfs_state = STATE_UMOUNT_REQ;
+ nfs_send();
+ /* And retry with another supported version */
+ nfs_state = STATE_PRCLOOKUP_PROG_MOUNT_REQ;
+ nfs_send();
+ } else {
+ nfs_state = STATE_READ_REQ;
+ nfs_offset = 0;
+ nfs_len = NFS_READ_SIZE;
+ nfs_send();
+ }
+ break;
+
+ case STATE_READLINK_REQ:
+ reply = nfs_readlink_reply(pkt, len);
+ if (reply == -NFS_RPC_DROP) {
+ break;
+ } else if (reply == -NFS_RPC_ERR) {
+ puts("*** ERROR: Symlink fail\n");
+ nfs_state = STATE_UMOUNT_REQ;
+ nfs_send();
+ } else {
+ debug("Symlink --> %s\n", nfs_path);
+ nfs_filename = basename(nfs_path);
+ nfs_path = dirname(nfs_path);
+
+ nfs_state = STATE_MOUNT_REQ;
+ nfs_send();
+ }
+ break;
+
+ case STATE_READ_REQ:
+ rlen = nfs_read_reply(pkt, len);
+ if (rlen == -NFS_RPC_DROP)
+ break;
+ net_set_timeout_handler(nfs_timeout, nfs_timeout_handler);
+ if (rlen > 0) {
+ nfs_offset += rlen;
+ nfs_send();
+ } else if ((rlen == -NFSERR_ISDIR) || (rlen == -NFSERR_INVAL)) {
+ /* symbolic link */
+ nfs_state = STATE_READLINK_REQ;
+ nfs_send();
+ } else {
+ if (!rlen)
+ nfs_download_state = NETLOOP_SUCCESS;
+ if (rlen < 0)
+ debug("NFS READ error (%d)\n", rlen);
+ nfs_state = STATE_UMOUNT_REQ;
+ nfs_send();
+ }
+ break;
+ }
+}
+
+
+void nfs_start(void)
+{
+ debug("%s\n", __func__);
+ nfs_download_state = NETLOOP_FAIL;
+
+ nfs_server_ip = net_server_ip;
+ nfs_path = (char *)nfs_path_buff;
+
+ if (nfs_path == NULL) {
+ net_set_state(NETLOOP_FAIL);
+ printf("*** ERROR: Fail allocate memory\n");
+ return;
+ }
+
+ if (!net_parse_bootfile(&nfs_server_ip, nfs_path,
+ sizeof(nfs_path_buff))) {
+ sprintf(nfs_path, "/nfsroot/%02X%02X%02X%02X.img",
+ net_ip.s_addr & 0xFF,
+ (net_ip.s_addr >> 8) & 0xFF,
+ (net_ip.s_addr >> 16) & 0xFF,
+ (net_ip.s_addr >> 24) & 0xFF);
+
+ printf("*** Warning: no boot file name; using '%s'\n",
+ nfs_path);
+ }
+
+ nfs_filename = basename(nfs_path);
+ nfs_path = dirname(nfs_path);
+
+ printf("Using %s device\n", eth_get_name());
+
+ printf("File transfer via NFS from server %pI4; our IP address is %pI4",
+ &nfs_server_ip, &net_ip);
+
+ /* Check if we need to send across this subnet */
+ if (net_gateway.s_addr && net_netmask.s_addr) {
+ struct in_addr our_net;
+ struct in_addr server_net;
+
+ our_net.s_addr = net_ip.s_addr & net_netmask.s_addr;
+ server_net.s_addr = nfs_server_ip.s_addr & net_netmask.s_addr;
+ if (our_net.s_addr != server_net.s_addr)
+ printf("; sending through gateway %pI4",
+ &net_gateway);
+ }
+ printf("\nFilename '%s/%s'.", nfs_path, nfs_filename);
+
+ if (net_boot_file_expected_size_in_blocks) {
+ printf(" Size is 0x%x Bytes = ",
+ net_boot_file_expected_size_in_blocks << 9);
+ print_size(net_boot_file_expected_size_in_blocks << 9, "");
+ }
+ printf("\nLoad address: 0x%lx\nLoading: *\b", image_load_addr);
+
+ net_set_timeout_handler(nfs_timeout, nfs_timeout_handler);
+ net_set_udp_handler(nfs_handler);
+
+ nfs_timeout_count = 0;
+ nfs_state = STATE_PRCLOOKUP_PROG_MOUNT_REQ;
+
+ /*nfs_our_port = 4096 + (get_ticks() % 3072);*/
+ /*FIX ME !!!*/
+ nfs_our_port = 1000;
+
+ /* zero out server ether in case the server ip has changed */
+ memset(net_server_ethaddr, 0, 6);
+
+ nfs_send();
+}
diff --git a/roms/u-boot/net/nfs.h b/roms/u-boot/net/nfs.h
new file mode 100644
index 000000000..68ada0efe
--- /dev/null
+++ b/roms/u-boot/net/nfs.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * (C) Masami Komiya <mkomiya@sonare.it> 2004
+ */
+
+#ifndef __NFS_H__
+#define __NFS_H__
+
+#define SUNRPC_PORT 111
+
+#define PROG_PORTMAP 100000
+#define PROG_NFS 100003
+#define PROG_MOUNT 100005
+
+#define MSG_CALL 0
+#define MSG_REPLY 1
+
+#define PORTMAP_GETPORT 3
+
+#define MOUNT_ADDENTRY 1
+#define MOUNT_UMOUNTALL 4
+
+#define NFS_LOOKUP 4
+#define NFS_READLINK 5
+#define NFS_READ 6
+
+#define NFS3PROC_LOOKUP 3
+
+#define NFS_FHSIZE 32
+#define NFS3_FHSIZE 64
+
+#define NFSERR_PERM 1
+#define NFSERR_NOENT 2
+#define NFSERR_ACCES 13
+#define NFSERR_ISDIR 21
+#define NFSERR_INVAL 22
+
+/*
+ * Block size used for NFS read accesses. A RPC reply packet (including all
+ * headers) must fit within a single Ethernet frame to avoid fragmentation.
+ * However, if CONFIG_IP_DEFRAG is set, a bigger value could be used. In any
+ * case, most NFS servers are optimized for a power of 2.
+ */
+#define NFS_READ_SIZE 1024 /* biggest power of two that fits Ether frame */
+#define NFS_MAX_ATTRS 26
+
+/* Values for Accept State flag on RPC answers (See: rfc1831) */
+enum rpc_accept_stat {
+ NFS_RPC_SUCCESS = 0, /* RPC executed successfully */
+ NFS_RPC_PROG_UNAVAIL = 1, /* remote hasn't exported program */
+ NFS_RPC_PROG_MISMATCH = 2, /* remote can't support version # */
+ NFS_RPC_PROC_UNAVAIL = 3, /* program can't support procedure */
+ NFS_RPC_GARBAGE_ARGS = 4, /* procedure can't decode params */
+ NFS_RPC_SYSTEM_ERR = 5 /* errors like memory allocation failure */
+};
+
+struct rpc_t {
+ union {
+ uint8_t data[NFS_READ_SIZE + (6 + NFS_MAX_ATTRS) *
+ sizeof(uint32_t)];
+ struct {
+ uint32_t id;
+ uint32_t type;
+ uint32_t rpcvers;
+ uint32_t prog;
+ uint32_t vers;
+ uint32_t proc;
+ uint32_t data[1];
+ } call;
+ struct {
+ uint32_t id;
+ uint32_t type;
+ uint32_t rstatus;
+ uint32_t verifier;
+ uint32_t v2;
+ uint32_t astatus;
+ uint32_t data[NFS_READ_SIZE / sizeof(uint32_t) +
+ NFS_MAX_ATTRS];
+ } reply;
+ } u;
+};
+void nfs_start(void); /* Begin NFS */
+
+
+/**********************************************************************/
+
+#endif /* __NFS_H__ */
diff --git a/roms/u-boot/net/pcap.c b/roms/u-boot/net/pcap.c
new file mode 100644
index 000000000..4036d8a3f
--- /dev/null
+++ b/roms/u-boot/net/pcap.c
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2019 Ramon Fried <rfried.dev@gmail.com>
+ */
+
+#include <common.h>
+#include <net.h>
+#include <net/pcap.h>
+#include <time.h>
+#include <asm/io.h>
+
+#define LINKTYPE_ETHERNET 1
+
+static bool initialized;
+static bool running;
+static bool buffer_full;
+static void *buf;
+static unsigned int max_size;
+static unsigned int pos;
+
+static unsigned long incoming_count;
+static unsigned long outgoing_count;
+
+struct pcap_header {
+ u32 magic;
+ u16 version_major;
+ u16 version_minor;
+ s32 thiszone;
+ u32 sigfigs;
+ u32 snaplen;
+ u32 network;
+};
+
+struct pcap_packet_header {
+ u32 ts_sec;
+ u32 ts_usec;
+ u32 incl_len;
+ u32 orig_len;
+};
+
+static struct pcap_header file_header = {
+ .magic = 0xa1b2c3d4,
+ .version_major = 2,
+ .version_minor = 4,
+ .snaplen = 65535,
+ .network = LINKTYPE_ETHERNET,
+};
+
+int pcap_init(phys_addr_t paddr, unsigned long size)
+{
+ buf = map_physmem(paddr, size, 0);
+ if (!buf) {
+ printf("Failed mapping PCAP memory\n");
+ return -ENOMEM;
+ }
+
+ printf("PCAP capture initialized: addr: 0x%lx max length: %lu\n",
+ (unsigned long)buf, size);
+
+ memcpy(buf, &file_header, sizeof(file_header));
+ pos = sizeof(file_header);
+ max_size = size;
+ initialized = true;
+ running = false;
+ buffer_full = false;
+ incoming_count = 0;
+ outgoing_count = 0;
+ return 0;
+}
+
+int pcap_start_stop(bool start)
+{
+ if (!initialized) {
+ printf("error: pcap was not initialized\n");
+ return -ENODEV;
+ }
+
+ running = start;
+
+ return 0;
+}
+
+int pcap_clear(void)
+{
+ if (!initialized) {
+ printf("error: pcap was not initialized\n");
+ return -ENODEV;
+ }
+
+ pos = sizeof(file_header);
+ incoming_count = 0;
+ outgoing_count = 0;
+ buffer_full = false;
+
+ printf("pcap capture cleared\n");
+ return 0;
+}
+
+int pcap_post(const void *packet, size_t len, bool outgoing)
+{
+ struct pcap_packet_header header;
+ u64 cur_time = timer_get_us();
+
+ if (!initialized || !running || !buf)
+ return -ENODEV;
+
+ if (buffer_full)
+ return -ENOMEM;
+
+ if ((pos + len + sizeof(header)) >= max_size) {
+ buffer_full = true;
+ printf("\n!!! Buffer is full, consider increasing buffer size !!!\n");
+ return -ENOMEM;
+ }
+
+ header.ts_sec = cur_time / 1000000;
+ header.ts_usec = cur_time % 1000000;
+ header.incl_len = len;
+ header.orig_len = len;
+
+ memcpy(buf + pos, &header, sizeof(header));
+ pos += sizeof(header);
+ memcpy(buf + pos, packet, len);
+ pos += len;
+
+ if (outgoing)
+ outgoing_count++;
+ else
+ incoming_count++;
+
+ env_set_hex("pcapsize", pos);
+
+ return 0;
+}
+
+int pcap_print_status(void)
+{
+ if (!initialized) {
+ printf("pcap was not initialized\n");
+ return -ENODEV;
+ }
+ printf("PCAP status:\n");
+ printf("\tInitialized addr: 0x%lx\tmax length: %u\n",
+ (unsigned long)buf, max_size);
+ printf("\tStatus: %s.\t file size: %u\n", running ? "Active" : "Idle",
+ pos);
+ printf("\tIncoming packets: %lu Outgoing packets: %lu\n",
+ incoming_count, outgoing_count);
+
+ return 0;
+}
+
+bool pcap_active(void)
+{
+ return running;
+}
diff --git a/roms/u-boot/net/ping.c b/roms/u-boot/net/ping.c
new file mode 100644
index 000000000..075df3663
--- /dev/null
+++ b/roms/u-boot/net/ping.c
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copied from Linux Monitor (LiMon) - Networking.
+ *
+ * Copyright 1994 - 2000 Neil Russell.
+ * (See License)
+ * Copyright 2000 Roland Borde
+ * Copyright 2000 Paolo Scaffardi
+ * Copyright 2000-2002 Wolfgang Denk, wd@denx.de
+ */
+
+#include "ping.h"
+#include "arp.h"
+#include <log.h>
+#include <net.h>
+
+static ushort ping_seq_number;
+
+/* The ip address to ping */
+struct in_addr net_ping_ip;
+
+static void set_icmp_header(uchar *pkt, struct in_addr dest)
+{
+ /*
+ * Construct an IP and ICMP header.
+ */
+ struct icmp_hdr *icmp = (struct icmp_hdr *)(pkt + IP_HDR_SIZE);
+
+ net_set_ip_header(pkt, dest, net_ip, IP_ICMP_HDR_SIZE, IPPROTO_ICMP);
+
+ icmp->type = ICMP_ECHO_REQUEST;
+ icmp->code = 0;
+ icmp->checksum = 0;
+ icmp->un.echo.id = 0;
+ icmp->un.echo.sequence = htons(ping_seq_number++);
+ icmp->checksum = compute_ip_checksum(icmp, ICMP_HDR_SIZE);
+}
+
+static int ping_send(void)
+{
+ uchar *pkt;
+ int eth_hdr_size;
+
+ /* XXX always send arp request */
+
+ debug_cond(DEBUG_DEV_PKT, "sending ARP for %pI4\n", &net_ping_ip);
+
+ net_arp_wait_packet_ip = net_ping_ip;
+
+ eth_hdr_size = net_set_ether(net_tx_packet, net_null_ethaddr, PROT_IP);
+ pkt = (uchar *)net_tx_packet + eth_hdr_size;
+
+ set_icmp_header(pkt, net_ping_ip);
+
+ /* size of the waiting packet */
+ arp_wait_tx_packet_size = eth_hdr_size + IP_ICMP_HDR_SIZE;
+
+ /* and do the ARP request */
+ arp_wait_try = 1;
+ arp_wait_timer_start = get_timer(0);
+ arp_request();
+ return 1; /* waiting */
+}
+
+static void ping_timeout_handler(void)
+{
+ eth_halt();
+ net_set_state(NETLOOP_FAIL); /* we did not get the reply */
+}
+
+void ping_start(void)
+{
+ printf("Using %s device\n", eth_get_name());
+ net_set_timeout_handler(10000UL, ping_timeout_handler);
+
+ ping_send();
+}
+
+void ping_receive(struct ethernet_hdr *et, struct ip_udp_hdr *ip, int len)
+{
+ struct icmp_hdr *icmph = (struct icmp_hdr *)&ip->udp_src;
+ struct in_addr src_ip;
+ int eth_hdr_size;
+ uchar *tx_packet;
+
+ switch (icmph->type) {
+ case ICMP_ECHO_REPLY:
+ src_ip = net_read_ip((void *)&ip->ip_src);
+ if (src_ip.s_addr == net_ping_ip.s_addr)
+ net_set_state(NETLOOP_SUCCESS);
+ return;
+ case ICMP_ECHO_REQUEST:
+ if (net_ip.s_addr == 0)
+ return;
+
+ eth_hdr_size = net_update_ether(et, et->et_src, PROT_IP);
+
+ debug_cond(DEBUG_DEV_PKT,
+ "Got ICMP ECHO REQUEST, return %d bytes\n",
+ eth_hdr_size + len);
+
+ ip->ip_sum = 0;
+ ip->ip_off = 0;
+ net_copy_ip((void *)&ip->ip_dst, &ip->ip_src);
+ net_copy_ip((void *)&ip->ip_src, &net_ip);
+ ip->ip_sum = compute_ip_checksum(ip, IP_HDR_SIZE);
+
+ icmph->type = ICMP_ECHO_REPLY;
+ icmph->checksum = 0;
+ icmph->checksum = compute_ip_checksum(icmph, len - IP_HDR_SIZE);
+
+ tx_packet = net_get_async_tx_pkt_buf();
+ memcpy(tx_packet, et, eth_hdr_size + len);
+ net_send_packet(tx_packet, eth_hdr_size + len);
+ return;
+/* default:
+ return;*/
+ }
+}
diff --git a/roms/u-boot/net/ping.h b/roms/u-boot/net/ping.h
new file mode 100644
index 000000000..7b6f4e566
--- /dev/null
+++ b/roms/u-boot/net/ping.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copied from Linux Monitor (LiMon) - Networking.
+ *
+ * Copyright 1994 - 2000 Neil Russell.
+ * (See License)
+ * Copyright 2000 Roland Borde
+ * Copyright 2000 Paolo Scaffardi
+ * Copyright 2000-2002 Wolfgang Denk, wd@denx.de
+ */
+
+#ifndef __PING_H__
+#define __PING_H__
+
+#include <common.h>
+#include <net.h>
+
+/*
+ * Initialize ping (beginning of netloop)
+ */
+void ping_start(void);
+
+/*
+ * Deal with the receipt of a ping packet
+ *
+ * @param et Ethernet header in packet
+ * @param ip IP header in the same packet
+ * @param len Packet length
+ */
+void ping_receive(struct ethernet_hdr *et, struct ip_udp_hdr *ip, int len);
+
+#endif /* __PING_H__ */
diff --git a/roms/u-boot/net/rarp.c b/roms/u-boot/net/rarp.c
new file mode 100644
index 000000000..a676a4253
--- /dev/null
+++ b/roms/u-boot/net/rarp.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2000-2002
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+#include <common.h>
+#include <command.h>
+#include <log.h>
+#include <net.h>
+#include <net/tftp.h>
+#include "nfs.h"
+#include "bootp.h"
+#include "rarp.h"
+
+#define TIMEOUT 5000UL /* Milliseconds before trying BOOTP again */
+#ifndef CONFIG_NET_RETRY_COUNT
+#define TIMEOUT_COUNT 5 /* # of timeouts before giving up */
+#else
+#define TIMEOUT_COUNT (CONFIG_NET_RETRY_COUNT)
+#endif
+
+int rarp_try;
+
+/*
+ * Handle a RARP received packet.
+ */
+void rarp_receive(struct ip_udp_hdr *ip, unsigned len)
+{
+ struct arp_hdr *arp;
+
+ debug_cond(DEBUG_NET_PKT, "Got RARP\n");
+ arp = (struct arp_hdr *)ip;
+ if (len < ARP_HDR_SIZE) {
+ printf("bad length %d < %d\n", len, ARP_HDR_SIZE);
+ return;
+ }
+
+ if ((ntohs(arp->ar_op) != RARPOP_REPLY) ||
+ (ntohs(arp->ar_hrd) != ARP_ETHER) ||
+ (ntohs(arp->ar_pro) != PROT_IP) ||
+ (arp->ar_hln != 6) || (arp->ar_pln != 4)) {
+ puts("invalid RARP header\n");
+ } else {
+ net_copy_ip(&net_ip, &arp->ar_data[16]);
+ if (net_server_ip.s_addr == 0)
+ net_copy_ip(&net_server_ip, &arp->ar_data[6]);
+ memcpy(net_server_ethaddr, &arp->ar_data[0], 6);
+ debug_cond(DEBUG_DEV_PKT, "Got good RARP\n");
+ net_auto_load();
+ }
+}
+
+
+/*
+ * Timeout on BOOTP request.
+ */
+static void rarp_timeout_handler(void)
+{
+ if (rarp_try >= TIMEOUT_COUNT) {
+ puts("\nRetry count exceeded; starting again\n");
+ net_start_again();
+ } else {
+ net_set_timeout_handler(TIMEOUT, rarp_timeout_handler);
+ rarp_request();
+ }
+}
+
+
+void rarp_request(void)
+{
+ uchar *pkt;
+ struct arp_hdr *rarp;
+ int eth_hdr_size;
+
+ printf("RARP broadcast %d\n", ++rarp_try);
+ pkt = net_tx_packet;
+
+ eth_hdr_size = net_set_ether(pkt, net_bcast_ethaddr, PROT_RARP);
+ pkt += eth_hdr_size;
+
+ rarp = (struct arp_hdr *)pkt;
+
+ rarp->ar_hrd = htons(ARP_ETHER);
+ rarp->ar_pro = htons(PROT_IP);
+ rarp->ar_hln = 6;
+ rarp->ar_pln = 4;
+ rarp->ar_op = htons(RARPOP_REQUEST);
+ memcpy(&rarp->ar_data[0], net_ethaddr, 6); /* source ET addr */
+ memcpy(&rarp->ar_data[6], &net_ip, 4); /* source IP addr */
+ /* dest ET addr = source ET addr ??*/
+ memcpy(&rarp->ar_data[10], net_ethaddr, 6);
+ /* dest IP addr set to broadcast */
+ memset(&rarp->ar_data[16], 0xff, 4);
+
+ net_send_packet(net_tx_packet, eth_hdr_size + ARP_HDR_SIZE);
+
+ net_set_timeout_handler(TIMEOUT, rarp_timeout_handler);
+}
diff --git a/roms/u-boot/net/rarp.h b/roms/u-boot/net/rarp.h
new file mode 100644
index 000000000..de4504e5d
--- /dev/null
+++ b/roms/u-boot/net/rarp.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * (C) Copyright 2000
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+#if defined(CONFIG_CMD_RARP)
+
+#ifndef __RARP_H__
+#define __RARP_H__
+
+#include <net.h>
+
+/**********************************************************************/
+/*
+ * Global functions and variables.
+ */
+
+extern int rarp_try;
+
+/* Process the receipt of a RARP packet */
+void rarp_receive(struct ip_udp_hdr *ip, unsigned len);
+void rarp_request(void); /* Send a RARP request */
+
+/**********************************************************************/
+
+#endif /* __RARP_H__ */
+#endif
diff --git a/roms/u-boot/net/sntp.c b/roms/u-boot/net/sntp.c
new file mode 100644
index 000000000..dac0f8cee
--- /dev/null
+++ b/roms/u-boot/net/sntp.c
@@ -0,0 +1,124 @@
+/*
+ * SNTP support driver
+ *
+ * Masami Komiya <mkomiya@sonare.it> 2005
+ *
+ */
+
+#include <common.h>
+#include <command.h>
+#include <dm.h>
+#include <log.h>
+#include <net.h>
+#include <rtc.h>
+
+#include <net/sntp.h>
+
+#define SNTP_TIMEOUT 10000UL
+
+static int sntp_our_port;
+
+/* NTP server IP address */
+struct in_addr net_ntp_server;
+/* offset time from UTC */
+int net_ntp_time_offset;
+
+static void sntp_send(void)
+{
+ struct sntp_pkt_t pkt;
+ int pktlen = SNTP_PACKET_LEN;
+ int sport;
+
+ debug("%s\n", __func__);
+
+ memset(&pkt, 0, sizeof(pkt));
+
+ pkt.li = NTP_LI_NOLEAP;
+ pkt.vn = NTP_VERSION;
+ pkt.mode = NTP_MODE_CLIENT;
+
+ memcpy((char *)net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE,
+ (char *)&pkt, pktlen);
+
+ sntp_our_port = 10000 + (get_timer(0) % 4096);
+ sport = NTP_SERVICE_PORT;
+
+ net_send_udp_packet(net_server_ethaddr, net_ntp_server, sport,
+ sntp_our_port, pktlen);
+}
+
+static void sntp_timeout_handler(void)
+{
+ puts("Timeout\n");
+ net_set_state(NETLOOP_FAIL);
+ return;
+}
+
+static void sntp_handler(uchar *pkt, unsigned dest, struct in_addr sip,
+ unsigned src, unsigned len)
+{
+ struct sntp_pkt_t *rpktp = (struct sntp_pkt_t *)pkt;
+ struct rtc_time tm;
+ ulong seconds;
+
+ debug("%s\n", __func__);
+
+ if (dest != sntp_our_port)
+ return;
+
+ /*
+ * As the RTC's used in U-Boot support second resolution only
+ * we simply ignore the sub-second field.
+ */
+ memcpy(&seconds, &rpktp->transmit_timestamp, sizeof(ulong));
+
+ rtc_to_tm(ntohl(seconds) - 2208988800UL + net_ntp_time_offset, &tm);
+#ifdef CONFIG_DM_RTC
+ struct udevice *dev;
+ int ret;
+
+ ret = uclass_get_device(UCLASS_RTC, 0, &dev);
+ if (ret)
+ printf("SNTP: cannot find RTC: err=%d\n", ret);
+ else
+ dm_rtc_set(dev, &tm);
+#elif defined(CONFIG_CMD_DATE)
+ rtc_set(&tm);
+#endif
+ printf("Date: %4d-%02d-%02d Time: %2d:%02d:%02d\n",
+ tm.tm_year, tm.tm_mon, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+
+ net_set_state(NETLOOP_SUCCESS);
+}
+
+/*
+ * SNTP:
+ *
+ * Prerequisites: - own ethernet address
+ * - own IP address
+ * We want: - network time
+ * Next step: none
+ */
+int sntp_prereq(void *data)
+{
+ if (net_ntp_server.s_addr == 0) {
+ puts("*** ERROR: NTP server address not given\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+int sntp_start(void *data)
+{
+ debug("%s\n", __func__);
+
+ net_set_timeout_handler(SNTP_TIMEOUT, sntp_timeout_handler);
+ net_set_udp_handler(sntp_handler);
+ memset(net_server_ethaddr, 0, sizeof(net_server_ethaddr));
+
+ sntp_send();
+
+ return 0;
+}
diff --git a/roms/u-boot/net/tftp.c b/roms/u-boot/net/tftp.c
new file mode 100644
index 000000000..00ab7ca0b
--- /dev/null
+++ b/roms/u-boot/net/tftp.c
@@ -0,0 +1,931 @@
+/*
+ * Copyright 1994, 1995, 2000 Neil Russell.
+ * (See License)
+ * Copyright 2000, 2001 DENX Software Engineering, Wolfgang Denk, wd@denx.de
+ * Copyright 2011 Comelit Group SpA,
+ * Luca Ceresoli <luca.ceresoli@comelit.it>
+ */
+#include <common.h>
+#include <command.h>
+#include <efi_loader.h>
+#include <env.h>
+#include <image.h>
+#include <lmb.h>
+#include <log.h>
+#include <mapmem.h>
+#include <net.h>
+#include <asm/global_data.h>
+#include <net/tftp.h>
+#include "bootp.h"
+#ifdef CONFIG_SYS_DIRECT_FLASH_TFTP
+#include <flash.h>
+#endif
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* Well known TFTP port # */
+#define WELL_KNOWN_PORT 69
+/* Millisecs to timeout for lost pkt */
+#define TIMEOUT 5000UL
+#ifndef CONFIG_NET_RETRY_COUNT
+/* # of timeouts before giving up */
+# define TIMEOUT_COUNT 10
+#else
+# define TIMEOUT_COUNT (CONFIG_NET_RETRY_COUNT * 2)
+#endif
+/* Number of "loading" hashes per line (for checking the image size) */
+#define HASHES_PER_LINE 65
+
+/*
+ * TFTP operations.
+ */
+#define TFTP_RRQ 1
+#define TFTP_WRQ 2
+#define TFTP_DATA 3
+#define TFTP_ACK 4
+#define TFTP_ERROR 5
+#define TFTP_OACK 6
+
+static ulong timeout_ms = TIMEOUT;
+static int timeout_count_max = TIMEOUT_COUNT;
+static ulong time_start; /* Record time we started tftp */
+
+/*
+ * These globals govern the timeout behavior when attempting a connection to a
+ * TFTP server. tftp_timeout_ms specifies the number of milliseconds to
+ * wait for the server to respond to initial connection. Second global,
+ * tftp_timeout_count_max, gives the number of such connection retries.
+ * tftp_timeout_count_max must be non-negative and tftp_timeout_ms must be
+ * positive. The globals are meant to be set (and restored) by code needing
+ * non-standard timeout behavior when initiating a TFTP transfer.
+ */
+ulong tftp_timeout_ms = TIMEOUT;
+int tftp_timeout_count_max = TIMEOUT_COUNT;
+
+enum {
+ TFTP_ERR_UNDEFINED = 0,
+ TFTP_ERR_FILE_NOT_FOUND = 1,
+ TFTP_ERR_ACCESS_DENIED = 2,
+ TFTP_ERR_DISK_FULL = 3,
+ TFTP_ERR_UNEXPECTED_OPCODE = 4,
+ TFTP_ERR_UNKNOWN_TRANSFER_ID = 5,
+ TFTP_ERR_FILE_ALREADY_EXISTS = 6,
+ TFTP_ERR_OPTION_NEGOTIATION = 8,
+};
+
+static struct in_addr tftp_remote_ip;
+/* The UDP port at their end */
+static int tftp_remote_port;
+/* The UDP port at our end */
+static int tftp_our_port;
+static int timeout_count;
+/* packet sequence number */
+static ulong tftp_cur_block;
+/* last packet sequence number received */
+static ulong tftp_prev_block;
+/* count of sequence number wraparounds */
+static ulong tftp_block_wrap;
+/* memory offset due to wrapping */
+static ulong tftp_block_wrap_offset;
+static int tftp_state;
+static ulong tftp_load_addr;
+#ifdef CONFIG_LMB
+static ulong tftp_load_size;
+#endif
+#ifdef CONFIG_TFTP_TSIZE
+/* The file size reported by the server */
+static int tftp_tsize;
+/* The number of hashes we printed */
+static short tftp_tsize_num_hash;
+#endif
+/* The window size negotiated */
+static ushort tftp_windowsize;
+/* Next block to send ack to */
+static ushort tftp_next_ack;
+/* Last nack block we send */
+static ushort tftp_last_nack;
+#ifdef CONFIG_CMD_TFTPPUT
+/* 1 if writing, else 0 */
+static int tftp_put_active;
+/* 1 if we have sent the last block */
+static int tftp_put_final_block_sent;
+#else
+#define tftp_put_active 0
+#endif
+
+#define STATE_SEND_RRQ 1
+#define STATE_DATA 2
+#define STATE_TOO_LARGE 3
+#define STATE_BAD_MAGIC 4
+#define STATE_OACK 5
+#define STATE_RECV_WRQ 6
+#define STATE_SEND_WRQ 7
+#define STATE_INVALID_OPTION 8
+
+/* default TFTP block size */
+#define TFTP_BLOCK_SIZE 512
+/* sequence number is 16 bit */
+#define TFTP_SEQUENCE_SIZE ((ulong)(1<<16))
+
+#define DEFAULT_NAME_LEN (8 + 4 + 1)
+static char default_filename[DEFAULT_NAME_LEN];
+
+#ifndef CONFIG_TFTP_FILE_NAME_MAX_LEN
+#define MAX_LEN 128
+#else
+#define MAX_LEN CONFIG_TFTP_FILE_NAME_MAX_LEN
+#endif
+
+static char tftp_filename[MAX_LEN];
+
+/* 512 is poor choice for ethernet, MTU is typically 1500.
+ * Minus eth.hdrs thats 1468. Can get 2x better throughput with
+ * almost-MTU block sizes. At least try... fall back to 512 if need be.
+ * (but those using CONFIG_IP_DEFRAG may want to set a larger block in cfg file)
+ */
+
+/* When windowsize is defined to 1,
+ * tftp behaves the same way as it was
+ * never declared
+ */
+#ifdef CONFIG_TFTP_WINDOWSIZE
+#define TFTP_WINDOWSIZE CONFIG_TFTP_WINDOWSIZE
+#else
+#define TFTP_WINDOWSIZE 1
+#endif
+
+static unsigned short tftp_block_size = TFTP_BLOCK_SIZE;
+static unsigned short tftp_block_size_option = CONFIG_TFTP_BLOCKSIZE;
+static unsigned short tftp_window_size_option = TFTP_WINDOWSIZE;
+
+static inline int store_block(int block, uchar *src, unsigned int len)
+{
+ ulong offset = block * tftp_block_size + tftp_block_wrap_offset -
+ tftp_block_size;
+ ulong newsize = offset + len;
+ ulong store_addr = tftp_load_addr + offset;
+#ifdef CONFIG_SYS_DIRECT_FLASH_TFTP
+ int i, rc = 0;
+
+ for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) {
+ /* start address in flash? */
+ if (flash_info[i].flash_id == FLASH_UNKNOWN)
+ continue;
+ if (store_addr >= flash_info[i].start[0]) {
+ rc = 1;
+ break;
+ }
+ }
+
+ if (rc) { /* Flash is destination for this packet */
+ rc = flash_write((char *)src, store_addr, len);
+ if (rc) {
+ flash_perror(rc);
+ return rc;
+ }
+ } else
+#endif /* CONFIG_SYS_DIRECT_FLASH_TFTP */
+ {
+ void *ptr;
+
+#ifdef CONFIG_LMB
+ ulong end_addr = tftp_load_addr + tftp_load_size;
+
+ if (!end_addr)
+ end_addr = ULONG_MAX;
+
+ if (store_addr < tftp_load_addr ||
+ store_addr + len > end_addr) {
+ puts("\nTFTP error: ");
+ puts("trying to overwrite reserved memory...\n");
+ return -1;
+ }
+#endif
+ ptr = map_sysmem(store_addr, len);
+ memcpy(ptr, src, len);
+ unmap_sysmem(ptr);
+ }
+
+ if (net_boot_file_size < newsize)
+ net_boot_file_size = newsize;
+
+ return 0;
+}
+
+/* Clear our state ready for a new transfer */
+static void new_transfer(void)
+{
+ tftp_prev_block = 0;
+ tftp_block_wrap = 0;
+ tftp_block_wrap_offset = 0;
+#ifdef CONFIG_CMD_TFTPPUT
+ tftp_put_final_block_sent = 0;
+#endif
+}
+
+#ifdef CONFIG_CMD_TFTPPUT
+/**
+ * Load the next block from memory to be sent over tftp.
+ *
+ * @param block Block number to send
+ * @param dst Destination buffer for data
+ * @param len Number of bytes in block (this one and every other)
+ * @return number of bytes loaded
+ */
+static int load_block(unsigned block, uchar *dst, unsigned len)
+{
+ /* We may want to get the final block from the previous set */
+ ulong offset = block * tftp_block_size + tftp_block_wrap_offset -
+ tftp_block_size;
+ ulong tosend = len;
+
+ tosend = min(net_boot_file_size - offset, tosend);
+ (void)memcpy(dst, (void *)(image_save_addr + offset), tosend);
+ debug("%s: block=%u, offset=%lu, len=%u, tosend=%lu\n", __func__,
+ block, offset, len, tosend);
+ return tosend;
+}
+#endif
+
+static void tftp_send(void);
+static void tftp_timeout_handler(void);
+
+/**********************************************************************/
+
+static void show_block_marker(void)
+{
+ ulong pos;
+
+#ifdef CONFIG_TFTP_TSIZE
+ if (tftp_tsize) {
+ pos = tftp_cur_block * tftp_block_size +
+ tftp_block_wrap_offset;
+ if (pos > tftp_tsize)
+ pos = tftp_tsize;
+
+ while (tftp_tsize_num_hash < pos * 50 / tftp_tsize) {
+ putc('#');
+ tftp_tsize_num_hash++;
+ }
+ } else
+#endif
+ {
+ pos = (tftp_cur_block - 1) +
+ (tftp_block_wrap * TFTP_SEQUENCE_SIZE);
+ if ((pos % 10) == 0)
+ putc('#');
+ else if (((pos + 1) % (10 * HASHES_PER_LINE)) == 0)
+ puts("\n\t ");
+ }
+}
+
+/**
+ * restart the current transfer due to an error
+ *
+ * @param msg Message to print for user
+ */
+static void restart(const char *msg)
+{
+ printf("\n%s; starting again\n", msg);
+ net_start_again();
+}
+
+/*
+ * Check if the block number has wrapped, and update progress
+ *
+ * TODO: The egregious use of global variables in this file should be tidied.
+ */
+static void update_block_number(void)
+{
+ /*
+ * RFC1350 specifies that the first data packet will
+ * have sequence number 1. If we receive a sequence
+ * number of 0 this means that there was a wrap
+ * around of the (16 bit) counter.
+ */
+ if (tftp_cur_block == 0 && tftp_prev_block != 0) {
+ tftp_block_wrap++;
+ tftp_block_wrap_offset += tftp_block_size * TFTP_SEQUENCE_SIZE;
+ timeout_count = 0; /* we've done well, reset the timeout */
+ }
+ show_block_marker();
+}
+
+/* The TFTP get or put is complete */
+static void tftp_complete(void)
+{
+#ifdef CONFIG_TFTP_TSIZE
+ /* Print hash marks for the last packet received */
+ while (tftp_tsize && tftp_tsize_num_hash < 49) {
+ putc('#');
+ tftp_tsize_num_hash++;
+ }
+ puts(" ");
+ print_size(tftp_tsize, "");
+#endif
+ time_start = get_timer(time_start);
+ if (time_start > 0) {
+ puts("\n\t "); /* Line up with "Loading: " */
+ print_size(net_boot_file_size /
+ time_start * 1000, "/s");
+ }
+ puts("\ndone\n");
+ if (IS_ENABLED(CONFIG_CMD_BOOTEFI)) {
+ if (!tftp_put_active)
+ efi_set_bootdev("Net", "", tftp_filename,
+ map_sysmem(tftp_load_addr, 0),
+ net_boot_file_size);
+ }
+ net_set_state(NETLOOP_SUCCESS);
+}
+
+static void tftp_send(void)
+{
+ uchar *pkt;
+ uchar *xp;
+ int len = 0;
+ ushort *s;
+ bool err_pkt = false;
+
+ /*
+ * We will always be sending some sort of packet, so
+ * cobble together the packet headers now.
+ */
+ pkt = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE;
+
+ switch (tftp_state) {
+ case STATE_SEND_RRQ:
+ case STATE_SEND_WRQ:
+ xp = pkt;
+ s = (ushort *)pkt;
+#ifdef CONFIG_CMD_TFTPPUT
+ *s++ = htons(tftp_state == STATE_SEND_RRQ ? TFTP_RRQ :
+ TFTP_WRQ);
+#else
+ *s++ = htons(TFTP_RRQ);
+#endif
+ pkt = (uchar *)s;
+ strcpy((char *)pkt, tftp_filename);
+ pkt += strlen(tftp_filename) + 1;
+ strcpy((char *)pkt, "octet");
+ pkt += 5 /*strlen("octet")*/ + 1;
+ strcpy((char *)pkt, "timeout");
+ pkt += 7 /*strlen("timeout")*/ + 1;
+ sprintf((char *)pkt, "%lu", timeout_ms / 1000);
+ debug("send option \"timeout %s\"\n", (char *)pkt);
+ pkt += strlen((char *)pkt) + 1;
+#ifdef CONFIG_TFTP_TSIZE
+ pkt += sprintf((char *)pkt, "tsize%c%u%c",
+ 0, net_boot_file_size, 0);
+#endif
+ /* try for more effic. blk size */
+ pkt += sprintf((char *)pkt, "blksize%c%d%c",
+ 0, tftp_block_size_option, 0);
+
+ /* try for more effic. window size.
+ * Implemented only for tftp get.
+ * Don't bother sending if it's 1
+ */
+ if (tftp_state == STATE_SEND_RRQ && tftp_window_size_option > 1)
+ pkt += sprintf((char *)pkt, "windowsize%c%d%c",
+ 0, tftp_window_size_option, 0);
+ len = pkt - xp;
+ break;
+
+ case STATE_OACK:
+
+ case STATE_RECV_WRQ:
+ case STATE_DATA:
+ xp = pkt;
+ s = (ushort *)pkt;
+ s[0] = htons(TFTP_ACK);
+ s[1] = htons(tftp_cur_block);
+ pkt = (uchar *)(s + 2);
+#ifdef CONFIG_CMD_TFTPPUT
+ if (tftp_put_active) {
+ int toload = tftp_block_size;
+ int loaded = load_block(tftp_cur_block, pkt, toload);
+
+ s[0] = htons(TFTP_DATA);
+ pkt += loaded;
+ tftp_put_final_block_sent = (loaded < toload);
+ }
+#endif
+ len = pkt - xp;
+ break;
+
+ case STATE_TOO_LARGE:
+ xp = pkt;
+ s = (ushort *)pkt;
+ *s++ = htons(TFTP_ERROR);
+ *s++ = htons(3);
+
+ pkt = (uchar *)s;
+ strcpy((char *)pkt, "File too large");
+ pkt += 14 /*strlen("File too large")*/ + 1;
+ len = pkt - xp;
+ err_pkt = true;
+ break;
+
+ case STATE_BAD_MAGIC:
+ xp = pkt;
+ s = (ushort *)pkt;
+ *s++ = htons(TFTP_ERROR);
+ *s++ = htons(2);
+ pkt = (uchar *)s;
+ strcpy((char *)pkt, "File has bad magic");
+ pkt += 18 /*strlen("File has bad magic")*/ + 1;
+ len = pkt - xp;
+ err_pkt = true;
+ break;
+
+ case STATE_INVALID_OPTION:
+ xp = pkt;
+ s = (ushort *)pkt;
+ *s++ = htons(TFTP_ERROR);
+ *s++ = htons(TFTP_ERR_OPTION_NEGOTIATION);
+ pkt = (uchar *)s;
+ strcpy((char *)pkt, "Option Negotiation Failed");
+ /* strlen("Option Negotiation Failed") + NULL*/
+ pkt += 25 + 1;
+ len = pkt - xp;
+ err_pkt = true;
+ break;
+ }
+
+ net_send_udp_packet(net_server_ethaddr, tftp_remote_ip,
+ tftp_remote_port, tftp_our_port, len);
+
+ if (err_pkt)
+ net_set_state(NETLOOP_FAIL);
+}
+
+#ifdef CONFIG_CMD_TFTPPUT
+static void icmp_handler(unsigned type, unsigned code, unsigned dest,
+ struct in_addr sip, unsigned src, uchar *pkt,
+ unsigned len)
+{
+ if (type == ICMP_NOT_REACH && code == ICMP_NOT_REACH_PORT) {
+ /* Oh dear the other end has gone away */
+ restart("TFTP server died");
+ }
+}
+#endif
+
+static void tftp_handler(uchar *pkt, unsigned dest, struct in_addr sip,
+ unsigned src, unsigned len)
+{
+ __be16 proto;
+ __be16 *s;
+ int i;
+ u16 timeout_val_rcvd;
+
+ if (dest != tftp_our_port) {
+ return;
+ }
+ if (tftp_state != STATE_SEND_RRQ && src != tftp_remote_port &&
+ tftp_state != STATE_RECV_WRQ && tftp_state != STATE_SEND_WRQ)
+ return;
+
+ if (len < 2)
+ return;
+ len -= 2;
+ /* warning: don't use increment (++) in ntohs() macros!! */
+ s = (__be16 *)pkt;
+ proto = *s++;
+ pkt = (uchar *)s;
+ switch (ntohs(proto)) {
+ case TFTP_RRQ:
+ break;
+
+ case TFTP_ACK:
+#ifdef CONFIG_CMD_TFTPPUT
+ if (tftp_put_active) {
+ if (tftp_put_final_block_sent) {
+ tftp_complete();
+ } else {
+ /*
+ * Move to the next block. We want our block
+ * count to wrap just like the other end!
+ */
+ int block = ntohs(*s);
+ int ack_ok = (tftp_cur_block == block);
+
+ tftp_prev_block = tftp_cur_block;
+ tftp_cur_block = (unsigned short)(block + 1);
+ update_block_number();
+ if (ack_ok)
+ tftp_send(); /* Send next data block */
+ }
+ }
+#endif
+ break;
+
+ default:
+ break;
+
+#ifdef CONFIG_CMD_TFTPSRV
+ case TFTP_WRQ:
+ debug("Got WRQ\n");
+ tftp_remote_ip = sip;
+ tftp_remote_port = src;
+ tftp_our_port = 1024 + (get_timer(0) % 3072);
+ new_transfer();
+ tftp_send(); /* Send ACK(0) */
+ break;
+#endif
+
+ case TFTP_OACK:
+ debug("Got OACK: ");
+ for (i = 0; i < len; i++) {
+ if (pkt[i] == '\0')
+ debug(" ");
+ else
+ debug("%c", pkt[i]);
+ }
+ debug("\n");
+ tftp_state = STATE_OACK;
+ tftp_remote_port = src;
+ /*
+ * Check for 'blksize' option.
+ * Careful: "i" is signed, "len" is unsigned, thus
+ * something like "len-8" may give a *huge* number
+ */
+ for (i = 0; i+8 < len; i++) {
+ if (strcasecmp((char *)pkt + i, "blksize") == 0) {
+ tftp_block_size = (unsigned short)
+ simple_strtoul((char *)pkt + i + 8,
+ NULL, 10);
+ debug("Blocksize oack: %s, %d\n",
+ (char *)pkt + i + 8, tftp_block_size);
+ if (tftp_block_size > tftp_block_size_option) {
+ printf("Invalid blk size(=%d)\n",
+ tftp_block_size);
+ tftp_state = STATE_INVALID_OPTION;
+ }
+ }
+ if (strcasecmp((char *)pkt + i, "timeout") == 0) {
+ timeout_val_rcvd = (unsigned short)
+ simple_strtoul((char *)pkt + i + 8,
+ NULL, 10);
+ debug("Timeout oack: %s, %d\n",
+ (char *)pkt + i + 8, timeout_val_rcvd);
+ if (timeout_val_rcvd != (timeout_ms / 1000)) {
+ printf("Invalid timeout val(=%d s)\n",
+ timeout_val_rcvd);
+ tftp_state = STATE_INVALID_OPTION;
+ }
+ }
+#ifdef CONFIG_TFTP_TSIZE
+ if (strcasecmp((char *)pkt + i, "tsize") == 0) {
+ tftp_tsize = simple_strtoul((char *)pkt + i + 6,
+ NULL, 10);
+ debug("size = %s, %d\n",
+ (char *)pkt + i + 6, tftp_tsize);
+ }
+#endif
+ if (strcasecmp((char *)pkt + i, "windowsize") == 0) {
+ tftp_windowsize =
+ simple_strtoul((char *)pkt + i + 11,
+ NULL, 10);
+ debug("windowsize = %s, %d\n",
+ (char *)pkt + i + 11, tftp_windowsize);
+ }
+ }
+
+ tftp_next_ack = tftp_windowsize;
+
+#ifdef CONFIG_CMD_TFTPPUT
+ if (tftp_put_active && tftp_state == STATE_OACK) {
+ /* Get ready to send the first block */
+ tftp_state = STATE_DATA;
+ tftp_cur_block++;
+ }
+#endif
+ tftp_send(); /* Send ACK or first data block */
+ break;
+ case TFTP_DATA:
+ if (len < 2)
+ return;
+ len -= 2;
+
+ if (ntohs(*(__be16 *)pkt) != (ushort)(tftp_cur_block + 1)) {
+ debug("Received unexpected block: %d, expected: %d\n",
+ ntohs(*(__be16 *)pkt),
+ (ushort)(tftp_cur_block + 1));
+ /*
+ * If one packet is dropped most likely
+ * all other buffers in the window
+ * that will arrive will cause a sending NACK.
+ * This just overwellms the server, let's just send one.
+ */
+ if (tftp_last_nack != tftp_cur_block) {
+ tftp_send();
+ tftp_last_nack = tftp_cur_block;
+ tftp_next_ack = (ushort)(tftp_cur_block +
+ tftp_windowsize);
+ }
+ break;
+ }
+
+ tftp_cur_block++;
+ tftp_cur_block %= TFTP_SEQUENCE_SIZE;
+
+ if (tftp_state == STATE_SEND_RRQ) {
+ debug("Server did not acknowledge any options!\n");
+ tftp_next_ack = tftp_windowsize;
+ }
+
+ if (tftp_state == STATE_SEND_RRQ || tftp_state == STATE_OACK ||
+ tftp_state == STATE_RECV_WRQ) {
+ /* first block received */
+ tftp_state = STATE_DATA;
+ tftp_remote_port = src;
+ new_transfer();
+
+ if (tftp_cur_block != 1) { /* Assertion */
+ puts("\nTFTP error: ");
+ printf("First block is not block 1 (%ld)\n",
+ tftp_cur_block);
+ puts("Starting again\n\n");
+ net_start_again();
+ break;
+ }
+ }
+
+ if (tftp_cur_block == tftp_prev_block) {
+ /* Same block again; ignore it. */
+ break;
+ }
+
+ update_block_number();
+ tftp_prev_block = tftp_cur_block;
+ timeout_count_max = tftp_timeout_count_max;
+ net_set_timeout_handler(timeout_ms, tftp_timeout_handler);
+
+ if (store_block(tftp_cur_block, pkt + 2, len)) {
+ eth_halt();
+ net_set_state(NETLOOP_FAIL);
+ break;
+ }
+
+ if (len < tftp_block_size) {
+ tftp_send();
+ tftp_complete();
+ break;
+ }
+
+ /*
+ * Acknowledge the block just received, which will prompt
+ * the remote for the next one.
+ */
+ if (tftp_cur_block == tftp_next_ack) {
+ tftp_send();
+ tftp_next_ack += tftp_windowsize;
+ }
+ break;
+
+ case TFTP_ERROR:
+ printf("\nTFTP error: '%s' (%d)\n",
+ pkt + 2, ntohs(*(__be16 *)pkt));
+
+ switch (ntohs(*(__be16 *)pkt)) {
+ case TFTP_ERR_FILE_NOT_FOUND:
+ case TFTP_ERR_ACCESS_DENIED:
+ puts("Not retrying...\n");
+ eth_halt();
+ net_set_state(NETLOOP_FAIL);
+ break;
+ case TFTP_ERR_UNDEFINED:
+ case TFTP_ERR_DISK_FULL:
+ case TFTP_ERR_UNEXPECTED_OPCODE:
+ case TFTP_ERR_UNKNOWN_TRANSFER_ID:
+ case TFTP_ERR_FILE_ALREADY_EXISTS:
+ default:
+ puts("Starting again\n\n");
+ net_start_again();
+ break;
+ }
+ break;
+ }
+}
+
+
+static void tftp_timeout_handler(void)
+{
+ if (++timeout_count > timeout_count_max) {
+ restart("Retry count exceeded");
+ } else {
+ puts("T ");
+ net_set_timeout_handler(timeout_ms, tftp_timeout_handler);
+ if (tftp_state != STATE_RECV_WRQ)
+ tftp_send();
+ }
+}
+
+/* Initialize tftp_load_addr and tftp_load_size from image_load_addr and lmb */
+static int tftp_init_load_addr(void)
+{
+#ifdef CONFIG_LMB
+ struct lmb lmb;
+ phys_size_t max_size;
+
+ lmb_init_and_reserve(&lmb, gd->bd, (void *)gd->fdt_blob);
+
+ max_size = lmb_get_free_size(&lmb, image_load_addr);
+ if (!max_size)
+ return -1;
+
+ tftp_load_size = max_size;
+#endif
+ tftp_load_addr = image_load_addr;
+ return 0;
+}
+
+void tftp_start(enum proto_t protocol)
+{
+#if CONFIG_NET_TFTP_VARS
+ char *ep; /* Environment pointer */
+
+ /*
+ * Allow the user to choose TFTP blocksize and timeout.
+ * TFTP protocol has a minimal timeout of 1 second.
+ */
+
+ ep = env_get("tftpblocksize");
+ if (ep != NULL)
+ tftp_block_size_option = simple_strtol(ep, NULL, 10);
+
+ ep = env_get("tftpwindowsize");
+ if (ep != NULL)
+ tftp_window_size_option = simple_strtol(ep, NULL, 10);
+
+ ep = env_get("tftptimeout");
+ if (ep != NULL)
+ timeout_ms = simple_strtol(ep, NULL, 10);
+
+ if (timeout_ms < 1000) {
+ printf("TFTP timeout (%ld ms) too low, set min = 1000 ms\n",
+ timeout_ms);
+ timeout_ms = 1000;
+ }
+
+ ep = env_get("tftptimeoutcountmax");
+ if (ep != NULL)
+ tftp_timeout_count_max = simple_strtol(ep, NULL, 10);
+
+ if (tftp_timeout_count_max < 0) {
+ printf("TFTP timeout count max (%d ms) negative, set to 0\n",
+ tftp_timeout_count_max);
+ tftp_timeout_count_max = 0;
+ }
+#endif
+
+ debug("TFTP blocksize = %i, TFTP windowsize = %d timeout = %ld ms\n",
+ tftp_block_size_option, tftp_window_size_option, timeout_ms);
+
+ tftp_remote_ip = net_server_ip;
+ if (!net_parse_bootfile(&tftp_remote_ip, tftp_filename, MAX_LEN)) {
+ sprintf(default_filename, "%02X%02X%02X%02X.img",
+ net_ip.s_addr & 0xFF,
+ (net_ip.s_addr >> 8) & 0xFF,
+ (net_ip.s_addr >> 16) & 0xFF,
+ (net_ip.s_addr >> 24) & 0xFF);
+
+ strncpy(tftp_filename, default_filename, DEFAULT_NAME_LEN);
+ tftp_filename[DEFAULT_NAME_LEN - 1] = 0;
+
+ printf("*** Warning: no boot file name; using '%s'\n",
+ tftp_filename);
+ }
+
+ printf("Using %s device\n", eth_get_name());
+ printf("TFTP %s server %pI4; our IP address is %pI4",
+#ifdef CONFIG_CMD_TFTPPUT
+ protocol == TFTPPUT ? "to" : "from",
+#else
+ "from",
+#endif
+ &tftp_remote_ip, &net_ip);
+
+ /* Check if we need to send across this subnet */
+ if (net_gateway.s_addr && net_netmask.s_addr) {
+ struct in_addr our_net;
+ struct in_addr remote_net;
+
+ our_net.s_addr = net_ip.s_addr & net_netmask.s_addr;
+ remote_net.s_addr = tftp_remote_ip.s_addr & net_netmask.s_addr;
+ if (our_net.s_addr != remote_net.s_addr)
+ printf("; sending through gateway %pI4", &net_gateway);
+ }
+ putc('\n');
+
+ printf("Filename '%s'.", tftp_filename);
+
+ if (net_boot_file_expected_size_in_blocks) {
+ printf(" Size is 0x%x Bytes = ",
+ net_boot_file_expected_size_in_blocks << 9);
+ print_size(net_boot_file_expected_size_in_blocks << 9, "");
+ }
+
+ putc('\n');
+#ifdef CONFIG_CMD_TFTPPUT
+ tftp_put_active = (protocol == TFTPPUT);
+ if (tftp_put_active) {
+ printf("Save address: 0x%lx\n", image_save_addr);
+ printf("Save size: 0x%lx\n", image_save_size);
+ net_boot_file_size = image_save_size;
+ puts("Saving: *\b");
+ tftp_state = STATE_SEND_WRQ;
+ new_transfer();
+ } else
+#endif
+ {
+ if (tftp_init_load_addr()) {
+ eth_halt();
+ net_set_state(NETLOOP_FAIL);
+ puts("\nTFTP error: ");
+ puts("trying to overwrite reserved memory...\n");
+ return;
+ }
+ printf("Load address: 0x%lx\n", tftp_load_addr);
+ puts("Loading: *\b");
+ tftp_state = STATE_SEND_RRQ;
+ }
+
+ time_start = get_timer(0);
+ timeout_count_max = tftp_timeout_count_max;
+
+ net_set_timeout_handler(timeout_ms, tftp_timeout_handler);
+ net_set_udp_handler(tftp_handler);
+#ifdef CONFIG_CMD_TFTPPUT
+ net_set_icmp_handler(icmp_handler);
+#endif
+ tftp_remote_port = WELL_KNOWN_PORT;
+ timeout_count = 0;
+ /* Use a pseudo-random port unless a specific port is set */
+ tftp_our_port = 1024 + (get_timer(0) % 3072);
+
+#ifdef CONFIG_TFTP_PORT
+ ep = env_get("tftpdstp");
+ if (ep != NULL)
+ tftp_remote_port = simple_strtol(ep, NULL, 10);
+ ep = env_get("tftpsrcp");
+ if (ep != NULL)
+ tftp_our_port = simple_strtol(ep, NULL, 10);
+#endif
+ tftp_cur_block = 0;
+ tftp_windowsize = 1;
+ tftp_last_nack = 0;
+ /* zero out server ether in case the server ip has changed */
+ memset(net_server_ethaddr, 0, 6);
+ /* Revert tftp_block_size to dflt */
+ tftp_block_size = TFTP_BLOCK_SIZE;
+#ifdef CONFIG_TFTP_TSIZE
+ tftp_tsize = 0;
+ tftp_tsize_num_hash = 0;
+#endif
+
+ tftp_send();
+}
+
+#ifdef CONFIG_CMD_TFTPSRV
+void tftp_start_server(void)
+{
+ tftp_filename[0] = 0;
+
+ if (tftp_init_load_addr()) {
+ eth_halt();
+ net_set_state(NETLOOP_FAIL);
+ puts("\nTFTP error: trying to overwrite reserved memory...\n");
+ return;
+ }
+ printf("Using %s device\n", eth_get_name());
+ printf("Listening for TFTP transfer on %pI4\n", &net_ip);
+ printf("Load address: 0x%lx\n", tftp_load_addr);
+
+ puts("Loading: *\b");
+
+ timeout_count_max = tftp_timeout_count_max;
+ timeout_count = 0;
+ timeout_ms = TIMEOUT;
+ net_set_timeout_handler(timeout_ms, tftp_timeout_handler);
+
+ /* Revert tftp_block_size to dflt */
+ tftp_block_size = TFTP_BLOCK_SIZE;
+ tftp_cur_block = 0;
+ tftp_our_port = WELL_KNOWN_PORT;
+
+#ifdef CONFIG_TFTP_TSIZE
+ tftp_tsize = 0;
+ tftp_tsize_num_hash = 0;
+#endif
+
+ tftp_state = STATE_RECV_WRQ;
+ net_set_udp_handler(tftp_handler);
+
+ /* zero out server ether in case the server ip has changed */
+ memset(net_server_ethaddr, 0, 6);
+}
+#endif /* CONFIG_CMD_TFTPSRV */
+
diff --git a/roms/u-boot/net/udp.c b/roms/u-boot/net/udp.c
new file mode 100644
index 000000000..a93822f51
--- /dev/null
+++ b/roms/u-boot/net/udp.c
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Philippe Reynes <philippe.reynes@softathome.com>
+ */
+
+#include <common.h>
+#include <net.h>
+#include <net/udp.h>
+
+static struct udp_ops *udp_ops;
+
+int udp_prereq(void)
+{
+ int ret = 0;
+
+ if (udp_ops->prereq)
+ ret = udp_ops->prereq(udp_ops->data);
+
+ return ret;
+}
+
+int udp_start(void)
+{
+ return udp_ops->start(udp_ops->data);
+}
+
+int udp_loop(struct udp_ops *ops)
+{
+ int ret = -1;
+
+ if (!ops) {
+ printf("%s: ops should not be null\n", __func__);
+ goto out;
+ }
+
+ if (!ops->start) {
+ printf("%s: no start function defined\n", __func__);
+ goto out;
+ }
+
+ udp_ops = ops;
+ ret = net_loop(UDP);
+
+ out:
+ return ret;
+}
diff --git a/roms/u-boot/net/wol.c b/roms/u-boot/net/wol.c
new file mode 100644
index 000000000..0a625668a
--- /dev/null
+++ b/roms/u-boot/net/wol.c
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2018 Lothar Felten, lothar.felten@gmail.com
+ */
+
+#include <common.h>
+#include <command.h>
+#include <env.h>
+#include <net.h>
+#include "wol.h"
+
+static ulong wol_timeout = WOL_DEFAULT_TIMEOUT;
+
+/*
+ * Check incoming Wake-on-LAN packet for:
+ * - sync bytes
+ * - sixteen copies of the target MAC address
+ *
+ * @param wol Wake-on-LAN packet
+ * @param len Packet length
+ */
+static int wol_check_magic(struct wol_hdr *wol, unsigned int len)
+{
+ int i;
+
+ if (len < sizeof(struct wol_hdr))
+ return 0;
+
+ for (i = 0; i < WOL_SYNC_COUNT; i++)
+ if (wol->wol_sync[i] != WOL_SYNC_BYTE)
+ return 0;
+
+ for (i = 0; i < WOL_MAC_REPETITIONS; i++)
+ if (memcmp(&wol->wol_dest[i * ARP_HLEN],
+ net_ethaddr, ARP_HLEN) != 0)
+ return 0;
+
+ return 1;
+}
+
+void wol_receive(struct ip_udp_hdr *ip, unsigned int len)
+{
+ struct wol_hdr *wol;
+
+ wol = (struct wol_hdr *)ip;
+
+ if (!wol_check_magic(wol, len))
+ return;
+
+ /* save the optional password using the ether-wake formats */
+ /* don't check for exact length, the packet might have padding */
+ if (len >= (sizeof(struct wol_hdr) + WOL_PASSWORD_6B)) {
+ eth_env_set_enetaddr("wolpassword", wol->wol_passwd);
+ } else if (len >= (sizeof(struct wol_hdr) + WOL_PASSWORD_4B)) {
+ char buffer[16];
+ struct in_addr *ip = (struct in_addr *)(wol->wol_passwd);
+
+ ip_to_string(*ip, buffer);
+ env_set("wolpassword", buffer);
+ }
+ net_set_state(NETLOOP_SUCCESS);
+}
+
+static void wol_udp_handler(uchar *pkt, unsigned int dest, struct in_addr sip,
+ unsigned int src, unsigned int len)
+{
+ struct wol_hdr *wol;
+
+ wol = (struct wol_hdr *)pkt;
+
+ /* UDP destination port must be 0, 7 or 9 */
+ if (dest != 0 && dest != 7 && dest != 9)
+ return;
+
+ if (!wol_check_magic(wol, len))
+ return;
+
+ net_set_state(NETLOOP_SUCCESS);
+}
+
+void wol_set_timeout(ulong timeout)
+{
+ wol_timeout = timeout;
+}
+
+static void wol_timeout_handler(void)
+{
+ eth_halt();
+ net_set_state(NETLOOP_FAIL);
+}
+
+void wol_start(void)
+{
+ net_set_timeout_handler(wol_timeout, wol_timeout_handler);
+ net_set_udp_handler(wol_udp_handler);
+}
diff --git a/roms/u-boot/net/wol.h b/roms/u-boot/net/wol.h
new file mode 100644
index 000000000..ebc81f24b
--- /dev/null
+++ b/roms/u-boot/net/wol.h
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * wol - Wake-on-LAN
+ *
+ * Supports both Wake-on-LAN packet types:
+ * - EtherType 0x0842 packets
+ * - UDP packets on ports 0, 7 and 9.
+ *
+ * Copyright 2018 Lothar Felten, lothar.felten@gmail.com
+ */
+
+#if defined(CONFIG_CMD_WOL)
+
+#ifndef __WOL_H__
+#define __WOL_H__
+
+#include <net.h>
+
+/**********************************************************************/
+
+#define WOL_SYNC_BYTE 0xFF
+#define WOL_SYNC_COUNT 6
+#define WOL_MAC_REPETITIONS 16
+#define WOL_DEFAULT_TIMEOUT 5000
+#define WOL_PASSWORD_4B 4
+#define WOL_PASSWORD_6B 6
+
+/*
+ * Wake-on-LAN header
+ */
+struct wol_hdr {
+ u8 wol_sync[WOL_SYNC_COUNT]; /* sync bytes */
+ u8 wol_dest[WOL_MAC_REPETITIONS * ARP_HLEN]; /* 16x MAC */
+ u8 wol_passwd[0]; /* optional */
+};
+
+/*
+ * Initialize wol (beginning of netloop)
+ */
+void wol_start(void);
+
+/*
+ * Check incoming Wake-on-LAN packet for:
+ * - sync bytes
+ * - sixteen copies of the target MAC address
+ *
+ * Optionally store the four or six byte password in the environment
+ * variable "wolpassword"
+ *
+ * @param ip IP header in the packet
+ * @param len Packet length
+ */
+void wol_receive(struct ip_udp_hdr *ip, unsigned int len);
+
+/*
+ * Set the timeout for the reception of a Wake-on-LAN packet
+ *
+ * @param timeout in milliseconds
+ */
+void wol_set_timeout(ulong timeout);
+
+/**********************************************************************/
+
+#endif /* __WOL_H__ */
+#endif