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