1
1
mirror of https://github.com/systemd/systemd-stable.git synced 2024-12-24 21:34:08 +03:00

Merge pull request #20983 from mxre/feature/aarch64

[sd-stub] Add support for aarch64 booting via pe-entry point
This commit is contained in:
Lennart Poettering 2021-10-15 10:26:10 +02:00 committed by GitHub
commit 0a51337df1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 446 additions and 167 deletions

View File

@ -49,12 +49,6 @@ typedef void (*free_func_t)(void *p);
#define malloc0(n) (calloc(1, (n) ?: 1)) #define malloc0(n) (calloc(1, (n) ?: 1))
#define mfree(memory) \
({ \
free(memory); \
(typeof(memory)) NULL; \
})
#define free_and_replace(a, b) \ #define free_and_replace(a, b) \
({ \ ({ \
typeof(a)* _a = &(a); \ typeof(a)* _a = &(a); \

View File

@ -1,109 +1,186 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* 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 <efi.h>
#include <efilib.h> #include <efilib.h>
#include "linux.h"
#include "initrd.h" #include "initrd.h"
#include "linux.h"
#include "pe.h"
#include "util.h" #include "util.h"
#ifdef __i386__ static EFI_LOADED_IMAGE * loaded_image_free(EFI_LOADED_IMAGE *img) {
#define __regparm0__ __attribute__((regparm(0))) if (!img)
#else return NULL;
#define __regparm0__ mfree(img->LoadOptions);
#endif return mfree(img);
}
typedef VOID(*handover_f)(VOID *image, EFI_SYSTEM_TABLE *table, struct boot_params *params) __regparm0__; static EFI_STATUS loaded_image_register(
const CHAR8 *cmdline, UINTN cmdline_len,
const VOID *linux_buffer, UINTN linux_length,
EFI_HANDLE *ret_image) {
static VOID linux_efi_handover(EFI_HANDLE image, struct boot_params *params) { EFI_LOADED_IMAGE *loaded_image = NULL;
handover_f handover; EFI_STATUS err;
UINTN start = (UINTN)params->hdr.code32_start;
assert(params); assert(cmdline || cmdline_len > 0);
assert(linux_buffer && linux_length > 0);
assert(ret_image);
#ifdef __x86_64__ /* create and install new LoadedImage Protocol */
asm volatile ("cli"); loaded_image = AllocatePool(sizeof(EFI_LOADED_IMAGE));
start += 512; if (!loaded_image)
#endif return EFI_OUT_OF_RESOURCES;
handover = (handover_f)(start + params->hdr.handover_offset);
handover(image, ST, params); /* provide the image base address and size */
*loaded_image = (EFI_LOADED_IMAGE) {
.ImageBase = (VOID *) linux_buffer,
.ImageSize = linux_length
};
/* 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);
}
/* 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_STATUS linux_exec(
EFI_HANDLE image, EFI_HANDLE image,
const CHAR8 *cmdline, UINTN cmdline_len, 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 VOID *initrd_buffer, UINTN initrd_length) {
const struct boot_params *image_params; _cleanup_(cleanup_initrd) EFI_HANDLE initrd_handle = NULL;
struct boot_params *boot_params; _cleanup_(cleanup_loaded_image) EFI_HANDLE loaded_image_handle = NULL;
EFI_HANDLE initrd_handle = NULL; UINT32 kernel_alignment, kernel_size_of_image, kernel_entry_address;
EFI_PHYSICAL_ADDRESS addr; EFI_IMAGE_ENTRY_POINT kernel_entry;
UINT8 setup_sectors; _cleanup_(cleanup_pages) struct pages kernel = {};
VOID *new_buffer;
EFI_STATUS err; EFI_STATUS err;
assert(image); assert(image);
assert(cmdline || cmdline_len == 0); assert(cmdline || cmdline_len == 0);
assert(linux_buffer); assert(linux_buffer && linux_length > 0);
assert(initrd_buffer || initrd_length == 0); assert(initrd_buffer || initrd_length == 0);
image_params = (const struct boot_params *) linux_buffer; /* 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);
if (image_params->hdr.boot_flag != 0xAA55 || /* Linux kernel complains if it's not loaded at a properly aligned memory address. The correct alignment
image_params->hdr.header != SETUP_MAGIC || is provided by Linux as the SegmentAlignment in the PeOptionalHeader. Additionally the kernel needs to
image_params->hdr.version < 0x20b || be in a memory segment thats SizeOfImage (again from PeOptionalHeader) large, so that the Kernel has
!image_params->hdr.relocatable_kernel) space for its BSS section. SizeOfImage is always larger than linux_length, which is only the size of
return EFI_LOAD_ERROR; Code, (static) Data and Headers.
addr = UINT32_MAX; /* Below the 32bit boundary */ 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( err = uefi_call_wrapper(
BS->AllocatePages, 4, BS->AllocatePages, 4,
AllocateMaxAddress, AllocateAnyPages, EfiLoaderData,
EfiLoaderData, kernel.num, &kernel.addr);
EFI_SIZE_TO_PAGES(0x4000), if (EFI_ERROR(err))
&addr); 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);
/* get the entry point inside the relocated kernel */
kernel_entry = (EFI_IMAGE_ENTRY_POINT) ((const UINT8 *)new_buffer + kernel_entry_address);
/* 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)) if (EFI_ERROR(err))
return err; return err;
boot_params = (struct boot_params *) PHYSICAL_ADDRESS_TO_POINTER(addr); /* register a LINUX_INITRD_MEDIA DevicePath to serve the initrd */
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); err = initrd_register(initrd_buffer, initrd_length, &initrd_handle);
if (EFI_ERROR(err)) if (EFI_ERROR(err))
return err; return err;
linux_efi_handover(image, boot_params);
(void) initrd_unregister(initrd_handle); /* call the kernel */
initrd_handle = NULL; return uefi_call_wrapper(kernel_entry, 2, loaded_image_handle, ST);
return EFI_LOAD_ERROR;
} }

View File

@ -2,90 +2,9 @@
#pragma once #pragma once
#include <efi.h> #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_STATUS linux_exec(
EFI_HANDLE image, EFI_HANDLE image,
const CHAR8 *cmdline, UINTN cmdline_len, 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 VOID *initrd_buffer, UINTN initrd_length);

