mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-10-29 19:18:22 +00:00 
			
		
		
		
	As part of bringing the master branch back in to next, we need to allow for all of these changes to exist here. Reported-by: Jonas Karlman <jonas@kwiboo.se> Signed-off-by: Tom Rini <trini@konsulko.com>
		
			
				
	
	
		
			254 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			254 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0+
 | |
| /*
 | |
|  * Copyright (C) 2023 Sean Anderson <seanga2@gmail.com>
 | |
|  */
 | |
| 
 | |
| #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, LEGACY_LZMA, DM_FLAGS);
 | |
| SPL_IMG_TEST(spl_test_net, IMX8, DM_FLAGS);
 | |
| SPL_IMG_TEST(spl_test_net, FIT_INTERNAL, DM_FLAGS);
 | |
| SPL_IMG_TEST(spl_test_net, FIT_EXTERNAL, DM_FLAGS);
 |