avionic design with actual uboot and tooling

submodule of avionic design uboot bootloader and with included tools to
get you started , read readme.md and readme-tk1-loader.md
This commit is contained in:
2026-03-03 21:46:32 +02:00
parent fe3ba02c96
commit 68d74d3181
11967 changed files with 2221897 additions and 0 deletions

51
u-boot/net/Kconfig Normal file
View File

@@ -0,0 +1,51 @@
#
# Network configuration
#
menuconfig NET
bool "Networking support"
if NET
config NET_RANDOM_ETHADDR
bool "Random ethaddr if unset"
select LIB_RAND
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 NET_TFTP_VARS
bool "Control TFTP timeout and count through environment"
default y
help
If set, allows controlling the TFTP timeout through the
environment variable tftptimeout, and the TFTP maximum
timeout count through the variable tftptimeoutcountmax.
If unset, timeout and maximum are hard-defined as 1 second
and 10 timouts per TFTP transfer.
config BOOTP_PXE_CLIENTARCH
hex
default 0x16 if ARM64
default 0x15 if ARM
default 0 if X86
config BOOTP_VCI_STRING
string
default "U-Boot.armv7" if CPU_V7 || CPU_V7M
default "U-Boot.armv8" if ARM64
default "U-Boot.arm" if ARM
default "U-Boot"
config SPL_NET_VCI_STRING
string
endif # if NET

27
u-boot/net/Makefile Normal file
View File

@@ -0,0 +1,27 @@
#
# (C) Copyright 2000-2006
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
#
# SPDX-License-Identifier: GPL-2.0+
#
#ccflags-y += -DDEBUG
obj-y += checksum.o
obj-$(CONFIG_CMD_NET) += arp.o
obj-$(CONFIG_CMD_NET) += bootp.o
obj-$(CONFIG_CMD_CDP) += cdp.o
obj-$(CONFIG_CMD_DNS) += dns.o
ifdef CONFIG_DM_ETH
obj-$(CONFIG_CMD_NET) += eth-uclass.o
else
obj-$(CONFIG_CMD_NET) += eth_legacy.o
endif
obj-$(CONFIG_CMD_NET) += eth_common.o
obj-$(CONFIG_CMD_LINK_LOCAL) += link_local.o
obj-$(CONFIG_CMD_NET) += net.o
obj-$(CONFIG_CMD_NFS) += nfs.o
obj-$(CONFIG_CMD_PING) += ping.o
obj-$(CONFIG_CMD_RARP) += rarp.o
obj-$(CONFIG_CMD_SNTP) += sntp.o
obj-$(CONFIG_CMD_NET) += tftp.o

237
u-boot/net/arp.c Normal file
View File

@@ -0,0 +1,237 @@
/*
* 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
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.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;
static 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 (!net_arp_wait_packet_ip.s_addr)
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;
uchar *pkt;
int eth_hdr_size;
/*
* 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");
pkt = (uchar *)et;
eth_hdr_size = net_update_ether(et, et->et_src, PROT_ARP);
pkt += eth_hdr_size;
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
net_send_packet((uchar *)et, eth_hdr_size + ARP_HDR_SIZE);
return;
case ARPOP_REPLY: /* arp reply */
/* are we waiting for a reply */
if (!net_arp_wait_packet_ip.s_addr)
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);
setenv("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;
}
}

31
u-boot/net/arp.h Normal file
View File

@@ -0,0 +1,31 @@
/*
* 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
* SPDX-License-Identifier: GPL-2.0
*/
#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;
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__ */

1099
u-boot/net/bootp.c Normal file

File diff suppressed because it is too large Load Diff

93
u-boot/net/bootp.h Normal file
View File

@@ -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 */
};
#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__ */

363
u-boot/net/cdp.c Normal file
View File

@@ -0,0 +1,363 @@
/*
* 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
* SPDX-License-Identifier: GPL-2.0
*/
#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();
}

22
u-boot/net/cdp.h Normal file
View File

@@ -0,0 +1,22 @@
/*
* 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
* SPDX-License-Identifier: GPL-2.0
*/
#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

60
u-boot/net/checksum.c Normal file
View File

@@ -0,0 +1,60 @@
/*
* This file was originally taken from the FreeBSD project.
*
* Copyright (c) 2001 Charles Mott <cm@linktel.net>
* Copyright (c) 2008 coresystems GmbH
* All rights reserved.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <common.h>
#include <net.h>
unsigned compute_ip_checksum(const void *vptr, unsigned nbytes)
{
int sum, oddbyte;
const unsigned short *ptr = vptr;
sum = 0;
while (nbytes > 1) {
sum += *ptr++;
nbytes -= 2;
}
if (nbytes == 1) {
oddbyte = 0;
((u8 *)&oddbyte)[0] = *(u8 *)ptr;
((u8 *)&oddbyte)[1] = 0;
sum += oddbyte;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
sum = ~sum & 0xffff;
return sum;
}
unsigned add_ip_checksums(unsigned offset, unsigned sum, unsigned new)
{
unsigned long checksum;
sum = ~sum & 0xffff;
new = ~new & 0xffff;
if (offset & 1) {
/*
* byte-swap the sum if it came from an odd offset; since the
* computation is endian independant this works.
*/
new = ((new >> 8) & 0xff) | ((new << 8) & 0xff00);
}
checksum = sum + new;
if (checksum > 0xffff)
checksum -= 0xffff;
return (~checksum) & 0xffff;
}
int ip_checksum_ok(const void *addr, unsigned nbytes)
{
return !(compute_ip_checksum(addr, nbytes) & 0xfffe);
}

