From patchwork Sun Sep 24 19:57:16 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Heinrich Schuchardt X-Patchwork-Id: 817977 X-Patchwork-Delegate: agraf@suse.de Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 3y0dT86gnVz9t3R for ; Mon, 25 Sep 2017 06:03:56 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id 89B47C220FC; Sun, 24 Sep 2017 20:01:35 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=-0.7 required=5.0 tests=FREEMAIL_FROM, RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H2 autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id AAD69C2213F; Sun, 24 Sep 2017 19:58:50 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 8B7F0C22141; Sun, 24 Sep 2017 19:58:21 +0000 (UTC) Received: from mout.gmx.net (mout.gmx.net [212.227.17.22]) by lists.denx.de (Postfix) with ESMTPS id E2C01C220F3 for ; Sun, 24 Sep 2017 19:58:17 +0000 (UTC) Received: from workstation4.fritz.box ([84.118.154.110]) by mail.gmx.com (mrgmx102 [212.227.17.174]) with ESMTPSA (Nemesis) id 0LnUna-1dR09A2pG2-00hf8y; Sun, 24 Sep 2017 21:57:34 +0200 From: Heinrich Schuchardt To: Alexander Graf Date: Sun, 24 Sep 2017 21:57:16 +0200 Message-Id: <20170924195716.3433-18-xypron.glpk@gmx.de> X-Mailer: git-send-email 2.14.1 In-Reply-To: <20170924195716.3433-1-xypron.glpk@gmx.de> References: <20170924195716.3433-1-xypron.glpk@gmx.de> X-Provags-ID: V03:K0:FndgS0nGPFX+/1qgWofcfTLSdUQCSALt6xErLwkm0YiWiqTBXGr i4cqRUBGgV/7eJLCRhdB1VUz3BkW1ByQQw7+Pzu6tt0BHydX9NlFWCyj2FQ7SHx2k3B0Od8 tfcjwyHP+mrIJN0jOehE2GJOC00lBLOe0Z1OgL8d7LCIjo6+6xuVmm3ND64OBZtGwx2ZZ/d SlUAQAuVQpuUwoFKmsXqw== X-UI-Out-Filterresults: notjunk:1; V01:K0:AeI6eVIf2ek=:la2KIWSw+hV3d74IfaB2xm E/LtpHEKjf0GrOtnfMOBl0yN5i7nFn71qys+sR8EE8t4e9IgYbytIQQUMQlvqaWfTdnnuz155 jKaRMo1VjVIrPflysBnUh/Fb7SAcCYKp1b9/WRfaKTpExj5rtq96yCf3W7z2jNUWU8+7+VA1o FKXAHs0FWtdZFrWCTOIcPjYSm+wH78R7I8F8WYKpwyI4KdgPL1m6u2TMvCKxE+ckOn03PQ9++ cMw+8EscB8LtL1DdVkMoNerJzCAk+Rl8sEWCKhq+rm4hqDQIAvJeqGlNnPzZH7uX8PcLlu2v4 K0RgGGKeujNzas+00t3X+0UxV0gLKeR02a6fq3I5RuLqC8+iOBDcSdU1pwJ7I7y9kjuI6O7sA DXcZ0l+hnHXniRrdIy7iVJelFeQjEu9jEgSJ1UyiGhcMlfM4OVnUHgbPelTUgxr+MedeMjMxh cZOQBy05m3Sth3h74JBpdBopbNKHuotGf3Lg6yr6qQaWgGCbYerrqcih/S/pPvGffxVI9wUMr h7Xhr6hrvLZ73AzYYRSZ69nqATryjRbSBqwdvRwnlaIFQruk4Xaqy/v+tMEmv6lhQhvlBQXPz mNYQZoBV3tZkLWqSFohCX/xwky2tJtaaVaAWD9/mAmhmrF6xPNYHMUTERSuD/8JsMQfR6gaS1 0I2PFZQq0T15HL6XmTvZXplilA20xac/+yctIZ9eD6/Z87Z3OsEetxrkC9RI5vCmvra7uXdrK yrFQ5J0o2bVL2R89ZjidPXJZzCdDGLaqfvHlR+pQk1+ifLNCoV+8dRDwhPrZeuFPillFMLjri LROLSS+Gkl80ur9yjlirCoQELX/yg== Cc: Heinrich Schuchardt , u-boot@lists.denx.de Subject: [U-Boot] [PATCH 17/17] efi_loader: supply EFI network test X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" This patch provices an EFI application to check the correct function of the Simple Network Protocol implementation. It sends a DHCP request and analyzes the DHCP offer. Different error conditions including a 10s timeout are checked. A successful execution will look like this: => bootefi nettest Scanning disk ide.blk#0... Found 1 disks WARNING: Invalid device tree, expect boot to fail Network test DHCP Discover DHCP reply received from 192.168.76.2 (52:55:c0:a8:4c:02) as broadcast message. OK. The test was completed successfully. Signed-off-by: Heinrich Schuchardt --- lib/efi_selftest/Makefile | 3 + lib/efi_selftest/efi_selftest_snp.c | 442 ++++++++++++++++++++++++++++++++++++ 2 files changed, 445 insertions(+) create mode 100644 lib/efi_selftest/efi_selftest_snp.c diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index 30f1960933..9bfecf357b 100644 --- a/lib/efi_selftest/Makefile +++ b/lib/efi_selftest/Makefile @@ -15,6 +15,8 @@ CFLAGS_efi_selftest_events.o := $(CFLAGS_EFI) CFLAGS_REMOVE_efi_selftest_events.o := $(CFLAGS_NON_EFI) CFLAGS_efi_selftest_exitbootservices.o := $(CFLAGS_EFI) CFLAGS_REMOVE_efi_selftest_exitbootservices.o := $(CFLAGS_NON_EFI) +CFLAGS_efi_selftest_snp.o := $(CFLAGS_EFI) +CFLAGS_REMOVE_efi_selftest_snp.o := $(CFLAGS_NON_EFI) CFLAGS_efi_selftest_tpl.o := $(CFLAGS_EFI) CFLAGS_REMOVE_efi_selftest_tpl.o := $(CFLAGS_NON_EFI) @@ -23,4 +25,5 @@ efi_selftest.o \ efi_selftest_console.o \ efi_selftest_events.o \ efi_selftest_exitbootservices.o \ +efi_selftest_snp.o \ efi_selftest_tpl.o diff --git a/lib/efi_selftest/efi_selftest_snp.c b/lib/efi_selftest/efi_selftest_snp.c new file mode 100644 index 0000000000..7bb81208ad --- /dev/null +++ b/lib/efi_selftest/efi_selftest_snp.c @@ -0,0 +1,442 @@ +/* + * efi_selftest_snp + * + * Copyright (c) 2017 Heinrich Schuchardt + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This unit test covers the Simple Network Protocol as well as + * the CopyMem and SetMem boottime services. + * + * A DHCP discover message is sent. The test is successful if a + * DHCP reply is received. + * + * TODO: Once ConnectController and DisconnectController are implemented + * we should connect our code as controller. + */ + +#include + +/* + * MAC address for broadcasts + */ +static const u8 BROADCAST_MAC[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + +struct dhcp_hdr { + u8 op; +#define BOOTREQUEST 1 +#define BOOTREPLY 2 + u8 htype; +# define HWT_ETHER 1 + u8 hlen; +# define HWL_ETHER 6 + u8 hops; + u32 xid; + u16 secs; + u16 flags; +#define DHCP_FLAGS_UNICAST 0x0000 +#define DHCP_FLAGS_BROADCAST 0x0080 + u32 ciaddr; + u32 yiaddr; + u32 siaddr; + u32 giaddr; + u8 chaddr[16]; + u8 sname[64]; + u8 file[128]; +}; + +/* + * Message type option. + */ +#define DHCP_MESSAGE_TYPE 0x35 +#define DHCPDISCOVER 1 +#define DHCPOFFER 2 +#define DHCPREQUEST 3 +#define DHCPDECLINE 4 +#define DHCPACK 5 +#define DHCPNAK 6 +#define DHCPRELEASE 7 + +struct dhcp { + struct ethernet_hdr eth_hdr; + struct ip_udp_hdr ip_udp; + struct dhcp_hdr dhcp_hdr; + u8 opt[128]; +} __packed; + +static struct efi_boot_services *boottime; +static struct efi_simple_network *net; +static struct efi_event *timer; +static const efi_guid_t efi_net_guid = EFI_SIMPLE_NETWORK_GUID; +/* IP packet ID */ +static unsigned int net_ip_id; + +/* + * Compare memory. + * We cannot use lib/string.c due to different CFLAGS values. + * + * Returns 0 if both buffers contain the same bytes. + * + * @buf1 first buffer + * @buf2 second buffer + * @length number of bytes to compare + */ +static int efi_st_memcmp(const void *buf1, const void *buf2, size_t length) +{ + const u8 *pos1 = buf1; + const u8 *pos2 = buf2; + + for (; length; --length) { + if (*pos1 != *pos2) + return pos1 - pos2; + ++pos1; + ++pos2; + } + return 0; +} + +/* + * Compute the checksum of the IP header. We cover even values of length only. + * We cannot use net/checksum.c due to different CFLAGS values. + * + * @buf IP header + * @len length of header in bytes + */ +static unsigned int efi_ip_checksum(const void *buf, size_t len) +{ + size_t i; + u32 sum = 0; + const u16 *pos = buf; + + for (i = 0; i < len; i += 2) + sum += *pos++; + + sum = (sum >> 16) + (sum & 0xffff); + sum += sum >> 16; + sum = ~sum & 0xffff; + + return sum; +} + +/* + * Transmit a DHCPDISCOVER message. + */ +static efi_status_t send_dhcp_discover(void) +{ + efi_status_t ret; + struct dhcp p = {}; + + /* + * Fill ethernet header + */ + boottime->copy_mem(p.eth_hdr.et_dest, (void *)BROADCAST_MAC, ARP_HLEN); + boottime->copy_mem(p.eth_hdr.et_src, &net->mode->current_address, + ARP_HLEN); + p.eth_hdr.et_protlen = htons(PROT_IP); + /* + * Fill IP header + */ + p.ip_udp.ip_hl_v = 0x45; + p.ip_udp.ip_len = htons(sizeof(struct dhcp) - + sizeof(struct ethernet_hdr)); + p.ip_udp.ip_id = htons(++net_ip_id); + p.ip_udp.ip_off = htons(IP_FLAGS_DFRAG); + p.ip_udp.ip_ttl = 0xff; /* time to live */ + p.ip_udp.ip_p = IPPROTO_UDP; + boottime->set_mem(&p.ip_udp.ip_dst, 4, 0xff); + p.ip_udp.ip_sum = efi_ip_checksum(&p.ip_udp, IP_HDR_SIZE); + + /* + * Fill UDP header + */ + p.ip_udp.udp_src = htons(68); + p.ip_udp.udp_dst = htons(67); + p.ip_udp.udp_len = htons(sizeof(struct dhcp) - + sizeof(struct ethernet_hdr) - + sizeof(struct ip_hdr)); + /* + * Fill DHCP header + */ + p.dhcp_hdr.op = BOOTREQUEST; + p.dhcp_hdr.htype = HWT_ETHER; + p.dhcp_hdr.hlen = HWL_ETHER; + p.dhcp_hdr.flags = htons(DHCP_FLAGS_UNICAST); + boottime->copy_mem(&p.dhcp_hdr.chaddr, + &net->mode->current_address, ARP_HLEN); + /* + * Fill options + */ + p.opt[0] = 0x63; /* DHCP magic cookie */ + p.opt[1] = 0x82; + p.opt[2] = 0x53; + p.opt[3] = 0x63; + p.opt[4] = DHCP_MESSAGE_TYPE; + p.opt[5] = 0x01; /* length */ + p.opt[6] = DHCPDISCOVER; + p.opt[7] = 0x39; /* maximum message size */ + p.opt[8] = 0x02; /* length */ + p.opt[9] = 0x02; /* 576 bytes */ + p.opt[10] = 0x40; + p.opt[11] = 0xff; /* end of options */ + + /* + * Transmit DHCPDISCOVER message. + */ + ret = net->transmit(net, 0, sizeof(struct dhcp), &p, NULL, NULL, 0); + if (ret != EFI_SUCCESS) + efi_st_error("Sending a DHCP request failed\n"); + else + efi_st_printf("DHCP Discover\n"); + return ret; +} + +/* + * Setup unit test. + * + * Create a 1 s periodic timer. + * Start the network driver. + * + * @handle: handle of the loaded image + * @systable: system table + */ +static int setup(const efi_handle_t handle, + const struct efi_system_table *systable) +{ + efi_status_t ret; + + boottime = systable->boottime; + + /* + * Create a timer event. + */ + ret = boottime->create_event(EVT_TIMER, TPL_CALLBACK, NULL, NULL, + &timer); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to create event\n"); + return 1; + } + /* + * Set timer period to 1s. + */ + ret = boottime->set_timer(timer, EFI_TIMER_PERIODIC, 10000000); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to locate simple network protocol\n"); + return 1; + } + /* + * Find an interface implementing the SNP protocol. + */ + ret = boottime->locate_protocol(&efi_net_guid, NULL, (void **)&net); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to locate simple network protocol\n"); + return 1; + } + /* + * Check hardware address size. + */ + if (!net->mode) { + efi_st_error("Mode not provided\n"); + return 1; + } + if (net->mode->hwaddr_size != ARP_HLEN) { + efi_st_error("HwAddressSize = %u, expected %u\n", + net->mode->hwaddr_size, ARP_HLEN); + return 1; + } + /* + * Check that WaitForPacket event exists. + */ + if (!net->wait_for_packet) { + efi_st_error("WaitForPacket event missing\n"); + return 1; + } + /* + * Initialize network adapter. + */ + ret = net->initialize(net, 0, 0); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to initialize network adapter\n"); + return 1; + } + /* + * Start network adapter. + */ + ret = net->start(net); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to start network adapter\n"); + return 1; + } + return 0; +} + +/* + * Execute unit test. + * + * A DHCP discover message is sent. The test is successful if a + * DHCP reply is received within 10 seconds. + */ +static int execute(void) +{ + efi_status_t ret; + struct efi_event *events[2]; + size_t index; + union { + struct dhcp p; + u8 b[PKTSIZE]; + } buffer; + struct efi_mac_address srcaddr; + struct efi_mac_address destaddr; + size_t buffer_size; + u8 *addr; + /* + * The timeout is to occur after 10 s. + */ + unsigned int timeout = 10; + + /* + * Send DHCP discover message + */ + ret = send_dhcp_discover(); + if (ret != EFI_SUCCESS) + return 1; + + /* + * If we would call WaitForEvent only with the WaitForPacket event, + * our code would block until a packet is received which might never + * occur. By calling WaitFor event with both a timer event and the + * WaitForPacket event we can escape this blocking situation. + * + * If the timer event occurs before we have received a DHCP reply + * a further DHCP discover message is sent. + */ + events[0] = timer; + events[1] = net->wait_for_packet; + for (;;) { + /* + * Wait for packet to be received or timer event. + */ + boottime->wait_for_event(2, events, &index); + if (index == 0) { + /* + * The timer event occurred. Check for timeout. + */ + --timeout; + if (!timeout) { + efi_st_error("Timeout occurred\n"); + return 1; + } + /* + * Send further DHCP discover message + */ + ret = send_dhcp_discover(); + if (ret != EFI_SUCCESS) + return 1; + continue; + } + /* + * Receive packet + */ + buffer_size = sizeof(buffer); + net->receive(net, NULL, &buffer_size, &buffer, + &srcaddr, &destaddr, NULL); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to receive packet"); + return 1; + } + /* + * Check the packet is meant for this system. + * Unfortunately QEMU ignores the broadcast flag. + * So we have to check for broadcasts too. + */ + if (efi_st_memcmp(&destaddr, &net->mode->current_address, + ARP_HLEN) && + efi_st_memcmp(&destaddr, BROADCAST_MAC, ARP_HLEN)) + continue; + /* + * Check this is a DHCP reply + */ + if (buffer.p.eth_hdr.et_protlen != ntohs(PROT_IP) || + buffer.p.ip_udp.ip_hl_v != 0x45 || + buffer.p.ip_udp.ip_p != IPPROTO_UDP || + buffer.p.ip_udp.udp_src != ntohs(67) || + buffer.p.ip_udp.udp_dst != ntohs(68) || + buffer.p.dhcp_hdr.op != BOOTREPLY) + continue; + /* + * We successfully received a DHCP reply. + */ + break; + } + + /* + * Write a log message. + */ + addr = (u8 *)&buffer.p.ip_udp.ip_src; + efi_st_printf("DHCP reply received from %u.%u.%u.%u (%pm) ", + addr[0], addr[1], addr[2], addr[3], &srcaddr); + if (!efi_st_memcmp(&destaddr, BROADCAST_MAC, ARP_HLEN)) + efi_st_printf("as broadcast message.\n"); + else + efi_st_printf("as unicast message.\n"); + + return 0; +} + +/* + * Tear down unit test. + * + * Close the timer event created in setup. + * Shut down the network adapter. + */ +static int teardown(void) +{ + efi_status_t ret; + int exit_status = 0; + + if (timer) { + /* + * Stop timer. + */ + ret = boottime->set_timer(timer, EFI_TIMER_STOP, 0); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to stop timer"); + exit_status = 1; + } + /* + * Close timer event. + */ + ret = boottime->close_event(timer); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to close event"); + exit_status = 1; + } + } + if (net) { + /* + * Stop network adapter. + */ + ret = net->stop(net); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to stop network adapter\n"); + exit_status = 1; + } + /* + * Shut down network adapter. + */ + ret = net->shutdown(net); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to shut down network adapter\n"); + exit_status = 1; + } + } + + return exit_status; +} + +EFI_UNIT_TEST(snp) = { + .name = "simple network protocol", + .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, + .setup = setup, + .execute = execute, + .teardown = teardown, +};