summaryrefslogtreecommitdiffstats
path: root/src/boot
diff options
context:
space:
mode:
authorJan Janssen <medhefgo@web.de>2022-09-21 11:07:53 +0200
committerJan Janssen <medhefgo@web.de>2022-10-17 14:47:39 +0200
commita529d8182e29d300385b742479fa7964724a6e04 (patch)
tree3982d31c1530472bf1889da9ba9f7cee76283310 /src/boot
parentstub: Rename image parameter (diff)
downloadsystemd-a529d8182e29d300385b742479fa7964724a6e04.tar.xz
systemd-a529d8182e29d300385b742479fa7964724a6e04.zip
stub: Use LoadImage/StartImage to start the kernel
This is the proper way to start any EFI binary. The fact this even ever worked was because the kernel does not have any PE relocations. The only downside is that the embedded kernel image has to be signed and trusted by the firmware under secure boot. A future commit will try to deal with that.
Diffstat (limited to 'src/boot')
-rw-r--r--src/boot/efi/linux.c169
-rw-r--r--src/boot/efi/stub.c2
2 files changed, 59 insertions, 112 deletions
diff --git a/src/boot/efi/linux.c b/src/boot/efi/linux.c
index 1cebae34ec..5b062d409a 100644
--- a/src/boot/efi/linux.c
+++ b/src/boot/efi/linux.c
@@ -16,80 +16,41 @@
#include "pe.h"
#include "util.h"
-static EFI_LOADED_IMAGE_PROTOCOL *loaded_image_free(EFI_LOADED_IMAGE_PROTOCOL *img) {
- if (!img)
- return NULL;
- mfree(img->LoadOptions);
- return mfree(img);
-}
-
-static EFI_STATUS loaded_image_register(
- const char *cmdline, UINTN cmdline_len,
- const void *linux_buffer, UINTN linux_length,
- EFI_HANDLE *ret_image) {
-
- EFI_LOADED_IMAGE_PROTOCOL *loaded_image = NULL;
- EFI_STATUS err;
+#define STUB_PAYLOAD_GUID \
+ { 0x55c5d1f8, 0x04cd, 0x46b5, { 0x8a, 0x20, 0xe5, 0x6c, 0xbb, 0x30, 0x52, 0xd0 } }
- assert(cmdline || cmdline_len > 0);
- assert(linux_buffer && linux_length > 0);
+EFI_STATUS load_image(EFI_HANDLE parent, const void *source, size_t len, EFI_HANDLE *ret_image) {
+ assert(parent);
+ assert(source);
assert(ret_image);
- /* create and install new LoadedImage Protocol */
- loaded_image = xnew(EFI_LOADED_IMAGE_PROTOCOL, 1);
- *loaded_image = (EFI_LOADED_IMAGE_PROTOCOL) {
- .ImageBase = (void *) linux_buffer,
- .ImageSize = linux_length
+ /* We could pass a NULL device path, but it's nicer to provide something. */
+ struct {
+ VENDOR_DEVICE_PATH payload;
+ EFI_DEVICE_PATH end;
+ } _packed_ payload_device_path = {
+ .payload = {
+ .Header = {
+ .Type = MEDIA_DEVICE_PATH,
+ .SubType = MEDIA_VENDOR_DP,
+ .Length = { sizeof(payload_device_path.payload), 0 },
+ },
+ .Guid = STUB_PAYLOAD_GUID,
+ },
+ .end = {
+ .Type = END_DEVICE_PATH_TYPE,
+ .SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE,
+ .Length = { sizeof(payload_device_path.end), 0 },
+ },
};
- /* if a cmdline is set convert it to UCS2 */
- if (cmdline) {
- loaded_image->LoadOptions = xstra_to_str(cmdline);
- loaded_image->LoadOptionsSize = strsize16(loaded_image->LoadOptions);
- }
-
- /* install a new LoadedImage protocol. ret_handle is a new image handle */
- err = BS->InstallMultipleProtocolInterfaces(
- ret_image,
- &LoadedImageProtocol, loaded_image,
- NULL);
- if (err != EFI_SUCCESS)
- 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 = BS->OpenProtocol(
- loaded_image_handle, &LoadedImageProtocol, (void **) &loaded_image,
- NULL, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
- if (err != EFI_SUCCESS)
- return err;
-
- /* close the handle */
- (void) BS->CloseProtocol(loaded_image_handle, &LoadedImageProtocol, NULL, NULL);
- err = BS->UninstallMultipleProtocolInterfaces(
- loaded_image_handle,
- &LoadedImageProtocol, loaded_image,
- NULL);
- if (err != EFI_SUCCESS)
- return err;
- loaded_image_handle = NULL;
- loaded_image = loaded_image_free(loaded_image);
-
- return EFI_SUCCESS;
-}
-
-static inline void cleanup_loaded_image(EFI_HANDLE *loaded_image_handle) {
- (void) loaded_image_unregister(*loaded_image_handle);
- *loaded_image_handle = NULL;
+ return BS->LoadImage(
+ /*BootPolicy=*/false,
+ parent,
+ &payload_device_path.payload.Header,
+ (void *) source,
+ len,
+ ret_image);
}
EFI_STATUS linux_exec(
@@ -98,11 +59,7 @@ EFI_STATUS linux_exec(
const void *linux_buffer, UINTN linux_length,
const void *initrd_buffer, UINTN initrd_length) {
- _cleanup_(cleanup_initrd) EFI_HANDLE initrd_handle = NULL;
- _cleanup_(cleanup_loaded_image) EFI_HANDLE loaded_image_handle = NULL;
- uint32_t kernel_alignment, kernel_size_of_image, kernel_entry_address;
- EFI_IMAGE_ENTRY_POINT kernel_entry;
- void *new_buffer;
+ uint32_t kernel_alignment, kernel_size_of_image, kernel_entry_address = 0;
EFI_STATUS err;
assert(parent);
@@ -126,46 +83,36 @@ EFI_STATUS linux_exec(
initrd_length);
#endif
if (err != EFI_SUCCESS)
- return err;
- /* sanity check */
- assert(kernel_size_of_image >= linux_length);
-
- /* Linux kernel complains if it's not loaded at a properly aligned memory address. The correct alignment
- is provided by Linux as the SegmentAlignment in the PeOptionalHeader. Additionally the kernel needs to
- be in a memory segment that's SizeOfImage (again from PeOptionalHeader) large, so that the Kernel has
- space for its BSS section. SizeOfImage is always larger than linux_length, which is only the size of
- Code, (static) Data and Headers.
-
- 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 */
- _cleanup_pages_ Pages kernel = xmalloc_pages(
- AllocateAnyPages,
- EfiLoaderCode,
- 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);
-
- /* get the entry point inside the relocated kernel */
- kernel_entry = (EFI_IMAGE_ENTRY_POINT) ((const uint8_t *)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);
+ return log_error_status_stall(err, u"Bad kernel image: %r", err);
+
+ _cleanup_(unload_imagep) EFI_HANDLE kernel_image = NULL;
+ err = load_image(parent, linux_buffer, linux_length, &kernel_image);
+ if (err != EFI_SUCCESS)
+ return log_error_status_stall(err, u"Error loading kernel image: %r", err);
+
+ EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
+ err = BS->HandleProtocol(kernel_image, &LoadedImageProtocol, (void **) &loaded_image);
if (err != EFI_SUCCESS)
- return err;
+ return log_error_status_stall(err, u"Error getting kernel loaded image protocol: %r", err);
+
+ if (cmdline) {
+ loaded_image->LoadOptions = xstra_to_str(cmdline);
+ loaded_image->LoadOptionsSize = strsize16(loaded_image->LoadOptions);
+ }
- /* register a LINUX_INITRD_MEDIA DevicePath to serve the initrd */
+ _cleanup_(cleanup_initrd) EFI_HANDLE initrd_handle = NULL;
err = initrd_register(initrd_buffer, initrd_length, &initrd_handle);
if (err != EFI_SUCCESS)
- return err;
+ return log_error_status_stall(err, u"Error registering initrd: %r", err);
+
+ err = BS->StartImage(kernel_image, NULL, NULL);
+
+ /* Try calling the kernel compat entry point if one exists. */
+ if (err == EFI_UNSUPPORTED && kernel_entry_address > 0) {
+ EFI_IMAGE_ENTRY_POINT compat_entry =
+ (EFI_IMAGE_ENTRY_POINT) ((uint8_t *) loaded_image->ImageBase + kernel_entry_address);
+ err = compat_entry(kernel_image, ST);
+ }
- /* call the kernel */
- return kernel_entry(loaded_image_handle, ST);
+ return log_error_status_stall(err, u"Error starting kernel image: %r", err);
}
diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c
index 8bd1e985c9..a842c5c679 100644
--- a/src/boot/efi/stub.c
+++ b/src/boot/efi/stub.c
@@ -378,5 +378,5 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
PHYSICAL_ADDRESS_TO_POINTER(linux_base), linux_size,
PHYSICAL_ADDRESS_TO_POINTER(initrd_base), initrd_size);
graphics_mode(false);
- return log_error_status_stall(err, L"Execution of embedded linux image failed: %r", err);
+ return err;
}