From: Chali Anis <[email protected]>

The handover boot support is Linux-specific and can be disabled
with CONFIG_EFI_HANDOVER_PROTOCOL. In preparation for adding a compliant
boot method, move the handover-specific parts into their own file.

Signed-off-by: Ahmad Fatoum <[email protected]>
Signed-off-by: Chali Anis <[email protected]>
---
 efi/payload/Kconfig        |   5 +
 efi/payload/Makefile       |   1 +
 efi/payload/handover.c     | 154 +++++++++++++++++++++++++++++
 efi/payload/image.c        | 192 +++++--------------------------------
 efi/payload/image.h        |  14 +++
 efi/payload/setup_header.h |  63 ++++++++++++
 6 files changed, 260 insertions(+), 169 deletions(-)
 create mode 100644 efi/payload/handover.c
 create mode 100644 efi/payload/image.h
 create mode 100644 efi/payload/setup_header.h

diff --git a/efi/payload/Kconfig b/efi/payload/Kconfig
index a403acc81a93..d0a46704a6b9 100644
--- a/efi/payload/Kconfig
+++ b/efi/payload/Kconfig
@@ -25,4 +25,9 @@ if EFI_PAYLOAD
 config EFI_STUB
        def_bool HAVE_EFI_STUB
 
+config EFI_HANDOVER_PROTOCOL
+       bool "EFI Handover protocol"
+       default y
+       depends on X86
+
 endif
diff --git a/efi/payload/Makefile b/efi/payload/Makefile
index 1250535302d3..d8b577bf3e23 100644
--- a/efi/payload/Makefile
+++ b/efi/payload/Makefile
@@ -2,6 +2,7 @@
 
 obj-y += init.o
 obj-y += image.o
+obj-$(CONFIG_EFI_HANDOVER_PROTOCOL) += handover.o
 obj-y += efi-initrd.o
 obj-$(CONFIG_OFTREE) += fdt.o
 bbenv-y += env-efi
