Pull request for UEFI sub-system for efi-2020-04-rc4 (2)

In Linux next-20200228 patches have been merged to load an initial ramdisk
 using an EFI_LOAD_FILE2_PROTOCOL provided by the firmware. See commit
 ec93fc371f01 ("efi/libstub: Add support for loading the initrd from a
 device path"). The idea behind it is that the firmware should be
 responsible for validating the initrd in a secure boot setup.
 
 This pull-request comprises a patch series which let's U-Boot provide an
 initial implementation of the EFI_LOAD_FILE2_PROTOCOL providing the initrd.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEbcT5xx8ppvoGt20zxIHbvCwFGsQFAl5ZYAYACgkQxIHbvCwF
 GsQWeg/+KzBLKSeu8TDBr/dSXGx/pLJK+riqRZsBYYkzVseIJEa6JBNgXUjg4qGD
 7+2zt+K1gCUXBo3UDM+aMcjEII7TeQ3179dm8WysdhW2LxUjb8gidfYpyM57iCRV
 BHGUgRYF+23JOUcJIB3O7rZbwT1G7RRL3uqi0N5JJmu88yxDJ1WEZE9blLJlcX3Q
 IwP7RkhYigWHPCtdLL2KQ+DTZmoynNemi6RIuTKRKa4K0mqRnbJqk1IpMzsgiQP/
 q+ykyRMYUx3iODiPTnF+Inn9Yr1OE5AVz7NN9jWyfNz1CAs7uRzN0XqTPobtOs8C
 aXaERjhp0U0SNhxUhKqmt7rZyHgwYmYUVfgnixZ3WZdGLXUgAWnkF1kRQRiR3ZcO
 XuteG20lirfapZhfkchhFXNLaWk/4FSRFGn5WDI5xCDv0/ie+fDw0c6BR5AdgHss
 ZF2EeaUln+bwyIBdWDrJEsSlTwTz2t0pFZft3hZ24KcdkRVFc9x/K890gTBfffIF
 qN4a0zwPRIq1tAHJSJvnyX7ViUGZkb8+q8dLTqNNY+yH0nXtAtayz4TslcxFK2lf
 Iw4URIWngBx1C/OVX5OEv4GNn4o3fL3ARc1BuH4/8tp2IhKq27ATFEm1AT1gJjXo
 4VTSuPc9d07SbapsfqwHsEMmRP1GFIO6hQnPErfzliRLYmr00C4=
 =yydK
 -----END PGP SIGNATURE-----

Merge tag 'efi-2020-04-rc4-2' of https://gitlab.denx.de/u-boot/custodians/u-boot-efi

Pull request for UEFI sub-system for efi-2020-04-rc4 (2)

In Linux next-20200228 patches have been merged to load an initial ramdisk
using an EFI_LOAD_FILE2_PROTOCOL provided by the firmware. See commit
ec93fc371f01 ("efi/libstub: Add support for loading the initrd from a
device path"). The idea behind it is that the firmware should be
responsible for validating the initrd in a secure boot setup.

This pull-request comprises a patch series which let's U-Boot provide an
initial implementation of the EFI_LOAD_FILE2_PROTOCOL providing the initrd.
This commit is contained in:
Tom Rini 2020-02-29 08:01:07 -05:00
commit 5045289820
12 changed files with 508 additions and 0 deletions

View file

@ -243,6 +243,10 @@ static const struct {
"HII Config Routing",
EFI_HII_CONFIG_ROUTING_PROTOCOL_GUID,
},
{
"Load File2",
EFI_LOAD_FILE2_PROTOCOL_GUID,
},
{
"Simple Network",
EFI_SIMPLE_NETWORK_PROTOCOL_GUID,

View file

@ -125,6 +125,15 @@ Graphical output protocol
.. kernel-doc:: lib/efi_loader/efi_gop.c
:internal:
Load file 2 protocol
~~~~~~~~~~~~~~~~~~~~
The load file 2 protocol can be used by the Linux kernel to load the initial
RAM disk. U-Boot can be configured to provide an implementation.
.. kernel-doc:: lib/efi_loader/efi_load_initrd.c
:internal:
Network protocols
~~~~~~~~~~~~~~~~~

View file

@ -356,6 +356,18 @@ This driver is only available if U-Boot is configured with::
CONFIG_BLK=y
CONFIG_PARTITIONS=y
Miscellaneous
-------------
Load file 2 protocol
~~~~~~~~~~~~~~~~~~~~
The load file 2 protocol can be used by the Linux kernel to load the initial
RAM disk. U-Boot can be configured to provide an implementation with::
EFI_LOAD_FILE2_INITRD=y
EFI_INITRD_FILESPEC=interface dev:part path_to_initrd
Links
-----

View file

@ -331,6 +331,14 @@ struct efi_runtime_services {
EFI_GUID(0xeb9d2d31, 0x2d88, 0x11d3, \
0x9a, 0x16, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d)
#define EFI_LOAD_FILE_PROTOCOL_GUID \
EFI_GUID(0x56ec3091, 0x954c, 0x11d2, \
0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b)
#define EFI_LOAD_FILE2_PROTOCOL_GUID \
EFI_GUID(0x4006c0c1, 0xfcb3, 0x403e, \
0x99, 0x6d, 0x4a, 0x6c, 0x87, 0x24, 0xe0, 0x6d)
struct efi_configuration_table {
efi_guid_t guid;
void *table;
@ -486,6 +494,7 @@ struct efi_device_path_nvme {
#define DEVICE_PATH_TYPE_MEDIA_DEVICE 0x04
# define DEVICE_PATH_SUB_TYPE_HARD_DRIVE_PATH 0x01
# define DEVICE_PATH_SUB_TYPE_CDROM_PATH 0x02
# define DEVICE_PATH_SUB_TYPE_VENDOR_PATH 0x03
# define DEVICE_PATH_SUB_TYPE_FILE_PATH 0x04
struct efi_device_path_hard_drive_path {
@ -1619,6 +1628,14 @@ struct efi_unicode_collation_protocol {
char *supported_languages;
};
struct efi_load_file_protocol {
efi_status_t (EFIAPI *load_file)(struct efi_load_file_protocol *this,
struct efi_device_path *file_path,
bool boot_policy,
efi_uintn_t *buffer_size,
void *buffer);
};
/* Boot manager load options */
#define LOAD_OPTION_ACTIVE 0x00000001
#define LOAD_OPTION_FORCE_RECONNECT 0x00000002

25
include/efi_load_initrd.h Normal file
View file

@ -0,0 +1,25 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (c) 2020, Linaro Limited
*/
#if !defined _EFI_LOAD_INITRD_H_
#define _EFI_LOAD_INITRD_H_
#include <efi.h>
#include <efi_api.h>
/*
* Vendor GUID used by Linux to identify the handle with the
* EFI_LOAD_FILE2_PROTOCOL and load an initial ramdisk.
*/
#define EFI_INITRD_MEDIA_GUID \
EFI_GUID(0x5568e427, 0x68fc, 0x4f3d, \
0xac, 0x74, 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68)
struct efi_initrd_dp {
struct efi_device_path_vendor vendor;
struct efi_device_path end;
} __packed;
#endif

View file

@ -378,6 +378,7 @@ efi_status_t efi_gop_register(void);
efi_status_t efi_net_register(void);
/* Called by bootefi to make the watchdog available */
efi_status_t efi_watchdog_register(void);
efi_status_t efi_initrd_register(void);
/* Called by bootefi to make SMBIOS tables available */
/**
* efi_acpi_register() - write out ACPI tables

View file

@ -130,4 +130,19 @@ config EFI_RNG_PROTOCOL
Provide a EFI_RNG_PROTOCOL implementation using the hardware random
number generator of the platform.
config EFI_LOAD_FILE2_INITRD
bool "EFI_FILE_LOAD2_PROTOCOL for Linux initial ramdisk"
default n
help
Expose a EFI_FILE_LOAD2_PROTOCOL that the Linux UEFI stub can
use to load the initial ramdisk. Once this is enabled using
initrd=<ramdisk> will stop working.
config EFI_INITRD_FILESPEC
string "initramfs path"
default "host 0:1 initrd"
depends on EFI_LOAD_FILE2_INITRD
help
Full path of the initramfs file, e.g. mmc 0:2 initramfs.cpio.gz.
endif

View file

@ -43,3 +43,4 @@ obj-$(CONFIG_NET) += efi_net.o
obj-$(CONFIG_GENERATE_ACPI_TABLE) += efi_acpi.o
obj-$(CONFIG_GENERATE_SMBIOS_TABLE) += efi_smbios.o
obj-$(CONFIG_EFI_RNG_PROTOCOL) += efi_rng.o
obj-$(CONFIG_EFI_LOAD_FILE2_INITRD) += efi_load_initrd.o

View file

@ -0,0 +1,198 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2020, Linaro Limited
*/
#include <common.h>
#include <env.h>
#include <malloc.h>
#include <mapmem.h>
#include <dm.h>
#include <fs.h>
#include <efi_loader.h>
#include <efi_load_initrd.h>
static const efi_guid_t efi_guid_load_file2_protocol =
EFI_LOAD_FILE2_PROTOCOL_GUID;
static efi_status_t EFIAPI
efi_load_file2_initrd(struct efi_load_file_protocol *this,
struct efi_device_path *file_path, bool boot_policy,
efi_uintn_t *buffer_size, void *buffer);
static const struct efi_load_file_protocol efi_lf2_protocol = {
.load_file = efi_load_file2_initrd,
};
/*
* Device path defined by Linux to identify the handle providing the
* EFI_LOAD_FILE2_PROTOCOL used for loading the initial ramdisk.
*/
static const struct efi_initrd_dp dp = {
.vendor = {
{
DEVICE_PATH_TYPE_MEDIA_DEVICE,
DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
sizeof(dp.vendor),
},
EFI_INITRD_MEDIA_GUID,
},
.end = {
DEVICE_PATH_TYPE_END,
DEVICE_PATH_SUB_TYPE_END,
sizeof(dp.end),
}
};
/**
* get_file_size() - retrieve the size of initramfs, set efi status on error
*
* @dev: device to read from. i.e "mmc"
* @part: device partition. i.e "0:1"
* @file: name fo file
* @status: EFI exit code in case of failure
*
* Return: size of file
*/
static loff_t get_file_size(const char *dev, const char *part, const char *file,
efi_status_t *status)
{
loff_t sz = 0;
int ret;
ret = fs_set_blk_dev(dev, part, FS_TYPE_ANY);
if (ret) {
*status = EFI_NO_MEDIA;
goto out;
}
ret = fs_size(file, &sz);
if (ret) {
sz = 0;
*status = EFI_NOT_FOUND;
goto out;
}
out:
return sz;
}
/**
* load_file2() - get information about random number generation
*
* This function implement the LoadFile2() service in order to load an initram
* disk requested by the Linux kernel stub.
* See the UEFI spec for details.
*
* @this: loadfile2 protocol instance
* @file_path: relative path of the file. "" in this case
* @boot_policy: must be false for Loadfile2
* @buffer_size: size of allocated buffer
* @buffer: buffer to load the file
*
* Return: status code
*/
static efi_status_t EFIAPI
efi_load_file2_initrd(struct efi_load_file_protocol *this,
struct efi_device_path *file_path, bool boot_policy,
efi_uintn_t *buffer_size, void *buffer)
{
const char *filespec = CONFIG_EFI_INITRD_FILESPEC;
efi_status_t status = EFI_NOT_FOUND;
loff_t file_sz = 0, read_sz = 0;
char *dev, *part, *file;
char *s;
int ret;
EFI_ENTRY("%p, %p, %d, %p, %p", this, file_path, boot_policy,
buffer_size, buffer);
s = strdup(filespec);
if (!s)
goto out;
if (!this || this != &efi_lf2_protocol ||
!buffer_size) {
status = EFI_INVALID_PARAMETER;
goto out;
}
if (file_path->type != dp.end.type ||
file_path->sub_type != dp.end.sub_type) {
status = EFI_INVALID_PARAMETER;
goto out;
}
if (boot_policy) {
status = EFI_UNSUPPORTED;
goto out;
}
/* expect something like 'mmc 0:1 initrd.cpio.gz' */
dev = strsep(&s, " ");
if (!dev)
goto out;
part = strsep(&s, " ");
if (!part)
goto out;
file = strsep(&s, " ");
if (!file)
goto out;
file_sz = get_file_size(dev, part, file, &status);
if (!file_sz)
goto out;
if (!buffer || *buffer_size < file_sz) {
status = EFI_BUFFER_TOO_SMALL;
*buffer_size = file_sz;
} else {
ret = fs_set_blk_dev(dev, part, FS_TYPE_ANY);
if (ret) {
status = EFI_NO_MEDIA;
goto out;
}
ret = fs_read(file, map_to_sysmem(buffer), 0, *buffer_size,
&read_sz);
if (ret || read_sz != file_sz)
goto out;
*buffer_size = read_sz;
status = EFI_SUCCESS;
}
out:
free(s);
return EFI_EXIT(status);
}
/**
* efi_initrd_register() - Register a handle and loadfile2 protocol
*
* This function creates a new handle and installs a linux specific GUID
* to handle initram disk loading during boot.
* See the UEFI spec for details.
*
* Return: status code
*/
efi_status_t efi_initrd_register(void)
{
efi_handle_t efi_initrd_handle = NULL;
efi_status_t ret;
/*
* Set up the handle with the EFI_LOAD_FILE2_PROTOCOL which Linux may
* use to load the initial ramdisk.
*/
ret = EFI_CALL(efi_install_multiple_protocol_interfaces
(&efi_initrd_handle,
/* initramfs */
&efi_guid_device_path, &dp,
/* LOAD_FILE2 */
&efi_guid_load_file2_protocol,
(void *)&efi_lf2_protocol,
NULL));
return ret;
}

View file

@ -155,6 +155,11 @@ efi_status_t efi_init_obj_list(void)
if (ret != EFI_SUCCESS)
goto out;
#endif
#ifdef CONFIG_EFI_LOAD_FILE2_INITRD
ret = efi_initrd_register();
if (ret != EFI_SUCCESS)
goto out;
#endif
#ifdef CONFIG_NET
ret = efi_net_register();
if (ret != EFI_SUCCESS)

View file

@ -49,6 +49,7 @@ obj-$(CONFIG_CPU_V7) += efi_selftest_unaligned.o
obj-$(CONFIG_EFI_LOADER_HII) += efi_selftest_hii.o
obj-$(CONFIG_EFI_RNG_PROTOCOL) += efi_selftest_rng.o
obj-$(CONFIG_EFI_GET_TIME) += efi_selftest_rtc.o
obj-$(CONFIG_EFI_LOAD_FILE2_INITRD) += efi_selftest_load_initrd.o
ifeq ($(CONFIG_GENERATE_ACPI_TABLE),)
obj-y += efi_selftest_fdt.o

View file

@ -0,0 +1,220 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* efi_selftest_load_initrd
*
* Copyright (c) 2020 Ilias Apalodimas <ilias.apalodimas@linaro.org>
*
* This test checks the FileLoad2 protocol.
* A known file is read from the file system and verified.
*
* An example usage - given a file image with a file system in partition 1
* holding file initrd - is:
*
* * Configure the sandbox with
*
* CONFIG_EFI_SELFTEST=y
* CONFIG_EFI_LOAD_FILE2_INITRD=y
* CONFIG_EFI_INITRD_FILESPEC="host 0:1 initrd"
*
* * Run ./u-boot and execute
*
* host bind 0 image
* setenv efi_selftest load initrd
* bootefi selftest
*
* This would provide a test output like:
*
* Testing EFI API implementation
*
* Selected test: 'load initrd'
*
* Setting up 'load initrd'
* Setting up 'load initrd' succeeded
*
* Executing 'load initrd'
* Loaded 12378613 bytes
* CRC32 2997478465
*
* Now the size and CRC32 can be compared to the provided file.
*/
#include <efi_selftest.h>
#include <efi_loader.h>
#include <efi_load_initrd.h>
static struct efi_boot_services *boottime;
static struct efi_initrd_dp dp = {
.vendor = {
{
DEVICE_PATH_TYPE_MEDIA_DEVICE,
DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
sizeof(dp.vendor),
},
EFI_INITRD_MEDIA_GUID,
},
.end = {
DEVICE_PATH_TYPE_END,
DEVICE_PATH_SUB_TYPE_END,
sizeof(dp.end),
}
};
static struct efi_initrd_dp dp_invalid = {
.vendor = {
{
DEVICE_PATH_TYPE_MEDIA_DEVICE,
DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
sizeof(dp.vendor),
},
EFI_INITRD_MEDIA_GUID,
},
.end = {
0x8f, /* invalid */
0xfe, /* invalid */
sizeof(dp.end),
}
};
static int setup(const efi_handle_t handle,
const struct efi_system_table *systable)
{
boottime = systable->boottime;
return EFI_ST_SUCCESS;
}
static int execute(void)
{
efi_guid_t lf2_proto_guid = EFI_LOAD_FILE2_PROTOCOL_GUID;
struct efi_load_file_protocol *lf2;
struct efi_device_path *dp2, *dp2_invalid;
efi_status_t status;
efi_handle_t handle;
char buffer[64];
efi_uintn_t buffer_size;
void *buf;
u32 crc32;
memset(buffer, 0, sizeof(buffer));
dp2 = (struct efi_device_path *)&dp;
status = boottime->locate_device_path(&lf2_proto_guid, &dp2, &handle);
if (status != EFI_SUCCESS) {
efi_st_error("Unable to locate device path\n");
return EFI_ST_FAILURE;
}
status = boottime->handle_protocol(handle, &lf2_proto_guid,
(void **)&lf2);
if (status != EFI_SUCCESS) {
efi_st_error("Unable to locate protocol\n");
return EFI_ST_FAILURE;
}
/* Case 1:
* buffer_size can't be NULL
* protocol can't be NULL
*/
status = lf2->load_file(lf2, dp2, false, NULL, &buffer);
if (status != EFI_INVALID_PARAMETER) {
efi_st_error("Buffer size can't be NULL\n");
return EFI_ST_FAILURE;
}
buffer_size = sizeof(buffer);
status = lf2->load_file(NULL, dp2, false, &buffer_size, &buffer);
if (status != EFI_INVALID_PARAMETER) {
efi_st_error("Protocol can't be NULL\n");
return EFI_ST_FAILURE;
}
/*
* Case 2: Match end node type/sub-type on device path
*/
dp2_invalid = (struct efi_device_path *)&dp_invalid;
buffer_size = sizeof(buffer);
status = lf2->load_file(lf2, dp2_invalid, false, &buffer_size, &buffer);
if (status != EFI_INVALID_PARAMETER) {
efi_st_error("Invalid device path type must return EFI_INVALID_PARAMETER\n");
return EFI_ST_FAILURE;
}
status = lf2->load_file(lf2, dp2_invalid, false, &buffer_size, &buffer);
if (status != EFI_INVALID_PARAMETER) {
efi_st_error("Invalid device path sub-type must return EFI_INVALID_PARAMETER\n");
return EFI_ST_FAILURE;
}
/*
* Case 3:
* BootPolicy 'true' must return EFI_UNSUPPORTED
*/
buffer_size = sizeof(buffer);
status = lf2->load_file(lf2, dp2, true, &buffer_size, &buffer);
if (status != EFI_UNSUPPORTED) {
efi_st_error("BootPolicy true must return EFI_UNSUPPORTED\n");
return EFI_ST_FAILURE;
}
/*
* Case: Pass buffer size as zero, firmware must return
* EFI_BUFFER_TOO_SMALL and an appropriate size
*/
buffer_size = 0;
status = lf2->load_file(lf2, dp2, false, &buffer_size, NULL);
if (status != EFI_BUFFER_TOO_SMALL || !buffer_size) {
efi_st_printf("buffer_size: %u\n", (unsigned int)buffer_size);
efi_st_printf("status: %x\n", (unsigned int)status);
efi_st_error("Buffer size not updated\n");
return EFI_ST_FAILURE;
}
/*
* Case: Pass buffer size as smaller than the file_size,
* firmware must return * EFI_BUFFER_TOO_SMALL and an appropriate size
*/
buffer_size = 1;
status = lf2->load_file(lf2, dp2, false, &buffer_size, &buffer);
if (status != EFI_BUFFER_TOO_SMALL || buffer_size <= 1) {
efi_st_error("Buffer size not updated\n");
return EFI_ST_FAILURE;
}
status = boottime->allocate_pool(EFI_BOOT_SERVICES_DATA, buffer_size,
&buf);
if (status != EFI_SUCCESS) {
efi_st_error("Cannot allocate buffer\n");
return EFI_ST_FAILURE;
}
/* Case: Pass correct buffer, load the file and verify checksum*/
status = lf2->load_file(lf2, dp2, false, &buffer_size, buf);
if (status != EFI_SUCCESS) {
efi_st_error("Loading initrd failed\n");
return EFI_ST_FAILURE;
}
efi_st_printf("Loaded %u bytes\n", (unsigned int)buffer_size);
status = boottime->calculate_crc32(buf, buffer_size, &crc32);
if (status != EFI_SUCCESS) {
efi_st_error("Could not determine CRC32\n");
return EFI_ST_FAILURE;
}
efi_st_printf("CRC32 %u\n", (unsigned int)crc32);
status = boottime->free_pool(buf);
if (status != EFI_SUCCESS) {
efi_st_error("Cannot free buffer\n");
return EFI_ST_FAILURE;
}
return EFI_ST_SUCCESS;
}
EFI_UNIT_TEST(load_initrd) = {
.name = "load initrd",
.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
.setup = setup,
.execute = execute,
.on_request = true,
};