[PATCH v1 45/54] efi: loader: implement bootm handler
Ahmad Fatoum
a.fatoum at pengutronix.de
Thu Dec 18 02:38:05 PST 2025
As counterpart to the EFI-less bootm handlers for the different
architectures, define EFI-enabled variants that will initialize the EFI
loader support and load the PE by EFI means.
Signed-off-by: Ahmad Fatoum <a.fatoum at pengutronix.de>
---
efi/loader/Makefile | 1 +
efi/loader/bootm.c | 358 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 359 insertions(+)
create mode 100644 efi/loader/bootm.c
diff --git a/efi/loader/Makefile b/efi/loader/Makefile
index 62483057b426..2a78361e58d5 100644
--- a/efi/loader/Makefile
+++ b/efi/loader/Makefile
@@ -14,3 +14,4 @@ obj-y += pe.o
obj-y += loadopts.o
obj-$(CONFIG_BOOT) += bootesp.o
obj-$(CONFIG_EFI_LOADER_BOOTMGR) += efibootmgr.o
+obj-$(CONFIG_BOOTM) += bootm.o
diff --git a/efi/loader/bootm.c b/efi/loader/bootm.c
new file mode 100644
index 000000000000..dffe53bcf9c8
--- /dev/null
+++ b/efi/loader/bootm.c
@@ -0,0 +1,358 @@
+// SPDX-License-Identifier: GPL-2.0
+// SPDX-Comment: Origin-URL: https://github.com/u-boot/u-boot/blob/162a6b3df349295bf766c1d128d71b4547e8d56e/lib/efi_loader/efi_helper.c
+/*
+ * Copyright (c) 2016 Alexander Graf
+ */
+#define pr_fmt(fmt) "efi-loader: bootm: " fmt
+
+#include <clock.h>
+#include <linux/sizes.h>
+#include <memory.h>
+#include <command.h>
+#include <magicvar.h>
+#include <init.h>
+#include <driver.h>
+#include <io.h>
+#include <bootargs.h>
+#include <malloc.h>
+#include <string.h>
+#include <linux/err.h>
+#include <fs.h>
+#include <filetype.h>
+#include <libfile.h>
+#include <bootm.h>
+#include <wchar.h>
+#include <efi/mode.h>
+#include <efi/loader.h>
+#include <efi/loader/object.h>
+#include <efi/loader/option.h>
+#include <efi/loader/table.h>
+#include <efi/loader/devicepath.h>
+#include <efi/loader/image.h>
+#include <efi/loader/event.h>
+#include <efi/guid.h>
+#include <efi/services.h>
+#include <efi/error.h>
+#include <efi/initrd.h>
+#include <efi/devicepath.h>
+
+/**
+ * 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)
+{
+ efi_status_t efiret;
+ void *fdt, *new_fdt;
+ static u64 new_fdt_addr;
+ static efi_uintn_t fdt_pages;
+ size_t fdt_size;
+
+ /*
+ * Remove the configuration table that might already be
+ * installed, ignoring EFI_NOT_FOUND if no device-tree
+ * is installed
+ */
+ efi_install_configuration_table(&efi_fdt_guid, NULL);
+
+ if (new_fdt_addr) {
+ pr_debug("%s: Found allocated memory at %#llx, with %#zx pages\n",
+ __func__, new_fdt_addr, fdt_pages);
+
+ efiret = efi_free_pages(new_fdt_addr, fdt_pages);
+ if (efiret != EFI_SUCCESS)
+ pr_err("Unable to free up existing FDT memory region\n");
+
+ new_fdt_addr = 0;
+ fdt_pages = 0;
+ }
+
+ /*
+ * 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;
+
+ /*
+ * Safe fdt location is at 127 MiB.
+ * On the sandbox convert from the sandbox address space.
+ */
+ efiret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
+ EFI_ACPI_RECLAIM_MEMORY, fdt_pages,
+ &new_fdt_addr, "FDT");
+ if (efiret != EFI_SUCCESS) {
+ pr_err("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 = new_fdt;
+
+done:
+ return efiret;
+}
+
+/**
+ * efi_install_fdt() - install device tree
+ *
+ * If fdt is not NULL, 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
+ * 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
+ */
+static 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.
+ */
+ efi_status_t ret;
+
+ /* Install device tree */
+ if (fdt_check_header(fdt)) {
+ pr_err("invalid device tree\n");
+ return EFI_LOAD_ERROR;
+ }
+
+ /* Prepare device tree for payload */
+ ret = copy_fdt(&fdt);
+ if (ret) {
+ pr_err("out of memory\n");
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ /* Install device tree as UEFI table */
+ ret = efi_install_configuration_table(&efi_fdt_guid, fdt);
+ if (ret != EFI_SUCCESS) {
+ pr_err("failed to install device tree\n");
+ return ret;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_install_initrd() - install initrd
+ *
+ * Install the initrd located at @initrd using the EFI_LOAD_FILE2
+ * protocol.
+ *
+ * @initrd: address of initrd or NULL if none is provided
+ * @initrd_sz: size of initrd
+ * Return: status code
+ */
+static efi_status_t efi_install_initrd(struct image_data *data,
+ struct resource *source)
+{
+ const struct resource *initrd_res;
+ unsigned long initrd_start;
+
+ if (!IS_ENABLED(CONFIG_BOOTM_INITRD))
+ return EFI_SUCCESS;
+
+ if (UIMAGE_IS_ADDRESS_VALID(data->initrd_address))
+ initrd_start = data->initrd_address;
+ else
+ initrd_start = EFI_PAGE_ALIGN(source->end + 1);
+
+ initrd_res = bootm_load_initrd(data, initrd_start);
+ if (IS_ERR(initrd_res))
+ return PTR_ERR(initrd_res);
+ if (initrd_res)
+ efi_initrd_register((void *)initrd_res->start,
+ resource_size(initrd_res));
+
+ return EFI_SUCCESS;
+}
+
+static int efi_loader_bootm(struct image_data *data)
+{
+ resource_size_t start, end;
+ void *load_option = NULL;
+ u32 load_option_size = 0;
+ efi_handle_t handle;
+ struct efi_device_path *file_path = NULL;
+ struct resource *source;
+ struct efi_event *evt;
+ size_t exit_data_size = 0;
+ u16 *exit_data = NULL;
+ efi_status_t efiret;
+ int ret = 0;
+ void *fdt;
+ int flags = 0;
+
+ memory_bank_first_find_space(&start, &end);
+ data->os_address = start;
+
+ source = file_to_sdram(data->os_file, data->os_address, MEMTYPE_LOADER_CODE);
+ if (!source)
+ return -EINVAL;
+
+ if (filetype_is_linux_efi_image(data->os_type)) {
+ const char *options;
+
+ options = linux_bootargs_get();
+ if (options) {
+ load_option = xstrdup_char_to_wchar(options);
+ load_option_size = (strlen(options) + 1) * sizeof(wchar_t);
+ }
+ }
+
+ file_path = efi_dp_from_file(AT_FDCWD, data->os_file);
+
+ pr_info("Loading %pD\n", file_path);
+
+ /* Initialize EFI drivers */
+ efiret = efi_init_obj_list();
+ if (efiret) {
+ pr_err("Cannot initialize UEFI sub-system: %pe\n",
+ ERR_PTR(-efi_errno(ret)));
+ goto out;
+ }
+
+ ret = -EINVAL;
+
+ fdt = bootm_get_devicetree(data);
+ if (IS_ERR(fdt))
+ return PTR_ERR(fdt);
+ if (fdt) {
+ ret = efi_install_fdt(fdt);
+ if (ret)
+ return ret;
+ }
+
+ efiret = efi_install_initrd(data, source);
+ if(efiret != EFI_SUCCESS)
+ goto out;
+
+ if (data->verbose >= 1)
+ flags |= EFI_VERBOSE_RUN;
+ if (data->dryrun)
+ flags |= EFI_DRYRUN;
+
+ efiret = efiloader_load_image(false, efi_root, file_path,
+ (void *)source->start,
+ resource_size(source), &handle);
+ if (efiret != EFI_SUCCESS) {
+ pr_err("Loading image failed\n");
+ goto out;
+ }
+
+ efiret = efi_set_load_options(handle, load_option_size, load_option);
+ if (efiret)
+ goto out;
+
+ /* On ARM, PBL should have already moved us into EL2 here, so
+ * no need to switch modes
+ */
+
+ /*
+ * 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) {
+ pr_err("failed to set watchdog timer\n");
+ goto out;
+ }
+
+ /* Call our payload! */
+ ret = __efi_start_image(handle, &exit_data_size, &exit_data, flags);
+ if (ret != EFI_SUCCESS) {
+ pr_err("## Application failed, r = %lu\n",
+ ret & ~EFI_ERROR_MASK);
+ if (exit_data) {
+ pr_err("## %ls\n", exit_data);
+ efi_free_pool(exit_data);
+ }
+ }
+
+ /* Notify EFI_EVENT_GROUP_RETURN_TO_EFIBOOTMGR event group. */
+ list_for_each_entry(evt, &efi_events, link) {
+ if (evt->group &&
+ !efi_guidcmp(*evt->group,
+ efi_guid_event_group_return_to_efibootmgr)) {
+ efi_signal_event(evt);
+ systab.boottime->close_event(evt);
+ break;
+ }
+ }
+
+ /* Control is returned to us, disable EFI watchdog */
+ efi_set_watchdog(0);
+
+ return ret;
+
+out:
+ efi_initrd_unregister();
+ efi_install_configuration_table(&efi_fdt_guid, NULL);
+ efi_free_pool(file_path);
+ free(load_option);
+ release_sdram_region(source);
+
+ return ret ?: -efi_errno(efiret);
+}
+
+static struct image_handler riscv_linux_efi_handler = {
+ .name = "RISC-V Linux/EFI image",
+ .bootm = efi_loader_bootm,
+ .check_image = bootm_efi_check_image,
+ .filetype = filetype_riscv_efi_linux_image,
+};
+
+static struct image_handler aarch64_linux_efi_handler = {
+ .name = "ARM aarch64 Linux/EFI image",
+ .bootm = efi_loader_bootm,
+ .check_image = bootm_efi_check_image,
+ .filetype = filetype_arm64_efi_linux_image,
+};
+
+static struct image_handler arm32_linux_efi_handler = {
+ .name = "ARM arm32 Linux/EFI image",
+ .bootm = efi_loader_bootm,
+ .check_image = bootm_efi_check_image,
+ .filetype = filetype_arm_efi_zimage,
+};
+
+static struct image_handler efi_image_loader = {
+ .name = "EFI Application",
+ .bootm = efi_loader_bootm,
+ .filetype = filetype_exe,
+};
+
+static int efiloader_bootm_register(void)
+{
+ if (IS_ENABLED(CONFIG_ARM32))
+ register_image_handler(&arm32_linux_efi_handler);
+ if (IS_ENABLED(CONFIG_ARM64))
+ register_image_handler(&aarch64_linux_efi_handler);
+ if (IS_ENABLED(CONFIG_RISCV))
+ register_image_handler(&riscv_linux_efi_handler);
+ return register_image_handler(&efi_image_loader);
+}
+late_initcall(efiloader_bootm_register);
--
2.47.3
More information about the barebox
mailing list