diff options
| author | Tom Rini <[email protected]> | 2026-08-13 08:58:29 -0600 |
|---|---|---|
| committer | Tom Rini <[email protected]> | 2026-08-13 08:58:29 -0600 |
| commit | c2ac5fc4e3bc91f59f059f115939d28fe35a7841 (patch) | |
| tree | b522dc46ea7ab571e7f9b550a596b20269189942 | |
| parent | 0b581a9612e46310ee52bf8359165950d38b2a2b (diff) | |
| parent | b752e4417900ae51d4d8fda879a5594db7025698 (diff) | |
Merge tag 'net-20260813' of https://git.u-boot-project.org/u-boot/custodians/u-boot-net
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
| -rw-r--r-- | cmd/Kconfig | 14 | ||||
| -rw-r--r-- | cmd/lwip/Makefile | 1 | ||||
| -rw-r--r-- | cmd/lwip/sntp.c | 1 | ||||
| -rw-r--r-- | cmd/lwip/tftpsrv.c | 11 | ||||
| -rw-r--r-- | cmd/lwip/wget.c | 3 | ||||
| -rw-r--r-- | cmd/net.c | 4 | ||||
| -rw-r--r-- | configs/qemu_arm64_lwip_defconfig | 1 | ||||
| -rw-r--r-- | doc/usage/cmd/tftpsrv.rst | 73 | ||||
| -rw-r--r-- | drivers/net/dm9000x.c | 6 | ||||
| -rw-r--r-- | drivers/net/dwc_eth_xgmac.c | 1 | ||||
| -rw-r--r-- | drivers/net/ftmac100.c | 6 | ||||
| -rw-r--r-- | drivers/net/ks8851_mll.c | 6 | ||||
| -rw-r--r-- | drivers/net/phy/dp83867.c | 18 | ||||
| -rw-r--r-- | drivers/net/phy/phy.c | 29 | ||||
| -rw-r--r-- | drivers/net/rtl8169.c | 103 | ||||
| -rw-r--r-- | drivers/net/smc911x.c | 6 | ||||
| -rw-r--r-- | include/net-lwip.h | 1 | ||||
| -rw-r--r-- | lib/lwip/u-boot/lwipopts.h | 11 | ||||
| -rw-r--r-- | net/lwip/dhcp.c | 2 | ||||
| -rw-r--r-- | net/lwip/net-lwip.c | 45 | ||||
| -rw-r--r-- | net/lwip/tftp.c | 188 | ||||
| -rw-r--r-- | net/net.c | 4 | ||||
| -rw-r--r-- | net/net_rand.h | 2 | ||||
| -rw-r--r-- | net/nfs-common.c | 12 | ||||
| -rw-r--r-- | test/dm/net_defrag.c | 36 | ||||
| -rw-r--r-- | test/py/tests/test_net.py | 94 |
26 files changed, 613 insertions, 65 deletions
diff --git a/cmd/Kconfig b/cmd/Kconfig index 35ce42f8397..ff90a87024c 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -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 diff --git a/cmd/lwip/Makefile b/cmd/lwip/Makefile index 90df1f5511c..245683a9672 100644 --- a/cmd/lwip/Makefile +++ b/cmd/lwip/Makefile @@ -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 diff --git a/cmd/lwip/sntp.c b/cmd/lwip/sntp.c index 5fa400b104a..584151ba7d1 100644 --- a/cmd/lwip/sntp.c +++ b/cmd/lwip/sntp.c @@ -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; } } diff --git a/cmd/lwip/tftpsrv.c b/cmd/lwip/tftpsrv.c new file mode 100644 index 00000000000..6370c900489 --- /dev/null +++ b/cmd/lwip/tftpsrv.c @@ -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."); diff --git a/cmd/lwip/wget.c b/cmd/lwip/wget.c index 4883ad61bce..531e886e986 100644 --- a/cmd/lwip/wget.c +++ b/cmd/lwip/wget.c @@ -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 diff --git a/cmd/net.c b/cmd/net.c index f6f556f36ae..0f0b2386d87 100644 --- a/cmd/net.c +++ b/cmd/net.c @@ -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." ); diff --git a/configs/qemu_arm64_lwip_defconfig b/configs/qemu_arm64_lwip_defconfig index a974970c3d3..06ed5a131db 100644 --- a/configs/qemu_arm64_lwip_defconfig +++ b/configs/qemu_arm64_lwip_defconfig @@ -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 diff --git a/doc/usage/cmd/tftpsrv.rst b/doc/usage/cmd/tftpsrv.rst new file mode 100644 index 00000000000..d067afdba57 --- /dev/null +++ b/doc/usage/cmd/tftpsrv.rst @@ -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. diff --git a/drivers/net/dm9000x.c b/drivers/net/dm9000x.c index 9e17f0b9c28..53225979b66 100644 --- a/drivers/net/dm9000x.c +++ b/drivers/net/dm9000x.c @@ -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), diff --git a/drivers/net/dwc_eth_xgmac.c b/drivers/net/dwc_eth_xgmac.c index 311b57011c3..4e664a88f68 100644 --- a/drivers/net/dwc_eth_xgmac.c +++ b/drivers/net/dwc_eth_xgmac.c @@ -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; } diff --git a/drivers/net/ftmac100.c b/drivers/net/ftmac100.c index fa0b3dbb6d1..d9826178e82 100644 --- a/drivers/net/ftmac100.c +++ b/drivers/net/ftmac100.c @@ -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, diff --git a/drivers/net/ks8851_mll.c b/drivers/net/ks8851_mll.c index 2c956154d09..c2f87d4222b 100644 --- a/drivers/net/ks8851_mll.c +++ b/drivers/net/ks8851_mll.c @@ -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, diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c index ebed61de133..34c871857e1 100644 --- a/drivers/net/phy/dp83867.c +++ b/drivers/net/phy/dp83867.c @@ -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, }; diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index d7e0c4fe02d..f2e9b2a9820 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -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 diff --git a/drivers/net/rtl8169.c b/drivers/net/rtl8169.c index e203faed26b..0208ff2d435 100644 --- a/drivers/net/rtl8169.c +++ b/drivers/net/rtl8169.c @@ -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; diff --git a/drivers/net/smc911x.c b/drivers/net/smc911x.c index f39ba40944f..2835eb45856 100644 --- a/drivers/net/smc911x.c +++ b/drivers/net/smc911x.c @@ -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, diff --git a/include/net-lwip.h b/include/net-lwip.h index 5d0627eb271..8e59a2299e0 100644 --- a/include/net-lwip.h +++ b/include/net-lwip.h @@ -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__ */ diff --git a/lib/lwip/u-boot/lwipopts.h b/lib/lwip/u-boot/lwipopts.h index 8dae004f1a2..1c1adc57c6b 100644 --- a/lib/lwip/u-boot/lwipopts.h +++ b/lib/lwip/u-boot/lwipopts.h @@ -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) diff --git a/net/lwip/dhcp.c b/net/lwip/dhcp.c index 18dc36ae7ca..a5e2e7d4da0 100644 --- a/net/lwip/dhcp.c +++ b/net/lwip/dhcp.c @@ -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) diff --git a/net/lwip/net-lwip.c b/net/lwip/net-lwip.c index cfe5a6a640d..8f8f9d69020 100644 --- a/net/lwip/net-lwip.c +++ b/net/lwip/net-lwip.c @@ -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; + } + + 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 ? pp : p->payload, p->len); - free(pp); + 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; diff --git a/net/lwip/tftp.c b/net/lwip/tftp.c index 571c38172f9..d6a9f29a260 100644 --- a/net/lwip/tftp.c +++ b/net/lwip/tftp.c @@ -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; diff --git a/net/net.c b/net/net.c index 61c5a6ef6c4..71666eb1113 100644 --- a/net/net.c +++ b/net/net.c @@ -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); diff --git a/net/net_rand.h b/net/net_rand.h index e21dff8569b..18ff7b68056 100644 --- a/net/net_rand.h +++ b/net/net_rand.h @@ -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; diff --git a/net/nfs-common.c b/net/nfs-common.c index 72d8fd823e3..637fcfd9bb8 100644 --- a/net/nfs-common.c +++ b/net/nfs-common.c @@ -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); diff --git a/test/dm/net_defrag.c b/test/dm/net_defrag.c index 3fd40de90cd..7501b252db9 100644 --- a/test/dm/net_defrag.c +++ b/test/dm/net_defrag.c @@ -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); diff --git a/test/py/tests/test_net.py b/test/py/tests/test_net.py index 27cdd73fd49..a2007c2fd3a 100644 --- a/test/py/tests/test_net.py +++ b/test/py/tests/test_net.py @@ -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 + + [email protected]("cmd_crc32") [email protected]("cmd_tftpsrv") [email protected]("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 |
