From: Chali Anis <[email protected]> Introduce support for booting Linux kernels with an EFI stub from the barebox EFI payload. This includes:
- Loading the kernel image via EFI LoadImage/StartImage - Installing the FDT into the EFI configuration table unless EFI_FDT_FORCE is enabled - Providing the initrd either through the Initrd Media GUID or the initrd media protocol - Registering image handlers for EFI applications, x86/ARM64 EFI Linux kernels, and a fallback handler for non-EFI x86 Linux images - Adding Kconfig options to control FDT forcing and initrd installation This enables barebox EFI payloads to directly boot Linux kernels with EFI stubs in a way compatible with the Linux EFI boot flow. Signed-off-by: Chali Anis <[email protected]> --- efi/payload/Kconfig | 6 +- efi/payload/Makefile | 1 + efi/payload/bootm.c | 219 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 225 insertions(+), 1 deletion(-) create mode 100644 efi/payload/bootm.c diff --git a/efi/payload/Kconfig b/efi/payload/Kconfig index d0a46704a6b9..e3a6a72d6a1c 100644 --- a/efi/payload/Kconfig +++ b/efi/payload/Kconfig @@ -27,7 +27,11 @@ config EFI_STUB config EFI_HANDOVER_PROTOCOL bool "EFI Handover protocol" - default y + depends on !EFI_PAYLOAD_BOOTM depends on X86 +config EFI_PAYLOAD_BOOTM + bool "EFI bootm protocol" + default !X86 + endif diff --git a/efi/payload/Makefile b/efi/payload/Makefile index d8b577bf3e23..083728c53cb4 100644 --- a/efi/payload/Makefile +++ b/efi/payload/Makefile @@ -3,6 +3,7 @@ obj-y += init.o obj-y += image.o obj-$(CONFIG_EFI_HANDOVER_PROTOCOL) += handover.o +obj-$(CONFIG_EFI_PAYLOAD_BOOTM) += bootm.o obj-y += efi-initrd.o obj-$(CONFIG_OFTREE) += fdt.o bbenv-y += env-efi diff --git a/efi/payload/bootm.c b/efi/payload/bootm.c new file mode 100644 index 000000000000..d0e995be5d7c --- /dev/null +++ b/efi/payload/bootm.c @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * image.c - barebox EFI payload support + * + * Copyright (c) 2014 Sascha Hauer <[email protected]>, Pengutronix + */ + +#define pr_fmt(fmt) "efi-bootm: " fmt + +#include <clock.h> +#include <common.h> +#include <linux/sizes.h> +#include <linux/ktime.h> +#include <memory.h> +#include <command.h> +#include <magicvar.h> +#include <init.h> +#include <driver.h> +#include <io.h> +#include <efi.h> +#include <malloc.h> +#include <string.h> +#include <linux/err.h> +#include <boot.h> +#include <bootm.h> +#include <fs.h> +#include <libfile.h> +#include <binfmt.h> +#include <wchar.h> +#include <image-fit.h> +#include <efi/efi-payload.h> +#include <efi/efi-device.h> + +#include "image.h" +#include "setup_header.h" + +static int efi_load_os(struct image_data *data, + struct efi_loaded_image **loaded_image, + efi_handle_t *handle) +{ + return efi_load_image(data->os_file, loaded_image, handle); +} + +static int efi_load_ramdisk(struct image_data *data, void **initrd) +{ + unsigned long initrd_size; + void *initrd_mem; + int ret; + + if (!data->initrd_file) + return 0; + + pr_info("Loading ramdisk from '%s'\n", data->initrd_file); + + initrd_mem = read_file(data->initrd_file, &initrd_size); + if (!initrd_mem) { + ret = -errno; + pr_err("Failed to read initrd from file '%s': %m\n", + data->initrd_file); + return ret; + } + + ret = efi_initrd_register(initrd_mem, initrd_size); + if (ret) { + pr_err("Failed to register initrd: %pe\n", ERR_PTR(ret)); + goto free_mem; + } + + *initrd = initrd_mem; + + return 0; + +free_mem: + free(initrd); + + return ret; +} + +static int efi_load_fdt(struct image_data *data, void **fdt) +{ + efi_physical_addr_t mem; + efi_status_t efiret; + void *of_tree, *vmem; + unsigned long of_size; + int ret; + + if (!data->oftree_file) + return 0; + + pr_info("Loading devicetree from '%s'\n", data->oftree_file); + + of_tree = read_file(data->oftree_file, &of_size); + if (!of_tree) { + ret = -errno; + pr_err("Failed to read oftree: %m\n"); + return ret; + } + + efiret = BS->allocate_pages(EFI_ALLOCATE_ANY_PAGES, + EFI_ACPI_RECLAIM_MEMORY, + DIV_ROUND_UP(SZ_2M, EFI_PAGE_SIZE), &mem); + if (EFI_ERROR(efiret)) { + pr_err("Failed to allocate pages for FDT: %s\n", efi_strerror(efiret)); + goto free_mem; + } + + vmem = efi_phys_to_virt(mem); + memcpy(vmem, of_tree, of_size); + + efiret = BS->install_configuration_table(&efi_fdt_guid, vmem); + if (EFI_ERROR(efiret)) { + pr_err("Failed to install FDT: %s\n", efi_strerror(efiret)); + goto free_efi_mem; + } + + *fdt = vmem; + return 0; + +free_efi_mem: + BS->free_pages(mem, DIV_ROUND_UP(SZ_2M, EFI_PAGE_SIZE)); +free_mem: + free(of_tree); + return -efi_errno(efiret); +} + +static void efi_unload_fdt(void *fdt) +{ + if (!fdt) + return; + + BS->install_configuration_table(&efi_fdt_guid, NULL); + BS->free_pages(efi_virt_to_phys(fdt), SZ_2M); +} + +static int do_bootm_efi_stub(struct image_data *data) +{ + struct efi_loaded_image *loaded_image; + void *fdt = NULL, *initrd = NULL; + efi_handle_t handle; + enum filetype type; + int ret; + + ret = efi_load_os(data, &loaded_image, &handle); + if (ret) + return ret; + + ret = efi_load_fdt(data, &fdt); + if (ret) + goto unload_os; + + ret = efi_load_ramdisk(data, &initrd); + if (ret) + goto unload_oftree; + + type = file_detect_type(loaded_image->image_base, PAGE_SIZE); + ret = efi_execute_image(handle, loaded_image, type); + if (ret) + goto unload_ramdisk; + + return 0; + +unload_ramdisk: + if (initrd) + efi_initrd_unregister(); +unload_oftree: + efi_unload_fdt(fdt); +unload_os: + BS->unload_image(handle); + + return ret; +} + +static int efi_app_execute(struct image_data *data) +{ + struct efi_loaded_image *loaded_image; + efi_handle_t handle; + enum filetype type; + int ret; + + ret = efi_load_image(data->os_file, &loaded_image, &handle); + if (ret) + return ret; + + type = file_detect_type(loaded_image->image_base, PAGE_SIZE); + + return efi_execute_image(handle, loaded_image, type); +} + +static struct image_handler efi_app_handle_tr = { + .name = "EFI Application", + .bootm = efi_app_execute, + .filetype = filetype_exe, +}; + +static struct image_handler efi_x86_linux_handle_tr = { + .name = "EFI X86 Linux kernel", + .bootm = do_bootm_efi_stub, + .filetype = filetype_x86_linux_image, +}; + +static struct image_handler efi_arm64_handle_tr = { + .name = "EFI ARM64 Linux kernel", + .bootm = do_bootm_efi_stub, + .filetype = filetype_arm64_efi_linux_image, +}; + +static int efi_register_bootm_handler(void) +{ + register_image_handler(&efi_app_handle_tr); + + if (IS_ENABLED(CONFIG_X86)) + register_image_handler(&efi_x86_linux_handle_tr); + + if (IS_ENABLED(CONFIG_ARM64)) + register_image_handler(&efi_arm64_handle_tr); + + return 0; +} +late_efi_initcall(efi_register_bootm_handler); -- 2.34.1
