From 3269bb43c9ebf8d41efd777f332d9ea710834bc4 Mon Sep 17 00:00:00 2001 From: Mate Kukri Date: Thu, 7 Aug 2025 17:28:58 +0100 Subject: [PATCH] Load Linux kernel the same way GRUB would Signed-off-by: Mate Kukri --- include/proto/device-path.h | 9 +++++ linux.c | 75 +++++++++++++++++-------------------- 2 files changed, 43 insertions(+), 41 deletions(-) diff --git a/include/proto/device-path.h b/include/proto/device-path.h index a394b0aa..436e9f6f 100644 --- a/include/proto/device-path.h +++ b/include/proto/device-path.h @@ -25,6 +25,8 @@ enum { END_INSTANCE_DEVICE_PATH_SUBTYPE = 0x01, END_ENTIRE_DEVICE_PATH_SUBTYPE = 0xff, + HW_MEMMAP_DP = 0x03, + MEDIA_HARDDRIVE_DP = 0x01, MEDIA_VENDOR_DP = 0x03, MEDIA_FILEPATH_DP = 0x04, @@ -45,6 +47,13 @@ typedef struct { EFI_GUID Guid; } _packed_ VENDOR_DEVICE_PATH; +typedef struct { + EFI_DEVICE_PATH Header; + uint32_t MemoryType; + EFI_PHYSICAL_ADDRESS StartingAddress; + EFI_PHYSICAL_ADDRESS EndingAddress; +} _packed_ MEMMAP_DEVICE_PATH; + #define MBR_TYPE_PCAT 0x01U #define MBR_TYPE_EFI_PARTITION_TABLE_HEADER 0x02U #define NO_DISK_SIGNATURE 0x00U diff --git a/linux.c b/linux.c index 07754eff..ed0851f3 100644 --- a/linux.c +++ b/linux.c @@ -18,11 +18,13 @@ #include "shim.h" #include "util.h" -#define STUB_PAYLOAD_GUID \ - { 0x55c5d1f8, 0x04cd, 0x46b5, { 0x8a, 0x20, 0xe5, 0x6c, 0xbb, 0x30, 0x52, 0xd0 } } +typedef struct { + MEMMAP_DEVICE_PATH memmap_path; + EFI_DEVICE_PATH end_path; +} _packed_ KERNEL_FILE_PATH; EFI_STATUS linux_exec( - EFI_HANDLE parent, + EFI_HANDLE parent_image, const char16_t *cmdline, const struct iovec *kernel, const struct iovec *initrd) { @@ -32,7 +34,7 @@ EFI_STATUS linux_exec( uint64_t image_base; EFI_STATUS err; - assert(parent); + assert(parent_image); assert(iovec_is_set(kernel)); assert(iovec_is_valid(initrd)); @@ -40,9 +42,14 @@ EFI_STATUS linux_exec( if (err != EFI_SUCCESS) return log_error_status(err, "Bad kernel image: %m"); + /* Re-use the parent_image(_handle) and parent_loaded_image for the kernel image we are about to execute. + * We have to do this, because if kernel stub code passes its own handle to certain firmware functions, + * the firmware could cast EFI_LOADED_IMAGE_PROTOCOL * to a larger struct to access its own private data, + * and if we allocated a smaller struct, that could cause problems. + * This is modeled exactly after GRUB behaviour, which has proven to be functional. */ EFI_LOADED_IMAGE_PROTOCOL* parent_loaded_image; err = BS->HandleProtocol( - parent, MAKE_GUID_PTR(EFI_LOADED_IMAGE_PROTOCOL), (void **) &parent_loaded_image); + parent_image, MAKE_GUID_PTR(EFI_LOADED_IMAGE_PROTOCOL), (void **) &parent_loaded_image); if (err != EFI_SUCCESS) return log_error_status(err, "Cannot get parent loaded image: %m"); @@ -79,31 +86,25 @@ EFI_STATUS linux_exec( h->VirtualSize - h->SizeOfRawData); } - _cleanup_free_ EFI_LOADED_IMAGE_PROTOCOL* loaded_image = xnew(EFI_LOADED_IMAGE_PROTOCOL, 1); - - VENDOR_DEVICE_PATH device_node = { - .Header = { - .Type = MEDIA_DEVICE_PATH, - .SubType = MEDIA_VENDOR_DP, - .Length = sizeof(device_node), - }, - .Guid = STUB_PAYLOAD_GUID, - }; - - *loaded_image = (EFI_LOADED_IMAGE_PROTOCOL) { - .Revision = 0x1000, - .ParentHandle = parent, - .SystemTable = ST, - .DeviceHandle = parent_loaded_image->DeviceHandle, - .ImageBase = loaded_kernel, - .ImageSize = kernel_size_in_memory, - .ImageCodeType = /*EFI_LOADER_CODE*/1, - .ImageDataType = /*EFI_LOADER_DATA*/2, - }; + _cleanup_free_ KERNEL_FILE_PATH *kernel_file_path = xnew(KERNEL_FILE_PATH, 1); + + kernel_file_path->memmap_path.Header.Type = HARDWARE_DEVICE_PATH; + kernel_file_path->memmap_path.Header.SubType = HW_MEMMAP_DP; + kernel_file_path->memmap_path.Header.Length = sizeof (MEMMAP_DEVICE_PATH); + kernel_file_path->memmap_path.MemoryType = EfiLoaderData; + kernel_file_path->memmap_path.StartingAddress = (EFI_PHYSICAL_ADDRESS) kernel->iov_base; + kernel_file_path->memmap_path.EndingAddress = (EFI_PHYSICAL_ADDRESS) kernel->iov_base + kernel->iov_len; + + kernel_file_path->end_path.Type = END_DEVICE_PATH_TYPE; + kernel_file_path->end_path.SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; + kernel_file_path->end_path.Length = sizeof (EFI_DEVICE_PATH); + + parent_loaded_image->ImageBase = loaded_kernel; + parent_loaded_image->ImageSize = kernel_size_in_memory; if (cmdline) { - loaded_image->LoadOptions = (void *) cmdline; - loaded_image->LoadOptionsSize = strsize16(loaded_image->LoadOptions); + parent_loaded_image->LoadOptions = (void *) cmdline; + parent_loaded_image->LoadOptionsSize = strsize16(parent_loaded_image->LoadOptions); } _cleanup_(cleanup_initrd) EFI_HANDLE initrd_handle = NULL; @@ -111,29 +112,21 @@ EFI_STATUS linux_exec( if (err != EFI_SUCCESS) return log_error_status(err, "Error registering initrd: %m"); - EFI_HANDLE kernel_image = NULL; - - err = BS->InstallMultipleProtocolInterfaces( - &kernel_image, MAKE_GUID_PTR(EFI_LOADED_IMAGE_PROTOCOL), loaded_image, - NULL); - if (err != EFI_SUCCESS) - return log_error_status(err, "Cannot install loaded image protocol: %m"); - log_wait(); if (entry_point > 0) { EFI_IMAGE_ENTRY_POINT entry = - (EFI_IMAGE_ENTRY_POINT) ((const uint8_t *) loaded_image->ImageBase + entry_point); - err = entry(kernel_image, ST); + (EFI_IMAGE_ENTRY_POINT) ((const uint8_t *) parent_loaded_image->ImageBase + entry_point); + err = entry(parent_image, ST); } else if (compat_entry_point > 0) { /* Try calling the kernel compat entry point if one exists. */ EFI_IMAGE_ENTRY_POINT compat_entry = - (EFI_IMAGE_ENTRY_POINT) ((const uint8_t *) loaded_image->ImageBase + compat_entry_point); - err = compat_entry(kernel_image, ST); + (EFI_IMAGE_ENTRY_POINT) ((const uint8_t *) parent_loaded_image->ImageBase + compat_entry_point); + err = compat_entry(parent_image, ST); } EFI_STATUS uninstall_err = BS->UninstallMultipleProtocolInterfaces( - kernel_image, MAKE_GUID_PTR(EFI_LOADED_IMAGE_PROTOCOL), loaded_image, + parent_image, MAKE_GUID_PTR(EFI_LOADED_IMAGE_PROTOCOL), parent_loaded_image, NULL); if (uninstall_err != EFI_SUCCESS) return log_error_status(uninstall_err, "Cannot uninstall loaded image protocol: %m");