aboutsummaryrefslogtreecommitdiff
path: root/lib/efi_loader
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2021-04-05 11:29:57 -0400
committerTom Rini <trini@konsulko.com>2021-04-05 11:29:57 -0400
commit90eba245a66aa20589404ba537215faf2012c1a3 (patch)
treec581cd1f00dd162aeac4262bb4e74c2a9fea98c9 /lib/efi_loader
parentb46dd116ce03e235f2a7d4843c6278e1da44b5e1 (diff)
parentdb8b46120aed6554d1ff405260ea6d2cc2439fcc (diff)
downloadu-boot-90eba245a66aa20589404ba537215faf2012c1a3.zip
u-boot-90eba245a66aa20589404ba537215faf2012c1a3.tar.gz
u-boot-90eba245a66aa20589404ba537215faf2012c1a3.tar.bz2
Merge branch 'next'
Diffstat (limited to 'lib/efi_loader')
-rw-r--r--lib/efi_loader/Kconfig24
-rw-r--r--lib/efi_loader/Makefile2
-rw-r--r--lib/efi_loader/efi_bootmgr.c19
-rw-r--r--lib/efi_loader/efi_boottime.c23
-rw-r--r--lib/efi_loader/efi_capsule.c8
-rw-r--r--lib/efi_loader/efi_device_path.c110
-rw-r--r--lib/efi_loader/efi_esrt.c510
-rw-r--r--lib/efi_loader/efi_file.c39
-rw-r--r--lib/efi_loader/efi_helper.c98
-rw-r--r--lib/efi_loader/efi_load_initrd.c202
-rw-r--r--lib/efi_loader/efi_setup.c6
-rw-r--r--lib/efi_loader/efi_tcg2.c75
-rw-r--r--lib/efi_loader/efi_var_common.c33
13 files changed, 1019 insertions, 130 deletions
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
index 634d3b1..e44f004 100644
--- a/lib/efi_loader/Kconfig
+++ b/lib/efi_loader/Kconfig
@@ -315,18 +315,13 @@ config EFI_TCG2_PROTOCOL_EVENTLOG_SIZE
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
+ default y
help
- Full path of the initramfs file, e.g. mmc 0:2 initramfs.cpio.gz.
+ Linux v5.7 and later can make use of this option. If the boot option
+ selected by the UEFI boot manager specifies an existing file to be used
+ as initial RAM disk, a Linux specific Load File2 protocol will be
+ installed and Linux 5.7+ will ignore any initrd=<ramdisk> command line
+ argument.
config EFI_SECURE_BOOT
bool "Enable EFI secure boot support"
@@ -347,4 +342,11 @@ config EFI_SECURE_BOOT
it is signed with a trusted key. To do that, you need to install,
at least, PK, KEK and db.
+config EFI_ESRT
+ bool "Enable the UEFI ESRT generation"
+ depends on EFI_CAPSULE_FIRMWARE_MANAGEMENT
+ default y
+ help
+ Enabling this option creates the ESRT UEFI system table.
+
endif
diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
index 10b42e8..8bd343e 100644
--- a/lib/efi_loader/Makefile
+++ b/lib/efi_loader/Makefile
@@ -23,6 +23,7 @@ endif
obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o
obj-$(CONFIG_CMD_BOOTEFI_BOOTMGR) += efi_bootmgr.o
obj-y += efi_boottime.o
+obj-y += efi_helper.o
obj-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += efi_capsule.o
obj-$(CONFIG_EFI_CAPSULE_FIRMWARE) += efi_firmware.o
obj-y += efi_console.o
@@ -52,6 +53,7 @@ obj-y += efi_variable.o
obj-$(CONFIG_EFI_VARIABLES_PRESEED) += efi_var_seed.o
endif
obj-y += efi_watchdog.o
+obj-$(CONFIG_EFI_ESRT) += efi_esrt.o
obj-$(CONFIG_LCD) += efi_gop.o
obj-$(CONFIG_DM_VIDEO) += efi_gop.o
obj-$(CONFIG_PARTITIONS) += efi_disk.o
diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c
index 25f5ceb..46c8011 100644
--- a/lib/efi_loader/efi_bootmgr.c
+++ b/lib/efi_loader/efi_bootmgr.c
@@ -118,11 +118,13 @@ static efi_status_t try_load_entry(u16 n, efi_handle_t *handle,
ret = efi_set_variable_int(L"BootCurrent",
&efi_global_variable_guid,
attributes, sizeof(n), &n, false);
- if (ret != EFI_SUCCESS) {
- if (EFI_CALL(efi_unload_image(*handle))
- != EFI_SUCCESS)
- log_err("Unloading image failed\n");
- goto error;
+ if (ret != EFI_SUCCESS)
+ goto unload;
+ /* try to register load file2 for initrd's */
+ if (IS_ENABLED(CONFIG_EFI_LOAD_FILE2_INITRD)) {
+ ret = efi_initrd_register();
+ if (ret != EFI_SUCCESS)
+ goto unload;
}
log_info("Booting: %ls\n", lo.label);
@@ -147,6 +149,13 @@ error:
free(load_option);
return ret;
+
+unload:
+ if (EFI_CALL(efi_unload_image(*handle)) != EFI_SUCCESS)
+ log_err("Unloading image failed\n");
+ free(load_option);
+
+ return ret;
}
/**
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
index 41b8949..4777b35 100644
--- a/lib/efi_loader/efi_boottime.c
+++ b/lib/efi_loader/efi_boottime.c
@@ -1406,10 +1406,9 @@ out:
*
* Return: status code
*/
-static efi_status_t EFIAPI efi_register_protocol_notify(
- const efi_guid_t *protocol,
- struct efi_event *event,
- void **registration)
+efi_status_t EFIAPI efi_register_protocol_notify(const efi_guid_t *protocol,
+ struct efi_event *event,
+ void **registration)
{
struct efi_register_notify_event *item;
efi_status_t ret = EFI_SUCCESS;
@@ -1877,7 +1876,6 @@ static
efi_status_t efi_load_image_from_file(struct efi_device_path *file_path,
void **buffer, efi_uintn_t *size)
{
- struct efi_file_info *info = NULL;
struct efi_file_handle *f;
efi_status_t ret;
u64 addr;
@@ -1888,18 +1886,7 @@ efi_status_t efi_load_image_from_file(struct efi_device_path *file_path,
if (!f)
return EFI_NOT_FOUND;
- /* Get file size */
- bs = 0;
- EFI_CALL(ret = f->getinfo(f, (efi_guid_t *)&efi_file_info_guid,
- &bs, info));
- if (ret != EFI_BUFFER_TOO_SMALL) {
- ret = EFI_DEVICE_ERROR;
- goto error;
- }
-
- info = malloc(bs);
- EFI_CALL(ret = f->getinfo(f, (efi_guid_t *)&efi_file_info_guid, &bs,
- info));
+ ret = efi_file_size(f, &bs);
if (ret != EFI_SUCCESS)
goto error;
@@ -1909,7 +1896,6 @@ efi_status_t efi_load_image_from_file(struct efi_device_path *file_path,
* allocate a buffer as EFI_BOOT_SERVICES_DATA. The caller has to
* update the reservation according to the image type.
*/
- bs = info->file_size;
ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
EFI_BOOT_SERVICES_DATA,
efi_size_in_pages(bs), &addr);
@@ -1926,7 +1912,6 @@ efi_status_t efi_load_image_from_file(struct efi_device_path *file_path,
*size = bs;
error:
EFI_CALL(f->close(f));
- free(info);
return ret;
}
diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
index 7ba1ced..9df9c35 100644
--- a/lib/efi_loader/efi_capsule.c
+++ b/lib/efi_loader/efi_capsule.c
@@ -482,6 +482,14 @@ efi_status_t EFIAPI efi_update_capsule(
goto out;
}
out:
+
+ if (IS_ENABLED(CONFIG_EFI_ESRT)) {
+ /* Rebuild the ESRT to reflect any updated FW images. */
+ ret = efi_esrt_populate();
+ if (ret != EFI_SUCCESS)
+ log_warning("EFI Capsule: failed to update ESRT\n");
+ }
+
return EFI_EXIT(ret);
}
diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c
index 398dbc6..4b20859 100644
--- a/lib/efi_loader/efi_device_path.c
+++ b/lib/efi_loader/efi_device_path.c
@@ -282,11 +282,31 @@ struct efi_device_path *efi_dp_dup(const struct efi_device_path *dp)
return ndp;
}
-struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1,
- const struct efi_device_path *dp2)
+/**
+ * efi_dp_append_or_concatenate() - Append or concatenate two device paths.
+ * Concatenated device path will be separated
+ * by a sub-type 0xff end node
+ *
+ * @dp1: First device path
+ * @dp2: Second device path
+ * @concat: If true the two device paths will be concatenated and separated
+ * by an end of entrire device path sub-type 0xff end node.
+ * If true the second device path will be appended to the first and
+ * terminated by an end node
+ *
+ * Return:
+ * concatenated device path or NULL. Caller must free the returned value
+ */
+static struct
+efi_device_path *efi_dp_append_or_concatenate(const struct efi_device_path *dp1,
+ const struct efi_device_path *dp2,
+ bool concat)
{
struct efi_device_path *ret;
+ size_t end_size = sizeof(END);
+ if (concat)
+ end_size = 2 * sizeof(END);
if (!dp1 && !dp2) {
/* return an end node */
ret = efi_dp_dup(&END);
@@ -298,18 +318,58 @@ struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1,
/* both dp1 and dp2 are non-null */
unsigned sz1 = efi_dp_size(dp1);
unsigned sz2 = efi_dp_size(dp2);
- void *p = dp_alloc(sz1 + sz2 + sizeof(END));
+ void *p = dp_alloc(sz1 + sz2 + end_size);
if (!p)
return NULL;
+ ret = p;
memcpy(p, dp1, sz1);
+ p += sz1;
+
+ if (concat) {
+ memcpy(p, &END, sizeof(END));
+ p += sizeof(END);
+ }
+
/* the end node of the second device path has to be retained */
- memcpy(p + sz1, dp2, sz2 + sizeof(END));
- ret = p;
+ memcpy(p, dp2, sz2);
+ p += sz2;
+ memcpy(p, &END, sizeof(END));
}
return ret;
}
+/**
+ * efi_dp_append() - Append a device to an existing device path.
+ *
+ * @dp1: First device path
+ * @dp2: Second device path
+ *
+ * Return:
+ * concatenated device path or NULL. Caller must free the returned value
+ */
+struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1,
+ const struct efi_device_path *dp2)
+{
+ return efi_dp_append_or_concatenate(dp1, dp2, false);
+}
+
+/**
+ * efi_dp_concat() - Concatenate 2 device paths. The final device path will
+ * contain two device paths separated by and end node (0xff).
+ *
+ * @dp1: First device path
+ * @dp2: Second device path
+ *
+ * Return:
+ * concatenated device path or NULL. Caller must free the returned value
+ */
+struct efi_device_path *efi_dp_concat(const struct efi_device_path *dp1,
+ const struct efi_device_path *dp2)
+{
+ return efi_dp_append_or_concatenate(dp1, dp2, true);
+}
+
struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp,
const struct efi_device_path *node)
{
@@ -1183,3 +1243,43 @@ ssize_t efi_dp_check_length(const struct efi_device_path *dp,
dp = (const struct efi_device_path *)((const u8 *)dp + len);
}
}
+
+/**
+ * efi_dp_from_lo() - Get the instance of a VenMedia node in a
+ * multi-instance device path that matches
+ * a specific GUID. This kind of device paths
+ * is found in Boot#### options describing an
+ * initrd location
+ *
+ * @lo: EFI_LOAD_OPTION containing a valid device path
+ * @size: size of the discovered device path
+ * @guid: guid to search for
+ *
+ * Return:
+ * device path including the VenMedia node or NULL.
+ * Caller must free the returned value.
+ */
+struct
+efi_device_path *efi_dp_from_lo(struct efi_load_option *lo,
+ efi_uintn_t *size, efi_guid_t guid)
+{
+ struct efi_device_path *fp = lo->file_path;
+ struct efi_device_path_vendor *vendor;
+ int lo_len = lo->file_path_length;
+
+ for (; lo_len >= sizeof(struct efi_device_path);
+ lo_len -= fp->length, fp = (void *)fp + fp->length) {
+ if (lo_len < 0 || efi_dp_check_length(fp, lo_len) < 0)
+ break;
+ if (fp->type != DEVICE_PATH_TYPE_MEDIA_DEVICE ||
+ fp->sub_type != DEVICE_PATH_SUB_TYPE_VENDOR_PATH)
+ continue;
+
+ vendor = (struct efi_device_path_vendor *)fp;
+ if (!guidcmp(&vendor->guid, &guid))
+ return efi_dp_dup(fp);
+ }
+ log_debug("VenMedia(%pUl) not found in %ls\n", &guid, lo->label);
+
+ return NULL;
+}
diff --git a/lib/efi_loader/efi_esrt.c b/lib/efi_loader/efi_esrt.c
new file mode 100644
index 0000000..947bdb5
--- /dev/null
+++ b/lib/efi_loader/efi_esrt.c
@@ -0,0 +1,510 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * EFI application ESRT tables support
+ *
+ * Copyright (C) 2021 Arm Ltd.
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+#include <log.h>
+#include <efi_api.h>
+#include <malloc.h>
+
+const efi_guid_t efi_esrt_guid = EFI_SYSTEM_RESOURCE_TABLE_GUID;
+
+static struct efi_system_resource_table *esrt;
+
+#define EFI_ESRT_VERSION 1
+
+/**
+ * efi_esrt_image_info_to_entry() - copy the information present in a fw image
+ * descriptor to a ESRT entry.
+ * The function ensures the ESRT entry matches the image_type_id in @img_info.
+ * In case of a mismatch we leave the entry unchanged.
+ *
+ * @img_info: the source image info descriptor
+ * @entry: pointer to the ESRT entry to be filled
+ * @desc_version: the version of the elements in img_info
+ * @image_type: the image type value to be set in the ESRT entry
+ * @flags: the capsule flags value to be set in the ESRT entry
+ *
+ * Return:
+ * - EFI_SUCCESS if the entry is correctly updated
+ * - EFI_INVALID_PARAMETER if entry does not match image_type_id in @img_info.
+ */
+static efi_status_t
+efi_esrt_image_info_to_entry(struct efi_firmware_image_descriptor *img_info,
+ struct efi_system_resource_entry *entry,
+ u32 desc_version, u32 image_type, u32 flags)
+{
+ if (guidcmp(&entry->fw_class, &img_info->image_type_id)) {
+ EFI_PRINT("ESRT entry %pUL mismatches img_type_id %pUL\n",
+ &entry->fw_class, &img_info->image_type_id);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ entry->fw_version = img_info->version;
+
+ entry->fw_type = image_type;
+ entry->capsule_flags = flags;
+
+ /*
+ * The field lowest_supported_image_version is only present
+ * on image info structure of version 2 or greater.
+ * See the EFI_FIRMWARE_IMAGE_DESCRIPTOR definition in UEFI.
+ */
+ if (desc_version >= 2)
+ entry->lowest_supported_fw_version =
+ img_info->lowest_supported_image_version;
+ else
+ entry->lowest_supported_fw_version = 0;
+
+ /*
+ * The fields last_attempt_version and last_attempt_status
+ * are only present on image info structure of version 3 or
+ * greater.
+ * See the EFI_FIRMWARE_IMAGE_DESCRIPTOR definition in UEFI.
+ */
+ if (desc_version >= 3) {
+ entry->last_attempt_version =
+ img_info->last_attempt_version;
+
+ entry->last_attempt_status =
+ img_info->last_attempt_status;
+ } else {
+ entry->last_attempt_version = 0;
+ entry->last_attempt_status = LAST_ATTEMPT_STATUS_SUCCESS;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_esrt_entries_to_size() - Obtain the bytes used by an ESRT
+ * datastructure with @num_entries.
+ *
+ * @num_entries: the number of entries in the ESRT.
+ *
+ * Return: the number of bytes an ESRT with @num_entries occupies in memory.
+ */
+static
+inline u32 efi_esrt_entries_to_size(u32 num_entries)
+{
+ u32 esrt_size = sizeof(struct efi_system_resource_table) +
+ num_entries * sizeof(struct efi_system_resource_entry);
+
+ return esrt_size;
+}
+
+/**
+ * efi_esrt_allocate_install() - Allocates @num_entries for the ESRT and
+ * performs basic ESRT initialization.
+ *
+ * @num_entries: the number of entries that the ESRT will hold.
+ *
+ * Return:
+ * - pointer to the ESRT if successful.
+ * - NULL otherwise.
+ */
+static
+efi_status_t efi_esrt_allocate_install(u32 num_entries)
+{
+ efi_status_t ret;
+ struct efi_system_resource_table *new_esrt;
+ u32 size = efi_esrt_entries_to_size(num_entries);
+ efi_guid_t esrt_guid = efi_esrt_guid;
+
+ /* Reserve num_pages for ESRT */
+ ret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, size,
+ (void **)&new_esrt);
+
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT cannot allocate memory for %d entries (%d bytes)\n",
+ num_entries, efi_esrt_entries_to_size(num_entries));
+
+ return ret;
+ }
+
+ new_esrt->fw_resource_count_max = num_entries;
+ new_esrt->fw_resource_count = 0;
+ new_esrt->fw_resource_version = EFI_ESRT_VERSION;
+
+ /* Install the ESRT in the system configuration table. */
+ ret = efi_install_configuration_table(&esrt_guid, (void *)new_esrt);
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT failed to install the ESRT in the system table\n");
+ return ret;
+ }
+
+ /* If there was a previous ESRT, deallocate its memory now. */
+ if (esrt)
+ ret = EFI_CALL(efi_free_pool(esrt));
+
+ esrt = new_esrt;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * esrt_find_entry() - Obtain the ESRT entry for the image with GUID
+ * @img_fw_class.
+ *
+ * If the img_fw_class is not yet present in the ESRT, this function
+ * reserves the tail element of the current ESRT as the entry for that fw_class.
+ * The number of elements in the ESRT is updated in that case.
+ *
+ * @img_fw_class: the GUID of the FW image which ESRT entry we want to obtain.
+ *
+ * Return:
+ * - A pointer to the ESRT entry for the image with GUID img_fw_class,
+ * - NULL if:
+ * - there is no more space in the ESRT,
+ * - ESRT is not initialized,
+ */
+static
+struct efi_system_resource_entry *esrt_find_entry(efi_guid_t *img_fw_class)
+{
+ u32 filled_entries;
+ u32 max_entries;
+ struct efi_system_resource_entry *entry;
+
+ if (!esrt) {
+ EFI_PRINT("ESRT access before initialized\n");
+ return NULL;
+ }
+
+ filled_entries = esrt->fw_resource_count;
+ entry = esrt->entries;
+
+ /* Check if the image with img_fw_class is already in the ESRT. */
+ for (u32 idx = 0; idx < filled_entries; idx++) {
+ if (!guidcmp(&entry[idx].fw_class, img_fw_class)) {
+ EFI_PRINT("ESRT found entry for image %pUl at index %d\n",
+ img_fw_class, idx);
+ return &entry[idx];
+ }
+ }
+
+ max_entries = esrt->fw_resource_count_max;
+ /*
+ * Since the image with img_fw_class is not present in the ESRT, check
+ * if ESRT is full before appending the new entry to it.
+ */
+ if (filled_entries == max_entries) {
+ EFI_PRINT("ESRT full, this should not happen\n");
+ return NULL;
+ }
+
+ /*
+ * This is a new entry for a fw image, increment the element
+ * number in the table and set the fw_class field.
+ */
+ esrt->fw_resource_count++;
+ entry[filled_entries].fw_class = *img_fw_class;
+ EFI_PRINT("ESRT allocated new entry for image %pUl at index %d\n",
+ img_fw_class, filled_entries);
+
+ return &entry[filled_entries];
+}
+
+/**
+ * efi_esrt_add_from_fmp() - Populates a sequence of ESRT entries from the FW
+ * images in the FMP.
+ *
+ * @fmp: the FMP instance from which FW images are added to the ESRT
+ *
+ * Return:
+ * - EFI_SUCCESS if all the FW images in the FMP are added to the ESRT
+ * - Error status otherwise
+ */
+static
+efi_status_t efi_esrt_add_from_fmp(struct efi_firmware_management_protocol *fmp)
+{
+ struct efi_system_resource_entry *entry = NULL;
+ size_t info_size = 0;
+ struct efi_firmware_image_descriptor *img_info = NULL;
+ u32 desc_version;
+ u8 desc_count;
+ size_t desc_size;
+ u32 package_version;
+ u16 *package_version_name;
+ efi_status_t ret = EFI_SUCCESS;
+
+ /*
+ * TODO: set the field image_type depending on the FW image type
+ * defined in a platform basis.
+ */
+ u32 image_type = ESRT_FW_TYPE_UNKNOWN;
+
+ /* TODO: set the capsule flags as a function of the FW image type. */
+ u32 flags = 0;
+
+ ret = EFI_CALL(fmp->get_image_info(fmp, &info_size, img_info,
+ &desc_version, &desc_count,
+ &desc_size, NULL, NULL));
+
+ if (ret != EFI_BUFFER_TOO_SMALL) {
+ /*
+ * An input of info_size=0 should always lead
+ * fmp->get_image_info to return BUFFER_TO_SMALL.
+ */
+ EFI_PRINT("Erroneous FMP implementation\n");
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ret = EFI_CALL(efi_allocate_pool(EFI_BOOT_SERVICES_DATA, info_size,
+ (void **)&img_info));
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT failed to allocate memory for image info.\n");
+ return ret;
+ }
+
+ ret = EFI_CALL(fmp->get_image_info(fmp, &info_size, img_info,
+ &desc_version, &desc_count,
+ &desc_size, &package_version,
+ &package_version_name));
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT failed to obtain the FMP image info\n");
+ goto out;
+ }
+
+ /*
+ * Iterate over all the FW images in the FMP.
+ */
+ for (u32 desc_idx = 0; desc_idx < desc_count; desc_idx++) {
+ struct efi_firmware_image_descriptor *cur_img_info =
+ (struct efi_firmware_image_descriptor *)
+ ((uintptr_t)img_info + desc_idx * desc_size);
+
+ /*
+ * Obtain the ESRT entry for the FW image with fw_class
+ * equal to cur_img_info->image_type_id.
+ */
+ entry = esrt_find_entry(&cur_img_info->image_type_id);
+
+ if (entry) {
+ ret = efi_esrt_image_info_to_entry(cur_img_info, entry,
+ desc_version,
+ image_type, flags);
+ if (ret != EFI_SUCCESS)
+ EFI_PRINT("ESRT entry mismatches image_type\n");
+
+ } else {
+ EFI_PRINT("ESRT failed to add entry for %pUl\n",
+ &cur_img_info->image_type_id);
+ continue;
+ }
+ }
+
+out:
+ EFI_CALL(efi_free_pool(img_info));
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_esrt_populate() - Populates the ESRT entries from the FMP instances
+ * present in the system.
+ * If an ESRT already exists, the old ESRT is replaced in the system table.
+ * The memory of the old ESRT is deallocated.
+ *
+ * Return:
+ * - EFI_SUCCESS if the ESRT is correctly created
+ * - error code otherwise.
+ */
+efi_status_t efi_esrt_populate(void)
+{
+ efi_handle_t *base_handle = NULL;
+ efi_handle_t *it_handle;
+ size_t no_handles = 0;
+ struct efi_firmware_management_protocol *fmp;
+ efi_status_t ret;
+ u32 num_entries = 0;
+ struct efi_handler *handler;
+
+ /*
+ * Obtain the number of registered FMP handles.
+ */
+ ret = EFI_CALL(efi_locate_handle_buffer(BY_PROTOCOL,
+ &efi_guid_firmware_management_protocol,
+ NULL, &no_handles,
+ (efi_handle_t **)&base_handle));
+
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT There are no FMP instances\n");
+
+ ret = efi_esrt_allocate_install(0);
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT failed to create table with 0 entries\n");
+ return ret;
+ }
+ return EFI_SUCCESS;
+ }
+
+ EFI_PRINT("ESRT populate esrt from (%ld) available FMP handles\n",
+ no_handles);
+
+ /*
+ * Iterate over all FMPs to determine an upper bound on the number of
+ * ESRT entries.
+ */
+ it_handle = base_handle;
+ for (u32 idx = 0; idx < no_handles; idx++, it_handle++) {
+ struct efi_firmware_image_descriptor *img_info = NULL;
+ size_t info_size = 0;
+ u32 desc_version = 0;
+ u8 desc_count = 0;
+ size_t desc_size = 0;
+ u32 package_version;
+ u16 *package_version_name;
+
+ ret = efi_search_protocol(*it_handle,
+ &efi_guid_firmware_management_protocol,
+ &handler);
+
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT Unable to find FMP handle (%d)\n",
+ idx);
+ goto out;
+ }
+ fmp = handler->protocol_interface;
+
+ ret = EFI_CALL(fmp->get_image_info(fmp, &info_size, NULL,
+ &desc_version, &desc_count,
+ &desc_size, &package_version,
+ &package_version_name));
+
+ if (ret != EFI_BUFFER_TOO_SMALL) {
+ /*
+ * An input of info_size=0 should always lead
+ * fmp->get_image_info to return BUFFER_TO_SMALL.
+ */
+ EFI_PRINT("ESRT erroneous FMP implementation\n");
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ ret = EFI_CALL(efi_allocate_pool(EFI_BOOT_SERVICES_DATA, info_size,
+ (void **)&img_info));
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT failed to allocate memory for image info\n");
+ goto out;
+ }
+
+ /*
+ * Calls to a FMP get_image_info method do not return the
+ * desc_count value if the return status differs from EFI_SUCCESS.
+ * We need to repeat the call to get_image_info with a properly
+ * sized buffer in order to obtain the real number of images
+ * handled by the FMP.
+ */
+ ret = EFI_CALL(fmp->get_image_info(fmp, &info_size, img_info,
+ &desc_version, &desc_count,
+ &desc_size, &package_version,
+ &package_version_name));
+
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT failed to obtain image info from FMP\n");
+ EFI_CALL(efi_free_pool(img_info));
+ goto out;
+ }
+
+ num_entries += desc_count;
+
+ EFI_CALL(efi_free_pool(img_info));
+ }
+
+ EFI_PRINT("ESRT create table with %d entries\n", num_entries);
+ /*
+ * Allocate an ESRT with the sufficient number of entries to accommodate
+ * all the FMPs in the system.
+ */
+ ret = efi_esrt_allocate_install(num_entries);
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT failed to initialize table\n");
+ goto out;
+ }
+
+ /*
+ * Populate the ESRT entries with all existing FMP.
+ */
+ it_handle = base_handle;
+ for (u32 idx = 0; idx < no_handles; idx++, it_handle++) {
+ ret = EFI_CALL(efi_search_protocol(*it_handle,
+ &efi_guid_firmware_management_protocol,
+ &handler));
+
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT unable to find FMP handle (%d)\n",
+ idx);
+ break;
+ }
+ fmp = handler->protocol_interface;
+
+ ret = efi_esrt_add_from_fmp(fmp);
+ if (ret != EFI_SUCCESS)
+ EFI_PRINT("ESRT failed to add FMP to the table\n");
+ }
+
+out:
+
+ EFI_CALL(efi_free_pool(base_handle));
+
+ return ret;
+}
+
+/**
+ * efi_esrt_new_fmp_notify() - Callback for the EVT_NOTIFY_SIGNAL event raised
+ * when a new FMP protocol instance is registered in the system.
+ */
+static void EFIAPI efi_esrt_new_fmp_notify(struct efi_event *event,
+ void *context)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY();
+
+ ret = efi_esrt_populate();
+ if (ret != EFI_SUCCESS)
+ EFI_PRINT("ESRT failed to populate ESRT entry\n");
+
+ EFI_EXIT(ret);
+}
+
+/**
+ * efi_esrt_register() - Install the ESRT system table.
+ *
+ * Return: status code
+ */
+efi_status_t efi_esrt_register(void)
+{
+ struct efi_event *ev = NULL;
+ void *registration;
+ efi_status_t ret;
+
+ EFI_PRINT("ESRT creation start\n");
+
+ ret = efi_esrt_populate();
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT failed to initiate the table\n");
+ return ret;
+ }
+
+ ret = EFI_CALL(efi_create_event(EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
+ efi_esrt_new_fmp_notify, NULL, NULL, &ev));
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT failed to create event\n");
+ return ret;
+ }
+
+ ret = EFI_CALL(efi_register_protocol_notify(&efi_guid_firmware_management_protocol,
+ ev, &registration));
+ if (ret != EFI_SUCCESS) {
+ EFI_PRINT("ESRT failed to register FMP callback\n");
+ return ret;
+ }
+
+ EFI_PRINT("ESRT table created\n");
+
+ return ret;
+}
diff --git a/lib/efi_loader/efi_file.c b/lib/efi_loader/efi_file.c
index 8ece8e7..204105e 100644
--- a/lib/efi_loader/efi_file.c
+++ b/lib/efi_loader/efi_file.c
@@ -409,6 +409,45 @@ static efi_status_t efi_get_file_size(struct file_handle *fh,
return EFI_SUCCESS;
}
+/**
+ * efi_file_size() - Get the size of a file using an EFI file handle
+ *
+ * @fh: EFI file handle
+ * @size: buffer to fill in the discovered size
+ *
+ * Return: size of the file
+ */
+efi_status_t efi_file_size(struct efi_file_handle *fh, efi_uintn_t *size)
+{
+ struct efi_file_info *info = NULL;
+ efi_uintn_t bs = 0;
+ efi_status_t ret;
+
+ *size = 0;
+ ret = EFI_CALL(fh->getinfo(fh, (efi_guid_t *)&efi_file_info_guid, &bs,
+ info));
+ if (ret != EFI_BUFFER_TOO_SMALL) {
+ ret = EFI_DEVICE_ERROR;
+ goto out;
+ }
+
+ info = malloc(bs);
+ if (!info) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+ ret = EFI_CALL(fh->getinfo(fh, (efi_guid_t *)&efi_file_info_guid, &bs,
+ info));
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ *size = info->file_size;
+
+out:
+ free(info);
+ return ret;
+}
+
static efi_status_t file_read(struct file_handle *fh, u64 *buffer_size,
void *buffer)
{
diff --git a/lib/efi_loader/efi_helper.c b/lib/efi_loader/efi_helper.c
new file mode 100644
index 0000000..d03a736
--- /dev/null
+++ b/lib/efi_loader/efi_helper.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2020, Linaro Limited
+ */
+
+#define LOG_CATEGORY LOGC_EFI
+#include <common.h>
+#include <env.h>
+#include <malloc.h>
+#include <dm.h>
+#include <fs.h>
+#include <efi_load_initrd.h>
+#include <efi_loader.h>
+#include <efi_variable.h>
+
+/**
+ * efi_create_current_boot_var() - Return Boot#### name were #### is replaced by
+ * the value of BootCurrent
+ *
+ * @var_name: variable name
+ * @var_name_size: size of var_name
+ *
+ * Return: Status code
+ */
+static efi_status_t efi_create_current_boot_var(u16 var_name[],
+ size_t var_name_size)
+{
+ efi_uintn_t boot_current_size;
+ efi_status_t ret;
+ u16 boot_current;
+ u16 *pos;
+
+ boot_current_size = sizeof(boot_current);
+ ret = efi_get_variable_int(L"BootCurrent",
+ &efi_global_variable_guid, NULL,
+ &boot_current_size, &boot_current, NULL);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ pos = efi_create_indexed_name(var_name, var_name_size, "Boot",
+ boot_current);
+ if (!pos) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+/**
+ * efi_get_dp_from_boot() - Retrieve and return a device path from an EFI
+ * Boot### variable.
+ * A boot option may contain an array of device paths.
+ * We use a VenMedia() with a specific GUID to identify
+ * the usage of the array members. This function is
+ * used to extract a specific device path
+ *
+ * @guid: vendor GUID of the VenMedia() device path node identifying the
+ * device path
+ *
+ * Return: device path or NULL. Caller must free the returned value
+ */
+struct efi_device_path *efi_get_dp_from_boot(const efi_guid_t guid)
+{
+ struct efi_device_path *file_path = NULL;
+ struct efi_device_path *tmp = NULL;
+ struct efi_load_option lo;
+ void *var_value = NULL;
+ efi_uintn_t size;
+ efi_status_t ret;
+ u16 var_name[16];
+
+ ret = efi_create_current_boot_var(var_name, sizeof(var_name));
+ if (ret != EFI_SUCCESS)
+ return NULL;
+
+ var_value = efi_get_var(var_name, &efi_global_variable_guid, &size);
+ if (!var_value)
+ return NULL;
+
+ ret = efi_deserialize_load_option(&lo, var_value, &size);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ tmp = efi_dp_from_lo(&lo, &size, guid);
+ if (!tmp)
+ goto out;
+
+ /* efi_dp_dup will just return NULL if efi_dp_next is NULL */
+ file_path = efi_dp_dup(efi_dp_next(tmp));
+
+out:
+ efi_free_pool(tmp);
+ free(var_value);
+
+ return file_path;
+}
diff --git a/lib/efi_loader/efi_load_initrd.c b/lib/efi_loader/efi_load_initrd.c
index b9ee883..e2a8063 100644
--- a/lib/efi_loader/efi_load_initrd.c
+++ b/lib/efi_loader/efi_load_initrd.c
@@ -3,9 +3,11 @@
* Copyright (c) 2020, Linaro Limited
*/
+#define LOG_CATEGORY LOGC_EFI
#include <common.h>
#include <efi_loader.h>
#include <efi_load_initrd.h>
+#include <efi_variable.h>
#include <fs.h>
#include <malloc.h>
#include <mapmem.h>
@@ -23,57 +25,56 @@ static const struct efi_load_file_protocol efi_lf2_protocol = {
* 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 = {
+static const struct efi_initrd_dp dp_lf2_handle = {
.vendor = {
{
DEVICE_PATH_TYPE_MEDIA_DEVICE,
DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
- sizeof(dp.vendor),
+ sizeof(dp_lf2_handle.vendor),
},
EFI_INITRD_MEDIA_GUID,
},
.end = {
DEVICE_PATH_TYPE_END,
DEVICE_PATH_SUB_TYPE_END,
- sizeof(dp.end),
+ sizeof(dp_lf2_handle.end),
}
};
+static efi_handle_t efi_initrd_handle;
+
/**
- * get_file_size() - retrieve the size of initramfs, set efi status on error
+ * get_initrd_fp() - Get initrd device path from a FilePathList device path
*
- * @dev: device to read from, e.g. "mmc"
- * @part: device partition, e.g. "0:1"
- * @file: name of file
- * @status: EFI exit code in case of failure
+ * @initrd_fp: the final initrd filepath
*
- * Return: size of file
+ * Return: status code. Caller must free initrd_fp
*/
-static loff_t get_file_size(const char *dev, const char *part, const char *file,
- efi_status_t *status)
+static efi_status_t get_initrd_fp(struct efi_device_path **initrd_fp)
{
- loff_t sz = 0;
- int ret;
-
- ret = fs_set_blk_dev(dev, part, FS_TYPE_ANY);
- if (ret) {
- *status = EFI_NO_MEDIA;
- goto out;
- }
+ const efi_guid_t lf2_initrd_guid = EFI_INITRD_MEDIA_GUID;
+ struct efi_device_path *dp = NULL;
- ret = fs_size(file, &sz);
- if (ret) {
- sz = 0;
- *status = EFI_NOT_FOUND;
- goto out;
- }
+ /*
+ * if bootmgr is setup with and initrd, the device path will be
+ * in the FilePathList[] of our load options in Boot####.
+ * The first device path of the multi instance device path will
+ * start with a VenMedia and the initrds will follow.
+ *
+ * If the device path is not found return EFI_INVALID_PARAMETER.
+ * We can then use this specific return value and not install the
+ * protocol, while allowing the boot to continue
+ */
+ dp = efi_get_dp_from_boot(lf2_initrd_guid);
+ if (!dp)
+ return EFI_INVALID_PARAMETER;
-out:
- return sz;
+ *initrd_fp = dp;
+ return EFI_SUCCESS;
}
/**
- * efi_load_file2initrd() - load initial RAM disk
+ * efi_load_file2_initrd() - load initial RAM disk
*
* This function implements the LoadFile service of the EFI_LOAD_FILE2_PROTOCOL
* in order to load an initial RAM disk requested by the Linux kernel stub.
@@ -93,102 +94,125 @@ 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)
{
- char *filespec;
- efi_status_t status = EFI_NOT_FOUND;
- loff_t file_sz = 0, read_sz = 0;
- char *dev, *part, *file;
- char *pos;
- int ret;
+ struct efi_device_path *initrd_fp = NULL;
+ efi_status_t ret = EFI_NOT_FOUND;
+ struct efi_file_handle *f = NULL;
+ efi_uintn_t bs;
EFI_ENTRY("%p, %p, %d, %p, %p", this, file_path, boot_policy,
buffer_size, buffer);
- filespec = strdup(CONFIG_EFI_INITRD_FILESPEC);
- if (!filespec)
- goto out;
- pos = filespec;
-
if (!this || this != &efi_lf2_protocol ||
!buffer_size) {
- status = EFI_INVALID_PARAMETER;
+ ret = EFI_INVALID_PARAMETER;
goto out;
}
- if (file_path->type != dp.end.type ||
- file_path->sub_type != dp.end.sub_type) {
- status = EFI_INVALID_PARAMETER;
+ if (file_path->type != dp_lf2_handle.end.type ||
+ file_path->sub_type != dp_lf2_handle.end.sub_type) {
+ ret = EFI_INVALID_PARAMETER;
goto out;
}
if (boot_policy) {
- status = EFI_UNSUPPORTED;
+ ret = EFI_UNSUPPORTED;
goto out;
}
- /*
- * expect a string with three space separated parts:
- *
- * * a block device type, e.g. "mmc"
- * * a device and partition identifier, e.g. "0:1"
- * * a file path on the block device, e.g. "/boot/initrd.cpio.gz"
- */
- dev = strsep(&pos, " ");
- if (!dev)
+ ret = get_initrd_fp(&initrd_fp);
+ if (ret != EFI_SUCCESS)
goto out;
- part = strsep(&pos, " ");
- if (!part)
- goto out;
- file = strsep(&pos, " ");
- if (!file)
+
+ /* Open file */
+ f = efi_file_from_path(initrd_fp);
+ if (!f) {
+ log_err("Can't find initrd specified in Boot####\n");
+ ret = EFI_NOT_FOUND;
goto out;
+ }
- file_sz = get_file_size(dev, part, file, &status);
- if (!file_sz)
+ /* Get file size */
+ ret = efi_file_size(f, &bs);
+ if (ret != EFI_SUCCESS)
goto out;
- if (!buffer || *buffer_size < file_sz) {
- status = EFI_BUFFER_TOO_SMALL;
- *buffer_size = file_sz;
+ if (!buffer || *buffer_size < bs) {
+ ret = EFI_BUFFER_TOO_SMALL;
+ *buffer_size = bs;
} 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;
+ ret = EFI_CALL(f->read(f, &bs, (void *)(uintptr_t)buffer));
+ *buffer_size = bs;
+ }
+
+out:
+ efi_free_pool(initrd_fp);
+ if (f)
+ EFI_CALL(f->close(f));
+ return EFI_EXIT(ret);
+}
+
+/**
+ * check_initrd() - Determine if the file defined as an initrd in Boot####
+ * load_options device path is present
+ *
+ * Return: status code
+ */
+static efi_status_t check_initrd(void)
+{
+ struct efi_device_path *initrd_fp = NULL;
+ struct efi_file_handle *f;
+ efi_status_t ret;
+
+ ret = get_initrd_fp(&initrd_fp);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ /*
+ * If the file is not found, but the file path is set, return an error
+ * and trigger the bootmgr fallback
+ */
+ f = efi_file_from_path(initrd_fp);
+ if (!f) {
+ log_err("Can't find initrd specified in Boot####\n");
+ ret = EFI_NOT_FOUND;
+ goto out;
}
+ EFI_CALL(f->close(f));
+
out:
- free(filespec);
- return EFI_EXIT(status);
+ efi_free_pool(initrd_fp);
+ return ret;
}
/**
* efi_initrd_register() - create handle for loading initial RAM disk
*
* This function creates a new handle and installs a Linux specific vendor
- * device path and an EFI_LOAD_FILE_2_PROTOCOL. Linux uses the device path
+ * device path and an EFI_LOAD_FILE2_PROTOCOL. Linux uses the device path
* to identify the handle and then calls the LoadFile service of the
- * EFI_LOAD_FILE_2_PROTOCOL to read the initial RAM disk.
+ * EFI_LOAD_FILE2_PROTOCOL to read the initial RAM disk.
*
* Return: status code
*/
efi_status_t efi_initrd_register(void)
{
- efi_handle_t efi_initrd_handle = NULL;
efi_status_t ret;
+ /*
+ * Allow the user to continue if Boot#### file path is not set for
+ * an initrd
+ */
+ ret = check_initrd();
+ if (ret == EFI_INVALID_PARAMETER)
+ return EFI_SUCCESS;
+ if (ret != EFI_SUCCESS)
+ return ret;
+
ret = EFI_CALL(efi_install_multiple_protocol_interfaces
(&efi_initrd_handle,
/* initramfs */
- &efi_guid_device_path, &dp,
+ &efi_guid_device_path, &dp_lf2_handle,
/* LOAD_FILE2 */
&efi_guid_load_file2_protocol,
(void *)&efi_lf2_protocol,
@@ -196,3 +220,17 @@ efi_status_t efi_initrd_register(void)
return ret;
}
+
+/**
+ * efi_initrd_deregister() - delete the handle for loading initial RAM disk
+ *
+ * This will delete the handle containing the Linux specific vendor device
+ * path and EFI_LOAD_FILE2_PROTOCOL for loading an initrd
+ *
+ * Return: status code
+ */
+void efi_initrd_deregister(void)
+{
+ efi_delete_handle(efi_initrd_handle);
+ efi_initrd_handle = NULL;
+}
diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c
index b1c5125..3c5cf9a 100644
--- a/lib/efi_loader/efi_setup.c
+++ b/lib/efi_loader/efi_setup.c
@@ -227,6 +227,12 @@ efi_status_t efi_init_obj_list(void)
if (ret != EFI_SUCCESS)
goto out;
+ if (IS_ENABLED(CONFIG_EFI_ESRT)) {
+ ret = efi_esrt_register();
+ if (ret != EFI_SUCCESS)
+ goto out;
+ }
+
if (IS_ENABLED(CONFIG_EFI_TCG2_PROTOCOL)) {
ret = efi_tcg2_register();
if (ret != EFI_SUCCESS)
diff --git a/lib/efi_loader/efi_tcg2.c b/lib/efi_loader/efi_tcg2.c
index 797d6eb..0904684 100644
--- a/lib/efi_loader/efi_tcg2.c
+++ b/lib/efi_loader/efi_tcg2.c
@@ -13,6 +13,7 @@
#include <efi_loader.h>
#include <efi_tcg2.h>
#include <log.h>
+#include <version.h>
#include <tpm-v2.h>
#include <u-boot/sha1.h>
#include <u-boot/sha256.h>
@@ -958,6 +959,23 @@ out:
}
/**
+ * tcg2_uninit - remove the final event table and free efi memory on failures
+ */
+void tcg2_uninit(void)
+{
+ efi_status_t ret;
+
+ ret = efi_install_configuration_table(&efi_guid_final_events, NULL);
+ if (ret != EFI_SUCCESS)
+ log_err("Failed to delete final events config table\n");
+
+ efi_free_pool(event_log.buffer);
+ event_log.buffer = NULL;
+ efi_free_pool(event_log.final_buffer);
+ event_log.final_buffer = NULL;
+}
+
+/**
* create_final_event() - Create the final event and install the config
* defined by the TCG EFI spec
*/
@@ -983,10 +1001,6 @@ static efi_status_t create_final_event(void)
event_log.final_pos = sizeof(*final_event);
ret = efi_install_configuration_table(&efi_guid_final_events,
final_event);
- if (ret != EFI_SUCCESS)
- goto out;
-
- return EFI_SUCCESS;
out:
return ret;
}
@@ -1041,6 +1055,40 @@ static efi_status_t efi_init_event_log(void)
event_log.last_event_size = event_log.pos;
ret = create_final_event();
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ return EFI_SUCCESS;
+out:
+ tcg2_uninit();
+ return ret;
+}
+
+/**
+ * efi_append_scrtm_version - Append an S-CRTM EV_S_CRTM_VERSION event on the
+ * eventlog and extend the PCRs
+ *
+ * @dev: TPM device
+ *
+ * @Return: status code
+ */
+static efi_status_t efi_append_scrtm_version(struct udevice *dev)
+{
+ struct tpml_digest_values digest_list;
+ u8 ver[] = U_BOOT_VERSION_STRING;
+ const int pcr_index = 0;
+ efi_status_t ret;
+
+ ret = tcg2_create_digest(ver, sizeof(ver), &digest_list);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ ret = tcg2_pcr_extend(dev, pcr_index, &digest_list);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ ret = tcg2_agile_log_append(pcr_index, EV_S_CRTM_VERSION, &digest_list,
+ sizeof(ver), ver);
out:
return ret;
@@ -1055,23 +1103,34 @@ out:
*/
efi_status_t efi_tcg2_register(void)
{
- efi_status_t ret;
+ efi_status_t ret = EFI_SUCCESS;
struct udevice *dev;
ret = platform_get_tpm2_device(&dev);
if (ret != EFI_SUCCESS) {
log_warning("Unable to find TPMv2 device\n");
- return EFI_SUCCESS;
+ ret = EFI_SUCCESS;
+ goto out;
}
ret = efi_init_event_log();
if (ret != EFI_SUCCESS)
- return ret;
+ goto fail;
+
+ ret = efi_append_scrtm_version(dev);
+ if (ret != EFI_SUCCESS)
+ goto out;
ret = efi_add_protocol(efi_root, &efi_guid_tcg2_protocol,
(void *)&efi_tcg2_protocol);
- if (ret != EFI_SUCCESS)
+ if (ret != EFI_SUCCESS) {
log_err("Cannot install EFI_TCG2_PROTOCOL\n");
+ goto fail;
+ }
+out:
+ return ret;
+fail:
+ tcg2_uninit();
return ret;
}
diff --git a/lib/efi_loader/efi_var_common.c b/lib/efi_loader/efi_var_common.c
index 1c74592..b11ed91 100644
--- a/lib/efi_loader/efi_var_common.c
+++ b/lib/efi_loader/efi_var_common.c
@@ -9,6 +9,7 @@
#include <common.h>
#include <efi_loader.h>
#include <efi_variable.h>
+#include <stdlib.h>
enum efi_secure_mode {
EFI_MODE_SETUP,
@@ -343,3 +344,35 @@ enum efi_auth_var_type efi_auth_var_get_type(u16 *name, const efi_guid_t *guid)
}
return EFI_AUTH_VAR_NONE;
}
+
+/**
+ * efi_get_var() - read value of an EFI variable
+ *
+ * @name: variable name
+ * @start: vendor GUID
+ * @size: size of allocated buffer
+ *
+ * Return: buffer with variable data or NULL
+ */
+void *efi_get_var(u16 *name, const efi_guid_t *vendor, efi_uintn_t *size)
+{
+ efi_status_t ret;
+ void *buf = NULL;
+
+ *size = 0;
+ ret = efi_get_variable_int(name, vendor, NULL, size, buf, NULL);
+ if (ret == EFI_BUFFER_TOO_SMALL) {
+ buf = malloc(*size);
+ if (!buf)
+ return NULL;
+ ret = efi_get_variable_int(name, vendor, NULL, size, buf, NULL);
+ }
+
+ if (ret != EFI_SUCCESS) {
+ free(buf);
+ *size = 0;
+ return NULL;
+ }
+
+ return buf;
+}