200
src/boot/efi/linux_x86.c Normal file
View File

@ -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;
}

View File

@ -39,7 +39,6 @@ systemd_boot_sources = '''
'''.split() '''.split()
stub_sources = ''' stub_sources = '''
linux.c
initrd.c initrd.c
splash.c splash.c
stub.c stub.c
@ -209,6 +208,11 @@ if have_gnu_efi
'-include', efi_config_h, '-include', efi_config_h,
'-include', version_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' if efi_arch == 'x86_64'
compile_args += ['-mno-red-zone', compile_args += ['-mno-red-zone',
'-mno-sse', '-mno-sse',

View File

@ -56,10 +56,46 @@ struct CoffFileHeader {
UINT16 Characteristics; UINT16 Characteristics;
} _packed_; } _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 { struct PeFileHeader {
UINT8 Magic[4]; UINT8 Magic[4];
struct CoffFileHeader FileHeader; struct CoffFileHeader FileHeader;
/* OptionalHeader omitted */ struct PeOptionalHeader OptionalHeader;
} _packed_; } _packed_;
struct PeSectionHeader { 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) { static inline UINTN section_table_offset(const struct DosFileHeader *dos, const struct PeFileHeader *pe) {
assert(dos); assert(dos);
assert(pe); 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( 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( EFI_STATUS pe_memory_locate_sections(
const CHAR8 *base, const CHAR8 *base,
const CHAR8 **sections, const CHAR8 **sections,

View File

@ -15,3 +15,9 @@ EFI_STATUS pe_file_locate_sections(
const CHAR8 **sections, const CHAR8 **sections,
UINTN *offsets, UINTN *offsets,
UINTN *sizes); UINTN *sizes);
EFI_STATUS pe_alignment_info(
const VOID *base,
UINT32 *ret_entry_point_address,
UINT32 *ret_size_of_image,
UINT32 *ret_section_alignment);

View File

@ -152,7 +152,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
NULL, 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; _cleanup_freepool_ VOID *credential_initrd = NULL, *sysext_initrd = NULL;
EFI_PHYSICAL_ADDRESS linux_base, initrd_base; EFI_PHYSICAL_ADDRESS linux_base, initrd_base;
EFI_LOADED_IMAGE *loaded_image; 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,
&sysext_initrd_size); &sysext_initrd_size);
linux_size = szs[SECTION_LINUX];
linux_base = POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[SECTION_LINUX]; linux_base = POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[SECTION_LINUX];
initrd_size = szs[SECTION_INITRD]; 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, 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); PHYSICAL_ADDRESS_TO_POINTER(initrd_base), initrd_size);
graphics_mode(FALSE); graphics_mode(FALSE);
return log_error_status_stall(err, L"Execution of embedded linux image failed: %r", err); return log_error_status_stall(err, L"Execution of embedded linux image failed: %r", err);

View File

@ -61,6 +61,7 @@
#endif #endif
#define memcpy(a, b, c) CopyMem((a), (b), (c)) #define memcpy(a, b, c) CopyMem((a), (b), (c))
#define free(a) FreePool(a)
#endif #endif
#if defined(static_assert) #if defined(static_assert)
@ -266,3 +267,9 @@
* @x: a string literal. * @x: a string literal.
*/ */
#define STRLEN(x) (sizeof(""x"") - sizeof(typeof(x[0]))) #define STRLEN(x) (sizeof(""x"") - sizeof(typeof(x[0])))
#define mfree(memory) \
({ \
free(memory); \
(typeof(memory)) NULL; \
})