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