207
u-boot/net/dns.c Normal file
View File

@@ -0,0 +1,207 @@
/*
* 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 <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;
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)
setenv(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();
}

36
u-boot/net/dns.h Normal file
View File

@@ -0,0 +1,36 @@
/*
* (C) Masami Komiya <mkomiya@sonare.it> 2005
* Copyright 2009, Robin Getz <rgetz@blackfin.uclinux.org>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#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 */
};
void dns_start(void); /* Begin DNS */
#endif

551
u-boot/net/eth-uclass.c Normal file
View File

@@ -0,0 +1,551 @@
/*
* (C) Copyright 2001-2015
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
* Joe Hershberger, National Instruments
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <environment.h>
#include <net.h>
#include <dm/device-internal.h>
#include <dm/uclass-internal.h>
#include "eth_internal.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;
};
/**
* 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;
uclass_get(UCLASS_ETH, &uc);
assert(uc);
return uc->priv;
}
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 first device. 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->current)
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");
/* 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);
}
uclass_get(UCLASS_ETH, &uc);
uclass_foreach_dev(it, uc) {
/*
* We need the seq to be valid, so try to probe it.
* If the probe fails, the seq will not match since it will be
* -1 instead of what we are looking for.
* 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 && it->seq == seq))
return it;
}
return NULL;
}
unsigned char *eth_get_ethaddr(void)
{
struct eth_pdata *pdata;
if (eth_get_dev()) {
pdata = eth_get_dev()->platdata;
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 = current->uclass_priv;
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 = current->uclass_priv;
priv->state = ETH_STATE_PASSIVE;
}
int eth_get_dev_index(void)
{
if (eth_get_dev())
return eth_get_dev()->seq;
return -1;
}
static int eth_write_hwaddr(struct udevice *dev)
{
struct eth_pdata *pdata = dev->platdata;
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)) {
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, false, &dev);
if (!retval) {
struct eth_pdata *pdata = dev->platdata;
switch (op) {
case env_op_create:
case env_op_overwrite:
eth_parse_enetaddr(value, pdata->enetaddr);
break;
case env_op_delete:
memset(pdata->enetaddr, 0, 6);
}
}
return 0;
}
U_BOOT_ENV_CALLBACK(ethaddr, on_ethaddr);
int eth_init(void)
{
char *ethact = getenv("ethact");
char *ethrotate = getenv("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) {
printf("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 =
current->uclass_priv;
priv->state = ETH_STATE_ACTIVE;
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 || !device_active(current))
return;
eth_get_ops(current)->stop(current);
priv = current->uclass_priv;
priv->state = ETH_STATE_PASSIVE;
}
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 (!device_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);
}
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 (!device_active(current))
return -EINVAL;
/* Process up to 32 packets at one time */
flags = ETH_RECV_CHECK_DEVICE;
for (i = 0; i < 32; 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(UCLASS_ETH, &dev);
if (!dev) {
printf("No ethernet found.\n");
bootstage_error(BOOTSTAGE_ID_NET_ETH_START);
} else {
char *ethprime = getenv("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 (num_devices)
printf(", ");
printf("eth%d: %s", dev->seq, dev->name);
if (ethprime && dev == prime_dev)
printf(" [PRIME]");
eth_write_hwaddr(dev);
uclass_next_device(&dev);
num_devices++;
} while (dev);
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;
}
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 int eth_post_probe(struct udevice *dev)
{
struct eth_device_priv *priv = dev->uclass_priv;
struct eth_pdata *pdata = dev->platdata;
unsigned char env_enetaddr[6];
#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;
#ifdef CONFIG_MCAST_TFTP
if (ops->mcast)
ops->mcast += gd->reloc_off;
#endif
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;
/* 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_getenv_enetaddr_by_index("eth", dev->seq, env_enetaddr);
if (!is_zero_ethaddr(env_enetaddr)) {
if (!is_zero_ethaddr(pdata->enetaddr) &&
memcmp(pdata->enetaddr, env_enetaddr, 6)) {
printf("\nWarning: %s MAC addresses don't match:\n",
dev->name);
printf("Address in SROM is %pM\n",
pdata->enetaddr);
printf("Address in environment is %pM\n",
env_enetaddr);
}
/* Override the ROM MAC address */
memcpy(pdata->enetaddr, env_enetaddr, 6);
} else if (is_valid_ethaddr(pdata->enetaddr)) {
eth_setenv_enetaddr_by_index("eth", dev->seq, pdata->enetaddr);
printf("\nWarning: %s using MAC address from ROM\n",
dev->name);
} else if (is_zero_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, pdata->enetaddr);
#else
printf("\nError: %s address not set.\n",
dev->name);
return -EINVAL;
#endif
}
return 0;
}
static int eth_pre_remove(struct udevice *dev)
{
struct eth_pdata *pdata = dev->platdata;
eth_get_ops(dev)->stop(dev);
/* clear the MAC address */
memset(pdata->enetaddr, 0, 6);
return 0;
}
UCLASS_DRIVER(eth) = {
.name = "eth",
.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_alloc_size = sizeof(struct eth_uclass_priv),
.per_device_auto_alloc_size = sizeof(struct eth_device_priv),
.flags = DM_UC_FLAG_SEQ_ALIAS,
};

