mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-10 07:04:28 +00:00
test: spl: Add a test for the NET load method
Add a test for loading U-Boot over TFTP. As with other sandbox net routines, we need to initialize our packets manually since things like net_set_ether and net_set_udp_header always use "our" addresses. We use BOOTP instead of DHCP, since DHCP has a tag/length-based format which is harder to parse. Our TFTP implementation doesn't define as many constants as I'd like, so I create some here. Note that the TFTP block size is one-based, but offsets are zero-based. In order to avoid address errors, we need to set up/define some additional address information settings. dram_init_banksize would be a good candidate for settig up bi_dram, but it gets called too late in board_init_r. Signed-off-by: Sean Anderson <seanga2@gmail.com> Reviewed-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
parent
6ba8ecaa96
commit
53d8bf8f9c
6 changed files with 272 additions and 1 deletions
|
@ -129,6 +129,10 @@ void spl_board_init(void)
|
|||
if (!CONFIG_IS_ENABLED(UNIT_TEST))
|
||||
return;
|
||||
|
||||
/* These are necessary so TFTP can use LMBs to check its load address */
|
||||
gd->bd->bi_dram[0].start = gd->ram_base;
|
||||
gd->bd->bi_dram[0].size = get_effective_memsize();
|
||||
|
||||
if (state->run_unittests) {
|
||||
struct unit_test *tests = UNIT_TEST_ALL_START();
|
||||
const int count = UNIT_TEST_ALL_COUNT();
|
||||
|
|
|
@ -12,6 +12,7 @@ enum {
|
|||
BOOT_DEVICE_MMC2_2,
|
||||
BOOT_DEVICE_BOARD,
|
||||
BOOT_DEVICE_VBE,
|
||||
BOOT_DEVICE_CPGMAC,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -12,7 +12,7 @@ CONFIG_SPL_DRIVERS_MISC=y
|
|||
CONFIG_SPL_SYS_MALLOC_F_LEN=0x8000
|
||||
CONFIG_SPL=y
|
||||
CONFIG_SPL_FS_FAT=y
|
||||
CONFIG_SYS_LOAD_ADDR=0x0
|
||||
CONFIG_SYS_LOAD_ADDR=0x1000000
|
||||
CONFIG_PCI=y
|
||||
CONFIG_SANDBOX_SPL=y
|
||||
CONFIG_DEBUG_UART=y
|
||||
|
@ -44,9 +44,11 @@ CONFIG_SPL_SYS_MALLOC_SIZE=0x4000000
|
|||
CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_USE_SECTOR=y
|
||||
CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR=0x0
|
||||
CONFIG_SPL_ENV_SUPPORT=y
|
||||
CONFIG_SPL_ETH=y
|
||||
CONFIG_SPL_FS_EXT4=y
|
||||
CONFIG_SPL_I2C=y
|
||||
CONFIG_SPL_MMC_WRITE=y
|
||||
CONFIG_SPL_NET=y
|
||||
CONFIG_SPL_RTC=y
|
||||
CONFIG_CMD_CPU=y
|
||||
CONFIG_CMD_LICENSE=y
|
||||
|
@ -109,6 +111,8 @@ CONFIG_ENV_IS_NOWHERE=y
|
|||
CONFIG_ENV_IS_IN_EXT4=y
|
||||
CONFIG_ENV_EXT4_INTERFACE="host"
|
||||
CONFIG_ENV_EXT4_DEVICE_AND_PART="0:0"
|
||||
CONFIG_USE_BOOTFILE=y
|
||||
CONFIG_BOOTFILE="uImage"
|
||||
CONFIG_BOOTP_SEND_HOSTNAME=y
|
||||
CONFIG_NETCONSOLE=y
|
||||
CONFIG_IP_DEFRAG=y
|
||||
|
|
|
@ -23,6 +23,15 @@ config SPL_UT_LOAD_FS
|
|||
help
|
||||
Test filesystems and the various load methods which use them.
|
||||
|
||||
config SPL_UT_LOAD_NET
|
||||
bool "Test loading over TFTP"
|
||||
depends on SANDBOX && SPL_OF_REAL
|
||||
depends on SPL_ETH
|
||||
depends on USE_BOOTFILE
|
||||
default y
|
||||
help
|
||||
Test loading images over TFTP using the NET image load method.
|
||||
|
||||
config SPL_UT_LOAD_OS
|
||||
bool "Test loading from the host OS"
|
||||
depends on SANDBOX && SPL_LOAD_FIT
|
||||
|
|
|
@ -4,4 +4,5 @@
|
|||
|
||||
obj-y += spl_load.o
|
||||
obj-$(CONFIG_SPL_UT_LOAD_FS) += spl_load_fs.o
|
||||
obj-$(CONFIG_SPL_UT_LOAD_NET) += spl_load_net.o
|
||||
obj-$(CONFIG_SPL_UT_LOAD_OS) += spl_load_os.o
|
||||
|
|
252
test/image/spl_load_net.c
Normal file
252
test/image/spl_load_net.c
Normal file
|
@ -0,0 +1,252 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2023 Sean Anderson <seanga2@gmail.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <spl.h>
|
||||
#include <test/spl.h>
|
||||
#include <asm/eth.h>
|
||||
#include <test/ut.h>
|
||||
#include "../../net/bootp.h"
|
||||
|
||||
/*
|
||||
* sandbox_eth_bootp_req_to_reply()
|
||||
*
|
||||
* Check if a BOOTP request was sent. If so, inject a reply
|
||||
*
|
||||
* returns 0 if injected, -EAGAIN if not
|
||||
*/
|
||||
static int sandbox_eth_bootp_req_to_reply(struct udevice *dev, void *packet,
|
||||
unsigned int len)
|
||||
{
|
||||
struct eth_sandbox_priv *priv = dev_get_priv(dev);
|
||||
struct ethernet_hdr *eth = packet;
|
||||
struct ip_udp_hdr *ip;
|
||||
struct bootp_hdr *bp;
|
||||
struct ethernet_hdr *eth_recv;
|
||||
struct ip_udp_hdr *ipr;
|
||||
struct bootp_hdr *bpr;
|
||||
|
||||
if (ntohs(eth->et_protlen) != PROT_IP)
|
||||
return -EAGAIN;
|
||||
|
||||
ip = packet + ETHER_HDR_SIZE;
|
||||
if (ip->ip_p != IPPROTO_UDP)
|
||||
return -EAGAIN;
|
||||
|
||||
if (ntohs(ip->udp_dst) != PORT_BOOTPS)
|
||||
return -EAGAIN;
|
||||
|
||||
bp = (void *)ip + IP_UDP_HDR_SIZE;
|
||||
if (bp->bp_op != OP_BOOTREQUEST)
|
||||
return -EAGAIN;
|
||||
|
||||
/* Don't allow the buffer to overrun */
|
||||
if (priv->recv_packets >= PKTBUFSRX)
|
||||
return 0;
|
||||
|
||||
/* reply to the request */
|
||||
eth_recv = (void *)priv->recv_packet_buffer[priv->recv_packets];
|
||||
memcpy(eth_recv, packet, len);
|
||||
ipr = (void *)eth_recv + ETHER_HDR_SIZE;
|
||||
bpr = (void *)ipr + IP_UDP_HDR_SIZE;
|
||||
memcpy(eth_recv->et_dest, eth->et_src, ARP_HLEN);
|
||||
memcpy(eth_recv->et_src, priv->fake_host_hwaddr, ARP_HLEN);
|
||||
ipr->ip_sum = 0;
|
||||
ipr->ip_off = 0;
|
||||
net_write_ip(&ipr->ip_dst, net_ip);
|
||||
net_write_ip(&ipr->ip_src, priv->fake_host_ipaddr);
|
||||
ipr->ip_sum = compute_ip_checksum(ipr, IP_HDR_SIZE);
|
||||
ipr->udp_src = ip->udp_dst;
|
||||
ipr->udp_dst = ip->udp_src;
|
||||
|
||||
bpr->bp_op = OP_BOOTREPLY;
|
||||
net_write_ip(&bpr->bp_yiaddr, net_ip);
|
||||
net_write_ip(&bpr->bp_siaddr, priv->fake_host_ipaddr);
|
||||
copy_filename(bpr->bp_file, CONFIG_BOOTFILE, sizeof(CONFIG_BOOTFILE));
|
||||
memset(&bpr->bp_vend, 0, sizeof(bpr->bp_vend));
|
||||
|
||||
priv->recv_packet_length[priv->recv_packets] = len;
|
||||
++priv->recv_packets;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct spl_test_net_priv {
|
||||
struct unit_test_state *uts;
|
||||
void *img;
|
||||
size_t img_size;
|
||||
u16 port;
|
||||
};
|
||||
|
||||
/* Well known TFTP port # */
|
||||
#define TFTP_PORT 69
|
||||
/* Transaction ID, chosen at random */
|
||||
#define TFTP_TID 21313
|
||||
|
||||
/*
|
||||
* TFTP operations.
|
||||
*/
|
||||
#define TFTP_RRQ 1
|
||||
#define TFTP_DATA 3
|
||||
#define TFTP_ACK 4
|
||||
|
||||
/* default TFTP block size */
|
||||
#define TFTP_BLOCK_SIZE 512
|
||||
|
||||
struct tftp_hdr {
|
||||
u16 opcode;
|
||||
u16 block;
|
||||
};
|
||||
|
||||
#define TFTP_HDR_SIZE sizeof(struct tftp_hdr)
|
||||
|
||||
/*
|
||||
* sandbox_eth_tftp_req_to_reply()
|
||||
*
|
||||
* Check if a TFTP request was sent. If so, inject a reply. We don't support
|
||||
* options, and we don't check for rollover, so we are limited files of less
|
||||
* than 32M.
|
||||
*
|
||||
* returns 0 if injected, -EAGAIN if not
|
||||
*/
|
||||
static int sandbox_eth_tftp_req_to_reply(struct udevice *dev, void *packet,
|
||||
unsigned int len)
|
||||
{
|
||||
struct eth_sandbox_priv *priv = dev_get_priv(dev);
|
||||
struct spl_test_net_priv *test_priv = priv->priv;
|
||||
struct ethernet_hdr *eth = packet;
|
||||
struct ip_udp_hdr *ip;
|
||||
struct tftp_hdr *tftp;
|
||||
struct ethernet_hdr *eth_recv;
|
||||
struct ip_udp_hdr *ipr;
|
||||
struct tftp_hdr *tftpr;
|
||||
size_t size;
|
||||
u16 block;
|
||||
|
||||
if (ntohs(eth->et_protlen) != PROT_IP)
|
||||
return -EAGAIN;
|
||||
|
||||
ip = packet + ETHER_HDR_SIZE;
|
||||
if (ip->ip_p != IPPROTO_UDP)
|
||||
return -EAGAIN;
|
||||
|
||||
if (ntohs(ip->udp_dst) == TFTP_PORT) {
|
||||
tftp = (void *)ip + IP_UDP_HDR_SIZE;
|
||||
if (htons(tftp->opcode) != TFTP_RRQ)
|
||||
return -EAGAIN;
|
||||
|
||||
block = 0;
|
||||
} else if (ntohs(ip->udp_dst) == TFTP_TID) {
|
||||
tftp = (void *)ip + IP_UDP_HDR_SIZE;
|
||||
if (htons(tftp->opcode) != TFTP_ACK)
|
||||
return -EAGAIN;
|
||||
|
||||
block = htons(tftp->block);
|
||||
} else {
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (block * TFTP_BLOCK_SIZE > test_priv->img_size)
|
||||
return 0;
|
||||
|
||||
size = min(test_priv->img_size - block * TFTP_BLOCK_SIZE,
|
||||
(size_t)TFTP_BLOCK_SIZE);
|
||||
|
||||
/* Don't allow the buffer to overrun */
|
||||
if (priv->recv_packets >= PKTBUFSRX)
|
||||
return 0;
|
||||
|
||||
/* reply to the request */
|
||||
eth_recv = (void *)priv->recv_packet_buffer[priv->recv_packets];
|
||||
memcpy(eth_recv->et_dest, eth->et_src, ARP_HLEN);
|
||||
memcpy(eth_recv->et_src, priv->fake_host_hwaddr, ARP_HLEN);
|
||||
eth_recv->et_protlen = htons(PROT_IP);
|
||||
|
||||
ipr = (void *)eth_recv + ETHER_HDR_SIZE;
|
||||
ipr->ip_hl_v = 0x45;
|
||||
ipr->ip_len = htons(IP_UDP_HDR_SIZE + TFTP_HDR_SIZE + size);
|
||||
ipr->ip_off = htons(IP_FLAGS_DFRAG);
|
||||
ipr->ip_ttl = 255;
|
||||
ipr->ip_p = IPPROTO_UDP;
|
||||
ipr->ip_sum = 0;
|
||||
net_copy_ip(&ipr->ip_dst, &ip->ip_src);
|
||||
net_copy_ip(&ipr->ip_src, &ip->ip_dst);
|
||||
ipr->ip_sum = compute_ip_checksum(ipr, IP_HDR_SIZE);
|
||||
|
||||
ipr->udp_src = htons(TFTP_TID);
|
||||
ipr->udp_dst = ip->udp_src;
|
||||
ipr->udp_len = htons(UDP_HDR_SIZE + TFTP_HDR_SIZE + size);
|
||||
ipr->udp_xsum = 0;
|
||||
|
||||
tftpr = (void *)ipr + IP_UDP_HDR_SIZE;
|
||||
tftpr->opcode = htons(TFTP_DATA);
|
||||
tftpr->block = htons(block + 1);
|
||||
memcpy((void *)tftpr + TFTP_HDR_SIZE,
|
||||
test_priv->img + block * TFTP_BLOCK_SIZE, size);
|
||||
|
||||
priv->recv_packet_length[priv->recv_packets] =
|
||||
ETHER_HDR_SIZE + IP_UDP_HDR_SIZE + TFTP_HDR_SIZE + size;
|
||||
++priv->recv_packets;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spl_net_handler(struct udevice *dev, void *packet,
|
||||
unsigned int len)
|
||||
{
|
||||
struct eth_sandbox_priv *priv = dev_get_priv(dev);
|
||||
int old_packets = priv->recv_packets;
|
||||
|
||||
priv->fake_host_ipaddr = string_to_ip("1.1.2.4");
|
||||
net_ip = string_to_ip("1.1.2.2");
|
||||
|
||||
sandbox_eth_arp_req_to_reply(dev, packet, len);
|
||||
sandbox_eth_bootp_req_to_reply(dev, packet, len);
|
||||
sandbox_eth_tftp_req_to_reply(dev, packet, len);
|
||||
|
||||
if (old_packets == priv->recv_packets)
|
||||
return 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spl_test_net_write_image(struct unit_test_state *uts, void *img,
|
||||
size_t img_size)
|
||||
{
|
||||
struct spl_test_net_priv *test_priv = malloc(sizeof(*test_priv));
|
||||
|
||||
ut_assertnonnull(test_priv);
|
||||
test_priv->uts = uts;
|
||||
test_priv->img = img;
|
||||
test_priv->img_size = img_size;
|
||||
|
||||
sandbox_eth_set_tx_handler(0, spl_net_handler);
|
||||
sandbox_eth_set_priv(0, test_priv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spl_test_net(struct unit_test_state *uts, const char *test_name,
|
||||
enum spl_test_image type)
|
||||
{
|
||||
struct eth_sandbox_priv *priv;
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
net_server_ip = string_to_ip("1.1.2.4");
|
||||
ret = do_spl_test_load(uts, test_name, type,
|
||||
SPL_LOAD_IMAGE_GET(0, BOOT_DEVICE_CPGMAC,
|
||||
spl_net_load_image_cpgmac),
|
||||
spl_test_net_write_image);
|
||||
|
||||
sandbox_eth_set_tx_handler(0, NULL);
|
||||
ut_assertok(uclass_get_device(UCLASS_ETH, 0, &dev));
|
||||
priv = dev_get_priv(dev);
|
||||
free(priv->priv);
|
||||
return ret;
|
||||
}
|
||||
SPL_IMG_TEST(spl_test_net, LEGACY, DM_FLAGS);
|
||||
SPL_IMG_TEST(spl_test_net, FIT_INTERNAL, DM_FLAGS);
|
||||
SPL_IMG_TEST(spl_test_net, FIT_EXTERNAL, DM_FLAGS);
|
Loading…
Reference in a new issue