xref: /kvm-unit-tests/lib/efi.c (revision b365a5b0d95c1e57019c49d7f692a5e484016596)
1 /*
2  * EFI-related functions to set up and run test cases in EFI
3  *
4  * Copyright (c) 2021, SUSE, Varad Gautam <varad.gautam@suse.com>
5  * Copyright (c) 2021, Google Inc, Zixuan Wang <zixuanwang@google.com>
6  *
7  * SPDX-License-Identifier: LGPL-2.0-or-later
8  */
9 #include <libcflat.h>
10 #include <argv.h>
11 #include <ctype.h>
12 #include <stdlib.h>
13 #include <asm/setup.h>
14 #include "efi.h"
15 #include "libfdt/libfdt.h"
16 
17 /* From each arch */
18 extern char *initrd;
19 extern u32 initrd_size;
20 
21 /* From lib/argv.c */
22 extern int __argc, __envc;
23 extern char *__argv[100];
24 extern char *__environ[200];
25 
26 extern char _text;
27 
28 extern int main(int argc, char **argv, char **envp);
29 
30 efi_system_table_t *efi_system_table = NULL;
31 
32 #ifdef __riscv
33 #define RISCV_EFI_BOOT_PROTOCOL_GUID EFI_GUID(0xccd15fec, 0x6f73, 0x4eec,  0x83, 0x95, 0x3e, 0x69, 0xe4, 0xb9, 0x40, 0xbf)
34 
35 unsigned long boot_hartid;
36 
37 struct riscv_efi_boot_protocol {
38 	u64 revision;
39 	efi_status_t (*get_boot_hartid)(struct riscv_efi_boot_protocol *,
40 		      unsigned long *boot_hartid);
41 };
42 
43 static efi_status_t efi_get_boot_hartid(void)
44 {
45 	efi_guid_t boot_protocol_guid = RISCV_EFI_BOOT_PROTOCOL_GUID;
46 	struct riscv_efi_boot_protocol *boot_protocol;
47 	efi_status_t status;
48 
49 	status = efi_bs_call(locate_protocol, &boot_protocol_guid, NULL,
50 			     (void **)&boot_protocol);
51 	if (status != EFI_SUCCESS)
52 		return status;
53 	return efi_call_proto(boot_protocol, get_boot_hartid, &boot_hartid);
54 }
55 #endif
56 
57 static void efi_free_pool(void *ptr)
58 {
59 	efi_bs_call(free_pool, ptr);
60 }
61 
62 efi_status_t efi_get_memory_map(struct efi_boot_memmap *map)
63 {
64 	efi_memory_desc_t *m = NULL;
65 	efi_status_t status;
66 	unsigned long key = 0, map_size = 0, desc_size = 0;
67 	u32 desc_ver;
68 
69 	status = efi_bs_call(get_memory_map, &map_size,
70 			     NULL, &key, &desc_size, &desc_ver);
71 	if (status != EFI_BUFFER_TOO_SMALL || map_size == 0)
72 		goto out;
73 
74 	/*
75 	 * Pad map_size with additional descriptors so we don't need to
76 	 * retry.
77 	 */
78 	map_size += 4 * desc_size;
79 	*map->buff_size = map_size;
80 	status = efi_bs_call(allocate_pool, EFI_LOADER_DATA,
81 			     map_size, (void **)&m);
82 	if (status != EFI_SUCCESS)
83 		goto out;
84 
85 	/* Get the map. */
86 	status = efi_bs_call(get_memory_map, &map_size,
87 			     m, &key, &desc_size, &desc_ver);
88 	if (status != EFI_SUCCESS) {
89 		efi_free_pool(m);
90 		goto out;
91 	}
92 
93 	*map->desc_ver = desc_ver;
94 	*map->desc_size = desc_size;
95 	*map->map_size = map_size;
96 	*map->key_ptr = key;
97 out:
98 	*map->map = m;
99 	return status;
100 }
101 
102 efi_status_t efi_exit_boot_services(void *handle, struct efi_boot_memmap *map)
103 {
104 	return efi_bs_call(exit_boot_services, handle, *map->key_ptr);
105 }
106 
107 efi_status_t efi_get_system_config_table(efi_guid_t table_guid, void **table)
108 {
109 	size_t i;
110 	efi_config_table_t *tables;
111 
112 	tables = (efi_config_table_t *)efi_system_table->tables;
113 	for (i = 0; i < efi_system_table->nr_tables; i++) {
114 		if (!memcmp(&table_guid, &tables[i].guid, sizeof(efi_guid_t))) {
115 			*table = tables[i].table;
116 			return EFI_SUCCESS;
117 		}
118 	}
119 	return EFI_NOT_FOUND;
120 }
121 
122 static void efi_exit(efi_status_t code)
123 {
124 	exit(code);
125 
126 	/*
127 	 * Fallback to UEFI reset_system() service, in case testdev is
128 	 * missing and exit() does not properly exit.
129 	 */
130 	efi_rs_call(reset_system, EFI_RESET_SHUTDOWN, code, 0, NULL);
131 }
132 
133 /* Adapted from drivers/firmware/efi/libstub/efi-stub.c */
134 static char *efi_convert_cmdline(struct efi_loaded_image_64 *image, int *cmd_line_len)
135 {
136 	const u16 *s2;
137 	unsigned long cmdline_addr = 0;
138 	int options_chars = image->load_options_size;
139 	const u16 *options = image->load_options;
140 	int options_bytes = 0, safe_options_bytes = 0;  /* UTF-8 bytes */
141 	bool in_quote = false;
142 	efi_status_t status;
143 	const int COMMAND_LINE_SIZE = 2048;
144 
145 	if (options) {
146 		s2 = options;
147 		while (options_bytes < COMMAND_LINE_SIZE && options_chars--) {
148 			u16 c = *s2++;
149 
150 			if (c < 0x80) {
151 				if (c == L'\0' || c == L'\n')
152 					break;
153 				if (c == L'"')
154 					in_quote = !in_quote;
155 				else if (!in_quote && isspace((char)c))
156 					safe_options_bytes = options_bytes;
157 
158 				options_bytes++;
159 				continue;
160 			}
161 
162 			/*
163 			 * Get the number of UTF-8 bytes corresponding to a
164 			 * UTF-16 character.
165 			 * The first part handles everything in the BMP.
166 			 */
167 			options_bytes += 2 + (c >= 0x800);
168 			/*
169 			 * Add one more byte for valid surrogate pairs. Invalid
170 			 * surrogates will be replaced with 0xfffd and take up
171 			 * only 3 bytes.
172 			 */
173 			if ((c & 0xfc00) == 0xd800) {
174 				/*
175 				 * If the very last word is a high surrogate,
176 				 * we must ignore it since we can't access the
177 				 * low surrogate.
178 				 */
179 				if (!options_chars) {
180 					options_bytes -= 3;
181 				} else if ((*s2 & 0xfc00) == 0xdc00) {
182 					options_bytes++;
183 					options_chars--;
184 					s2++;
185 				}
186 			}
187 		}
188 		if (options_bytes >= COMMAND_LINE_SIZE) {
189 			options_bytes = safe_options_bytes;
190 			printf("Command line is too long: truncated to %d bytes\n",
191 			       options_bytes);
192 		}
193 	}
194 
195 	options_bytes++;        /* NUL termination */
196 
197 	status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, options_bytes, (void **)&cmdline_addr);
198 	if (status != EFI_SUCCESS)
199 		return NULL;
200 
201 	snprintf((char *)cmdline_addr, options_bytes, "%.*ls", options_bytes - 1, options);
202 
203 	*cmd_line_len = options_bytes;
204 	return (char *)cmdline_addr;
205 }
206 
207 #if defined(__aarch64__) || defined(__riscv)
208 /*
209  * Open the file and read it into a buffer.
210  */
211 static void efi_load_image(efi_handle_t handle, struct efi_loaded_image_64 *image, void **data,
212 			   int *datasize, efi_char16_t *path_name)
213 {
214 	uint64_t buffer_size = sizeof(efi_file_info_t);
215 	efi_file_info_t *file_info;
216 	efi_file_io_interface_t *io_if;
217 	efi_file_t *root, *file;
218 	efi_status_t status;
219 	efi_guid_t file_system_proto_guid = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
220 	efi_guid_t file_info_guid = EFI_FILE_INFO_ID;
221 
222 	/* Open the device */
223 	status = efi_bs_call(handle_protocol, image->device_handle, &file_system_proto_guid,
224 			     (void **)&io_if);
225 	if (status != EFI_SUCCESS)
226 		return;
227 
228 	status = io_if->open_volume(io_if, &root);
229 	if (status != EFI_SUCCESS)
230 		return;
231 
232 	/* And then open the file */
233 	status = root->open(root, &file, path_name, EFI_FILE_MODE_READ, 0);
234 	if (status != EFI_SUCCESS) {
235 		printf("Failed to open %ls - %lx\n", path_name, status);
236 		assert(status == EFI_SUCCESS);
237 	}
238 
239 	/* Find the file size in order to allocate the buffer */
240 	status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, buffer_size, (void **)&file_info);
241 	if (status != EFI_SUCCESS)
242 		return;
243 
244 	status = file->get_info(file, &file_info_guid, &buffer_size, file_info);
245 	if (status == EFI_BUFFER_TOO_SMALL) {
246 		efi_free_pool(file_info);
247 		status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, buffer_size, (void **)&file_info);
248 		assert(file_info);
249 		status = file->get_info(file, &file_info_guid, &buffer_size, file_info);
250 	}
251 	assert(status == EFI_SUCCESS);
252 
253 	buffer_size = file_info->file_size;
254 
255 	efi_free_pool(file_info);
256 
257 	status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, buffer_size, (void **)data);
258 	assert(*data);
259 	/* Perform the actual read */
260 	status = file->read(file, &buffer_size, *data);
261 	if (status == EFI_BUFFER_TOO_SMALL) {
262 		efi_free_pool(*data);
263 		status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, buffer_size, (void **)data);
264 		status = file->read(file, &buffer_size, *data);
265 	}
266 	assert(status == EFI_SUCCESS);
267 
268 	*datasize = buffer_size;
269 }
270 
271 static int efi_grow_buffer(efi_status_t *status, void **buffer, uint64_t buffer_size)
272 {
273 	int try_again;
274 
275 	if (!*buffer && buffer_size) {
276 		*status = EFI_BUFFER_TOO_SMALL;
277 	}
278 
279 	try_again = 0;
280 	if (*status == EFI_BUFFER_TOO_SMALL) {
281 		if (*buffer)
282 			efi_free_pool(*buffer);
283 
284 		efi_bs_call(allocate_pool, EFI_LOADER_DATA, buffer_size, buffer);
285 		if (*buffer) {
286 			try_again = 1;
287 		} else {
288 			*status = EFI_OUT_OF_RESOURCES;
289 		}
290 	}
291 
292 	if (!try_again && EFI_ERROR(*status) && *buffer) {
293 		efi_free_pool(*buffer);
294 		*buffer = NULL;
295 	}
296 
297 	return try_again;
298 }
299 
300 static void* efi_get_var(efi_handle_t handle, struct efi_loaded_image_64 *image, efi_char16_t *var)
301 {
302 	efi_status_t status = EFI_SUCCESS;
303 	void *val = NULL;
304 	uint64_t val_size = 100;
305 	efi_guid_t efi_var_guid = EFI_VAR_GUID;
306 
307 	while (efi_grow_buffer(&status, &val, val_size + sizeof(efi_char16_t)))
308 		status = efi_rs_call(get_variable, var, &efi_var_guid, NULL, &val_size, val);
309 
310 	if (val)
311 		((efi_char16_t *)val)[val_size / sizeof(efi_char16_t)] = L'\0';
312 
313 	return val;
314 }
315 
316 static void *efi_get_fdt(efi_handle_t handle, struct efi_loaded_image_64 *image)
317 {
318 	efi_char16_t var[] = ENV_VARNAME_DTBFILE;
319 	efi_char16_t *val;
320 	void *fdt = NULL;
321 	int fdtsize = 0;
322 
323 	val = efi_get_var(handle, image, var);
324 	if (val) {
325 		efi_load_image(handle, image, &fdt, &fdtsize, val);
326 		if (fdtsize == 0)
327 			return NULL;
328 	} else if (efi_get_system_config_table(DEVICE_TREE_GUID, &fdt) != EFI_SUCCESS) {
329 		return NULL;
330 	}
331 
332 	return fdt_check_header(fdt) == 0 ? fdt : NULL;
333 }
334 #else
335 static void *efi_get_fdt(efi_handle_t handle, struct efi_loaded_image_64 *image)
336 {
337 	return NULL;
338 }
339 #endif
340 
341 static const struct {
342 	struct efi_vendor_dev_path	vendor;
343 	struct efi_generic_dev_path	end;
344 } __packed initrd_dev_path = {
345 	{
346 		{
347 			EFI_DEV_MEDIA,
348 			EFI_DEV_MEDIA_VENDOR,
349 			sizeof(struct efi_vendor_dev_path),
350 		},
351 		LINUX_EFI_INITRD_MEDIA_GUID
352 	}, {
353 		EFI_DEV_END_PATH,
354 		EFI_DEV_END_ENTIRE,
355 		sizeof(struct efi_generic_dev_path)
356 	}
357 };
358 
359 static void efi_load_initrd(void)
360 {
361 	efi_guid_t lf2_proto_guid = EFI_LOAD_FILE2_PROTOCOL_GUID;
362 	efi_device_path_protocol_t *dp;
363 	efi_load_file2_protocol_t *lf2;
364 	efi_handle_t handle;
365 	efi_status_t status;
366 	unsigned long file_size = 0;
367 
368 	initrd = NULL;
369 	initrd_size = 0;
370 
371 	dp = (efi_device_path_protocol_t *)&initrd_dev_path;
372 	status = efi_bs_call(locate_device_path, &lf2_proto_guid, &dp, &handle);
373 	if (status != EFI_SUCCESS)
374 		return;
375 
376 	status = efi_bs_call(handle_protocol, handle, &lf2_proto_guid, (void **)&lf2);
377 	assert(status == EFI_SUCCESS);
378 
379 	status = efi_call_proto(lf2, load_file, dp, false, &file_size, NULL);
380 	assert(status == EFI_BUFFER_TOO_SMALL);
381 
382 	status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, file_size, (void **)&initrd);
383 	assert(status == EFI_SUCCESS);
384 
385 	status = efi_call_proto(lf2, load_file, dp, false, &file_size, (void *)initrd);
386 	assert(status == EFI_SUCCESS);
387 
388 	initrd_size = (u32)file_size;
389 
390 	/*
391 	 * UEFI appends initrd=initrd to the command line when an initrd is present.
392 	 * Remove it in order to avoid confusing unit tests.
393 	 */
394 	if (!strcmp(__argv[__argc - 1], "initrd=initrd")) {
395 		__argv[__argc - 1] = NULL;
396 		__argc -= 1;
397 	}
398 }
399 
400 efi_status_t efi_main(efi_handle_t handle, efi_system_table_t *sys_tab)
401 {
402 	int ret;
403 	efi_status_t status;
404 	efi_bootinfo_t efi_bootinfo;
405 
406 	efi_system_table = sys_tab;
407 
408 	/* Memory map struct values */
409 	efi_memory_desc_t *map;
410 	unsigned long map_size, desc_size, key, buff_size;
411 	u32 desc_ver;
412 
413 	/* Helper variables needed to get the cmdline */
414 	struct efi_loaded_image_64 *image;
415 	efi_guid_t loaded_image_proto = LOADED_IMAGE_PROTOCOL_GUID;
416 	char *cmdline_ptr = NULL;
417 	int cmdline_size = 0;
418 
419 	/*
420 	 * Get a handle to the loaded image protocol.  This is used to get
421 	 * information about the running image, such as size and the command
422 	 * line.
423 	 */
424 	status = efi_bs_call(handle_protocol, handle, &loaded_image_proto, (void *)&image);
425 	if (status != EFI_SUCCESS) {
426 		printf("Failed to get loaded image protocol\n");
427 		goto efi_main_error;
428 	}
429 
430 	cmdline_ptr = efi_convert_cmdline(image, &cmdline_size);
431 	if (!cmdline_ptr) {
432 		printf("getting command line via LOADED_IMAGE_PROTOCOL\n");
433 		status = EFI_OUT_OF_RESOURCES;
434 		goto efi_main_error;
435 	}
436 	setup_args(cmdline_ptr);
437 
438 	efi_load_initrd();
439 
440 	efi_bootinfo.fdt = efi_get_fdt(handle, image);
441 	/* Set up efi_bootinfo */
442 	efi_bootinfo.mem_map.map = &map;
443 	efi_bootinfo.mem_map.map_size = &map_size;
444 	efi_bootinfo.mem_map.desc_size = &desc_size;
445 	efi_bootinfo.mem_map.desc_ver = &desc_ver;
446 	efi_bootinfo.mem_map.key_ptr = &key;
447 	efi_bootinfo.mem_map.buff_size = &buff_size;
448 
449 #ifdef __riscv
450 	status = efi_get_boot_hartid();
451 	if (status != EFI_SUCCESS) {
452 		printf("Failed to get boot haritd\n");
453 		goto efi_main_error;
454 	}
455 #endif
456 
457 	status = EFI_INVALID_PARAMETER;
458 	while (status == EFI_INVALID_PARAMETER) {
459 		status = efi_get_memory_map(&efi_bootinfo.mem_map);
460 		if (status != EFI_SUCCESS) {
461 			printf("Failed to get memory map\n");
462 			goto efi_main_error;
463 		}
464 		/*
465 		 * Exit EFI boot services, let kvm-unit-tests take full
466 		 * control of the guest.
467 		 */
468 		status = efi_exit_boot_services(handle, &efi_bootinfo.mem_map);
469 		if (status == EFI_INVALID_PARAMETER)
470 			efi_free_pool(*efi_bootinfo.mem_map.map);
471 	}
472 
473 	if (status != EFI_SUCCESS) {
474 		printf("Failed to exit boot services\n");
475 		goto efi_main_error;
476 	}
477 
478 	/* Set up arch-specific resources */
479 	status = setup_efi(&efi_bootinfo);
480 	if (status != EFI_SUCCESS) {
481 		printf("Failed to set up arch-specific resources\n");
482 		goto efi_main_error;
483 	}
484 
485 	printf("Address of image is: 0x%lx\n", (unsigned long)&_text);
486 
487 	/* Run the test case */
488 	ret = main(__argc, __argv, __environ);
489 
490 	/* Shutdown the guest VM */
491 	efi_exit(ret);
492 
493 	/* Unreachable */
494 	return EFI_UNSUPPORTED;
495 
496 efi_main_error:
497 	/* Shutdown the guest with error EFI status */
498 	efi_exit(status);
499 
500 	/* Unreachable */
501 	return EFI_UNSUPPORTED;
502 }
503