Commit d78e40d651972ef061c960e4b7da7843383c2ec9
Committed by
Alexander Graf
1 parent
1f66a12e23
Exists in
smarc_8mq_lf_v2020.04
and in
17 other branches
efi_selftest: allow to select a single test for execution
Environment variable efi_selftest is passed as load options to the selftest application. It is used to select a single test to be executed. The load options are an UTF8 string. Yet I decided to keep the name propertiy of the tests as char[] to reduce code size. Special value 'list' displays a list of all available tests. Tests get an on_request property. If this property is set the tests are only executed if explicitly requested. The invocation of efi_selftest is changed to reflect that bootefi selftest with efi_selftest = 'list' will call the Exit bootservice. Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de> Signed-off-by: Alexander Graf <agraf@suse.de>
Showing 5 changed files with 147 additions and 9 deletions Side-by-side Diff
cmd/bootefi.c
... | ... | @@ -6,10 +6,12 @@ |
6 | 6 | * SPDX-License-Identifier: GPL-2.0+ |
7 | 7 | */ |
8 | 8 | |
9 | +#include <charset.h> | |
9 | 10 | #include <common.h> |
10 | 11 | #include <command.h> |
11 | 12 | #include <dm.h> |
12 | 13 | #include <efi_loader.h> |
14 | +#include <efi_selftest.h> | |
13 | 15 | #include <errno.h> |
14 | 16 | #include <libfdt.h> |
15 | 17 | #include <libfdt_env.h> |
... | ... | @@ -50,6 +52,32 @@ |
50 | 52 | efi_get_time_init(); |
51 | 53 | } |
52 | 54 | |
55 | +/* | |
56 | + * Set the load options of an image from an environment variable. | |
57 | + * | |
58 | + * @loaded_image_info: the image | |
59 | + * @env_var: name of the environment variable | |
60 | + */ | |
61 | +static void set_load_options(struct efi_loaded_image *loaded_image_info, | |
62 | + const char *env_var) | |
63 | +{ | |
64 | + size_t size; | |
65 | + const char *env = env_get(env_var); | |
66 | + | |
67 | + loaded_image_info->load_options = NULL; | |
68 | + loaded_image_info->load_options_size = 0; | |
69 | + if (!env) | |
70 | + return; | |
71 | + size = strlen(env) + 1; | |
72 | + loaded_image_info->load_options = calloc(size, sizeof(u16)); | |
73 | + if (!loaded_image_info->load_options) { | |
74 | + printf("ERROR: Out of memory\n"); | |
75 | + return; | |
76 | + } | |
77 | + utf8_to_utf16(loaded_image_info->load_options, (u8 *)env, size); | |
78 | + loaded_image_info->load_options_size = size * 2; | |
79 | +} | |
80 | + | |
53 | 81 | static void *copy_fdt(void *fdt) |
54 | 82 | { |
55 | 83 | u64 fdt_size = fdt_totalsize(fdt); |
... | ... | @@ -317,7 +345,12 @@ |
317 | 345 | /* Initialize and populate EFI object list */ |
318 | 346 | if (!efi_obj_list_initalized) |
319 | 347 | efi_init_obj_list(); |
320 | - return efi_selftest(&loaded_image_info, &systab); | |
348 | + /* Transfer environment variable efi_selftest as load options */ | |
349 | + set_load_options(&loaded_image_info, "efi_selftest"); | |
350 | + /* Execute the test */ | |
351 | + r = efi_selftest(&loaded_image_info, &systab); | |
352 | + free(loaded_image_info.load_options); | |
353 | + return r; | |
321 | 354 | } else |
322 | 355 | #endif |
323 | 356 | if (!strcmp(argv[1], "bootmgr")) { |
... | ... | @@ -363,6 +396,8 @@ |
363 | 396 | #ifdef CONFIG_CMD_BOOTEFI_SELFTEST |
364 | 397 | "bootefi selftest\n" |
365 | 398 | " - boot an EFI selftest application stored within U-Boot\n" |
399 | + " Use environment variable efi_selftest to select a single test.\n" | |
400 | + " Use 'setenv efi_selftest list' to enumerate all tests.\n" | |
366 | 401 | #endif |
367 | 402 | "bootmgr [fdt addr]\n" |
368 | 403 | " - load and boot EFI payload based on BootOrder/BootXXXX variables.\n" |
include/efi_selftest.h
... | ... | @@ -12,6 +12,7 @@ |
12 | 12 | #include <common.h> |
13 | 13 | #include <efi.h> |
14 | 14 | #include <efi_api.h> |
15 | +#include <efi_loader.h> | |
15 | 16 | #include <linker_lists.h> |
16 | 17 | |
17 | 18 | #define EFI_ST_SUCCESS 0 |
... | ... | @@ -72,6 +73,15 @@ |
72 | 73 | int efi_st_memcmp(const void *buf1, const void *buf2, size_t length); |
73 | 74 | |
74 | 75 | /* |
76 | + * Compare an u16 string to a char string. | |
77 | + * | |
78 | + * @buf1: u16 string | |
79 | + * @buf2: char string | |
80 | + * @return: 0 if both buffers contain the same bytes | |
81 | + */ | |
82 | +int efi_st_strcmp_16_8(const u16 *buf1, const char *buf2); | |
83 | + | |
84 | +/* | |
75 | 85 | * Reads an Unicode character from the input device. |
76 | 86 | * |
77 | 87 | * @return: Unicode character |
... | ... | @@ -88,6 +98,7 @@ |
88 | 98 | * @setup: set up the unit test |
89 | 99 | * @teardown: tear down the unit test |
90 | 100 | * @execute: execute the unit test |
101 | + * @on_request: test is only executed on request | |
91 | 102 | */ |
92 | 103 | struct efi_unit_test { |
93 | 104 | const char *name; |
... | ... | @@ -96,6 +107,7 @@ |
96 | 107 | const struct efi_system_table *systable); |
97 | 108 | int (*execute)(void); |
98 | 109 | int (*teardown)(void); |
110 | + bool on_request; | |
99 | 111 | }; |
100 | 112 | |
101 | 113 | /* Declare a new EFI unit test */ |
lib/efi_selftest/efi_selftest.c
... | ... | @@ -141,19 +141,58 @@ |
141 | 141 | } |
142 | 142 | |
143 | 143 | /* |
144 | + * Check that a test exists. | |
145 | + * | |
146 | + * @testname: name of the test | |
147 | + * @return: test | |
148 | + */ | |
149 | +static struct efi_unit_test *find_test(const u16 *testname) | |
150 | +{ | |
151 | + struct efi_unit_test *test; | |
152 | + | |
153 | + for (test = ll_entry_start(struct efi_unit_test, efi_unit_test); | |
154 | + test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) { | |
155 | + if (!efi_st_strcmp_16_8(testname, test->name)) | |
156 | + return test; | |
157 | + } | |
158 | + efi_st_printf("\nTest '%ps' not found\n", testname); | |
159 | + return NULL; | |
160 | +} | |
161 | + | |
162 | +/* | |
163 | + * List all available tests. | |
164 | + */ | |
165 | +static void list_all_tests(void) | |
166 | +{ | |
167 | + struct efi_unit_test *test; | |
168 | + | |
169 | + /* List all tests */ | |
170 | + efi_st_printf("\nAvailable tests:\n"); | |
171 | + for (test = ll_entry_start(struct efi_unit_test, efi_unit_test); | |
172 | + test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) { | |
173 | + efi_st_printf("'%s'%s\n", test->name, | |
174 | + test->on_request ? " - on request" : ""); | |
175 | + } | |
176 | +} | |
177 | + | |
178 | +/* | |
144 | 179 | * Execute test steps of one phase. |
145 | 180 | * |
181 | + * @testname name of a single selected test or NULL | |
146 | 182 | * @phase test phase |
147 | 183 | * @steps steps to execute |
148 | 184 | * failures returns EFI_ST_SUCCESS if all test steps succeeded |
149 | 185 | */ |
150 | -void efi_st_do_tests(unsigned int phase, unsigned int steps, | |
151 | - unsigned int *failures) | |
186 | +void efi_st_do_tests(const u16 *testname, unsigned int phase, | |
187 | + unsigned int steps, unsigned int *failures) | |
152 | 188 | { |
153 | 189 | struct efi_unit_test *test; |
154 | 190 | |
155 | 191 | for (test = ll_entry_start(struct efi_unit_test, efi_unit_test); |
156 | 192 | test < ll_entry_end(struct efi_unit_test, efi_unit_test); ++test) { |
193 | + if (testname ? | |
194 | + efi_st_strcmp_16_8(testname, test->name) : test->on_request) | |
195 | + continue; | |
157 | 196 | if (test->phase != phase) |
158 | 197 | continue; |
159 | 198 | if (steps & EFI_ST_SETUP) |
... | ... | @@ -186,6 +225,9 @@ |
186 | 225 | struct efi_system_table *systab) |
187 | 226 | { |
188 | 227 | unsigned int failures = 0; |
228 | + const u16 *testname = NULL; | |
229 | + struct efi_loaded_image *loaded_image; | |
230 | + efi_status_t ret; | |
189 | 231 | |
190 | 232 | systable = systab; |
191 | 233 | boottime = systable->boottime; |
192 | 234 | |
193 | 235 | |
194 | 236 | |
195 | 237 | |
196 | 238 | |
... | ... | @@ -194,27 +236,57 @@ |
194 | 236 | con_out = systable->con_out; |
195 | 237 | con_in = systable->con_in; |
196 | 238 | |
239 | + ret = boottime->handle_protocol(image_handle, &efi_guid_loaded_image, | |
240 | + (void **)&loaded_image); | |
241 | + if (ret != EFI_SUCCESS) { | |
242 | + efi_st_error("Cannot open loaded image protocol"); | |
243 | + return ret; | |
244 | + } | |
245 | + | |
246 | + if (loaded_image->load_options) | |
247 | + testname = (u16 *)loaded_image->load_options; | |
248 | + | |
249 | + if (testname) { | |
250 | + if (!efi_st_strcmp_16_8(testname, "list") || | |
251 | + !find_test(testname)) { | |
252 | + list_all_tests(); | |
253 | + /* | |
254 | + * TODO: | |
255 | + * Once the Exit boottime service is correctly | |
256 | + * implemented we should call | |
257 | + * boottime->exit(image_handle, EFI_SUCCESS, 0, NULL); | |
258 | + * here, cf. | |
259 | + * https://lists.denx.de/pipermail/u-boot/2017-October/308720.html | |
260 | + */ | |
261 | + return EFI_SUCCESS; | |
262 | + } | |
263 | + } | |
264 | + | |
197 | 265 | efi_st_printf("\nTesting EFI API implementation\n"); |
198 | 266 | |
199 | - efi_st_printf("\nNumber of tests to execute: %u\n", | |
200 | - ll_entry_count(struct efi_unit_test, efi_unit_test)); | |
267 | + if (testname) | |
268 | + efi_st_printf("\nSelected test: '%ps'\n", testname); | |
269 | + else | |
270 | + efi_st_printf("\nNumber of tests to execute: %u\n", | |
271 | + ll_entry_count(struct efi_unit_test, | |
272 | + efi_unit_test)); | |
201 | 273 | |
202 | 274 | /* Execute boottime tests */ |
203 | - efi_st_do_tests(EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, | |
275 | + efi_st_do_tests(testname, EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, | |
204 | 276 | EFI_ST_SETUP | EFI_ST_EXECUTE | EFI_ST_TEARDOWN, |
205 | 277 | &failures); |
206 | 278 | |
207 | 279 | /* Execute mixed tests */ |
208 | - efi_st_do_tests(EFI_SETUP_BEFORE_BOOTTIME_EXIT, | |
280 | + efi_st_do_tests(testname, EFI_SETUP_BEFORE_BOOTTIME_EXIT, | |
209 | 281 | EFI_ST_SETUP, &failures); |
210 | 282 | |
211 | 283 | efi_st_exit_boot_services(); |
212 | 284 | |
213 | - efi_st_do_tests(EFI_SETUP_BEFORE_BOOTTIME_EXIT, | |
285 | + efi_st_do_tests(testname, EFI_SETUP_BEFORE_BOOTTIME_EXIT, | |
214 | 286 | EFI_ST_EXECUTE | EFI_ST_TEARDOWN, &failures); |
215 | 287 | |
216 | 288 | /* Execute runtime tests */ |
217 | - efi_st_do_tests(EFI_SETUP_AFTER_BOOTTIME_EXIT, | |
289 | + efi_st_do_tests(testname, EFI_SETUP_AFTER_BOOTTIME_EXIT, | |
218 | 290 | EFI_ST_SETUP | EFI_ST_EXECUTE | EFI_ST_TEARDOWN, |
219 | 291 | &failures); |
220 | 292 |
lib/efi_selftest/efi_selftest_console.c
... | ... | @@ -142,6 +142,7 @@ |
142 | 142 | const char *c; |
143 | 143 | u16 *pos = buf; |
144 | 144 | const char *s; |
145 | + const u16 *u; | |
145 | 146 | |
146 | 147 | va_start(args, fmt); |
147 | 148 | |
148 | 149 | |
... | ... | @@ -179,8 +180,17 @@ |
179 | 180 | case 'p': |
180 | 181 | ++c; |
181 | 182 | switch (*c) { |
183 | + /* MAC address */ | |
182 | 184 | case 'm': |
183 | 185 | mac(va_arg(args, void*), &pos); |
186 | + break; | |
187 | + | |
188 | + /* u16 string */ | |
189 | + case 's': | |
190 | + u = va_arg(args, u16*); | |
191 | + /* Ensure string fits into buffer */ | |
192 | + for (; *u && pos < buf + 120; ++u) | |
193 | + *pos++ = *u; | |
184 | 194 | break; |
185 | 195 | default: |
186 | 196 | --c; |
lib/efi_selftest/efi_selftest_util.c