Commit 623b3a579765f8e05723bd1eff6f8c7e56d33922

Authored by Heinrich Schuchardt
Committed by Alexander Graf
1 parent ea630ce9ea

efi_selftest: provide an EFI selftest application

A testing framework for the EFI API is provided.
It can be executed with the 'bootefi selftest' command.

It is coded in a way that at a later stage we may turn it
into a standalone EFI application. The current build system
does not allow this yet.

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.

Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
Signed-off-by: Alexander Graf <agraf@suse.de>

Showing 10 changed files with 558 additions and 5 deletions Side-by-side Diff

... ... @@ -259,8 +259,9 @@
259 259 M: Alexander Graf <agraf@suse.de>
260 260 S: Maintained
261 261 T: git git://github.com/agraf/u-boot.git
262   -F: include/efi_loader.h
263   -F: lib/efi_loader/
  262 +F: include/efi*
  263 +F: lib/efi*
  264 +F: test/py/tests/test_efi*
264 265 F: cmd/bootefi.c
265 266  
266 267 FLATTENED DEVICE TREE
... ... @@ -222,6 +222,8 @@
222 222 for testing that EFI is working at a basic level, and for bringing
223 223 up EFI support on a new architecture.
224 224  
  225 +source lib/efi_selftest/Kconfig
  226 +
225 227 config CMD_BOOTMENU
226 228 bool "bootmenu"
227 229 select MENU
... ... @@ -285,7 +285,6 @@
285 285 return efi_do_enter(&loaded_image_info, &systab, entry);
286 286 }
287 287  
288   -
289 288 /* Interpreter command to boot an arbitrary EFI image from memory */
290 289 static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
291 290 {
... ... @@ -307,6 +306,22 @@
307 306 memcpy((char *)addr, __efi_helloworld_begin, size);
308 307 } else
309 308 #endif
  309 +#ifdef CONFIG_CMD_BOOTEFI_SELFTEST
  310 + if (!strcmp(argv[1], "selftest")) {
  311 + /*
  312 + * gd lives in a fixed register which may get clobbered while we
  313 + * execute the payload. So save it here and restore it on every
  314 + * callback entry
  315 + */
  316 + efi_save_gd();
  317 + /* Initialize and populate EFI object list */
  318 + if (!efi_obj_list_initalized)
  319 + efi_init_obj_list();
  320 + loaded_image_info.device_handle = bootefi_device_path;
  321 + loaded_image_info.file_path = bootefi_image_path;
  322 + return efi_selftest(&loaded_image_info, &systab);
  323 + } else
  324 +#endif
310 325 {
311 326 saddr = argv[1];
312 327  
... ... @@ -336,8 +351,12 @@
336 351 " If specified, the device tree located at <fdt address> gets\n"
337 352 " exposed as EFI configuration table.\n"
338 353 #ifdef CONFIG_CMD_BOOTEFI_HELLO
339   - "hello\n"
340   - " - boot a sample Hello World application stored within U-Boot"
  354 + "bootefi hello\n"
  355 + " - boot a sample Hello World application stored within U-Boot\n"
  356 +#endif
  357 +#ifdef CONFIG_CMD_BOOTEFI_SELFTEST
  358 + "bootefi selftest\n"
  359 + " - boot an EFI selftest application stored within U-Boot\n"
341 360 #endif
342 361 ;
343 362 #endif
include/efi_loader.h
... ... @@ -254,6 +254,15 @@
254 254 struct efi_time_cap *capabilities);
255 255 void efi_get_time_init(void);
256 256  
  257 +#ifdef CONFIG_CMD_BOOTEFI_SELFTEST
  258 +/*
  259 + * Entry point for the tests of the EFI API.
  260 + * It is called by 'bootefi selftest'
  261 + */
  262 +efi_status_t EFIAPI efi_selftest(efi_handle_t image_handle,
  263 + struct efi_system_table *systab);
  264 +#endif
  265 +
