diff options
Diffstat (limited to 'cmd/bootefi.c')
-rw-r--r-- | cmd/bootefi.c | 252 |
1 files changed, 252 insertions, 0 deletions
diff --git a/cmd/bootefi.c b/cmd/bootefi.c new file mode 100644 index 00000000000..8e8752127ed --- /dev/null +++ b/cmd/bootefi.c @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * EFI application loader + * + * Copyright (c) 2016 Alexander Graf + */ + +#define LOG_CATEGORY LOGC_EFI + +#include <command.h> +#include <efi.h> +#include <efi_device_path.h> +#include <efi_loader.h> +#include <exports.h> +#include <log.h> +#include <malloc.h> +#include <mapmem.h> +#include <vsprintf.h> +#include <asm-generic/sections.h> +#include <asm/global_data.h> +#include <linux/string.h> + +DECLARE_GLOBAL_DATA_PTR; + +static struct efi_device_path *test_image_path; +static struct efi_device_path *test_device_path; + +static efi_status_t bootefi_run_prepare(const char *load_options_path, + struct efi_device_path *device_path, + struct efi_device_path *image_path, + struct efi_loaded_image_obj **image_objp, + struct efi_loaded_image **loaded_image_infop) +{ + efi_status_t ret; + u16 *load_options; + + ret = efi_setup_loaded_image(device_path, image_path, image_objp, + loaded_image_infop); + if (ret != EFI_SUCCESS) + return ret; + + (*image_objp)->auth_status = EFI_IMAGE_AUTH_PASSED; + (*image_objp)->entry = efi_selftest; + + /* Transfer environment variable as load options */ + return efi_env_set_load_options((efi_handle_t)*image_objp, + load_options_path, + &load_options); +} + +/** + * bootefi_test_prepare() - prepare to run an EFI test + * + * Prepare to run a test as if it were provided by a loaded image. + * + * @image_objp: pointer to be set to the loaded image handle + * @loaded_image_infop: pointer to be set to the loaded image protocol + * @path: dummy file path used to construct the device path + * set in the loaded image protocol + * @load_options_path: name of a U-Boot environment variable. Its value is + * set as load options in the loaded image protocol. + * Return: status code + */ +static efi_status_t bootefi_test_prepare + (struct efi_loaded_image_obj **image_objp, + struct efi_loaded_image **loaded_image_infop, const char *path, + const char *load_options_path) +{ + efi_status_t ret; + + /* Construct a dummy device path */ + test_device_path = efi_dp_from_mem(EFI_RESERVED_MEMORY_TYPE, 0, 0); + if (!test_device_path) + return EFI_OUT_OF_RESOURCES; + + test_image_path = efi_dp_from_file(NULL, path); + if (!test_image_path) { + ret = EFI_OUT_OF_RESOURCES; + goto failure; + } + + ret = bootefi_run_prepare(load_options_path, test_device_path, + test_image_path, image_objp, + loaded_image_infop); + if (ret == EFI_SUCCESS) + return ret; + +failure: + efi_free_pool(test_device_path); + efi_free_pool(test_image_path); + /* TODO: not sure calling clear function is necessary */ + efi_clear_bootdev(); + return ret; +} + +/** + * do_efi_selftest() - execute EFI selftest + * + * Return: status code + */ +static int do_efi_selftest(void) +{ + struct efi_loaded_image_obj *image_obj; + struct efi_loaded_image *loaded_image_info; + efi_status_t ret; + + ret = bootefi_test_prepare(&image_obj, &loaded_image_info, + "\\selftest", "efi_selftest"); + if (ret != EFI_SUCCESS) + return CMD_RET_FAILURE; + + /* Execute the test */ + ret = do_bootefi_exec(&image_obj->header, + loaded_image_info->load_options); + efi_free_pool(test_device_path); + efi_free_pool(test_image_path); + if (ret != EFI_SUCCESS) + efi_delete_handle(&image_obj->header); + else + ret = efi_delete_handle(&image_obj->header); + + return ret != EFI_SUCCESS; +} + +/** + * do_bootefi() - execute `bootefi` command + * + * @cmdtp: table entry describing command + * @flag: bitmap indicating how the command was invoked + * @argc: number of arguments + * @argv: command line arguments + * Return: status code + */ +static int do_bootefi(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + efi_status_t ret; + char *p; + void *fdt, *image_buf; + unsigned long addr, size; + void *image_addr; + size_t image_size; + + if (argc < 2) + return CMD_RET_USAGE; + + if (argc > 2) { + uintptr_t fdt_addr; + + fdt_addr = hextoul(argv[2], NULL); + fdt = map_sysmem(fdt_addr, 0); + } else { + fdt = EFI_FDT_USE_INTERNAL; + } + + if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR) && + !strcmp(argv[1], "bootmgr")) { + ret = efi_bootmgr_run(fdt); + + if (ret != EFI_SUCCESS) + return CMD_RET_FAILURE; + + return CMD_RET_SUCCESS; + } + + if (IS_ENABLED(CONFIG_CMD_BOOTEFI_SELFTEST) && + !strcmp(argv[1], "selftest")) { + /* Initialize EFI drivers */ + ret = efi_init_obj_list(); + if (ret != EFI_SUCCESS) { + log_err("Error: Cannot initialize UEFI sub-system, r = %lu\n", + ret & ~EFI_ERROR_MASK); + return CMD_RET_FAILURE; + } + + ret = efi_install_fdt(fdt); + if (ret != EFI_SUCCESS) + return CMD_RET_FAILURE; + + return do_efi_selftest(); + } + + if (!IS_ENABLED(CONFIG_CMD_BOOTEFI_BINARY)) + return CMD_RET_SUCCESS; + + if (IS_ENABLED(CONFIG_CMD_BOOTEFI_HELLO) && + !strcmp(argv[1], "hello")) { + image_buf = __efi_helloworld_begin; + size = __efi_helloworld_end - __efi_helloworld_begin; + /* TODO: not sure calling clear function is necessary */ + efi_clear_bootdev(); + } else { + addr = strtoul(argv[1], NULL, 16); + /* Check that a numeric value was passed */ + if (!addr) + return CMD_RET_USAGE; + image_buf = map_sysmem(addr, 0); + + p = strchr(argv[1], ':'); + if (p) { + size = strtoul(++p, NULL, 16); + if (!size) + return CMD_RET_USAGE; + efi_clear_bootdev(); + } else { + /* Image should be already loaded */ + efi_get_image_parameters(&image_addr, &image_size); + + if (image_buf != image_addr) { + log_err("No UEFI binary known at %s\n", + argv[1]); + return CMD_RET_FAILURE; + } + size = image_size; + } + } + + ret = efi_binary_run(image_buf, size, fdt, NULL, 0); + + if (ret != EFI_SUCCESS) + return CMD_RET_FAILURE; + + return CMD_RET_SUCCESS; +} + +U_BOOT_LONGHELP(bootefi, + "<image address>[:<image size>] [<fdt address>]\n" + " - boot EFI payload\n" +#ifdef CONFIG_CMD_BOOTEFI_HELLO + "bootefi hello\n" + " - boot a sample Hello World application stored within U-Boot\n" +#endif +#ifdef CONFIG_CMD_BOOTEFI_SELFTEST + "bootefi selftest [fdt address]\n" + " - boot an EFI selftest application stored within U-Boot\n" + " Use environment variable efi_selftest to select a single test.\n" + " Use 'setenv efi_selftest list' to enumerate all tests.\n" +#endif +#ifdef CONFIG_CMD_BOOTEFI_BOOTMGR + "bootefi bootmgr [fdt address]\n" + " - load and boot EFI payload based on BootOrder/BootXXXX variables.\n" + "\n" + " If specified, the device tree located at <fdt address> gets\n" + " exposed as EFI configuration table.\n" +#endif + ); + +U_BOOT_CMD( + bootefi, 4, 0, do_bootefi, + "Boots an EFI payload from memory", + bootefi_help_text +); |