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:
commit
0a51337df1
@ -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); \
|
||||||
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
@ -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
200
src/boot/efi/linux_x86.c
Normal 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;
|
||||||
|
}
|
@ -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',
|
||||||
|
@ -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,
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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; \
|
||||||
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user