Re: [PATCH v2 06/12] cmd: bootefi: move library interfaces under lib/efi_loader

Heinrich Schuchardt
2023-12-17T11:51:19Z

On 11/21/23 02:29, AKASHI Takahiro wrote:
> In the prior commits, interfaces for executing EFI binary and boot manager
> were carved out. Move them under efi_loader directory so that they can
> be called from other places without depending on bootefi command.
>
> Only efi_selftest-related code will be left in bootefi.c.
>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
> cmd/bootefi.c | 539 +----------------------------------
> include/efi_loader.h | 10 +
> lib/efi_loader/efi_bootmgr.c | 529 ++++++++++++++++++++++++++++++++++
> 3 files changed, 550 insertions(+), 528 deletions(-)
>
> diff --git a/cmd/bootefi.c b/cmd/bootefi.c
> index 7930c99def44..9cf9027bf409 100644
> --- a/cmd/bootefi.c
> +++ b/cmd/bootefi.c
> @@ -7,545 +7,22 @@
>
> #define LOG_CATEGORY LOGC_EFI
>
> -#include <common.h>
> -#include <bootm.h>
> -#include <charset.h>
> #include <command.h>
> -#include <dm.h>
> +#include <efi.h>
> #include <efi_loader.h>
> -#include <efi_selftest.h>
> -#include <env.h>
> -#include <errno.h>
> -#include <image.h>
> +#include <exports.h>
> #include <log.h>
> #include <malloc.h>
> -#include <asm/global_data.h>
> -#include <linux/libfdt.h>
> -#include <linux/libfdt_env.h>
> #include <mapmem.h>
> -#include <memalign.h>
> +#include <vsprintf.h>
> #include <asm-generic/sections.h>
> -#include <linux/linkage.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 struct efi_device_path *bootefi_image_path;
> -static struct efi_device_path *bootefi_device_path;
> -static void *image_addr;
> -static size_t image_size;
> -
> -/**
> - * efi_get_image_parameters() - return image parameters
> - *
> - * @img_addr: address of loaded image in memory
> - * @img_size: size of loaded image
> - */
> -void efi_get_image_parameters(void **img_addr, size_t *img_size)
> -{
> - *img_addr = image_addr;
> - *img_size = image_size;
> -}
> -
> -/**
> - * efi_clear_bootdev() - clear boot device
> - */
> -static void efi_clear_bootdev(void)
> -{
> - efi_free_pool(bootefi_device_path);
> - efi_free_pool(bootefi_image_path);
> - bootefi_device_path = NULL;
> - bootefi_image_path = NULL;
> - image_addr = NULL;
> - image_size = 0;
> -}
> -
> -/**
> - * efi_set_bootdev() - set boot device
> - *
> - * This function is called when a file is loaded, e.g. via the 'load' command.
> - * We use the path to this file to inform the UEFI binary about the boot device.
> - *
> - * @dev: device, e.g. "MMC"
> - * @devnr: number of the device, e.g. "1:2"
> - * @path: path to file loaded
> - * @buffer: buffer with file loaded
> - * @buffer_size: size of file loaded
> - */
> -void efi_set_bootdev(const char *dev, const char *devnr, const char *path,
> - void *buffer, size_t buffer_size)
> -{
> - struct efi_device_path *device, *image;
> - efi_status_t ret;
> -
> - log_debug("dev=%s, devnr=%s, path=%s, buffer=%p, size=%zx\n", dev,
> - devnr, path, buffer, buffer_size);
> -
> - /* Forget overwritten image */
> - if (buffer + buffer_size >= image_addr &&
> - image_addr + image_size >= buffer)
> - efi_clear_bootdev();
> -
> - /* Remember only PE-COFF and FIT images */
> - if (efi_check_pe(buffer, buffer_size, NULL) != EFI_SUCCESS) {
> - if (IS_ENABLED(CONFIG_FIT) &&
> - !fit_check_format(buffer, IMAGE_SIZE_INVAL)) {
> - /*
> - * FIT images of type EFI_OS are started via command
> - * bootm. We should not use their boot device with the
> - * bootefi command.
> - */
> - buffer = 0;
> - buffer_size = 0;
> - } else {
> - log_debug("- not remembering image\n");
> - return;
> - }
> - }
> -
> - /* efi_set_bootdev() is typically called repeatedly, recover memory */
> - efi_clear_bootdev();
> -
> - image_addr = buffer;
> - image_size = buffer_size;
> -
> - ret = efi_dp_from_name(dev, devnr, path, &device, &image);
> - if (ret == EFI_SUCCESS) {
> - bootefi_device_path = device;
> - if (image) {
> - /* FIXME: image should not contain device */
> - struct efi_device_path *image_tmp = image;
> -
> - efi_dp_split_file_path(image, &device, &image);
> - efi_free_pool(image_tmp);
> - }
> - bootefi_image_path = image;
> - log_debug("- boot device %pD\n", device);
> - if (image)
> - log_debug("- image %pD\n", image);
> - } else {
> - log_debug("- efi_dp_from_name() failed, err=%lx\n", ret);
> - efi_clear_bootdev();
> - }
> -}
> -
> -/**
> - * efi_env_set_load_options() - set load options from environment variable
> - *
> - * @handle: the image handle
> - * @env_var: name of the environment variable
> - * @load_options: pointer to load options (output)
> - * Return: status code
> - */
> -static efi_status_t efi_env_set_load_options(efi_handle_t handle,
> - const char *env_var,
> - u16 **load_options)
> -{
> - const char *env = env_get(env_var);
> - size_t size;
> - u16 *pos;
> - efi_status_t ret;
> -
> - *load_options = NULL;
> - if (!env)
> - return EFI_SUCCESS;
> - size = sizeof(u16) * (utf8_utf16_strlen(env) + 1);
> - pos = calloc(size, 1);
> - if (!pos)
> - return EFI_OUT_OF_RESOURCES;
> - *load_options = pos;
> - utf8_utf16_strcpy(&pos, env);
> - ret = efi_set_load_options(handle, size, *load_options);
> - if (ret != EFI_SUCCESS) {
> - free(*load_options);
> - *load_options = NULL;
> - }
> - return ret;
> -}
> -
> -#if !CONFIG_IS_ENABLED(GENERATE_ACPI_TABLE)
> -
> -/**
> - * copy_fdt() - Copy the device tree to a new location available to EFI
> - *
> - * The FDT is copied to a suitable location within the EFI memory map.
> - * Additional 12 KiB are added to the space in case the device tree needs to be
> - * expanded later with fdt_open_into().
> - *
> - * @fdtp: On entry a pointer to the flattened device tree.
> - * On exit a pointer to the copy of the flattened device tree.
> - * FDT start
> - * Return: status code
> - */
> -static efi_status_t copy_fdt(void **fdtp)
> -{
> - unsigned long fdt_ram_start = -1L, fdt_pages;
> - efi_status_t ret = 0;
> - void *fdt, *new_fdt;
> - u64 new_fdt_addr;
> - uint fdt_size;
> - int i;
> -
> - for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
> - u64 ram_start = gd->bd->bi_dram[i].start;
> - u64 ram_size = gd->bd->bi_dram[i].size;
> -
> - if (!ram_size)
> - continue;
> -
> - if (ram_start < fdt_ram_start)
> - fdt_ram_start = ram_start;
> - }
> -
> - /*
> - * Give us at least 12 KiB of breathing room in case the device tree
> - * needs to be expanded later.
> - */
> - fdt = *fdtp;
> - fdt_pages = efi_size_in_pages(fdt_totalsize(fdt) + 0x3000);
> - fdt_size = fdt_pages << EFI_PAGE_SHIFT;
> -
> - ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
> - EFI_ACPI_RECLAIM_MEMORY, fdt_pages,
> - &new_fdt_addr);
> - if (ret != EFI_SUCCESS) {
> - log_err("ERROR: Failed to reserve space for FDT\n");
> - goto done;
> - }
> - new_fdt = (void *)(uintptr_t)new_fdt_addr;
> - memcpy(new_fdt, fdt, fdt_totalsize(fdt));
> - fdt_set_totalsize(new_fdt, fdt_size);
> -
> - *fdtp = (void *)(uintptr_t)new_fdt_addr;
> -done:
> - return ret;
> -}
> -
> -/**
> - * get_config_table() - get configuration table
> - *
> - * @guid: GUID of the configuration table
> - * Return: pointer to configuration table or NULL
> - */
> -static void *get_config_table(const efi_guid_t *guid)
> -{
> - size_t i;
> -
> - for (i = 0; i < systab.nr_tables; i++) {
> - if (!guidcmp(guid, &systab.tables[i].guid))
> - return systab.tables[i].table;
> - }
> - return NULL;
> -}
> -
> -#endif /* !CONFIG_IS_ENABLED(GENERATE_ACPI_TABLE) */
> -
> -/**
> - * efi_install_fdt() - install device tree
> - *
> - * If fdt is not EFI_FDT_USE_INTERNAL, the device tree located at that memory
> - * address will will be installed as configuration table, otherwise the device
> - * tree located at the address indicated by environment variable fdt_addr or as
> - * fallback fdtcontroladdr will be used.
> - *
> - * On architectures using ACPI tables device trees shall not be installed as
> - * configuration table.
> - *
> - * @fdt: address of device tree or EFI_FDT_USE_INTERNAL to use the
> - * the hardware device tree as indicated by environment variable
> - * fdt_addr or as fallback the internal device tree as indicated by
> - * the environment variable fdtcontroladdr
> - * Return: status code
> - */
> -efi_status_t efi_install_fdt(void *fdt)
> -{
> - /*
> - * The EBBR spec requires that we have either an FDT or an ACPI table
> - * but not both.
> - */
> -#if CONFIG_IS_ENABLED(GENERATE_ACPI_TABLE)
> - if (fdt) {
> - log_warning("WARNING: Can't have ACPI table and device tree - ignoring DT.\n");
> - return EFI_SUCCESS;
> - }
> -#else
> - struct bootm_headers img = { 0 };
> - efi_status_t ret;
> -
> - if (fdt == EFI_FDT_USE_INTERNAL) {
> - const char *fdt_opt;
> - uintptr_t fdt_addr;
> -
> - /* Look for device tree that is already installed */
> - if (get_config_table(&efi_guid_fdt))
> - return EFI_SUCCESS;
> - /* Check if there is a hardware device tree */
> - fdt_opt = env_get("fdt_addr");
> - /* Use our own device tree as fallback */
> - if (!fdt_opt) {
> - fdt_opt = env_get("fdtcontroladdr");
> - if (!fdt_opt) {
> - log_err("ERROR: need device tree\n");
> - return EFI_NOT_FOUND;
> - }
> - }
> - fdt_addr = hextoul(fdt_opt, NULL);
> - if (!fdt_addr) {
> - log_err("ERROR: invalid $fdt_addr or $fdtcontroladdr\n");
> - return EFI_LOAD_ERROR;
> - }
> - fdt = map_sysmem(fdt_addr, 0);
> - }
> -
> - /* Install device tree */
> - if (fdt_check_header(fdt)) {
> - log_err("ERROR: invalid device tree\n");
> - return EFI_LOAD_ERROR;
> - }
> -
> - /* Prepare device tree for payload */
> - ret = copy_fdt(&fdt);
> - if (ret) {
> - log_err("ERROR: out of memory\n");
> - return EFI_OUT_OF_RESOURCES;
> - }
> -
> - if (image_setup_libfdt(&img, fdt, 0, NULL)) {
> - log_err("ERROR: failed to process device tree\n");
> - return EFI_LOAD_ERROR;
> - }
> -
> - /* Create memory reservations as indicated by the device tree */
> - efi_carve_out_dt_rsv(fdt);
> -
> - efi_try_purge_kaslr_seed(fdt);
> -
> - if (CONFIG_IS_ENABLED(EFI_TCG2_PROTOCOL_MEASURE_DTB)) {
> - ret = efi_tcg2_measure_dtb(fdt);
> - if (ret == EFI_SECURITY_VIOLATION) {
> - log_err("ERROR: failed to measure DTB\n");
> - return ret;
> - }
> - }
> -
> - /* Install device tree as UEFI table */
> - ret = efi_install_configuration_table(&efi_guid_fdt, fdt);
> - if (ret != EFI_SUCCESS) {
> - log_err("ERROR: failed to install device tree\n");
> - return ret;
> - }
> -#endif /* GENERATE_ACPI_TABLE */
> -
> - return EFI_SUCCESS;
> -}
> -
> -/**
> - * do_bootefi_exec() - execute EFI binary
> - *
> - * The image indicated by @handle is started. When it returns the allocated
> - * memory for the @load_options is freed.
> - *
> - * @handle: handle of loaded image
> - * @load_options: load options
> - * Return: status code
> - *
> - * Load the EFI binary into a newly assigned memory unwinding the relocation
> - * information, install the loaded image protocol, and call the binary.
> - */
> -static efi_status_t do_bootefi_exec(efi_handle_t handle, void *load_options)
> -{
> - efi_status_t ret;
> - efi_uintn_t exit_data_size = 0;
> - u16 *exit_data = NULL;
> - struct efi_event *evt;
> -
> - /* On ARM switch from EL3 or secure mode to EL2 or non-secure mode */
> - switch_to_non_secure_mode();
> -
> - /*
> - * The UEFI standard requires that the watchdog timer is set to five
> - * minutes when invoking an EFI boot option.
> - *
> - * Unified Extensible Firmware Interface (UEFI), version 2.7 Errata A
> - * 7.5. Miscellaneous Boot Services - EFI_BOOT_SERVICES.SetWatchdogTimer
> - */
> - ret = efi_set_watchdog(300);
> - if (ret != EFI_SUCCESS) {
> - log_err("ERROR: Failed to set watchdog timer\n");
> - goto out;
> - }
> -
> - /* Call our payload! */
> - ret = EFI_CALL(efi_start_image(handle, &exit_data_size, &exit_data));
> - if (ret != EFI_SUCCESS) {
> - log_err("## Application failed, r = %lu\n",
> - ret & ~EFI_ERROR_MASK);
> - if (exit_data) {
> - log_err("## %ls\n", exit_data);
> - efi_free_pool(exit_data);
> - }
> - }
> -
> - efi_restore_gd();
> -
> -out:
> - free(load_options);
> -
> - if (IS_ENABLED(CONFIG_EFI_LOAD_FILE2_INITRD)) {
> - if (efi_initrd_deregister() != EFI_SUCCESS)
> - log_err("Failed to remove loadfile2 for initrd\n");
> - }
> -
> - /* Notify EFI_EVENT_GROUP_RETURN_TO_EFIBOOTMGR event group. */
> - list_for_each_entry(evt, &efi_events, link) {
> - if (evt->group &&
> - !guidcmp(evt->group,
> - &efi_guid_event_group_return_to_efibootmgr)) {
> - efi_signal_event(evt);
> - EFI_CALL(systab.boottime->close_event(evt));
> - break;
> - }
> - }
> -
> - /* Control is returned to U-Boot, disable EFI watchdog */
> - efi_set_watchdog(0);
> -
> - return ret;
> -}
> -
> -/**
> - * efi_bootmgr_run() - execute EFI boot manager
> - * fdt: Flat device tree
> - *
> - * Invoke EFI boot manager and execute a binary depending on
> - * boot options. If @fdt is not NULL, it will be passed to
> - * the executed binary.
> - *
> - * Return: status code
> - */
> -static efi_status_t efi_bootmgr_run(void *fdt)
> -{
> - efi_handle_t handle;
> - void *load_options;
> - efi_status_t ret;
> -
> - /* 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 ret;
> -
> - ret = efi_bootmgr_load(&handle, &load_options);
> - if (ret != EFI_SUCCESS) {
> - log_notice("EFI boot manager: Cannot load any image\n");
> - return ret;
> - }
> -
> - return do_bootefi_exec(handle, load_options);
> -}
> -
> -/**
> - * efi_run_image() - run loaded UEFI image
> - *
> - * @source_buffer: memory address of the UEFI image
> - * @source_size: size of the UEFI image
> - * Return: status code
> - */
> -efi_status_t efi_run_image(void *source_buffer, efi_uintn_t source_size)
> -{
> - efi_handle_t mem_handle = NULL, handle;
> - struct efi_device_path *file_path = NULL;
> - struct efi_device_path *msg_path;
> - efi_status_t ret, ret2;
> - u16 *load_options;
> -
> - if (!bootefi_device_path || !bootefi_image_path) {
> - log_debug("Not loaded from disk\n");
> - /*
> - * Special case for efi payload not loaded from disk,
> - * such as 'bootefi hello' or for example payload
> - * loaded directly into memory via JTAG, etc:
> - */
> - file_path = efi_dp_from_mem(EFI_RESERVED_MEMORY_TYPE,
> - (uintptr_t)source_buffer,
> - source_size);
> - /*
> - * Make sure that device for device_path exist
> - * in load_image(). Otherwise, shell and grub will fail.
> - */
> - ret = efi_install_multiple_protocol_interfaces(&mem_handle,
> - &efi_guid_device_path,
> - file_path, NULL);
> - if (ret != EFI_SUCCESS)
> - goto out;
> - msg_path = file_path;
> - } else {
> - file_path = efi_dp_append(bootefi_device_path,
> - bootefi_image_path);
> - msg_path = bootefi_image_path;
> - log_debug("Loaded from disk\n");
> - }
> -
> - log_info("Booting %pD\n", msg_path);
> -
> - ret = EFI_CALL(efi_load_image(false, efi_root, file_path, source_buffer,
> - source_size, &handle));
> - if (ret != EFI_SUCCESS) {
> - log_err("Loading image failed\n");
> - goto out;
> - }
> -
> - /* Transfer environment variable as load options */
> - ret = efi_env_set_load_options(handle, "bootargs", &load_options);
> - if (ret != EFI_SUCCESS)
> - goto out;
> -
> - ret = do_bootefi_exec(handle, load_options);
> -
> -out:
> - ret2 = efi_uninstall_multiple_protocol_interfaces(mem_handle,
> - &efi_guid_device_path,
> - file_path, NULL);
> - efi_free_pool(file_path);
> - return (ret != EFI_SUCCESS) ? ret : ret2;
> -}
> -
> -/**
> - * efi_binary_run() - run loaded UEFI image
> - *
> - * @image: memory address of the UEFI image
> - * @size: size of the UEFI image
> - *
> - * Execute an EFI binary image loaded at @image.
> - * @size may be zero if the binary is loaded with U-Boot load command.
> - *
> - * Return: status code
> - */
> -static efi_status_t efi_binary_run(void *image, size_t size, void *fdt)
> -{
> - efi_status_t ret;
> -
> - /* 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 ret;
> - }
> -
> - ret = efi_install_fdt(fdt);
> - if (ret != EFI_SUCCESS)
> - return ret;
> -
> - return efi_run_image(image, size);
> -}
>
> static efi_status_t bootefi_run_prepare(const char *load_options_path,
> struct efi_device_path *device_path,
> @@ -658,6 +135,8 @@ static int do_bootefi(struct cmd_tbl *cmdtp, int flag, int argc,
> char *p;
> void *fdt, *image_buf;
> unsigned long addr, size;
> + void *image_addr;
> + size_t image_size;
>
> if (argc < 2)
> return CMD_RET_USAGE;
> @@ -709,6 +188,7 @@ static int do_bootefi(struct cmd_tbl *cmdtp, int flag, int argc,
> !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);
> @@ -724,6 +204,9 @@ static int do_bootefi(struct cmd_tbl *cmdtp, int flag, int argc,
> 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]);
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index 44436d346286..34e7fbbf1840 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -90,6 +90,8 @@ efi_status_t efi_add_runtime_mmio(void *mmio_ptr, u64 len);
> * back to u-boot world
> */
> void efi_restore_gd(void);
> +/* Call this to unset the current device name */
> +void efi_clear_bootdev(void);
> /* Call this to set the current device name */
> void efi_set_bootdev(const char *dev, const char *devnr, const char *path,
> void *buffer, size_t buffer_size);
> @@ -114,6 +116,7 @@ static inline efi_status_t efi_add_runtime_mmio(void *mmio_ptr, u64 len)
>
> /* No loader configured, stub out EFI_ENTRY */
> static inline void efi_restore_gd(void) { }
> +static inline void efi_clear_bootdev(void) { }
> static inline void efi_set_bootdev(const char *dev, const char *devnr,
> const char *path, void *buffer,
> size_t buffer_size) { }
> @@ -527,14 +530,21 @@ efi_status_t efi_bootmgr_get_unused_bootoption(u16 *buf,
> efi_status_t efi_bootmgr_update_media_device_boot_option(void);
> /* Delete selected boot option */
> efi_status_t efi_bootmgr_delete_boot_option(u16 boot_index);
> +/* Invoke EFI boot manager */
> +efi_status_t efi_bootmgr_run(void *fdt);
> /* search the boot option index in BootOrder */
> bool efi_search_bootorder(u16 *bootorder, efi_uintn_t num, u32 target, u32 *index);
> /* Set up console modes */
> void efi_setup_console_size(void);
> +/* Set up load options from environment variable */
> +efi_status_t efi_env_set_load_options(efi_handle_t handle, const char *env_var,
> + u16 **load_options);
> /* Install device tree */
> efi_status_t efi_install_fdt(void *fdt);
> /* Run loaded UEFI image */
> efi_status_t efi_run_image(void *source_buffer, efi_uintn_t source_size);
> +/* Run loaded UEFI image with given fdt */
> +efi_status_t efi_binary_run(void *image, size_t size, void *fdt);
> /* Initialize variable services */
> efi_status_t efi_init_variables(void);
> /* Notify ExitBootServices() is called */
> diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c
> index 48153bd5ffbb..e910f1bb2a23 100644
> --- a/lib/efi_loader/efi_bootmgr.c
> +++ b/lib/efi_loader/efi_bootmgr.c
> @@ -3,6 +3,8 @@
> * EFI boot manager
> *
> * Copyright (c) 2017 Rob Clark
> + * For the code moved from cmd/bootefi.c
> + * Copyright (c) 2016 Alexander Graf
> */
>
> #define LOG_CATEGORY LOGC_EFI
> @@ -20,6 +22,17 @@
> #include <efi_variable.h>
> #include <asm/unaligned.h>
>
> +/* TODO: temporarily added here; clean up later */
> +#include <bootm.h>
> +#include <efi_selftest.h>
> +#include <env.h>
> +#include <mapmem.h>
> +#include <asm/global_data.h>
> +#include <linux/libfdt.h>
> +#include <linux/libfdt_env.h>
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> static const struct efi_boot_services *bs;
> static const struct efi_runtime_services *rs;
>
> @@ -1115,3 +1128,519 @@ out:
> return EFI_SUCCESS;
> return ret;
> }
> +
> +static struct efi_device_path *bootefi_image_path;
> +static struct efi_device_path *bootefi_device_path;
> +static void *image_addr;
> +static size_t image_size;
> +
> +/**
> + * efi_get_image_parameters() - return image parameters
> + *
> + * @img_addr: address of loaded image in memory
> + * @img_size: size of loaded image
> + */
> +void efi_get_image_parameters(void **img_addr, size_t *img_size)
> +{
> + *img_addr = image_addr;
> + *img_size = image_size;
> +}
> +
> +/**
> + * efi_clear_bootdev() - clear boot device
> + */
> +void efi_clear_bootdev(void)
> +{
> + efi_free_pool(bootefi_device_path);
> + efi_free_pool(bootefi_image_path);
> + bootefi_device_path = NULL;
> + bootefi_image_path = NULL;
> + image_addr = NULL;
> + image_size = 0;
> +}
> +
> +/**
> + * efi_set_bootdev() - set boot device
> + *
> + * This function is called when a file is loaded, e.g. via the 'load' command.
> + * We use the path to this file to inform the UEFI binary about the boot device.
> + *
> + * @dev: device, e.g. "MMC"
> + * @devnr: number of the device, e.g. "1:2"
> + * @path: path to file loaded
> + * @buffer: buffer with file loaded
> + * @buffer_size: size of file loaded
> + */
> +void efi_set_bootdev(const char *dev, const char *devnr, const char *path,
> + void *buffer, size_t buffer_size)
> +{
> + struct efi_device_path *device, *image;
> + efi_status_t ret;
> +
> + log_debug("dev=%s, devnr=%s, path=%s, buffer=%p, size=%zx\n", dev,
> + devnr, path, buffer, buffer_size);
> +
> + /* Forget overwritten image */
> + if (buffer + buffer_size >= image_addr &&
> + image_addr + image_size >= buffer)
> + efi_clear_bootdev();
> +
> + /* Remember only PE-COFF and FIT images */
> + if (efi_check_pe(buffer, buffer_size, NULL) != EFI_SUCCESS) {
> + if (IS_ENABLED(CONFIG_FIT) &&
> + !fit_check_format(buffer, IMAGE_SIZE_INVAL)) {
> + /*
> + * FIT images of type EFI_OS are started via command
> + * bootm. We should not use their boot device with the
> + * bootefi command.
> + */
> + buffer = 0;
> + buffer_size = 0;
> + } else {
> + log_debug("- not remembering image\n");
> + return;
> + }
> + }
> +
> + /* efi_set_bootdev() is typically called repeatedly, recover memory */
> + efi_clear_bootdev();
> +
> + image_addr = buffer;
> + image_size = buffer_size;
> +
> + ret = efi_dp_from_name(dev, devnr, path, &device, &image);
> + if (ret == EFI_SUCCESS) {
> + bootefi_device_path = device;
> + if (image) {
> + /* FIXME: image should not contain device */
> + struct efi_device_path *image_tmp = image;
> +
> + efi_dp_split_file_path(image, &device, &image);
> + efi_free_pool(image_tmp);
> + }
> + bootefi_image_path = image;
> + log_debug("- boot device %pD\n", device);
> + if (image)
> + log_debug("- image %pD\n", image);
> + } else {
> + log_debug("- efi_dp_from_name() failed, err=%lx\n", ret);
> + efi_clear_bootdev();
> + }
> +}
> +
> +/**
> + * efi_env_set_load_options() - set load options from environment variable
> + *
> + * @handle: the image handle
> + * @env_var: name of the environment variable
> + * @load_options: pointer to load options (output)
> + * Return: status code
> + */
> +efi_status_t efi_env_set_load_options(efi_handle_t handle,
> + const char *env_var,
> + u16 **load_options)
> +{
> + const char *env = env_get(env_var);
> + size_t size;
> + u16 *pos;
> + efi_status_t ret;
> +
> + *load_options = NULL;
> + if (!env)
> + return EFI_SUCCESS;
> + size = sizeof(u16) * (utf8_utf16_strlen(env) + 1);
> + pos = calloc(size, 1);
> + if (!pos)
> + return EFI_OUT_OF_RESOURCES;
> + *load_options = pos;
> + utf8_utf16_strcpy(&pos, env);
> + ret = efi_set_load_options(handle, size, *load_options);
> + if (ret != EFI_SUCCESS) {
> + free(*load_options);
> + *load_options = NULL;
> + }
> + return ret;
> +}
> +
> +#if !CONFIG_IS_ENABLED(GENERATE_ACPI_TABLE)
> +
> +/**
> + * copy_fdt() - Copy the device tree to a new location available to EFI
> + *
> + * The FDT is copied to a suitable location within the EFI memory map.
> + * Additional 12 KiB are added to the space in case the device tree needs to be
> + * expanded later with fdt_open_into().
> + *
> + * @fdtp: On entry a pointer to the flattened device tree.
> + * On exit a pointer to the copy of the flattened device tree.
> + * FDT start
> + * Return: status code
> + */
> +static efi_status_t copy_fdt(void **fdtp)
> +{
> + unsigned long fdt_ram_start = -1L, fdt_pages;
> + efi_status_t ret = 0;
> + void *fdt, *new_fdt;
> + u64 new_fdt_addr;
> + uint fdt_size;
> + int i;
> +
> + for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
> + u64 ram_start = gd->bd->bi_dram[i].start;
> + u64 ram_size = gd->bd->bi_dram[i].size;
> +
> + if (!ram_size)
> + continue;
> +
> + if (ram_start < fdt_ram_start)
> + fdt_ram_start = ram_start;
> + }
> +
> + /*
> + * Give us at least 12 KiB of breathing room in case the device tree
> + * needs to be expanded later.
> + */
> + fdt = *fdtp;
> + fdt_pages = efi_size_in_pages(fdt_totalsize(fdt) + 0x3000);
> + fdt_size = fdt_pages << EFI_PAGE_SHIFT;
> +
> + ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
> + EFI_ACPI_RECLAIM_MEMORY, fdt_pages,
> + &new_fdt_addr);
> + if (ret != EFI_SUCCESS) {
> + log_err("ERROR: Failed to reserve space for FDT\n");
> + goto done;
> + }
> + new_fdt = (void *)(uintptr_t)new_fdt_addr;
> + memcpy(new_fdt, fdt, fdt_totalsize(fdt));
> + fdt_set_totalsize(new_fdt, fdt_size);
> +
> + *fdtp = (void *)(uintptr_t)new_fdt_addr;
> +done:
> + return ret;
> +}
> +
> +/**
> + * get_config_table() - get configuration table
> + *
> + * @guid: GUID of the configuration table
> + * Return: pointer to configuration table or NULL
> + */
> +static void *get_config_table(const efi_guid_t *guid)
> +{
> + size_t i;
> +
> + for (i = 0; i < systab.nr_tables; i++) {
> + if (!guidcmp(guid, &systab.tables[i].guid))
> + return systab.tables[i].table;
> + }
> + return NULL;
> +}
> +
> +#endif /* !CONFIG_IS_ENABLED(GENERATE_ACPI_TABLE) */
> +
> +/**
> + * efi_install_fdt() - install device tree
> + *
> + * If fdt is not EFI_FDT_USE_INTERNAL, the device tree located at that memory
> + * address will be installed as configuration table, otherwise the device
> + * tree located at the address indicated by environment variable fdt_addr or as
> + * fallback fdtcontroladdr will be used.
> + *
> + * On architectures using ACPI tables device trees shall not be installed as
> + * configuration table.
> + *
> + * @fdt: address of device tree or EFI_FDT_USE_INTERNAL to use
> + * the hardware device tree as indicated by environment variable
> + * fdt_addr or as fallback the internal device tree as indicated by
> + * the environment variable fdtcontroladdr
> + * Return: status code
> + */
> +efi_status_t efi_install_fdt(void *fdt)
> +{
> + /*
> + * The EBBR spec requires that we have either an FDT or an ACPI table
> + * but not both.
> + */
> +#if CONFIG_IS_ENABLED(GENERATE_ACPI_TABLE)
> + if (fdt) {
> + log_warning("WARNING: Can't have ACPI table and device tree - ignoring DT.\n");
> + return EFI_SUCCESS;
> + }
> +#else
> + struct bootm_headers img = { 0 };
> + efi_status_t ret;
> +
> + if (fdt == EFI_FDT_USE_INTERNAL) {
> + const char *fdt_opt;
> + uintptr_t fdt_addr;
> +
> + /* Look for device tree that is already installed */
> + if (get_config_table(&efi_guid_fdt))
> + return EFI_SUCCESS;
> + /* Check if there is a hardware device tree */
> + fdt_opt = env_get("fdt_addr");
> + /* Use our own device tree as fallback */
> + if (!fdt_opt) {
> + fdt_opt = env_get("fdtcontroladdr");
> + if (!fdt_opt) {
> + log_err("ERROR: need device tree\n");
> + return EFI_NOT_FOUND;
> + }
> + }
> + fdt_addr = hextoul(fdt_opt, NULL);
> + if (!fdt_addr) {
> + log_err("ERROR: invalid $fdt_addr or $fdtcontroladdr\n");
> + return EFI_LOAD_ERROR;
> + }
> + fdt = map_sysmem(fdt_addr, 0);
> + }
> +
> + /* Install device tree */
> + if (fdt_check_header(fdt)) {
> + log_err("ERROR: invalid device tree\n");
> + return EFI_LOAD_ERROR;
> + }
> +
> + /* Prepare device tree for payload */
> + ret = copy_fdt(&fdt);
> + if (ret) {
> + log_err("ERROR: out of memory\n");
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + if (image_setup_libfdt(&img, fdt, 0, NULL)) {
> + log_err("ERROR: failed to process device tree\n");
> + return EFI_LOAD_ERROR;
> + }
> +
> + /* Create memory reservations as indicated by the device tree */
> + efi_carve_out_dt_rsv(fdt);
> +
> + efi_try_purge_kaslr_seed(fdt);
> +
> + if (CONFIG_IS_ENABLED(EFI_TCG2_PROTOCOL_MEASURE_DTB)) {
> + ret = efi_tcg2_measure_dtb(fdt);
> + if (ret == EFI_SECURITY_VIOLATION) {
> + log_err("ERROR: failed to measure DTB\n");
> + return ret;
> + }
> + }
> +
> + /* Install device tree as UEFI table */
> + ret = efi_install_configuration_table(&efi_guid_fdt, fdt);
> + if (ret != EFI_SUCCESS) {
> + log_err("ERROR: failed to install device tree\n");
> + return ret;
> + }
> +#endif /* GENERATE_ACPI_TABLE */
> +
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + * do_bootefi_exec() - execute EFI binary
> + *
> + * The image indicated by @handle is started. When it returns the allocated
> + * memory for the @load_options is freed.
> + *
> + * @handle: handle of loaded image
> + * @load_options: load options
> + * Return: status code
> + *
> + * Load the EFI binary into a newly assigned memory unwinding the relocation
> + * information, install the loaded image protocol, and call the binary.
> + */
> +static efi_status_t do_bootefi_exec(efi_handle_t handle, void *load_options)
> +{
> + efi_status_t ret;
> + efi_uintn_t exit_data_size = 0;
> + u16 *exit_data = NULL;
> + struct efi_event *evt;
> +
> + /* On ARM switch from EL3 or secure mode to EL2 or non-secure mode */
> + switch_to_non_secure_mode();
> +
> + /*
> + * The UEFI standard requires that the watchdog timer is set to five
> + * minutes when invoking an EFI boot option.
> + *
> + * Unified Extensible Firmware Interface (UEFI), version 2.7 Errata A
> + * 7.5. Miscellaneous Boot Services - EFI_BOOT_SERVICES.SetWatchdogTimer
> + */
> + ret = efi_set_watchdog(300);
> + if (ret != EFI_SUCCESS) {
> + log_err("ERROR: Failed to set watchdog timer\n");
> + goto out;
> + }
> +
> + /* Call our payload! */
> + ret = EFI_CALL(efi_start_image(handle, &exit_data_size, &exit_data));
> + if (ret != EFI_SUCCESS) {
> + log_err("## Application failed, r = %lu\n",
> + ret & ~EFI_ERROR_MASK);
> + if (exit_data) {
> + log_err("## %ls\n", exit_data);
> + efi_free_pool(exit_data);
> + }
> + }
> +
> + efi_restore_gd();
> +
> +out:
> + free(load_options);
> +
> + if (IS_ENABLED(CONFIG_EFI_LOAD_FILE2_INITRD)) {
> + if (efi_initrd_deregister() != EFI_SUCCESS)
> + log_err("Failed to remove loadfile2 for initrd\n");
> + }
> +
> + /* Notify EFI_EVENT_GROUP_RETURN_TO_EFIBOOTMGR event group. */
> + list_for_each_entry(evt, &efi_events, link) {
> + if (evt->group &&
> + !guidcmp(evt->group,
> + &efi_guid_event_group_return_to_efibootmgr)) {
> + efi_signal_event(evt);
> + EFI_CALL(systab.boottime->close_event(evt));
> + break;
> + }
> + }
> +
> + /* Control is returned to U-Boot, disable EFI watchdog */
> + efi_set_watchdog(0);
> +
> + return ret;
> +}
> +
> +/**
> + * efi_bootmgr_run() - execute EFI boot manager
> + * fdt: Flat device tree

'make htmldocs' fails as this documentation does not comply to the Sphix
style.

./lib/efi_loader/efi_bootmgr.c:1526: warning: Function parameter or
member 'fdt' not described in 'efi_bootmgr_run'

> + *
> + * Invoke EFI boot manager and execute a binary depending on
> + * boot options. If @fdt is not NULL, it will be passed to
> + * the executed binary.
> + *
> + * Return: status code
> + */
> +efi_status_t efi_bootmgr_run(void *fdt)
> +{
> + efi_handle_t handle;
> + void *load_options;
> + efi_status_t ret;
> +
> + /* 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 ret;
> +
> + ret = efi_bootmgr_load(&handle, &load_options);
> + if (ret != EFI_SUCCESS) {
> + log_notice("EFI boot manager: Cannot load any image\n");
> + return ret;
> + }
> +
> + return do_bootefi_exec(handle, load_options);
> +}
> +
> +/**
> + * efi_run_image() - run loaded UEFI image
> + *
> + * @source_buffer: memory address of the UEFI image
> + * @source_size: size of the UEFI image
> + * Return: status code
> + */
> +efi_status_t efi_run_image(void *source_buffer, efi_uintn_t source_size)
> +{
> + efi_handle_t mem_handle = NULL, handle;
> + struct efi_device_path *file_path = NULL;
> + struct efi_device_path *msg_path;
> + efi_status_t ret, ret2;
> + u16 *load_options;
> +
> + if (!bootefi_device_path || !bootefi_image_path) {
> + log_debug("Not loaded from disk\n");
> + /*
> + * Special case for efi payload not loaded from disk,
> + * such as 'bootefi hello' or for example payload
> + * loaded directly into memory via JTAG, etc:
> + */
> + file_path = efi_dp_from_mem(EFI_RESERVED_MEMORY_TYPE,
> + (uintptr_t)source_buffer,
> + source_size);
> + /*
> + * Make sure that device for device_path exist
> + * in load_image(). Otherwise, shell and grub will fail.
> + */
> + ret = efi_install_multiple_protocol_interfaces(&mem_handle,
> + &efi_guid_device_path,
> + file_path, NULL);
> + if (ret != EFI_SUCCESS)
> + goto out;
> + msg_path = file_path;
> + } else {
> + file_path = efi_dp_append(bootefi_device_path,
> + bootefi_image_path);
> + msg_path = bootefi_image_path;
> + log_debug("Loaded from disk\n");
> + }
> +
> + log_info("Booting %pD\n", msg_path);
> +
> + ret = EFI_CALL(efi_load_image(false, efi_root, file_path, source_buffer,
> + source_size, &handle));
> + if (ret != EFI_SUCCESS) {
> + log_err("Loading image failed\n");
> + goto out;
> + }
> +
> + /* Transfer environment variable as load options */
> + ret = efi_env_set_load_options(handle, "bootargs", &load_options);
> + if (ret != EFI_SUCCESS)
> + goto out;
> +
> + ret = do_bootefi_exec(handle, load_options);
> +
> +out:
> + ret2 = efi_uninstall_multiple_protocol_interfaces(mem_handle,
> + &efi_guid_device_path,
> + file_path, NULL);
> + efi_free_pool(file_path);
> + return (ret != EFI_SUCCESS) ? ret : ret2;
> +}
> +
> +/**
> + * efi_binary_run() - run loaded UEFI image
> + *
> + * @image: memory address of the UEFI image
> + * @size: size of the UEFI image

'make htmldocs' fails as parameter fdt is not described.

Best regards

Heinrich

> + *
> + * Execute an EFI binary image loaded at @image.
> + * @size may be zero if the binary is loaded with U-Boot load command.
> + *
> + * Return: status code
> + */
> +efi_status_t efi_binary_run(void *image, size_t size, void *fdt)
> +{
> + efi_status_t ret;
> +
> + /* 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 -1;
> + }
> +
> + ret = efi_install_fdt(fdt);
> + if (ret != EFI_SUCCESS)
> + return ret;
> +
> + return efi_run_image(image, size);
> +}