blob: 64cc9789274ecdaac9ed7722475d2bf3a46e67ff [file] [log] [blame]
/*
* EFI-related functions to set up and run test cases in EFI
*
* Copyright (c) 2021, SUSE, Varad Gautam <varad.gautam@suse.com>
* Copyright (c) 2021, Google Inc, Zixuan Wang <zixuanwang@google.com>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "efi.h"
#include <libcflat.h>
#include <asm/setup.h>
/* From lib/argv.c */
extern int __argc, __envc;
extern char *__argv[100];
extern char *__environ[200];
extern int main(int argc, char **argv, char **envp);
efi_system_table_t *efi_system_table = NULL;
static void efi_free_pool(void *ptr)
{
efi_bs_call(free_pool, ptr);
}
efi_status_t efi_get_memory_map(struct efi_boot_memmap *map)
{
efi_memory_desc_t *m = NULL;
efi_status_t status;
unsigned long key = 0, map_size = 0, desc_size = 0;
u32 desc_ver;
status = efi_bs_call(get_memory_map, &map_size,
NULL, &key, &desc_size, &desc_ver);
if (status != EFI_BUFFER_TOO_SMALL || map_size == 0)
goto out;
/*
* Pad map_size with additional descriptors so we don't need to
* retry.
*/
map_size += 4 * desc_size;
*map->buff_size = map_size;
status = efi_bs_call(allocate_pool, EFI_LOADER_DATA,
map_size, (void **)&m);
if (status != EFI_SUCCESS)
goto out;
/* Get the map. */
status = efi_bs_call(get_memory_map, &map_size,
m, &key, &desc_size, &desc_ver);
if (status != EFI_SUCCESS) {
efi_free_pool(m);
goto out;
}
*map->desc_ver = desc_ver;
*map->desc_size = desc_size;
*map->map_size = map_size;
*map->key_ptr = key;
out:
*map->map = m;
return status;
}
efi_status_t efi_exit_boot_services(void *handle, struct efi_boot_memmap *map)
{
return efi_bs_call(exit_boot_services, handle, *map->key_ptr);
}
efi_status_t efi_get_system_config_table(efi_guid_t table_guid, void **table)
{
size_t i;
efi_config_table_t *tables;
tables = (efi_config_table_t *)efi_system_table->tables;
for (i = 0; i < efi_system_table->nr_tables; i++) {
if (!memcmp(&table_guid, &tables[i].guid, sizeof(efi_guid_t))) {
*table = tables[i].table;
return EFI_SUCCESS;
}
}
return EFI_NOT_FOUND;
}
static void efi_exit(efi_status_t code)
{
exit(code);
/*
* Fallback to UEFI reset_system() service, in case testdev is
* missing and exit() does not properly exit.
*/
efi_rs_call(reset_system, EFI_RESET_SHUTDOWN, code, 0, NULL);
}
efi_status_t efi_main(efi_handle_t handle, efi_system_table_t *sys_tab)
{
int ret;
efi_status_t status;
efi_bootinfo_t efi_bootinfo;
efi_system_table = sys_tab;
/* Memory map struct values */
efi_memory_desc_t *map = NULL;
unsigned long map_size = 0, desc_size = 0, key = 0, buff_size = 0;
u32 desc_ver;
/* Set up efi_bootinfo */
efi_bootinfo.mem_map.map = &map;
efi_bootinfo.mem_map.map_size = &map_size;
efi_bootinfo.mem_map.desc_size = &desc_size;
efi_bootinfo.mem_map.desc_ver = &desc_ver;
efi_bootinfo.mem_map.key_ptr = &key;
efi_bootinfo.mem_map.buff_size = &buff_size;
/* Get EFI memory map */
status = efi_get_memory_map(&efi_bootinfo.mem_map);
if (status != EFI_SUCCESS) {
printf("Failed to get memory map\n");
goto efi_main_error;
}
/*
* Exit EFI boot services, let kvm-unit-tests take full control of the
* guest
*/
status = efi_exit_boot_services(handle, &efi_bootinfo.mem_map);
if (status != EFI_SUCCESS) {
printf("Failed to exit boot services\n");
goto efi_main_error;
}
/* Set up arch-specific resources */
status = setup_efi(&efi_bootinfo);
if (status != EFI_SUCCESS) {
printf("Failed to set up arch-specific resources\n");
goto efi_main_error;
}
/* Run the test case */
ret = main(__argc, __argv, __environ);
/* Shutdown the guest VM */
efi_exit(ret);
/* Unreachable */
return EFI_UNSUPPORTED;
efi_main_error:
/* Shutdown the guest with error EFI status */
efi_exit(status);
/* Unreachable */
return EFI_UNSUPPORTED;
}