xref: /kvm-unit-tests/lib/efi.c (revision 2c96b77ec9d3b1fcec7525174e23a6240ee05949)
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 
10 #include "efi.h"
11 #include <libcflat.h>
12 #include <asm/setup.h>
13 
14 /* From lib/argv.c */
15 extern int __argc, __envc;
16 extern char *__argv[100];
17 extern char *__environ[200];
18 
19 extern int main(int argc, char **argv, char **envp);
20 
21 efi_system_table_t *efi_system_table = NULL;
22 
23 static void efi_free_pool(void *ptr)
24 {
25 	efi_bs_call(free_pool, ptr);
26 }
27 
28 efi_status_t efi_get_memory_map(struct efi_boot_memmap *map)
29 {
30 	efi_memory_desc_t *m = NULL;
31 	efi_status_t status;
32 	unsigned long key = 0, map_size = 0, desc_size = 0;
33 	u32 desc_ver;
34 
35 	status = efi_bs_call(get_memory_map, &map_size,
36 			     NULL, &key, &desc_size, &desc_ver);
37 	if (status != EFI_BUFFER_TOO_SMALL || map_size == 0)
38 		goto out;
39 
40 	/*
41 	 * Pad map_size with additional descriptors so we don't need to
42 	 * retry.
43 	 */
44 	map_size += 4 * desc_size;
45 	*map->buff_size = map_size;
46 	status = efi_bs_call(allocate_pool, EFI_LOADER_DATA,
47 			     map_size, (void **)&m);
48 	if (status != EFI_SUCCESS)
49 		goto out;
50 
51 	/* Get the map. */
52 	status = efi_bs_call(get_memory_map, &map_size,
53 			     m, &key, &desc_size, &desc_ver);
54 	if (status != EFI_SUCCESS) {
55 		efi_free_pool(m);
56 		goto out;
57 	}
58 
59 	*map->desc_ver = desc_ver;
60 	*map->desc_size = desc_size;
61 	*map->map_size = map_size;
62 	*map->key_ptr = key;
63 out:
64 	*map->map = m;
65 	return status;
66 }
67 
68 efi_status_t efi_exit_boot_services(void *handle, struct efi_boot_memmap *map)
69 {
70 	return efi_bs_call(exit_boot_services, handle, *map->key_ptr);
71 }
72 
73 efi_status_t efi_get_system_config_table(efi_guid_t table_guid, void **table)
74 {
75 	size_t i;
76 	efi_config_table_t *tables;
77 
78 	tables = (efi_config_table_t *)efi_system_table->tables;
79 	for (i = 0; i < efi_system_table->nr_tables; i++) {
80 		if (!memcmp(&table_guid, &tables[i].guid, sizeof(efi_guid_t))) {
81 			*table = tables[i].table;
82 			return EFI_SUCCESS;
83 		}
84 	}
85 	return EFI_NOT_FOUND;
86 }
87 
88 static void efi_exit(efi_status_t code)
89 {
90 	exit(code);
91 
92 	/*
93 	 * Fallback to UEFI reset_system() service, in case testdev is
94 	 * missing and exit() does not properly exit.
95 	 */
96 	efi_rs_call(reset_system, EFI_RESET_SHUTDOWN, code, 0, NULL);
97 }
98 
99 efi_status_t efi_main(efi_handle_t handle, efi_system_table_t *sys_tab)
100 {
101 	int ret;
102 	efi_status_t status;
103 	efi_bootinfo_t efi_bootinfo;
104 
105 	efi_system_table = sys_tab;
106 
107 	/* Memory map struct values */
108 	efi_memory_desc_t *map = NULL;
109 	unsigned long map_size = 0, desc_size = 0, key = 0, buff_size = 0;
110 	u32 desc_ver;
111 
112 	/* Set up efi_bootinfo */
113 	efi_bootinfo.mem_map.map = &map;
114 	efi_bootinfo.mem_map.map_size = &map_size;
115 	efi_bootinfo.mem_map.desc_size = &desc_size;
116 	efi_bootinfo.mem_map.desc_ver = &desc_ver;
117 	efi_bootinfo.mem_map.key_ptr = &key;
118 	efi_bootinfo.mem_map.buff_size = &buff_size;
119 
120 	/* Get EFI memory map */
121 	status = efi_get_memory_map(&efi_bootinfo.mem_map);
122 	if (status != EFI_SUCCESS) {
123 		printf("Failed to get memory map\n");
124 		goto efi_main_error;
125 	}
126 
127 	/*
128 	 * Exit EFI boot services, let kvm-unit-tests take full control of the
129 	 * guest
130 	 */
131 	status = efi_exit_boot_services(handle, &efi_bootinfo.mem_map);
132 	if (status != EFI_SUCCESS) {
133 		printf("Failed to exit boot services\n");
134 		goto efi_main_error;
135 	}
136 
137 	/* Set up arch-specific resources */
138 	status = setup_efi(&efi_bootinfo);
139 	if (status != EFI_SUCCESS) {
140 		printf("Failed to set up arch-specific resources\n");
141 		goto efi_main_error;
142 	}
143 
144 	/* Run the test case */
145 	ret = main(__argc, __argv, __environ);
146 
147 	/* Shutdown the guest VM */
148 	efi_exit(ret);
149 
150 	/* Unreachable */
151 	return EFI_UNSUPPORTED;
152 
153 efi_main_error:
154 	/* Shutdown the guest with error EFI status */
155 	efi_exit(status);
156 
157 	/* Unreachable */
158 	return EFI_UNSUPPORTED;
159 }
160