diff --git a/efi/payload/handover.c b/efi/payload/handover.c
new file mode 100644
index 000000000000..82f4cb4f5286
--- /dev/null
+++ b/efi/payload/handover.c
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * handover.c - legacy x86 EFI handover protocol
+ *
+ * Copyright (c) 2014 Sascha Hauer <[email protected]>, Pengutronix
+ */
+
+#define pr_fmt(fmt) "efi-handover: " 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 <efi/efi-payload.h>
+#include <efi/efi-device.h>
+
+#include "image.h"
+#include "setup_header.h"
+
+static int efi_do_execute_image(enum filetype filetype, const char *file)
+{
+       efi_handle_t handle;
+       struct efi_loaded_image *loaded_image;
+       int ret;
+
+       ret = efi_load_image(file, &loaded_image, &handle);
+       if (ret)
+               return ret;
+
+       return efi_execute_image(handle, loaded_image, filetype);
+}
+
+typedef void(*handover_fn)(void *image, struct efi_system_table *table,
+                          struct x86_setup_header *header);
+
+static inline void linux_efi_handover(efi_handle_t handle,
+                                     struct x86_setup_header *header)
+{
+       handover_fn handover;
+       uintptr_t addr;
+
+       addr = header->code32_start + header->handover_offset;
+       if (IS_ENABLED(CONFIG_X86_64))
+               addr += 512;
+
+       handover = efi_phys_to_virt(addr);
+       handover(handle, efi_sys_table, header);
+}
+
+static int do_bootm_efi(struct image_data *data)
+{
+       void *tmp;
+       void *initrd = NULL;
+       size_t size;
+       efi_handle_t handle;
+       int ret;
+       const char *options;
+       struct efi_loaded_image *loaded_image;
+       struct x86_setup_header *image_header, *boot_header;
+
+       ret = efi_load_image(data->os_file, &loaded_image, &handle);
+       if (ret)
+               return ret;
+
+       image_header = (struct x86_setup_header *)loaded_image->image_base;
+
+       if (!is_x86_setup_header(image_header) ||
+           image_header->version < 0x20b ||
+           !image_header->relocatable_kernel) {
+               pr_err("Not a valid kernel image!\n");
+               BS->unload_image(handle);
+               return -EINVAL;
+       }
+
+       boot_header = xmalloc(0x4000);
+       memset(boot_header, 0, 0x4000);
+       memcpy(boot_header, image_header, sizeof(*image_header));
+
+       /* Refer to Linux kernel commit a27e292b8a54
+        * ("Documentation/x86/boot: Reserve type_of_loader=13 for barebox")
+        */
+       boot_header->type_of_loader = 0x13;
+
+       if (data->initrd_file) {
+               tmp = read_file(data->initrd_file, &size);
+               initrd = xmemalign(PAGE_SIZE, PAGE_ALIGN(size));
+               memcpy(initrd, tmp, size);
+               memset(initrd + size, 0, PAGE_ALIGN(size) - size);
+               free(tmp);
+               boot_header->ramdisk_image = efi_virt_to_phys(initrd);
+               boot_header->ramdisk_size = PAGE_ALIGN(size);
+       }
+
+       options = linux_bootargs_get();
+       if (options) {
+               boot_header->cmd_line_ptr = efi_virt_to_phys(options);
+               boot_header->cmdline_size = strlen(options);
+       }
+
+       boot_header->code32_start = efi_virt_to_phys(loaded_image->image_base +
+                       (image_header->setup_sects+1) * 512);
+
+       if (bootm_verbose(data)) {
+               printf("\nStarting kernel at 0x%p", loaded_image->image_base);
+               if (data->initrd_file)
+                       printf(", initrd at 0x%08x",
+                              boot_header->ramdisk_image);
+               printf("...\n");
+       }
+
+       if (data->dryrun) {
+               BS->unload_image(handle);
+               free(boot_header);
+               free(initrd);
+               return 0;
+       }
+
+       efi_set_variable_usec("LoaderTimeExecUSec", &efi_systemd_vendor_guid,
+                             ktime_to_us(ktime_get()));
+
+       shutdown_barebox();
+       linux_efi_handover(handle, boot_header);
+
+       return 0;
+}
+
+static struct image_handler efi_handle_tr = {
+       .name = "EFI Application",
+       .bootm = do_bootm_efi,
+       .filetype = filetype_exe,
+};
+
+static int efi_register_handover_handler(void)
+{
+       register_image_handler(&efi_handle_tr);
+       return 0;
+}
+late_efi_initcall(efi_register_handover_handler);
diff --git a/efi/payload/image.c b/efi/payload/image.c
index 33c5e18dac27..ae7678188d84 100644
--- a/efi/payload/image.c
+++ b/efi/payload/image.c
@@ -5,13 +5,12 @@
  * Copyright (c) 2014 Sascha Hauer <[email protected]>, Pengutronix
  */
 
+#define pr_fmt(fmt) "efi-image: " 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>
@@ -19,7 +18,7 @@
 #include <malloc.h>
 #include <string.h>
 #include <linux/err.h>
-#include <boot.h>
+#include <bootargs.h>
 #include <bootm.h>
 #include <fs.h>
 #include <libfile.h>
@@ -28,54 +27,8 @@
 #include <efi/efi-payload.h>
 #include <efi/efi-device.h>
 