257 266 #else /* defined(EFI_LOADER) && !defined(CONFIG_SPL_BUILD) */
258 267  
259 268 /* Without CONFIG_EFI_LOADER we don't have a runtime section, stub it out */
include/efi_selftest.h
  1 +/*
  2 + * EFI application loader
  3 + *
  4 + * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
  5 + *
  6 + * SPDX-License-Identifier: GPL-2.0+
  7 + */
  8 +
  9 +#ifndef _EFI_SELFTEST_H
  10 +#define _EFI_SELFTEST_H
  11 +
  12 +#include <common.h>
  13 +#include <efi.h>
  14 +#include <efi_api.h>
  15 +#include <linker_lists.h>
  16 +
  17 +/*
  18 + * Prints an error message.
  19 + *
  20 + * @... format string followed by fields to print
  21 + */
  22 +#define efi_st_error(...) \
  23 + efi_st_printf("%s(%u):\nERROR: ", __FILE__, __LINE__); \
  24 + efi_st_printf(__VA_ARGS__) \
  25 +
  26 +/*
  27 + * A test may be setup and executed at boottime,
  28 + * it may be setup at boottime and executed at runtime,
  29 + * or it may be setup and executed at runtime.
  30 + */
  31 +enum efi_test_phase {
  32 + EFI_EXECUTE_BEFORE_BOOTTIME_EXIT = 1,
  33 + EFI_SETUP_BEFORE_BOOTTIME_EXIT,
  34 + EFI_SETUP_AFTER_BOOTTIME_EXIT,
  35 +};
  36 +
  37 +extern struct efi_simple_text_output_protocol *con_out;
  38 +extern struct efi_simple_input_interface *con_in;
  39 +
  40 +/*
  41 + * Exit the boot services.
  42 + *
  43 + * The size of the memory map is determined.
  44 + * Pool memory is allocated to copy the memory map.
  45 + * The memory amp is copied and the map key is obtained.
  46 + * The map key is used to exit the boot services.
  47 + */
  48 +void efi_st_exit_boot_services(void);
  49 +
  50 +/*
  51 + * Print a pointer to an u16 string
  52 + *
  53 + * @pointer: pointer
  54 + * @buf: pointer to buffer address
  55 + * on return position of terminating zero word
  56 + */
  57 +void efi_st_printf(const char *fmt, ...)
  58 + __attribute__ ((format (__printf__, 1, 2)));
  59 +
  60 +/*
  61 + * Reads an Unicode character from the input device.
  62 + *
  63 + * @return: Unicode character
  64 + */
  65 +u16 efi_st_get_key(void);
  66 +
  67 +/**
  68 + * struct efi_unit_test - EFI unit test
  69 + *
  70 + * An efi_unit_test provides a interface to an EFI unit test.
  71 + *
  72 + * @name: name of unit test
  73 + * @phase: specifies when setup and execute are executed
  74 + * @setup: set up the unit test
  75 + * @teardown: tear down the unit test
  76 + * @execute: execute the unit test
  77 + */
  78 +struct efi_unit_test {
  79 + const char *name;
  80 + const enum efi_test_phase phase;
  81 + int (*setup)(const efi_handle_t handle,
  82 + const struct efi_system_table *systable);
  83 + int (*execute)(void);
  84 + int (*teardown)(void);
  85 +};
  86 +
  87 +/* Declare a new EFI unit test */
  88 +#define EFI_UNIT_TEST(__name) \
  89 + ll_entry_declare(struct efi_unit_test, __name, efi_unit_test)
  90 +
  91 +#endif /* _EFI_SELFTEST_H */
... ... @@ -9,6 +9,7 @@
9 9  
10 10 obj-$(CONFIG_EFI) += efi/
11 11 obj-$(CONFIG_EFI_LOADER) += efi_loader/
  12 +obj-$(CONFIG_EFI_LOADER) += efi_selftest/
