From: Max Resch Date: Mon, 11 Oct 2021 13:15:30 +0000 (+0200) Subject: [sd-stub] Add support for aarch64 booting via pe-entry point X-Git-Tag: v250-rc1~500^2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=dc4679283153463f81ba3f96f80960bb2c476378;p=thirdparty%2Fsystemd.git [sd-stub] Add support for aarch64 booting via pe-entry point --- diff --git a/src/boot/efi/linux.c b/src/boot/efi/linux.c index 2c27ed50300..98f1db24e0e 100644 --- a/src/boot/efi/linux.c +++ b/src/boot/efi/linux.c @@ -1,109 +1,186 @@ /* 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 #include -#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); } diff --git a/src/boot/efi/linux.h b/src/boot/efi/linux.h index 01049d33626..e030e4f31ea 100644 --- a/src/boot/efi/linux.h +++ b/src/boot/efi/linux.h @@ -2,90 +2,9 @@ #pragma once #include -#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 index 00000000000..539ad723634 --- /dev/null +++ b/src/boot/efi/linux_x86.c @@ -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 +#include + +#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; +} diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build index cf3d3e60798..fdcf279f13f 100644 --- a/src/boot/efi/meson.build +++ b/src/boot/efi/meson.build @@ -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', diff --git a/src/boot/efi/pe.c b/src/boot/efi/pe.c index 04b4504c2ee..1309eb13a26 100644 --- a/src/boot/efi/pe.c +++ b/src/boot/efi/pe.c @@ -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, diff --git a/src/boot/efi/pe.h b/src/boot/efi/pe.h index e3552112235..438cfe1b613 100644 --- a/src/boot/efi/pe.h +++ b/src/boot/efi/pe.h @@ -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); diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index aa8ca9666bd..4af9704627d 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -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);