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