]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
[sd-stub] Add support for aarch64 booting via pe-entry point 20983/head
authorMax Resch <resch.max@gmail.com>
Mon, 11 Oct 2021 13:15:30 +0000 (15:15 +0200)
committerMax Resch <resch.max@gmail.com>
Wed, 13 Oct 2021 16:20:32 +0000 (18:20 +0200)
src/boot/efi/linux.c
src/boot/efi/linux.h
src/boot/efi/linux_x86.c [new file with mode: 0644]
src/boot/efi/meson.build
src/boot/efi/pe.c
src/boot/efi/pe.h
src/boot/efi/stub.c

index 2c27ed50300cd55fd9154baf3257ad4d3f542941..98f1db24e0eea127a33382aeed147c76695744cb 100644 (file)
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+/*
+ * Generic Linux boot protocol using the EFI/PE entry point of the kernel. Passes
+ * initrd with the LINUX_INITRD_MEDIA_GUID DevicePath and cmdline with
+ * EFI LoadedImageProtocol.
+ *
+ * This method works for Linux 5.8 and newer on ARM/Aarch64, x86/x68_64 and RISC-V.
+ */
+
 #include <efi.h>
 #include <efilib.h>
 
-#include "linux.h"
 #include "initrd.h"
+#include "linux.h"
+#include "pe.h"
 #include "util.h"
 