-struct linux_kernel_header {
-       /* first sector of the image */
-       uint8_t code1[0x0020];
-       uint16_t cl_magic;              /**< Magic number 0xA33F */
-       uint16_t cl_offset;             /**< The offset of command line */
-       uint8_t code2[0x01F1 - 0x0020 - 2 - 2];
-       uint8_t setup_sects;            /**< The size of the setup in sectors */
-       uint16_t root_flags;            /**< If the root is mounted readonly */
-       uint16_t syssize;               /**< obsolete */
-       uint16_t swap_dev;              /**< obsolete */
-       uint16_t ram_size;              /**< obsolete */
-       uint16_t vid_mode;              /**< Video mode control */
-       uint16_t root_dev;              /**< Default root device number */
-       uint16_t boot_flag;             /**< 0xAA55 magic number */
-
-       /* second sector of the image */
-       uint16_t jump;                  /**< Jump instruction (this is code!) */
-       uint32_t header;                /**< Magic signature "HdrS" */
-       uint16_t version;               /**< Boot protocol version supported */
-       uint32_t realmode_swtch;        /**< Boot loader hook */
-       uint16_t start_sys;             /**< The load-low segment (obsolete) */
-       uint16_t kernel_version;        /**< Points to kernel version string */
-       uint8_t type_of_loader;         /**< Boot loader identifier */
-       uint8_t loadflags;              /**< Boot protocol option flags */
-       uint16_t setup_move_size;       /**< Move to high memory size */
-       uint32_t code32_start;          /**< Boot loader hook */
-       uint32_t ramdisk_image;         /**< initrd load address */
-       uint32_t ramdisk_size;          /**< initrd size */
-       uint32_t bootsect_kludge;       /**< obsolete */
-       uint16_t heap_end_ptr;          /**< Free memory after setup end */
-       uint8_t ext_loader_ver;         /**< boot loader's extension of the 
version number */
-       uint8_t ext_loader_type;        /**< boot loader's extension of its 
type */
-       uint32_t cmd_line_ptr;          /**< Points to the kernel command line 
*/
-       uint32_t initrd_addr_max;       /**< Highest address for initrd */
-       uint32_t kernel_alignment;      /**< Alignment unit required by the 
kernel */
-       uint8_t relocatable_kernel;     /** */
-       uint8_t min_alignment;          /** */
-       uint16_t xloadflags;            /** */
-       uint32_t cmdline_size;          /** */
-       uint32_t hardware_subarch;      /** */
-       uint64_t hardware_subarch_data; /** */
-       uint32_t payload_offset;        /** */
-       uint32_t payload_length;        /** */
-       uint64_t setup_data;            /** */
-       uint64_t pref_address;          /** */
-       uint32_t init_size;             /** */
-       uint32_t handover_offset;       /** */
-} __attribute__ ((packed));
+#include "image.h"
+#include "setup_header.h"
 
 static void *efi_read_file(const char *file, size_t *size)
 {
@@ -85,10 +38,6 @@ static void *efi_read_file(const char *file, size_t *size)
        char *buf;
        ssize_t ret;
 
-       buf = read_file(file, size);
-       if (buf || errno != ENOMEM)
-               return buf;
-
        ret = stat(file, &s);
        if (ret)
                return NULL;
@@ -122,8 +71,8 @@ static void efi_free_file(void *_mem, size_t size)
                BS->free_pages(mem, DIV_ROUND_UP(size, EFI_PAGE_SIZE));
 }
 
