summaryrefslogtreecommitdiff
path: root/lib/efi_selftest
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2017-12-05 17:52:16 -0500
committerTom Rini <trini@konsulko.com>2017-12-05 17:52:16 -0500
commit9188c4315cbf670417adf196fa1c6a386e885ef1 (patch)
treebdef16af1e11d8df66211f88bdbdf509660df41e /lib/efi_selftest
parent9da7fb4a39149c3061cb148bfbaa76b4b52b9008 (diff)
parentbb0bb91cf0aa2c13528fe39be2006f32f7b9c437 (diff)
Merge tag 'signed-efi-next' of git://github.com/agraf/u-boot
Patch queue for efi - 2017-12-05 Highlights for this release: - Dynamic EFI object creation (lists instead of static arrays) - EFI selftest improvements - Minor fixes
Diffstat (limited to 'lib/efi_selftest')
-rw-r--r--lib/efi_selftest/Makefile22
-rw-r--r--lib/efi_selftest/efi_selftest.c153
-rw-r--r--lib/efi_selftest/efi_selftest_console.c10
-rw-r--r--lib/efi_selftest/efi_selftest_devicepath.c390
-rw-r--r--lib/efi_selftest/efi_selftest_events.c2
-rw-r--r--lib/efi_selftest/efi_selftest_gop.c95
-rw-r--r--lib/efi_selftest/efi_selftest_manageprotocols.c354
-rw-r--r--lib/efi_selftest/efi_selftest_snp.c2
-rw-r--r--lib/efi_selftest/efi_selftest_textoutput.c53
-rw-r--r--lib/efi_selftest/efi_selftest_tpl.c4
-rw-r--r--lib/efi_selftest/efi_selftest_util.c11
-rw-r--r--lib/efi_selftest/efi_selftest_watchdog.c231
12 files changed, 1272 insertions, 55 deletions
diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile
index e446046e02..837e86228e 100644
--- a/lib/efi_selftest/Makefile
+++ b/lib/efi_selftest/Makefile
@@ -7,26 +7,16 @@
# 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)
-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)
-CFLAGS_efi_selftest_util.o := $(CFLAGS_EFI)
-CFLAGS_REMOVE_efi_selftest_util.o := $(CFLAGS_NON_EFI)
-
obj-$(CONFIG_CMD_BOOTEFI_SELFTEST) += \
efi_selftest.o \
efi_selftest_console.o \
+efi_selftest_devicepath.o \
efi_selftest_events.o \
efi_selftest_exitbootservices.o \
+efi_selftest_gop.o \
+efi_selftest_manageprotocols.o \
efi_selftest_snp.o \
+efi_selftest_textoutput.o \
efi_selftest_tpl.o \
-efi_selftest_util.o
+efi_selftest_util.o \
+efi_selftest_watchdog.o
diff --git a/lib/efi_selftest/efi_selftest.c b/lib/efi_selftest/efi_selftest.c
index 45d8d3d384..4e5a12c47c 100644
--- a/lib/efi_selftest/efi_selftest.c
+++ b/lib/efi_selftest/efi_selftest.c
@@ -9,6 +9,13 @@
#include <efi_selftest.h>
#include <vsprintf.h>
+/*
+ * Constants for test step bitmap
+ */
+#define EFI_ST_SETUP 1
+#define EFI_ST_EXECUTE 2
+#define EFI_ST_TEARDOWN 4
+
static const struct efi_system_table *systable;
static const struct efi_boot_services *boottime;
static const struct efi_runtime_services *runtime;
@@ -25,9 +32,9 @@ static u16 reset_message[] = L"Selftest completed";
*/
void efi_st_exit_boot_services(void)
{
- unsigned long map_size = 0;
- unsigned long map_key;
- unsigned long desc_size;
+ efi_uintn_t map_size = 0;
+ efi_uintn_t map_key;
+ efi_uintn_t desc_size;
u32 desc_version;
efi_status_t ret;
struct efi_mem_desc *memory_map;
@@ -134,6 +141,70 @@ static int teardown(struct efi_unit_test *test, unsigned int *failures)
}
/*
+ * Check that a test exists.
+ *
+ * @testname: name of the test
+ * @return: test
+ */
+static struct efi_unit_test *find_test(const u16 *testname)
+{
+ struct efi_unit_test *test;
+
+ 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 (!efi_st_strcmp_16_8(testname, test->name))
+ return test;
+ }
+ efi_st_printf("\nTest '%ps' not found\n", testname);
+ return NULL;
+}
+
+/*
+ * List all available tests.
+ */
+static void list_all_tests(void)
+{
+ struct efi_unit_test *test;
+
+ /* List all tests */
+ efi_st_printf("\nAvailable tests:\n");
+ for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
+ test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) {
+ efi_st_printf("'%s'%s\n", test->name,
+ test->on_request ? " - on request" : "");
+ }
+}
+
+/*
+ * Execute test steps of one phase.
+ *
+ * @testname name of a single selected test or NULL
+ * @phase test phase
+ * @steps steps to execute
+ * failures returns EFI_ST_SUCCESS if all test steps succeeded
+ */
+void efi_st_do_tests(const u16 *testname, unsigned int phase,
+ unsigned int steps, unsigned int *failures)
+{
+ struct efi_unit_test *test;
+
+ 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 (testname ?
+ efi_st_strcmp_16_8(testname, test->name) : test->on_request)
+ continue;
+ if (test->phase != phase)
+ continue;
+ if (steps & EFI_ST_SETUP)
+ setup(test, failures);
+ if (steps & EFI_ST_EXECUTE)
+ execute(test, failures);
+ if (steps & EFI_ST_TEARDOWN)
+ teardown(test, failures);
+ }
+}
+
+/*
* Execute selftest of the EFI API
*
* This is the main entry point of the EFI selftest application.
@@ -153,8 +224,10 @@ static int teardown(struct efi_unit_test *test, unsigned int *failures)
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;
+ const u16 *testname = NULL;
+ struct efi_loaded_image *loaded_image;
+ efi_status_t ret;
systable = systab;
boottime = systable->boottime;
@@ -163,47 +236,59 @@ efi_status_t EFIAPI efi_selftest(efi_handle_t image_handle,
con_out = systable->con_out;
con_in = systable->con_in;
- efi_st_printf("\nTesting EFI API implementation\n");
+ ret = boottime->handle_protocol(image_handle, &efi_guid_loaded_image,
+ (void **)&loaded_image);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Cannot open loaded image protocol\n");
+ return ret;
+ }
- efi_st_printf("\nNumber of tests to execute: %u\n",
- ll_entry_count(struct efi_unit_test, efi_unit_test));
+ if (loaded_image->load_options)
+ testname = (u16 *)loaded_image->load_options;
- /* 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);
+ if (testname) {
+ if (!efi_st_strcmp_16_8(testname, "list") ||
+ !find_test(testname)) {
+ list_all_tests();
+ /*
+ * TODO:
+ * Once the Exit boottime service is correctly
+ * implemented we should call
+ * boottime->exit(image_handle, EFI_SUCCESS, 0, NULL);
+ * here, cf.
+ * https://lists.denx.de/pipermail/u-boot/2017-October/308720.html
+ */
+ return EFI_SUCCESS;
}
}
+ efi_st_printf("\nTesting EFI API implementation\n");
+
+ if (testname)
+ efi_st_printf("\nSelected test: '%ps'\n", testname);
+ else
+ efi_st_printf("\nNumber of tests to execute: %u\n",
+ ll_entry_count(struct efi_unit_test,
+ efi_unit_test));
+
+ /* Execute boottime tests */
+ efi_st_do_tests(testname, EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ EFI_ST_SETUP | EFI_ST_EXECUTE | EFI_ST_TEARDOWN,
+ &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_do_tests(testname, EFI_SETUP_BEFORE_BOOTTIME_EXIT,
+ EFI_ST_SETUP, &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);
- }
- }
+ efi_st_do_tests(testname, EFI_SETUP_BEFORE_BOOTTIME_EXIT,
+ EFI_ST_EXECUTE | EFI_ST_TEARDOWN, &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);
- }
- }
+ efi_st_do_tests(testname, EFI_SETUP_AFTER_BOOTTIME_EXIT,
+ EFI_ST_SETUP | EFI_ST_EXECUTE | EFI_ST_TEARDOWN,
+ &failures);
/* Give feedback */
efi_st_printf("\nSummary: %u failures\n\n", failures);
diff --git a/lib/efi_selftest/efi_selftest_console.c b/lib/efi_selftest/efi_selftest_console.c
index 840e2290c6..6a7fd20da5 100644
--- a/lib/efi_selftest/efi_selftest_console.c
+++ b/lib/efi_selftest/efi_selftest_console.c
@@ -142,6 +142,7 @@ void efi_st_printf(const char *fmt, ...)
const char *c;
u16 *pos = buf;
const char *s;
+ const u16 *u;
va_start(args, fmt);
@@ -179,9 +180,18 @@ void efi_st_printf(const char *fmt, ...)
case 'p':
++c;
switch (*c) {
+ /* MAC address */
case 'm':
mac(va_arg(args, void*), &pos);
break;
+
+ /* u16 string */
+ case 's':
+ u = va_arg(args, u16*);
+ /* Ensure string fits into buffer */
+ for (; *u && pos < buf + 120; ++u)
+ *pos++ = *u;
+ break;
default:
--c;
pointer(va_arg(args, void*), &pos);
diff --git a/lib/efi_selftest/efi_selftest_devicepath.c b/lib/efi_selftest/efi_selftest_devicepath.c
new file mode 100644
index 0000000000..1ab54ebb37
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_devicepath.c
@@ -0,0 +1,390 @@
+/*
+ * efi_selftest_devicepath
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This unit test checks the following protocol services:
+ * DevicePathToText
+ */
+
+#include <efi_selftest.h>
+
+static struct efi_boot_services *boottime;
+
+static efi_handle_t handle1;
+static efi_handle_t handle2;
+static efi_handle_t handle3;
+
+struct interface {
+ void (EFIAPI * inc)(void);
+} interface;
+
+static efi_guid_t guid_device_path = DEVICE_PATH_GUID;
+
+static efi_guid_t guid_device_path_to_text_protocol =
+ EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID;
+
+static efi_guid_t guid_protocol =
+ EFI_GUID(0xdbca4c98, 0x6cb0, 0x694d,
+ 0x08, 0x72, 0x81, 0x9c, 0x65, 0x0c, 0xbb, 0x7d);
+
+static efi_guid_t guid_vendor1 =
+ EFI_GUID(0xdbca4c98, 0x6cb0, 0x694d,
+ 0x08, 0x72, 0x81, 0x9c, 0x65, 0x0c, 0xbb, 0xb1);
+
+static efi_guid_t guid_vendor2 =
+ EFI_GUID(0xdbca4c98, 0x6cb0, 0x694d,
+ 0x08, 0x72, 0x81, 0x9c, 0x65, 0x0c, 0xbb, 0xa2);
+
+static efi_guid_t guid_vendor3 =
+ EFI_GUID(0xdbca4c98, 0x6cb0, 0x694d,
+ 0x08, 0x72, 0x81, 0x9c, 0x65, 0x0c, 0xbb, 0xc3);
+
+static u8 *dp1;
+static u8 *dp2;
+static u8 *dp3;
+
+struct efi_device_path_to_text_protocol *device_path_to_text;
+
+/*
+ * Setup unit test.
+ *
+ * Create three handles. Install a new protocol on two of them and
+ * provice device paths.
+ *
+ * handle1
+ * guid interface
+ * handle2
+ * guid interface
+ * handle3
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ */
+static int setup(const efi_handle_t img_handle,
+ const struct efi_system_table *systable)
+{
+ struct efi_device_path_vendor vendor_node;
+ struct efi_device_path end_node;
+ efi_status_t ret;
+
+ boottime = systable->boottime;
+
+ ret = boottime->locate_protocol(&guid_device_path_to_text_protocol,
+ NULL, (void **)&device_path_to_text);
+ if (ret != EFI_SUCCESS) {
+ device_path_to_text = NULL;
+ efi_st_error(
+ "Device path to text protocol is not available.\n");
+ return EFI_ST_FAILURE;
+ }
+
+ ret = boottime->allocate_pool(EFI_LOADER_DATA,
+ sizeof(struct efi_device_path_vendor) +
+ sizeof(struct efi_device_path),
+ (void **)&dp1);
+ if (ret != EFI_SUCCESS)
+ goto out_of_memory;
+
+ ret = boottime->allocate_pool(EFI_LOADER_DATA, 2 *
+ sizeof(struct efi_device_path_vendor) +
+ sizeof(struct efi_device_path),
+ (void **)&dp2);
+ if (ret != EFI_SUCCESS)
+ goto out_of_memory;
+
+ ret = boottime->allocate_pool(EFI_LOADER_DATA, 3 *
+ sizeof(struct efi_device_path_vendor) +
+ sizeof(struct efi_device_path),
+ (void **)&dp3);
+ if (ret != EFI_SUCCESS)
+ goto out_of_memory;
+
+ vendor_node.dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE;
+ vendor_node.dp.sub_type = DEVICE_PATH_SUB_TYPE_VENDOR;
+ vendor_node.dp.length = sizeof(struct efi_device_path_vendor);
+
+ boottime->copy_mem(&vendor_node.guid, &guid_vendor1,
+ sizeof(efi_guid_t));
+ boottime->copy_mem(dp1, &vendor_node,
+ sizeof(struct efi_device_path_vendor));
+ boottime->copy_mem(dp2, &vendor_node,
+ sizeof(struct efi_device_path_vendor));
+ boottime->copy_mem(dp3, &vendor_node,
+ sizeof(struct efi_device_path_vendor));
+
+ boottime->copy_mem(&vendor_node.guid, &guid_vendor2,
+ sizeof(efi_guid_t));
+ boottime->copy_mem(dp2 + sizeof(struct efi_device_path_vendor),
+ &vendor_node, sizeof(struct efi_device_path_vendor));
+ boottime->copy_mem(dp3 + sizeof(struct efi_device_path_vendor),
+ &vendor_node, sizeof(struct efi_device_path_vendor));
+
+ boottime->copy_mem(&vendor_node.guid, &guid_vendor3,
+ sizeof(efi_guid_t));
+ boottime->copy_mem(dp3 + 2 * sizeof(struct efi_device_path_vendor),
+ &vendor_node, sizeof(struct efi_device_path_vendor));
+
+ end_node.type = DEVICE_PATH_TYPE_END;
+ end_node.sub_type = DEVICE_PATH_SUB_TYPE_END;
+ end_node.length = sizeof(struct efi_device_path);
+ boottime->copy_mem(dp1 + sizeof(struct efi_device_path_vendor),
+ &end_node, sizeof(struct efi_device_path));
+ boottime->copy_mem(dp2 + 2 * sizeof(struct efi_device_path_vendor),
+ &end_node, sizeof(struct efi_device_path));
+ boottime->copy_mem(dp3 + 3 * sizeof(struct efi_device_path_vendor),
+ &end_node, sizeof(struct efi_device_path));
+
+ ret = boottime->install_protocol_interface(&handle1,
+ &guid_device_path,
+ EFI_NATIVE_INTERFACE,
+ dp1);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("InstallProtocolInterface failed\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->install_protocol_interface(&handle1,
+ &guid_protocol,
+ EFI_NATIVE_INTERFACE,
+ &interface);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("InstallProtocolInterface failed\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->install_protocol_interface(&handle2,
+ &guid_device_path,
+ EFI_NATIVE_INTERFACE,
+ dp2);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("InstallProtocolInterface failed\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->install_protocol_interface(&handle2,
+ &guid_protocol,
+ EFI_NATIVE_INTERFACE,
+ &interface);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("InstallProtocolInterface failed\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->install_protocol_interface(&handle3,
+ &guid_device_path,
+ EFI_NATIVE_INTERFACE,
+ dp3);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("InstallProtocolInterface failed\n");
+ return EFI_ST_FAILURE;
+ }
+ return EFI_ST_SUCCESS;
+
+out_of_memory:
+ efi_st_error("Out of memory\n");
+ return EFI_ST_FAILURE;
+}
+
+/*
+ * Tear down unit test.
+ *
+ */
+static int teardown(void)
+{
+ efi_status_t ret;
+
+ ret = boottime->uninstall_protocol_interface(&handle1,
+ &guid_device_path,
+ dp1);
+ if (ret != EFI_SUCCESS)
+ efi_st_todo("UninstallProtocolInterface failed\n");
+ ret = boottime->uninstall_protocol_interface(&handle1,
+ &guid_protocol,
+ &interface);
+ if (ret != EFI_SUCCESS)
+ efi_st_todo("UninstallProtocolInterface failed\n");
+ ret = boottime->uninstall_protocol_interface(&handle2,
+ &guid_device_path,
+ dp2);
+ if (ret != EFI_SUCCESS)
+ efi_st_todo("UninstallProtocolInterface failed\n");
+ ret = boottime->uninstall_protocol_interface(&handle2,
+ &guid_protocol,
+ &interface);
+ if (ret != EFI_SUCCESS)
+ efi_st_todo("UninstallProtocolInterface failed\n");
+ ret = boottime->uninstall_protocol_interface(&handle3,
+ &guid_device_path,
+ dp3);
+ if (ret != EFI_SUCCESS)
+ efi_st_todo("UninstallProtocolInterface failed\n");
+ if (dp1) {
+ ret = boottime->free_pool(dp1);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
+ }
+ if (dp2) {
+ ret = boottime->free_pool(dp2);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
+ }
+ if (dp3) {
+ ret = boottime->free_pool(dp3);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
+ }
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute unit test.
+ *
+ */
+static int execute(void)
+{
+ struct efi_device_path *remaining_dp;
+ void *handle;
+ /*
+ * This device path node ends with the letter 't' of 'u-boot'.
+ * The following '.bin' does not belong to the node but is
+ * helps to test the correct truncation.
+ */
+ struct {
+ struct efi_device_path dp;
+ u16 text[12];
+ } __packed dp_node = {
+ { DEVICE_PATH_TYPE_MEDIA_DEVICE,
+ DEVICE_PATH_SUB_TYPE_FILE_PATH,
+ sizeof(struct efi_device_path) + 12},
+ L"u-boot.bin",
+ };
+ u16 *string;
+ efi_status_t ret;
+ efi_uintn_t i, no_handles;
+ efi_handle_t *handles;
+ struct efi_device_path *dp;
+
+ /* Display all available device paths */
+ ret = boottime->locate_handle_buffer(BY_PROTOCOL,
+ &guid_device_path,
+ NULL, &no_handles, &handles);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Cannot retrieve device path protocols.\n");
+ return EFI_ST_FAILURE;
+ }
+
+ efi_st_printf("Installed device path protocols:\n");
+ for (i = 0; i < no_handles; ++i) {
+ ret = boottime->open_protocol(handles[i], &guid_device_path,
+ (void **)&dp, NULL, NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Cannot open device path protocol.\n");
+ return EFI_ST_FAILURE;
+ }
+ string = device_path_to_text->convert_device_path_to_text(
+ dp, true, false);
+ if (!string) {
+ efi_st_error("ConvertDevicePathToText failed\n");
+ return EFI_ST_FAILURE;
+ }
+ efi_st_printf("%ps\n", string);
+ ret = boottime->free_pool(string);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->close_protocol(handles[i], &guid_device_path,
+ NULL, NULL);
+ if (ret != EFI_SUCCESS)
+ efi_st_todo("Cannot close device path protocol.\n");
+ }
+ ret = boottime->free_pool(handles);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
+ efi_st_printf("\n");
+
+ /* Test ConvertDevicePathToText */
+ string = device_path_to_text->convert_device_path_to_text(
+ (struct efi_device_path *)dp2, true, false);
+ if (!string) {
+ efi_st_error("ConvertDevicePathToText failed\n");
+ return EFI_ST_FAILURE;
+ }
+ efi_st_printf("dp2: %ps\n", string);
+ if (efi_st_strcmp_16_8(
+ string,
+ "/VenHw(dbca4c98-6cb0-694d-0872-819c650cbbb1)/VenHw(dbca4c98-6cb0-694d-0872-819c650cbba2)")
+ ) {
+ efi_st_error("Incorrect text from ConvertDevicePathToText\n");
+ return EFI_ST_FAILURE;
+ }
+
+ ret = boottime->free_pool(string);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Test ConvertDeviceNodeToText */
+ string = device_path_to_text->convert_device_node_to_text(
+ (struct efi_device_path *)&dp_node, true, false);
+ if (!string) {
+ efi_st_error("ConvertDeviceNodeToText failed\n");
+ return EFI_ST_FAILURE;
+ }
+ efi_st_printf("dp_node: %ps\n", string);
+ ret = boottime->free_pool(string);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("FreePool failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (efi_st_strcmp_16_8(string, "u-boot")) {
+ efi_st_error(
+ "Incorrect conversion by ConvertDeviceNodeToText\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /* Test LocateDevicePath */
+ remaining_dp = (struct efi_device_path *)dp3;
+ ret = boottime->locate_device_path(&guid_protocol, &remaining_dp,
+ &handle);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("LocateDevicePath failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (handle != handle2) {
+ efi_st_error("LocateDevicePath returned wrong handle\n");
+ return EFI_ST_FAILURE;
+ }
+ string = device_path_to_text->convert_device_path_to_text(remaining_dp,
+ true, false);
+ if (!string) {
+ efi_st_error("ConvertDevicePathToText failed\n");
+ return EFI_ST_FAILURE;
+ }
+ efi_st_printf("remaining device path: %ps\n", string);
+ if (efi_st_strcmp_16_8(string,
+ "/VenHw(dbca4c98-6cb0-694d-0872-819c650cbbc3)")
+ ) {
+ efi_st_error("LocateDevicePath: wrong remaining device path\n");
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(devicepath) = {
+ .name = "device path",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .setup = setup,
+ .execute = execute,
+ .teardown = teardown,
+};
diff --git a/lib/efi_selftest/efi_selftest_events.c b/lib/efi_selftest/efi_selftest_events.c
index 081f31257f..ad9490bd25 100644
--- a/lib/efi_selftest/efi_selftest_events.c
+++ b/lib/efi_selftest/efi_selftest_events.c
@@ -108,7 +108,7 @@ static int teardown(void)
*/
static int execute(void)
{
- size_t index;
+ efi_uintn_t index;
efi_status_t ret;
/* Set 10 ms timer */
diff --git a/lib/efi_selftest/efi_selftest_gop.c b/lib/efi_selftest/efi_selftest_gop.c
new file mode 100644
index 0000000000..2a0553b115
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_gop.c
@@ -0,0 +1,95 @@
+/*
+ * efi_selftest_gop
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * Test the graphical output protocol.
+ */
+
+#include <efi_selftest.h>
+
+static struct efi_boot_services *boottime;
+static efi_guid_t efi_gop_guid = EFI_GOP_GUID;
+static struct efi_gop *gop;
+
+/*
+ * Setup unit test.
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int setup(const efi_handle_t handle,
+ const struct efi_system_table *systable)
+{
+ efi_status_t ret;
+
+ boottime = systable->boottime;
+
+ ret = boottime->locate_protocol(&efi_gop_guid, NULL, (void **)&gop);
+ if (ret != EFI_SUCCESS) {
+ gop = NULL;
+ efi_st_printf("Graphical output protocol is not available.\n");
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Tear down unit test.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int teardown(void)
+{
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute unit test.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int execute(void)
+{
+ efi_status_t ret;
+ u32 i, max_mode;
+ efi_uintn_t size;
+ struct efi_gop_mode_info *info;
+
+ if (!gop)
+ return EFI_ST_SUCCESS;
+
+ if (!gop->mode) {
+ efi_st_error("EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE missing\n");
+ return EFI_ST_FAILURE;
+ }
+ max_mode = gop->mode->max_mode;
+ if (!max_mode) {
+ efi_st_error("No graphical mode available\n");
+ return EFI_ST_FAILURE;
+ }
+ efi_st_printf("Number of available modes: %u\n", max_mode);
+
+ for (i = 0; i < max_mode; ++i) {
+ ret = gop->query_mode(gop, i, &size, &info);
+ if (ret != EFI_SUCCESS) {
+ efi_st_printf("Could not query mode %u\n", i);
+ return EFI_ST_FAILURE;
+ }
+ efi_st_printf("Mode %u: %u x %u\n",
+ i, info->width, info->height);
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(gop) = {
+ .name = "graphical output",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .setup = setup,
+ .execute = execute,
+ .teardown = teardown,
+};
diff --git a/lib/efi_selftest/efi_selftest_manageprotocols.c b/lib/efi_selftest/efi_selftest_manageprotocols.c
new file mode 100644
index 0000000000..f20f1528d4
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_manageprotocols.c
@@ -0,0 +1,354 @@
+/*
+ * efi_selftest_manageprotocols
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This unit test checks the following protocol services:
+ * InstallProtocolInterface, UninstallProtocolInterface,
+ * InstallMultipleProtocolsInterfaces, UninstallMultipleProtocolsInterfaces,
+ * HandleProtocol, ProtocolsPerHandle,
+ * LocateHandle, LocateHandleBuffer.
+ */
+
+#include <efi_selftest.h>
+
+/*
+ * The test currently does not actually call the interface function.
+ * So this is just a dummy structure.
+ */
+struct interface {
+ void (EFIAPI * inc)(void);
+};
+
+static struct efi_boot_services *boottime;
+static efi_guid_t guid1 =
+ EFI_GUID(0x2e7ca819, 0x21d3, 0x0a3a,
+ 0xf7, 0x91, 0x82, 0x1f, 0x7a, 0x83, 0x67, 0xaf);
+static efi_guid_t guid2 =
+ EFI_GUID(0xf909f2bb, 0x90a8, 0x0d77,
+ 0x94, 0x0c, 0x3e, 0xa8, 0xea, 0x38, 0xd6, 0x6f);
+static efi_guid_t guid3 =
+ EFI_GUID(0x06d641a3, 0xf4e7, 0xe0c9,
+ 0xe7, 0x8d, 0x41, 0x2d, 0x72, 0xa6, 0xb1, 0x24);
+static efi_handle_t handle1;
+static efi_handle_t handle2;
+static struct interface interface1;
+static struct interface interface2;
+static struct interface interface3;
+static struct interface interface4;
+
+/*
+ * Find a handle in an array.
+ *
+ * @handle: handle to find
+ * @count: number of entries in the array
+ * @buffer: array to search
+ */
+efi_status_t find_in_buffer(efi_handle_t handle, size_t count,
+ efi_handle_t *buffer)
+{
+ size_t i;
+
+ for (i = 0; i < count; ++i) {
+ if (buffer[i] == handle)
+ return EFI_SUCCESS;
+ }
+ return EFI_NOT_FOUND;
+}
+
+/*
+ * Setup unit test.
+ *
+ * Create two handles and install two out of three protocol interfaces on each
+ * of them:
+ *
+ * handle1
+ * guid1 interface1
+ * guid3 interface3
+ * handle2
+ * guid1 interface4
+ * guid2 interface2
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ */
+static int setup(const efi_handle_t img_handle,
+ const struct efi_system_table *systable)
+{
+ efi_status_t ret;
+ efi_handle_t handle;
+
+ boottime = systable->boottime;
+
+ ret = boottime->install_protocol_interface(&handle1, &guid3,
+ EFI_NATIVE_INTERFACE,
+ &interface3);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("InstallProtocolInterface failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (!handle1) {
+ efi_st_error("InstallProtocolInterface failed to create handle\n");
+ return EFI_ST_FAILURE;
+ }
+ handle = handle1;
+ ret = boottime->install_protocol_interface(&handle1, &guid1,
+ EFI_NATIVE_INTERFACE,
+ &interface1);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("InstallProtocolInterface failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (handle != handle1) {
+ efi_st_error("InstallProtocolInterface failed to use handle\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->install_multiple_protocol_interfaces(&handle2,
+ &guid1, &interface4, &guid2, &interface2, NULL);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("InstallMultipleProtocolInterfaces failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (!handle2 || handle1 == handle2) {
+ efi_st_error("InstallMultipleProtocolInterfaces failed to create handle\n");
+ return EFI_ST_FAILURE;
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Tear down unit test.
+ *
+ */
+static int teardown(void)
+{
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute unit test.
+ *
+ */
+static int execute(void)
+{
+ struct interface *interface;
+ efi_status_t ret;
+ efi_handle_t *buffer;
+ size_t buffer_size;
+ efi_uintn_t count = 0;
+ efi_guid_t **prot_buffer;
+ efi_uintn_t prot_count;
+
+ /*
+ * Test HandleProtocol
+ */
+ ret = boottime->handle_protocol(handle1, &guid3, (void **)&interface);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("HandleProtocol failed to retrieve interface\n");
+ return EFI_ST_FAILURE;
+ }
+ if (interface != &interface3) {
+ efi_st_error("HandleProtocol returned wrong interface\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->handle_protocol(handle1, &guid2, (void **)&interface);
+ if (ret == EFI_SUCCESS) {
+ efi_st_error("HandleProtocol returned not installed interface\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /*
+ * Test LocateHandleBuffer with AllHandles
+ */
+ ret = boottime->locate_handle_buffer(ALL_HANDLES, NULL, NULL,
+ &count, &buffer);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("LocateHandleBuffer with AllHandles failed\n");
+ return EFI_ST_FAILURE;
+ }
+ buffer_size = count;
+ ret = find_in_buffer(handle1, count, buffer);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("LocateHandleBuffer failed to locate new handle\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = find_in_buffer(handle2, count, buffer);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("LocateHandleBuffer failed to locate new handle\n");
+ return EFI_ST_FAILURE;
+ }
+ boottime->set_mem(buffer, sizeof(efi_handle_t) * buffer_size, 0);
+
+ /*
+ * Test error handling in UninstallMultipleProtocols
+ *
+ * Try to uninstall more protocols than there are installed.
+ */
+ ret = boottime->uninstall_multiple_protocol_interfaces(
+ handle2,
+ &guid1, &interface4,
+ &guid2, &interface2,
+ &guid3, &interface3,
+ NULL);
+ if (ret == EFI_SUCCESS) {
+ efi_st_todo("UninstallMultipleProtocolInterfaces did not catch error\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /*
+ * Test LocateHandleBuffer with ByProtocol
+ */
+ count = buffer_size;
+ ret = boottime->locate_handle_buffer(BY_PROTOCOL, &guid1, NULL,
+ &count, &buffer);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("LocateHandleBuffer failed to locate new handles\n");
+ return EFI_ST_FAILURE;
+ }
+ if (count != 2) {
+ efi_st_error("LocateHandleBuffer failed to locate new handles\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = find_in_buffer(handle1, count, buffer);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("LocateHandleBuffer failed to locate new handle\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = find_in_buffer(handle2, count, buffer);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("LocateHandleBuffer failed to locate new handle\n");
+ return EFI_ST_FAILURE;
+ }
+ boottime->set_mem(buffer, sizeof(efi_handle_t) * buffer_size, 0);
+
+ /*
+ * Test LocateHandle with ByProtocol
+ */
+ count = buffer_size * sizeof(efi_handle_t);
+ ret = boottime->locate_handle(BY_PROTOCOL, &guid1, NULL,
+ &count, buffer);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("LocateHandle with ByProtocol failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (count / sizeof(efi_handle_t) != 2) {
+ efi_st_error("LocateHandle failed to locate new handles\n");
+ return EFI_ST_FAILURE;
+ }
+ buffer_size = count;
+ ret = find_in_buffer(handle1, count, buffer);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("LocateHandle failed to locate new handles\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = find_in_buffer(handle2, count, buffer);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("LocateHandle failed to locate new handles\n");
+ return EFI_ST_FAILURE;
+ }
+ boottime->set_mem(buffer, sizeof(efi_handle_t) * buffer_size, 0);
+
+ /*
+ * Test LocateProtocol
+ */
+ ret = boottime->locate_protocol(&guid1, NULL, (void **)&interface);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("LocateProtocol failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (interface != &interface1 && interface != &interface4) {
+ efi_st_error("LocateProtocol failed to locate protocol\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /*
+ * Test UninstallMultipleProtocols
+ */
+ ret = boottime->uninstall_multiple_protocol_interfaces(
+ handle2,
+ &guid1, &interface4,
+ &guid2, &interface2,
+ NULL);
+ if (ret != EFI_SUCCESS) {
+ efi_st_todo("UninstallMultipleProtocolInterfaces failed\n");
+ /* This test is known to fail due to missing implementation */
+ }
+ /*
+ * Check that the protocols are really uninstalled.
+ */
+ count = buffer_size;
+ ret = boottime->locate_handle_buffer(BY_PROTOCOL, &guid1, NULL,
+ &count, &buffer);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("LocateHandleBuffer failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (count != 1) {
+ efi_st_todo("UninstallMultipleProtocolInterfaces failed to uninstall protocols\n");
+ /* This test is known to fail due to missing implementation */
+ }
+ ret = find_in_buffer(handle1, count, buffer);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to locate new handle\n");
+ return EFI_ST_FAILURE;
+ }
+ boottime->set_mem(buffer, sizeof(efi_handle_t) * buffer_size, 0);
+
+ /*
+ * Test ProtocolsPerHandle
+ */
+ ret = boottime->protocols_per_handle(handle1,
+ &prot_buffer, &prot_count);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Failed to get protocols per handle\n");
+ return EFI_ST_FAILURE;
+ }
+ if (prot_count != 2) {
+ efi_st_error("Failed to get protocols per handle\n");
+ return EFI_ST_FAILURE;
+ }
+ if (efi_st_memcmp(prot_buffer[0], &guid1, 16) &&
+ efi_st_memcmp(prot_buffer[1], &guid1, 16)) {
+ efi_st_error("Failed to get protocols per handle\n");
+ return EFI_ST_FAILURE;
+ }
+ if (efi_st_memcmp(prot_buffer[0], &guid3, 16) &&
+ efi_st_memcmp(prot_buffer[1], &guid3, 16)) {
+ efi_st_error("Failed to get protocols per handle\n");
+ return EFI_ST_FAILURE;
+ }
+
+ /*
+ * Uninstall remaining protocols
+ */
+ ret = boottime->uninstall_protocol_interface(handle1, &guid1,
+ &interface1);
+ if (ret != EFI_SUCCESS) {
+ efi_st_todo("UninstallProtocolInterface failed\n");
+ /* This test is known to fail due to missing implementation */
+ }
+ ret = boottime->handle_protocol(handle1, &guid1, (void **)&interface);
+ if (ret == EFI_SUCCESS) {
+ efi_st_todo("UninstallProtocolInterface failed\n");
+ /* This test is known to fail due to missing implementation */
+ }
+ ret = boottime->uninstall_protocol_interface(handle1, &guid3,
+ &interface1);
+ if (ret != EFI_SUCCESS) {
+ efi_st_todo("UninstallProtocolInterface failed\n");
+ /* This test is known to fail due to missing implementation */
+ }
+
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(protserv) = {
+ .name = "manage protocols",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .setup = setup,
+ .execute = execute,
+ .teardown = teardown,
+};
diff --git a/lib/efi_selftest/efi_selftest_snp.c b/lib/efi_selftest/efi_selftest_snp.c
index bdd6ce20da..cc0705fb59 100644
--- a/lib/efi_selftest/efi_selftest_snp.c
+++ b/lib/efi_selftest/efi_selftest_snp.c
@@ -260,7 +260,7 @@ static int execute(void)
{
efi_status_t ret;
struct efi_event *events[2];
- size_t index;
+ efi_uintn_t index;
union {
struct dhcp p;
u8 b[PKTSIZE];
diff --git a/lib/efi_selftest/efi_selftest_textoutput.c b/lib/efi_selftest/efi_selftest_textoutput.c
new file mode 100644
index 0000000000..6e8c90cc8b
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_textoutput.c
@@ -0,0 +1,53 @@
+/*
+ * efi_selftest_textoutput
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * Test the EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.
+ *
+ * The following services are tested:
+ * OutputString, TestString, SetAttribute.
+ */
+
+#include <efi_selftest.h>
+
+/*
+ * Execute unit test.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int execute(void)
+{
+ size_t foreground;
+ size_t background;
+ size_t attrib;
+ efi_status_t ret;
+
+ /* SetAttribute */
+ efi_st_printf("\nColor palette\n");
+ for (foreground = 0; foreground < 0x10; ++foreground) {
+ for (background = 0; background < 0x80; background += 0x10) {
+ attrib = foreground | background;
+ con_out->set_attribute(con_out, attrib);
+ efi_st_printf("%p", (void *)attrib);
+ }
+ con_out->set_attribute(con_out, 0);
+ efi_st_printf("\n");
+ }
+ /* TestString */
+ ret = con_out->test_string(con_out,
+ L" !\"#$%&'()*+,-./0-9:;<=>?@A-Z[\\]^_`a-z{|}~\n");
+ if (ret != EFI_ST_SUCCESS) {
+ efi_st_error("TestString failed for ANSI characters\n");
+ return EFI_ST_FAILURE;
+ }
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(textoutput) = {
+ .name = "text output",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .execute = execute,
+};
diff --git a/lib/efi_selftest/efi_selftest_tpl.c b/lib/efi_selftest/efi_selftest_tpl.c
index ddb67ed268..6ea0bb7177 100644
--- a/lib/efi_selftest/efi_selftest_tpl.c
+++ b/lib/efi_selftest/efi_selftest_tpl.c
@@ -111,9 +111,9 @@ static int teardown(void)
*/
static int execute(void)
{
- size_t index;
+ efi_uintn_t index;
efi_status_t ret;
- UINTN old_tpl;
+ efi_uintn_t old_tpl;
/* Set 10 ms timer */
notification_count = 0;
diff --git a/lib/efi_selftest/efi_selftest_util.c b/lib/efi_selftest/efi_selftest_util.c
index 5cffe383d8..1b17bf4d4b 100644
--- a/lib/efi_selftest/efi_selftest_util.c
+++ b/lib/efi_selftest/efi_selftest_util.c
@@ -21,5 +21,14 @@ int efi_st_memcmp(const void *buf1, const void *buf2, size_t length)
++pos1;
++pos2;
}
- return EFI_ST_SUCCESS;
+ return 0;
+}
+
+int efi_st_strcmp_16_8(const u16 *buf1, const char *buf2)
+{
+ for (; *buf1 || *buf2; ++buf1, ++buf2) {
+ if (*buf1 != *buf2)
+ return *buf1 - *buf2;
+ }
+ return 0;
}
diff --git a/lib/efi_selftest/efi_selftest_watchdog.c b/lib/efi_selftest/efi_selftest_watchdog.c
new file mode 100644
index 0000000000..e4af38407f
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_watchdog.c
@@ -0,0 +1,231 @@
+/*
+ * efi_selftest_watchdog
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * The 'watchdog timer' unit test checks that the watchdog timer
+ * will not cause a system restart during the timeout period after
+ * a timer reset.
+ *
+ * The 'watchdog reboot' unit test checks that the watchdog timer
+ * actually reboots the system after a timeout. The test is only
+ * executed on explicit request. Use the following commands:
+ *
+ * setenv efi_selftest watchdog reboot
+ * bootefi selftest
+ */
+
+#include <efi_selftest.h>
+
+/*
+ * This is the communication structure for the notification function.
+ */
+struct notify_context {
+ /* Status code returned when resetting watchdog */
+ efi_status_t status;
+ /* Number of invocations of the notification function */
+ unsigned int timer_ticks;
+};
+
+static struct efi_event *event_notify;
+static struct efi_event *event_wait;
+static struct efi_boot_services *boottime;
+static struct notify_context notification_context;
+static bool watchdog_reset;
+
+/*
+ * Notification function, increments the notfication count if parameter
+ * context is provided.
+ *
+ * @event notified event
+ * @context pointer to the timeout
+ */
+static void EFIAPI notify(struct efi_event *event, void *context)
+{
+ struct notify_context *notify_context = context;
+ efi_status_t ret = EFI_SUCCESS;
+
+ if (!notify_context)
+ return;
+
+ /* Reset watchdog timer to one second */
+ ret = boottime->set_watchdog_timer(1, 0, 0, NULL);
+ if (ret != EFI_SUCCESS)
+ notify_context->status = ret;
+ /* Count number of calls */
+ notify_context->timer_ticks++;
+}
+
+/*
+ * Setup unit test.
+ *
+ * Create two timer events.
+ * One with EVT_NOTIFY_SIGNAL, the other with EVT_NOTIFY_WAIT.
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int setup(const efi_handle_t handle,
+ const struct efi_system_table *systable)
+{
+ efi_status_t ret;
+
+ boottime = systable->boottime;
+
+ notification_context.status = EFI_SUCCESS;
+ notification_context.timer_ticks = 0;
+ ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK, notify,
+ (void *)&notification_context,
+ &event_notify);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("could not create event\n");
+ return EFI_ST_FAILURE;
+ }
+ ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_WAIT,
+ TPL_CALLBACK, notify, NULL, &event_wait);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("could not create event\n");
+ return EFI_ST_FAILURE;
+ }
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute the test resetting the watchdog in a timely manner. No reboot occurs.
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int setup_timer(const efi_handle_t handle,
+ const struct efi_system_table *systable)
+{
+ watchdog_reset = true;
+ return setup(handle, systable);
+}
+
+/*
+ * Execute the test without resetting the watchdog. A system reboot occurs.
+ *
+ * @handle: handle of the loaded image
+ * @systable: system table
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int setup_reboot(const efi_handle_t handle,
+ const struct efi_system_table *systable)
+{
+ watchdog_reset = false;
+ return setup(handle, systable);
+}
+
+/*
+ * Tear down unit test.
+ *
+ * Close the events created in setup.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int teardown(void)
+{
+ efi_status_t ret;
+
+ /* Set the watchdog timer to the five minute default value */
+ ret = boottime->set_watchdog_timer(300, 0, 0, NULL);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Setting watchdog timer failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (event_notify) {
+ ret = boottime->close_event(event_notify);
+ event_notify = NULL;
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Could not close event\n");
+ return EFI_ST_FAILURE;
+ }
+ }
+ if (event_wait) {
+ ret = boottime->close_event(event_wait);
+ event_wait = NULL;
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Could not close event\n");
+ return EFI_ST_FAILURE;
+ }
+ }
+ return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute unit test.
+ *
+ * Run a 600 ms periodic timer that resets the watchdog to one second
+ * on every timer tick.
+ *
+ * Run a 1350 ms single shot timer and check that the 600ms timer has
+ * been called 2 times.
+ *
+ * @return: EFI_ST_SUCCESS for success
+ */
+static int execute(void)
+{
+ size_t index;
+ efi_status_t ret;
+
+ /* Set the watchdog timeout to one second */
+ ret = boottime->set_watchdog_timer(1, 0, 0, NULL);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Setting watchdog timer failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (watchdog_reset) {
+ /* Set 600 ms timer */
+ ret = boottime->set_timer(event_notify, EFI_TIMER_PERIODIC,
+ 6000000);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Could not set timer\n");
+ return EFI_ST_FAILURE;
+ }
+ }
+ /* Set 1350 ms timer */
+ ret = boottime->set_timer(event_wait, EFI_TIMER_RELATIVE, 13500000);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Could not set timer\n");
+ return EFI_ST_FAILURE;
+ }
+
+ ret = boottime->wait_for_event(1, &event_wait, &index);
+ if (ret != EFI_SUCCESS) {
+ efi_st_error("Could not wait for event\n");
+ return EFI_ST_FAILURE;
+ }
+ if (notification_context.status != EFI_SUCCESS) {
+ efi_st_error("Setting watchdog timer failed\n");
+ return EFI_ST_FAILURE;
+ }
+ if (notification_context.timer_ticks != 2) {
+ efi_st_error("The timer was called %u times, expected 2.\n",
+ notification_context.timer_ticks);
+ return EFI_ST_FAILURE;
+ }
+ return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(watchdog1) = {
+ .name = "watchdog timer",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .setup = setup_timer,
+ .execute = execute,
+ .teardown = teardown,
+};
+
+EFI_UNIT_TEST(watchdog2) = {
+ .name = "watchdog reboot",
+ .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+ .setup = setup_reboot,
+ .execute = execute,
+ .teardown = teardown,
+ .on_request = true,
+};