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