12 13 obj-$(CONFIG_LZMA) += lzma/
13 14 obj-$(CONFIG_LZO) += lzo/
14 15 obj-$(CONFIG_BZIP2) += bzip2/
lib/efi_selftest/Kconfig
  1 +config CMD_BOOTEFI_SELFTEST
  2 + bool "Allow booting an EFI efi_selftest"
  3 + depends on CMD_BOOTEFI
  4 + help
  5 + This adds an EFI test application to U-Boot that can be executed
  6 + with the 'bootefi selftest' command. It provides extended tests of
  7 + the EFI API implementation.
lib/efi_selftest/Makefile
  1 +:
  2 +# (C) Copyright 2017, Heinrich Schuchardt <xypron.glpk@gmx.de>
  3 +#
  4 +# SPDX-License-Identifier: GPL-2.0+
  5 +#
  6 +
  7 +# This file only gets included with CONFIG_EFI_LOADER set, so all
  8 +# object inclusion implicitly depends on it
  9 +
  10 +CFLAGS_efi_selftest.o := $(CFLAGS_EFI)
  11 +CFLAGS_REMOVE_efi_selftest.o := $(CFLAGS_NON_EFI)
  12 +CFLAGS_efi_selftest_console.o := $(CFLAGS_EFI)
  13 +CFLAGS_REMOVE_efi_selftest_console.o := $(CFLAGS_NON_EFI)
  14 +
  15 +obj-$(CONFIG_CMD_BOOTEFI_SELFTEST) += \
  16 +efi_selftest.o \
  17 +efi_selftest_console.o
