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 
17 static const struct efi_system_table *systable;
18 static const struct efi_boot_services *boottime;
19 static const struct efi_runtime_services *runtime;
20 static efi_handle_t handle;
21 static u16 reset_message[] = L"Selftest completed";
22 static 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  */
efi_st_exit_boot_services(void)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 	/* 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  */
setup(struct efi_unit_test * test,unsigned int * failures)80 static 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  */
execute(struct efi_unit_test * test,unsigned int * failures)105 static 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  */
teardown(struct efi_unit_test * test,unsigned int * failures)130 static 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  */
need_reset(const u16 * testname)154 static 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  */
find_test(const u16 * testname)175 static 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  */
list_all_tests(void)191 static 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  */
efi_st_do_tests(const u16 * testname,unsigned int phase,unsigned int steps,unsigned int * failures)212 void 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  */
efi_selftest(efi_handle_t image_handle,struct efi_system_table * systab)252 efi_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