-static int efi_load_image(const char *file, struct efi_loaded_image 
**loaded_image,
-               efi_handle_t *h)
+int efi_load_image(const char *file, struct efi_loaded_image **loaded_image,
+                  efi_handle_t *h)
 {
        void *exe;
        size_t size;
@@ -158,10 +107,7 @@ static int efi_load_image(const char *file, struct 
efi_loaded_image **loaded_ima
 
 static bool is_linux_image(enum filetype filetype, const void *base)
 {
-       const struct linux_kernel_header *hdr = base;
-
-       if (IS_ENABLED(CONFIG_X86) &&
-           hdr->boot_flag == 0xAA55 && hdr->header == 0x53726448)
+       if (IS_ENABLED(CONFIG_X86) && is_x86_setup_header(base))
                return true;
 
        if (IS_ENABLED(CONFIG_ARM64) &&
@@ -171,18 +117,13 @@ static bool is_linux_image(enum filetype filetype, const 
void *base)
        return false;
 }
 
-static int efi_execute_image(enum filetype filetype, const char *file)
+int efi_execute_image(efi_handle_t handle,
+                     struct efi_loaded_image *loaded_image,
+                     enum filetype filetype)
 {
-       efi_handle_t handle;
-       struct efi_loaded_image *loaded_image;
        efi_status_t efiret;
        const char *options;
        bool is_driver;
-       int ret;
-
-       ret = efi_load_image(file, &loaded_image, &handle);
-       if (ret)
-               return ret;
 
        is_driver = (loaded_image->image_code_type == EFI_BOOT_SERVICES_CODE) ||
                (loaded_image->image_code_type == EFI_RUNTIME_SERVICES_CODE);
@@ -216,110 +157,17 @@ static int efi_execute_image(enum filetype filetype, 
const char *file)
        return -efi_errno(efiret);
 }
 
-typedef void(*handover_fn)(void *image, struct efi_system_table *table,
-               struct linux_kernel_header *header);
-
-static inline void linux_efi_handover(efi_handle_t handle,
-               struct linux_kernel_header *header)
-{
-       handover_fn handover;
-       uintptr_t addr;
-
-       addr = header->code32_start + header->handover_offset;
-       if (IS_ENABLED(CONFIG_X86_64))
-               addr += 512;
-
-       handover = efi_phys_to_virt(addr);
-       handover(handle, efi_sys_table, header);
-}
-
-static int do_bootm_efi(struct image_data *data)
+static int efi_execute(struct binfmt_hook *b, char *file, int argc, char 
**argv)
 {
-       void *tmp;
-       void *initrd = NULL;
-       size_t size;
+       struct efi_loaded_image *loaded_image;
        efi_handle_t handle;
        int ret;
-       const char *options;
-       struct efi_loaded_image *loaded_image;
-       struct linux_kernel_header *image_header, *boot_header;
 
-       ret = efi_load_image(data->os_file, &loaded_image, &handle);
+       ret = efi_load_image(file, &loaded_image, &handle);
        if (ret)
                return ret;
 
-       image_header = (struct linux_kernel_header *)loaded_image->image_base;
-
-       if (image_header->boot_flag != 0xAA55 ||
-           image_header->header != 0x53726448 ||
-           image_header->version < 0x20b ||
-           !image_header->relocatable_kernel) {
-               pr_err("Not a valid kernel image!\n");
-               BS->unload_image(handle);
-               return -EINVAL;
-       }
-
-       boot_header = xmalloc(0x4000);
-       memset(boot_header, 0, 0x4000);
-       memcpy(boot_header, image_header, sizeof(*image_header));
-
-       /* Refer to Linux kernel commit a27e292b8a54
-        * ("Documentation/x86/boot: Reserve type_of_loader=13 for barebox")
-        */
-       boot_header->type_of_loader = 0x13;
-
-       if (data->initrd_file) {
-               tmp = read_file(data->initrd_file, &size);
-               initrd = xmemalign(PAGE_SIZE, PAGE_ALIGN(size));
-               memcpy(initrd, tmp, size);
-               memset(initrd + size, 0, PAGE_ALIGN(size) - size);
-               free(tmp);
-               boot_header->ramdisk_image = efi_virt_to_phys(initrd);
-               boot_header->ramdisk_size = PAGE_ALIGN(size);
-       }
-
-       options = linux_bootargs_get();
-       if (options) {
-               boot_header->cmd_line_ptr = efi_virt_to_phys(options);
-               boot_header->cmdline_size = strlen(options);
-       }
-
-       boot_header->code32_start = efi_virt_to_phys(loaded_image->image_base +
-                       (image_header->setup_sects+1) * 512);
-
-       if (bootm_verbose(data)) {
-               printf("\nStarting kernel at 0x%p", loaded_image->image_base);
-               if (data->initrd_file)
-                       printf(", initrd at 0x%08x",
-                              boot_header->ramdisk_image);
-               printf("...\n");
-       }
-
-       if (data->dryrun) {
-               BS->unload_image(handle);
-               free(boot_header);
-               free(initrd);
-               return 0;
-       }
-
-       efi_set_variable_usec("LoaderTimeExecUSec", &efi_systemd_vendor_guid,
-                             ktime_to_us(ktime_get()));
-
-       shutdown_barebox();
-       linux_efi_handover(handle, boot_header);
-
-       return 0;
-}
-
-static struct image_handler efi_handle_tr = {
-       .name = "EFI Application",
-       .bootm = do_bootm_efi,
-       .filetype = filetype_exe,
-};
-
-static int efi_execute(struct binfmt_hook *b, char *file, int argc, char 
**argv)
-{
-       return efi_execute_image(b->type, file);
+       return efi_execute_image(handle, loaded_image, b->type);
 }
 
 static struct binfmt_hook binfmt_efi_hook = {
@@ -352,13 +200,19 @@ static struct binfmt_hook binfmt_arm64_efi_hook = {
        .hook = efi_execute,
 };
 
+static struct binfmt_hook binfmt_x86_efi_hook = {
+       .type = filetype_x86_linux_image,
+       .hook = efi_execute,
+};
+
 static int efi_register_image_handler(void)
 {
-       register_image_handler(&efi_handle_tr);
        binfmt_register(&binfmt_efi_hook);
 
-       if (IS_ENABLED(CONFIG_X86))
+       if (IS_ENABLED(CONFIG_X86)) {
                register_image_handler(&non_efi_handle_linux_x86);
+               binfmt_register(&binfmt_x86_efi_hook);
+       }
 
        if (IS_ENABLED(CONFIG_ARM64))
                binfmt_register(&binfmt_arm64_efi_hook);
diff --git a/efi/payload/image.h b/efi/payload/image.h
new file mode 100644
index 000000000000..673b21db839a
--- /dev/null
+++ b/efi/payload/image.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef __EFI_PAYLOAD_IMAGE_H__
+#define __EFI_PAYLOAD_IMAGE_H__
+
+#include <efi/types.h>
+
+int efi_load_image(const char *file, struct efi_loaded_image **loaded_image,
+                  efi_handle_t *h);
+
+int efi_execute_image(efi_handle_t handle,
+                     struct efi_loaded_image *loaded_image,
+                     enum filetype filetype);
+
+#endif
diff --git a/efi/payload/setup_header.h b/efi/payload/setup_header.h
new file mode 100644
index 000000000000..4f094b844415
--- /dev/null
+++ b/efi/payload/setup_header.h
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef __EFI_PAYLOAD_SETUP_HEADER_H__
+#define __EFI_PAYLOAD_SETUP_HEADER_H__
+
+#include <linux/types.h>
+
+struct x86_setup_header {
+       /* first sector of the image */
+       uint8_t code1[0x0020];
+       uint16_t cl_magic;              /**< Magic number 0xA33F */
+       uint16_t cl_offset;             /**< The offset of command line */
+       uint8_t code2[0x01F1 - 0x0020 - 2 - 2];
+       uint8_t setup_sects;            /**< The size of the setup in sectors */
+       uint16_t root_flags;            /**< If the root is mounted readonly */
+       uint16_t syssize;               /**< obsolete */
+       uint16_t swap_dev;              /**< obsolete */
+       uint16_t ram_size;              /**< obsolete */
+       uint16_t vid_mode;              /**< Video mode control */
+       uint16_t root_dev;              /**< Default root device number */
+       uint16_t boot_flag;             /**< 0xAA55 magic number */
+
+       /* second sector of the image */
+       uint16_t jump;                  /**< Jump instruction (this is code!) */
+       uint32_t header;                /**< Magic signature "HdrS" */
+       uint16_t version;               /**< Boot protocol version supported */
+       uint32_t realmode_swtch;        /**< Boot loader hook */
+       uint16_t start_sys;             /**< The load-low segment (obsolete) */
+       uint16_t kernel_version;        /**< Points to kernel version string */
+       uint8_t type_of_loader;         /**< Boot loader identifier */
+       uint8_t loadflags;              /**< Boot protocol option flags */
+       uint16_t setup_move_size;       /**< Move to high memory size */
+       uint32_t code32_start;          /**< Boot loader hook */
+       uint32_t ramdisk_image;         /**< initrd load address */
+       uint32_t ramdisk_size;          /**< initrd size */
+       uint32_t bootsect_kludge;       /**< obsolete */
+       uint16_t heap_end_ptr;          /**< Free memory after setup end */
+       uint8_t ext_loader_ver;         /**< boot loader's extension of the 
version number */
+       uint8_t ext_loader_type;        /**< boot loader's extension of its 
type */
+       uint32_t cmd_line_ptr;          /**< Points to the kernel command line 
*/
+       uint32_t initrd_addr_max;       /**< Highest address for initrd */
+       uint32_t kernel_alignment;      /**< Alignment unit required by the 
kernel */
+       uint8_t relocatable_kernel;     /** */
+       uint8_t min_alignment;          /** */
+       uint16_t xloadflags;            /** */
+       uint32_t cmdline_size;          /** */
+       uint32_t hardware_subarch;      /** */
+       uint64_t hardware_subarch_data; /** */
+       uint32_t payload_offset;        /** */
+       uint32_t payload_length;        /** */
+       uint64_t setup_data;            /** */
+       uint64_t pref_address;          /** */
+       uint32_t init_size;             /** */
+       uint32_t handover_offset;       /** */
+} __attribute__ ((packed));
+
+static inline bool is_x86_setup_header(const void *base)
+{
+       const struct x86_setup_header *hdr = base;
+
+       return hdr->boot_flag == 0xAA55 && hdr->header == 0x53726448;
+}
+
+#endif
-- 
2.34.1


Reply via email to