166
u-boot/net/eth_common.c Normal file
View File

@@ -0,0 +1,166 @@
/*
* (C) Copyright 2001-2015
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
* Joe Hershberger, National Instruments
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <miiphy.h>
#include <net.h>
#include "eth_internal.h"
void eth_parse_enetaddr(const char *addr, uchar *enetaddr)
{
char *end;
int i;
for (i = 0; i < 6; ++i) {
enetaddr[i] = addr ? simple_strtoul(addr, &end, 16) : 0;
if (addr)
addr = (*end) ? end + 1 : end;
}
}
int eth_getenv_enetaddr(const char *name, uchar *enetaddr)
{
eth_parse_enetaddr(getenv(name), enetaddr);
return is_valid_ethaddr(enetaddr);
}
int eth_setenv_enetaddr(const char *name, const uchar *enetaddr)
{
char buf[20];
sprintf(buf, "%pM", enetaddr);
return setenv(name, buf);
}
int eth_getenv_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_getenv_enetaddr(enetvar, enetaddr);
}
int eth_setenv_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_setenv_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 = getenv(enetvar);
return skip_state != NULL;
}
void eth_current_changed(void)
{
char *act = getenv("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 = getenv("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)
setenv("ethact", eth_get_name());
}
/*
* remove the variable completely if there is no active
* interface
*/
else if (act != NULL)
setenv("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 = getenv("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 = get_env_id();
if ((act == NULL) || (env_changed_id != env_id)) {
act = getenv("ethact");
env_changed_id = env_id;
}
if (act == NULL) {
char *ethprime = getenv("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";
}

40
u-boot/net/eth_internal.h Normal file
View File

@@ -0,0 +1,40 @@
/*
* (C) Copyright 2001-2015
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
* Joe Hershberger, National Instruments
*
* SPDX-License-Identifier: GPL-2.0+
*/
#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_setenv_enetaddr_by_index() - set the MAC address envrionment 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_setenv_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

439
u-boot/net/eth_legacy.c Normal file
View File

@@ -0,0 +1,439 @@
/*
* (C) Copyright 2001-2015
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
* Joe Hershberger, National Instruments
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <command.h>
#include <environment.h>
#include <net.h>
#include <phy.h>
#include <asm/errno.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(bd_t *bis)
{
return -1;
}
int cpu_eth_init(bd_t *bis) __attribute__((weak, alias("__def_eth_init")));
int board_eth_init(bd_t *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:
eth_parse_enetaddr(value, dev->enetaddr);
break;
case env_op_delete:
memset(dev->enetaddr, 0, 6);
}
}
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[6];
int ret = 0;
eth_getenv_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, 6)) {
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, 6);
} else if (is_valid_ethaddr(dev->enetaddr)) {
eth_setenv_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) {
puts("No ethernet found.\n");
bootstage_error(BOOTSTAGE_ID_NET_ETH_START);
} else {
struct eth_device *dev = eth_devices;
char *ethprime = getenv("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;
}
#ifdef CONFIG_MCAST_TFTP
/* 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[6];
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);
}
/* the 'way' for ethernet-CRC-32. Spliced in from Linux lib/crc32.c
* and this is the ethernet-crc method needed for TSEC -- and perhaps
* some other adapter -- hash tables
*/
#define CRCPOLY_LE 0xedb88320
u32 ether_crc(size_t len, unsigned char const *p)
{
int i;
u32 crc;
crc = ~0;
while (len--) {
crc ^= *p++;
for (i = 0; i < 8; i++)
crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
}
/* an reverse the bits, cuz of way they arrive -- last-first */
crc = (crc >> 16) | (crc << 16);
crc = (crc >> 8 & 0x00ff00ff) | (crc << 8 & 0xff00ff00);
crc = (crc >> 4 & 0x0f0f0f0f) | (crc << 4 & 0xf0f0f0f0);
crc = (crc >> 2 & 0x33333333) | (crc << 2 & 0xcccccccc);
crc = (crc >> 1 & 0x55555555) | (crc << 1 & 0xaaaaaaaa);
return crc;
}
#endif
int eth_init(void)
{
struct eth_device *old_current;
if (!eth_current) {
puts("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)
{
if (!eth_current)
return -ENODEV;
return eth_current->send(eth_current, packet, length);
}
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 */

346
u-boot/net/link_local.c Normal file
View File

@@ -0,0 +1,346 @@
/*
* 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 <net.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 = getenv_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 = 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();
}

24
u-boot/net/link_local.h Normal file
View File

@@ -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

1548
u-boot/net/net.c Normal file

File diff suppressed because it is too large Load Diff

43
u-boot/net/net_rand.h Normal file
View File

@@ -0,0 +1,43 @@
/*
* 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>
/*
* Return a seed for the PRNG derived from the eth0 MAC address.
*/
static inline unsigned int seed_mac(void)
{
unsigned char enetaddr[6];
unsigned int seed;
/* get our mac */
eth_getenv_enetaddr("ethaddr", enetaddr);
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)
{
srand(seed_mac());
}
#endif /* __NET_RAND_H__ */

789
u-boot/net/nfs.c Normal file
View File

@@ -0,0 +1,789 @@
/*
* 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. */
#include <common.h>
#include <command.h>
#include <net.h>
#include <malloc.h>
#include <mapmem.h>
#include "nfs.h"
#include "bootp.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]; /* file handle of directory */
static char filefh[NFS_FHSIZE]; /* file handle of kernel image */
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 default_filename[64];
static char *nfs_filename;
static char *nfs_path;
static char nfs_path_buff[2048];
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 (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)(load_addr+offset), len);
if (rc) {
flash_perror(rc);
return -1;
}
} else
#endif /* CONFIG_SYS_DIRECT_FLASH_NFS */
{
void *ptr = map_sysmem(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)
{
int hl;
int hostnamelen;
char hostname[256];
strcpy(hostname, "");
hostnamelen = strlen(hostname);
/* 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). */
hl = (hostnamelen + 3) & ~3;
/* Provide an AUTH_UNIX credential. */
*p++ = htonl(1); /* AUTH_UNIX */
*p++ = htonl(hl+20); /* auth length */
*p++ = htonl(0); /* stamp */
*p++ = htonl(hostnamelen); /* hostname string */
if (hostnamelen & 3)
*(p + hostnamelen / 4) = 0; /* add zero padding */
memcpy(p, hostname, hostnamelen);
p += hl / 4;
*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 pkt;
unsigned long id;
uint32_t *p;
int pktlen;
int sport;
id = ++rpc_id;
pkt.u.call.id = htonl(id);
pkt.u.call.type = htonl(MSG_CALL);
pkt.u.call.rpcvers = htonl(2); /* use RPC version 2 */
pkt.u.call.prog = htonl(rpc_prog);
pkt.u.call.vers = htonl(2); /* portmapper is version 2 */
pkt.u.call.proc = htonl(rpc_proc);
p = (uint32_t *)&(pkt.u.call.data);
if (datalen)
memcpy((char *)p, (char *)data, datalen*sizeof(uint32_t));
pktlen = (char *)p + datalen*sizeof(uint32_t) - (char *)&pkt;
memcpy((char *)net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE,
(char *)&pkt, 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);
memcpy(p, filefh, NFS_FHSIZE);
p += (NFS_FHSIZE / 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);
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);
}
/**************************************************************************
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);
memcpy(p, filefh, NFS_FHSIZE);
p += (NFS_FHSIZE / 4);
*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:
rpc_lookup_req(PROG_MOUNT, 1);
break;
case STATE_PRCLOOKUP_PROG_NFS_REQ:
rpc_lookup_req(PROG_NFS, 2);
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((unsigned char *)&rpc_pkt, 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((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;
fs_mounted = 1;
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((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)
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((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]) {
switch (ntohl(rpc_pkt.u.reply.astatus)) {
case 0: /* Not an error */
break;
case 2: /* Remote can't support NFS version */
printf("*** ERROR: NFS version not supported: Requested: V%d, accepted: min V%d - max V%d\n",
2,
ntohl(rpc_pkt.u.reply.data[0]),
ntohl(rpc_pkt.u.reply.data[1]));
break;
default: /* Unknown error on 'accept state' flag */
printf("*** ERROR: accept state error (%d)\n",
ntohl(rpc_pkt.u.reply.astatus));
break;
}
return -1;
}
memcpy(filefh, rpc_pkt.u.reply.data + 1, NFS_FHSIZE);
return 0;
}
static int nfs_readlink_reply(uchar *pkt, unsigned len)
{
struct rpc_t rpc_pkt;
int rlen;
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;
rlen = ntohl(rpc_pkt.u.reply.data[1]); /* new path length */
if (*((char *)&(rpc_pkt.u.reply.data[2])) != '/') {
int pathlen;
strcat(nfs_path, "/");
pathlen = strlen(nfs_path);
memcpy(nfs_path + pathlen, (uchar *)&(rpc_pkt.u.reply.data[2]),
rlen);
nfs_path[pathlen + rlen] = 0;
} else {
memcpy(nfs_path, (uchar *)&(rpc_pkt.u.reply.data[2]), rlen);
nfs_path[rlen] = 0;
}
return 0;
}
static int nfs_read_reply(uchar *pkt, unsigned len)
{
struct rpc_t rpc_pkt;
int rlen;
debug("%s\n", __func__);
memcpy((uchar *)&rpc_pkt, 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('#');
rlen = ntohl(rpc_pkt.u.reply.data[18]);
if (store_block((uchar *)pkt + sizeof(rpc_pkt.u.reply),
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 (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) {
puts("*** 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 {
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);
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;
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);
puts("*** ERROR: Fail allocate memory\n");
return;
}
if (net_boot_file_name[0] == '\0') {
sprintf(default_filename, "/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);
strcpy(nfs_path, default_filename);
printf("*** Warning: no boot file name; using '%s'\n",
nfs_path);
} else {
char *p = net_boot_file_name;
p = strchr(p, ':');
if (p != NULL) {
nfs_server_ip = string_to_ip(net_boot_file_name);
++p;
strcpy(nfs_path, p);
} else {
strcpy(nfs_path, net_boot_file_name);
}
}
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 = net_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\n"
"Loading: *\b", 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();
}

77
u-boot/net/nfs.h Normal file
View File

@@ -0,0 +1,77 @@
/*
* (C) Masami Komiya <mkomiya@sonare.it> 2004
*
* SPDX-License-Identifier: GPL-2.0+
*/
#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 NFS_FHSIZE 32
#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, the config file may want to use a
* bigger value. In any case, most NFS servers are optimized for a power of 2.
*/
#ifdef CONFIG_NFS_READ_SIZE
#define NFS_READ_SIZE CONFIG_NFS_READ_SIZE
#else
#define NFS_READ_SIZE 1024 /* biggest power of two that fits Ether frame */
#endif
#define NFS_MAXLINKDEPTH 16
struct rpc_t {
union {
uint8_t data[2048];
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[19];
} reply;
} u;
};
void nfs_start(void); /* Begin NFS */
/**********************************************************************/
#endif /* __NFS_H__ */

115
u-boot/net/ping.c Normal file
View File

@@ -0,0 +1,115 @@
/*
* 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
* SPDX-License-Identifier: GPL-2.0
*/
#include "ping.h"
#include "arp.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 ip_hdr *ip = (struct ip_hdr *)pkt;
struct icmp_hdr *icmp = (struct icmp_hdr *)(pkt + IP_HDR_SIZE);
net_set_ip_header(pkt, dest, net_ip);
ip->ip_len = htons(IP_ICMP_HDR_SIZE);
ip->ip_p = IPPROTO_ICMP;
ip->ip_sum = compute_ip_checksum(ip, IP_HDR_SIZE);
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;
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:
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);
net_send_packet((uchar *)et, eth_hdr_size + len);
return;
/* default:
return;*/
}
}

