1623b3a57SHeinrich Schuchardt /*
2623b3a57SHeinrich Schuchardt  * EFI efi_selftest
3623b3a57SHeinrich Schuchardt  *
4623b3a57SHeinrich Schuchardt  * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
5623b3a57SHeinrich Schuchardt  *
6623b3a57SHeinrich Schuchardt  * SPDX-License-Identifier:     GPL-2.0+
7623b3a57SHeinrich Schuchardt  */
8623b3a57SHeinrich Schuchardt 
9623b3a57SHeinrich Schuchardt #include <efi_selftest.h>
10623b3a57SHeinrich Schuchardt #include <vsprintf.h>
11623b3a57SHeinrich Schuchardt 
121f66a12eSHeinrich Schuchardt /*
131f66a12eSHeinrich Schuchardt  * Constants for test step bitmap
141f66a12eSHeinrich Schuchardt  */
151f66a12eSHeinrich Schuchardt #define EFI_ST_SETUP	1
161f66a12eSHeinrich Schuchardt #define EFI_ST_EXECUTE	2
171f66a12eSHeinrich Schuchardt #define EFI_ST_TEARDOWN	4
181f66a12eSHeinrich Schuchardt 
19623b3a57SHeinrich Schuchardt static const struct efi_system_table *systable;
20623b3a57SHeinrich Schuchardt static const struct efi_boot_services *boottime;
21623b3a57SHeinrich Schuchardt static const struct efi_runtime_services *runtime;
22623b3a57SHeinrich Schuchardt static efi_handle_t handle;
23623b3a57SHeinrich Schuchardt static u16 reset_message[] = L"Selftest completed";
24623b3a57SHeinrich Schuchardt 
25623b3a57SHeinrich Schuchardt /*
26623b3a57SHeinrich Schuchardt  * Exit the boot services.
27623b3a57SHeinrich Schuchardt  *
28623b3a57SHeinrich Schuchardt  * The size of the memory map is determined.
29623b3a57SHeinrich Schuchardt  * Pool memory is allocated to copy the memory map.
30623b3a57SHeinrich Schuchardt  * The memory amp is copied and the map key is obtained.
31623b3a57SHeinrich Schuchardt  * The map key is used to exit the boot services.
32623b3a57SHeinrich Schuchardt  */
33623b3a57SHeinrich Schuchardt void efi_st_exit_boot_services(void)
34623b3a57SHeinrich Schuchardt {
35623b3a57SHeinrich Schuchardt 	unsigned long map_size = 0;
36623b3a57SHeinrich Schuchardt 	unsigned long map_key;
37623b3a57SHeinrich Schuchardt 	unsigned long desc_size;
38623b3a57SHeinrich Schuchardt 	u32 desc_version;
39623b3a57SHeinrich Schuchardt 	efi_status_t ret;
40623b3a57SHeinrich Schuchardt 	struct efi_mem_desc *memory_map;
41623b3a57SHeinrich Schuchardt 
42623b3a57SHeinrich Schuchardt 	ret = boottime->get_memory_map(&map_size, NULL, &map_key, &desc_size,
43623b3a57SHeinrich Schuchardt 				       &desc_version);
44623b3a57SHeinrich Schuchardt 	if (ret != EFI_BUFFER_TOO_SMALL) {
45037ee6f9SHeinrich Schuchardt 		efi_st_error(
46037ee6f9SHeinrich Schuchardt 			"GetMemoryMap did not return EFI_BUFFER_TOO_SMALL\n");
47623b3a57SHeinrich Schuchardt 		return;
48623b3a57SHeinrich Schuchardt 	}
49623b3a57SHeinrich Schuchardt 	/* Allocate extra space for newly allocated memory */
50623b3a57SHeinrich Schuchardt 	map_size += sizeof(struct efi_mem_desc);
51623b3a57SHeinrich Schuchardt 	ret = boottime->allocate_pool(EFI_BOOT_SERVICES_DATA, map_size,
52623b3a57SHeinrich Schuchardt 				      (void **)&memory_map);
53623b3a57SHeinrich Schuchardt 	if (ret != EFI_SUCCESS) {
54037ee6f9SHeinrich Schuchardt 		efi_st_error("AllocatePool did not return EFI_SUCCESS\n");
55623b3a57SHeinrich Schuchardt 		return;
56623b3a57SHeinrich Schuchardt 	}
57623b3a57SHeinrich Schuchardt 	ret = boottime->get_memory_map(&map_size, memory_map, &map_key,
58623b3a57SHeinrich Schuchardt 				       &desc_size, &desc_version);
59623b3a57SHeinrich Schuchardt 	if (ret != EFI_SUCCESS) {
60037ee6f9SHeinrich Schuchardt 		efi_st_error("GetMemoryMap did not return EFI_SUCCESS\n");
61623b3a57SHeinrich Schuchardt 		return;
62623b3a57SHeinrich Schuchardt 	}
63623b3a57SHeinrich Schuchardt 	ret = boottime->exit_boot_services(handle, map_key);
64623b3a57SHeinrich Schuchardt 	if (ret != EFI_SUCCESS) {
65037ee6f9SHeinrich Schuchardt 		efi_st_error("ExitBootServices did not return EFI_SUCCESS\n");
66623b3a57SHeinrich Schuchardt 		return;
67623b3a57SHeinrich Schuchardt 	}
68623b3a57SHeinrich Schuchardt 	efi_st_printf("\nBoot services terminated\n");
69623b3a57SHeinrich Schuchardt }
70623b3a57SHeinrich Schuchardt 
71623b3a57SHeinrich Schuchardt /*
72623b3a57SHeinrich Schuchardt  * Set up a test.
73623b3a57SHeinrich Schuchardt  *
74623b3a57SHeinrich Schuchardt  * @test	the test to be executed
75623b3a57SHeinrich Schuchardt  * @failures	counter that will be incremented if a failure occurs
76e67e7249SHeinrich Schuchardt  * @return	EFI_ST_SUCCESS for success
77623b3a57SHeinrich Schuchardt  */
78623b3a57SHeinrich Schuchardt static int setup(struct efi_unit_test *test, unsigned int *failures)
79623b3a57SHeinrich Schuchardt {
80623b3a57SHeinrich Schuchardt 	int ret;
81623b3a57SHeinrich Schuchardt 
82623b3a57SHeinrich Schuchardt 	if (!test->setup)
83e67e7249SHeinrich Schuchardt 		return EFI_ST_SUCCESS;
84623b3a57SHeinrich Schuchardt 	efi_st_printf("\nSetting up '%s'\n", test->name);
85623b3a57SHeinrich Schuchardt 	ret = test->setup(handle, systable);
86e67e7249SHeinrich Schuchardt 	if (ret != EFI_ST_SUCCESS) {
87037ee6f9SHeinrich Schuchardt 		efi_st_error("Setting up '%s' failed\n", test->name);
88623b3a57SHeinrich Schuchardt 		++*failures;
89623b3a57SHeinrich Schuchardt 	} else {
90623b3a57SHeinrich Schuchardt 		efi_st_printf("Setting up '%s' succeeded\n", test->name);
91623b3a57SHeinrich Schuchardt 	}
92623b3a57SHeinrich Schuchardt 	return ret;
93623b3a57SHeinrich Schuchardt }
94623b3a57SHeinrich Schuchardt 
95623b3a57SHeinrich Schuchardt /*
96623b3a57SHeinrich Schuchardt  * Execute a test.
97623b3a57SHeinrich Schuchardt  *
98623b3a57SHeinrich Schuchardt  * @test	the test to be executed
99623b3a57SHeinrich Schuchardt  * @failures	counter that will be incremented if a failure occurs
100e67e7249SHeinrich Schuchardt  * @return	EFI_ST_SUCCESS for success
101623b3a57SHeinrich Schuchardt  */
102623b3a57SHeinrich Schuchardt static int execute(struct efi_unit_test *test, unsigned int *failures)
103623b3a57SHeinrich Schuchardt {
104623b3a57SHeinrich Schuchardt 	int ret;
105623b3a57SHeinrich Schuchardt 
106623b3a57SHeinrich Schuchardt 	if (!test->execute)
107e67e7249SHeinrich Schuchardt 		return EFI_ST_SUCCESS;
108623b3a57SHeinrich Schuchardt 	efi_st_printf("\nExecuting '%s'\n", test->name);
109623b3a57SHeinrich Schuchardt 	ret = test->execute();
110e67e7249SHeinrich Schuchardt 	if (ret != EFI_ST_SUCCESS) {
111037ee6f9SHeinrich Schuchardt 		efi_st_error("Executing '%s' failed\n", test->name);
112623b3a57SHeinrich Schuchardt 		++*failures;
113623b3a57SHeinrich Schuchardt 	} else {
114623b3a57SHeinrich Schuchardt 		efi_st_printf("Executing '%s' succeeded\n", test->name);
115623b3a57SHeinrich Schuchardt 	}
116623b3a57SHeinrich Schuchardt 	return ret;
117623b3a57SHeinrich Schuchardt }
118623b3a57SHeinrich Schuchardt 
119623b3a57SHeinrich Schuchardt /*
120623b3a57SHeinrich Schuchardt  * Tear down a test.
121623b3a57SHeinrich Schuchardt  *
122623b3a57SHeinrich Schuchardt  * @test	the test to be torn down
123623b3a57SHeinrich Schuchardt  * @failures	counter that will be incremented if a failure occurs
124e67e7249SHeinrich Schuchardt  * @return	EFI_ST_SUCCESS for success
125623b3a57SHeinrich Schuchardt  */
126623b3a57SHeinrich Schuchardt static int teardown(struct efi_unit_test *test, unsigned int *failures)
127623b3a57SHeinrich Schuchardt {
128623b3a57SHeinrich Schuchardt 	int ret;
129623b3a57SHeinrich Schuchardt 
130623b3a57SHeinrich Schuchardt 	if (!test->teardown)
131e67e7249SHeinrich Schuchardt 		return EFI_ST_SUCCESS;
132623b3a57SHeinrich Schuchardt 	efi_st_printf("\nTearing down '%s'\n", test->name);
133623b3a57SHeinrich Schuchardt 	ret = test->teardown();
134e67e7249SHeinrich Schuchardt 	if (ret != EFI_ST_SUCCESS) {
135037ee6f9SHeinrich Schuchardt 		efi_st_error("Tearing down '%s' failed\n", test->name);
136623b3a57SHeinrich Schuchardt 		++*failures;
137623b3a57SHeinrich Schuchardt 	} else {
138623b3a57SHeinrich Schuchardt 		efi_st_printf("Tearing down '%s' succeeded\n", test->name);
139623b3a57SHeinrich Schuchardt 	}
140623b3a57SHeinrich Schuchardt 	return ret;
141623b3a57SHeinrich Schuchardt }
142623b3a57SHeinrich Schuchardt 
143623b3a57SHeinrich Schuchardt /*
144*d78e40d6SHeinrich Schuchardt  * Check that a test exists.
1451f66a12eSHeinrich Schuchardt  *
146*d78e40d6SHeinrich Schuchardt  * @testname:	name of the test
147*d78e40d6SHeinrich Schuchardt  * @return:	test
1481f66a12eSHeinrich Schuchardt  */
149*d78e40d6SHeinrich Schuchardt static struct efi_unit_test *find_test(const u16 *testname)
1501f66a12eSHeinrich Schuchardt {
1511f66a12eSHeinrich Schuchardt 	struct efi_unit_test *test;
1521f66a12eSHeinrich Schuchardt 
1531f66a12eSHeinrich Schuchardt 	for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
1541f66a12eSHeinrich Schuchardt 	     test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) {
155*d78e40d6SHeinrich Schuchardt 		if (!efi_st_strcmp_16_8(testname, test->name))
156*d78e40d6SHeinrich Schuchardt 			return test;
157*d78e40d6SHeinrich Schuchardt 	}
158*d78e40d6SHeinrich Schuchardt 	efi_st_printf("\nTest '%ps' not found\n", testname);
159*d78e40d6SHeinrich Schuchardt 	return NULL;
160*d78e40d6SHeinrich Schuchardt }
161*d78e40d6SHeinrich Schuchardt 
162*d78e40d6SHeinrich Schuchardt /*
163*d78e40d6SHeinrich Schuchardt  * List all available tests.
164*d78e40d6SHeinrich Schuchardt  */
165*d78e40d6SHeinrich Schuchardt static void list_all_tests(void)
166*d78e40d6SHeinrich Schuchardt {
167*d78e40d6SHeinrich Schuchardt 	struct efi_unit_test *test;
168*d78e40d6SHeinrich Schuchardt 
169*d78e40d6SHeinrich Schuchardt 	/* List all tests */
170*d78e40d6SHeinrich Schuchardt 	efi_st_printf("\nAvailable tests:\n");
171*d78e40d6SHeinrich Schuchardt 	for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
172*d78e40d6SHeinrich Schuchardt 	     test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) {
173*d78e40d6SHeinrich Schuchardt 		efi_st_printf("'%s'%s\n", test->name,
174*d78e40d6SHeinrich Schuchardt 			      test->on_request ? " - on request" : "");
175*d78e40d6SHeinrich Schuchardt 	}
176*d78e40d6SHeinrich Schuchardt }
177*d78e40d6SHeinrich Schuchardt 
178*d78e40d6SHeinrich Schuchardt /*
179*d78e40d6SHeinrich Schuchardt  * Execute test steps of one phase.
180*d78e40d6SHeinrich Schuchardt  *
181*d78e40d6SHeinrich Schuchardt  * @testname	name of a single selected test or NULL
182*d78e40d6SHeinrich Schuchardt  * @phase	test phase
183*d78e40d6SHeinrich Schuchardt  * @steps	steps to execute
184*d78e40d6SHeinrich Schuchardt  * failures	returns EFI_ST_SUCCESS if all test steps succeeded
185*d78e40d6SHeinrich Schuchardt  */
186*d78e40d6SHeinrich Schuchardt void efi_st_do_tests(const u16 *testname, unsigned int phase,
187*d78e40d6SHeinrich Schuchardt 		     unsigned int steps, unsigned int *failures)
188*d78e40d6SHeinrich Schuchardt {
189*d78e40d6SHeinrich Schuchardt 	struct efi_unit_test *test;
190*d78e40d6SHeinrich Schuchardt 
191*d78e40d6SHeinrich Schuchardt 	for (test = ll_entry_start(struct efi_unit_test, efi_unit_test);
192*d78e40d6SHeinrich Schuchardt 	     test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) {
193*d78e40d6SHeinrich Schuchardt 		if (testname ?
194*d78e40d6SHeinrich Schuchardt 		    efi_st_strcmp_16_8(testname, test->name) : test->on_request)
195*d78e40d6SHeinrich Schuchardt 			continue;
1961f66a12eSHeinrich Schuchardt 		if (test->phase != phase)
1971f66a12eSHeinrich Schuchardt 			continue;
1981f66a12eSHeinrich Schuchardt 		if (steps & EFI_ST_SETUP)
1991f66a12eSHeinrich Schuchardt 			setup(test, failures);
2001f66a12eSHeinrich Schuchardt 		if (steps & EFI_ST_EXECUTE)
2011f66a12eSHeinrich Schuchardt 			execute(test, failures);
2021f66a12eSHeinrich Schuchardt 		if (steps & EFI_ST_TEARDOWN)
2031f66a12eSHeinrich Schuchardt 			teardown(test, failures);
2041f66a12eSHeinrich Schuchardt 	}
2051f66a12eSHeinrich Schuchardt }
2061f66a12eSHeinrich Schuchardt 
2071f66a12eSHeinrich Schuchardt /*
208623b3a57SHeinrich Schuchardt  * Execute selftest of the EFI API
209623b3a57SHeinrich Schuchardt  *
210623b3a57SHeinrich Schuchardt  * This is the main entry point of the EFI selftest application.
211623b3a57SHeinrich Schuchardt  *
212623b3a57SHeinrich Schuchardt  * All tests use a driver model and are run in three phases:
213623b3a57SHeinrich Schuchardt  * setup, execute, teardown.
214623b3a57SHeinrich Schuchardt  *
215623b3a57SHeinrich Schuchardt  * A test may be setup and executed at boottime,
216623b3a57SHeinrich Schuchardt  * it may be setup at boottime and executed at runtime,
217623b3a57SHeinrich Schuchardt  * or it may be setup and executed at runtime.
218623b3a57SHeinrich Schuchardt  *
219623b3a57SHeinrich Schuchardt  * After executing all tests the system is reset.
220623b3a57SHeinrich Schuchardt  *
221623b3a57SHeinrich Schuchardt  * @image_handle:	handle of the loaded EFI image
222623b3a57SHeinrich Schuchardt  * @systab:		EFI system table
223623b3a57SHeinrich Schuchardt  */
224623b3a57SHeinrich Schuchardt efi_status_t EFIAPI efi_selftest(efi_handle_t image_handle,
225623b3a57SHeinrich Schuchardt 				 struct efi_system_table *systab)
226623b3a57SHeinrich Schuchardt {
227623b3a57SHeinrich Schuchardt 	unsigned int failures = 0;
228*d78e40d6SHeinrich Schuchardt 	const u16 *testname = NULL;
229*d78e40d6SHeinrich Schuchardt 	struct efi_loaded_image *loaded_image;
230*d78e40d6SHeinrich Schuchardt 	efi_status_t ret;
231623b3a57SHeinrich Schuchardt 
232623b3a57SHeinrich Schuchardt 	systable = systab;
233623b3a57SHeinrich Schuchardt 	boottime = systable->boottime;
234623b3a57SHeinrich Schuchardt 	runtime = systable->runtime;
235623b3a57SHeinrich Schuchardt 	handle = image_handle;
236623b3a57SHeinrich Schuchardt 	con_out = systable->con_out;
237623b3a57SHeinrich Schuchardt 	con_in = systable->con_in;
238623b3a57SHeinrich Schuchardt 
239*d78e40d6SHeinrich Schuchardt 	ret = boottime->handle_protocol(image_handle, &efi_guid_loaded_image,
240*d78e40d6SHeinrich Schuchardt 					(void **)&loaded_image);
241*d78e40d6SHeinrich Schuchardt 	if (ret != EFI_SUCCESS) {
242*d78e40d6SHeinrich Schuchardt 		efi_st_error("Cannot open loaded image protocol");
243*d78e40d6SHeinrich Schuchardt 		return ret;
244*d78e40d6SHeinrich Schuchardt 	}
245*d78e40d6SHeinrich Schuchardt 
246*d78e40d6SHeinrich Schuchardt 	if (loaded_image->load_options)
247*d78e40d6SHeinrich Schuchardt 		testname = (u16 *)loaded_image->load_options;
248*d78e40d6SHeinrich Schuchardt 
249*d78e40d6SHeinrich Schuchardt 	if (testname) {
250*d78e40d6SHeinrich Schuchardt 		if (!efi_st_strcmp_16_8(testname, "list") ||
251*d78e40d6SHeinrich Schuchardt 		    !find_test(testname)) {
252*d78e40d6SHeinrich Schuchardt 			list_all_tests();
253*d78e40d6SHeinrich Schuchardt 			/*
254*d78e40d6SHeinrich Schuchardt 			 * TODO:
255*d78e40d6SHeinrich Schuchardt 			 * Once the Exit boottime service is correctly
256*d78e40d6SHeinrich Schuchardt 			 * implemented we should call
257*d78e40d6SHeinrich Schuchardt 			 *   boottime->exit(image_handle, EFI_SUCCESS, 0, NULL);
258*d78e40d6SHeinrich Schuchardt 			 * here, cf.
259*d78e40d6SHeinrich Schuchardt 			 * https://lists.denx.de/pipermail/u-boot/2017-October/308720.html
260*d78e40d6SHeinrich Schuchardt 			 */
261*d78e40d6SHeinrich Schuchardt 			return EFI_SUCCESS;
262*d78e40d6SHeinrich Schuchardt 		}
263*d78e40d6SHeinrich Schuchardt 	}
264*d78e40d6SHeinrich Schuchardt 
265623b3a57SHeinrich Schuchardt 	efi_st_printf("\nTesting EFI API implementation\n");
266623b3a57SHeinrich Schuchardt 
267*d78e40d6SHeinrich Schuchardt 	if (testname)
268*d78e40d6SHeinrich Schuchardt 		efi_st_printf("\nSelected test: '%ps'\n", testname);
269*d78e40d6SHeinrich Schuchardt 	else
270623b3a57SHeinrich Schuchardt 		efi_st_printf("\nNumber of tests to execute: %u\n",
271*d78e40d6SHeinrich Schuchardt 			      ll_entry_count(struct efi_unit_test,
272*d78e40d6SHeinrich Schuchardt 					     efi_unit_test));
273623b3a57SHeinrich Schuchardt 
274623b3a57SHeinrich Schuchardt 	/* Execute boottime tests */
275*d78e40d6SHeinrich Schuchardt 	efi_st_do_tests(testname, EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
2761f66a12eSHeinrich Schuchardt 			EFI_ST_SETUP | EFI_ST_EXECUTE | EFI_ST_TEARDOWN,
2771f66a12eSHeinrich Schuchardt 			&failures);
278623b3a57SHeinrich Schuchardt 
279623b3a57SHeinrich Schuchardt 	/* Execute mixed tests */
280*d78e40d6SHeinrich Schuchardt 	efi_st_do_tests(testname, EFI_SETUP_BEFORE_BOOTTIME_EXIT,
2811f66a12eSHeinrich Schuchardt 			EFI_ST_SETUP, &failures);
282623b3a57SHeinrich Schuchardt 
283623b3a57SHeinrich Schuchardt 	efi_st_exit_boot_services();
284623b3a57SHeinrich Schuchardt 
285*d78e40d6SHeinrich Schuchardt 	efi_st_do_tests(testname, EFI_SETUP_BEFORE_BOOTTIME_EXIT,
2861f66a12eSHeinrich Schuchardt 			EFI_ST_EXECUTE | EFI_ST_TEARDOWN, &failures);
287623b3a57SHeinrich Schuchardt 
288623b3a57SHeinrich Schuchardt 	/* Execute runtime tests */
289*d78e40d6SHeinrich Schuchardt 	efi_st_do_tests(testname, EFI_SETUP_AFTER_BOOTTIME_EXIT,
2901f66a12eSHeinrich Schuchardt 			EFI_ST_SETUP | EFI_ST_EXECUTE | EFI_ST_TEARDOWN,
2911f66a12eSHeinrich Schuchardt 			&failures);
292623b3a57SHeinrich Schuchardt 
293623b3a57SHeinrich Schuchardt 	/* Give feedback */
294623b3a57SHeinrich Schuchardt 	efi_st_printf("\nSummary: %u failures\n\n", failures);
295623b3a57SHeinrich Schuchardt 
296623b3a57SHeinrich Schuchardt 	/* Reset system */
297623b3a57SHeinrich Schuchardt 	efi_st_printf("Preparing for reset. Press any key.\n");
298623b3a57SHeinrich Schuchardt 	efi_st_get_key();
299623b3a57SHeinrich Schuchardt 	runtime->reset_system(EFI_RESET_WARM, EFI_NOT_READY,
300623b3a57SHeinrich Schuchardt 			      sizeof(reset_message), reset_message);
301037ee6f9SHeinrich Schuchardt 	efi_st_printf("\n");
302037ee6f9SHeinrich Schuchardt 	efi_st_error("Reset failed.\n");
303623b3a57SHeinrich Schuchardt 
304623b3a57SHeinrich Schuchardt 	return EFI_UNSUPPORTED;
305623b3a57SHeinrich Schuchardt }
306