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 */ 90eaa5176SVarad Gautam 10ad5fb883SZixuan Wang #include "efi.h" 11*85c3c524SNikos Nikoleris #include <argv.h> 12*85c3c524SNikos Nikoleris #include <stdlib.h> 13*85c3c524SNikos Nikoleris #include <ctype.h> 14ad5fb883SZixuan Wang #include <libcflat.h> 15ad5fb883SZixuan Wang #include <asm/setup.h> 16ad5fb883SZixuan Wang 17ad5fb883SZixuan Wang /* From lib/argv.c */ 18ad5fb883SZixuan Wang extern int __argc, __envc; 19ad5fb883SZixuan Wang extern char *__argv[100]; 20ad5fb883SZixuan Wang extern char *__environ[200]; 21ad5fb883SZixuan Wang 22ad5fb883SZixuan Wang extern int main(int argc, char **argv, char **envp); 23ad5fb883SZixuan Wang 240eaa5176SVarad Gautam efi_system_table_t *efi_system_table = NULL; 250eaa5176SVarad Gautam 260eaa5176SVarad Gautam static void efi_free_pool(void *ptr) 270eaa5176SVarad Gautam { 280eaa5176SVarad Gautam efi_bs_call(free_pool, ptr); 290eaa5176SVarad Gautam } 300eaa5176SVarad Gautam 31ad5fb883SZixuan Wang efi_status_t efi_get_memory_map(struct efi_boot_memmap *map) 320eaa5176SVarad Gautam { 330eaa5176SVarad Gautam efi_memory_desc_t *m = NULL; 340eaa5176SVarad Gautam efi_status_t status; 350eaa5176SVarad Gautam unsigned long key = 0, map_size = 0, desc_size = 0; 361ae9072eSZixuan Wang u32 desc_ver; 370eaa5176SVarad Gautam 380eaa5176SVarad Gautam status = efi_bs_call(get_memory_map, &map_size, 391ae9072eSZixuan Wang NULL, &key, &desc_size, &desc_ver); 400eaa5176SVarad Gautam if (status != EFI_BUFFER_TOO_SMALL || map_size == 0) 410eaa5176SVarad Gautam goto out; 420eaa5176SVarad Gautam 430eaa5176SVarad Gautam /* 440eaa5176SVarad Gautam * Pad map_size with additional descriptors so we don't need to 450eaa5176SVarad Gautam * retry. 460eaa5176SVarad Gautam */ 470eaa5176SVarad Gautam map_size += 4 * desc_size; 480eaa5176SVarad Gautam *map->buff_size = map_size; 490eaa5176SVarad Gautam status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, 500eaa5176SVarad Gautam map_size, (void **)&m); 510eaa5176SVarad Gautam if (status != EFI_SUCCESS) 520eaa5176SVarad Gautam goto out; 530eaa5176SVarad Gautam 540eaa5176SVarad Gautam /* Get the map. */ 550eaa5176SVarad Gautam status = efi_bs_call(get_memory_map, &map_size, 561ae9072eSZixuan Wang m, &key, &desc_size, &desc_ver); 570eaa5176SVarad Gautam if (status != EFI_SUCCESS) { 580eaa5176SVarad Gautam efi_free_pool(m); 590eaa5176SVarad Gautam goto out; 600eaa5176SVarad Gautam } 610eaa5176SVarad Gautam 621ae9072eSZixuan Wang *map->desc_ver = desc_ver; 630eaa5176SVarad Gautam *map->desc_size = desc_size; 640eaa5176SVarad Gautam *map->map_size = map_size; 650eaa5176SVarad Gautam *map->key_ptr = key; 660eaa5176SVarad Gautam out: 670eaa5176SVarad Gautam *map->map = m; 680eaa5176SVarad Gautam return status; 690eaa5176SVarad Gautam } 700eaa5176SVarad Gautam 71b4e8c300SZixuan Wang efi_status_t efi_exit_boot_services(void *handle, struct efi_boot_memmap *map) 720eaa5176SVarad Gautam { 73b4e8c300SZixuan Wang return efi_bs_call(exit_boot_services, handle, *map->key_ptr); 740eaa5176SVarad Gautam } 750eaa5176SVarad Gautam 76f20589d6SZixuan Wang efi_status_t efi_get_system_config_table(efi_guid_t table_guid, void **table) 77f20589d6SZixuan Wang { 78f20589d6SZixuan Wang size_t i; 79f20589d6SZixuan Wang efi_config_table_t *tables; 80f20589d6SZixuan Wang 81f20589d6SZixuan Wang tables = (efi_config_table_t *)efi_system_table->tables; 82f20589d6SZixuan Wang for (i = 0; i < efi_system_table->nr_tables; i++) { 83f20589d6SZixuan Wang if (!memcmp(&table_guid, &tables[i].guid, sizeof(efi_guid_t))) { 84f20589d6SZixuan Wang *table = tables[i].table; 85f20589d6SZixuan Wang return EFI_SUCCESS; 86f20589d6SZixuan Wang } 87f20589d6SZixuan Wang } 88f20589d6SZixuan Wang return EFI_NOT_FOUND; 89f20589d6SZixuan Wang } 90f20589d6SZixuan Wang 912f47d025SZixuan Wang static void efi_exit(efi_status_t code) 922f47d025SZixuan Wang { 932f47d025SZixuan Wang exit(code); 942f47d025SZixuan Wang 952f47d025SZixuan Wang /* 962f47d025SZixuan Wang * Fallback to UEFI reset_system() service, in case testdev is 972f47d025SZixuan Wang * missing and exit() does not properly exit. 982f47d025SZixuan Wang */ 992f47d025SZixuan Wang efi_rs_call(reset_system, EFI_RESET_SHUTDOWN, code, 0, NULL); 1002f47d025SZixuan Wang } 1012f47d025SZixuan Wang 102*85c3c524SNikos Nikoleris /* Adapted from drivers/firmware/efi/libstub/efi-stub.c */ 103*85c3c524SNikos Nikoleris static char *efi_convert_cmdline(struct efi_loaded_image_64 *image, int *cmd_line_len) 104*85c3c524SNikos Nikoleris { 105*85c3c524SNikos Nikoleris const u16 *s2; 106*85c3c524SNikos Nikoleris unsigned long cmdline_addr = 0; 107*85c3c524SNikos Nikoleris int options_chars = image->load_options_size; 108*85c3c524SNikos Nikoleris const u16 *options = image->load_options; 109*85c3c524SNikos Nikoleris int options_bytes = 0, safe_options_bytes = 0; /* UTF-8 bytes */ 110*85c3c524SNikos Nikoleris bool in_quote = false; 111*85c3c524SNikos Nikoleris efi_status_t status; 112*85c3c524SNikos Nikoleris const int COMMAND_LINE_SIZE = 2048; 113*85c3c524SNikos Nikoleris 114*85c3c524SNikos Nikoleris if (options) { 115*85c3c524SNikos Nikoleris s2 = options; 116*85c3c524SNikos Nikoleris while (options_bytes < COMMAND_LINE_SIZE && options_chars--) { 117*85c3c524SNikos Nikoleris u16 c = *s2++; 118*85c3c524SNikos Nikoleris 119*85c3c524SNikos Nikoleris if (c < 0x80) { 120*85c3c524SNikos Nikoleris if (c == L'\0' || c == L'\n') 121*85c3c524SNikos Nikoleris break; 122*85c3c524SNikos Nikoleris if (c == L'"') 123*85c3c524SNikos Nikoleris in_quote = !in_quote; 124*85c3c524SNikos Nikoleris else if (!in_quote && isspace((char)c)) 125*85c3c524SNikos Nikoleris safe_options_bytes = options_bytes; 126*85c3c524SNikos Nikoleris 127*85c3c524SNikos Nikoleris options_bytes++; 128*85c3c524SNikos Nikoleris continue; 129*85c3c524SNikos Nikoleris } 130*85c3c524SNikos Nikoleris 131*85c3c524SNikos Nikoleris /* 132*85c3c524SNikos Nikoleris * Get the number of UTF-8 bytes corresponding to a 133*85c3c524SNikos Nikoleris * UTF-16 character. 134*85c3c524SNikos Nikoleris * The first part handles everything in the BMP. 135*85c3c524SNikos Nikoleris */ 136*85c3c524SNikos Nikoleris options_bytes += 2 + (c >= 0x800); 137*85c3c524SNikos Nikoleris /* 138*85c3c524SNikos Nikoleris * Add one more byte for valid surrogate pairs. Invalid 139*85c3c524SNikos Nikoleris * surrogates will be replaced with 0xfffd and take up 140*85c3c524SNikos Nikoleris * only 3 bytes. 141*85c3c524SNikos Nikoleris */ 142*85c3c524SNikos Nikoleris if ((c & 0xfc00) == 0xd800) { 143*85c3c524SNikos Nikoleris /* 144*85c3c524SNikos Nikoleris * If the very last word is a high surrogate, 145*85c3c524SNikos Nikoleris * we must ignore it since we can't access the 146*85c3c524SNikos Nikoleris * low surrogate. 147*85c3c524SNikos Nikoleris */ 148*85c3c524SNikos Nikoleris if (!options_chars) { 149*85c3c524SNikos Nikoleris options_bytes -= 3; 150*85c3c524SNikos Nikoleris } else if ((*s2 & 0xfc00) == 0xdc00) { 151*85c3c524SNikos Nikoleris options_bytes++; 152*85c3c524SNikos Nikoleris options_chars--; 153*85c3c524SNikos Nikoleris s2++; 154*85c3c524SNikos Nikoleris } 155*85c3c524SNikos Nikoleris } 156*85c3c524SNikos Nikoleris } 157*85c3c524SNikos Nikoleris if (options_bytes >= COMMAND_LINE_SIZE) { 158*85c3c524SNikos Nikoleris options_bytes = safe_options_bytes; 159*85c3c524SNikos Nikoleris printf("Command line is too long: truncated to %d bytes\n", 160*85c3c524SNikos Nikoleris options_bytes); 161*85c3c524SNikos Nikoleris } 162*85c3c524SNikos Nikoleris } 163*85c3c524SNikos Nikoleris 164*85c3c524SNikos Nikoleris options_bytes++; /* NUL termination */ 165*85c3c524SNikos Nikoleris 166*85c3c524SNikos Nikoleris status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, options_bytes, (void **)&cmdline_addr); 167*85c3c524SNikos Nikoleris if (status != EFI_SUCCESS) 168*85c3c524SNikos Nikoleris return NULL; 169*85c3c524SNikos Nikoleris 170*85c3c524SNikos Nikoleris snprintf((char *)cmdline_addr, options_bytes, "%.*ls", options_bytes - 1, options); 171*85c3c524SNikos Nikoleris 172*85c3c524SNikos Nikoleris *cmd_line_len = options_bytes; 173*85c3c524SNikos Nikoleris return (char *)cmdline_addr; 174*85c3c524SNikos Nikoleris } 175*85c3c524SNikos Nikoleris 176ad5fb883SZixuan Wang efi_status_t efi_main(efi_handle_t handle, efi_system_table_t *sys_tab) 1770eaa5176SVarad Gautam { 178ad5fb883SZixuan Wang int ret; 1791ae9072eSZixuan Wang efi_status_t status; 1801ae9072eSZixuan Wang efi_bootinfo_t efi_bootinfo; 181ad5fb883SZixuan Wang 1820eaa5176SVarad Gautam efi_system_table = sys_tab; 1830eaa5176SVarad Gautam 184b4e8c300SZixuan Wang /* Memory map struct values */ 185b4e8c300SZixuan Wang efi_memory_desc_t *map = NULL; 186b4e8c300SZixuan Wang unsigned long map_size = 0, desc_size = 0, key = 0, buff_size = 0; 187b4e8c300SZixuan Wang u32 desc_ver; 188b4e8c300SZixuan Wang 189*85c3c524SNikos Nikoleris /* Helper variables needed to get the cmdline */ 190*85c3c524SNikos Nikoleris struct efi_loaded_image_64 *image; 191*85c3c524SNikos Nikoleris efi_guid_t loaded_image_proto = LOADED_IMAGE_PROTOCOL_GUID; 192*85c3c524SNikos Nikoleris char *cmdline_ptr = NULL; 193*85c3c524SNikos Nikoleris int cmdline_size = 0; 194*85c3c524SNikos Nikoleris 195*85c3c524SNikos Nikoleris /* 196*85c3c524SNikos Nikoleris * Get a handle to the loaded image protocol. This is used to get 197*85c3c524SNikos Nikoleris * information about the running image, such as size and the command 198*85c3c524SNikos Nikoleris * line. 199*85c3c524SNikos Nikoleris */ 200*85c3c524SNikos Nikoleris status = efi_bs_call(handle_protocol, handle, &loaded_image_proto, (void *)&image); 201*85c3c524SNikos Nikoleris if (status != EFI_SUCCESS) { 202*85c3c524SNikos Nikoleris printf("Failed to get loaded image protocol\n"); 203*85c3c524SNikos Nikoleris goto efi_main_error; 204*85c3c524SNikos Nikoleris } 205*85c3c524SNikos Nikoleris 206*85c3c524SNikos Nikoleris cmdline_ptr = efi_convert_cmdline(image, &cmdline_size); 207*85c3c524SNikos Nikoleris if (!cmdline_ptr) { 208*85c3c524SNikos Nikoleris printf("getting command line via LOADED_IMAGE_PROTOCOL\n"); 209*85c3c524SNikos Nikoleris status = EFI_OUT_OF_RESOURCES; 210*85c3c524SNikos Nikoleris goto efi_main_error; 211*85c3c524SNikos Nikoleris } 212*85c3c524SNikos Nikoleris setup_args(cmdline_ptr); 213*85c3c524SNikos Nikoleris 214b4e8c300SZixuan Wang /* Set up efi_bootinfo */ 215b4e8c300SZixuan Wang efi_bootinfo.mem_map.map = ↦ 216b4e8c300SZixuan Wang efi_bootinfo.mem_map.map_size = &map_size; 217b4e8c300SZixuan Wang efi_bootinfo.mem_map.desc_size = &desc_size; 218b4e8c300SZixuan Wang efi_bootinfo.mem_map.desc_ver = &desc_ver; 219b4e8c300SZixuan Wang efi_bootinfo.mem_map.key_ptr = &key; 220b4e8c300SZixuan Wang efi_bootinfo.mem_map.buff_size = &buff_size; 221b4e8c300SZixuan Wang 222b4e8c300SZixuan Wang /* Get EFI memory map */ 223b4e8c300SZixuan Wang status = efi_get_memory_map(&efi_bootinfo.mem_map); 2241ae9072eSZixuan Wang if (status != EFI_SUCCESS) { 225b4e8c300SZixuan Wang printf("Failed to get memory map\n"); 226b4e8c300SZixuan Wang goto efi_main_error; 2271ae9072eSZixuan Wang } 2281ae9072eSZixuan Wang 229b4e8c300SZixuan Wang /* 230b4e8c300SZixuan Wang * Exit EFI boot services, let kvm-unit-tests take full control of the 231b4e8c300SZixuan Wang * guest 232b4e8c300SZixuan Wang */ 233b4e8c300SZixuan Wang status = efi_exit_boot_services(handle, &efi_bootinfo.mem_map); 2341ae9072eSZixuan Wang if (status != EFI_SUCCESS) { 2351ae9072eSZixuan Wang printf("Failed to exit boot services\n"); 236b4e8c300SZixuan Wang goto efi_main_error; 2371ae9072eSZixuan Wang } 2381ae9072eSZixuan Wang 239b4e8c300SZixuan Wang /* Set up arch-specific resources */ 240b4e8c300SZixuan Wang status = setup_efi(&efi_bootinfo); 241b4e8c300SZixuan Wang if (status != EFI_SUCCESS) { 242b4e8c300SZixuan Wang printf("Failed to set up arch-specific resources\n"); 243b4e8c300SZixuan Wang goto efi_main_error; 244b4e8c300SZixuan Wang } 245b4e8c300SZixuan Wang 246b4e8c300SZixuan Wang /* Run the test case */ 247ad5fb883SZixuan Wang ret = main(__argc, __argv, __environ); 248ad5fb883SZixuan Wang 2492f47d025SZixuan Wang /* Shutdown the guest VM */ 2502f47d025SZixuan Wang efi_exit(ret); 251ad5fb883SZixuan Wang 252ad5fb883SZixuan Wang /* Unreachable */ 253ad5fb883SZixuan Wang return EFI_UNSUPPORTED; 254b4e8c300SZixuan Wang 255b4e8c300SZixuan Wang efi_main_error: 256b4e8c300SZixuan Wang /* Shutdown the guest with error EFI status */ 2572f47d025SZixuan Wang efi_exit(status); 258b4e8c300SZixuan Wang 259b4e8c300SZixuan Wang /* Unreachable */ 260b4e8c300SZixuan Wang return EFI_UNSUPPORTED; 2610eaa5176SVarad Gautam } 262