diff options
author | 2023-10-10 14:33:42 +0000 | |
---|---|---|
committer | 2023-10-10 14:33:42 +0000 | |
commit | af1a266670d040d2f4083ff309d732d648afba2a (patch) | |
tree | 2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/u-boot/net | |
parent | e02cda008591317b1625707ff8e115a4841aa889 (diff) |
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/u-boot/net')
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", ðernet)) + 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", ®)) + 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 |