diff options
author | Tom Rini <trini@konsulko.com> | 2024-10-16 08:12:28 -0600 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2024-10-16 11:11:57 -0600 |
commit | 608a31bdec6284ad6f821226e4c62c9cd3052874 (patch) | |
tree | c43e6664c7530f354f73c05cb817e37d10d93615 /net/lwip/tftp.c | |
parent | 1ca0ddb643f3b152439f7bb8f6ee3f5826d7102d (diff) | |
parent | 4820cb4b16c44ec332b18dbe7207ddd8c0a1d446 (diff) |
Merge patch series "Introduce the lwIP network stack"
Jerome Forissier <jerome.forissier@linaro.org> says:
This is a rework of a patch series by Maxim Uvarov: "net/lwip: add lwip
library for the network stack" [1]. The goal is to introduce the lwIP TCP/IP
stack [2] [3] as an alternative to the current implementation in net/,
selectable with Kconfig, and ultimately keep only lwIP if possible. Some
reasons for doing so are:
- Make the support of HTTPS in the wget command easier. Javier T. and
Raymond M. (CC'd) have some additional lwIP and Mbed TLS patches to do
so. With that it becomes possible to fetch and launch a distro installer
such as Debian etc. using a secure, authenticated connection directly
from the U-Boot shell. Several use cases:
* Authentication: prevent MITM attack (third party replacing the
binary with a different one)
* Confidentiality: prevent third parties from grabbing a copy of the
image as it is being downloaded
* Allow connection to servers that do not support plain HTTP anymore
(this is becoming more and more common on the Internet these days)
- Possibly benefit from additional features implemented in lwIP
- Less code to maintain in U-Boot
Prior to applying this series, the lwIP stack needs to be added as a
Git subtree with the following command:
$ git subtree add --squash --prefix lib/lwip/lwip \
https://github.com/lwip-tcpip/lwip.git STABLE-2_2_0_RELEASE
Notes
1. A number of features are currently incompatible with NET_LWIP:
DFU_TFTP, FASTBOOT, SPL_NET, ETH_SANDBOX, ETH_SANDBOX_RAW, DM_ETH. They
all make assumptions on how the network stack is implemented and/or
pull sybols that are not trivially exported from lwIP. Some interface
rework may be needed.
2. Due to the above, and in order to provide some level of testing of the
lwIP code in CI even when the legacy NET is the default, a new QEMU
configuration is introduced (qemu_arm64_lwip_defconfig) which is
based on qemu_arm64_defconfig with NET_LWIP and CMD_*_LWIP enabled.
In addition to that, this series has some [TESTING] patches
which make NET_LWIP the default.
[1] https://lore.kernel.org/all/20231127125726.3735-1-maxim.uvarov@linaro.org/
[2] https://www.nongnu.org/lwip/
[3] https://en.wikipedia.org/wiki/LwIP
Link: https://lore.kernel.org/r/cover.1729070678.git.jerome.forissier@linaro.org
Diffstat (limited to 'net/lwip/tftp.c')
-rw-r--r-- | net/lwip/tftp.c | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/net/lwip/tftp.c b/net/lwip/tftp.c new file mode 100644 index 00000000000..f4d0a6aa19a --- /dev/null +++ b/net/lwip/tftp.c @@ -0,0 +1,290 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright (C) 2024 Linaro Ltd. */ + +#include <command.h> +#include <console.h> +#include <display_options.h> +#include <dm/device.h> +#include <efi_loader.h> +#include <image.h> +#include <linux/delay.h> +#include <lwip/apps/tftp_client.h> +#include <lwip/timeouts.h> +#include <mapmem.h> +#include <net.h> +#include <time.h> + +#define PROGRESS_PRINT_STEP_BYTES (10 * 1024) + +enum done_state { + NOT_DONE = 0, + SUCCESS, + FAILURE, + ABORTED +}; + +struct tftp_ctx { + ulong daddr; + ulong size; + ulong block_count; + ulong start_time; + enum done_state done; +}; + +static void *tftp_open(const char *fname, const char *mode, u8_t is_write) +{ + return NULL; +} + +static void tftp_close(void *handle) +{ + struct tftp_ctx *ctx = handle; + ulong elapsed; + + if (ctx->done == FAILURE || ctx->done == ABORTED) { + /* Closing after an error or Ctrl-C */ + return; + } + ctx->done = SUCCESS; + + elapsed = get_timer(ctx->start_time); + if (elapsed > 0) { + puts("\n\t "); /* Line up with "Loading: " */ + print_size(ctx->size / elapsed * 1000, "/s"); + } + puts("\ndone\n"); + printf("Bytes transferred = %lu (%lx hex)\n", ctx->size, ctx->size); + + if (env_set_hex("filesize", ctx->size)) { + log_err("filesize not updated\n"); + return; + } +} + +static int tftp_read(void *handle, void *buf, int bytes) +{ + return 0; +} + +static int tftp_write(void *handle, struct pbuf *p) +{ + struct tftp_ctx *ctx = handle; + struct pbuf *q; + + for (q = p; q != NULL; q = q->next) { + memcpy((void *)ctx->daddr, q->payload, q->len); + ctx->daddr += q->len; + ctx->size += q->len; + ctx->block_count++; + if (ctx->block_count % 10 == 0) { + putc('#'); + if (ctx->block_count % (65 * 10) == 0) + puts("\n\t "); + } + } + + return 0; +} + +static void tftp_error(void *handle, int err, const char *msg, int size) +{ + struct tftp_ctx *ctx = handle; + char message[100]; + + ctx->done = FAILURE; + memset(message, 0, sizeof(message)); + memcpy(message, msg, LWIP_MIN(sizeof(message) - 1, (size_t)size)); + + printf("\nTFTP error: %d (%s)\n", err, message); +} + +static const struct tftp_context tftp_context = { + tftp_open, + tftp_close, + tftp_read, + tftp_write, + tftp_error +}; + +static int tftp_loop(struct udevice *udev, ulong addr, char *fname, + ip_addr_t srvip, uint16_t srvport) +{ + struct netif *netif; + struct tftp_ctx ctx; + err_t err; + + if (!fname || addr == 0) + return -1; + + if (!srvport) + srvport = TFTP_PORT; + + netif = net_lwip_new_netif(udev); + if (!netif) + return -1; + + ctx.done = NOT_DONE; + ctx.size = 0; + ctx.block_count = 0; + ctx.daddr = addr; + + printf("Using %s device\n", udev->name); + printf("TFTP from server %s; our IP address is %s\n", + ip4addr_ntoa(&srvip), env_get("ipaddr")); + printf("Filename '%s'.\n", fname); + printf("Load address: 0x%lx\n", ctx.daddr); + printf("Loading: "); + + err = tftp_init_client(&tftp_context); + if (!(err == ERR_OK || err == ERR_USE)) + log_err("tftp_init_client err: %d\n", err); + + tftp_client_set_blksize(CONFIG_TFTP_BLOCKSIZE); + + ctx.start_time = get_timer(0); + err = tftp_get(&ctx, &srvip, srvport, fname, TFTP_MODE_OCTET); + /* might return different errors, like routing problems */ + if (err != ERR_OK) { + printf("tftp_get() error %d\n", err); + net_lwip_remove_netif(netif); + return -1; + } + + while (!ctx.done) { + net_lwip_rx(udev, netif); + sys_check_timeouts(); + if (ctrlc()) { + printf("\nAbort\n"); + ctx.done = ABORTED; + break; + } + } + + tftp_cleanup(); + + net_lwip_remove_netif(netif); + + if (ctx.done == SUCCESS) { + if (env_set_hex("fileaddr", addr)) { + log_err("fileaddr not updated\n"); + return -1; + } + efi_set_bootdev("Net", "", fname, map_sysmem(addr, 0), + ctx.size); + return 0; + } + + return -1; +} + +int do_tftpb(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + int ret = CMD_RET_SUCCESS; + char *arg = NULL; + char *words[3] = { }; + char *fname = NULL; + char *server_ip = NULL; + char *server_port = NULL; + char *end; + ip_addr_t srvip; + uint16_t port = TFTP_PORT; + ulong laddr; + ulong addr; + int i; + + laddr = env_get_ulong("loadaddr", 16, image_load_addr); + + switch (argc) { + case 1: + fname = env_get("bootfile"); + break; + case 2: + /* + * Only one arg - accept two forms: + * Just load address, or just boot file name. The latter + * form must be written in a format which can not be + * mis-interpreted as a valid number. + */ + addr = hextoul(argv[1], &end); + if (end == (argv[1] + strlen(argv[1]))) { + laddr = addr; + fname = env_get("bootfile"); + } else { + arg = strdup(argv[1]); + } + break; + case 3: + laddr = hextoul(argv[1], NULL); + arg = strdup(argv[2]); + break; + default: + ret = CMD_RET_USAGE; + goto out; + } + + if (!arg) + arg = net_boot_file_name; + + if (arg) { + /* Parse [ip:[port:]]fname */ + i = 0; + while ((*(words + i) = strsep(&arg,":"))) + i++; + + switch (i) { + case 3: + server_ip = words[0]; + server_port = words[1]; + fname = words[2]; + break; + case 2: + server_ip = words[0]; + fname = words[1]; + break; + case 1: + fname = words[0]; + break; + default: + break; + } + } + + if (!server_ip) + server_ip = env_get("tftpserverip"); + if (!server_ip) + server_ip = env_get("serverip"); + if (!server_ip) { + log_err("error: tftpserverip/serverip has to be set\n"); + ret = CMD_RET_FAILURE; + goto out; + } + + if (server_port) + port = dectoul(server_port, NULL); + + if (!ipaddr_aton(server_ip, &srvip)) { + log_err("error: ipaddr_aton\n"); + ret = CMD_RET_FAILURE; + goto out; + } + + if (!fname) { + log_err("error: no file name\n"); + ret = CMD_RET_FAILURE; + goto out; + } + + if (!laddr) { + log_err("error: no load address\n"); + ret = CMD_RET_FAILURE; + goto out; + } + + eth_set_current(); + + if (tftp_loop(eth_get_dev(), laddr, fname, srvip, port) < 0) + ret = CMD_RET_FAILURE; +out: + free(arg); + return ret; +} |