xref: /kvm-unit-tests/lib/efi.c (revision 7b0147ea57dc29ba844f5b60393a0639e55e88af)
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 
16 /* From each arch */
17 extern char *initrd;
18 extern u32 initrd_size;
19 
20 /* From lib/argv.c */
21 extern int __argc, __envc;
22 extern char *__argv[100];
23 extern char *__environ[200];
24 
25 extern char _text;
26 
27 extern int main(int argc, char **argv, char **envp);
28 
29 efi_system_table_t *efi_system_table = NULL;
30 
31 #ifdef __riscv
32 #define RISCV_EFI_BOOT_PROTOCOL_GUID EFI_GUID(0xccd15fec, 0x6f73, 0x4eec,  0x83, 0x95, 0x3e, 0x69, 0xe4, 0xb9, 0x40, 0xbf)
33 
34 unsigned long boot_hartid;
35 
36 struct riscv_efi_boot_protocol {
37 	u64 revision;
38 	efi_status_t (*get_boot_hartid)(struct riscv_efi_boot_protocol *,
39 		      unsigned long *boot_hartid);
40 };
41 
efi_get_boot_hartid(void)42 static efi_status_t efi_get_boot_hartid(void)
43 {
44 	efi_guid_t boot_protocol_guid = RISCV_EFI_BOOT_PROTOCOL_GUID;
45 	struct riscv_efi_boot_protocol *boot_protocol;
46 	efi_status_t status;
47 
48 	status = efi_bs_call(locate_protocol, &boot_protocol_guid, NULL,
49 			     (void **)&boot_protocol);
50 	if (status != EFI_SUCCESS)
51 		return status;
52 	return efi_call_proto(boot_protocol, get_boot_hartid, &boot_hartid);
53 }
54 #endif
55 
efi_free_pool(void * ptr)56 static void efi_free_pool(void *ptr)
57 {
58 	efi_bs_call(free_pool, ptr);
59 }
60 
efi_get_memory_map(struct efi_boot_memmap * map)61 efi_status_t efi_get_memory_map(struct efi_boot_memmap *map)
62 {
63 	efi_memory_desc_t *m = NULL;
64 	efi_status_t status;
65 	unsigned long key = 0, map_size = 0, desc_size = 0;
66 	u32 desc_ver;
67 
68 	status = efi_bs_call(get_memory_map, &map_size,
69 			     NULL, &key, &desc_size, &desc_ver);
70 	if (status != EFI_BUFFER_TOO_SMALL || map_size == 0)
71 		goto out;
72 
73 	/*
74 	 * Pad map_size with additional descriptors so we don't need to
75 	 * retry.
76 	 */
77 	map_size += 4 * desc_size;
78 	*map->buff_size = map_size;
79 	status = efi_bs_call(allocate_pool, EFI_LOADER_DATA,
80 			     map_size, (void **)&m);
81 	if (status != EFI_SUCCESS)
82 		goto out;
83 
84 	/* Get the map. */
85 	status = efi_bs_call(get_memory_map, &map_size,
86 			     m, &key, &desc_size, &desc_ver);
87 	if (status != EFI_SUCCESS) {
88 		efi_free_pool(m);
89 		goto out;
90 	}
91 
92 	*map->desc_ver = desc_ver;
93 	*map->desc_size = desc_size;
94 	*map->map_size = map_size;
95 	*map->key_ptr = key;
96 out:
97 	*map->map = m;
98 	return status;
99 }
100 
efi_exit_boot_services(void * handle,struct efi_boot_memmap * map)101 efi_status_t efi_exit_boot_services(void *handle, struct efi_boot_memmap *map)
102 {
103 	return efi_bs_call(exit_boot_services, handle, *map->key_ptr);
104 }
105 
efi_get_system_config_table(efi_guid_t table_guid,void ** table)106 efi_status_t efi_get_system_config_table(efi_guid_t table_guid, void **table)
107 {
108 	size_t i;
109 	efi_config_table_t *tables;
110 
111 	tables = (efi_config_table_t *)efi_system_table->tables;
112 	for (i = 0; i < efi_system_table->nr_tables; i++) {
113 		if (!memcmp(&table_guid, &tables[i].guid, sizeof(efi_guid_t))) {
114 			*table = tables[i].table;
115 			return EFI_SUCCESS;
116 		}
117 	}
118 	return EFI_NOT_FOUND;
119 }
120 
efi_exit(efi_status_t code)121 static void efi_exit(efi_status_t code)
122 {
123 	exit(code);
124 
125 	/*
126 	 * Fallback to UEFI reset_system() service, in case testdev is
127 	 * missing and exit() does not properly exit.
128 	 */
129 	efi_rs_call(reset_system, EFI_RESET_SHUTDOWN, code, 0, NULL);
130 }
131 
132 /* Adapted from drivers/firmware/efi/libstub/efi-stub.c */
efi_convert_cmdline(struct efi_loaded_image_64 * image,int * cmd_line_len)133 static char *efi_convert_cmdline(struct efi_loaded_image_64 *image, int *cmd_line_len)
134 {
135 	const u16 *s2;
136 	unsigned long cmdline_addr = 0;
137 	int options_chars = image->load_options_size;
138 	const u16 *options = image->load_options;
139 	int options_bytes = 0, safe_options_bytes = 0;  /* UTF-8 bytes */
140 	bool in_quote = false;
141 	efi_status_t status;
142 	const int COMMAND_LINE_SIZE = 2048;
143 
144 	if (options) {
145 		s2 = options;
146 		while (options_bytes < COMMAND_LINE_SIZE && options_chars--) {
147 			u16 c = *s2++;
148 
149 			if (c < 0x80) {
150 				if (c == L'\0' || c == L'\n')
151 					break;
152 				if (c == L'"')
153 					in_quote = !in_quote;
154 				else if (!in_quote && isspace((char)c))
155 					safe_options_bytes = options_bytes;
156 
157 				options_bytes++;
158 				continue;
159 			}
160 
161 			/*
162 			 * Get the number of UTF-8 bytes corresponding to a
163 			 * UTF-16 character.
164 			 * The first part handles everything in the BMP.
165 			 */
166 			options_bytes += 2 + (c >= 0x800);
167 			/*
168 			 * Add one more byte for valid surrogate pairs. Invalid
169 			 * surrogates will be replaced with 0xfffd and take up
170 			 * only 3 bytes.
171 			 */
172 			if ((c & 0xfc00) == 0xd800) {
173 				/*
174 				 * If the very last word is a high surrogate,
175 				 * we must ignore it since we can't access the
176 				 * low surrogate.
177 				 */
178 				if (!options_chars) {
179 					options_bytes -= 3;
180 				} else if ((*s2 & 0xfc00) == 0xdc00) {
181 					options_bytes++;
182 					options_chars--;
183 					s2++;
184 				}
185 			}
186 		}
187 		if (options_bytes >= COMMAND_LINE_SIZE) {
188 			options_bytes = safe_options_bytes;
189 			printf("Command line is too long: truncated to %d bytes\n",
190 			       options_bytes);
191 		}
192 	}
193 
194 	options_bytes++;        /* NUL termination */
195 
196 	status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, options_bytes, (void **)&cmdline_addr);
197 	if (status != EFI_SUCCESS)
198 		return NULL;
199 
200 	snprintf((char *)cmdline_addr, options_bytes, "%.*ls", options_bytes - 1, options);
201 
202 	*cmd_line_len = options_bytes;
203 	return (char *)cmdline_addr;
204 }
205 
206 #if defined(__aarch64__) || defined(__riscv)
207 #include "libfdt/libfdt.h"
208 /*
209  * Open the file and read it into a buffer.
210  */
efi_load_image(efi_handle_t handle,struct efi_loaded_image_64 * image,void ** data,int * datasize,efi_char16_t * path_name)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 
efi_grow_buffer(efi_status_t * status,void ** buffer,uint64_t buffer_size)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 
efi_get_var(efi_handle_t handle,struct efi_loaded_image_64 * image,efi_char16_t * var)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 
efi_get_fdt(efi_handle_t handle,struct efi_loaded_image_64 * image)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
efi_get_fdt(efi_handle_t handle,struct efi_loaded_image_64 * image)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 
efi_load_initrd(void)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 
efi_main(efi_handle_t handle,efi_system_table_t * sys_tab)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