arm64/efi/linux: Implement LoadFile2 initrd loading protocol for Linux
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 <ardb@kernel.org>
Reviewed-by: Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
Tested-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
Reviewed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
This commit is contained in:
parent
7b0809bc3a
commit
75e8d0d980
@ -55,6 +55,7 @@ struct known_protocol
|
|||||||
{ GRUB_EFI_ABSOLUTE_POINTER_PROTOCOL_GUID, "absolute pointer" },
|
{ GRUB_EFI_ABSOLUTE_POINTER_PROTOCOL_GUID, "absolute pointer" },
|
||||||
{ GRUB_EFI_DRIVER_BINDING_PROTOCOL_GUID, "EFI driver binding" },
|
{ GRUB_EFI_DRIVER_BINDING_PROTOCOL_GUID, "EFI driver binding" },
|
||||||
{ GRUB_EFI_LOAD_FILE_PROTOCOL_GUID, "load file" },
|
{ 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_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID, "simple FS" },
|
||||||
{ GRUB_EFI_TAPE_IO_PROTOCOL_GUID, "tape I/O" },
|
{ GRUB_EFI_TAPE_IO_PROTOCOL_GUID, "tape I/O" },
|
||||||
{ GRUB_EFI_UNICODE_COLLATION_PROTOCOL_GUID, "unicode collation" },
|
{ GRUB_EFI_UNICODE_COLLATION_PROTOCOL_GUID, "unicode collation" },
|
||||||
|
|||||||
@ -33,6 +33,7 @@
|
|||||||
#include <grub/i18n.h>
|
#include <grub/i18n.h>
|
||||||
#include <grub/lib/cmdline.h>
|
#include <grub/lib/cmdline.h>
|
||||||
#include <grub/verify.h>
|
#include <grub/verify.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
GRUB_MOD_LICENSE ("GPLv3+");
|
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_start;
|
||||||
static grub_addr_t initrd_end;
|
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_err_t
|
||||||
grub_arch_efi_linux_load_image_header (grub_file_t file,
|
grub_arch_efi_linux_load_image_header (grub_file_t file,
|
||||||
struct linux_arch_kernel_header * lh)
|
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");
|
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;
|
return GRUB_ERR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,6 +243,8 @@ grub_linux_boot (void)
|
|||||||
static grub_err_t
|
static grub_err_t
|
||||||
grub_linux_unload (void)
|
grub_linux_unload (void)
|
||||||
{
|
{
|
||||||
|
grub_efi_boot_services_t *b = grub_efi_system_table->boot_services;
|
||||||
|
|
||||||
grub_dl_unref (my_mod);
|
grub_dl_unref (my_mod);
|
||||||
loaded = 0;
|
loaded = 0;
|
||||||
if (initrd_start)
|
if (initrd_start)
|
||||||
@ -208,6 +256,18 @@ grub_linux_unload (void)
|
|||||||
grub_efi_free_pages ((grub_addr_t) kernel_addr,
|
grub_efi_free_pages ((grub_addr_t) kernel_addr,
|
||||||
GRUB_EFI_BYTES_TO_PAGES (kernel_size));
|
GRUB_EFI_BYTES_TO_PAGES (kernel_size));
|
||||||
grub_fdt_unload ();
|
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;
|
return GRUB_ERR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,13 +307,50 @@ allocate_initrd_mem (int initrd_pages)
|
|||||||
GRUB_EFI_LOADER_DATA);
|
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
|
static grub_err_t
|
||||||
grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
|
grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
|
||||||
int argc, char *argv[])
|
int argc, char *argv[])
|
||||||
{
|
{
|
||||||
struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 };
|
|
||||||
int initrd_size, initrd_pages;
|
int initrd_size, initrd_pages;
|
||||||
void *initrd_mem = NULL;
|
void *initrd_mem = NULL;
|
||||||
|
grub_efi_boot_services_t *b = grub_efi_system_table->boot_services;
|
||||||
|
grub_efi_status_t status;
|
||||||
|
|
||||||
if (argc == 0)
|
if (argc == 0)
|
||||||
{
|
{
|
||||||
@ -271,6 +368,31 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
|
|||||||
if (grub_initrd_init (argc, argv, &initrd_ctx))
|
if (grub_initrd_init (argc, argv, &initrd_ctx))
|
||||||
goto fail;
|
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);
|
initrd_size = grub_get_initrd_size (&initrd_ctx);
|
||||||
grub_dprintf ("linux", "Loading initrd\n");
|
grub_dprintf ("linux", "Loading initrd\n");
|
||||||
|
|
||||||
|
|||||||
@ -149,6 +149,11 @@
|
|||||||
{ 0x8E, 0x3F, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B } \
|
{ 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 \
|
#define GRUB_EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID \
|
||||||
{ 0x0964e5b22, 0x6459, 0x11d2, \
|
{ 0x0964e5b22, 0x6459, 0x11d2, \
|
||||||
{ 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b } \
|
{ 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b } \
|
||||||
@ -364,6 +369,11 @@
|
|||||||
{ 0x86, 0x2e, 0xc0, 0x1c, 0xdc, 0x29, 0x1f, 0x44 } \
|
{ 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
|
struct grub_efi_sal_system_table
|
||||||
{
|
{
|
||||||
grub_uint32_t signature;
|
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;
|
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) \
|
#define GRUB_EFI_ERROR_CODE(value) \
|
||||||
((((grub_efi_status_t) 1) << (sizeof (grub_efi_status_t) * 8 - 1)) | (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;
|
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__) \
|
#if (GRUB_TARGET_SIZEOF_VOID_P == 4) || defined (__ia64__) \
|
||||||
|| defined (__aarch64__) || defined (__MINGW64__) || defined (__CYGWIN__) \
|
|| defined (__aarch64__) || defined (__MINGW64__) || defined (__CYGWIN__) \
|
||||||
|| defined(__riscv)
|
|| defined(__riscv)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user