lib/efi_selftest/efi_selftest.c
  1 +/*
  2 + * EFI efi_selftest
  3 + *
  4 + * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
  5 + *
  6 + * SPDX-License-Identifier: GPL-2.0+
  7 + */
  8 +
  9 +#include <efi_selftest.h>
  10 +#include <vsprintf.h>
  11 +
  12 +static const struct efi_system_table *systable;
  13 +static const struct efi_boot_services *boottime;
  14 +static const struct efi_runtime_services *runtime;
  15 +static efi_handle_t handle;
  16 +static u16 reset_message[] = L"Selftest completed";
  17 +
  18 +/*
  19 + * Exit the boot services.
  20 + *
  21 + * The size of the memory map is determined.
  22 + * Pool memory is allocated to copy the memory map.
  23 + * The memory amp is copied and the map key is obtained.
  24 + * The map key is used to exit the boot services.
  25 + */
  26 +void efi_st_exit_boot_services(void)
  27 +{
  28 + unsigned long map_size = 0;
  29 + unsigned long map_key;
  30 + unsigned long desc_size;
  31 + u32 desc_version;
  32 + efi_status_t ret;
  33 + struct efi_mem_desc *memory_map;
  34 +
  35 + ret = boottime->get_memory_map(&map_size, NULL, &map_key, &desc_size,
  36 + &desc_version);
  37 + if (ret != EFI_BUFFER_TOO_SMALL) {
  38 + efi_st_printf("ERROR: GetMemoryMap did not return "
  39 + "EFI_BUFFER_TOO_SMALL\n");
  40 + return;
  41 + }
  42 + /* Allocate extra space for newly allocated memory */
  43 + map_size += sizeof(struct efi_mem_desc);
  44 + ret = boottime->allocate_pool(EFI_BOOT_SERVICES_DATA, map_size,
  45 + (void **)&memory_map);
  46 + if (ret != EFI_SUCCESS) {
  47 + efi_st_printf("ERROR: AllocatePool did not return "
  48 + "EFI_SUCCESS\n");
  49 + return;
  50 + }
  51 + ret = boottime->get_memory_map(&map_size, memory_map, &map_key,
  52 + &desc_size, &desc_version);
  53 + if (ret != EFI_SUCCESS) {
  54 + efi_st_printf("ERROR: GetMemoryMap did not return "
  55 + "EFI_SUCCESS\n");
  56 + return;
  57 + }
  58 + ret = boottime->exit_boot_services(handle, map_key);
  59 + if (ret != EFI_SUCCESS) {
  60 + efi_st_printf("ERROR: ExitBootServices did not return "
  61 + "EFI_SUCCESS\n");
  62 + return;
  63 + }
  64 + efi_st_printf("\nBoot services terminated\n");
  65 +}
  66 +
  67 +/*
  68 + * Set up a test.
  69 + *
  70 + * @test the test to be executed
  71 + * @failures counter that will be incremented if a failure occurs
  72 + */
  73 +static int setup(struct efi_unit_test *test, unsigned int *failures)
  74 +{
  75 + int ret;
  76 +
  77 + if (!test->setup)
  78 + return 0;
  79 + efi_st_printf("\nSetting up '%s'\n", test->name);
  80 + ret = test->setup(handle, systable);
  81 + if (ret) {
  82 + efi_st_printf("ERROR: Setting up '%s' failed\n", test->name);
  83 + ++*failures;
  84 + } else {
  85 + efi_st_printf("Setting up '%s' succeeded\n", test->name);
  86 + }
  87 + return ret;
  88 +}
  89 +
  90 +/*
  91 + * Execute a test.
  92 + *
  93 + * @test the test to be executed
  94 + * @failures counter that will be incremented if a failure occurs
  95 + */
  96 +static int execute(struct efi_unit_test *test, unsigned int *failures)
  97 +{
  98 + int ret;
  99 +
  100 + if (!test->execute)
  101 + return 0;
  102 + efi_st_printf("\nExecuting '%s'\n", test->name);
  103 + ret = test->execute();
  104 + if (ret) {
  105 + efi_st_printf("ERROR: Executing '%s' failed\n", test->name);
  106 + ++*failures;
  107 + } else {
  108 + efi_st_printf("Executing '%s' succeeded\n", test->name);
  109 + }
  110 + return ret;
  111 +}
  112 +
  113 +/*
  114 + * Tear down a test.
  115 + *
  116 + * @test the test to be torn down
  117 + * @failures counter that will be incremented if a failure occurs
  118 + */
  119 +static int teardown(struct efi_unit_test *test, unsigned int *failures)
  120 +{
  121 + int ret;
  122 +
  123 + if (!test->teardown)
  124 + return 0;
  125 + efi_st_printf("\nTearing down '%s'\n", test->name);
  126 + ret = test->teardown();
  127 + if (ret) {
  128 + efi_st_printf("ERROR: Tearing down '%s' failed\n", test->name);
  129 + ++*failures;
  130 + } else {
  131 + efi_st_printf("Tearing down '%s' succeeded\n", test->name);
  132 + }
  133 + return ret;
  134 +}
  135 +
  136 +/*
  137 + * Execute selftest of the EFI API
  138 + *
  139 + * This is the main entry point of the EFI selftest application.
  140 + *
  141 + * All tests use a driver model and are run in three phases:
  142 + * setup, execute, teardown.
  143 + *
  144 + * A test may be setup and executed at boottime,
  145 + * it may be setup at boottime and executed at runtime,
  146 + * or it may be setup and executed at runtime.
  147 + *
  148 + * After executing all tests the system is reset.
  149 + *
  150 + * @image_handle: handle of the loaded EFI image
  151 + * @systab: EFI system table
  152 + */
  153 +efi_status_t EFIAPI efi_selftest(efi_handle_t image_handle,
  154 + struct efi_system_table *systab)
  155 +{
  156 + struct efi_unit_test *test;
  157 + unsigned int failures = 0;
  158 +
  159 + systable = systab;
  160 + boottime = systable->boottime;
  161 + runtime = systable->runtime;
  162 + handle = image_handle;
  163 + con_out = systable->con_out;
  164 + con_in = systable->con_in;
  165 +
  166 + efi_st_printf("\nTesting EFI API implementation\n");
  167 +
  168 + efi_st_printf("\nNumber of tests to execute: %u\n",
  169 + ll_entry_count(struct efi_unit_test, efi_unit_test));
  170 +
  171 + /* Execute boottime tests */
  172 + for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
  173 + test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) {
  174 + if (test->phase == EFI_EXECUTE_BEFORE_BOOTTIME_EXIT) {
  175 + setup(test, &failures);
  176 + execute(test, &failures);
  177 + teardown(test, &failures);
  178 + }
  179 + }
  180 +
  181 + /* Execute mixed tests */
  182 + for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
  183 + test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) {
  184 + if (test->phase == EFI_SETUP_BEFORE_BOOTTIME_EXIT)
  185 + setup(test, &failures);
  186 + }
  187 +
  188 + efi_st_exit_boot_services();
  189 +
  190 + for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
  191 + test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) {
  192 + if (test->phase == EFI_SETUP_BEFORE_BOOTTIME_EXIT) {
  193 + execute(test, &failures);
  194 + teardown(test, &failures);
  195 + }
  196 + }
  197 +
  198 + /* Execute runtime tests */
  199 + for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
  200 + test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) {
  201 + if (test->phase == EFI_SETUP_AFTER_BOOTTIME_EXIT) {
  202 + setup(test, &failures);
  203 + execute(test, &failures);
  204 + teardown(test, &failures);
  205 + }
  206 + }
  207 +
  208 + /* Give feedback */
  209 + efi_st_printf("\nSummary: %u failures\n\n", failures);
  210 +
  211 + /* Reset system */
  212 + efi_st_printf("Preparing for reset. Press any key.\n");
  213 + efi_st_get_key();
  214 + runtime->reset_system(EFI_RESET_WARM, EFI_NOT_READY,
  215 + sizeof(reset_message), reset_message);
  216 + efi_st_printf("\nERROR: reset failed.\n");
  217 +
  218 + return EFI_UNSUPPORTED;
  219 +}
