From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from list by lists.gnu.org with archive (Exim 4.90_1) id 1okrv1-0007Rl-A7 for mharc-grub-devel@gnu.org; Tue, 18 Oct 2022 15:06:28 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:47146) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1okruW-0007NZ-PS for grub-devel@gnu.org; Tue, 18 Oct 2022 15:05:59 -0400 Received: from ams.source.kernel.org ([145.40.68.75]:54360) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1okruE-0000mV-6r for grub-devel@gnu.org; Tue, 18 Oct 2022 15:05:49 -0400 Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ams.source.kernel.org (Postfix) with ESMTPS id 9AA2FB820FA; Tue, 18 Oct 2022 19:05:34 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 26E10C433C1; Tue, 18 Oct 2022 19:05:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1666119933; bh=a7dJcR/Iq35dKWdlKgUctOBN8W00Ynh0rAISq8fMHV4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=a37PuKcm/lBBinv2BKy1R4ZjAhMlAw7FvxdnXUB6c66woSEET099qDPkk4a6gOWj4 flFWpJ7L/ozVupXz6xho/ZujCcsckpCz7Xw353vPLUo79uAv7hXjAmeCahc6JpUeKT DTHYzh4i1Uwz8zp1MSm7OWJMLcq8nQE7y8TIXGdEM4cSDb7K6QYWdmnWJ9iSG3V+/m hwEJyR+WVXiojsz+1HJyolHcugiZMVrkgwDNXpq//XWd8ENdA1Xwbx3yjbGa3bVazf GTPWcolYDQBTO2F8TEdb6SN0py8EXgauvVlDQeXSJ6BxiP2UhEJIXP9ROEQCo63MQU 1yurpiP4jeP6g== From: Ard Biesheuvel To: grub-devel@gnu.org Cc: Ard Biesheuvel , Daniel Kiper , Leif Lindholm , Nikita Ermakov , Atish Patra , Huacai Chen , Heinrich Schuchardt , dann frazier , Julian Andres Klode , Ilias Apalodimas Subject: [PATCH v5 5/6] efi: implement LoadFile2 initrd loading protocol for Linux Date: Tue, 18 Oct 2022 21:05:06 +0200 Message-Id: <20221018190508.177568-6-ardb@kernel.org> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20221018190508.177568-1-ardb@kernel.org> References: <20221018190508.177568-1-ardb@kernel.org> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=10882; i=ardb@kernel.org; h=from:subject; bh=a7dJcR/Iq35dKWdlKgUctOBN8W00Ynh0rAISq8fMHV4=; b=owEB7QES/pANAwAKAcNPIjmS2Y8kAcsmYgBjTvjhxS5uCKtcb10JCXs+PMcdbIa/x7B+7wPSkTx5 7CrZjsCJAbMEAAEKAB0WIQT72WJ8QGnJQhU3VynDTyI5ktmPJAUCY0744QAKCRDDTyI5ktmPJFaIC/ 41qb7y3Ue5eDUrC8/tSUscN2rvpaghtHBcG51/Ndba22NR4f8GiWkAHMxFKRk23JgUROn4pHeYiT7a doKbjHq7IkC/RO7uPhev8REP1aEIyN3KSPF8cXmikIeLE5RjeauP4ZH16Uqq/ZNk/4qp7gd+ptoAIm dKIHmm1YyabgydxMBX1itOKfjPrez8MvUBoNQauljN1FNqV5MFjqBU+9RdFv1ZGDiVHWgibqVPvfE6 dlfDdoo7b0D7mWmTJR3uYl/XpIBmbcRhnF42E/nh/jeikYgmW/x5NE3K5SoXoRs37qHuiHrn3AmsWV 1CmdAxsesqq8dEy7UAhG9Gt5H846kI7rt6p1AxzbBYk41GSSuBtSLiY/flH2W+yPv95aRL0aNnIzbJ 2UxSkPKpP2IfxTChMDukJYG9AO96nE4Ho2EIfeY/5yM1bMw0N3yTH80UDLuQYtoqPcplfuTInJysfC sm34UeBw9/07q0c80Bg2b1dZfxsns550tH24tbQTDmzLU= X-Developer-Key: i=ardb@kernel.org; a=openpgp; fpr=F43D03328115A198C90016883D200E9CA6329909 Content-Transfer-Encoding: 8bit Received-SPF: pass client-ip=145.40.68.75; envelope-from=ardb@kernel.org; helo=ams.source.kernel.org X-Spam_score_int: -73 X-Spam_score: -7.4 X-Spam_bar: ------- X-Spam_report: (-7.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.256, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_HI=-5, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: grub-devel@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: The development of GNU GRUB List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 18 Oct 2022 19:06:00 -0000 Recent Linux kernels will invoke the LoadFile2 protocol installed on a well-known vendor media path to load the initrd if it is exposed by the firmware. Using this method is preferred for two reasons: - the Linux kernel is in charge of allocating the memory, and so it can implement any placement policy it wants (given that these tend to change between kernel versions), - it is no longer necessary to modify the device tree provided by the firmware. So let's install this protocol when handling the 'initrd' command if such a recent kernel was detected (based on the PE/COFF image version), and defer loading the initrd contents until the point where the kernel invokes the LoadFile2 protocol. Signed-off-by: Ard Biesheuvel Reviewed-by: Heinrich Schuchardt Tested-by: Ilias Apalodimas Reviewed-by: Ilias Apalodimas --- grub-core/commands/efi/lsefi.c | 1 + grub-core/loader/arm64/linux.c | 124 +++++++++++++++++++- include/grub/efi/api.h | 40 +++++++ 3 files changed, 164 insertions(+), 1 deletion(-) diff --git a/grub-core/commands/efi/lsefi.c b/grub-core/commands/efi/lsefi.c index f46ba3b49384..c304d25ccdd6 100644 --- a/grub-core/commands/efi/lsefi.c +++ b/grub-core/commands/efi/lsefi.c @@ -55,6 +55,7 @@ struct known_protocol { GRUB_EFI_ABSOLUTE_POINTER_PROTOCOL_GUID, "absolute pointer" }, { GRUB_EFI_DRIVER_BINDING_PROTOCOL_GUID, "EFI driver binding" }, { GRUB_EFI_LOAD_FILE_PROTOCOL_GUID, "load file" }, + { GRUB_EFI_LOAD_FILE2_PROTOCOL_GUID, "load file2" }, { GRUB_EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID, "simple FS" }, { GRUB_EFI_TAPE_IO_PROTOCOL_GUID, "tape I/O" }, { GRUB_EFI_UNICODE_COLLATION_PROTOCOL_GUID, "unicode collation" }, diff --git a/grub-core/loader/arm64/linux.c b/grub-core/loader/arm64/linux.c index 754db84a96cf..9a4522d2f7bf 100644 --- a/grub-core/loader/arm64/linux.c +++ b/grub-core/loader/arm64/linux.c @@ -33,6 +33,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -48,6 +49,39 @@ static grub_uint32_t cmdline_size; static grub_addr_t initrd_start; static grub_addr_t initrd_end; +static struct grub_linux_initrd_context initrd_ctx = {0, 0, 0}; +static grub_efi_handle_t initrd_lf2_handle = NULL; +static bool initrd_use_loadfile2 = false; + +static grub_efi_guid_t load_file2_guid = GRUB_EFI_LOAD_FILE2_PROTOCOL_GUID; +static grub_efi_guid_t device_path_guid = GRUB_EFI_DEVICE_PATH_GUID; + +static initrd_media_device_path_t initrd_lf2_device_path = { + { + { + GRUB_EFI_MEDIA_DEVICE_PATH_TYPE, + GRUB_EFI_VENDOR_MEDIA_DEVICE_PATH_SUBTYPE, + sizeof(grub_efi_vendor_media_device_path_t), + }, + LINUX_EFI_INITRD_MEDIA_GUID + }, { + GRUB_EFI_END_DEVICE_PATH_TYPE, + GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE, + sizeof(grub_efi_device_path_t) + } +}; + +static grub_efi_status_t __grub_efi_api +grub_efi_initrd_load_file2 (grub_efi_load_file2_t *this, + grub_efi_device_path_t *device_path, + grub_efi_boolean_t boot_policy, + grub_efi_uintn_t *buffer_size, + void *buffer); + +static grub_efi_load_file2_t initrd_lf2 = { + grub_efi_initrd_load_file2 +}; + grub_err_t grub_arch_efi_linux_load_image_header (grub_file_t file, struct linux_arch_kernel_header * lh) @@ -78,6 +112,18 @@ grub_arch_efi_linux_load_image_header (grub_file_t file, return grub_error (GRUB_ERR_FILE_READ_ERROR, "failed to read COFF image header"); } + /* + * Linux kernels built for any architecture are guaranteed to support the + * LoadFile2 based initrd loading protocol if the image version is >= 1. + */ + if (lh->pe_image_header.optional_header.major_image_version >= 1) + initrd_use_loadfile2 = true; + else + initrd_use_loadfile2 = false; + + grub_dprintf ("linux", "LoadFile2 initrd loading %sabled\n", + initrd_use_loadfile2 ? "en" : "dis"); + return GRUB_ERR_NONE; } @@ -197,6 +243,8 @@ grub_linux_boot (void) static grub_err_t grub_linux_unload (void) { + grub_efi_boot_services_t *b = grub_efi_system_table->boot_services; + grub_dl_unref (my_mod); loaded = 0; if (initrd_start) @@ -208,6 +256,18 @@ grub_linux_unload (void) grub_efi_free_pages ((grub_addr_t) kernel_addr, GRUB_EFI_BYTES_TO_PAGES (kernel_size)); grub_fdt_unload (); + + if (initrd_lf2_handle != NULL) + { + b->uninstall_multiple_protocol_interfaces (initrd_lf2_handle, + &load_file2_guid, + &initrd_lf2, + &device_path_guid, + &initrd_lf2_device_path, + NULL); + initrd_lf2_handle = NULL; + initrd_use_loadfile2 = false; + } return GRUB_ERR_NONE; } @@ -247,13 +307,50 @@ allocate_initrd_mem (int initrd_pages) GRUB_EFI_LOADER_DATA); } +static grub_efi_status_t __grub_efi_api +grub_efi_initrd_load_file2 (grub_efi_load_file2_t *this, + grub_efi_device_path_t *device_path, + grub_efi_boolean_t boot_policy, + grub_efi_uintn_t *buffer_size, + void *buffer) +{ + grub_efi_status_t status = GRUB_EFI_SUCCESS; + grub_efi_uintn_t initrd_size; + + if (this != &initrd_lf2 || buffer_size == NULL) + return GRUB_EFI_INVALID_PARAMETER; + + if (device_path->type != GRUB_EFI_END_DEVICE_PATH_TYPE || + device_path->subtype != GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE) + return GRUB_EFI_NOT_FOUND; + + if (boot_policy) + return GRUB_EFI_UNSUPPORTED; + + initrd_size = grub_get_initrd_size (&initrd_ctx); + if (buffer == NULL || *buffer_size < initrd_size) + { + *buffer_size = initrd_size; + return GRUB_EFI_BUFFER_TOO_SMALL; + } + + grub_dprintf ("linux", "Providing initrd via EFI_LOAD_FILE2_PROTOCOL\n"); + + if (grub_initrd_load (&initrd_ctx, buffer)) + status = GRUB_EFI_DEVICE_ERROR; + + grub_initrd_close (&initrd_ctx); + return status; +} + static grub_err_t grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), int argc, char *argv[]) { - struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 }; int initrd_size, initrd_pages; void *initrd_mem = NULL; + grub_efi_boot_services_t *b = grub_efi_system_table->boot_services; + grub_efi_status_t status; if (argc == 0) { @@ -271,6 +368,31 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), if (grub_initrd_init (argc, argv, &initrd_ctx)) goto fail; + if (initrd_use_loadfile2) + { + if (initrd_lf2_handle == NULL) + { + status = b->install_multiple_protocol_interfaces (&initrd_lf2_handle, + &load_file2_guid, + &initrd_lf2, + &device_path_guid, + &initrd_lf2_device_path, + NULL); + if (status == GRUB_EFI_OUT_OF_RESOURCES) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + goto fail; + } + else if (status != GRUB_EFI_SUCCESS) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("failed to install protocols")); + goto fail; + } + } + grub_dprintf ("linux", "Using LoadFile2 initrd loading protocol\n"); + return GRUB_ERR_NONE; + } + initrd_size = grub_get_initrd_size (&initrd_ctx); grub_dprintf ("linux", "Loading initrd\n"); diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h index b0d003aa1164..b1a7259b92c8 100644 --- a/include/grub/efi/api.h +++ b/include/grub/efi/api.h @@ -149,6 +149,11 @@ { 0x8E, 0x3F, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B } \ } +#define GRUB_EFI_LOAD_FILE2_PROTOCOL_GUID \ + { 0x4006c0c1, 0xfcb3, 0x403e, \ + { 0x99, 0x6d, 0x4a, 0x6c, 0x87, 0x24, 0xe0, 0x6d } \ + } + #define GRUB_EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID \ { 0x0964e5b22, 0x6459, 0x11d2, \ { 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b } \ @@ -364,6 +369,11 @@ { 0x86, 0x2e, 0xc0, 0x1c, 0xdc, 0x29, 0x1f, 0x44 } \ } +#define LINUX_EFI_INITRD_MEDIA_GUID \ + { 0x5568e427, 0x68fc, 0x4f3d, \ + { 0xac, 0x74, 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68 } \ + } + struct grub_efi_sal_system_table { grub_uint32_t signature; @@ -564,6 +574,20 @@ typedef grub_uint16_t grub_efi_char16_t; typedef grub_efi_uintn_t grub_efi_status_t; +/* + * On x86, the EFI calling convention may deviate from the local one, so + * callback functions exposed to the firmware must carry the follow attribute + * annotation. (This includes protocols implemented by GRUB that are installed + * into the EFI protocol database.) + */ +#if defined(__i386__) +#define __grub_efi_api __attribute__((regparm(0))) +#elif defined(__x86_64__) +#define __grub_efi_api __attribute__((ms_abi)) +#else +#define __grub_efi_api +#endif + #define GRUB_EFI_ERROR_CODE(value) \ ((((grub_efi_status_t) 1) << (sizeof (grub_efi_status_t) * 8 - 1)) | (value)) @@ -1755,6 +1779,22 @@ struct grub_efi_rng_protocol }; typedef struct grub_efi_rng_protocol grub_efi_rng_protocol_t; +struct grub_efi_load_file2 +{ + grub_efi_status_t (__grub_efi_api *load_file)(struct grub_efi_load_file2 *this, + grub_efi_device_path_t *file_path, + grub_efi_boolean_t boot_policy, + grub_efi_uintn_t *buffer_size, + void *buffer); +}; +typedef struct grub_efi_load_file2 grub_efi_load_file2_t; + +struct initrd_media_device_path { + grub_efi_vendor_media_device_path_t vendor; + grub_efi_device_path_t end; +} GRUB_PACKED; +typedef struct initrd_media_device_path initrd_media_device_path_t; + #if (GRUB_TARGET_SIZEOF_VOID_P == 4) || defined (__ia64__) \ || defined (__aarch64__) || defined (__MINGW64__) || defined (__CYGWIN__) \ || defined(__riscv) -- 2.35.1