uboot/lib/efi_selftest/efi_selftest.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * EFI efi_selftest
   4 *
   5 * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
   6 */
   7
   8#include <command.h>
   9#include <efi_selftest.h>
  10#include <vsprintf.h>
  11
  12/* Constants for test step bitmap */
  13#define EFI_ST_SETUP    1
  14#define EFI_ST_EXECUTE  2
  15#define EFI_ST_TEARDOWN 4
  16
  17static const struct efi_system_table *systable;
  18static const struct efi_boot_services *boottime;
  19static const struct efi_runtime_services *runtime;
  20static efi_handle_t handle;
  21static u16 reset_message[] = L"Selftest completed";
  22static int *setup_status;
  23
  24/*
  25 * Exit the boot services.
  26 *
  27 * The size of the memory map is determined.
  28 * Pool memory is allocated to copy the memory map.
  29 * The memory map is copied and the map key is obtained.
  30 * The map key is used to exit the boot services.
  31 */
  32void efi_st_exit_boot_services(void)
  33{
  34        efi_uintn_t map_size = 0;
  35        efi_uintn_t map_key;
  36        efi_uintn_t desc_size;
  37        u32 desc_version;
  38        efi_status_t ret;
  39        struct efi_mem_desc *memory_map;
  40
  41        /* Do not detach devices in ExitBootServices. We need the console. */
  42        efi_st_keep_devices = true;
  43
  44        ret = boottime->get_memory_map(&map_size, NULL, &map_key, &desc_size,
  45                                       &desc_version);
  46        if (ret != EFI_BUFFER_TOO_SMALL) {
  47                efi_st_error(
  48                        "GetMemoryMap did not return EFI_BUFFER_TOO_SMALL\n");
  49                return;
  50        }
  51        /* Allocate extra space for newly allocated memory */
  52        map_size += sizeof(struct efi_mem_desc);
  53        ret = boottime->allocate_pool(EFI_BOOT_SERVICES_DATA, map_size,
  54                                      (void **)&memory_map);
  55        if (ret != EFI_SUCCESS) {
  56                efi_st_error("AllocatePool did not return EFI_SUCCESS\n");
  57                return;
  58        }
  59        ret = boottime->get_memory_map(&map_size, memory_map, &map_key,
  60                                       &desc_size, &desc_version);
  61        if (ret != EFI_SUCCESS) {
  62                efi_st_error("GetMemoryMap did not return EFI_SUCCESS\n");
  63                return;
  64        }
  65        ret = boottime->exit_boot_services(handle, map_key);
  66        if (ret != EFI_SUCCESS) {
  67                efi_st_error("ExitBootServices did not return EFI_SUCCESS\n");
  68                return;
  69        }
  70        efi_st_printc(EFI_WHITE, "\nBoot services terminated\n");
  71}
  72
  73/*
  74 * Set up a test.
  75 *
  76 * @test        the test to be executed
  77 * @failures    counter that will be incremented if a failure occurs
  78 * @return      EFI_ST_SUCCESS for success
  79 */
  80static int setup(struct efi_unit_test *test, unsigned int *failures)
  81{
  82        int ret;
  83
  84        if (!test->setup)
  85                return EFI_ST_SUCCESS;
  86        efi_st_printc(EFI_LIGHTBLUE, "\nSetting up '%s'\n", test->name);
  87        ret = test->setup(handle, systable);
  88        if (ret != EFI_ST_SUCCESS) {
  89                efi_st_error("Setting up '%s' failed\n", test->name);
  90                ++*failures;
  91        } else {
  92                efi_st_printc(EFI_LIGHTGREEN,
  93                              "Setting up '%s' succeeded\n", test->name);
  94        }
  95        return ret;
  96}
  97
  98/*
  99 * Execute a test.
 100 *
 101 * @test        the test to be executed
 102 * @failures    counter that will be incremented if a failure occurs
 103 * @return      EFI_ST_SUCCESS for success
 104 */
 105static int execute(struct efi_unit_test *test, unsigned int *failures)
 106{
 107        int ret;
 108
 109        if (!test->execute)
 110                return EFI_ST_SUCCESS;
 111        efi_st_printc(EFI_LIGHTBLUE, "\nExecuting '%s'\n", test->name);
 112        ret = test->execute();
 113        if (ret != EFI_ST_SUCCESS) {
 114                efi_st_error("Executing '%s' failed\n", test->name);
 115                ++*failures;
 116        } else {
 117                efi_st_printc(EFI_LIGHTGREEN,
 118                              "Executing '%s' succeeded\n", test->name);
 119        }
 120        return ret;
 121}
 122
 123/*
 124 * Tear down a test.
 125 *
 126 * @test        the test to be torn down
 127 * @failures    counter that will be incremented if a failure occurs
 128 * @return      EFI_ST_SUCCESS for success
 129 */
 130static int teardown(struct efi_unit_test *test, unsigned int *failures)
 131{
 132        int ret;
 133
 134        if (!test->teardown)
 135                return EFI_ST_SUCCESS;
 136        efi_st_printc(EFI_LIGHTBLUE, "\nTearing down '%s'\n", test->name);
 137        ret = test->teardown();
 138        if (ret != EFI_ST_SUCCESS) {
 139                efi_st_error("Tearing down '%s' failed\n", test->name);
 140                ++*failures;
 141        } else {
 142                efi_st_printc(EFI_LIGHTGREEN,
 143                              "Tearing down '%s' succeeded\n", test->name);
 144        }
 145        return ret;
 146}
 147
 148/*
 149 * Check that a test requiring reset exists.
 150 *
 151 * @testname:   name of the test
 152 * @return:     test, or NULL if not found
 153 */
 154static bool need_reset(const u16 *testname)
 155{
 156        struct efi_unit_test *test;
 157
 158        for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
 159             test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) {
 160                if (testname && efi_st_strcmp_16_8(testname, test->name))
 161                        continue;
 162                if (test->phase == EFI_SETUP_BEFORE_BOOTTIME_EXIT ||
 163                    test->phase == EFI_SETTING_VIRTUAL_ADDRESS_MAP)
 164                        return true;
 165        }
 166        return false;
 167}
 168
 169/*
 170 * Check that a test exists.
 171 *
 172 * @testname:   name of the test
 173 * @return:     test, or NULL if not found
 174 */
 175static struct efi_unit_test *find_test(const u16 *testname)
 176{
 177        struct efi_unit_test *test;
 178
 179        for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
 180             test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) {
 181                if (!efi_st_strcmp_16_8(testname, test->name))
 182                        return test;
 183        }
 184        efi_st_printf("\nTest '%ps' not found\n", testname);
 185        return NULL;
 186}
 187
 188/*
 189 * List all available tests.
 190 */
 191static void list_all_tests(void)
 192{
 193        struct efi_unit_test *test;
 194
 195        /* List all tests */
 196        efi_st_printf("\nAvailable tests:\n");
 197        for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
 198             test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) {
 199                efi_st_printf("'%s'%s\n", test->name,
 200                              test->on_request ? " - on request" : "");
 201        }
 202}
 203
 204/*
 205 * Execute test steps of one phase.
 206 *
 207 * @testname    name of a single selected test or NULL
 208 * @phase       test phase
 209 * @steps       steps to execute (mask with bits from EFI_ST_...)
 210 * failures     returns EFI_ST_SUCCESS if all test steps succeeded
 211 */
 212void efi_st_do_tests(const u16 *testname, unsigned int phase,
 213                     unsigned int steps, unsigned int *failures)
 214{
 215        int i = 0;
 216        struct efi_unit_test *test;
 217
 218        for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
 219             test < ll_entry_end(struct efi_unit_test, efi_unit_test);
 220             ++test, ++i) {
 221                if (testname ?
 222                    efi_st_strcmp_16_8(testname, test->name) : test->on_request)
 223                        continue;
 224                if (test->phase != phase)
 225                        continue;
 226                if (steps & EFI_ST_SETUP)
 227                        setup_status[i] = setup(test, failures);
 228                if (steps & EFI_ST_EXECUTE && setup_status[i] == EFI_ST_SUCCESS)
 229                        execute(test, failures);
 230                if (steps & EFI_ST_TEARDOWN)
 231                        teardown(test, failures);
 232        }
 233}
 234
 235/*
 236 * Execute selftest of the EFI API
 237 *
 238 * This is the main entry point of the EFI selftest application.
 239 *
 240 * All tests use a driver model and are run in three phases:
 241 * setup, execute, teardown.
 242 *
 243 * A test may be setup and executed at boottime,
 244 * it may be setup at boottime and executed at runtime,
 245 * or it may be setup and executed at runtime.
 246 *
 247 * After executing all tests the system is reset.
 248 *
 249 * @image_handle:       handle of the loaded EFI image
 250 * @systab:             EFI system table
 251 */
 252efi_status_t EFIAPI efi_selftest(efi_handle_t image_handle,
 253                                 struct efi_system_table *systab)
 254{
 255        unsigned int failures = 0;
 256        const u16 *testname = NULL;
 257        struct efi_loaded_image *loaded_image;
 258        efi_status_t ret;
 259
 260        systable = systab;
 261        boottime = systable->boottime;
 262        runtime = systable->runtime;
 263        handle = image_handle;
 264        con_out = systable->con_out;
 265        con_in = systable->con_in;
 266
 267        ret = boottime->handle_protocol(image_handle, &efi_guid_loaded_image,
 268                                        (void **)&loaded_image);
 269        if (ret != EFI_SUCCESS) {
 270                efi_st_error("Cannot open loaded image protocol\n");
 271                return ret;
 272        }
 273
 274        if (loaded_image->load_options)
 275                testname = (u16 *)loaded_image->load_options;
 276
 277        if (testname) {
 278                if (!efi_st_strcmp_16_8(testname, "list") ||
 279                    !find_test(testname)) {
 280                        list_all_tests();
 281                        /*
 282                         * TODO:
 283                         * Once the Exit boottime service is correctly
 284                         * implemented we should call
 285                         *   boottime->exit(image_handle, EFI_SUCCESS, 0, NULL);
 286                         * here, cf.
 287                         * https://lists.denx.de/pipermail/u-boot/2017-October/308720.html
 288                         */
 289                        return EFI_SUCCESS;
 290                }
 291        }
 292
 293        efi_st_printc(EFI_WHITE, "\nTesting EFI API implementation\n");
 294
 295        if (testname)
 296                efi_st_printc(EFI_WHITE, "\nSelected test: '%ps'\n", testname);
 297        else
 298                efi_st_printc(EFI_WHITE, "\nNumber of tests to execute: %u\n",
 299                              ll_entry_count(struct efi_unit_test,
 300                                             efi_unit_test));
 301
 302        /* Allocate buffer for setup results */
 303        ret = boottime->allocate_pool(EFI_RUNTIME_SERVICES_DATA, sizeof(int) *
 304                                      ll_entry_count(struct efi_unit_test,
 305                                                     efi_unit_test),
 306                                      (void **)&setup_status);
 307        if (ret != EFI_SUCCESS) {
 308                efi_st_error("Allocate pool failed\n");
 309                return ret;
 310        }
 311
 312        /* Execute boottime tests */
 313        efi_st_do_tests(testname, EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
 314                        EFI_ST_SETUP | EFI_ST_EXECUTE | EFI_ST_TEARDOWN,
 315                        &failures);
 316
 317        if (!need_reset(testname)) {
 318                if (failures)
 319                        ret = EFI_PROTOCOL_ERROR;
 320
 321                /* Give feedback */
 322                efi_st_printc(EFI_WHITE, "\nSummary: %u failures\n\n",
 323                              failures);
 324                return ret;
 325        }
 326
 327        /* Execute mixed tests */
 328        efi_st_do_tests(testname, EFI_SETUP_BEFORE_BOOTTIME_EXIT,
 329                        EFI_ST_SETUP, &failures);
 330        efi_st_do_tests(testname, EFI_SETTING_VIRTUAL_ADDRESS_MAP,
 331                        EFI_ST_SETUP, &failures);
 332
 333        efi_st_exit_boot_services();
 334
 335        efi_st_do_tests(testname, EFI_SETUP_BEFORE_BOOTTIME_EXIT,
 336                        EFI_ST_EXECUTE | EFI_ST_TEARDOWN, &failures);
 337        /* Execute test setting the virtual address map */
 338        efi_st_do_tests(testname, EFI_SETTING_VIRTUAL_ADDRESS_MAP,
 339                        EFI_ST_EXECUTE | EFI_ST_TEARDOWN,
 340                        &failures);
 341
 342        /* Give feedback */
 343        efi_st_printc(EFI_WHITE, "\nSummary: %u failures\n\n", failures);
 344
 345        /* Reset system */
 346        efi_st_printf("Preparing for reset. Press any key...\n");
 347        efi_st_get_key();
 348
 349        if (IS_ENABLED(CONFIG_EFI_HAVE_RUNTIME_RESET)) {
 350                runtime->reset_system(EFI_RESET_WARM, EFI_NOT_READY,
 351                                      sizeof(reset_message), reset_message);
 352        } else {
 353                efi_restore_gd();
 354                do_reset(NULL, 0, 0, NULL);
 355        }
 356
 357        efi_st_printf("\n");
 358        efi_st_error("Reset failed\n");
 359
 360        return EFI_UNSUPPORTED;
 361}
 362