-#ifdef __i386__
-#define __regparm0__ __attribute__((regparm(0)))
-#else
-#define __regparm0__
-#endif
+static EFI_LOADED_IMAGE * loaded_image_free(EFI_LOADED_IMAGE *img) {
+        if (!img)
+                return NULL;
+        mfree(img->LoadOptions);
+        return mfree(img);
+}
+
+static EFI_STATUS loaded_image_register(
+                const CHAR8 *cmdline, UINTN cmdline_len,
+                const VOID *linux_buffer, UINTN linux_length,
+                EFI_HANDLE *ret_image) {
+
+        EFI_LOADED_IMAGE *loaded_image = NULL;
+        EFI_STATUS err;
+
+        assert(cmdline || cmdline_len > 0);
+        assert(linux_buffer && linux_length > 0);
+        assert(ret_image);
 
-typedef VOID(*handover_f)(VOID *image, EFI_SYSTEM_TABLE *table, struct boot_params *params) __regparm0__;
+        /* create and install new LoadedImage Protocol */
+        loaded_image = AllocatePool(sizeof(EFI_LOADED_IMAGE));
+        if (!loaded_image)
+                return EFI_OUT_OF_RESOURCES;
 
-static VOID linux_efi_handover(EFI_HANDLE image, struct boot_params *params) {
-        handover_f handover;
-        UINTN start = (UINTN)params->hdr.code32_start;
+        /* provide the image base address and size */
+        *loaded_image = (EFI_LOADED_IMAGE) {
+                .ImageBase = (VOID *) linux_buffer,
+                .ImageSize = linux_length
+        };
 
-        assert(params);
+        /* if a cmdline is set convert it to UTF16 */
+        if (cmdline) {
+                loaded_image->LoadOptions = stra_to_str(cmdline);
+                if (!loaded_image->LoadOptions) {
+                        loaded_image = loaded_image_free(loaded_image);
+                        return EFI_OUT_OF_RESOURCES;
+                }
+                loaded_image->LoadOptionsSize = StrSize(loaded_image->LoadOptions);
+        }
 
-#ifdef __x86_64__
-        asm volatile ("cli");
-        start += 512;
-#endif
-        handover = (handover_f)(start + params->hdr.handover_offset);
-        handover(image, ST, params);
+        /* install a new LoadedImage protocol. ret_handle is a new image handle */
+        err = uefi_call_wrapper(BS->InstallMultipleProtocolInterfaces, 4,
+                        ret_image,
+                        &LoadedImageProtocol, loaded_image,
+                        NULL);
+        if (EFI_ERROR(err))
+                loaded_image = loaded_image_free(loaded_image);
+
+        return err;
+}
+
+static EFI_STATUS loaded_image_unregister(EFI_HANDLE loaded_image_handle) {
+        EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
+        EFI_STATUS err;
+
+        if (!loaded_image_handle)
+                return EFI_SUCCESS;
+
+        /* get the LoadedImage protocol that we allocated earlier */
+        err = uefi_call_wrapper(
+                        BS->OpenProtocol, 6,
+                        loaded_image_handle, &LoadedImageProtocol, (VOID **) &loaded_image,
+                        NULL, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+        if (EFI_ERROR(err))
+                return err;
+
+        /* close the handle */
+        (void) uefi_call_wrapper(
+                        BS->CloseProtocol, 4,
+                        loaded_image_handle, &LoadedImageProtocol, NULL, NULL);
+        err = uefi_call_wrapper(BS->UninstallMultipleProtocolInterfaces, 4,
+                        loaded_image_handle,
+                        &LoadedImageProtocol, loaded_image,
+                        NULL);
+        if (EFI_ERROR(err))
+                return err;
+        loaded_image_handle = NULL;
+        loaded_image = loaded_image_free(loaded_image);
+
+        return EFI_SUCCESS;
+}
+
+static inline void cleanup_initrd(EFI_HANDLE *initrd_handle) {
+        (void) initrd_unregister(*initrd_handle);
+        *initrd_handle = NULL;
+}
+
+static inline void cleanup_loaded_image(EFI_HANDLE *loaded_image_handle) {
+        (void) loaded_image_unregister(*loaded_image_handle);
+        *loaded_image_handle = NULL;
+}
+
+/* struct to call cleanup_pages */
+struct pages {
+        EFI_PHYSICAL_ADDRESS addr;
+        UINTN num;
+};
+
+static inline void cleanup_pages(struct pages *p) {
+        if (p->addr == 0)
+                return;
+        (void) uefi_call_wrapper(BS->FreePages, 2, p->addr, p->num);
 }
 
 EFI_STATUS linux_exec(
                 EFI_HANDLE image,
                 const CHAR8 *cmdline, UINTN cmdline_len,
-                const VOID *linux_buffer,
+                const VOID *linux_buffer, UINTN linux_length,
                 const VOID *initrd_buffer, UINTN initrd_length) {
 
-        const struct boot_params *image_params;
-        struct boot_params *boot_params;
-        EFI_HANDLE initrd_handle = NULL;
-        EFI_PHYSICAL_ADDRESS addr;
-        UINT8 setup_sectors;
+        _cleanup_(cleanup_initrd) EFI_HANDLE initrd_handle = NULL;
+        _cleanup_(cleanup_loaded_image) EFI_HANDLE loaded_image_handle = NULL;
+        UINT32 kernel_alignment, kernel_size_of_image, kernel_entry_address;
+        EFI_IMAGE_ENTRY_POINT kernel_entry;
+        _cleanup_(cleanup_pages) struct pages kernel = {};
+        VOID *new_buffer;
         EFI_STATUS err;
 
         assert(image);
         assert(cmdline || cmdline_len == 0);
-        assert(linux_buffer);
+        assert(linux_buffer && linux_length > 0);
         assert(initrd_buffer || initrd_length == 0);
 
-        image_params = (const struct boot_params *) linux_buffer;
-
-        if (image_params->hdr.boot_flag != 0xAA55 ||
-            image_params->hdr.header != SETUP_MAGIC ||
-            image_params->hdr.version < 0x20b ||
-            !image_params->hdr.relocatable_kernel)
-                return EFI_LOAD_ERROR;
-
-        addr = UINT32_MAX; /* Below the 32bit boundary */
-        err = uefi_call_wrapper(
-                        BS->AllocatePages, 4,
-                        AllocateMaxAddress,
-                        EfiLoaderData,
-                        EFI_SIZE_TO_PAGES(0x4000),
-                        &addr);
+        /* get the necessary fields from the PE header */
+        err = pe_alignment_info(linux_buffer, &kernel_entry_address, &kernel_size_of_image, &kernel_alignment);
         if (EFI_ERROR(err))
                 return err;
+        /* sanity check */
+        assert(kernel_size_of_image >= linux_length);
+
+        /* Linux kernel complains if it's not loaded at a properly aligned memory address. The correct alignment
+           is provided by Linux as the SegmentAlignment in the PeOptionalHeader. Additionally the kernel needs to
+           be in a memory segment thats SizeOfImage (again from PeOptionalHeader) large, so that the Kernel has
+           space for its BSS section. SizeOfImage is always larger than linux_length, which is only the size of
+           Code, (static) Data and Headers.
+
+           Interrestingly only ARM/Aarch64 and RISC-V kernel stubs check these assertions and can even boot (with warnings)
+           if they are not met. x86 and x86_64 kernel stubs don't do checks and fail if the BSS section is too small.
+        */
+        /* allocate SizeOfImage + SectionAlignment because the new_buffer can move up to Alignment-1 bytes */
+        kernel.num = EFI_SIZE_TO_PAGES(ALIGN_TO(kernel_size_of_image, kernel_alignment) + kernel_alignment);
+        err = uefi_call_wrapper(
+                BS->AllocatePages, 4,
+                AllocateAnyPages, EfiLoaderData,
+                kernel.num, &kernel.addr);
+        if (EFI_ERROR(err))
+                return EFI_OUT_OF_RESOURCES;
+        new_buffer = PHYSICAL_ADDRESS_TO_POINTER(ALIGN_TO(kernel.addr, kernel_alignment));
+        CopyMem(new_buffer, linux_buffer, linux_length);
+        /* zero out rest of memory (probably not needed, but BSS section should be 0) */
+        SetMem((UINT8 *)new_buffer + linux_length, kernel_size_of_image - linux_length, 0);
 
-        boot_params = (struct boot_params *) PHYSICAL_ADDRESS_TO_POINTER(addr);
-        ZeroMem(boot_params, 0x4000);
-        boot_params->hdr = image_params->hdr;
-        boot_params->hdr.type_of_loader = 0xff;
-        setup_sectors = image_params->hdr.setup_sects > 0 ? image_params->hdr.setup_sects : 4;
-        boot_params->hdr.code32_start = (UINT32) POINTER_TO_PHYSICAL_ADDRESS(linux_buffer) + (setup_sectors + 1) * 512;
+        /* get the entry point inside the relocated kernel */
+        kernel_entry = (EFI_IMAGE_ENTRY_POINT) ((const UINT8 *)new_buffer + kernel_entry_address);
 
-        if (cmdline) {
-                addr = 0xA0000;
-
-                err = uefi_call_wrapper(
-                                BS->AllocatePages, 4,
-                                AllocateMaxAddress,
-                                EfiLoaderData,
-                                EFI_SIZE_TO_PAGES(cmdline_len + 1),
-                                &addr);
-                if (EFI_ERROR(err))
-                        return err;
-
-                CopyMem(PHYSICAL_ADDRESS_TO_POINTER(addr), cmdline, cmdline_len);
-                ((CHAR8 *) PHYSICAL_ADDRESS_TO_POINTER(addr))[cmdline_len] = 0;
-                boot_params->hdr.cmd_line_ptr = (UINT32) addr;
-        }
-
-        /* Providing the initrd via LINUX_INITRD_MEDIA_GUID is only supported by Linux 5.8+ (5.7+ on ARM64).
-           Until supported kernels become more established, we continue to set ramdisk in the handover struct.
-           This value is overridden by kernels that support LINUX_INITRD_MEDIA_GUID.
-           If you need to know which protocol was used by the kernel, pass "efi=debug" to the kernel,
-           this will print a line when InitrdMediaGuid was successfully used to load the initrd.
-         */
-        boot_params->hdr.ramdisk_image = (UINT32) POINTER_TO_PHYSICAL_ADDRESS(initrd_buffer);
-        boot_params->hdr.ramdisk_size = (UINT32) initrd_length;
+        /* register a LoadedImage Protocol in order to pass on the commandline */
+        err = loaded_image_register(cmdline, cmdline_len, new_buffer, linux_length, &loaded_image_handle);
+        if (EFI_ERROR(err))
+                return err;
 
-        /* register LINUX_INITRD_MEDIA_GUID */
+        /* register a LINUX_INITRD_MEDIA DevicePath to serve the initrd */
         err = initrd_register(initrd_buffer, initrd_length, &initrd_handle);
         if (EFI_ERROR(err))
                 return err;
-        linux_efi_handover(image, boot_params);
-        (void) initrd_unregister(initrd_handle);
-        initrd_handle = NULL;
-        return EFI_LOAD_ERROR;
+
+        /* call the kernel */
+        return uefi_call_wrapper(kernel_entry, 2, loaded_image_handle, ST);
 }
index 01049d336263f026040100c0b89abe39e20afdc0..e030e4f31ea97d3806edcb793cfc7e2b3eba4141 100644 (file)
@@ -2,90 +2,9 @@
 #pragma once
 
 #include <efi.h>
-#include "macro-fundamental.h"
-
-#define SETUP_MAGIC             0x53726448      /* "HdrS" */
-
-struct setup_header {
-        UINT8  setup_sects;
-        UINT16 root_flags;
-        UINT32 syssize;
-        UINT16 ram_size;
-        UINT16 vid_mode;
-        UINT16 root_dev;
-        UINT16 boot_flag;
-        UINT16 jump;
-        UINT32 header;
-        UINT16 version;
-        UINT32 realmode_swtch;
-        UINT16 start_sys_seg;
-        UINT16 kernel_version;
-        UINT8  type_of_loader;
-        UINT8  loadflags;
-        UINT16 setup_move_size;
-        UINT32 code32_start;
-        UINT32 ramdisk_image;
-        UINT32 ramdisk_size;
-        UINT32 bootsect_kludge;
-        UINT16 heap_end_ptr;
-        UINT8  ext_loader_ver;
-        UINT8  ext_loader_type;
-        UINT32 cmd_line_ptr;
-        UINT32 initrd_addr_max;
-        UINT32 kernel_alignment;
-        UINT8  relocatable_kernel;
-        UINT8  min_alignment;
-        UINT16 xloadflags;
-        UINT32 cmdline_size;
-        UINT32 hardware_subarch;
-        UINT64 hardware_subarch_data;
-        UINT32 payload_offset;
-        UINT32 payload_length;
-        UINT64 setup_data;
-        UINT64 pref_address;
-        UINT32 init_size;
-        UINT32 handover_offset;
-} _packed_;
-
-/* adapted from linux' bootparam.h */
-struct boot_params {
-        UINT8  screen_info[64];         // was: struct screen_info
-        UINT8  apm_bios_info[20];       // was: struct apm_bios_info
-        UINT8  _pad2[4];
-        UINT64 tboot_addr;
-        UINT8  ist_info[16];            // was: struct ist_info
-        UINT8  _pad3[16];
-        UINT8  hd0_info[16];
-        UINT8  hd1_info[16];
-        UINT8  sys_desc_table[16];      // was: struct sys_desc_table
-        UINT8  olpc_ofw_header[16];     // was: struct olpc_ofw_header
-        UINT32 ext_ramdisk_image;
-        UINT32 ext_ramdisk_size;
-        UINT32 ext_cmd_line_ptr;
-        UINT8  _pad4[116];
-        UINT8  edid_info[128];          // was: struct edid_info
-        UINT8  efi_info[32];            // was: struct efi_info
-        UINT32 alt_mem_k;
-        UINT32 scratch;
-        UINT8  e820_entries;
-        UINT8  eddbuf_entries;
-        UINT8  edd_mbr_sig_buf_entries;
-        UINT8  kbd_status;
-        UINT8  secure_boot;
-        UINT8  _pad5[2];
-        UINT8  sentinel;
-        UINT8  _pad6[1];
-        struct setup_header hdr;
-        UINT8  _pad7[0x290-0x1f1-sizeof(struct setup_header)];
-        UINT32 edd_mbr_sig_buffer[16];  // was: edd_mbr_sig_buffer[EDD_MBR_SIG_MAX]
-        UINT8  e820_table[20*128];      // was: struct boot_e820_entry e820_table[E820_MAX_ENTRIES_ZEROPAGE]
-        UINT8  _pad8[48];
-        UINT8  eddbuf[6*82];            // was: struct edd_info eddbuf[EDDMAXNR]
-        UINT8  _pad9[276];
-} _packed_;
 
 EFI_STATUS linux_exec(
                 EFI_HANDLE image,
                 const CHAR8 *cmdline, UINTN cmdline_len,
-                const VOID *linux_buffer,
+                const VOID *linux_buffer, UINTN linux_length,
                 const VOID *initrd_buffer, UINTN initrd_length);
diff --git a/src/boot/efi/linux_x86.c b/src/boot/efi/linux_x86.c
new file mode 100644 (file)
index 0000000..539ad72
--- /dev/null
@@ -0,0 +1,200 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+/*
+ * x86 specific code to for EFI handover boot protocol
+ * Linux kernels version 5.8 and newer support providing the initrd by
+ * LINUX_INITRD_MEDIA_GUID DevicePath. In order to support older kernels too,
+ * this x86 specific linux_exec function passes the initrd by setting the
+ * corresponding fields in the setup_header struct.
+ *
+ * see https://www.kernel.org/doc/html/latest/x86/boot.html
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "initrd.h"
+#include "linux.h"
+#include "macro-fundamental.h"
+#include "util.h"
+
+#define SETUP_MAGIC             0x53726448      /* "HdrS" */
+
+struct setup_header {
+        UINT8  setup_sects;
+        UINT16 root_flags;
+        UINT32 syssize;
+        UINT16 ram_size;
+        UINT16 vid_mode;
+        UINT16 root_dev;
+        UINT16 boot_flag;
+        UINT16 jump;
+        UINT32 header;
+        UINT16 version;
+        UINT32 realmode_swtch;
+        UINT16 start_sys_seg;
+        UINT16 kernel_version;
+        UINT8  type_of_loader;
+        UINT8  loadflags;
+        UINT16 setup_move_size;
+        UINT32 code32_start;
+        UINT32 ramdisk_image;
+        UINT32 ramdisk_size;
+        UINT32 bootsect_kludge;
+        UINT16 heap_end_ptr;
+        UINT8  ext_loader_ver;
+        UINT8  ext_loader_type;
+        UINT32 cmd_line_ptr;
+        UINT32 initrd_addr_max;
+        UINT32 kernel_alignment;
+        UINT8  relocatable_kernel;
+        UINT8  min_alignment;
+        UINT16 xloadflags;
+        UINT32 cmdline_size;
+        UINT32 hardware_subarch;
+        UINT64 hardware_subarch_data;
+        UINT32 payload_offset;
+        UINT32 payload_length;
+        UINT64 setup_data;
+        UINT64 pref_address;
+        UINT32 init_size;
+        UINT32 handover_offset;
+} _packed_;
+
+/* adapted from linux' bootparam.h */
+struct boot_params {
+        UINT8  screen_info[64];         // was: struct screen_info
+        UINT8  apm_bios_info[20];       // was: struct apm_bios_info
+        UINT8  _pad2[4];
+        UINT64 tboot_addr;
+        UINT8  ist_info[16];            // was: struct ist_info
+        UINT8  _pad3[16];
+        UINT8  hd0_info[16];
+        UINT8  hd1_info[16];
+        UINT8  sys_desc_table[16];      // was: struct sys_desc_table
+        UINT8  olpc_ofw_header[16];     // was: struct olpc_ofw_header
+        UINT32 ext_ramdisk_image;
+        UINT32 ext_ramdisk_size;
+        UINT32 ext_cmd_line_ptr;
+        UINT8  _pad4[116];
+        UINT8  edid_info[128];          // was: struct edid_info
+        UINT8  efi_info[32];            // was: struct efi_info
+        UINT32 alt_mem_k;
+        UINT32 scratch;
+        UINT8  e820_entries;
+        UINT8  eddbuf_entries;
+        UINT8  edd_mbr_sig_buf_entries;
+        UINT8  kbd_status;
+        UINT8  secure_boot;
+        UINT8  _pad5[2];
+        UINT8  sentinel;
+        UINT8  _pad6[1];
+        struct setup_header hdr;
+        UINT8  _pad7[0x290-0x1f1-sizeof(struct setup_header)];
+        UINT32 edd_mbr_sig_buffer[16];  // was: edd_mbr_sig_buffer[EDD_MBR_SIG_MAX]
+        UINT8  e820_table[20*128];      // was: struct boot_e820_entry e820_table[E820_MAX_ENTRIES_ZEROPAGE]
+        UINT8  _pad8[48];
+        UINT8  eddbuf[6*82];            // was: struct edd_info eddbuf[EDDMAXNR]
+        UINT8  _pad9[276];
+} _packed_;
+
+#ifdef __i386__
+#define __regparm0__ __attribute__((regparm(0)))
+#else
+#define __regparm0__
+#endif
+
+typedef VOID(*handover_f)(VOID *image, EFI_SYSTEM_TABLE *table, struct boot_params *params) __regparm0__;
+
+static VOID linux_efi_handover(EFI_HANDLE image, struct boot_params *params) {
+        handover_f handover;
+        UINTN start = (UINTN)params->hdr.code32_start;
+
+        assert(params);
+
+#ifdef __x86_64__
+        asm volatile ("cli");
+        start += 512;
+#endif
+        handover = (handover_f)(start + params->hdr.handover_offset);
+        handover(image, ST, params);
+}
+
+EFI_STATUS linux_exec(
+                EFI_HANDLE image,
+                const CHAR8 *cmdline, UINTN cmdline_len,
+                const VOID *linux_buffer, UINTN linux_length,
+                const VOID *initrd_buffer, UINTN initrd_length) {
+
+        const struct boot_params *image_params;
+        struct boot_params *boot_params;
+        EFI_HANDLE initrd_handle = NULL;
+        EFI_PHYSICAL_ADDRESS addr;
+        UINT8 setup_sectors;
+        EFI_STATUS err;
+
+        assert(image);
+        assert(cmdline || cmdline_len == 0);
+        assert(linux_buffer);
+        assert(initrd_buffer || initrd_length == 0);
+
+        image_params = (const struct boot_params *) linux_buffer;
+
+        if (image_params->hdr.boot_flag != 0xAA55 ||
+            image_params->hdr.header != SETUP_MAGIC ||
+            image_params->hdr.version < 0x20b ||
+            !image_params->hdr.relocatable_kernel)
+                return EFI_LOAD_ERROR;
+
+        addr = UINT32_MAX; /* Below the 32bit boundary */
+        err = uefi_call_wrapper(
+                        BS->AllocatePages, 4,
+                        AllocateMaxAddress,
+                        EfiLoaderData,
+                        EFI_SIZE_TO_PAGES(0x4000),
+                        &addr);
+        if (EFI_ERROR(err))
+                return err;
+
+        boot_params = (struct boot_params *) PHYSICAL_ADDRESS_TO_POINTER(addr);
+        ZeroMem(boot_params, 0x4000);
+        boot_params->hdr = image_params->hdr;
+        boot_params->hdr.type_of_loader = 0xff;
+        setup_sectors = image_params->hdr.setup_sects > 0 ? image_params->hdr.setup_sects : 4;
+        boot_params->hdr.code32_start = (UINT32) POINTER_TO_PHYSICAL_ADDRESS(linux_buffer) + (setup_sectors + 1) * 512;
+
+        if (cmdline) {
+                addr = 0xA0000;
+
+                err = uefi_call_wrapper(
+                                BS->AllocatePages, 4,
+                                AllocateMaxAddress,
+                                EfiLoaderData,
+                                EFI_SIZE_TO_PAGES(cmdline_len + 1),
+                                &addr);
+                if (EFI_ERROR(err))
+                        return err;
+
+                CopyMem(PHYSICAL_ADDRESS_TO_POINTER(addr), cmdline, cmdline_len);
+                ((CHAR8 *) PHYSICAL_ADDRESS_TO_POINTER(addr))[cmdline_len] = 0;
+                boot_params->hdr.cmd_line_ptr = (UINT32) addr;
+        }
+
+        /* Providing the initrd via LINUX_INITRD_MEDIA_GUID is only supported by Linux 5.8+ (5.7+ on ARM64).
+           Until supported kernels become more established, we continue to set ramdisk in the handover struct.
+           This value is overridden by kernels that support LINUX_INITRD_MEDIA_GUID.
+           If you need to know which protocol was used by the kernel, pass "efi=debug" to the kernel,
+           this will print a line when InitrdMediaGuid was successfully used to load the initrd.
+         */
+        boot_params->hdr.ramdisk_image = (UINT32) POINTER_TO_PHYSICAL_ADDRESS(initrd_buffer);
+        boot_params->hdr.ramdisk_size = (UINT32) initrd_length;
+
+        /* register LINUX_INITRD_MEDIA_GUID */
+        err = initrd_register(initrd_buffer, initrd_length, &initrd_handle);
+        if (EFI_ERROR(err))
+                return err;
+        linux_efi_handover(image, boot_params);
+        (void) initrd_unregister(initrd_handle);
+        initrd_handle = NULL;
+        return EFI_LOAD_ERROR;
+}
index cf3d3e60798be5ed8e0102aee66ec1cfe70e9a6d..fdcf279f13f657ea1c75f7bdd54eb6955277eff2 100644 (file)
@@ -39,7 +39,6 @@ systemd_boot_sources = '''
 '''.split()
 
 stub_sources = '''
-        linux.c
         initrd.c
         splash.c
         stub.c
@@ -209,6 +208,11 @@ if have_gnu_efi
                 '-include', efi_config_h,
                 '-include', version_h,
         ]
+        if ['ia32', 'x86_64'].contains(efi_arch)
+                stub_sources += 'linux_x86.c'
+        else
+                stub_sources += 'linux.c'
+        endif
         if efi_arch == 'x86_64'
                 compile_args += ['-mno-red-zone',
                                  '-mno-sse',
index 04b4504c2ee3babdbd6ff2714c8b8445b55ea804..1309eb13a26758171f261dda4b54d0a0da85c623 100644 (file)
@@ -56,10 +56,46 @@ struct CoffFileHeader {
         UINT16  Characteristics;
 } _packed_;
 
+#define OPTHDR32_MAGIC 0x10B /* PE32  OptionalHeader */
+#define OPTHDR64_MAGIC 0x20B /* PE32+ OptionalHeader */
+
+struct PeOptionalHeader {
+        UINT16  Magic;
+        UINT8   LinkerMajor;
+        UINT8   LinkerMinor;
+        UINT32  SizeOfCode;
+        UINT32  SizeOfInitializedData;
+        UINT32  SizeOfUninitializeData;
+        UINT32  AddressOfEntryPoint;
+        UINT32  BaseOfCode;
+        union {
+                struct { /* PE32 */
+                        UINT32 BaseOfData;
+                        UINT32 ImageBase32;
+                };
+                UINT64 ImageBase64; /* PE32+ */
+        };
+        UINT32 SectionAlignment;
+        UINT32 FileAlignment;
+        UINT16 MajorOperatingSystemVersion;
+        UINT16 MinorOperatingSystemVersion;
+        UINT16 MajorImageVersion;
+        UINT16 MinorImageVersion;
+        UINT16 MajorSubsystemVersion;
+        UINT16 MinorSubsystemVersion;
+        UINT32 Win32VersionValue;
+        UINT32 SizeOfImage;
+        UINT32 SizeOfHeaders;
+        UINT32 CheckSum;
+        UINT16 Subsystem;
+        UINT16 DllCharacteristics;
+        /* fields with different sizes for 32/64 omitted */
+} _packed_;
+
 struct PeFileHeader {
         UINT8   Magic[4];
         struct CoffFileHeader FileHeader;
-        /* OptionalHeader omitted */
+        struct PeOptionalHeader OptionalHeader;
 } _packed_;
 
 struct PeSectionHeader {
@@ -91,7 +127,7 @@ static inline BOOLEAN verify_pe(const struct PeFileHeader *pe) {
 static inline UINTN section_table_offset(const struct DosFileHeader *dos, const struct PeFileHeader *pe) {
         assert(dos);
         assert(pe);
-        return dos->ExeHeader + sizeof(struct PeFileHeader) + pe->FileHeader.SizeOfOptionalHeader;
+        return dos->ExeHeader + OFFSETOF(struct PeFileHeader, OptionalHeader) + pe->FileHeader.SizeOfOptionalHeader;
 }
 
 static VOID locate_sections(
@@ -122,6 +158,41 @@ static VOID locate_sections(
         }
 }
 
+EFI_STATUS pe_alignment_info(
+                const VOID *base,
+                UINT32 *ret_entry_point_address,
+                UINT32 *ret_size_of_image,
+                UINT32 *ret_section_alignment) {
+
+        const struct DosFileHeader *dos;
+        const struct PeFileHeader *pe;
+
+        assert(base);
+        assert(ret_entry_point_address);
+        assert(ret_size_of_image);
+        assert(ret_section_alignment);
+
+        dos = (const struct DosFileHeader *) base;
+        if (!verify_dos(dos))
+                return EFI_LOAD_ERROR;
+
+        pe = (const struct PeFileHeader*) ((const UINT8 *)base + dos->ExeHeader);
+        if (!verify_pe(pe))
+                return EFI_LOAD_ERROR;
+
+        *ret_entry_point_address = pe->OptionalHeader.AddressOfEntryPoint;
+
+        if (pe->OptionalHeader.Magic == OPTHDR32_MAGIC) {
+                *ret_size_of_image = pe->OptionalHeader.SizeOfImage;
+                *ret_section_alignment = pe->OptionalHeader.SectionAlignment;
+        } else if (pe->OptionalHeader.Magic == OPTHDR64_MAGIC) {
+                *ret_size_of_image = pe->OptionalHeader.SizeOfImage;
+                *ret_section_alignment = pe->OptionalHeader.SectionAlignment;
+        } else
+                return EFI_UNSUPPORTED;
+        return EFI_SUCCESS;
+}
+
 EFI_STATUS pe_memory_locate_sections(
                 const CHAR8 *base,
                 const CHAR8 **sections,
index e35521122353571a63e7b85b64586b5f09b4547f..438cfe1b6135dfd8b39017d82da776f839cf52e8 100644 (file)
@@ -15,3 +15,9 @@ EFI_STATUS pe_file_locate_sections(
                 const CHAR8 **sections,
                 UINTN *offsets,
                 UINTN *sizes);
+
+EFI_STATUS pe_alignment_info(
+                const VOID *base,
+                UINT32 *ret_entry_point_address,
+                UINT32 *ret_size_of_image,
+                UINT32 *ret_section_alignment);
index aa8ca9666bd2289f6e7c88eab633af7277f09c6d..4af9704627d5bb10cbccd90f6c2f151b349705f8 100644 (file)
@@ -152,7 +152,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
                 NULL,
         };
 
-        UINTN cmdline_len = 0, initrd_size, credential_initrd_size = 0, sysext_initrd_size = 0;
+        UINTN cmdline_len = 0, linux_size, initrd_size, credential_initrd_size = 0, sysext_initrd_size = 0;
         _cleanup_freepool_ VOID *credential_initrd = NULL, *sysext_initrd = NULL;
         EFI_PHYSICAL_ADDRESS linux_base, initrd_base;
         EFI_LOADED_IMAGE *loaded_image;
@@ -222,6 +222,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
                          &sysext_initrd,
                          &sysext_initrd_size);
 
+        linux_size = szs[SECTION_LINUX];
         linux_base = POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[SECTION_LINUX];
 
         initrd_size = szs[SECTION_INITRD];
@@ -250,7 +251,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
         }
 
         err = linux_exec(image, cmdline, cmdline_len,
-                         PHYSICAL_ADDRESS_TO_POINTER(linux_base),
+                         PHYSICAL_ADDRESS_TO_POINTER(linux_base), linux_size,
                          PHYSICAL_ADDRESS_TO_POINTER(initrd_base), initrd_size);
         graphics_mode(FALSE);
         return log_error_status_stall(err, L"Execution of embedded linux image failed: %r", err);