blob: 4af6e21b8b98e6c4b3fbb05dced68244e1e1e5ea [file] [log] [blame]
Sean Andersonc4f86c02023-10-14 16:48:03 -04001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2023 Sean Anderson <seanga2@gmail.com>
4 */
5
Sean Andersonc4f86c02023-10-14 16:48:03 -04006#include <dm.h>
7#include <spl.h>
8#include <test/spl.h>
9#include <asm/eth.h>
10#include <test/ut.h>
11#include "../../net/bootp.h"
12
13/*
14 * sandbox_eth_bootp_req_to_reply()
15 *
16 * Check if a BOOTP request was sent. If so, inject a reply
17 *
18 * returns 0 if injected, -EAGAIN if not
19 */
20static int sandbox_eth_bootp_req_to_reply(struct udevice *dev, void *packet,
21 unsigned int len)
22{
23 struct eth_sandbox_priv *priv = dev_get_priv(dev);
24 struct ethernet_hdr *eth = packet;
25 struct ip_udp_hdr *ip;
26 struct bootp_hdr *bp;
27 struct ethernet_hdr *eth_recv;
28 struct ip_udp_hdr *ipr;
29 struct bootp_hdr *bpr;
30
31 if (ntohs(eth->et_protlen) != PROT_IP)
32 return -EAGAIN;
33
34 ip = packet + ETHER_HDR_SIZE;
35 if (ip->ip_p != IPPROTO_UDP)
36 return -EAGAIN;
37
38 if (ntohs(ip->udp_dst) != PORT_BOOTPS)
39 return -EAGAIN;
40
41 bp = (void *)ip + IP_UDP_HDR_SIZE;
42 if (bp->bp_op != OP_BOOTREQUEST)
43 return -EAGAIN;
44
45 /* Don't allow the buffer to overrun */
46 if (priv->recv_packets >= PKTBUFSRX)
47 return 0;
48
49 /* reply to the request */
50 eth_recv = (void *)priv->recv_packet_buffer[priv->recv_packets];
51 memcpy(eth_recv, packet, len);
52 ipr = (void *)eth_recv + ETHER_HDR_SIZE;
53 bpr = (void *)ipr + IP_UDP_HDR_SIZE;
54 memcpy(eth_recv->et_dest, eth->et_src, ARP_HLEN);
55 memcpy(eth_recv->et_src, priv->fake_host_hwaddr, ARP_HLEN);
56 ipr->ip_sum = 0;
57 ipr->ip_off = 0;
58 net_write_ip(&ipr->ip_dst, net_ip);
59 net_write_ip(&ipr->ip_src, priv->fake_host_ipaddr);
60 ipr->ip_sum = compute_ip_checksum(ipr, IP_HDR_SIZE);
61 ipr->udp_src = ip->udp_dst;
62 ipr->udp_dst = ip->udp_src;
63
64 bpr->bp_op = OP_BOOTREPLY;
65 net_write_ip(&bpr->bp_yiaddr, net_ip);
66 net_write_ip(&bpr->bp_siaddr, priv->fake_host_ipaddr);
67 copy_filename(bpr->bp_file, CONFIG_BOOTFILE, sizeof(CONFIG_BOOTFILE));
68 memset(&bpr->bp_vend, 0, sizeof(bpr->bp_vend));
69
70 priv->recv_packet_length[priv->recv_packets] = len;
71 ++priv->recv_packets;
72
73 return 0;
74}
75
76struct spl_test_net_priv {
77 struct unit_test_state *uts;
78 void *img;
79 size_t img_size;
80 u16 port;
81};
82
83/* Well known TFTP port # */
84#define TFTP_PORT 69
85/* Transaction ID, chosen at random */
86#define TFTP_TID 21313
87
88/*
89 * TFTP operations.
90 */
91#define TFTP_RRQ 1
92#define TFTP_DATA 3
93#define TFTP_ACK 4
94
95/* default TFTP block size */
96#define TFTP_BLOCK_SIZE 512
97
98struct tftp_hdr {
99 u16 opcode;
100 u16 block;
101};
102
103#define TFTP_HDR_SIZE sizeof(struct tftp_hdr)
104
105/*
106 * sandbox_eth_tftp_req_to_reply()
107 *
108 * Check if a TFTP request was sent. If so, inject a reply. We don't support
109 * options, and we don't check for rollover, so we are limited files of less
110 * than 32M.
111 *
112 * returns 0 if injected, -EAGAIN if not
113 */
114static int sandbox_eth_tftp_req_to_reply(struct udevice *dev, void *packet,
115 unsigned int len)
116{
117 struct eth_sandbox_priv *priv = dev_get_priv(dev);
118 struct spl_test_net_priv *test_priv = priv->priv;
119 struct ethernet_hdr *eth = packet;
120 struct ip_udp_hdr *ip;
121 struct tftp_hdr *tftp;
122 struct ethernet_hdr *eth_recv;
123 struct ip_udp_hdr *ipr;
124 struct tftp_hdr *tftpr;
125 size_t size;
126 u16 block;
127
128 if (ntohs(eth->et_protlen) != PROT_IP)
129 return -EAGAIN;
130
131 ip = packet + ETHER_HDR_SIZE;
132 if (ip->ip_p != IPPROTO_UDP)
133 return -EAGAIN;
134
135 if (ntohs(ip->udp_dst) == TFTP_PORT) {
136 tftp = (void *)ip + IP_UDP_HDR_SIZE;
137 if (htons(tftp->opcode) != TFTP_RRQ)
138 return -EAGAIN;
139
140 block = 0;
141 } else if (ntohs(ip->udp_dst) == TFTP_TID) {
142 tftp = (void *)ip + IP_UDP_HDR_SIZE;
143 if (htons(tftp->opcode) != TFTP_ACK)
144 return -EAGAIN;
145
146 block = htons(tftp->block);
147 } else {
148 return -EAGAIN;
149 }
150
151 if (block * TFTP_BLOCK_SIZE > test_priv->img_size)
152 return 0;
153
154 size = min(test_priv->img_size - block * TFTP_BLOCK_SIZE,
155 (size_t)TFTP_BLOCK_SIZE);
156
157 /* Don't allow the buffer to overrun */
158 if (priv->recv_packets >= PKTBUFSRX)
159 return 0;
160
161 /* reply to the request */
162 eth_recv = (void *)priv->recv_packet_buffer[priv->recv_packets];
163 memcpy(eth_recv->et_dest, eth->et_src, ARP_HLEN);
164 memcpy(eth_recv->et_src, priv->fake_host_hwaddr, ARP_HLEN);
165 eth_recv->et_protlen = htons(PROT_IP);
166
167 ipr = (void *)eth_recv + ETHER_HDR_SIZE;
168 ipr->ip_hl_v = 0x45;
169 ipr->ip_len = htons(IP_UDP_HDR_SIZE + TFTP_HDR_SIZE + size);
170 ipr->ip_off = htons(IP_FLAGS_DFRAG);
171 ipr->ip_ttl = 255;
172 ipr->ip_p = IPPROTO_UDP;
173 ipr->ip_sum = 0;
174 net_copy_ip(&ipr->ip_dst, &ip->ip_src);
175 net_copy_ip(&ipr->ip_src, &ip->ip_dst);
176 ipr->ip_sum = compute_ip_checksum(ipr, IP_HDR_SIZE);
177
178 ipr->udp_src = htons(TFTP_TID);
179 ipr->udp_dst = ip->udp_src;
180 ipr->udp_len = htons(UDP_HDR_SIZE + TFTP_HDR_SIZE + size);
181 ipr->udp_xsum = 0;
182
183 tftpr = (void *)ipr + IP_UDP_HDR_SIZE;
184 tftpr->opcode = htons(TFTP_DATA);
185 tftpr->block = htons(block + 1);
186 memcpy((void *)tftpr + TFTP_HDR_SIZE,
187 test_priv->img + block * TFTP_BLOCK_SIZE, size);
188
189 priv->recv_packet_length[priv->recv_packets] =
190 ETHER_HDR_SIZE + IP_UDP_HDR_SIZE + TFTP_HDR_SIZE + size;
191 ++priv->recv_packets;
192
193 return 0;
194}
195
196static int spl_net_handler(struct udevice *dev, void *packet,
197 unsigned int len)
198{
199 struct eth_sandbox_priv *priv = dev_get_priv(dev);
200 int old_packets = priv->recv_packets;
201
202 priv->fake_host_ipaddr = string_to_ip("1.1.2.4");
203 net_ip = string_to_ip("1.1.2.2");
204
205 sandbox_eth_arp_req_to_reply(dev, packet, len);
206 sandbox_eth_bootp_req_to_reply(dev, packet, len);
207 sandbox_eth_tftp_req_to_reply(dev, packet, len);
208
209 if (old_packets == priv->recv_packets)
210 return 0;
211
212 return 0;
213}
214
215static int spl_test_net_write_image(struct unit_test_state *uts, void *img,
216 size_t img_size)
217{
218 struct spl_test_net_priv *test_priv = malloc(sizeof(*test_priv));
219
220 ut_assertnonnull(test_priv);
221 test_priv->uts = uts;
222 test_priv->img = img;
223 test_priv->img_size = img_size;
224
225 sandbox_eth_set_tx_handler(0, spl_net_handler);
226 sandbox_eth_set_priv(0, test_priv);
227 return 0;
228}
229
230static int spl_test_net(struct unit_test_state *uts, const char *test_name,
231 enum spl_test_image type)
232{
233 struct eth_sandbox_priv *priv;
234 struct udevice *dev;
235 int ret;
236
237 net_server_ip = string_to_ip("1.1.2.4");
238 ret = do_spl_test_load(uts, test_name, type,
239 SPL_LOAD_IMAGE_GET(0, BOOT_DEVICE_CPGMAC,
240 spl_net_load_image_cpgmac),
241 spl_test_net_write_image);
242
243 sandbox_eth_set_tx_handler(0, NULL);
244 ut_assertok(uclass_get_device(UCLASS_ETH, 0, &dev));
245 priv = dev_get_priv(dev);
246 free(priv->priv);
247 return ret;
248}
249SPL_IMG_TEST(spl_test_net, LEGACY, DM_FLAGS);
Sean Anderson810e16e2023-11-08 11:48:52 -0500250SPL_IMG_TEST(spl_test_net, LEGACY_LZMA, DM_FLAGS);
251SPL_IMG_TEST(spl_test_net, IMX8, DM_FLAGS);
Sean Andersonc4f86c02023-10-14 16:48:03 -0400252SPL_IMG_TEST(spl_test_net, FIT_INTERNAL, DM_FLAGS);
253SPL_IMG_TEST(spl_test_net, FIT_EXTERNAL, DM_FLAGS);