xref: /kvm-unit-tests/lib/efi.c (revision 85c3c524ebe0a64c15acca8f6cff9d91b2c935f5)
10eaa5176SVarad Gautam /*
20eaa5176SVarad Gautam  * EFI-related functions to set up and run test cases in EFI
30eaa5176SVarad Gautam  *
40eaa5176SVarad Gautam  * Copyright (c) 2021, SUSE, Varad Gautam <varad.gautam@suse.com>
5ad5fb883SZixuan Wang  * Copyright (c) 2021, Google Inc, Zixuan Wang <zixuanwang@google.com>
60eaa5176SVarad Gautam  *
70eaa5176SVarad Gautam  * SPDX-License-Identifier: LGPL-2.0-or-later
80eaa5176SVarad Gautam  */
90eaa5176SVarad Gautam 
10ad5fb883SZixuan Wang #include "efi.h"
11*85c3c524SNikos Nikoleris #include <argv.h>
12*85c3c524SNikos Nikoleris #include <stdlib.h>
13*85c3c524SNikos Nikoleris #include <ctype.h>
14ad5fb883SZixuan Wang #include <libcflat.h>
15ad5fb883SZixuan Wang #include <asm/setup.h>
16ad5fb883SZixuan Wang 
17ad5fb883SZixuan Wang /* From lib/argv.c */
18ad5fb883SZixuan Wang extern int __argc, __envc;
19ad5fb883SZixuan Wang extern char *__argv[100];
20ad5fb883SZixuan Wang extern char *__environ[200];
21ad5fb883SZixuan Wang 
22ad5fb883SZixuan Wang extern int main(int argc, char **argv, char **envp);
23ad5fb883SZixuan Wang 
240eaa5176SVarad Gautam efi_system_table_t *efi_system_table = NULL;
250eaa5176SVarad Gautam 
260eaa5176SVarad Gautam static void efi_free_pool(void *ptr)
270eaa5176SVarad Gautam {
280eaa5176SVarad Gautam 	efi_bs_call(free_pool, ptr);
290eaa5176SVarad Gautam }
300eaa5176SVarad Gautam 
31ad5fb883SZixuan Wang efi_status_t efi_get_memory_map(struct efi_boot_memmap *map)
320eaa5176SVarad Gautam {
330eaa5176SVarad Gautam 	efi_memory_desc_t *m = NULL;
340eaa5176SVarad Gautam 	efi_status_t status;
350eaa5176SVarad Gautam 	unsigned long key = 0, map_size = 0, desc_size = 0;
361ae9072eSZixuan Wang 	u32 desc_ver;
370eaa5176SVarad Gautam 
380eaa5176SVarad Gautam 	status = efi_bs_call(get_memory_map, &map_size,
391ae9072eSZixuan Wang 			     NULL, &key, &desc_size, &desc_ver);
400eaa5176SVarad Gautam 	if (status != EFI_BUFFER_TOO_SMALL || map_size == 0)
410eaa5176SVarad Gautam 		goto out;
420eaa5176SVarad Gautam 
430eaa5176SVarad Gautam 	/*
440eaa5176SVarad Gautam 	 * Pad map_size with additional descriptors so we don't need to
450eaa5176SVarad Gautam 	 * retry.
460eaa5176SVarad Gautam 	 */
470eaa5176SVarad Gautam 	map_size += 4 * desc_size;
480eaa5176SVarad Gautam 	*map->buff_size = map_size;
490eaa5176SVarad Gautam 	status = efi_bs_call(allocate_pool, EFI_LOADER_DATA,
500eaa5176SVarad Gautam 			     map_size, (void **)&m);
510eaa5176SVarad Gautam 	if (status != EFI_SUCCESS)
520eaa5176SVarad Gautam 		goto out;
530eaa5176SVarad Gautam 
540eaa5176SVarad Gautam 	/* Get the map. */
550eaa5176SVarad Gautam 	status = efi_bs_call(get_memory_map, &map_size,
561ae9072eSZixuan Wang 			     m, &key, &desc_size, &desc_ver);
570eaa5176SVarad Gautam 	if (status != EFI_SUCCESS) {
580eaa5176SVarad Gautam 		efi_free_pool(m);
590eaa5176SVarad Gautam 		goto out;
600eaa5176SVarad Gautam 	}
610eaa5176SVarad Gautam 
621ae9072eSZixuan Wang 	*map->desc_ver = desc_ver;
630eaa5176SVarad Gautam 	*map->desc_size = desc_size;
640eaa5176SVarad Gautam 	*map->map_size = map_size;
650eaa5176SVarad Gautam 	*map->key_ptr = key;
660eaa5176SVarad Gautam out:
670eaa5176SVarad Gautam 	*map->map = m;
680eaa5176SVarad Gautam 	return status;
690eaa5176SVarad Gautam }
700eaa5176SVarad Gautam 
71b4e8c300SZixuan Wang efi_status_t efi_exit_boot_services(void *handle, struct efi_boot_memmap *map)
720eaa5176SVarad Gautam {
73b4e8c300SZixuan Wang 	return efi_bs_call(exit_boot_services, handle, *map->key_ptr);
740eaa5176SVarad Gautam }
750eaa5176SVarad Gautam 
76f20589d6SZixuan Wang efi_status_t efi_get_system_config_table(efi_guid_t table_guid, void **table)
77f20589d6SZixuan Wang {
78f20589d6SZixuan Wang 	size_t i;
79f20589d6SZixuan Wang 	efi_config_table_t *tables;
80f20589d6SZixuan Wang 
81f20589d6SZixuan Wang 	tables = (efi_config_table_t *)efi_system_table->tables;
82f20589d6SZixuan Wang 	for (i = 0; i < efi_system_table->nr_tables; i++) {
83f20589d6SZixuan Wang 		if (!memcmp(&table_guid, &tables[i].guid, sizeof(efi_guid_t))) {
84f20589d6SZixuan Wang 			*table = tables[i].table;
85f20589d6SZixuan Wang 			return EFI_SUCCESS;
86f20589d6SZixuan Wang 		}
87f20589d6SZixuan Wang 	}
88f20589d6SZixuan Wang 	return EFI_NOT_FOUND;
89f20589d6SZixuan Wang }
90f20589d6SZixuan Wang 
912f47d025SZixuan Wang static void efi_exit(efi_status_t code)
922f47d025SZixuan Wang {
932f47d025SZixuan Wang 	exit(code);
942f47d025SZixuan Wang 
952f47d025SZixuan Wang 	/*
962f47d025SZixuan Wang 	 * Fallback to UEFI reset_system() service, in case testdev is
972f47d025SZixuan Wang 	 * missing and exit() does not properly exit.
982f47d025SZixuan Wang 	 */
992f47d025SZixuan Wang 	efi_rs_call(reset_system, EFI_RESET_SHUTDOWN, code, 0, NULL);
1002f47d025SZixuan Wang }
1012f47d025SZixuan Wang 
102*85c3c524SNikos Nikoleris /* Adapted from drivers/firmware/efi/libstub/efi-stub.c */
103*85c3c524SNikos Nikoleris static char *efi_convert_cmdline(struct efi_loaded_image_64 *image, int *cmd_line_len)
104*85c3c524SNikos Nikoleris {
105*85c3c524SNikos Nikoleris 	const u16 *s2;
106*85c3c524SNikos Nikoleris 	unsigned long cmdline_addr = 0;
107*85c3c524SNikos Nikoleris 	int options_chars = image->load_options_size;
108*85c3c524SNikos Nikoleris 	const u16 *options = image->load_options;
109*85c3c524SNikos Nikoleris 	int options_bytes = 0, safe_options_bytes = 0;  /* UTF-8 bytes */
110*85c3c524SNikos Nikoleris 	bool in_quote = false;
111*85c3c524SNikos Nikoleris 	efi_status_t status;
112*85c3c524SNikos Nikoleris 	const int COMMAND_LINE_SIZE = 2048;
113*85c3c524SNikos Nikoleris 
114*85c3c524SNikos Nikoleris 	if (options) {
115*85c3c524SNikos Nikoleris 		s2 = options;
116*85c3c524SNikos Nikoleris 		while (options_bytes < COMMAND_LINE_SIZE && options_chars--) {
117*85c3c524SNikos Nikoleris 			u16 c = *s2++;
118*85c3c524SNikos Nikoleris 
119*85c3c524SNikos Nikoleris 			if (c < 0x80) {
120*85c3c524SNikos Nikoleris 				if (c == L'\0' || c == L'\n')
121*85c3c524SNikos Nikoleris 					break;
122*85c3c524SNikos Nikoleris 				if (c == L'"')
123*85c3c524SNikos Nikoleris 					in_quote = !in_quote;
124*85c3c524SNikos Nikoleris 				else if (!in_quote && isspace((char)c))
125*85c3c524SNikos Nikoleris 					safe_options_bytes = options_bytes;
126*85c3c524SNikos Nikoleris 
127*85c3c524SNikos Nikoleris 				options_bytes++;
128*85c3c524SNikos Nikoleris 				continue;
129*85c3c524SNikos Nikoleris 			}
130*85c3c524SNikos Nikoleris 
131*85c3c524SNikos Nikoleris 			/*
132*85c3c524SNikos Nikoleris 			 * Get the number of UTF-8 bytes corresponding to a
133*85c3c524SNikos Nikoleris 			 * UTF-16 character.
134*85c3c524SNikos Nikoleris 			 * The first part handles everything in the BMP.
135*85c3c524SNikos Nikoleris 			 */
136*85c3c524SNikos Nikoleris 			options_bytes += 2 + (c >= 0x800);
137*85c3c524SNikos Nikoleris 			/*
138*85c3c524SNikos Nikoleris 			 * Add one more byte for valid surrogate pairs. Invalid
139*85c3c524SNikos Nikoleris 			 * surrogates will be replaced with 0xfffd and take up
140*85c3c524SNikos Nikoleris 			 * only 3 bytes.
141*85c3c524SNikos Nikoleris 			 */
142*85c3c524SNikos Nikoleris 			if ((c & 0xfc00) == 0xd800) {
143*85c3c524SNikos Nikoleris 				/*
144*85c3c524SNikos Nikoleris 				 * If the very last word is a high surrogate,
145*85c3c524SNikos Nikoleris 				 * we must ignore it since we can't access the
146*85c3c524SNikos Nikoleris 				 * low surrogate.
147*85c3c524SNikos Nikoleris 				 */
148*85c3c524SNikos Nikoleris 				if (!options_chars) {
149*85c3c524SNikos Nikoleris 					options_bytes -= 3;
150*85c3c524SNikos Nikoleris 				} else if ((*s2 & 0xfc00) == 0xdc00) {
151*85c3c524SNikos Nikoleris 					options_bytes++;
152*85c3c524SNikos Nikoleris 					options_chars--;
153*85c3c524SNikos Nikoleris 					s2++;
154*85c3c524SNikos Nikoleris 				}
155*85c3c524SNikos Nikoleris 			}
156*85c3c524SNikos Nikoleris 		}
157*85c3c524SNikos Nikoleris 		if (options_bytes >= COMMAND_LINE_SIZE) {
158*85c3c524SNikos Nikoleris 			options_bytes = safe_options_bytes;
159*85c3c524SNikos Nikoleris 			printf("Command line is too long: truncated to %d bytes\n",
160*85c3c524SNikos Nikoleris 			       options_bytes);
161*85c3c524SNikos Nikoleris 		}
162*85c3c524SNikos Nikoleris 	}
163*85c3c524SNikos Nikoleris 
164*85c3c524SNikos Nikoleris 	options_bytes++;        /* NUL termination */
165*85c3c524SNikos Nikoleris 
166*85c3c524SNikos Nikoleris 	status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, options_bytes, (void **)&cmdline_addr);
167*85c3c524SNikos Nikoleris 	if (status != EFI_SUCCESS)
168*85c3c524SNikos Nikoleris 		return NULL;
169*85c3c524SNikos Nikoleris 
170*85c3c524SNikos Nikoleris 	snprintf((char *)cmdline_addr, options_bytes, "%.*ls", options_bytes - 1, options);
171*85c3c524SNikos Nikoleris 
172*85c3c524SNikos Nikoleris 	*cmd_line_len = options_bytes;
173*85c3c524SNikos Nikoleris 	return (char *)cmdline_addr;
174*85c3c524SNikos Nikoleris }
175*85c3c524SNikos Nikoleris 
176ad5fb883SZixuan Wang efi_status_t efi_main(efi_handle_t handle, efi_system_table_t *sys_tab)
1770eaa5176SVarad Gautam {
178ad5fb883SZixuan Wang 	int ret;
1791ae9072eSZixuan Wang 	efi_status_t status;
1801ae9072eSZixuan Wang 	efi_bootinfo_t efi_bootinfo;
181ad5fb883SZixuan Wang 
1820eaa5176SVarad Gautam 	efi_system_table = sys_tab;
1830eaa5176SVarad Gautam 
184b4e8c300SZixuan Wang 	/* Memory map struct values */
185b4e8c300SZixuan Wang 	efi_memory_desc_t *map = NULL;
186b4e8c300SZixuan Wang 	unsigned long map_size = 0, desc_size = 0, key = 0, buff_size = 0;
187b4e8c300SZixuan Wang 	u32 desc_ver;
188b4e8c300SZixuan Wang 
189*85c3c524SNikos Nikoleris 	/* Helper variables needed to get the cmdline */
190*85c3c524SNikos Nikoleris 	struct efi_loaded_image_64 *image;
191*85c3c524SNikos Nikoleris 	efi_guid_t loaded_image_proto = LOADED_IMAGE_PROTOCOL_GUID;
192*85c3c524SNikos Nikoleris 	char *cmdline_ptr = NULL;
193*85c3c524SNikos Nikoleris 	int cmdline_size = 0;
194*85c3c524SNikos Nikoleris 
195*85c3c524SNikos Nikoleris 	/*
196*85c3c524SNikos Nikoleris 	 * Get a handle to the loaded image protocol.  This is used to get
197*85c3c524SNikos Nikoleris 	 * information about the running image, such as size and the command
198*85c3c524SNikos Nikoleris 	 * line.
199*85c3c524SNikos Nikoleris 	 */
200*85c3c524SNikos Nikoleris 	status = efi_bs_call(handle_protocol, handle, &loaded_image_proto, (void *)&image);
201*85c3c524SNikos Nikoleris 	if (status != EFI_SUCCESS) {
202*85c3c524SNikos Nikoleris 		printf("Failed to get loaded image protocol\n");
203*85c3c524SNikos Nikoleris 		goto efi_main_error;
204*85c3c524SNikos Nikoleris 	}
205*85c3c524SNikos Nikoleris 
206*85c3c524SNikos Nikoleris 	cmdline_ptr = efi_convert_cmdline(image, &cmdline_size);
207*85c3c524SNikos Nikoleris 	if (!cmdline_ptr) {
208*85c3c524SNikos Nikoleris 		printf("getting command line via LOADED_IMAGE_PROTOCOL\n");
209*85c3c524SNikos Nikoleris 		status = EFI_OUT_OF_RESOURCES;
210*85c3c524SNikos Nikoleris 		goto efi_main_error;
211*85c3c524SNikos Nikoleris 	}
212*85c3c524SNikos Nikoleris 	setup_args(cmdline_ptr);
213*85c3c524SNikos Nikoleris 
214b4e8c300SZixuan Wang 	/* Set up efi_bootinfo */
215b4e8c300SZixuan Wang 	efi_bootinfo.mem_map.map = &map;
216b4e8c300SZixuan Wang 	efi_bootinfo.mem_map.map_size = &map_size;
217b4e8c300SZixuan Wang 	efi_bootinfo.mem_map.desc_size = &desc_size;
218b4e8c300SZixuan Wang 	efi_bootinfo.mem_map.desc_ver = &desc_ver;
219b4e8c300SZixuan Wang 	efi_bootinfo.mem_map.key_ptr = &key;
220b4e8c300SZixuan Wang 	efi_bootinfo.mem_map.buff_size = &buff_size;
221b4e8c300SZixuan Wang 
222b4e8c300SZixuan Wang 	/* Get EFI memory map */
223b4e8c300SZixuan Wang 	status = efi_get_memory_map(&efi_bootinfo.mem_map);
2241ae9072eSZixuan Wang 	if (status != EFI_SUCCESS) {
225b4e8c300SZixuan Wang 		printf("Failed to get memory map\n");
226b4e8c300SZixuan Wang 		goto efi_main_error;
2271ae9072eSZixuan Wang 	}
2281ae9072eSZixuan Wang 
229b4e8c300SZixuan Wang 	/*
230b4e8c300SZixuan Wang 	 * Exit EFI boot services, let kvm-unit-tests take full control of the
231b4e8c300SZixuan Wang 	 * guest
232b4e8c300SZixuan Wang 	 */
233b4e8c300SZixuan Wang 	status = efi_exit_boot_services(handle, &efi_bootinfo.mem_map);
2341ae9072eSZixuan Wang 	if (status != EFI_SUCCESS) {
2351ae9072eSZixuan Wang 		printf("Failed to exit boot services\n");
236b4e8c300SZixuan Wang 		goto efi_main_error;
2371ae9072eSZixuan Wang 	}
2381ae9072eSZixuan Wang 
239b4e8c300SZixuan Wang 	/* Set up arch-specific resources */
240b4e8c300SZixuan Wang 	status = setup_efi(&efi_bootinfo);
241b4e8c300SZixuan Wang 	if (status != EFI_SUCCESS) {
242b4e8c300SZixuan Wang 		printf("Failed to set up arch-specific resources\n");
243b4e8c300SZixuan Wang 		goto efi_main_error;
244b4e8c300SZixuan Wang 	}
245b4e8c300SZixuan Wang 
246b4e8c300SZixuan Wang 	/* Run the test case */
247ad5fb883SZixuan Wang 	ret = main(__argc, __argv, __environ);
248ad5fb883SZixuan Wang 
2492f47d025SZixuan Wang 	/* Shutdown the guest VM */
2502f47d025SZixuan Wang 	efi_exit(ret);
251ad5fb883SZixuan Wang 
252ad5fb883SZixuan Wang 	/* Unreachable */
253ad5fb883SZixuan Wang 	return EFI_UNSUPPORTED;
254b4e8c300SZixuan Wang 
255b4e8c300SZixuan Wang efi_main_error:
256b4e8c300SZixuan Wang 	/* Shutdown the guest with error EFI status */
2572f47d025SZixuan Wang 	efi_exit(status);
258b4e8c300SZixuan Wang 
259b4e8c300SZixuan Wang 	/* Unreachable */
260b4e8c300SZixuan Wang 	return EFI_UNSUPPORTED;
2610eaa5176SVarad Gautam }
262