183d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
25ca23ed5SHeinrich Schuchardt /*
35ca23ed5SHeinrich Schuchardt  * efi_selftest_snp
45ca23ed5SHeinrich Schuchardt  *
55ca23ed5SHeinrich Schuchardt  * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
65ca23ed5SHeinrich Schuchardt  *
75ca23ed5SHeinrich Schuchardt  * This unit test covers the Simple Network Protocol as well as
85ca23ed5SHeinrich Schuchardt  * the CopyMem and SetMem boottime services.
95ca23ed5SHeinrich Schuchardt  *
105ca23ed5SHeinrich Schuchardt  * A DHCP discover message is sent. The test is successful if a
115ca23ed5SHeinrich Schuchardt  * DHCP reply is received.
125ca23ed5SHeinrich Schuchardt  *
135ca23ed5SHeinrich Schuchardt  * TODO: Once ConnectController and DisconnectController are implemented
145ca23ed5SHeinrich Schuchardt  *	 we should connect our code as controller.
155ca23ed5SHeinrich Schuchardt  */
165ca23ed5SHeinrich Schuchardt 
175ca23ed5SHeinrich Schuchardt #include <efi_selftest.h>
185ca23ed5SHeinrich Schuchardt 
195ca23ed5SHeinrich Schuchardt /*
205ca23ed5SHeinrich Schuchardt  * MAC address for broadcasts
215ca23ed5SHeinrich Schuchardt  */
225ca23ed5SHeinrich Schuchardt static const u8 BROADCAST_MAC[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
235ca23ed5SHeinrich Schuchardt 
245ca23ed5SHeinrich Schuchardt struct dhcp_hdr {
255ca23ed5SHeinrich Schuchardt 	u8 op;
265ca23ed5SHeinrich Schuchardt #define BOOTREQUEST 1
275ca23ed5SHeinrich Schuchardt #define BOOTREPLY 2
285ca23ed5SHeinrich Schuchardt 	u8 htype;
295ca23ed5SHeinrich Schuchardt # define HWT_ETHER 1
305ca23ed5SHeinrich Schuchardt 	u8 hlen;
315ca23ed5SHeinrich Schuchardt # define HWL_ETHER 6
325ca23ed5SHeinrich Schuchardt 	u8 hops;
335ca23ed5SHeinrich Schuchardt 	u32 xid;
345ca23ed5SHeinrich Schuchardt 	u16 secs;
355ca23ed5SHeinrich Schuchardt 	u16 flags;
365ca23ed5SHeinrich Schuchardt #define DHCP_FLAGS_UNICAST	0x0000
375ca23ed5SHeinrich Schuchardt #define DHCP_FLAGS_BROADCAST	0x0080
385ca23ed5SHeinrich Schuchardt 	u32 ciaddr;
395ca23ed5SHeinrich Schuchardt 	u32 yiaddr;
405ca23ed5SHeinrich Schuchardt 	u32 siaddr;
415ca23ed5SHeinrich Schuchardt 	u32 giaddr;
425ca23ed5SHeinrich Schuchardt 	u8 chaddr[16];
435ca23ed5SHeinrich Schuchardt 	u8 sname[64];
445ca23ed5SHeinrich Schuchardt 	u8 file[128];
455ca23ed5SHeinrich Schuchardt };
465ca23ed5SHeinrich Schuchardt 
475ca23ed5SHeinrich Schuchardt /*
485ca23ed5SHeinrich Schuchardt  * Message type option.
495ca23ed5SHeinrich Schuchardt  */
505ca23ed5SHeinrich Schuchardt #define DHCP_MESSAGE_TYPE	0x35
515ca23ed5SHeinrich Schuchardt #define DHCPDISCOVER		1
525ca23ed5SHeinrich Schuchardt #define DHCPOFFER		2
535ca23ed5SHeinrich Schuchardt #define DHCPREQUEST		3
545ca23ed5SHeinrich Schuchardt #define DHCPDECLINE		4
555ca23ed5SHeinrich Schuchardt #define DHCPACK			5
565ca23ed5SHeinrich Schuchardt #define DHCPNAK			6
575ca23ed5SHeinrich Schuchardt #define DHCPRELEASE		7
585ca23ed5SHeinrich Schuchardt 
595ca23ed5SHeinrich Schuchardt struct dhcp {
605ca23ed5SHeinrich Schuchardt 	struct ethernet_hdr eth_hdr;
615ca23ed5SHeinrich Schuchardt 	struct ip_udp_hdr ip_udp;
625ca23ed5SHeinrich Schuchardt 	struct dhcp_hdr dhcp_hdr;
635ca23ed5SHeinrich Schuchardt 	u8 opt[128];
645ca23ed5SHeinrich Schuchardt } __packed;
655ca23ed5SHeinrich Schuchardt 
665ca23ed5SHeinrich Schuchardt static struct efi_boot_services *boottime;
675ca23ed5SHeinrich Schuchardt static struct efi_simple_network *net;
685ca23ed5SHeinrich Schuchardt static struct efi_event *timer;
695ca23ed5SHeinrich Schuchardt static const efi_guid_t efi_net_guid = EFI_SIMPLE_NETWORK_GUID;
705ca23ed5SHeinrich Schuchardt /* IP packet ID */
715ca23ed5SHeinrich Schuchardt static unsigned int net_ip_id;
725ca23ed5SHeinrich Schuchardt 
735ca23ed5SHeinrich Schuchardt /*
745ca23ed5SHeinrich Schuchardt  * Compute the checksum of the IP header. We cover even values of length only.
755ca23ed5SHeinrich Schuchardt  * We cannot use net/checksum.c due to different CFLAGS values.
765ca23ed5SHeinrich Schuchardt  *
775ca23ed5SHeinrich Schuchardt  * @buf:	IP header
785ca23ed5SHeinrich Schuchardt  * @len:	length of header in bytes
795ca23ed5SHeinrich Schuchardt  * @return:	checksum
805ca23ed5SHeinrich Schuchardt  */
efi_ip_checksum(const void * buf,size_t len)815ca23ed5SHeinrich Schuchardt static unsigned int efi_ip_checksum(const void *buf, size_t len)
825ca23ed5SHeinrich Schuchardt {
835ca23ed5SHeinrich Schuchardt 	size_t i;
845ca23ed5SHeinrich Schuchardt 	u32 sum = 0;
855ca23ed5SHeinrich Schuchardt 	const u16 *pos = buf;
865ca23ed5SHeinrich Schuchardt 
875ca23ed5SHeinrich Schuchardt 	for (i = 0; i < len; i += 2)
885ca23ed5SHeinrich Schuchardt 		sum += *pos++;
895ca23ed5SHeinrich Schuchardt 
905ca23ed5SHeinrich Schuchardt 	sum = (sum >> 16) + (sum & 0xffff);
915ca23ed5SHeinrich Schuchardt 	sum += sum >> 16;
925ca23ed5SHeinrich Schuchardt 	sum = ~sum & 0xffff;
935ca23ed5SHeinrich Schuchardt 
945ca23ed5SHeinrich Schuchardt 	return sum;
955ca23ed5SHeinrich Schuchardt }
965ca23ed5SHeinrich Schuchardt 
975ca23ed5SHeinrich Schuchardt /*
985ca23ed5SHeinrich Schuchardt  * Transmit a DHCPDISCOVER message.
995ca23ed5SHeinrich Schuchardt  */
send_dhcp_discover(void)1005ca23ed5SHeinrich Schuchardt static efi_status_t send_dhcp_discover(void)
1015ca23ed5SHeinrich Schuchardt {
1025ca23ed5SHeinrich Schuchardt 	efi_status_t ret;
1035ca23ed5SHeinrich Schuchardt 	struct dhcp p = {};
1045ca23ed5SHeinrich Schuchardt 
1055ca23ed5SHeinrich Schuchardt 	/*
1060fdb9e30SHeinrich Schuchardt 	 * Fill Ethernet header
1075ca23ed5SHeinrich Schuchardt 	 */
1085ca23ed5SHeinrich Schuchardt 	boottime->copy_mem(p.eth_hdr.et_dest, (void *)BROADCAST_MAC, ARP_HLEN);
1095ca23ed5SHeinrich Schuchardt 	boottime->copy_mem(p.eth_hdr.et_src, &net->mode->current_address,
1105ca23ed5SHeinrich Schuchardt 			   ARP_HLEN);
1115ca23ed5SHeinrich Schuchardt 	p.eth_hdr.et_protlen = htons(PROT_IP);
1125ca23ed5SHeinrich Schuchardt 	/*
1135ca23ed5SHeinrich Schuchardt 	 * Fill IP header
1145ca23ed5SHeinrich Schuchardt 	 */
1155ca23ed5SHeinrich Schuchardt 	p.ip_udp.ip_hl_v	= 0x45;
1165ca23ed5SHeinrich Schuchardt 	p.ip_udp.ip_len		= htons(sizeof(struct dhcp) -
1175ca23ed5SHeinrich Schuchardt 					sizeof(struct ethernet_hdr));
1185ca23ed5SHeinrich Schuchardt 	p.ip_udp.ip_id		= htons(++net_ip_id);
1195ca23ed5SHeinrich Schuchardt 	p.ip_udp.ip_off		= htons(IP_FLAGS_DFRAG);
1205ca23ed5SHeinrich Schuchardt 	p.ip_udp.ip_ttl		= 0xff; /* time to live */
1215ca23ed5SHeinrich Schuchardt 	p.ip_udp.ip_p		= IPPROTO_UDP;
1225ca23ed5SHeinrich Schuchardt 	boottime->set_mem(&p.ip_udp.ip_dst, 4, 0xff);
1235ca23ed5SHeinrich Schuchardt 	p.ip_udp.ip_sum		= efi_ip_checksum(&p.ip_udp, IP_HDR_SIZE);
1245ca23ed5SHeinrich Schuchardt 
1255ca23ed5SHeinrich Schuchardt 	/*
1265ca23ed5SHeinrich Schuchardt 	 * Fill UDP header
1275ca23ed5SHeinrich Schuchardt 	 */
1285ca23ed5SHeinrich Schuchardt 	p.ip_udp.udp_src	= htons(68);
1295ca23ed5SHeinrich Schuchardt 	p.ip_udp.udp_dst	= htons(67);
1305ca23ed5SHeinrich Schuchardt 	p.ip_udp.udp_len	= htons(sizeof(struct dhcp) -
1315ca23ed5SHeinrich Schuchardt 					sizeof(struct ethernet_hdr) -
1325ca23ed5SHeinrich Schuchardt 					sizeof(struct ip_hdr));
1335ca23ed5SHeinrich Schuchardt 	/*
1345ca23ed5SHeinrich Schuchardt 	 * Fill DHCP header
1355ca23ed5SHeinrich Schuchardt 	 */
1365ca23ed5SHeinrich Schuchardt 	p.dhcp_hdr.op		= BOOTREQUEST;
1375ca23ed5SHeinrich Schuchardt 	p.dhcp_hdr.htype	= HWT_ETHER;
1385ca23ed5SHeinrich Schuchardt 	p.dhcp_hdr.hlen		= HWL_ETHER;
1395ca23ed5SHeinrich Schuchardt 	p.dhcp_hdr.flags	= htons(DHCP_FLAGS_UNICAST);
1405ca23ed5SHeinrich Schuchardt 	boottime->copy_mem(&p.dhcp_hdr.chaddr,
1415ca23ed5SHeinrich Schuchardt 			   &net->mode->current_address, ARP_HLEN);
1425ca23ed5SHeinrich Schuchardt 	/*
1435ca23ed5SHeinrich Schuchardt 	 * Fill options
1445ca23ed5SHeinrich Schuchardt 	 */
1455ca23ed5SHeinrich Schuchardt 	p.opt[0]	= 0x63; /* DHCP magic cookie */
1465ca23ed5SHeinrich Schuchardt 	p.opt[1]	= 0x82;
1475ca23ed5SHeinrich Schuchardt 	p.opt[2]	= 0x53;
1485ca23ed5SHeinrich Schuchardt 	p.opt[3]	= 0x63;
1495ca23ed5SHeinrich Schuchardt 	p.opt[4]	= DHCP_MESSAGE_TYPE;
1505ca23ed5SHeinrich Schuchardt 	p.opt[5]	= 0x01; /* length */
1515ca23ed5SHeinrich Schuchardt 	p.opt[6]	= DHCPDISCOVER;
1525ca23ed5SHeinrich Schuchardt 	p.opt[7]	= 0x39; /* maximum message size */
1535ca23ed5SHeinrich Schuchardt 	p.opt[8]	= 0x02; /* length */
1545ca23ed5SHeinrich Schuchardt 	p.opt[9]	= 0x02; /* 576 bytes */
1555ca23ed5SHeinrich Schuchardt 	p.opt[10]	= 0x40;
1565ca23ed5SHeinrich Schuchardt 	p.opt[11]	= 0xff; /* end of options */
1575ca23ed5SHeinrich Schuchardt 
1585ca23ed5SHeinrich Schuchardt 	/*
1595ca23ed5SHeinrich Schuchardt 	 * Transmit DHCPDISCOVER message.
1605ca23ed5SHeinrich Schuchardt 	 */
1615ca23ed5SHeinrich Schuchardt 	ret = net->transmit(net, 0, sizeof(struct dhcp), &p, NULL, NULL, 0);
1625ca23ed5SHeinrich Schuchardt 	if (ret != EFI_SUCCESS)
1635ca23ed5SHeinrich Schuchardt 		efi_st_error("Sending a DHCP request failed\n");
1645ca23ed5SHeinrich Schuchardt 	else
1655ca23ed5SHeinrich Schuchardt 		efi_st_printf("DHCP Discover\n");
1665ca23ed5SHeinrich Schuchardt 	return ret;
1675ca23ed5SHeinrich Schuchardt }
1685ca23ed5SHeinrich Schuchardt 
1695ca23ed5SHeinrich Schuchardt /*
1705ca23ed5SHeinrich Schuchardt  * Setup unit test.
1715ca23ed5SHeinrich Schuchardt  *
1725ca23ed5SHeinrich Schuchardt  * Create a 1 s periodic timer.
1735ca23ed5SHeinrich Schuchardt  * Start the network driver.
1745ca23ed5SHeinrich Schuchardt  *
1755ca23ed5SHeinrich Schuchardt  * @handle:	handle of the loaded image
1765ca23ed5SHeinrich Schuchardt  * @systable:	system table
1775ca23ed5SHeinrich Schuchardt  * @return:	EFI_ST_SUCCESS for success
1785ca23ed5SHeinrich Schuchardt  */
setup(const efi_handle_t handle,const struct efi_system_table * systable)1795ca23ed5SHeinrich Schuchardt static int setup(const efi_handle_t handle,
1805ca23ed5SHeinrich Schuchardt 		 const struct efi_system_table *systable)
1815ca23ed5SHeinrich Schuchardt {
1825ca23ed5SHeinrich Schuchardt 	efi_status_t ret;
1835ca23ed5SHeinrich Schuchardt 
1845ca23ed5SHeinrich Schuchardt 	boottime = systable->boottime;
1855ca23ed5SHeinrich Schuchardt 
1865ca23ed5SHeinrich Schuchardt 	/*
1875ca23ed5SHeinrich Schuchardt 	 * Create a timer event.
1885ca23ed5SHeinrich Schuchardt 	 */
1895ca23ed5SHeinrich Schuchardt 	ret = boottime->create_event(EVT_TIMER, TPL_CALLBACK, NULL, NULL,
1905ca23ed5SHeinrich Schuchardt 				     &timer);
1915ca23ed5SHeinrich Schuchardt 	if (ret != EFI_SUCCESS) {
1925ca23ed5SHeinrich Schuchardt 		efi_st_error("Failed to create event\n");
1935ca23ed5SHeinrich Schuchardt 		return EFI_ST_FAILURE;
1945ca23ed5SHeinrich Schuchardt 	}
1955ca23ed5SHeinrich Schuchardt 	/*
1965ca23ed5SHeinrich Schuchardt 	 * Set timer period to 1s.
1975ca23ed5SHeinrich Schuchardt 	 */
1985ca23ed5SHeinrich Schuchardt 	ret = boottime->set_timer(timer, EFI_TIMER_PERIODIC, 10000000);
1995ca23ed5SHeinrich Schuchardt 	if (ret != EFI_SUCCESS) {
200fdd04563SHeinrich Schuchardt 		efi_st_error("Failed to set timer\n");
2015ca23ed5SHeinrich Schuchardt 		return EFI_ST_FAILURE;
2025ca23ed5SHeinrich Schuchardt 	}
2035ca23ed5SHeinrich Schuchardt 	/*
2045ca23ed5SHeinrich Schuchardt 	 * Find an interface implementing the SNP protocol.
2055ca23ed5SHeinrich Schuchardt 	 */
2065ca23ed5SHeinrich Schuchardt 	ret = boottime->locate_protocol(&efi_net_guid, NULL, (void **)&net);
2075ca23ed5SHeinrich Schuchardt 	if (ret != EFI_SUCCESS) {
208fdd04563SHeinrich Schuchardt 		net = NULL;
2095ca23ed5SHeinrich Schuchardt 		efi_st_error("Failed to locate simple network protocol\n");
2105ca23ed5SHeinrich Schuchardt 		return EFI_ST_FAILURE;
2115ca23ed5SHeinrich Schuchardt 	}
2125ca23ed5SHeinrich Schuchardt 	/*
2135ca23ed5SHeinrich Schuchardt 	 * Check hardware address size.
2145ca23ed5SHeinrich Schuchardt 	 */
2155ca23ed5SHeinrich Schuchardt 	if (!net->mode) {
2165ca23ed5SHeinrich Schuchardt 		efi_st_error("Mode not provided\n");
2175ca23ed5SHeinrich Schuchardt 		return EFI_ST_FAILURE;
2185ca23ed5SHeinrich Schuchardt 	}
2195ca23ed5SHeinrich Schuchardt 	if (net->mode->hwaddr_size != ARP_HLEN) {
2205ca23ed5SHeinrich Schuchardt 		efi_st_error("HwAddressSize = %u, expected %u\n",
2215ca23ed5SHeinrich Schuchardt 			     net->mode->hwaddr_size, ARP_HLEN);
2225ca23ed5SHeinrich Schuchardt 		return EFI_ST_FAILURE;
2235ca23ed5SHeinrich Schuchardt 	}
2245ca23ed5SHeinrich Schuchardt 	/*
2255ca23ed5SHeinrich Schuchardt 	 * Check that WaitForPacket event exists.
2265ca23ed5SHeinrich Schuchardt 	 */
2275ca23ed5SHeinrich Schuchardt 	if (!net->wait_for_packet) {
2285ca23ed5SHeinrich Schuchardt 		efi_st_error("WaitForPacket event missing\n");
2295ca23ed5SHeinrich Schuchardt 		return EFI_ST_FAILURE;
2305ca23ed5SHeinrich Schuchardt 	}
2315ca23ed5SHeinrich Schuchardt 	/*
2320fdb9e30SHeinrich Schuchardt 	 * Start network adapter.
2330fdb9e30SHeinrich Schuchardt 	 */
2340fdb9e30SHeinrich Schuchardt 	ret = net->start(net);
2350fdb9e30SHeinrich Schuchardt 	if (ret != EFI_SUCCESS && ret != EFI_ALREADY_STARTED) {
2360fdb9e30SHeinrich Schuchardt 		efi_st_error("Failed to start network adapter\n");
2370fdb9e30SHeinrich Schuchardt 		return EFI_ST_FAILURE;
2380fdb9e30SHeinrich Schuchardt 	}
2390fdb9e30SHeinrich Schuchardt 	/*
2405ca23ed5SHeinrich Schuchardt 	 * Initialize network adapter.
2415ca23ed5SHeinrich Schuchardt 	 */
2425ca23ed5SHeinrich Schuchardt 	ret = net->initialize(net, 0, 0);
2435ca23ed5SHeinrich Schuchardt 	if (ret != EFI_SUCCESS) {
2445ca23ed5SHeinrich Schuchardt 		efi_st_error("Failed to initialize network adapter\n");
2455ca23ed5SHeinrich Schuchardt 		return EFI_ST_FAILURE;
2465ca23ed5SHeinrich Schuchardt 	}
2475ca23ed5SHeinrich Schuchardt 	return EFI_ST_SUCCESS;
2485ca23ed5SHeinrich Schuchardt }
2495ca23ed5SHeinrich Schuchardt 
2505ca23ed5SHeinrich Schuchardt /*
2515ca23ed5SHeinrich Schuchardt  * Execute unit test.
2525ca23ed5SHeinrich Schuchardt  *
2535ca23ed5SHeinrich Schuchardt  * A DHCP discover message is sent. The test is successful if a
2545ca23ed5SHeinrich Schuchardt  * DHCP reply is received within 10 seconds.
2555ca23ed5SHeinrich Schuchardt  *
2565ca23ed5SHeinrich Schuchardt  * @return:	EFI_ST_SUCCESS for success
2575ca23ed5SHeinrich Schuchardt  */
execute(void)2585ca23ed5SHeinrich Schuchardt static int execute(void)
2595ca23ed5SHeinrich Schuchardt {
2605ca23ed5SHeinrich Schuchardt 	efi_status_t ret;
2615ca23ed5SHeinrich Schuchardt 	struct efi_event *events[2];
262f5a2a938SHeinrich Schuchardt 	efi_uintn_t index;
2635ca23ed5SHeinrich Schuchardt 	union {
2645ca23ed5SHeinrich Schuchardt 		struct dhcp p;
2655ca23ed5SHeinrich Schuchardt 		u8 b[PKTSIZE];
2665ca23ed5SHeinrich Schuchardt 	} buffer;
2675ca23ed5SHeinrich Schuchardt 	struct efi_mac_address srcaddr;
2685ca23ed5SHeinrich Schuchardt 	struct efi_mac_address destaddr;
2695ca23ed5SHeinrich Schuchardt 	size_t buffer_size;
2705ca23ed5SHeinrich Schuchardt 	u8 *addr;
2715ca23ed5SHeinrich Schuchardt 	/*
2725ca23ed5SHeinrich Schuchardt 	 * The timeout is to occur after 10 s.
2735ca23ed5SHeinrich Schuchardt 	 */
2745ca23ed5SHeinrich Schuchardt 	unsigned int timeout = 10;
2755ca23ed5SHeinrich Schuchardt 
276fdd04563SHeinrich Schuchardt 	/* Setup may have failed */
277fdd04563SHeinrich Schuchardt 	if (!net || !timer) {
278fdd04563SHeinrich Schuchardt 		efi_st_error("Cannot execute test after setup failure\n");
279fdd04563SHeinrich Schuchardt 		return EFI_ST_FAILURE;
280fdd04563SHeinrich Schuchardt 	}
281fdd04563SHeinrich Schuchardt 
2825ca23ed5SHeinrich Schuchardt 	/*
2835ca23ed5SHeinrich Schuchardt 	 * Send DHCP discover message
2845ca23ed5SHeinrich Schuchardt 	 */
2855ca23ed5SHeinrich Schuchardt 	ret = send_dhcp_discover();
2865ca23ed5SHeinrich Schuchardt 	if (ret != EFI_SUCCESS)
2875ca23ed5SHeinrich Schuchardt 		return EFI_ST_FAILURE;
2885ca23ed5SHeinrich Schuchardt 
2895ca23ed5SHeinrich Schuchardt 	/*
2905ca23ed5SHeinrich Schuchardt 	 * If we would call WaitForEvent only with the WaitForPacket event,
2915ca23ed5SHeinrich Schuchardt 	 * our code would block until a packet is received which might never
2925ca23ed5SHeinrich Schuchardt 	 * occur. By calling WaitFor event with both a timer event and the
2935ca23ed5SHeinrich Schuchardt 	 * WaitForPacket event we can escape this blocking situation.
2945ca23ed5SHeinrich Schuchardt 	 *
2955ca23ed5SHeinrich Schuchardt 	 * If the timer event occurs before we have received a DHCP reply
2965ca23ed5SHeinrich Schuchardt 	 * a further DHCP discover message is sent.
2975ca23ed5SHeinrich Schuchardt 	 */
2985ca23ed5SHeinrich Schuchardt 	events[0] = timer;
2995ca23ed5SHeinrich Schuchardt 	events[1] = net->wait_for_packet;
3005ca23ed5SHeinrich Schuchardt 	for (;;) {
3015ca23ed5SHeinrich Schuchardt 		/*
3025ca23ed5SHeinrich Schuchardt 		 * Wait for packet to be received or timer event.
3035ca23ed5SHeinrich Schuchardt 		 */
3045ca23ed5SHeinrich Schuchardt 		boottime->wait_for_event(2, events, &index);
3055ca23ed5SHeinrich Schuchardt 		if (index == 0) {
3065ca23ed5SHeinrich Schuchardt 			/*
3075ca23ed5SHeinrich Schuchardt 			 * The timer event occurred. Check for timeout.
3085ca23ed5SHeinrich Schuchardt 			 */
3095ca23ed5SHeinrich Schuchardt 			--timeout;
3105ca23ed5SHeinrich Schuchardt 			if (!timeout) {
3115ca23ed5SHeinrich Schuchardt 				efi_st_error("Timeout occurred\n");
3125ca23ed5SHeinrich Schuchardt 				return EFI_ST_FAILURE;
3135ca23ed5SHeinrich Schuchardt 			}
3145ca23ed5SHeinrich Schuchardt 			/*
3155ca23ed5SHeinrich Schuchardt 			 * Send further DHCP discover message
3165ca23ed5SHeinrich Schuchardt 			 */
3175ca23ed5SHeinrich Schuchardt 			ret = send_dhcp_discover();
3185ca23ed5SHeinrich Schuchardt 			if (ret != EFI_SUCCESS)
3195ca23ed5SHeinrich Schuchardt 				return EFI_ST_FAILURE;
3205ca23ed5SHeinrich Schuchardt 			continue;
3215ca23ed5SHeinrich Schuchardt 		}
3225ca23ed5SHeinrich Schuchardt 		/*
3235ca23ed5SHeinrich Schuchardt 		 * Receive packet
3245ca23ed5SHeinrich Schuchardt 		 */
3255ca23ed5SHeinrich Schuchardt 		buffer_size = sizeof(buffer);
3265ca23ed5SHeinrich Schuchardt 		net->receive(net, NULL, &buffer_size, &buffer,
3275ca23ed5SHeinrich Schuchardt 			     &srcaddr, &destaddr, NULL);
3285ca23ed5SHeinrich Schuchardt 		if (ret != EFI_SUCCESS) {
3295ca23ed5SHeinrich Schuchardt 			efi_st_error("Failed to receive packet");
3305ca23ed5SHeinrich Schuchardt 			return EFI_ST_FAILURE;
3315ca23ed5SHeinrich Schuchardt 		}
3325ca23ed5SHeinrich Schuchardt 		/*
3335ca23ed5SHeinrich Schuchardt 		 * Check the packet is meant for this system.
3345ca23ed5SHeinrich Schuchardt 		 * Unfortunately QEMU ignores the broadcast flag.
3355ca23ed5SHeinrich Schuchardt 		 * So we have to check for broadcasts too.
3365ca23ed5SHeinrich Schuchardt 		 */
3375ca23ed5SHeinrich Schuchardt 		if (efi_st_memcmp(&destaddr, &net->mode->current_address,
3385ca23ed5SHeinrich Schuchardt 				  ARP_HLEN) &&
3395ca23ed5SHeinrich Schuchardt 		    efi_st_memcmp(&destaddr, BROADCAST_MAC, ARP_HLEN))
3405ca23ed5SHeinrich Schuchardt 			continue;
3415ca23ed5SHeinrich Schuchardt 		/*
3425ca23ed5SHeinrich Schuchardt 		 * Check this is a DHCP reply
3435ca23ed5SHeinrich Schuchardt 		 */
3445ca23ed5SHeinrich Schuchardt 		if (buffer.p.eth_hdr.et_protlen != ntohs(PROT_IP) ||
3455ca23ed5SHeinrich Schuchardt 		    buffer.p.ip_udp.ip_hl_v != 0x45 ||
3465ca23ed5SHeinrich Schuchardt 		    buffer.p.ip_udp.ip_p != IPPROTO_UDP ||
3475ca23ed5SHeinrich Schuchardt 		    buffer.p.ip_udp.udp_src != ntohs(67) ||
3485ca23ed5SHeinrich Schuchardt 		    buffer.p.ip_udp.udp_dst != ntohs(68) ||
3495ca23ed5SHeinrich Schuchardt 		    buffer.p.dhcp_hdr.op != BOOTREPLY)
3505ca23ed5SHeinrich Schuchardt 			continue;
3515ca23ed5SHeinrich Schuchardt 		/*
3525ca23ed5SHeinrich Schuchardt 		 * We successfully received a DHCP reply.
3535ca23ed5SHeinrich Schuchardt 		 */
3545ca23ed5SHeinrich Schuchardt 		break;
3555ca23ed5SHeinrich Schuchardt 	}
3565ca23ed5SHeinrich Schuchardt 
3575ca23ed5SHeinrich Schuchardt 	/*
3585ca23ed5SHeinrich Schuchardt 	 * Write a log message.
3595ca23ed5SHeinrich Schuchardt 	 */
3605ca23ed5SHeinrich Schuchardt 	addr = (u8 *)&buffer.p.ip_udp.ip_src;
3615ca23ed5SHeinrich Schuchardt 	efi_st_printf("DHCP reply received from %u.%u.%u.%u (%pm) ",
3625ca23ed5SHeinrich Schuchardt 		      addr[0], addr[1], addr[2], addr[3], &srcaddr);
3635ca23ed5SHeinrich Schuchardt 	if (!efi_st_memcmp(&destaddr, BROADCAST_MAC, ARP_HLEN))
3645ca23ed5SHeinrich Schuchardt 		efi_st_printf("as broadcast message.\n");
3655ca23ed5SHeinrich Schuchardt 	else
3665ca23ed5SHeinrich Schuchardt 		efi_st_printf("as unicast message.\n");
3675ca23ed5SHeinrich Schuchardt 
3685ca23ed5SHeinrich Schuchardt 	return EFI_ST_SUCCESS;
3695ca23ed5SHeinrich Schuchardt }
3705ca23ed5SHeinrich Schuchardt 
3715ca23ed5SHeinrich Schuchardt /*
3725ca23ed5SHeinrich Schuchardt  * Tear down unit test.
3735ca23ed5SHeinrich Schuchardt  *
3745ca23ed5SHeinrich Schuchardt  * Close the timer event created in setup.
3755ca23ed5SHeinrich Schuchardt  * Shut down the network adapter.
3765ca23ed5SHeinrich Schuchardt  *
3775ca23ed5SHeinrich Schuchardt  * @return:	EFI_ST_SUCCESS for success
3785ca23ed5SHeinrich Schuchardt  */
teardown(void)3795ca23ed5SHeinrich Schuchardt static int teardown(void)
3805ca23ed5SHeinrich Schuchardt {
3815ca23ed5SHeinrich Schuchardt 	efi_status_t ret;
3825ca23ed5SHeinrich Schuchardt 	int exit_status = EFI_ST_SUCCESS;
3835ca23ed5SHeinrich Schuchardt 
3845ca23ed5SHeinrich Schuchardt 	if (timer) {
3855ca23ed5SHeinrich Schuchardt 		/*
3865ca23ed5SHeinrich Schuchardt 		 * Stop timer.
3875ca23ed5SHeinrich Schuchardt 		 */
3885ca23ed5SHeinrich Schuchardt 		ret = boottime->set_timer(timer, EFI_TIMER_STOP, 0);
3895ca23ed5SHeinrich Schuchardt 		if (ret != EFI_SUCCESS) {
3905ca23ed5SHeinrich Schuchardt 			efi_st_error("Failed to stop timer");
3915ca23ed5SHeinrich Schuchardt 			exit_status = EFI_ST_FAILURE;
3925ca23ed5SHeinrich Schuchardt 		}
3935ca23ed5SHeinrich Schuchardt 		/*
3945ca23ed5SHeinrich Schuchardt 		 * Close timer event.
3955ca23ed5SHeinrich Schuchardt 		 */
3965ca23ed5SHeinrich Schuchardt 		ret = boottime->close_event(timer);
3975ca23ed5SHeinrich Schuchardt 		if (ret != EFI_SUCCESS) {
3985ca23ed5SHeinrich Schuchardt 			efi_st_error("Failed to close event");
3995ca23ed5SHeinrich Schuchardt 			exit_status = EFI_ST_FAILURE;
4005ca23ed5SHeinrich Schuchardt 		}
4015ca23ed5SHeinrich Schuchardt 	}
4025ca23ed5SHeinrich Schuchardt 	if (net) {
4035ca23ed5SHeinrich Schuchardt 		/*
4045ca23ed5SHeinrich Schuchardt 		 * Stop network adapter.
4055ca23ed5SHeinrich Schuchardt 		 */
4065ca23ed5SHeinrich Schuchardt 		ret = net->stop(net);
4075ca23ed5SHeinrich Schuchardt 		if (ret != EFI_SUCCESS) {
4085ca23ed5SHeinrich Schuchardt 			efi_st_error("Failed to stop network adapter\n");
4095ca23ed5SHeinrich Schuchardt 			exit_status = EFI_ST_FAILURE;
4105ca23ed5SHeinrich Schuchardt 		}
4115ca23ed5SHeinrich Schuchardt 		/*
4125ca23ed5SHeinrich Schuchardt 		 * Shut down network adapter.
4135ca23ed5SHeinrich Schuchardt 		 */
4145ca23ed5SHeinrich Schuchardt 		ret = net->shutdown(net);
4155ca23ed5SHeinrich Schuchardt 		if (ret != EFI_SUCCESS) {
4165ca23ed5SHeinrich Schuchardt 			efi_st_error("Failed to shut down network adapter\n");
4175ca23ed5SHeinrich Schuchardt 			exit_status = EFI_ST_FAILURE;
4185ca23ed5SHeinrich Schuchardt 		}
4195ca23ed5SHeinrich Schuchardt 	}
4205ca23ed5SHeinrich Schuchardt 
4215ca23ed5SHeinrich Schuchardt 	return exit_status;
4225ca23ed5SHeinrich Schuchardt }
4235ca23ed5SHeinrich Schuchardt 
4245ca23ed5SHeinrich Schuchardt EFI_UNIT_TEST(snp) = {
4255ca23ed5SHeinrich Schuchardt 	.name = "simple network protocol",
4265ca23ed5SHeinrich Schuchardt 	.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
4275ca23ed5SHeinrich Schuchardt 	.setup = setup,
4285ca23ed5SHeinrich Schuchardt 	.execute = execute,
4295ca23ed5SHeinrich Schuchardt 	.teardown = teardown,
430*8a42641aSHeinrich Schuchardt #ifdef CONFIG_SANDBOX
431*8a42641aSHeinrich Schuchardt 	/*
432*8a42641aSHeinrich Schuchardt 	 * Running this test on the sandbox requires setting environment
433*8a42641aSHeinrich Schuchardt 	 * variable ethact to a network interface connected to a DHCP server and
434*8a42641aSHeinrich Schuchardt 	 * ethrotate to 'no'.
435*8a42641aSHeinrich Schuchardt 	 */
436*8a42641aSHeinrich Schuchardt 	.on_request = true,
437*8a42641aSHeinrich Schuchardt #endif
4385ca23ed5SHeinrich Schuchardt };
439