stub: Always use LINUX_INITRD_MEDIA_GUID if available

The kernel PE image version can be used to detect support for this
feature, allowing us to prefer the generic code of the deprecated EFI
handover protocol.
This commit is contained in:
Jan Janssen
2022-09-20 13:05:41 +02:00
parent a3461db85f
commit ba2a105c2b
7 changed files with 32 additions and 22 deletions
+1 -1
View File
@@ -2415,7 +2415,7 @@ static EFI_STATUS image_start(
if (err == EFI_UNSUPPORTED && entry->type == LOADER_LINUX) {
uint32_t kernel_entry_address;
err = pe_alignment_info(loaded_image->ImageBase, &kernel_entry_address, NULL, NULL);
err = pe_kernel_info(loaded_image->ImageBase, &kernel_entry_address, NULL, NULL);
if (err != EFI_SUCCESS) {
if (err != EFI_UNSUPPORTED)
return log_error_status_stall(err, L"Error finding kernel compat entry address: %r", err);
+17 -1
View File
@@ -111,7 +111,20 @@ EFI_STATUS linux_exec(
assert(initrd_buffer || initrd_length == 0);
/* get the necessary fields from the PE header */
err = pe_alignment_info(linux_buffer, &kernel_entry_address, &kernel_size_of_image, &kernel_alignment);
err = pe_kernel_info(linux_buffer, &kernel_entry_address, &kernel_size_of_image, &kernel_alignment);
#if defined(__i386__) || defined(__x86_64__)
if (err == EFI_UNSUPPORTED)
/* Kernel is too old to support LINUX_INITRD_MEDIA_GUID, try the deprecated EFI handover
* protocol. */
return linux_exec_efi_handover(
image,
cmdline,
cmdline_len,
linux_buffer,
linux_length,
initrd_buffer,
initrd_length);
#endif
if (err != EFI_SUCCESS)
return err;
/* sanity check */
@@ -133,6 +146,9 @@ EFI_STATUS linux_exec(
EFI_SIZE_TO_PAGES(ALIGN_TO(kernel_size_of_image, kernel_alignment) + kernel_alignment),
0);
new_buffer = PHYSICAL_ADDRESS_TO_POINTER(ALIGN_TO(kernel.addr, kernel_alignment));
if (!new_buffer) /* Silence gcc 11.2.0, assert(new_buffer) doesn't work. */
return EFI_OUT_OF_RESOURCES;
memcpy(new_buffer, linux_buffer, linux_length);
/* zero out rest of memory (probably not needed, but BSS section should be 0) */
memset((uint8_t *)new_buffer + linux_length, 0, kernel_size_of_image - linux_length);
+5
View File
@@ -8,3 +8,8 @@ EFI_STATUS linux_exec(
const char *cmdline, UINTN cmdline_len,
const void *linux_buffer, UINTN linux_length,
const void *initrd_buffer, UINTN initrd_length);
EFI_STATUS linux_exec_efi_handover(
EFI_HANDLE image,
const char *cmdline, UINTN cmdline_len,
const void *linux_buffer, UINTN linux_length,
const void *initrd_buffer, UINTN initrd_length);
+2 -16
View File
@@ -23,6 +23,7 @@
#define SETUP_MAGIC 0x53726448u /* "HdrS" */
#define SETUP_VERSION_2_11 0x20bu
#define SETUP_VERSION_2_12 0x20cu
#define SETUP_VERSION_2_15 0x20fu
#define CMDLINE_PTR_MAX 0xA0000u
enum {
@@ -119,15 +120,12 @@ static void linux_efi_handover(EFI_HANDLE image, uintptr_t kernel, BootParams *p
handover(image, ST, params);
}
EFI_STATUS linux_exec(
EFI_STATUS linux_exec_efi_handover(
EFI_HANDLE image,
const char *cmdline, UINTN cmdline_len,
const void *linux_buffer, UINTN linux_length,
const void *initrd_buffer, UINTN initrd_length) {
EFI_HANDLE initrd_handle = NULL;
EFI_STATUS err;
assert(image);
assert(cmdline || cmdline_len == 0);
assert(linux_buffer);
@@ -196,23 +194,11 @@ EFI_STATUS linux_exec(
assert(can_4g || cmdline_pages.addr <= CMDLINE_PTR_MAX);
}
/* 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 = (uintptr_t) initrd_buffer;
boot_params->ext_ramdisk_image = POINTER_TO_PHYSICAL_ADDRESS(initrd_buffer) >> 32;
boot_params->hdr.ramdisk_size = initrd_length;
boot_params->ext_ramdisk_size = ((uint64_t) initrd_length) >> 32;
/* register LINUX_INITRD_MEDIA_GUID */
err = initrd_register(initrd_buffer, initrd_length, &initrd_handle);
if (err != EFI_SUCCESS)
return err;
linux_efi_handover(image, (uintptr_t) linux_buffer, boot_params);
(void) initrd_unregister(initrd_handle);
initrd_handle = NULL;
return EFI_LOAD_ERROR;
}
+1 -2
View File
@@ -392,14 +392,13 @@ systemd_boot_sources = files(
stub_sources = files(
'cpio.c',
'linux.c',
'splash.c',
'stub.c',
)
if efi_arch[1] in ['ia32', 'x86_64']
stub_sources += files('linux_x86.c')
else
stub_sources += files('linux.c')
endif
tests += [
+5 -1
View File
@@ -209,7 +209,7 @@ static uint32_t get_compatibility_entry_address(const DosFileHeader *dos, const
return 0;
}
EFI_STATUS pe_alignment_info(
EFI_STATUS pe_kernel_info(
const void *base,
uint32_t *ret_entry_point_address,
uint32_t *ret_size_of_image,
@@ -229,6 +229,10 @@ EFI_STATUS pe_alignment_info(
if (!verify_pe(pe, /* allow_compatibility= */ true))
return EFI_LOAD_ERROR;
/* Support for LINUX_INITRD_MEDIA_GUID was added in kernel stub 1.0. */
if (pe->OptionalHeader.MajorImageVersion < 1)
return EFI_UNSUPPORTED;
uint32_t entry_address = pe->OptionalHeader.AddressOfEntryPoint;
/* Look for a compat entry point. */
+1 -1
View File
@@ -17,7 +17,7 @@ EFI_STATUS pe_file_locate_sections(
UINTN *offsets,
UINTN *sizes);
EFI_STATUS pe_alignment_info(
EFI_STATUS pe_kernel_info(
const void *base,
uint32_t *ret_entry_point_address,
uint32_t *ret_size_of_image,