32
u-boot/net/ping.h Normal file
View File

@@ -0,0 +1,32 @@
/*
* 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
* SPDX-License-Identifier: GPL-2.0
*/
#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__ */

99
u-boot/net/rarp.c Normal file
View File

@@ -0,0 +1,99 @@
/*
* (C) Copyright 2000-2002
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <command.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);
}

29
u-boot/net/rarp.h Normal file
View File

@@ -0,0 +1,29 @@
/*
* (C) Copyright 2000
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#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

104
u-boot/net/sntp.c Normal file
View File

@@ -0,0 +1,104 @@
/*
* SNTP support driver
*
* Masami Komiya <mkomiya@sonare.it> 2005
*
*/
#include <common.h>
#include <command.h>
#include <dm.h>
#include <net.h>
#include <rtc.h>
#include "sntp.h"
#define SNTP_TIMEOUT 10000UL
static int sntp_our_port;
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)
{
#ifdef CONFIG_TIMESTAMP
struct sntp_pkt_t *rpktp = (struct sntp_pkt_t *)pkt;
struct rtc_time tm;
ulong seconds;
#endif
debug("%s\n", __func__);
if (dest != sntp_our_port)
return;
#ifdef CONFIG_TIMESTAMP
/*
* 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);
#if defined(CONFIG_CMD_DATE)
# 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);
# else
rtc_set(&tm);
# endif
#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);
#endif
net_set_state(NETLOOP_SUCCESS);
}
void sntp_start(void)
{
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();
}

58
u-boot/net/sntp.h Normal file
View File

@@ -0,0 +1,58 @@
/*
* (C) Masami Komiya <mkomiya@sonare.it> 2005
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef __SNTP_H__
#define __SNTP_H__
#define NTP_SERVICE_PORT 123
#define SNTP_PACKET_LEN 48
/* Leap Indicator */
#define NTP_LI_NOLEAP 0x0
#define NTP_LI_61SECS 0x1
#define NTP_LI_59SECS 0x2
#define NTP_LI_ALARM 0x3
/* Version */
#define NTP_VERSION 4
/* Mode */
#define NTP_MODE_RESERVED 0
#define NTP_MODE_SYMACTIVE 1 /* Symmetric Active */
#define NTP_MODE_SYMPASSIVE 2 /* Symmetric Passive */
#define NTP_MODE_CLIENT 3
#define NTP_MODE_SERVER 4
#define NTP_MODE_BROADCAST 5
#define NTP_MODE_NTPCTRL 6 /* Reserved for NTP control message */
#define NTP_MODE_PRIVATE 7 /* Reserved for private use */
struct sntp_pkt_t {
#if __LITTLE_ENDIAN
uchar mode:3;
uchar vn:3;
uchar li:2;
#else
uchar li:2;
uchar vn:3;
uchar mode:3;
#endif
uchar stratum;
uchar poll;
uchar precision;
uint root_delay;
uint root_dispersion;
uint reference_id;
unsigned long long reference_timestamp;
unsigned long long originate_timestamp;
unsigned long long receive_timestamp;
unsigned long long transmit_timestamp;
};
void sntp_start(void); /* Begin SNTP */
#endif /* __SNTP_H__ */

981
u-boot/net/tftp.c Normal file
View File

@@ -0,0 +1,981 @@
/*
* 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 <mapmem.h>
#include <net.h>
#include <net/tftp.h>
#include "bootp.h"
#ifdef CONFIG_SYS_DIRECT_FLASH_TFTP
#include <flash.h>
#endif
/* 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,
};
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;
#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
#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
/* 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)
*/
#ifdef CONFIG_TFTP_BLOCKSIZE
#define TFTP_MTU_BLOCKSIZE CONFIG_TFTP_BLOCKSIZE
#else
#define TFTP_MTU_BLOCKSIZE 1468
#endif
static unsigned short tftp_block_size = TFTP_BLOCK_SIZE;
static unsigned short tftp_block_size_option = TFTP_MTU_BLOCKSIZE;
#ifdef CONFIG_MCAST_TFTP
#include <malloc.h>
#define MTFTP_BITMAPSIZE 0x1000
static unsigned *tftp_mcast_bitmap;
static int tftp_mcast_prev_hole;
static int tftp_mcast_bitmap_size = MTFTP_BITMAPSIZE;
static int tftp_mcast_disabled;
static int tftp_mcast_master_client;
static int tftp_mcast_active;
static int tftp_mcast_port;
/* can get 'last' block before done..*/
static ulong tftp_mcast_ending_block;
static void parse_multicast_oack(char *pkt, int len);
static void mcast_cleanup(void)
{
if (net_mcast_addr)
eth_mcast_join(net_mcast_addr, 0);
if (tftp_mcast_bitmap)
free(tftp_mcast_bitmap);
tftp_mcast_bitmap = NULL;
net_mcast_addr.s_addr = 0;
tftp_mcast_active = 0;
tftp_mcast_port = 0;
tftp_mcast_ending_block = -1;
}
#endif /* CONFIG_MCAST_TFTP */
static inline void store_block(int block, uchar *src, unsigned len)
{
ulong offset = block * tftp_block_size + tftp_block_wrap_offset;
ulong newsize = offset + len;
#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 (load_addr + offset >= flash_info[i].start[0]) {
rc = 1;
break;
}
}
if (rc) { /* Flash is destination for this packet */
rc = flash_write((char *)src, (ulong)(load_addr+offset), len);
if (rc) {
flash_perror(rc);
net_set_state(NETLOOP_FAIL);
return;
}
} else
#endif /* CONFIG_SYS_DIRECT_FLASH_TFTP */
{
void *ptr = map_sysmem(load_addr + offset, len);
memcpy(ptr, src, len);
unmap_sysmem(ptr);
}
#ifdef CONFIG_MCAST_TFTP
if (tftp_mcast_active)
ext2_set_bit(block, tftp_mcast_bitmap);
#endif
if (net_boot_file_size < newsize)
net_boot_file_size = newsize;
}
/* 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 = ((int)block - 1) * len + tftp_block_wrap_offset;
ulong tosend = len;
tosend = min(net_boot_file_size - offset, tosend);
(void)memcpy(dst, (void *)(save_addr + offset), tosend);
debug("%s: block=%d, offset=%ld, len=%d, tosend=%ld\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)
{
#ifdef CONFIG_TFTP_TSIZE
if (tftp_tsize) {
ulong 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
{
if (((tftp_cur_block - 1) % 10) == 0)
putc('#');
else if ((tftp_cur_block % (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);
#ifdef CONFIG_MCAST_TFTP
mcast_cleanup();
#endif
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 */
} else {
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");
net_set_state(NETLOOP_SUCCESS);
}
static void tftp_send(void)
{
uchar *pkt;
uchar *xp;
int len = 0;
ushort *s;
#ifdef CONFIG_MCAST_TFTP
/* Multicast TFTP.. non-MasterClients do not ACK data. */
if (tftp_mcast_active && tftp_state == STATE_DATA &&
tftp_mcast_master_client == 0)
return;
#endif
/*
* 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);
#ifdef CONFIG_MCAST_TFTP
/* Check all preconditions before even trying the option */
if (!tftp_mcast_disabled) {
tftp_mcast_bitmap = malloc(tftp_mcast_bitmap_size);
if (tftp_mcast_bitmap && eth_get_dev()->mcast) {
free(tftp_mcast_bitmap);
tftp_mcast_bitmap = NULL;
pkt += sprintf((char *)pkt, "multicast%c%c",
0, 0);
}
}
#endif /* CONFIG_MCAST_TFTP */
len = pkt - xp;
break;
case STATE_OACK:
#ifdef CONFIG_MCAST_TFTP
/* My turn! Start at where I need blocks I missed. */
if (tftp_mcast_active)
tftp_cur_block = ext2_find_next_zero_bit(
tftp_mcast_bitmap,
tftp_mcast_bitmap_size * 8, 0);
/* fall through */
#endif
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;
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;
break;
}
net_send_udp_packet(net_server_ethaddr, tftp_remote_ip,
tftp_remote_port, tftp_our_port, len);
}
#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;
if (dest != tftp_our_port) {
#ifdef CONFIG_MCAST_TFTP
if (tftp_mcast_active &&
(!tftp_mcast_port || dest != tftp_mcast_port))
#endif
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_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: %s %s\n",
pkt, pkt + strlen((char *)pkt) + 1);
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 (strcmp((char *)pkt + i, "blksize") == 0) {
tftp_block_size = (unsigned short)
simple_strtoul((char *)pkt + i + 8,
NULL, 10);
debug("Blocksize ack: %s, %d\n",
(char *)pkt + i + 8, tftp_block_size);
}
#ifdef CONFIG_TFTP_TSIZE
if (strcmp((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
}
#ifdef CONFIG_MCAST_TFTP
parse_multicast_oack((char *)pkt, len - 1);
if ((tftp_mcast_active) && (!tftp_mcast_master_client))
tftp_state = STATE_DATA; /* passive.. */
else
#endif
#ifdef CONFIG_CMD_TFTPPUT
if (tftp_put_active) {
/* 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;
tftp_cur_block = ntohs(*(__be16 *)pkt);
update_block_number();
if (tftp_state == STATE_SEND_RRQ)
debug("Server did not acknowledge timeout option!\n");
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();
#ifdef CONFIG_MCAST_TFTP
if (tftp_mcast_active) { /* start!=1 common if mcast */
tftp_prev_block = tftp_cur_block - 1;
} else
#endif
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;
}
tftp_prev_block = tftp_cur_block;
timeout_count_max = tftp_timeout_count_max;
net_set_timeout_handler(timeout_ms, tftp_timeout_handler);
store_block(tftp_cur_block - 1, pkt + 2, len);
/*
* Acknowledge the block just received, which will prompt
* the remote for the next one.
*/
#ifdef CONFIG_MCAST_TFTP
/* if I am the MasterClient, actively calculate what my next
* needed block is; else I'm passive; not ACKING
*/
if (tftp_mcast_active) {
if (len < tftp_block_size) {
tftp_mcast_ending_block = tftp_cur_block;
} else if (tftp_mcast_master_client) {
tftp_mcast_prev_hole = ext2_find_next_zero_bit(
tftp_mcast_bitmap,
tftp_mcast_bitmap_size * 8,
tftp_mcast_prev_hole);
tftp_cur_block = tftp_mcast_prev_hole;
if (tftp_cur_block >
((tftp_mcast_bitmap_size * 8) - 1)) {
debug("tftpfile too big\n");
/* try to double it and retry */
tftp_mcast_bitmap_size <<= 1;
mcast_cleanup();
net_start_again();
return;
}
tftp_prev_block = tftp_cur_block;
}
}
#endif
tftp_send();
#ifdef CONFIG_MCAST_TFTP
if (tftp_mcast_active) {
if (tftp_mcast_master_client &&
(tftp_cur_block >= tftp_mcast_ending_block)) {
puts("\nMulticast tftp done\n");
mcast_cleanup();
net_set_state(NETLOOP_SUCCESS);
}
} else
#endif
if (len < tftp_block_size)
tftp_complete();
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");
#ifdef CONFIG_MCAST_TFTP
mcast_cleanup();
#endif
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();
}
}
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 = getenv("tftpblocksize");
if (ep != NULL)
tftp_block_size_option = simple_strtol(ep, NULL, 10);
ep = getenv("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 = getenv("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, timeout = %ld ms\n",
tftp_block_size_option, timeout_ms);
tftp_remote_ip = net_server_ip;
if (net_boot_file_name[0] == '\0') {
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, MAX_LEN);
tftp_filename[MAX_LEN - 1] = 0;
printf("*** Warning: no boot file name; using '%s'\n",
tftp_filename);
} else {
char *p = strchr(net_boot_file_name, ':');
if (p == NULL) {
strncpy(tftp_filename, net_boot_file_name, MAX_LEN);
tftp_filename[MAX_LEN - 1] = 0;
} else {
tftp_remote_ip = string_to_ip(net_boot_file_name);
strncpy(tftp_filename, p + 1, MAX_LEN);
tftp_filename[MAX_LEN - 1] = 0;
}
}
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", save_addr);
printf("Save size: 0x%lx\n", save_size);
net_boot_file_size = save_size;
puts("Saving: *\b");
tftp_state = STATE_SEND_WRQ;
new_transfer();
} else
#endif
{
printf("Load address: 0x%lx\n", load_addr);
puts("Loading: *\b");
tftp_state = STATE_SEND_RRQ;
efi_set_bootdev("Net", "", tftp_filename);
}
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 = getenv("tftpdstp");
if (ep != NULL)
tftp_remote_port = simple_strtol(ep, NULL, 10);
ep = getenv("tftpsrcp");
if (ep != NULL)
tftp_our_port = simple_strtol(ep, NULL, 10);
#endif
tftp_cur_block = 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_MCAST_TFTP
mcast_cleanup();
#endif
#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;
printf("Using %s device\n", eth_get_name());
printf("Listening for TFTP transfer on %pI4\n", &net_ip);
printf("Load address: 0x%lx\n", 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 */
#ifdef CONFIG_MCAST_TFTP
/*
* Credits: atftp project.
*/
/*
* Pick up BcastAddr, Port, and whether I am [now] the master-client.
* Frame:
* +-------+-----------+---+-------~~-------+---+
* | opc | multicast | 0 | addr, port, mc | 0 |
* +-------+-----------+---+-------~~-------+---+
* The multicast addr/port becomes what I listen to, and if 'mc' is '1' then
* I am the new master-client so must send ACKs to DataBlocks. If I am not
* master-client, I'm a passive client, gathering what DataBlocks I may and
* making note of which ones I got in my bitmask.
* In theory, I never go from master->passive..
* .. this comes in with pkt already pointing just past opc
*/
static void parse_multicast_oack(char *pkt, int len)
{
int i;
struct in_addr addr;
char *mc_adr;
char *port;
char *mc;
mc_adr = NULL;
port = NULL;
mc = NULL;
/* march along looking for 'multicast\0', which has to start at least
* 14 bytes back from the end.
*/
for (i = 0; i < len - 14; i++)
if (strcmp(pkt + i, "multicast") == 0)
break;
if (i >= (len - 14)) /* non-Multicast OACK, ign. */
return;
i += 10; /* strlen multicast */
mc_adr = pkt + i;
for (; i < len; i++) {
if (*(pkt + i) == ',') {
*(pkt + i) = '\0';
if (port) {
mc = pkt + i + 1;
break;
} else {
port = pkt + i + 1;
}
}
}
if (!port || !mc_adr || !mc)
return;
if (tftp_mcast_active && tftp_mcast_master_client) {
printf("I got a OACK as master Client, WRONG!\n");
return;
}
/* ..I now accept packets destined for this MCAST addr, port */
if (!tftp_mcast_active) {
if (tftp_mcast_bitmap) {
printf("Internal failure! no mcast.\n");
free(tftp_mcast_bitmap);
tftp_mcast_bitmap = NULL;
tftp_mcast_disabled = 1;
return;
}
/* I malloc instead of pre-declare; so that if the file ends
* up being too big for this bitmap I can retry
*/
tftp_mcast_bitmap = malloc(tftp_mcast_bitmap_size);
if (!tftp_mcast_bitmap) {
printf("No bitmap, no multicast. Sorry.\n");
tftp_mcast_disabled = 1;
return;
}
memset(tftp_mcast_bitmap, 0, tftp_mcast_bitmap_size);
tftp_mcast_prev_hole = 0;
tftp_mcast_active = 1;
}
addr = string_to_ip(mc_adr);
if (net_mcast_addr.s_addr != addr.s_addr) {
if (net_mcast_addr.s_addr)
eth_mcast_join(net_mcast_addr, 0);
net_mcast_addr = addr;
if (eth_mcast_join(net_mcast_addr, 1)) {
printf("Fail to set mcast, revert to TFTP\n");
tftp_mcast_disabled = 1;
mcast_cleanup();
net_start_again();
}
}
tftp_mcast_master_client = simple_strtoul((char *)mc, NULL, 10);
tftp_mcast_port = (unsigned short)simple_strtoul(port, NULL, 10);
printf("Multicast: %s:%d [%d]\n", mc_adr, tftp_mcast_port,
tftp_mcast_master_client);
return;
}
#endif /* Multicast TFTP */