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