lib/efi_selftest/efi_selftest_console.c
  1 +/*
  2 + * EFI efi_selftest
  3 + *
  4 + * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
  5 + *
  6 + * SPDX-License-Identifier: GPL-2.0+
  7 + */
  8 +
  9 +#include <efi_selftest.h>
  10 +#include <vsprintf.h>
  11 +
  12 +struct efi_simple_text_output_protocol *con_out;
  13 +struct efi_simple_input_interface *con_in;
  14 +
  15 +/*
  16 + * Print a pointer to an u16 string
  17 + *
  18 + * @pointer: pointer
  19 + * @buf: pointer to buffer address
  20 + * on return position of terminating zero word
  21 + */
  22 +static void pointer(void *pointer, u16 **buf)
  23 +{
  24 + int i;
  25 + u16 c;
  26 + uintptr_t p = (uintptr_t)pointer;
  27 + u16 *pos = *buf;
  28 +
  29 + for (i = 8 * sizeof(p) - 4; i >= 0; i -= 4) {
  30 + c = (p >> i) & 0x0f;
  31 + c += '0';
  32 + if (c > '9')
  33 + c += 'a' - '9' - 1;
  34 + *pos++ = c;
  35 + }
  36 + *pos = 0;
  37 + *buf = pos;
  38 +}
  39 +
  40 +/*
  41 + * Print an unsigned 32bit value as decimal number to an u16 string
  42 + *
  43 + * @value: value to be printed
  44 + * @buf: pointer to buffer address
  45 + * on return position of terminating zero word
  46 + */
  47 +static void uint2dec(u32 value, u16 **buf)
  48 +{
  49 + u16 *pos = *buf;
  50 + int i;
  51 + u16 c;
  52 + u64 f;
  53 +
  54 + /*
  55 + * Increment by .5 and multiply with
  56 + * (2 << 60) / 1,000,000,000 = 0x44B82FA0.9B5A52CC
  57 + * to move the first digit to bit 60-63.
  58 + */
  59 + f = 0x225C17D0;
  60 + f += (0x9B5A52DULL * value) >> 28;
  61 + f += 0x44B82FA0ULL * value;
  62 +
  63 + for (i = 0; i < 10; ++i) {
  64 + /* Write current digit */
  65 + c = f >> 60;
  66 + if (c || pos != *buf)
  67 + *pos++ = c + '0';
  68 + /* Eliminate current digit */
  69 + f &= 0xfffffffffffffff;
  70 + /* Get next digit */
  71 + f *= 0xaULL;
  72 + }
  73 + if (pos == *buf)
  74 + *pos++ = '0';
  75 + *pos = 0;
  76 + *buf = pos;
  77 +}
  78 +
  79 +/*
  80 + * Print a signed 32bit value as decimal number to an u16 string
  81 + *
  82 + * @value: value to be printed
  83 + * @buf: pointer to buffer address
  84 + * on return position of terminating zero word
  85 + */
  86 +static void int2dec(s32 value, u16 **buf)
  87 +{
  88 + u32 u;
  89 + u16 *pos = *buf;
  90 +
  91 + if (value < 0) {
  92 + *pos++ = '-';
  93 + u = -value;
  94 + } else {
  95 + u = value;
  96 + }
  97 + uint2dec(u, &pos);
  98 + *buf = pos;
  99 +}
  100 +
  101 +/*
  102 + * Print a formatted string to the EFI console
  103 + *
  104 + * @fmt: format string
  105 + * @...: optional arguments
  106 + */
  107 +void efi_st_printf(const char *fmt, ...)
  108 +{
  109 + va_list args;
  110 + u16 buf[160];
  111 + const char *c;
  112 + u16 *pos = buf;
  113 + const char *s;
  114 +
  115 + va_start(args, fmt);
  116 +
  117 + c = fmt;
  118 + for (; *c; ++c) {
  119 + switch (*c) {
  120 + case '\\':
  121 + ++c;
  122 + switch (*c) {
  123 + case '\0':
  124 + --c;
  125 + break;
  126 + case 'n':
  127 + *pos++ = '\n';
  128 + break;
  129 + case 'r':
  130 + *pos++ = '\r';
  131 + break;
  132 + case 't':
  133 + *pos++ = '\t';
  134 + break;
  135 + default:
  136 + *pos++ = *c;
  137 + }
  138 + break;
  139 + case '%':
  140 + ++c;
  141 + switch (*c) {
  142 + case '\0':
  143 + --c;
  144 + break;
  145 + case 'd':
  146 + int2dec(va_arg(args, s32), &pos);
  147 + break;
  148 + case 'p':
  149 + pointer(va_arg(args, void*), &pos);
  150 + break;
  151 + case 's':
  152 + s = va_arg(args, const char *);
  153 + for (; *s; ++s)
  154 + *pos++ = *s;
  155 + break;
  156 + case 'u':
  157 + uint2dec(va_arg(args, u32), &pos);
  158 + break;
  159 + default:
  160 + break;
  161 + }
  162 + break;
  163 + default:
  164 + *pos++ = *c;
  165 + }
  166 + }
  167 + va_end(args);
  168 + *pos = 0;
  169 + con_out->output_string(con_out, buf);
  170 +}
  171 +
  172 +/*
  173 + * Reads an Unicode character from the input device.
  174 + *
  175 + * @return: Unicode character
  176 + */
  177 +u16 efi_st_get_key(void)
  178 +{
  179 + struct efi_input_key input_key;
  180 + efi_status_t ret;
  181 +
  182 + /* Wait for next key */
  183 + do {
  184 + ret = con_in->read_key_stroke(con_in, &input_key);
  185 + } while (ret == EFI_NOT_READY);
  186 + return input_key.unicode_char;
  187 +}