xref: /kvm-unit-tests/lib/efi.c (revision 7b0147ea57dc29ba844f5b60393a0639e55e88af)
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  */
9ad5fb883SZixuan Wang #include <libcflat.h>
109632ce44SAndrew Jones #include <argv.h>
119632ce44SAndrew Jones #include <ctype.h>
129632ce44SAndrew Jones #include <stdlib.h>
13ad5fb883SZixuan Wang #include <asm/setup.h>
149632ce44SAndrew Jones #include "efi.h"
15ad5fb883SZixuan Wang 
16b9f8ff7cSAndrew Jones /* From each arch */
17b9f8ff7cSAndrew Jones extern char *initrd;
18b9f8ff7cSAndrew Jones extern u32 initrd_size;
19b9f8ff7cSAndrew Jones 
20ad5fb883SZixuan Wang /* From lib/argv.c */
21ad5fb883SZixuan Wang extern int __argc, __envc;
22ad5fb883SZixuan Wang extern char *__argv[100];
23ad5fb883SZixuan Wang extern char *__environ[200];
24ad5fb883SZixuan Wang 
2525ef5154SNadav Amit extern char _text;
2625ef5154SNadav Amit 
27ad5fb883SZixuan Wang extern int main(int argc, char **argv, char **envp);
28ad5fb883SZixuan Wang 
290eaa5176SVarad Gautam efi_system_table_t *efi_system_table = NULL;
300eaa5176SVarad Gautam 
31ba0a6fd4SAndrew Jones #ifdef __riscv
32ba0a6fd4SAndrew Jones #define RISCV_EFI_BOOT_PROTOCOL_GUID EFI_GUID(0xccd15fec, 0x6f73, 0x4eec,  0x83, 0x95, 0x3e, 0x69, 0xe4, 0xb9, 0x40, 0xbf)
33ba0a6fd4SAndrew Jones 
34ba0a6fd4SAndrew Jones unsigned long boot_hartid;
35ba0a6fd4SAndrew Jones 
36ba0a6fd4SAndrew Jones struct riscv_efi_boot_protocol {
37ba0a6fd4SAndrew Jones 	u64 revision;
38ba0a6fd4SAndrew Jones 	efi_status_t (*get_boot_hartid)(struct riscv_efi_boot_protocol *,
39ba0a6fd4SAndrew Jones 		      unsigned long *boot_hartid);
40ba0a6fd4SAndrew Jones };
41ba0a6fd4SAndrew Jones 
efi_get_boot_hartid(void)42ba0a6fd4SAndrew Jones static efi_status_t efi_get_boot_hartid(void)
43ba0a6fd4SAndrew Jones {
44ba0a6fd4SAndrew Jones 	efi_guid_t boot_protocol_guid = RISCV_EFI_BOOT_PROTOCOL_GUID;
45ba0a6fd4SAndrew Jones 	struct riscv_efi_boot_protocol *boot_protocol;
46ba0a6fd4SAndrew Jones 	efi_status_t status;
47ba0a6fd4SAndrew Jones 
48ba0a6fd4SAndrew Jones 	status = efi_bs_call(locate_protocol, &boot_protocol_guid, NULL,
49ba0a6fd4SAndrew Jones 			     (void **)&boot_protocol);
50ba0a6fd4SAndrew Jones 	if (status != EFI_SUCCESS)
51ba0a6fd4SAndrew Jones 		return status;
52ba0a6fd4SAndrew Jones 	return efi_call_proto(boot_protocol, get_boot_hartid, &boot_hartid);
53ba0a6fd4SAndrew Jones }
54ba0a6fd4SAndrew Jones #endif
55ba0a6fd4SAndrew Jones 
efi_free_pool(void * ptr)560eaa5176SVarad Gautam static void efi_free_pool(void *ptr)
570eaa5176SVarad Gautam {
580eaa5176SVarad Gautam 	efi_bs_call(free_pool, ptr);
590eaa5176SVarad Gautam }
600eaa5176SVarad Gautam 
efi_get_memory_map(struct efi_boot_memmap * map)61ad5fb883SZixuan Wang efi_status_t efi_get_memory_map(struct efi_boot_memmap *map)
620eaa5176SVarad Gautam {
630eaa5176SVarad Gautam 	efi_memory_desc_t *m = NULL;
640eaa5176SVarad Gautam 	efi_status_t status;
650eaa5176SVarad Gautam 	unsigned long key = 0, map_size = 0, desc_size = 0;
661ae9072eSZixuan Wang 	u32 desc_ver;
670eaa5176SVarad Gautam 
680eaa5176SVarad Gautam 	status = efi_bs_call(get_memory_map, &map_size,
691ae9072eSZixuan Wang 			     NULL, &key, &desc_size, &desc_ver);
700eaa5176SVarad Gautam 	if (status != EFI_BUFFER_TOO_SMALL || map_size == 0)
710eaa5176SVarad Gautam 		goto out;
720eaa5176SVarad Gautam 
730eaa5176SVarad Gautam 	/*
740eaa5176SVarad Gautam 	 * Pad map_size with additional descriptors so we don't need to
750eaa5176SVarad Gautam 	 * retry.
760eaa5176SVarad Gautam 	 */
770eaa5176SVarad Gautam 	map_size += 4 * desc_size;
780eaa5176SVarad Gautam 	*map->buff_size = map_size;
790eaa5176SVarad Gautam 	status = efi_bs_call(allocate_pool, EFI_LOADER_DATA,
800eaa5176SVarad Gautam 			     map_size, (void **)&m);
810eaa5176SVarad Gautam 	if (status != EFI_SUCCESS)
820eaa5176SVarad Gautam 		goto out;
830eaa5176SVarad Gautam 
840eaa5176SVarad Gautam 	/* Get the map. */
850eaa5176SVarad Gautam 	status = efi_bs_call(get_memory_map, &map_size,
861ae9072eSZixuan Wang 			     m, &key, &desc_size, &desc_ver);
870eaa5176SVarad Gautam 	if (status != EFI_SUCCESS) {
880eaa5176SVarad Gautam 		efi_free_pool(m);
890eaa5176SVarad Gautam 		goto out;
900eaa5176SVarad Gautam 	}
910eaa5176SVarad Gautam 
921ae9072eSZixuan Wang 	*map->desc_ver = desc_ver;
930eaa5176SVarad Gautam 	*map->desc_size = desc_size;
940eaa5176SVarad Gautam 	*map->map_size = map_size;
950eaa5176SVarad Gautam 	*map->key_ptr = key;
960eaa5176SVarad Gautam out:
970eaa5176SVarad Gautam 	*map->map = m;
980eaa5176SVarad Gautam 	return status;
990eaa5176SVarad Gautam }
1000eaa5176SVarad Gautam 
efi_exit_boot_services(void * handle,struct efi_boot_memmap * map)101b4e8c300SZixuan Wang efi_status_t efi_exit_boot_services(void *handle, struct efi_boot_memmap *map)
1020eaa5176SVarad Gautam {
103b4e8c300SZixuan Wang 	return efi_bs_call(exit_boot_services, handle, *map->key_ptr);
1040eaa5176SVarad Gautam }
1050eaa5176SVarad Gautam 
efi_get_system_config_table(efi_guid_t table_guid,void ** table)106f20589d6SZixuan Wang efi_status_t efi_get_system_config_table(efi_guid_t table_guid, void **table)
107f20589d6SZixuan Wang {
108f20589d6SZixuan Wang 	size_t i;
109f20589d6SZixuan Wang 	efi_config_table_t *tables;
110f20589d6SZixuan Wang 
111f20589d6SZixuan Wang 	tables = (efi_config_table_t *)efi_system_table->tables;
112f20589d6SZixuan Wang 	for (i = 0; i < efi_system_table->nr_tables; i++) {
113f20589d6SZixuan Wang 		if (!memcmp(&table_guid, &tables[i].guid, sizeof(efi_guid_t))) {
114f20589d6SZixuan Wang 			*table = tables[i].table;
115f20589d6SZixuan Wang 			return EFI_SUCCESS;
116f20589d6SZixuan Wang 		}
117f20589d6SZixuan Wang 	}
118f20589d6SZixuan Wang 	return EFI_NOT_FOUND;
119f20589d6SZixuan Wang }
120f20589d6SZixuan Wang 
efi_exit(efi_status_t code)1212f47d025SZixuan Wang static void efi_exit(efi_status_t code)
1222f47d025SZixuan Wang {
1232f47d025SZixuan Wang 	exit(code);
1242f47d025SZixuan Wang 
1252f47d025SZixuan Wang 	/*
1262f47d025SZixuan Wang 	 * Fallback to UEFI reset_system() service, in case testdev is
1272f47d025SZixuan Wang 	 * missing and exit() does not properly exit.
1282f47d025SZixuan Wang 	 */
1292f47d025SZixuan Wang 	efi_rs_call(reset_system, EFI_RESET_SHUTDOWN, code, 0, NULL);
1302f47d025SZixuan Wang }
1312f47d025SZixuan Wang 
13285c3c524SNikos Nikoleris /* Adapted from drivers/firmware/efi/libstub/efi-stub.c */
efi_convert_cmdline(struct efi_loaded_image_64 * image,int * cmd_line_len)13385c3c524SNikos Nikoleris static char *efi_convert_cmdline(struct efi_loaded_image_64 *image, int *cmd_line_len)
13485c3c524SNikos Nikoleris {
13585c3c524SNikos Nikoleris 	const u16 *s2;
13685c3c524SNikos Nikoleris 	unsigned long cmdline_addr = 0;
13785c3c524SNikos Nikoleris 	int options_chars = image->load_options_size;
13885c3c524SNikos Nikoleris 	const u16 *options = image->load_options;
13985c3c524SNikos Nikoleris 	int options_bytes = 0, safe_options_bytes = 0;  /* UTF-8 bytes */
14085c3c524SNikos Nikoleris 	bool in_quote = false;
14185c3c524SNikos Nikoleris 	efi_status_t status;
14285c3c524SNikos Nikoleris 	const int COMMAND_LINE_SIZE = 2048;
14385c3c524SNikos Nikoleris 
14485c3c524SNikos Nikoleris 	if (options) {
14585c3c524SNikos Nikoleris 		s2 = options;
14685c3c524SNikos Nikoleris 		while (options_bytes < COMMAND_LINE_SIZE && options_chars--) {
14785c3c524SNikos Nikoleris 			u16 c = *s2++;
14885c3c524SNikos Nikoleris 
14985c3c524SNikos Nikoleris 			if (c < 0x80) {
15085c3c524SNikos Nikoleris 				if (c == L'\0' || c == L'\n')
15185c3c524SNikos Nikoleris 					break;
15285c3c524SNikos Nikoleris 				if (c == L'"')
15385c3c524SNikos Nikoleris 					in_quote = !in_quote;
15485c3c524SNikos Nikoleris 				else if (!in_quote && isspace((char)c))
15585c3c524SNikos Nikoleris 					safe_options_bytes = options_bytes;
15685c3c524SNikos Nikoleris 
15785c3c524SNikos Nikoleris 				options_bytes++;
15885c3c524SNikos Nikoleris 				continue;
15985c3c524SNikos Nikoleris 			}
16085c3c524SNikos Nikoleris 
16185c3c524SNikos Nikoleris 			/*
16285c3c524SNikos Nikoleris 			 * Get the number of UTF-8 bytes corresponding to a
16385c3c524SNikos Nikoleris 			 * UTF-16 character.
16485c3c524SNikos Nikoleris 			 * The first part handles everything in the BMP.
16585c3c524SNikos Nikoleris 			 */
16685c3c524SNikos Nikoleris 			options_bytes += 2 + (c >= 0x800);
16785c3c524SNikos Nikoleris 			/*
16885c3c524SNikos Nikoleris 			 * Add one more byte for valid surrogate pairs. Invalid
16985c3c524SNikos Nikoleris 			 * surrogates will be replaced with 0xfffd and take up
17085c3c524SNikos Nikoleris 			 * only 3 bytes.
17185c3c524SNikos Nikoleris 			 */
17285c3c524SNikos Nikoleris 			if ((c & 0xfc00) == 0xd800) {
17385c3c524SNikos Nikoleris 				/*
17485c3c524SNikos Nikoleris 				 * If the very last word is a high surrogate,
17585c3c524SNikos Nikoleris 				 * we must ignore it since we can't access the
17685c3c524SNikos Nikoleris 				 * low surrogate.
17785c3c524SNikos Nikoleris 				 */
17885c3c524SNikos Nikoleris 				if (!options_chars) {
17985c3c524SNikos Nikoleris 					options_bytes -= 3;
18085c3c524SNikos Nikoleris 				} else if ((*s2 & 0xfc00) == 0xdc00) {
18185c3c524SNikos Nikoleris 					options_bytes++;
18285c3c524SNikos Nikoleris 					options_chars--;
18385c3c524SNikos Nikoleris 					s2++;
18485c3c524SNikos Nikoleris 				}
18585c3c524SNikos Nikoleris 			}
18685c3c524SNikos Nikoleris 		}
18785c3c524SNikos Nikoleris 		if (options_bytes >= COMMAND_LINE_SIZE) {
18885c3c524SNikos Nikoleris 			options_bytes = safe_options_bytes;
18985c3c524SNikos Nikoleris 			printf("Command line is too long: truncated to %d bytes\n",
19085c3c524SNikos Nikoleris 			       options_bytes);
19185c3c524SNikos Nikoleris 		}
19285c3c524SNikos Nikoleris 	}
19385c3c524SNikos Nikoleris 
19485c3c524SNikos Nikoleris 	options_bytes++;        /* NUL termination */
19585c3c524SNikos Nikoleris 
19685c3c524SNikos Nikoleris 	status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, options_bytes, (void **)&cmdline_addr);
19785c3c524SNikos Nikoleris 	if (status != EFI_SUCCESS)
19885c3c524SNikos Nikoleris 		return NULL;
19985c3c524SNikos Nikoleris 
20085c3c524SNikos Nikoleris 	snprintf((char *)cmdline_addr, options_bytes, "%.*ls", options_bytes - 1, options);
20185c3c524SNikos Nikoleris 
20285c3c524SNikos Nikoleris 	*cmd_line_len = options_bytes;
20385c3c524SNikos Nikoleris 	return (char *)cmdline_addr;
20485c3c524SNikos Nikoleris }
20585c3c524SNikos Nikoleris 
206bb294dfdSPavan Kumar Paluri #if defined(__aarch64__) || defined(__riscv)
207*fc35f6ccSVasant Karasulli #include "libfdt/libfdt.h"
208529ab889SNikos Nikoleris /*
209529ab889SNikos Nikoleris  * Open the file and read it into a buffer.
210529ab889SNikos Nikoleris  */
efi_load_image(efi_handle_t handle,struct efi_loaded_image_64 * image,void ** data,int * datasize,efi_char16_t * path_name)211529ab889SNikos Nikoleris static void efi_load_image(efi_handle_t handle, struct efi_loaded_image_64 *image, void **data,
212529ab889SNikos Nikoleris 			   int *datasize, efi_char16_t *path_name)
213529ab889SNikos Nikoleris {
214529ab889SNikos Nikoleris 	uint64_t buffer_size = sizeof(efi_file_info_t);
215529ab889SNikos Nikoleris 	efi_file_info_t *file_info;
216529ab889SNikos Nikoleris 	efi_file_io_interface_t *io_if;
217529ab889SNikos Nikoleris 	efi_file_t *root, *file;
218529ab889SNikos Nikoleris 	efi_status_t status;
219529ab889SNikos Nikoleris 	efi_guid_t file_system_proto_guid = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
220529ab889SNikos Nikoleris 	efi_guid_t file_info_guid = EFI_FILE_INFO_ID;
221529ab889SNikos Nikoleris 
222529ab889SNikos Nikoleris 	/* Open the device */
223529ab889SNikos Nikoleris 	status = efi_bs_call(handle_protocol, image->device_handle, &file_system_proto_guid,
224529ab889SNikos Nikoleris 			     (void **)&io_if);
225529ab889SNikos Nikoleris 	if (status != EFI_SUCCESS)
226529ab889SNikos Nikoleris 		return;
227529ab889SNikos Nikoleris 
228529ab889SNikos Nikoleris 	status = io_if->open_volume(io_if, &root);
229529ab889SNikos Nikoleris 	if (status != EFI_SUCCESS)
230529ab889SNikos Nikoleris 		return;
231529ab889SNikos Nikoleris 
232529ab889SNikos Nikoleris 	/* And then open the file */
233529ab889SNikos Nikoleris 	status = root->open(root, &file, path_name, EFI_FILE_MODE_READ, 0);
234529ab889SNikos Nikoleris 	if (status != EFI_SUCCESS) {
235529ab889SNikos Nikoleris 		printf("Failed to open %ls - %lx\n", path_name, status);
236529ab889SNikos Nikoleris 		assert(status == EFI_SUCCESS);
237529ab889SNikos Nikoleris 	}
238529ab889SNikos Nikoleris 
239529ab889SNikos Nikoleris 	/* Find the file size in order to allocate the buffer */
240529ab889SNikos Nikoleris 	status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, buffer_size, (void **)&file_info);
241529ab889SNikos Nikoleris 	if (status != EFI_SUCCESS)
242529ab889SNikos Nikoleris 		return;
243529ab889SNikos Nikoleris 
244529ab889SNikos Nikoleris 	status = file->get_info(file, &file_info_guid, &buffer_size, file_info);
245529ab889SNikos Nikoleris 	if (status == EFI_BUFFER_TOO_SMALL) {
246529ab889SNikos Nikoleris 		efi_free_pool(file_info);
247529ab889SNikos Nikoleris 		status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, buffer_size, (void **)&file_info);
248529ab889SNikos Nikoleris 		assert(file_info);
249529ab889SNikos Nikoleris 		status = file->get_info(file, &file_info_guid, &buffer_size, file_info);
250529ab889SNikos Nikoleris 	}
251529ab889SNikos Nikoleris 	assert(status == EFI_SUCCESS);
252529ab889SNikos Nikoleris 
253529ab889SNikos Nikoleris 	buffer_size = file_info->file_size;
254529ab889SNikos Nikoleris 
255529ab889SNikos Nikoleris 	efi_free_pool(file_info);
256529ab889SNikos Nikoleris 
257529ab889SNikos Nikoleris 	status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, buffer_size, (void **)data);
258529ab889SNikos Nikoleris 	assert(*data);
259529ab889SNikos Nikoleris 	/* Perform the actual read */
260529ab889SNikos Nikoleris 	status = file->read(file, &buffer_size, *data);
261529ab889SNikos Nikoleris 	if (status == EFI_BUFFER_TOO_SMALL) {
262529ab889SNikos Nikoleris 		efi_free_pool(*data);
263529ab889SNikos Nikoleris 		status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, buffer_size, (void **)data);
264529ab889SNikos Nikoleris 		status = file->read(file, &buffer_size, *data);
265529ab889SNikos Nikoleris 	}
266529ab889SNikos Nikoleris 	assert(status == EFI_SUCCESS);
267529ab889SNikos Nikoleris 
268529ab889SNikos Nikoleris 	*datasize = buffer_size;
269529ab889SNikos Nikoleris }
270529ab889SNikos Nikoleris 
efi_grow_buffer(efi_status_t * status,void ** buffer,uint64_t buffer_size)271529ab889SNikos Nikoleris static int efi_grow_buffer(efi_status_t *status, void **buffer, uint64_t buffer_size)
272529ab889SNikos Nikoleris {
273529ab889SNikos Nikoleris 	int try_again;
274529ab889SNikos Nikoleris 
275529ab889SNikos Nikoleris 	if (!*buffer && buffer_size) {
276529ab889SNikos Nikoleris 		*status = EFI_BUFFER_TOO_SMALL;
277529ab889SNikos Nikoleris 	}
278529ab889SNikos Nikoleris 
279529ab889SNikos Nikoleris 	try_again = 0;
280529ab889SNikos Nikoleris 	if (*status == EFI_BUFFER_TOO_SMALL) {
281529ab889SNikos Nikoleris 		if (*buffer)
282529ab889SNikos Nikoleris 			efi_free_pool(*buffer);
283529ab889SNikos Nikoleris 
284529ab889SNikos Nikoleris 		efi_bs_call(allocate_pool, EFI_LOADER_DATA, buffer_size, buffer);
285529ab889SNikos Nikoleris 		if (*buffer) {
286529ab889SNikos Nikoleris 			try_again = 1;
287529ab889SNikos Nikoleris 		} else {
288529ab889SNikos Nikoleris 			*status = EFI_OUT_OF_RESOURCES;
289529ab889SNikos Nikoleris 		}
290529ab889SNikos Nikoleris 	}
291529ab889SNikos Nikoleris 
292529ab889SNikos Nikoleris 	if (!try_again && EFI_ERROR(*status) && *buffer) {
293529ab889SNikos Nikoleris 		efi_free_pool(*buffer);
294529ab889SNikos Nikoleris 		*buffer = NULL;
295529ab889SNikos Nikoleris 	}
296529ab889SNikos Nikoleris 
297529ab889SNikos Nikoleris 	return try_again;
298529ab889SNikos Nikoleris }
299529ab889SNikos Nikoleris 
efi_get_var(efi_handle_t handle,struct efi_loaded_image_64 * image,efi_char16_t * var)300529ab889SNikos Nikoleris static void* efi_get_var(efi_handle_t handle, struct efi_loaded_image_64 *image, efi_char16_t *var)
301529ab889SNikos Nikoleris {
302529ab889SNikos Nikoleris 	efi_status_t status = EFI_SUCCESS;
303529ab889SNikos Nikoleris 	void *val = NULL;
304529ab889SNikos Nikoleris 	uint64_t val_size = 100;
305529ab889SNikos Nikoleris 	efi_guid_t efi_var_guid = EFI_VAR_GUID;
306529ab889SNikos Nikoleris 
307529ab889SNikos Nikoleris 	while (efi_grow_buffer(&status, &val, val_size + sizeof(efi_char16_t)))
308529ab889SNikos Nikoleris 		status = efi_rs_call(get_variable, var, &efi_var_guid, NULL, &val_size, val);
309529ab889SNikos Nikoleris 
310529ab889SNikos Nikoleris 	if (val)
311529ab889SNikos Nikoleris 		((efi_char16_t *)val)[val_size / sizeof(efi_char16_t)] = L'\0';
312529ab889SNikos Nikoleris 
313529ab889SNikos Nikoleris 	return val;
314529ab889SNikos Nikoleris }
315529ab889SNikos Nikoleris 
efi_get_fdt(efi_handle_t handle,struct efi_loaded_image_64 * image)316529ab889SNikos Nikoleris static void *efi_get_fdt(efi_handle_t handle, struct efi_loaded_image_64 *image)
317529ab889SNikos Nikoleris {
318529ab889SNikos Nikoleris 	efi_char16_t var[] = ENV_VARNAME_DTBFILE;
319529ab889SNikos Nikoleris 	efi_char16_t *val;
320529ab889SNikos Nikoleris 	void *fdt = NULL;
3219632ce44SAndrew Jones 	int fdtsize = 0;
322529ab889SNikos Nikoleris 
323529ab889SNikos Nikoleris 	val = efi_get_var(handle, image, var);
3249632ce44SAndrew Jones 	if (val) {
325529ab889SNikos Nikoleris 		efi_load_image(handle, image, &fdt, &fdtsize, val);
3269632ce44SAndrew Jones 		if (fdtsize == 0)
3279632ce44SAndrew Jones 			return NULL;
3289632ce44SAndrew Jones 	} else if (efi_get_system_config_table(DEVICE_TREE_GUID, &fdt) != EFI_SUCCESS) {
3299632ce44SAndrew Jones 		return NULL;
3309632ce44SAndrew Jones 	}
331529ab889SNikos Nikoleris 
3329632ce44SAndrew Jones 	return fdt_check_header(fdt) == 0 ? fdt : NULL;
333529ab889SNikos Nikoleris }
334bb294dfdSPavan Kumar Paluri #else
efi_get_fdt(efi_handle_t handle,struct efi_loaded_image_64 * image)335bb294dfdSPavan Kumar Paluri static void *efi_get_fdt(efi_handle_t handle, struct efi_loaded_image_64 *image)
336bb294dfdSPavan Kumar Paluri {
337bb294dfdSPavan Kumar Paluri 	return NULL;
338bb294dfdSPavan Kumar Paluri }
339bb294dfdSPavan Kumar Paluri #endif
340529ab889SNikos Nikoleris 
341b9f8ff7cSAndrew Jones static const struct {
342b9f8ff7cSAndrew Jones 	struct efi_vendor_dev_path	vendor;
343b9f8ff7cSAndrew Jones 	struct efi_generic_dev_path	end;
344b9f8ff7cSAndrew Jones } __packed initrd_dev_path = {
345b9f8ff7cSAndrew Jones 	{
346b9f8ff7cSAndrew Jones 		{
347b9f8ff7cSAndrew Jones 			EFI_DEV_MEDIA,
348b9f8ff7cSAndrew Jones 			EFI_DEV_MEDIA_VENDOR,
349b9f8ff7cSAndrew Jones 			sizeof(struct efi_vendor_dev_path),
350b9f8ff7cSAndrew Jones 		},
351b9f8ff7cSAndrew Jones 		LINUX_EFI_INITRD_MEDIA_GUID
352b9f8ff7cSAndrew Jones 	}, {
353b9f8ff7cSAndrew Jones 		EFI_DEV_END_PATH,
354b9f8ff7cSAndrew Jones 		EFI_DEV_END_ENTIRE,
355b9f8ff7cSAndrew Jones 		sizeof(struct efi_generic_dev_path)
356b9f8ff7cSAndrew Jones 	}
357b9f8ff7cSAndrew Jones };
358b9f8ff7cSAndrew Jones 
efi_load_initrd(void)359b9f8ff7cSAndrew Jones static void efi_load_initrd(void)
360b9f8ff7cSAndrew Jones {
361b9f8ff7cSAndrew Jones 	efi_guid_t lf2_proto_guid = EFI_LOAD_FILE2_PROTOCOL_GUID;
362b9f8ff7cSAndrew Jones 	efi_device_path_protocol_t *dp;
363b9f8ff7cSAndrew Jones 	efi_load_file2_protocol_t *lf2;
364b9f8ff7cSAndrew Jones 	efi_handle_t handle;
365b9f8ff7cSAndrew Jones 	efi_status_t status;
366b9f8ff7cSAndrew Jones 	unsigned long file_size = 0;
367b9f8ff7cSAndrew Jones 
368b9f8ff7cSAndrew Jones 	initrd = NULL;
369b9f8ff7cSAndrew Jones 	initrd_size = 0;
370b9f8ff7cSAndrew Jones 
371b9f8ff7cSAndrew Jones 	dp = (efi_device_path_protocol_t *)&initrd_dev_path;
372b9f8ff7cSAndrew Jones 	status = efi_bs_call(locate_device_path, &lf2_proto_guid, &dp, &handle);
373b9f8ff7cSAndrew Jones 	if (status != EFI_SUCCESS)
374b9f8ff7cSAndrew Jones 		return;
375b9f8ff7cSAndrew Jones 
376b9f8ff7cSAndrew Jones 	status = efi_bs_call(handle_protocol, handle, &lf2_proto_guid, (void **)&lf2);
377b9f8ff7cSAndrew Jones 	assert(status == EFI_SUCCESS);
378b9f8ff7cSAndrew Jones 
379b9f8ff7cSAndrew Jones 	status = efi_call_proto(lf2, load_file, dp, false, &file_size, NULL);
380b9f8ff7cSAndrew Jones 	assert(status == EFI_BUFFER_TOO_SMALL);
381b9f8ff7cSAndrew Jones 
382b9f8ff7cSAndrew Jones 	status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, file_size, (void **)&initrd);
383b9f8ff7cSAndrew Jones 	assert(status == EFI_SUCCESS);
384b9f8ff7cSAndrew Jones 
385b9f8ff7cSAndrew Jones 	status = efi_call_proto(lf2, load_file, dp, false, &file_size, (void *)initrd);
386b9f8ff7cSAndrew Jones 	assert(status == EFI_SUCCESS);
387b9f8ff7cSAndrew Jones 
388b9f8ff7cSAndrew Jones 	initrd_size = (u32)file_size;
389b9f8ff7cSAndrew Jones 
390b9f8ff7cSAndrew Jones 	/*
391b9f8ff7cSAndrew Jones 	 * UEFI appends initrd=initrd to the command line when an initrd is present.
392b9f8ff7cSAndrew Jones 	 * Remove it in order to avoid confusing unit tests.
393b9f8ff7cSAndrew Jones 	 */
394b9f8ff7cSAndrew Jones 	if (!strcmp(__argv[__argc - 1], "initrd=initrd")) {
395b9f8ff7cSAndrew Jones 		__argv[__argc - 1] = NULL;
396b9f8ff7cSAndrew Jones 		__argc -= 1;
397b9f8ff7cSAndrew Jones 	}
398b9f8ff7cSAndrew Jones }
399b9f8ff7cSAndrew Jones 
efi_main(efi_handle_t handle,efi_system_table_t * sys_tab)400ad5fb883SZixuan Wang efi_status_t efi_main(efi_handle_t handle, efi_system_table_t *sys_tab)
4010eaa5176SVarad Gautam {
402ad5fb883SZixuan Wang 	int ret;
4031ae9072eSZixuan Wang 	efi_status_t status;
4041ae9072eSZixuan Wang 	efi_bootinfo_t efi_bootinfo;
405ad5fb883SZixuan Wang 
4060eaa5176SVarad Gautam 	efi_system_table = sys_tab;
4070eaa5176SVarad Gautam 
408b4e8c300SZixuan Wang 	/* Memory map struct values */
409b365a5b0SPavan Kumar Paluri 	efi_memory_desc_t *map;
410b365a5b0SPavan Kumar Paluri 	unsigned long map_size, desc_size, key, buff_size;
411b4e8c300SZixuan Wang 	u32 desc_ver;
412b4e8c300SZixuan Wang 
41385c3c524SNikos Nikoleris 	/* Helper variables needed to get the cmdline */
41485c3c524SNikos Nikoleris 	struct efi_loaded_image_64 *image;
41585c3c524SNikos Nikoleris 	efi_guid_t loaded_image_proto = LOADED_IMAGE_PROTOCOL_GUID;
41685c3c524SNikos Nikoleris 	char *cmdline_ptr = NULL;
41785c3c524SNikos Nikoleris 	int cmdline_size = 0;
41885c3c524SNikos Nikoleris 
41985c3c524SNikos Nikoleris 	/*
42085c3c524SNikos Nikoleris 	 * Get a handle to the loaded image protocol.  This is used to get
42185c3c524SNikos Nikoleris 	 * information about the running image, such as size and the command
42285c3c524SNikos Nikoleris 	 * line.
42385c3c524SNikos Nikoleris 	 */
42485c3c524SNikos Nikoleris 	status = efi_bs_call(handle_protocol, handle, &loaded_image_proto, (void *)&image);
42585c3c524SNikos Nikoleris 	if (status != EFI_SUCCESS) {
42685c3c524SNikos Nikoleris 		printf("Failed to get loaded image protocol\n");
42785c3c524SNikos Nikoleris 		goto efi_main_error;
42885c3c524SNikos Nikoleris 	}
42985c3c524SNikos Nikoleris 
43085c3c524SNikos Nikoleris 	cmdline_ptr = efi_convert_cmdline(image, &cmdline_size);
43185c3c524SNikos Nikoleris 	if (!cmdline_ptr) {
43285c3c524SNikos Nikoleris 		printf("getting command line via LOADED_IMAGE_PROTOCOL\n");
43385c3c524SNikos Nikoleris 		status = EFI_OUT_OF_RESOURCES;
43485c3c524SNikos Nikoleris 		goto efi_main_error;
43585c3c524SNikos Nikoleris 	}
43685c3c524SNikos Nikoleris 	setup_args(cmdline_ptr);
43785c3c524SNikos Nikoleris 
438b9f8ff7cSAndrew Jones 	efi_load_initrd();
439b9f8ff7cSAndrew Jones 
440529ab889SNikos Nikoleris 	efi_bootinfo.fdt = efi_get_fdt(handle, image);
441b4e8c300SZixuan Wang 	/* Set up efi_bootinfo */
442b4e8c300SZixuan Wang 	efi_bootinfo.mem_map.map = &map;
443b4e8c300SZixuan Wang 	efi_bootinfo.mem_map.map_size = &map_size;
444b4e8c300SZixuan Wang 	efi_bootinfo.mem_map.desc_size = &desc_size;
445b4e8c300SZixuan Wang 	efi_bootinfo.mem_map.desc_ver = &desc_ver;
446b4e8c300SZixuan Wang 	efi_bootinfo.mem_map.key_ptr = &key;
447b4e8c300SZixuan Wang 	efi_bootinfo.mem_map.buff_size = &buff_size;
448b4e8c300SZixuan Wang 
449ba0a6fd4SAndrew Jones #ifdef __riscv
450ba0a6fd4SAndrew Jones 	status = efi_get_boot_hartid();
451ba0a6fd4SAndrew Jones 	if (status != EFI_SUCCESS) {
452ba0a6fd4SAndrew Jones 		printf("Failed to get boot haritd\n");
453ba0a6fd4SAndrew Jones 		goto efi_main_error;
454ba0a6fd4SAndrew Jones 	}
455ba0a6fd4SAndrew Jones #endif
456ba0a6fd4SAndrew Jones 
457b365a5b0SPavan Kumar Paluri 	status = EFI_INVALID_PARAMETER;
458b365a5b0SPavan Kumar Paluri 	while (status == EFI_INVALID_PARAMETER) {
459b365a5b0SPavan Kumar Paluri 		status = efi_get_memory_map(&efi_bootinfo.mem_map);
460b365a5b0SPavan Kumar Paluri 		if (status != EFI_SUCCESS) {
461b365a5b0SPavan Kumar Paluri 			printf("Failed to get memory map\n");
462b365a5b0SPavan Kumar Paluri 			goto efi_main_error;
463b365a5b0SPavan Kumar Paluri 		}
464b4e8c300SZixuan Wang 		/*
465b365a5b0SPavan Kumar Paluri 		 * Exit EFI boot services, let kvm-unit-tests take full
466b365a5b0SPavan Kumar Paluri 		 * control of the guest.
467b4e8c300SZixuan Wang 		 */
468b4e8c300SZixuan Wang 		status = efi_exit_boot_services(handle, &efi_bootinfo.mem_map);
469b365a5b0SPavan Kumar Paluri 		if (status == EFI_INVALID_PARAMETER)
470b365a5b0SPavan Kumar Paluri 			efi_free_pool(*efi_bootinfo.mem_map.map);
471b365a5b0SPavan Kumar Paluri 	}
472b365a5b0SPavan Kumar Paluri 
4731ae9072eSZixuan Wang 	if (status != EFI_SUCCESS) {
4741ae9072eSZixuan Wang 		printf("Failed to exit boot services\n");
475b4e8c300SZixuan Wang 		goto efi_main_error;
4761ae9072eSZixuan Wang 	}
4771ae9072eSZixuan Wang 
478b4e8c300SZixuan Wang 	/* Set up arch-specific resources */
479b4e8c300SZixuan Wang 	status = setup_efi(&efi_bootinfo);
480b4e8c300SZixuan Wang 	if (status != EFI_SUCCESS) {
481b4e8c300SZixuan Wang 		printf("Failed to set up arch-specific resources\n");
482b4e8c300SZixuan Wang 		goto efi_main_error;
483b4e8c300SZixuan Wang 	}
484b4e8c300SZixuan Wang 
48525ef5154SNadav Amit 	printf("Address of image is: 0x%lx\n", (unsigned long)&_text);
48625ef5154SNadav Amit 
487b4e8c300SZixuan Wang 	/* Run the test case */
488ad5fb883SZixuan Wang 	ret = main(__argc, __argv, __environ);
489ad5fb883SZixuan Wang 
4902f47d025SZixuan Wang 	/* Shutdown the guest VM */
4912f47d025SZixuan Wang 	efi_exit(ret);
492ad5fb883SZixuan Wang 
493ad5fb883SZixuan Wang 	/* Unreachable */
494ad5fb883SZixuan Wang 	return EFI_UNSUPPORTED;
495b4e8c300SZixuan Wang 
496b4e8c300SZixuan Wang efi_main_error:
497b4e8c300SZixuan Wang 	/* Shutdown the guest with error EFI status */
4982f47d025SZixuan Wang 	efi_exit(status);
499b4e8c300SZixuan Wang 
500b4e8c300SZixuan Wang 	/* Unreachable */
501b4e8c300SZixuan Wang 	return EFI_UNSUPPORTED;
5020eaa5176SVarad Gautam }
503