Pull request net-20260813.

net:
- phy: dp83867: enable extended read / write for driver
- phy: fix duplicate eth_phy binding
- Drop unnecessary device_set_name
- dwc_eth_xgmac: Return -ENODEV when phy_connect() fails
- nfs: clean up bounds checks in nfs_readlink_reply()
- rtl8169: add support for RTL8126A and RTL8127A
- srand_mac(): fix -ENODEV crash with CONFIG_DM_RNG

net-legacy:
- Fix out-of-bounds write in IP fragment reassembly
- test: net: add regression test for IP reassembly overflow

net-lwip:
- Add tftpsrv command
- Handle chained pbufs in transmit path
- sntp: fix netif leak when ntpserverip is unset
- wget: free mbedtls x509 cert context to avoid memory leak
- Fix DHCP fine timer interval
This commit is contained in:
Tom Rini
2026-08-13 08:58:29 -06:00
26 changed files with 613 additions and 65 deletions
+8 -6
View File
@@ -2135,12 +2135,6 @@ config CMD_TFTPPUT
help
TFTP put command, for uploading files to a server
config CMD_TFTPSRV
bool "tftpsrv"
depends on CMD_TFTPBOOT
help
Act as a TFTP server and boot the first received file
config NET_TFTP_VARS
bool "Control TFTP timeout and count through environment"
depends on CMD_TFTPBOOT
@@ -2287,6 +2281,14 @@ config CMD_TFTPBOOT
help
tftpboot - load file via network using TFTP protocol
config CMD_TFTPSRV
bool "tftpsrv"
depends on CMD_TFTPBOOT
help
Act as a TFTP server and receive the first incoming file into
memory. The command returns successfully after the transfer so
boot scripts can boot the received image from the load address.
config CMD_WGET
bool "wget"
default y if SANDBOX || ARCH_QEMU
+1
View File
@@ -4,4 +4,5 @@ obj-$(CONFIG_CMD_NFS) += nfs.o
obj-$(CONFIG_CMD_PING) += ping.o
obj-$(CONFIG_CMD_SNTP) += sntp.o
obj-$(CONFIG_CMD_TFTPBOOT) += tftp.o
obj-$(CONFIG_CMD_TFTPSRV) += tftpsrv.o
obj-$(CONFIG_CMD_WGET) += wget.o
+1
View File
@@ -71,6 +71,7 @@ static int sntp_loop(struct udevice *udev, ip_addr_t *srvip)
} else {
if (!ntp_server_known()) {
log_err("error: ntpserverip not set\n");
net_lwip_remove_netif(netif);
return -1;
}
}
+11
View File
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-2.0+
#include <command.h>
#include <net.h>
U_BOOT_CMD(tftpsrv, 2, 1, do_tftpsrv,
"act as a TFTP server and receive the first file",
"[loadAddress]\n"
"Listen for an incoming TFTP transfer and receive a file into memory.\n"
"The transfer is aborted if a transfer has not been started after\n"
"about 50 seconds or if Ctrl-C is pressed.");
+3
View File
@@ -67,12 +67,15 @@ static int _set_cacert(const void *addr, size_t sz)
if (ret) {
if (!wget_info->silent)
printf("Could not parse certificates (%d)\n", ret);
mbedtls_x509_crt_free(&crt);
free(cacert);
cacert = NULL;
cacert_size = 0;
return CMD_RET_FAILURE;
}
mbedtls_x509_crt_free(&crt);
#if CONFIG_IS_ENABLED(WGET_BUILTIN_CACERT)
cacert_initialized = true;
#endif
+2 -2
View File
@@ -89,9 +89,9 @@ static int do_tftpsrv(struct cmd_tbl *cmdtp, int flag, int argc,
U_BOOT_CMD(
tftpsrv, 2, 1, do_tftpsrv,
"act as a TFTP server and boot the first received file",
"act as a TFTP server and receive the first file",
"[loadAddress]\n"
"Listen for an incoming TFTP transfer, receive a file and boot it.\n"
"Listen for an incoming TFTP transfer and receive a file into memory.\n"
"The transfer is aborted if a transfer has not been started after\n"
"about 50 seconds or if Ctrl-C is pressed."
);
+1
View File
@@ -7,6 +7,7 @@ CONFIG_NET_LWIP=y
CONFIG_CMD_DNS=y
CONFIG_CMD_NFS=y
CONFIG_CMD_SNTP=y
CONFIG_CMD_TFTPSRV=y
CONFIG_CMD_WGET=y
CONFIG_EFI_HTTP_BOOT=y
CONFIG_WGET_HTTPS=y
+73
View File
@@ -0,0 +1,73 @@
.. SPDX-License-Identifier: GPL-2.0+
.. index::
single: tftpsrv (command)
tftpsrv command
===============
Synopsis
--------
::
tftpsrv [loadAddress]
Description
-----------
The tftpsrv command listens for an incoming TFTP write request and receives
the first transferred file into memory.
loadAddress
memory address where the received file is stored. If not provided, the
address is taken from the *loadaddr* environment variable or the default
image load address.
After a successful transfer, the *fileaddr* and *filesize* environment
variables describe the received file. The command returns successfully after
the transfer has completed. It does not boot the file automatically; boot
scripts can use commands such as bootm, booti or bootefi to boot from the
load address.
The transfer is aborted if no transfer has started after about 50 seconds or
if Ctrl-C is pressed.
Example
-------
In the example the following steps are executed:
* setup the board network address
* receive a FIT image from a host
* boot the received FIT image
::
=> setenv autoload no
=> dhcp
BOOTP broadcast 1
DHCP client bound to address 192.168.1.40 (7 ms)
=> tftpsrv $loadaddr
Using ethernet@1c30000 device
Listening for TFTP transfer on 192.168.1.40
Load address: 0x42000000
Loading: #################################################################
6.5 MiB/s
done
Bytes transferred = 1048576 (100000 hex)
=> bootm $fileaddr
On the host, send the file to the board while U-Boot is listening:
::
$ curl --upload-file image.fit tftp://192.168.1.40/image.fit
Configuration
-------------
The command is only available if CONFIG_CMD_TFTPSRV=y.
The command is supported by both the legacy network stack and the lwIP network
stack.
-6
View File
@@ -638,11 +638,6 @@ static int dm9000_read_rom_hwaddr(struct udevice *dev)
return !is_valid_ethaddr(pdata->enetaddr);
}
static int dm9000_bind(struct udevice *dev)
{
return device_set_name(dev, dev->name);
}
static int dm9000_of_to_plat(struct udevice *dev)
{
struct dm9000_priv *db = dev_get_priv(dev);
@@ -673,7 +668,6 @@ U_BOOT_DRIVER(dm9000) = {
.name = "eth_dm9000",
.id = UCLASS_ETH,
.of_match = dm9000_ids,
.bind = dm9000_bind,
.of_to_plat = dm9000_of_to_plat,
.ops = &dm9000_ops,
.priv_auto = sizeof(struct dm9000_priv),
+1
View File
@@ -507,6 +507,7 @@ static int xgmac_start(struct udevice *dev)
xgmac->config->interface(dev));
if (!xgmac->phy) {
pr_err("%s phy_connect() failed\n", dev->name);
ret = -ENODEV;
goto err_stop_resets;
}
-6
View File
@@ -429,11 +429,6 @@ static int ftmac100_remove(struct udevice *dev)
return 0;
}
static int ftmac100_bind(struct udevice *dev)
{
return device_set_name(dev, dev->name);
}
static const struct eth_ops ftmac100_ops = {
.start = ftmac100_start,
.send = ftmac100_send,
@@ -451,7 +446,6 @@ U_BOOT_DRIVER(ftmac100) = {
.name = "ftmac100",
.id = UCLASS_ETH,
.of_match = ftmac100_ids,
.bind = ftmac100_bind,
.of_to_plat = ftmac100_of_to_plat,
.probe = ftmac100_probe,
.remove = ftmac100_remove,
-6
View File
@@ -571,11 +571,6 @@ static int ks8851_read_rom_hwaddr(struct udevice *dev)
return !is_valid_ethaddr(pdata->enetaddr);
}
static int ks8851_bind(struct udevice *dev)
{
return device_set_name(dev, dev->name);
}
static int ks8851_probe(struct udevice *dev)
{
struct ks_net *ks = dev_get_priv(dev);
@@ -615,7 +610,6 @@ U_BOOT_DRIVER(ks8851) = {
.name = "eth_ks8851",
.id = UCLASS_ETH,
.of_match = ks8851_ids,
.bind = ks8851_bind,
.of_to_plat = ks8851_of_to_plat,
.probe = ks8851_probe,
.ops = &ks8851_ops,
+18
View File
@@ -126,6 +126,22 @@ struct dp83867_private {
bool sgmii_ref_clk_en;
};
static int dp83867_phy_extread(struct phy_device *phydev,
int addr, int devad, int reg)
{
if (devad != DP83867_DEVADDR)
return -EINVAL;
return phy_read_mmd(phydev, devad, addr);
};
static int dp83867_phy_extwrite(struct phy_device *phydev, int addr,
int devad, int reg, u16 val)
{
if (devad != DP83867_DEVADDR)
return -EINVAL;
return phy_write_mmd(phydev, devad, addr, (u32)val);
};
static int dp83867_config_port_mirroring(struct phy_device *phydev)
{
struct dp83867_private *dp83867 =
@@ -410,4 +426,6 @@ U_BOOT_PHY_DRIVER(dp83867) = {
.config = &dp83867_config,
.startup = &genphy_startup,
.shutdown = &genphy_shutdown,
.readext = dp83867_phy_extread,
.writeext = dp83867_phy_extwrite,
};
+28 -1
View File
@@ -20,6 +20,7 @@
#include <asm-generic/gpio.h>
#include <dm/device_compat.h>
#include <dm/of_extra.h>
#include <dm/uclass-internal.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/err.h>
@@ -921,6 +922,27 @@ static struct phy_device *phy_connect_fixed(struct mii_dev *bus,
}
#endif
#ifdef CONFIG_DM_ETH_PHY
static struct phy_device *phy_connect_dm_bound(struct mii_dev *bus,
struct udevice *dev,
uint mask)
{
struct udevice *dm_phy_dev;
struct phy_device *phydev;
if (!uclass_find_device_by_phandle(UCLASS_ETH_PHY, dev,
"phy-handle", &dm_phy_dev)) {
phydev = phy_find_by_mask(bus, mask);
if (phydev)
phydev->node = dev_ofnode(dm_phy_dev);
return phydev;
}
return NULL;
}
#endif
struct phy_device *phy_connect(struct mii_dev *bus, int addr,
struct udevice *dev,
phy_interface_t interface)
@@ -928,8 +950,13 @@ struct phy_device *phy_connect(struct mii_dev *bus, int addr,
struct phy_device *phydev = NULL;
uint mask = (addr >= 0) ? (1 << addr) : 0xffffffff;
#ifdef CONFIG_DM_ETH_PHY
phydev = phy_connect_dm_bound(bus, dev, mask);
#endif
#ifdef CONFIG_PHY_FIXED
phydev = phy_connect_fixed(bus, dev);
if (!phydev)
phydev = phy_connect_fixed(bus, dev);
#endif
#ifdef CONFIG_PHY_NCSI
+94 -9
View File
@@ -49,6 +49,7 @@
#include <asm/cache.h>
#include <asm/io.h>
#include <pci.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/printk.h>
@@ -152,8 +153,24 @@ enum RTL8125_registers {
IntrMask_8125 = 0x38,
IntrStatus_8125 = 0x3C,
TxPoll_8125 = 0x90,
RX_DESC_MODE = 0xd8,
MACOCP = 0xb0,
RADMFIFO_PROTECT = 0x0402,
TX_DESC_MODE = 0xeb58,
};
/* MAC OCP indirect access via the MACOCP register */
#define RTL8125_OCP_WRITE 0x80000000
#define RTL8125_OCP_ADDR_SHIFT 16
#define RX_DESC_CRC_DROP BIT(0)
#define RX_DESC_TYPE BIT(1)
/* TX_DESC_MODE (MAC OCP 0xeb58): TX descriptor format select, bits [1:0] */
#define TX_DESC_FMT_MASK GENMASK(1, 0)
#define TX_DESC_FMT_STD BIT(0)
#define TX_DESC_FMT_V3 BIT(1)
#define RX_PAUSE_SLOT_ON BIT(11)
enum RTL8169_register_content {
/*InterruptStatusBits */
SYSErr = 0x8000,
@@ -271,6 +288,8 @@ static struct {
{"RTL-8168h/8111h", 0x54, 0xff7e1880,},
{"RTL-8125B", 0x64, 0xff7e1880,},
{"RTL-8125d", 0x6a, 0xff7e5880,},
{"RTL-8126A", 0x66, 0xff7e5880,},
{"RTL-8127A", 0x6e, 0xff7e5880,},
};
enum _DescStatusBit {
@@ -355,6 +374,8 @@ static const unsigned int rtl8169_rx_config =
static struct pci_device_id supported[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8125) },
{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8126) },
{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8127) },
{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8161) },
{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8167) },
{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8168) },
@@ -398,6 +419,33 @@ int mdio_read(int RegAddr)
return value;
}
/* RTL8125/8126/8127 share the same register layout and quirks */
static bool rtl_is_8125(struct pci_child_plat *pplat)
{
return pplat->device == 0x8125 || pplat->device == 0x8126 ||
pplat->device == 0x8127;
}
static void rtl_mac_ocp_write(u16 reg_addr, u16 value)
{
u32 data32;
data32 = (u32)(reg_addr / 2) << RTL8125_OCP_ADDR_SHIFT;
data32 += value;
data32 |= RTL8125_OCP_WRITE;
RTL_W32(MACOCP, data32);
}
static u16 rtl_mac_ocp_read(u16 reg_addr)
{
u32 data32;
data32 = (u32)(reg_addr / 2) << RTL8125_OCP_ADDR_SHIFT;
RTL_W32(MACOCP, data32);
return (u16)RTL_R32(MACOCP);
}
static int rtl8169_init_board(unsigned long dev_iobase, const char *name)
{
int i;
@@ -571,12 +619,15 @@ static int rtl_recv_common(struct udevice *dev, unsigned long dev_iobase,
return length;
} else {
u32 IntrStatus = IntrStatus_8169;
u32 sts;
if (pplat->device == 0x8125)
IntrStatus = IntrStatus_8125;
ushort sts = RTL_R8(IntrStatus);
RTL_W8(IntrStatus, sts & ~(TxErr | RxErr | SYSErr));
if (rtl_is_8125(pplat)) {
sts = RTL_R32(IntrStatus_8125);
RTL_W32(IntrStatus_8125, sts & ~(TxErr | RxErr | SYSErr));
} else {
sts = RTL_R16(IntrStatus_8169);
RTL_W16(IntrStatus_8169, sts & ~(TxErr | RxErr | SYSErr));
}
udelay(100); /* wait */
}
tpc->cur_rx = cur_rx;
@@ -636,8 +687,8 @@ static int rtl_send_common(struct udevice *dev, unsigned long dev_iobase,
((len > ETH_ZLEN) ? len : ETH_ZLEN));
}
rtl_flush_tx_desc(&tpc->TxDescArray[entry]);
if (pplat->device == 0x8125)
RTL_W8(TxPoll_8125, 0x1); /* set polling bit */
if (rtl_is_8125(pplat))
RTL_W32(TxPoll_8125, 0x1); /* set polling bit */
else
RTL_W8(TxPoll_8169, 0x40); /* set polling bit */
@@ -697,6 +748,8 @@ static void rtl8169_set_rx_mode(void)
static void rtl8169_hw_start(struct udevice *dev)
{
u8 version = rtl_chip_info[tpc->chipset].version;
u16 tx_desc_mode;
u32 i;
#ifdef DEBUG_RTL8169
@@ -719,6 +772,36 @@ static void rtl8169_hw_start(struct udevice *dev)
RTL_W8(Cfg9346, Cfg9346_Unlock);
/*
* RTL8125D/8126A/8127A require explicit descriptor-type and CRC-drop setup.
* RTL8125B (0x64) is intentionally excluded to avoid regressing working hardware.
*/
switch (version) {
case 0x6a:
case 0x66:
case 0x6e:
/* Reg 0xD8: Disable CRC drop and force legacy 16-byte RX desc */
RTL_W8(RX_DESC_MODE, RTL_R8(RX_DESC_MODE) & ~(RX_DESC_CRC_DROP | RX_DESC_TYPE));
/* RxConfig 0x44: enable RX pause slot (RTL8125B and later) */
RTL_W32(RxConfig, RTL_R32(RxConfig) | RX_PAUSE_SLOT_ON);
/* MAC OCP 0xEB58: TX descriptor format setup */
tx_desc_mode = rtl_mac_ocp_read(TX_DESC_MODE) & ~TX_DESC_FMT_MASK;
if (version == 0x6e) {
/* RTL8127A specific: Tx desc V3 and RADM FIFO protection */
rtl_mac_ocp_write(TX_DESC_MODE, tx_desc_mode | TX_DESC_FMT_V3);
RTL_W16(RADMFIFO_PROTECT, 0x2001);
} else {
/* RTL8125D / RTL8126A specific: standard Tx desc */
rtl_mac_ocp_write(TX_DESC_MODE, tx_desc_mode | TX_DESC_FMT_STD);
}
break;
default:
break;
}
/* RTL-8169sb/8110sb or previous version */
if (tpc->chipset <= 5)
RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb);
@@ -860,8 +943,8 @@ static void rtl_halt_common(struct udevice *dev)
RTL_W8(ChipCmd, 0x00);
/* Disable interrupts by clearing the interrupt mask. */
if (pplat->device == 0x8125)
RTL_W16(IntrMask_8125, 0x0000);
if (rtl_is_8125(pplat))
RTL_W32(IntrMask_8125, 0x00000000);
else
RTL_W16(IntrMask_8169, 0x0000);
@@ -1052,6 +1135,8 @@ static int rtl8169_eth_probe(struct udevice *dev)
switch (pplat->device) {
case 0x8125:
case 0x8126:
case 0x8127:
case 0x8161:
case 0x8168:
region = 2;
-6
View File
@@ -431,11 +431,6 @@ static int smc911x_read_rom_hwaddr(struct udevice *dev)
return 0;
}
static int smc911x_bind(struct udevice *dev)
{
return device_set_name(dev, dev->name);
}
static int smc911x_probe(struct udevice *dev)
{
struct smc911x_priv *priv = dev_get_priv(dev);
@@ -487,7 +482,6 @@ U_BOOT_DRIVER(smc911x) = {
.name = "eth_smc911x",
.id = UCLASS_ETH,
.of_match = smc911x_ids,
.bind = smc911x_bind,
.of_to_plat = smc911x_of_to_plat,
.probe = smc911x_probe,
.ops = &smc911x_ops,
+1
View File
@@ -53,6 +53,7 @@ bool wget_validate_uri(char *uri);
int do_dns(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
int do_nfs(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
int do_tftpsrv(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
int do_wget(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]);
#endif /* __NET_LWIP_H__ */
+11
View File
@@ -72,6 +72,17 @@
#define PBUF_POOL_RESERVE 4
#define TFTP_BLOCKSIZE_THRESHOLD 4096
/*
* Match the legacy U-Boot TFTP filename buffer. The legacy
* CONFIG_TFTP_FILE_NAME_MAX_LEN value is a buffer size including the trailing
* NUL, while lwIP's TFTP_MAX_FILENAME_LEN is the usable string length.
*/
#ifdef CONFIG_TFTP_FILE_NAME_MAX_LEN
#define TFTP_MAX_FILENAME_LEN (CONFIG_TFTP_FILE_NAME_MAX_LEN - 1)
#else
#define TFTP_MAX_FILENAME_LEN 127
#endif
#if defined(CONFIG_TFTP_BLOCKSIZE) && (CONFIG_TFTP_BLOCKSIZE > TFTP_BLOCKSIZE_THRESHOLD)
#define PBUF_POOL_SIZE (((CONFIG_TFTP_BLOCKSIZE + (IP_FRAG_MTU_USABLE - 1)) / \
IP_FRAG_MTU_USABLE) + PBUF_POOL_HEADROOM)
+1 -1
View File
@@ -25,7 +25,7 @@ static char boot_file_name[DHCP_BOOT_FILE_LEN];
static void call_lwip_dhcp_fine_tmr(void *ctx)
{
dhcp_fine_tmr();
sys_timeout(10, call_lwip_dhcp_fine_tmr, NULL);
sys_timeout(DHCP_FINE_TIMER_MSECS, call_lwip_dhcp_fine_tmr, NULL);
}
static int dhcp_loop(struct udevice *udev)
+32 -13
View File
@@ -40,28 +40,47 @@ char *pxelinux_configfile;
static err_t net_lwip_tx(struct netif *netif, struct pbuf *p)
{
struct udevice *udev = netif->state;
void *pp = NULL;
bool pp_allocated = false;
u32 plen;
void *pp;
int err;
if (CONFIG_IS_ENABLED(LWIP_DEBUG_RXTX)) {
printf("net_lwip_tx: %u bytes, udev %s\n", p->len, udev->name);
print_hex_dump("net_lwip_tx: ", 0, 16, 1, p->payload, p->len,
true);
}
if ((unsigned long)p->payload % PKTALIGN) {
if ((unsigned long)p->payload % PKTALIGN || p->len != p->tot_len) {
/*
* Some net drivers have strict alignment requirements and may
* fail or output invalid data if the packet is not aligned.
*
* A packet may also be stored in multiple chained pbufs. In
* this case, assemble the fragments into one contiguous packet
* buffer before passing it to the Ethernet driver.
*/
pp = memalign(PKTALIGN, p->len);
pp = memalign(PKTALIGN, p->tot_len);
if (!pp)
return ERR_ABRT;
memcpy(pp, p->payload, p->len);
return ERR_MEM;
pp_allocated = true;
plen = pbuf_copy_partial(p, pp, p->tot_len, 0);
if (plen != p->tot_len) {
free(pp);
return ERR_BUF;
}
} else {
pp = p->payload;
plen = p->len;
}
err = eth_get_ops(udev)->send(udev, pp ? pp : p->payload, p->len);
free(pp);
if (CONFIG_IS_ENABLED(LWIP_DEBUG_RXTX)) {
printf("net_lwip_tx: %u bytes, udev %s\n", plen, udev->name);
print_hex_dump("net_lwip_tx: ", 0, 16, 1, pp, plen, true);
}
err = eth_get_ops(udev)->send(udev, pp, plen);
if (pp_allocated)
free(pp);
if (err) {
debug("send error %d\n", err);
return ERR_ABRT;
+183 -5
View File
@@ -11,6 +11,7 @@
#include <linux/delay.h>
#include <linux/kconfig.h>
#include <lwip/apps/tftp_client.h>
#include <lwip/apps/tftp_server.h>
#include <lwip/timeouts.h>
#include <mapmem.h>
#include <net.h>
@@ -19,6 +20,8 @@
#define PROGRESS_PRINT_STEP_BYTES (10 * 1024)
/* Max time to wait for first data packet from server */
#define NO_RSP_TIMEOUT_MS 10000
/* Max time to wait for an incoming TFTP write request */
#define TFTPSRV_LISTEN_TIMEOUT_MS 50000
enum done_state {
NOT_DONE = 0,
@@ -34,8 +37,31 @@ struct tftp_ctx {
ulong hash_count;
ulong start_time;
enum done_state done;
bool is_server;
bool wrq_accepted;
char fname[TFTP_MAX_FILENAME_LEN + 1];
};
/*
* The lwIP TFTP server open callback has no user-data argument. Keep the
* current server context here so tftp_open() can return it.
*/
static struct tftp_ctx *tftpsrv_active_ctx;
static void transfer_timeout(void *arg)
{
struct tftp_ctx *ctx = (struct tftp_ctx *)arg;
printf("Timeout!\n");
ctx->done = FAILURE;
}
static void restart_transfer_timeout(struct tftp_ctx *ctx)
{
sys_untimeout(transfer_timeout, ctx);
sys_timeout(TFTP_TIMEOUT_MSECS, transfer_timeout, ctx);
}
/**
* store_block() - copy received data
*
@@ -71,7 +97,7 @@ static int store_block(struct tftp_ctx *ctx, void *src, u16_t len)
ctx->size += len;
ctx->block_count++;
tftp_tsize = tftp_client_get_tsize();
tftp_tsize = ctx->is_server ? 0 : tftp_client_get_tsize();
if (tftp_tsize) {
pos = clamp(ctx->size, 0UL, tftp_tsize);
@@ -92,7 +118,20 @@ static int store_block(struct tftp_ctx *ctx, void *src, u16_t len)
static void *tftp_open(const char *fname, const char *mode, u8_t is_write)
{
return NULL;
struct tftp_ctx *ctx = tftpsrv_active_ctx;
if (!IS_ENABLED(CONFIG_CMD_TFTPSRV) || !ctx || !is_write)
return NULL;
ctx->wrq_accepted = true;
ctx->start_time = get_timer(0);
snprintf(ctx->fname, sizeof(ctx->fname), "%s", fname);
restart_transfer_timeout(ctx);
printf("\nReceiving '%s' mode '%s'\n", fname, mode);
puts("Loading: ");
return ctx;
}
static void tftp_close(void *handle)
@@ -101,13 +140,15 @@ static void tftp_close(void *handle)
ulong tftp_tsize;
ulong elapsed;
sys_untimeout(transfer_timeout, ctx);
if (ctx->done == FAILURE || ctx->done == ABORTED) {
/* Closing after an error or Ctrl-C */
return;
}
ctx->done = SUCCESS;
tftp_tsize = tftp_client_get_tsize();
tftp_tsize = ctx->is_server ? 0 : tftp_client_get_tsize();
if (tftp_tsize) {
/* Print hash marks for the last packet received */
while (ctx->hash_count < 49) {
@@ -142,9 +183,14 @@ static int tftp_write(void *handle, struct pbuf *p)
struct tftp_ctx *ctx = handle;
struct pbuf *q;
for (q = p; q; q = q->next)
if (store_block(ctx, q->payload, q->len) < 0)
for (q = p; q; q = q->next) {
if (store_block(ctx, q->payload, q->len) < 0) {
ctx->done = FAILURE;
return -1;
}
}
restart_transfer_timeout(ctx);
return 0;
}
@@ -204,6 +250,9 @@ static int tftp_loop(struct udevice *udev, ulong addr, char *fname,
ctx.block_count = 0;
ctx.hash_count = 0;
ctx.daddr = addr;
ctx.is_server = false;
ctx.wrq_accepted = false;
ctx.fname[0] = '\0';
printf("Using %s device\n", udev->name);
printf("TFTP from server %s; our IP address is %s\n",
@@ -258,6 +307,135 @@ static int tftp_loop(struct udevice *udev, ulong addr, char *fname,
return -1;
}
static void no_request(void *arg)
{
struct tftp_ctx *ctx = (struct tftp_ctx *)arg;
if (ctx->wrq_accepted)
return;
printf("Timeout!\n");
ctx->done = FAILURE;
}
static int tftpsrv_loop(struct udevice *udev, ulong addr)
{
struct netif *netif;
struct tftp_ctx ctx;
const char *ipaddr;
int ret = -1;
err_t err;
if (addr == 0)
return -1;
ipaddr = env_get("ipaddr");
if (!ipaddr || !*ipaddr) {
log_err("error: ipaddr has to be set\n");
return -1;
}
netif = net_lwip_new_netif(udev);
if (!netif)
return -1;
memset(&ctx, 0, sizeof(ctx));
ctx.done = NOT_DONE;
ctx.daddr = addr;
ctx.is_server = true;
printf("Using %s device\n", udev->name);
printf("Listening for TFTP transfer on %s\n", ipaddr);
printf("Load address: 0x%lx\n", ctx.daddr);
tftpsrv_active_ctx = &ctx;
err = tftp_init_server(&tftp_context);
if (err != ERR_OK) {
log_err("tftp_init_server err: %d\n", err);
goto out_remove_netif;
}
ctx.start_time = get_timer(0);
sys_timeout(TFTPSRV_LISTEN_TIMEOUT_MS, no_request, &ctx);
while (!ctx.done) {
net_lwip_rx(udev, netif);
if (ctrlc()) {
printf("\nAbort\n");
ctx.done = ABORTED;
break;
}
}
sys_untimeout(no_request, &ctx);
sys_untimeout(transfer_timeout, &ctx);
tftp_cleanup();
if (ctx.done == SUCCESS) {
if (env_set_hex("fileaddr", addr)) {
log_err("fileaddr not updated\n");
goto out_remove_netif;
}
efi_set_bootdev("Net", "", ctx.fname, map_sysmem(addr, 0),
ctx.size);
ret = 0;
}
out_remove_netif:
tftpsrv_active_ctx = NULL;
net_lwip_remove_netif(netif);
return ret;
}
int do_tftpsrv(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
{
int ret = CMD_RET_SUCCESS;
char *end;
ulong laddr;
ulong addr;
if (!IS_ENABLED(CONFIG_CMD_TFTPSRV))
return CMD_RET_FAILURE;
laddr = env_get_ulong("loadaddr", 16, image_load_addr);
switch (argc) {
case 1:
break;
case 2:
addr = hextoul(argv[1], &end);
if (end == argv[1] || *end) {
ret = CMD_RET_USAGE;
goto out;
}
laddr = addr;
break;
default:
ret = CMD_RET_USAGE;
goto out;
}
if (!laddr) {
log_err("error: no load address\n");
ret = CMD_RET_FAILURE;
goto out;
}
if (net_lwip_eth_start() < 0) {
ret = CMD_RET_FAILURE;
goto out;
}
if (tftpsrv_loop(eth_get_dev(), laddr) < 0)
ret = CMD_RET_FAILURE;
else
image_load_addr = laddr;
net_lwip_eth_stop();
out:
return ret;
}
int do_tftpb(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
{
int ret = CMD_RET_SUCCESS;
+4
View File
@@ -1076,6 +1076,8 @@ static struct ip_udp_hdr *__net_defragment(struct ip_udp_hdr *ip, int *lenp)
} else if (h >= thisfrag) {
/* overlaps with initial part of the hole: move this hole */
newh = thisfrag + (len / 8);
if ((uchar *)(newh + 1) > pkt_buff + IP_PKTSIZE)
return NULL; /* hole descriptor would overflow pkt_buff */
*newh = *h;
h = newh;
if (h->next_hole)
@@ -1088,6 +1090,8 @@ static struct ip_udp_hdr *__net_defragment(struct ip_udp_hdr *ip, int *lenp)
} else {
/* fragment sits in the middle: split the hole */
newh = thisfrag + (len / 8);
if ((uchar *)(newh + 1) > pkt_buff + IP_PKTSIZE)
return NULL; /* hole descriptor would overflow pkt_buff */
*newh = *h;
h->last_byte = start;
h->next_hole = (newh - payload);
+1 -1
View File
@@ -44,7 +44,7 @@ static inline void srand_mac(void)
if (CONFIG_IS_ENABLED(DM_RNG)) {
ret = uclass_get_device(UCLASS_RNG, 0, &devp);
if (ret) {
if (!ret) {
ret = dm_rng_read(devp, &randv, sizeof(randv));
if (ret < 0)
randv = 0;
+9 -3
View File
@@ -671,18 +671,24 @@ static int nfs_readlink_reply(uchar *pkt, unsigned int len)
if (*((char *)&rpc_pkt.u.reply.data[2 + nfsv3_data_offset]) != '/') {
int pathlen;
int new_len;
strcat(nfs_path, "/");
pathlen = strlen(nfs_path);
if (pathlen + rlen >= sizeof(nfs_path_buff))
new_len = pathlen + rlen;
if (new_len >= sizeof(nfs_path_buff)) {
printf("NFS: symlink too long (%d bytes)\n", new_len);
return -NFS_RPC_DROP;
}
memcpy(nfs_path + pathlen,
(uchar *)&rpc_pkt.u.reply.data[2 + nfsv3_data_offset],
rlen);
nfs_path[pathlen + rlen] = 0;
nfs_path[new_len] = 0;
} else {
if (rlen >= sizeof(nfs_path_buff))
if (rlen >= sizeof(nfs_path_buff)) {
printf("NFS: symlink too long (%d bytes)\n", rlen);
return -NFS_RPC_DROP;
}
memcpy(nfs_path,
(uchar *)&rpc_pkt.u.reply.data[2 + nfsv3_data_offset],
rlen);
+36
View File
@@ -80,3 +80,39 @@ static int dm_test_net_ip_defrag_dup_last(struct unit_test_state *uts)
}
DM_TEST(dm_test_net_ip_defrag_dup_last, 0);
/*
* A fragment placed at the very top of the reassembly buffer takes the
* split-hole branch, which writes an 8-byte "struct hole" at
* pkt_buff + IP_HDR_SIZE + (offset8 + len / 8) * 8. With start + len equal to
* IP_MAXUDP that write reaches the end of pkt_buff and spills past it. pkt_buff
* is a static array, so this is flagged under AddressSanitizer; the fix rejects
* such a fragment instead. The datagram is incomplete, so nothing is delivered
* either way.
*/
static int dm_test_net_ip_defrag_oob(struct unit_test_state *uts)
{
rxhand_f *saved_handler = net_get_udp_handler();
uchar frame[FRAME_LEN];
struct ip_udp_hdr *ip = (struct ip_udp_hdr *)(frame + ETHER_HDR_SIZE);
u16 payload[4] = { 0, 0, 0, 0 };
/* Offset (8-byte units) so that start + FRAG_LEN == IP_MAXUDP. */
u16 off8 = (CONFIG_NET_MAXDEFRAG - IP_HDR_SIZE - FRAG_LEN) / 8;
udp_rx_count = 0;
net_set_udp_handler(defrag_udp_handler);
build_frag(frame, IP_FLAGS_MFRAG | off8, payload);
/* A distinct id forces a fresh reassembly independent of earlier tests. */
ip->ip_id = htons(0x7abc);
ip->ip_sum = 0;
ip->ip_sum = compute_ip_checksum(ip, IP_HDR_SIZE);
net_process_received_packet(frame, FRAME_LEN);
ut_asserteq(0, udp_rx_count);
net_set_udp_handler(saved_handler);
return 0;
}
DM_TEST(dm_test_net_ip_defrag_oob, 0);
+94
View File
@@ -59,6 +59,19 @@ For example:
'fnu': 'ubtest-upload.bin',
}
# Details regarding a file that may be written to U-Boot using the tftpsrv
# command. This variable may be omitted or set to None if tftpsrv testing
# is not possible or desired. The test uses host-side curl TFTP support to
# upload a generated file to U-Boot. The optional tftpsrv_url entry may be
# used when the host must use a forwarded address instead of U-Boot's
# ipaddr value.
env__net_tftpsrv_file = {
'fn': 'ubtest-tftpsrv.bin',
'addr': 0x10000000,
'size': 4096,
'timeout': 50000,
}
# Details regarding a file that may be read from a NFS server. This variable
# may be omitted or set to None if NFS testing is not possible or desired.
env__net_nfs_readable_file = {
@@ -89,6 +102,8 @@ import utils
import uuid
import datetime
import re
import tempfile
import zlib
net_set_up = False
net6_set_up = False
@@ -460,3 +475,82 @@ def test_net_tftpput(ubman):
output = ubman.run_command("crc32 $fileaddr $filesize")
assert expected_tftpb_crc in output
@pytest.mark.buildconfigspec("cmd_crc32")
@pytest.mark.buildconfigspec("cmd_tftpsrv")
@pytest.mark.requiredtool("curl")
def test_net_tftpsrv(ubman):
"""Test the tftpsrv command.
A file is generated on the host, uploaded to U-Boot using TFTP and then
validated in U-Boot using its size and CRC32.
The details of the file to upload are provided by the boardenv_* file;
see the comment at the beginning of this file.
"""
if not net_set_up:
pytest.skip("Network not initialized")
f = ubman.config.env.get("env__net_tftpsrv_file", None)
if not f:
pytest.skip("No tftpsrv file to write")
curl_version = utils.run_and_log(ubman, ["curl", "--version"])
if "tftp" not in curl_version.split():
pytest.skip("curl does not support TFTP")
addr = f.get("addr", None)
if not addr:
addr = utils.find_ram_base(ubman)
timeout = f.get("timeout", ubman.p.timeout)
timeout_secs = max(1, (timeout + 999) // 1000)
size = f.get("size", 4096)
fn = f.get("fn", "ubtest-tftpsrv.bin")
url = f.get("tftpsrv_url", None)
data = bytes([i % 251 for i in range(size)])
crc = "%08x" % (zlib.crc32(data) & 0xffffffff)
ip = ubman.run_command("echo $ipaddr").strip()
if not ip:
pytest.skip("No U-Boot IP address")
if not url:
url = "tftp://%s/%s" % (ip, fn)
with tempfile.NamedTemporaryFile() as tmp:
tmp.write(data)
tmp.flush()
done = False
with ubman.temporary_timeout(timeout):
try:
ubman.run_command("tftpsrv %x" % addr,
wait_for_prompt=False)
ubman.wait_for("Listening for TFTP transfer")
utils.run_and_log(
ubman,
[
"curl",
"--fail",
"--max-time",
str(timeout_secs),
"--upload-file",
tmp.name,
url,
],
)
ubman.wait_for("Bytes transferred = %d" % size)
ubman.wait_for(ubman.prompt)
done = True
finally:
if not done:
ubman.ctrlc()
ubman.drain_console()
output = ubman.run_command("echo $filesize")
assert "%x" % size in output
output = ubman.run_command("crc32 $fileaddr $filesize")
assert crc in output