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