271 lines
9.0 KiB
Diff
271 lines
9.0 KiB
Diff
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||
|
From: Matthew Garrett <mjg@redhat.com>
|
||
|
Date: Mon, 15 Apr 2024 11:19:31 -0600
|
||
|
Subject: [PATCH] Add support for Linux EFI stub loading on arm architectures
|
||
|
|
||
|
---
|
||
|
grub-core/loader/arm64/xen_boot.c | 1 -
|
||
|
grub-core/loader/efi/linux.c | 116 ++++++++++++++++++--------------------
|
||
|
include/grub/efi/efi.h | 28 ++++++++-
|
||
|
3 files changed, 80 insertions(+), 65 deletions(-)
|
||
|
|
||
|
diff --git a/grub-core/loader/arm64/xen_boot.c b/grub-core/loader/arm64/xen_boot.c
|
||
|
index 26e1472c9d2..9838a0f878b 100644
|
||
|
--- a/grub-core/loader/arm64/xen_boot.c
|
||
|
+++ b/grub-core/loader/arm64/xen_boot.c
|
||
|
@@ -252,7 +252,6 @@ xen_boot (void)
|
||
|
return err;
|
||
|
|
||
|
return grub_arch_efi_linux_boot_image (xen_hypervisor->start,
|
||
|
- xen_hypervisor->size,
|
||
|
xen_hypervisor->cmdline);
|
||
|
}
|
||
|
|
||
|
diff --git a/grub-core/loader/efi/linux.c b/grub-core/loader/efi/linux.c
|
||
|
index 44587df3858..8742e303d85 100644
|
||
|
--- a/grub-core/loader/efi/linux.c
|
||
|
+++ b/grub-core/loader/efi/linux.c
|
||
|
@@ -28,6 +28,7 @@
|
||
|
#include <grub/efi/efi.h>
|
||
|
#include <grub/efi/fdtload.h>
|
||
|
#include <grub/efi/memory.h>
|
||
|
+#include <grub/efi/linux.h>
|
||
|
#include <grub/efi/pe32.h>
|
||
|
#include <grub/efi/linux.h>
|
||
|
#include <grub/efi/sb.h>
|
||
|
@@ -42,6 +43,7 @@ static int loaded;
|
||
|
|
||
|
static void *kernel_addr;
|
||
|
static grub_uint64_t kernel_size;
|
||
|
+static grub_uint32_t handover_offset;
|
||
|
|
||
|
static char *linux_args;
|
||
|
static grub_uint32_t cmdline_size;
|
||
|
@@ -94,7 +96,7 @@ static grub_efi_load_file2_t initrd_lf2 = {
|
||
|
typedef void (*handover_func) (void *, grub_efi_system_table_t *, void *);
|
||
|
|
||
|
grub_err_t
|
||
|
-grub_efi_linux_boot (void *kernel_address, grub_off_t handover_offset,
|
||
|
+grub_efi_linux_boot (void *kernel_address, grub_off_t ho_offset,
|
||
|
void *kernel_params)
|
||
|
{
|
||
|
grub_efi_loaded_image_t *loaded_image = NULL;
|
||
|
@@ -117,8 +119,8 @@ grub_efi_linux_boot (void *kernel_address, grub_off_t handover_offset,
|
||
|
grub_dprintf ("linux", "Loaded Image base address could not be set\n");
|
||
|
|
||
|
grub_dprintf ("linux", "kernel_address: %p handover_offset: %p params: %p\n",
|
||
|
- kernel_address, (void *)(grub_efi_uintn_t)handover_offset, kernel_params);
|
||
|
- hf = (handover_func)((char *)kernel_address + handover_offset + offset);
|
||
|
+ kernel_address, (void *)(grub_efi_uintn_t)ho_offset, kernel_params);
|
||
|
+ hf = (handover_func)((char *)kernel_address + ho_offset + offset);
|
||
|
hf (grub_efi_image_handle, grub_efi_system_table, kernel_params);
|
||
|
|
||
|
return GRUB_ERR_BUG;
|
||
|
@@ -178,7 +180,8 @@ grub_arch_efi_linux_load_image_header (grub_file_t file,
|
||
|
static grub_err_t
|
||
|
finalize_params_linux (void)
|
||
|
{
|
||
|
- int node, retval;
|
||
|
+ grub_efi_loaded_image_t *loaded_image = NULL;
|
||
|
+ int node, retval, len;
|
||
|
|
||
|
void *fdt;
|
||
|
|
||
|
@@ -213,79 +216,65 @@ finalize_params_linux (void)
|
||
|
if (grub_fdt_install() != GRUB_ERR_NONE)
|
||
|
goto failure;
|
||
|
|
||
|
- return GRUB_ERR_NONE;
|
||
|
-
|
||
|
-failure:
|
||
|
- grub_fdt_unload();
|
||
|
- return grub_error(GRUB_ERR_BAD_OS, "failed to install/update FDT");
|
||
|
-}
|
||
|
-#endif
|
||
|
-
|
||
|
-grub_err_t
|
||
|
-grub_arch_efi_linux_boot_image (grub_addr_t addr, grub_size_t size, char *args)
|
||
|
-{
|
||
|
- grub_efi_memory_mapped_device_path_t *mempath;
|
||
|
- grub_efi_handle_t image_handle;
|
||
|
- grub_efi_boot_services_t *b;
|
||
|
- grub_efi_status_t status;
|
||
|
- grub_efi_loaded_image_t *loaded_image;
|
||
|
- int len;
|
||
|
-
|
||
|
- mempath = grub_malloc (2 * sizeof (grub_efi_memory_mapped_device_path_t));
|
||
|
- if (!mempath)
|
||
|
- return grub_errno;
|
||
|
-
|
||
|
- mempath[0].header.type = GRUB_EFI_HARDWARE_DEVICE_PATH_TYPE;
|
||
|
- mempath[0].header.subtype = GRUB_EFI_MEMORY_MAPPED_DEVICE_PATH_SUBTYPE;
|
||
|
- mempath[0].header.length = grub_cpu_to_le16_compile_time (sizeof (*mempath));
|
||
|
- mempath[0].memory_type = GRUB_EFI_LOADER_DATA;
|
||
|
- mempath[0].start_address = addr;
|
||
|
- mempath[0].end_address = addr + size;
|
||
|
-
|
||
|
- mempath[1].header.type = GRUB_EFI_END_DEVICE_PATH_TYPE;
|
||
|
- mempath[1].header.subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
|
||
|
- mempath[1].header.length = sizeof (grub_efi_device_path_t);
|
||
|
-
|
||
|
- b = grub_efi_system_table->boot_services;
|
||
|
- status = b->load_image (0, grub_efi_image_handle,
|
||
|
- (grub_efi_device_path_t *) mempath,
|
||
|
- (void *) addr, size, &image_handle);
|
||
|
- if (status != GRUB_EFI_SUCCESS)
|
||
|
- return grub_error (GRUB_ERR_BAD_OS, "cannot load image");
|
||
|
-
|
||
|
- grub_dprintf ("linux", "linux command line: '%s'\n", args);
|
||
|
+ grub_dprintf ("linux", "Installed/updated FDT configuration table @ %p\n",
|
||
|
+ fdt);
|
||
|
|
||
|
/* Convert command line to UCS-2 */
|
||
|
- loaded_image = grub_efi_get_loaded_image (image_handle);
|
||
|
+ loaded_image = grub_efi_get_loaded_image (grub_efi_image_handle);
|
||
|
if (loaded_image == NULL)
|
||
|
{
|
||
|
grub_error (GRUB_ERR_BAD_FIRMWARE, "missing loaded_image proto");
|
||
|
- goto unload;
|
||
|
+ goto failure;
|
||
|
}
|
||
|
loaded_image->load_options_size = len =
|
||
|
- (grub_strlen (args) + 1) * sizeof (grub_efi_char16_t);
|
||
|
+ (grub_strlen (linux_args) + 1) * sizeof (grub_efi_char16_t);
|
||
|
loaded_image->load_options =
|
||
|
grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (loaded_image->load_options_size));
|
||
|
if (!loaded_image->load_options)
|
||
|
- return grub_errno;
|
||
|
+ return grub_error(GRUB_ERR_BAD_OS, "failed to create kernel parameters");
|
||
|
|
||
|
loaded_image->load_options_size =
|
||
|
2 * grub_utf8_to_utf16 (loaded_image->load_options, len,
|
||
|
- (grub_uint8_t *) args, len, NULL);
|
||
|
+ (grub_uint8_t *) linux_args, len, NULL);
|
||
|
|
||
|
- grub_dprintf ("linux", "starting image %p\n", image_handle);
|
||
|
- status = b->start_image (image_handle, 0, NULL);
|
||
|
+ return GRUB_ERR_NONE;
|
||
|
|
||
|
- /* When successful, not reached */
|
||
|
- grub_error (GRUB_ERR_BAD_OS, "start_image() returned 0x%" PRIxGRUB_EFI_UINTN_T, status);
|
||
|
- grub_efi_free_pages ((grub_addr_t) loaded_image->load_options,
|
||
|
- GRUB_EFI_BYTES_TO_PAGES (loaded_image->load_options_size));
|
||
|
-unload:
|
||
|
- b->unload_image (image_handle);
|
||
|
+failure:
|
||
|
+ grub_fdt_unload();
|
||
|
+ return grub_error(GRUB_ERR_BAD_OS, "failed to install/update FDT");
|
||
|
+}
|
||
|
+#endif
|
||
|
+
|
||
|
+static void
|
||
|
+free_params (void)
|
||
|
+{
|
||
|
+ grub_efi_loaded_image_t *loaded_image = NULL;
|
||
|
|
||
|
- return grub_errno;
|
||
|
+ loaded_image = grub_efi_get_loaded_image (grub_efi_image_handle);
|
||
|
+ if (loaded_image)
|
||
|
+ {
|
||
|
+ if (loaded_image->load_options)
|
||
|
+ grub_efi_free_pages ((grub_efi_physical_address_t)(grub_efi_uintn_t)loaded_image->load_options,
|
||
|
+ GRUB_EFI_BYTES_TO_PAGES (loaded_image->load_options_size));
|
||
|
+ loaded_image->load_options = NULL;
|
||
|
+ loaded_image->load_options_size = 0;
|
||
|
+ }
|
||
|
}
|
||
|
|
||
|
+grub_err_t
|
||
|
+grub_arch_efi_linux_boot_image (grub_addr_t addr, char *args)
|
||
|
+{
|
||
|
+ grub_err_t retval;
|
||
|
+
|
||
|
+ grub_dprintf ("linux", "linux command line: '%s'\n", args);
|
||
|
+
|
||
|
+ retval = grub_efi_linux_boot ((char *)addr, handover_offset, (void *)addr);
|
||
|
+
|
||
|
+ /* Never reached... */
|
||
|
+ free_params();
|
||
|
+ return retval;
|
||
|
+ }
|
||
|
+
|
||
|
static grub_err_t
|
||
|
grub_linux_boot (void)
|
||
|
{
|
||
|
@@ -294,8 +283,7 @@ grub_linux_boot (void)
|
||
|
return grub_errno;
|
||
|
#endif
|
||
|
|
||
|
- return grub_arch_efi_linux_boot_image ((grub_addr_t) kernel_addr,
|
||
|
- kernel_size, linux_args);
|
||
|
+ return grub_arch_efi_linux_boot_image ((grub_addr_t) kernel_addr, linux_args);
|
||
|
}
|
||
|
|
||
|
static grub_err_t
|
||
|
@@ -572,6 +560,12 @@ fallback:
|
||
|
|
||
|
grub_dprintf ("linux", "kernel @ %p\n", kernel_addr);
|
||
|
|
||
|
+#if !defined(__i386__) && !defined(__x86_64__)
|
||
|
+ struct grub_armxx_linux_pe_header *pe;
|
||
|
+ pe = (void *)((unsigned long)kernel_addr + lh.hdr_offset);
|
||
|
+ handover_offset = pe->opt.entry_addr;
|
||
|
+#endif
|
||
|
+
|
||
|
cmdline_size = grub_loader_cmdline_size (argc, argv) + sizeof (LINUX_IMAGE);
|
||
|
linux_args = grub_malloc (cmdline_size);
|
||
|
if (!linux_args)
|
||
|
diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h
|
||
|
index 83ddbe26e57..bff833468ee 100644
|
||
|
--- a/include/grub/efi/efi.h
|
||
|
+++ b/include/grub/efi/efi.h
|
||
|
@@ -20,6 +20,7 @@
|
||
|
#ifndef GRUB_EFI_EFI_HEADER
|
||
|
#define GRUB_EFI_EFI_HEADER 1
|
||
|
|
||
|
+#include <grub/efi/pe32.h>
|
||
|
#include <grub/types.h>
|
||
|
#include <grub/dl.h>
|
||
|
#include <grub/efi/api.h>
|
||
|
@@ -36,7 +37,29 @@ struct linux_arch_kernel_header {
|
||
|
struct grub_pe_image_header pe_image_header;
|
||
|
};
|
||
|
|
||
|
-#define GRUB_EFI_GRUB_VARIABLE_GUID \
|
||
|
+struct grub_arm_linux_pe_header
|
||
|
+{
|
||
|
+ grub_uint32_t magic;
|
||
|
+ struct grub_pe32_coff_header coff;
|
||
|
+ struct grub_pe32_optional_header opt;
|
||
|
+};
|
||
|
+
|
||
|
+struct grub_arm64_linux_pe_header
|
||
|
+{
|
||
|
+ grub_uint32_t magic;
|
||
|
+ struct grub_pe32_coff_header coff;
|
||
|
+ struct grub_pe64_optional_header opt;
|
||
|
+};
|
||
|
+
|
||
|
+#if defined(__arm__)
|
||
|
+# define grub_armxx_linux_pe_header grub_arm_linux_pe_header
|
||
|
+#endif
|
||
|
+
|
||
|
+#if defined(__aarch64__)
|
||
|
+# define grub_armxx_linux_pe_header grub_arm64_linux_pe_header
|
||
|
+#endif
|
||
|
+
|
||
|
+#define GRUB_EFI_GRUB_VARIABLE_GUID \
|
||
|
{ 0x91376aff, 0xcba6, 0x42be, \
|
||
|
{ 0x94, 0x9d, 0x06, 0xfd, 0xe8, 0x11, 0x28, 0xe8 } \
|
||
|
}
|
||
|
@@ -173,8 +196,7 @@ grub_err_t EXPORT_FUNC(grub_efi_get_ram_base)(grub_addr_t *);
|
||
|
#include <grub/file.h>
|
||
|
grub_err_t grub_arch_efi_linux_load_image_header(grub_file_t file,
|
||
|
struct linux_arch_kernel_header *lh);
|
||
|
-grub_err_t grub_arch_efi_linux_boot_image(grub_addr_t addr, grub_size_t size,
|
||
|
- char *args);
|
||
|
+grub_err_t grub_arch_efi_linux_boot_image(grub_addr_t addr, char *args);
|
||
|
|
||
|
grub_addr_t grub_efi_section_addr (const char *section);
|
||
|
|