fdtdec: support multiple phandles in memory carveout
[oweals/u-boot.git] / lib / efi_selftest / efi_selftest_snp.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * efi_selftest_snp
4  *
5  * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
6  *
7  * This unit test covers the Simple Network Protocol as well as
8  * the CopyMem and SetMem boottime services.
9  *
10  * A DHCP discover message is sent. The test is successful if a
11  * DHCP reply is received.
12  *
13  * TODO: Once ConnectController and DisconnectController are implemented
14  *       we should connect our code as controller.
15  */
16
17 #include <efi_selftest.h>
18
19 /*
20  * MAC address for broadcasts
21  */
22 static const u8 BROADCAST_MAC[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
23
24 struct dhcp_hdr {
25         u8 op;
26 #define BOOTREQUEST 1
27 #define BOOTREPLY 2
28         u8 htype;
29 # define HWT_ETHER 1
30         u8 hlen;
31 # define HWL_ETHER 6
32         u8 hops;
33         u32 xid;
34         u16 secs;
35         u16 flags;
36 #define DHCP_FLAGS_UNICAST      0x0000
37 #define DHCP_FLAGS_BROADCAST    0x0080
38         u32 ciaddr;
39         u32 yiaddr;
40         u32 siaddr;
41         u32 giaddr;
42         u8 chaddr[16];
43         u8 sname[64];
44         u8 file[128];
45 };
46
47 /*
48  * Message type option.
49  */
50 #define DHCP_MESSAGE_TYPE       0x35
51 #define DHCPDISCOVER            1
52 #define DHCPOFFER               2
53 #define DHCPREQUEST             3
54 #define DHCPDECLINE             4
55 #define DHCPACK                 5
56 #define DHCPNAK                 6
57 #define DHCPRELEASE             7
58
59 struct dhcp {
60         struct ethernet_hdr eth_hdr;
61         struct ip_udp_hdr ip_udp;
62         struct dhcp_hdr dhcp_hdr;
63         u8 opt[128];
64 } __packed;
65
66 static struct efi_boot_services *boottime;
67 static struct efi_simple_network *net;
68 static struct efi_event *timer;
69 static const efi_guid_t efi_net_guid = EFI_SIMPLE_NETWORK_PROTOCOL_GUID;
70 /* IP packet ID */
71 static unsigned int net_ip_id;
72
73 /*
74  * Compute the checksum of the IP header. We cover even values of length only.
75  * We cannot use net/checksum.c due to different CFLAGS values.
76  *
77  * @buf:        IP header
78  * @len:        length of header in bytes
79  * @return:     checksum
80  */
81 static unsigned int efi_ip_checksum(const void *buf, size_t len)
82 {
83         size_t i;
84         u32 sum = 0;
85         const u16 *pos = buf;
86
87         for (i = 0; i < len; i += 2)
88                 sum += *pos++;
89
90         sum = (sum >> 16) + (sum & 0xffff);
91         sum += sum >> 16;
92         sum = ~sum & 0xffff;
93
94         return sum;
95 }
96
97 /*
98  * Transmit a DHCPDISCOVER message.
99  */
100 static efi_status_t send_dhcp_discover(void)
101 {
102         efi_status_t ret;
103         struct dhcp p = {};
104
105         /*
106          * Fill Ethernet header
107          */
108         boottime->copy_mem(p.eth_hdr.et_dest, (void *)BROADCAST_MAC, ARP_HLEN);
109         boottime->copy_mem(p.eth_hdr.et_src, &net->mode->current_address,
110                            ARP_HLEN);
111         p.eth_hdr.et_protlen = htons(PROT_IP);
112         /*
113          * Fill IP header
114          */
115         p.ip_udp.ip_hl_v        = 0x45;
116         p.ip_udp.ip_len         = htons(sizeof(struct dhcp) -
117                                         sizeof(struct ethernet_hdr));
118         p.ip_udp.ip_id          = htons(++net_ip_id);
119         p.ip_udp.ip_off         = htons(IP_FLAGS_DFRAG);
120         p.ip_udp.ip_ttl         = 0xff; /* time to live */
121         p.ip_udp.ip_p           = IPPROTO_UDP;
122         boottime->set_mem(&p.ip_udp.ip_dst, 4, 0xff);
123         p.ip_udp.ip_sum         = efi_ip_checksum(&p.ip_udp, IP_HDR_SIZE);
124
125         /*
126          * Fill UDP header
127          */
128         p.ip_udp.udp_src        = htons(68);
129         p.ip_udp.udp_dst        = htons(67);
130         p.ip_udp.udp_len        = htons(sizeof(struct dhcp) -
131                                         sizeof(struct ethernet_hdr) -
132                                         sizeof(struct ip_hdr));
133         /*
134          * Fill DHCP header
135          */
136         p.dhcp_hdr.op           = BOOTREQUEST;
137         p.dhcp_hdr.htype        = HWT_ETHER;
138         p.dhcp_hdr.hlen         = HWL_ETHER;
139         p.dhcp_hdr.flags        = htons(DHCP_FLAGS_UNICAST);
140         boottime->copy_mem(&p.dhcp_hdr.chaddr,
141                            &net->mode->current_address, ARP_HLEN);
142         /*
143          * Fill options
144          */
145         p.opt[0]        = 0x63; /* DHCP magic cookie */
146         p.opt[1]        = 0x82;
147         p.opt[2]        = 0x53;
148         p.opt[3]        = 0x63;
149         p.opt[4]        = DHCP_MESSAGE_TYPE;
150         p.opt[5]        = 0x01; /* length */
151         p.opt[6]        = DHCPDISCOVER;
152         p.opt[7]        = 0x39; /* maximum message size */
153         p.opt[8]        = 0x02; /* length */
154         p.opt[9]        = 0x02; /* 576 bytes */
155         p.opt[10]       = 0x40;
156         p.opt[11]       = 0xff; /* end of options */
157
158         /*
159          * Transmit DHCPDISCOVER message.
160          */
161         ret = net->transmit(net, 0, sizeof(struct dhcp), &p, NULL, NULL, 0);
162         if (ret != EFI_SUCCESS)
163                 efi_st_error("Sending a DHCP request failed\n");
164         else
165                 efi_st_printf("DHCP Discover\n");
166         return ret;
167 }
168
169 /*
170  * Setup unit test.
171  *
172  * Create a 1 s periodic timer.
173  * Start the network driver.
174  *
175  * @handle:     handle of the loaded image
176  * @systable:   system table
177  * @return:     EFI_ST_SUCCESS for success
178  */
179 static int setup(const efi_handle_t handle,
180                  const struct efi_system_table *systable)
181 {
182         efi_status_t ret;
183
184         boottime = systable->boottime;
185
186         /*
187          * Create a timer event.
188          */
189         ret = boottime->create_event(EVT_TIMER, TPL_CALLBACK, NULL, NULL,
190                                      &timer);
191         if (ret != EFI_SUCCESS) {
192                 efi_st_error("Failed to create event\n");
193                 return EFI_ST_FAILURE;
194         }
195         /*
196          * Set timer period to 1s.
197          */
198         ret = boottime->set_timer(timer, EFI_TIMER_PERIODIC, 10000000);
199         if (ret != EFI_SUCCESS) {
200                 efi_st_error("Failed to set timer\n");
201                 return EFI_ST_FAILURE;
202         }
203         /*
204          * Find an interface implementing the SNP protocol.
205          */
206         ret = boottime->locate_protocol(&efi_net_guid, NULL, (void **)&net);
207         if (ret != EFI_SUCCESS) {
208                 net = NULL;
209                 efi_st_error("Failed to locate simple network protocol\n");
210                 return EFI_ST_FAILURE;
211         }
212         /*
213          * Check hardware address size.
214          */
215         if (!net->mode) {
216                 efi_st_error("Mode not provided\n");
217                 return EFI_ST_FAILURE;
218         }
219         if (net->mode->hwaddr_size != ARP_HLEN) {
220                 efi_st_error("HwAddressSize = %u, expected %u\n",
221                              net->mode->hwaddr_size, ARP_HLEN);
222                 return EFI_ST_FAILURE;
223         }
224         /*
225          * Check that WaitForPacket event exists.
226          */
227         if (!net->wait_for_packet) {
228                 efi_st_error("WaitForPacket event missing\n");
229                 return EFI_ST_FAILURE;
230         }
231         if (net->mode->state == EFI_NETWORK_INITIALIZED) {
232                 /*
233                  * Shut down network adapter.
234                  */
235                 ret = net->shutdown(net);
236                 if (ret != EFI_SUCCESS) {
237                         efi_st_error("Failed to shut down network adapter\n");
238                         return EFI_ST_FAILURE;
239                 }
240         }
241         if (net->mode->state == EFI_NETWORK_STARTED) {
242                 /*
243                  * Stop network adapter.
244                  */
245                 ret = net->stop(net);
246                 if (ret != EFI_SUCCESS) {
247                         efi_st_error("Failed to stop network adapter\n");
248                         return EFI_ST_FAILURE;
249                 }
250         }
251         /*
252          * Start network adapter.
253          */
254         ret = net->start(net);
255         if (ret != EFI_SUCCESS && ret != EFI_ALREADY_STARTED) {
256                 efi_st_error("Failed to start network adapter\n");
257                 return EFI_ST_FAILURE;
258         }
259         if (net->mode->state != EFI_NETWORK_STARTED) {
260                 efi_st_error("Failed to start network adapter\n");
261                 return EFI_ST_FAILURE;
262         }
263         /*
264          * Initialize network adapter.
265          */
266         ret = net->initialize(net, 0, 0);
267         if (ret != EFI_SUCCESS) {
268                 efi_st_error("Failed to initialize network adapter\n");
269                 return EFI_ST_FAILURE;
270         }
271         if (net->mode->state != EFI_NETWORK_INITIALIZED) {
272                 efi_st_error("Failed to initialize network adapter\n");
273                 return EFI_ST_FAILURE;
274         }
275         return EFI_ST_SUCCESS;
276 }
277
278 /*
279  * Execute unit test.
280  *
281  * A DHCP discover message is sent. The test is successful if a
282  * DHCP reply is received within 10 seconds.
283  *
284  * @return:     EFI_ST_SUCCESS for success
285  */
286 static int execute(void)
287 {
288         efi_status_t ret;
289         struct efi_event *events[2];
290         efi_uintn_t index;
291         union {
292                 struct dhcp p;
293                 u8 b[PKTSIZE];
294         } buffer;
295         struct efi_mac_address srcaddr;
296         struct efi_mac_address destaddr;
297         size_t buffer_size;
298         u8 *addr;
299
300         /*
301          * The timeout is to occur after 10 s.
302          */
303         unsigned int timeout = 10;
304
305         /* Setup may have failed */
306         if (!net || !timer) {
307                 efi_st_error("Cannot execute test after setup failure\n");
308                 return EFI_ST_FAILURE;
309         }
310
311         /*
312          * Send DHCP discover message
313          */
314         ret = send_dhcp_discover();
315         if (ret != EFI_SUCCESS)
316                 return EFI_ST_FAILURE;
317
318         /*
319          * If we would call WaitForEvent only with the WaitForPacket event,
320          * our code would block until a packet is received which might never
321          * occur. By calling WaitFor event with both a timer event and the
322          * WaitForPacket event we can escape this blocking situation.
323          *
324          * If the timer event occurs before we have received a DHCP reply
325          * a further DHCP discover message is sent.
326          */
327         events[0] = timer;
328         events[1] = net->wait_for_packet;
329         for (;;) {
330                 u32 int_status;
331
332                 /*
333                  * Wait for packet to be received or timer event.
334                  */
335                 boottime->wait_for_event(2, events, &index);
336                 if (index == 0) {
337                         /*
338                          * The timer event occurred. Check for timeout.
339                          */
340                         --timeout;
341                         if (!timeout) {
342                                 efi_st_error("Timeout occurred\n");
343                                 return EFI_ST_FAILURE;
344                         }
345                         /*
346                          * Send further DHCP discover message
347                          */
348                         ret = send_dhcp_discover();
349                         if (ret != EFI_SUCCESS)
350                                 return EFI_ST_FAILURE;
351                         continue;
352                 }
353                 /*
354                  * Receive packet
355                  */
356                 buffer_size = sizeof(buffer);
357                 ret = net->get_status(net, &int_status, NULL);
358                 if (ret != EFI_SUCCESS) {
359                         efi_st_error("Failed to get status");
360                         return EFI_ST_FAILURE;
361                 }
362                 if (!(int_status & EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT)) {
363                         efi_st_error("RX interrupt not set");
364                         return EFI_ST_FAILURE;
365                 }
366                 ret = net->receive(net, NULL, &buffer_size, &buffer,
367                                    &srcaddr, &destaddr, NULL);
368                 if (ret != EFI_SUCCESS) {
369                         efi_st_error("Failed to receive packet");
370                         return EFI_ST_FAILURE;
371                 }
372                 /*
373                  * Check the packet is meant for this system.
374                  * Unfortunately QEMU ignores the broadcast flag.
375                  * So we have to check for broadcasts too.
376                  */
377                 if (memcmp(&destaddr, &net->mode->current_address, ARP_HLEN) &&
378                     memcmp(&destaddr, BROADCAST_MAC, ARP_HLEN))
379                         continue;
380                 /*
381                  * Check this is a DHCP reply
382                  */
383                 if (buffer.p.eth_hdr.et_protlen != ntohs(PROT_IP) ||
384                     buffer.p.ip_udp.ip_hl_v != 0x45 ||
385                     buffer.p.ip_udp.ip_p != IPPROTO_UDP ||
386                     buffer.p.ip_udp.udp_src != ntohs(67) ||
387                     buffer.p.ip_udp.udp_dst != ntohs(68) ||
388                     buffer.p.dhcp_hdr.op != BOOTREPLY)
389                         continue;
390                 /*
391                  * We successfully received a DHCP reply.
392                  */
393                 break;
394         }
395
396         /*
397          * Write a log message.
398          */
399         addr = (u8 *)&buffer.p.ip_udp.ip_src;
400         efi_st_printf("DHCP reply received from %u.%u.%u.%u (%pm) ",
401                       addr[0], addr[1], addr[2], addr[3], &srcaddr);
402         if (!memcmp(&destaddr, BROADCAST_MAC, ARP_HLEN))
403                 efi_st_printf("as broadcast message.\n");
404         else
405                 efi_st_printf("as unicast message.\n");
406
407         return EFI_ST_SUCCESS;
408 }
409
410 /*
411  * Tear down unit test.
412  *
413  * Close the timer event created in setup.
414  * Shut down the network adapter.
415  *
416  * @return:     EFI_ST_SUCCESS for success
417  */
418 static int teardown(void)
419 {
420         efi_status_t ret;
421         int exit_status = EFI_ST_SUCCESS;
422
423         if (timer) {
424                 /*
425                  * Stop timer.
426                  */
427                 ret = boottime->set_timer(timer, EFI_TIMER_STOP, 0);
428                 if (ret != EFI_SUCCESS) {
429                         efi_st_error("Failed to stop timer");
430                         exit_status = EFI_ST_FAILURE;
431                 }
432                 /*
433                  * Close timer event.
434                  */
435                 ret = boottime->close_event(timer);
436                 if (ret != EFI_SUCCESS) {
437                         efi_st_error("Failed to close event");
438                         exit_status = EFI_ST_FAILURE;
439                 }
440         }
441         if (net) {
442                 /*
443                  * Shut down network adapter.
444                  */
445                 ret = net->shutdown(net);
446                 if (ret != EFI_SUCCESS) {
447                         efi_st_error("Failed to shut down network adapter\n");
448                         exit_status = EFI_ST_FAILURE;
449                 }
450                 if (net->mode->state != EFI_NETWORK_STARTED) {
451                         efi_st_error("Failed to shutdown network adapter\n");
452                         return EFI_ST_FAILURE;
453                 }
454                 /*
455                  * Stop network adapter.
456                  */
457                 ret = net->stop(net);
458                 if (ret != EFI_SUCCESS) {
459                         efi_st_error("Failed to stop network adapter\n");
460                         exit_status = EFI_ST_FAILURE;
461                 }
462                 if (net->mode->state != EFI_NETWORK_STOPPED) {
463                         efi_st_error("Failed to stop network adapter\n");
464                         return EFI_ST_FAILURE;
465                 }
466         }
467
468         return exit_status;
469 }
470
471 EFI_UNIT_TEST(snp) = {
472         .name = "simple network protocol",
473         .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
474         .setup = setup,
475         .execute = execute,
476         .teardown = teardown,
477 #ifdef CONFIG_SANDBOX
478         /*
479          * Running this test on the sandbox requires setting environment
480          * variable ethact to a network interface connected to a DHCP server and
481          * ethrotate to 'no'.
482          */
483         .on_request = true,
484 #endif
485 };