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