]> Git Repo - u-boot.git/blob - test/image/spl_load_net.c
Merge patch series "spl: Use common function for loading/parsing images"
[u-boot.git] / test / image / spl_load_net.c
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);
251 SPL_IMG_TEST(spl_test_net, LEGACY_LZMA, DM_FLAGS);
252 SPL_IMG_TEST(spl_test_net, IMX8, DM_FLAGS);
253 SPL_IMG_TEST(spl_test_net, FIT_INTERNAL, DM_FLAGS);
254 SPL_IMG_TEST(spl_test_net, FIT_EXTERNAL, DM_FLAGS);
This page took 0.041368 seconds and 4 git commands to generate.