summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile1
-rw-r--r--lib/efi_selftest/Kconfig7
-rw-r--r--lib/efi_selftest/Makefile17
-rw-r--r--lib/efi_selftest/efi_selftest.c219
-rw-r--r--lib/efi_selftest/efi_selftest_console.c187
5 files changed, 431 insertions, 0 deletions
diff --git a/lib/Makefile b/lib/Makefile
index faf4538fb5..8e1c9d1bb7 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -9,6 +9,7 @@ ifndef CONFIG_SPL_BUILD
obj-$(CONFIG_EFI) += efi/
obj-$(CONFIG_EFI_LOADER) += efi_loader/
+obj-$(CONFIG_EFI_LOADER) += efi_selftest/
obj-$(CONFIG_LZMA) += lzma/
obj-$(CONFIG_LZO) += lzo/
obj-$(CONFIG_BZIP2) += bzip2/
diff --git a/lib/efi_selftest/Kconfig b/lib/efi_selftest/Kconfig
new file mode 100644
index 0000000000..3b5f3a1230
--- /dev/null
+++ b/lib/efi_selftest/Kconfig
@@ -0,0 +1,7 @@
+config CMD_BOOTEFI_SELFTEST
+ bool "Allow booting an EFI efi_selftest"
+ depends on CMD_BOOTEFI
+ help
+ This adds an EFI test application to U-Boot that can be executed
+ with the 'bootefi selftest' command. It provides extended tests of
+ the EFI API implementation.
diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile
new file mode 100644
index 0000000000..34f5ff1838
--- /dev/null
+++ b/lib/efi_selftest/Makefile
@@ -0,0 +1,17 @@
+:
+# (C) Copyright 2017, Heinrich Schuchardt <xypron.glpk@gmx.de>
+#
+# SPDX-License-Identifier: GPL-2.0+
+#
+
+# This file only gets included with CONFIG_EFI_LOADER set, so all
+# object inclusion implicitly depends on it
+
+CFLAGS_efi_selftest.o := $(CFLAGS_EFI)
+CFLAGS_REMOVE_efi_selftest.o := $(CFLAGS_NON_EFI)
+CFLAGS_efi_selftest_console.o := $(CFLAGS_EFI)
+CFLAGS_REMOVE_efi_selftest_console.o := $(CFLAGS_NON_EFI)
+
+obj-$(CONFIG_CMD_BOOTEFI_SELFTEST) += \
+efi_selftest.o \
+efi_selftest_console.o
diff --git a/lib/efi_selftest/efi_selftest.c b/lib/efi_selftest/efi_selftest.c
new file mode 100644
index 0000000000..efec832e98
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest.c
@@ -0,0 +1,219 @@
+/*
+ * EFI efi_selftest
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <efi_selftest.h>
+#include <vsprintf.h>
+
+static const struct efi_system_table *systable;
+static const struct efi_boot_services *boottime;
+static const struct efi_runtime_services *runtime;
+static efi_handle_t handle;
+static u16 reset_message[] = L"Selftest completed";
+
+/*
+ * Exit the boot services.
+ *
+ * The size of the memory map is determined.
+ * Pool memory is allocated to copy the memory map.
+ * The memory amp is copied and the map key is obtained.
+ * The map key is used to exit the boot services.
+ */
+void efi_st_exit_boot_services(void)
+{
+ unsigned long map_size = 0;
+ unsigned long map_key;
+ unsigned long desc_size;
+ u32 desc_version;
+ efi_status_t ret;
+ struct efi_mem_desc *memory_map;
+
+ ret = boottime->get_memory_map(&map_size, NULL, &map_key, &desc_size,
+ &desc_version);
+ if (ret != EFI_BUFFER_TOO_SMALL) {
+ efi_st_printf("ERROR: GetMemoryMap did not return "
+ "EFI_BUFFER_TOO_SMALL\n");
+ return;
+ }
+ /* Allocate extra space for newly allocated memory */
+ map_size += sizeof(struct efi_mem_desc);
+ ret = boottime->allocate_pool(EFI_BOOT_SERVICES_DATA, map_size,
+ (void **)&memory_map);
+ if (ret != EFI_SUCCESS) {
+ efi_st_printf("ERROR: AllocatePool did not return "
+ "EFI_SUCCESS\n");
+ return;
+ }
+ ret = boottime->get_memory_map(&map_size, memory_map, &map_key,
+ &desc_size, &desc_version);
+ if (ret != EFI_SUCCESS) {
+ efi_st_printf("ERROR: GetMemoryMap did not return "
+ "EFI_SUCCESS\n");
+ return;
+ }
+ ret = boottime->exit_boot_services(handle, map_key);
+ if (ret != EFI_SUCCESS) {
+ efi_st_printf("ERROR: ExitBootServices did not return "
+ "EFI_SUCCESS\n");
+ return;
+ }
+ efi_st_printf("\nBoot services terminated\n");
+}
+
+/*
+ * Set up a test.
+ *
+ * @test the test to be executed
+ * @failures counter that will be incremented if a failure occurs
+ */
+static int setup(struct efi_unit_test *test, unsigned int *failures)
+{
+ int ret;
+
+ if (!test->setup)
+ return 0;
+ efi_st_printf("\nSetting up '%s'\n", test->name);
+ ret = test->setup(handle, systable);
+ if (ret) {
+ efi_st_printf("ERROR: Setting up '%s' failed\n", test->name);
+ ++*failures;
+ } else {
+ efi_st_printf("Setting up '%s' succeeded\n", test->name);
+ }
+ return ret;
+}
+
+/*
+ * Execute a test.
+ *
+ * @test the test to be executed
+ * @failures counter that will be incremented if a failure occurs
+ */
+static int execute(struct efi_unit_test *test, unsigned int *failures)
+{
+ int ret;
+
+ if (!test->execute)
+ return 0;
+ efi_st_printf("\nExecuting '%s'\n", test->name);
+ ret = test->execute();
+ if (ret) {
+ efi_st_printf("ERROR: Executing '%s' failed\n", test->name);
+ ++*failures;
+ } else {
+ efi_st_printf("Executing '%s' succeeded\n", test->name);
+ }
+ return ret;
+}
+
+/*
+ * Tear down a test.
+ *
+ * @test the test to be torn down
+ * @failures counter that will be incremented if a failure occurs
+ */
+static int teardown(struct efi_unit_test *test, unsigned int *failures)
+{
+ int ret;
+
+ if (!test->teardown)
+ return 0;
+ efi_st_printf("\nTearing down '%s'\n", test->name);
+ ret = test->teardown();
+ if (ret) {
+ efi_st_printf("ERROR: Tearing down '%s' failed\n", test->name);
+ ++*failures;
+ } else {
+ efi_st_printf("Tearing down '%s' succeeded\n", test->name);
+ }
+ return ret;
+}
+
+/*
+ * Execute selftest of the EFI API
+ *
+ * This is the main entry point of the EFI selftest application.
+ *
+ * All tests use a driver model and are run in three phases:
+ * setup, execute, teardown.
+ *
+ * A test may be setup and executed at boottime,
+ * it may be setup at boottime and executed at runtime,
+ * or it may be setup and executed at runtime.
+ *
+ * After executing all tests the system is reset.
+ *
+ * @image_handle: handle of the loaded EFI image
+ * @systab: EFI system table
+ */
+efi_status_t EFIAPI efi_selftest(efi_handle_t image_handle,
+ struct efi_system_table *systab)
+{
+ struct efi_unit_test *test;
+ unsigned int failures = 0;
+
+ systable = systab;
+ boottime = systable->boottime;
+ runtime = systable->runtime;
+ handle = image_handle;
+ con_out = systable->con_out;
+ con_in = systable->con_in;
+
+ efi_st_printf("\nTesting EFI API implementation\n");
+
+ efi_st_printf("\nNumber of tests to execute: %u\n",
+ ll_entry_count(struct efi_unit_test, efi_unit_test));
+
+ /* Execute boottime tests */
+ for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
+ test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) {
+ if (test->phase == EFI_EXECUTE_BEFORE_BOOTTIME_EXIT) {
+ setup(test, &failures);
+ execute(test, &failures);
+ teardown(test, &failures);
+ }
+ }
+
+ /* Execute mixed tests */
+ for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
+ test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) {
+ if (test->phase == EFI_SETUP_BEFORE_BOOTTIME_EXIT)
+ setup(test, &failures);
+ }
+
+ efi_st_exit_boot_services();
+
+ for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
+ test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) {
+ if (test->phase == EFI_SETUP_BEFORE_BOOTTIME_EXIT) {
+ execute(test, &failures);
+ teardown(test, &failures);
+ }
+ }
+
+ /* Execute runtime tests */
+ for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
+ test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) {
+ if (test->phase == EFI_SETUP_AFTER_BOOTTIME_EXIT) {
+ setup(test, &failures);
+ execute(test, &failures);
+ teardown(test, &failures);
+ }
+ }
+
+ /* Give feedback */
+ efi_st_printf("\nSummary: %u failures\n\n", failures);
+
+ /* Reset system */
+ efi_st_printf("Preparing for reset. Press any key.\n");
+ efi_st_get_key();
+ runtime->reset_system(EFI_RESET_WARM, EFI_NOT_READY,
+ sizeof(reset_message), reset_message);
+ efi_st_printf("\nERROR: reset failed.\n");
+
+ return EFI_UNSUPPORTED;
+}
diff --git a/lib/efi_selftest/efi_selftest_console.c b/lib/efi_selftest/efi_selftest_console.c
new file mode 100644
index 0000000000..7b5b724a61
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_console.c
@@ -0,0 +1,187 @@
+/*
+ * EFI efi_selftest
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <efi_selftest.h>
+#include <vsprintf.h>
+
+struct efi_simple_text_output_protocol *con_out;
+struct efi_simple_input_interface *con_in;
+
+/*
+ * Print a pointer to an u16 string
+ *
+ * @pointer: pointer
+ * @buf: pointer to buffer address
+ * on return position of terminating zero word
+ */
+static void pointer(void *pointer, u16 **buf)
+{
+ int i;
+ u16 c;
+ uintptr_t p = (uintptr_t)pointer;
+ u16 *pos = *buf;
+
+ for (i = 8 * sizeof(p) - 4; i >= 0; i -= 4) {
+ c = (p >> i) & 0x0f;
+ c += '0';
+ if (c > '9')
+ c += 'a' - '9' - 1;
+ *pos++ = c;
+ }
+ *pos = 0;
+ *buf = pos;
+}
+
+/*
+ * Print an unsigned 32bit value as decimal number to an u16 string
+ *
+ * @value: value to be printed
+ * @buf: pointer to buffer address
+ * on return position of terminating zero word
+ */
+static void uint2dec(u32 value, u16 **buf)
+{
+ u16 *pos = *buf;
+ int i;
+ u16 c;
+ u64 f;
+
+ /*
+ * Increment by .5 and multiply with
+ * (2 << 60) / 1,000,000,000 = 0x44B82FA0.9B5A52CC
+ * to move the first digit to bit 60-63.
+ */
+ f = 0x225C17D0;
+ f += (0x9B5A52DULL * value) >> 28;
+ f += 0x44B82FA0ULL * value;
+
+ for (i = 0; i < 10; ++i) {
+ /* Write current digit */
+ c = f >> 60;
+ if (c || pos != *buf)
+ *pos++ = c + '0';
+ /* Eliminate current digit */
+ f &= 0xfffffffffffffff;
+ /* Get next digit */
+ f *= 0xaULL;
+ }
+ if (pos == *buf)
+ *pos++ = '0';
+ *pos = 0;
+ *buf = pos;
+}
+
+/*
+ * Print a signed 32bit value as decimal number to an u16 string
+ *
+ * @value: value to be printed
+ * @buf: pointer to buffer address
+ * on return position of terminating zero word
+ */
+static void int2dec(s32 value, u16 **buf)
+{
+ u32 u;
+ u16 *pos = *buf;
+
+ if (value < 0) {
+ *pos++ = '-';
+ u = -value;
+ } else {
+ u = value;
+ }
+ uint2dec(u, &pos);
+ *buf = pos;
+}
+
+/*
+ * Print a formatted string to the EFI console
+ *
+ * @fmt: format string
+ * @...: optional arguments
+ */
+void efi_st_printf(const char *fmt, ...)
+{
+ va_list args;
+ u16 buf[160];
+ const char *c;
+ u16 *pos = buf;
+ const char *s;
+
+ va_start(args, fmt);
+
+ c = fmt;
+ for (; *c; ++c) {
+ switch (*c) {
+ case '\\':
+ ++c;
+ switch (*c) {
+ case '\0':
+ --c;
+ break;
+ case 'n':
+ *pos++ = '\n';
+ break;
+ case 'r':
+ *pos++ = '\r';
+ break;
+ case 't':
+ *pos++ = '\t';
+ break;
+ default:
+ *pos++ = *c;
+ }
+ break;
+ case '%':
+ ++c;
+ switch (*c) {
+ case '\0':
+ --c;
+ break;
+ case 'd':
+ int2dec(va_arg(args, s32), &pos);
+ break;
+ case 'p':
+ pointer(va_arg(args, void*), &pos);
+ break;
+ case 's':
+ s = va_arg(args, const char *);
+ for (; *s; ++s)
+ *pos++ = *s;
+ break;
+ case 'u':
+ uint2dec(va_arg(args, u32), &pos);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ *pos++ = *c;
+ }
+ }
+ va_end(args);
+ *pos = 0;
+ con_out->output_string(con_out, buf);
+}
+
+/*
+ * Reads an Unicode character from the input device.
+ *
+ * @return: Unicode character
+ */
+u16 efi_st_get_key(void)
+{
+ struct efi_input_key input_key;
+ efi_status_t ret;
+
+ /* Wait for next key */
+ do {
+ ret = con_in->read_key_stroke(con_in, &input_key);
+ } while (ret == EFI_NOT_READY);
+ return input_key.unicode_char;
+}