From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Matthew Garrett 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 #include #include +#include #include #include #include @@ -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 #include #include #include @@ -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_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);