Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions include/proto/device-path.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
Expand Down
75 changes: 34 additions & 41 deletions linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -32,17 +34,22 @@ 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));

err = pe_kernel_info(kernel->iov_base, &entry_point, &compat_entry_point, &image_base, &kernel_size_in_memory);
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");

Expand Down Expand Up @@ -79,61 +86,47 @@ 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;
err = initrd_register(initrd->iov_base, initrd->iov_len, &initrd_handle);
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");
Expand Down