[PATCH 1/2] efi: Add ESRT to the EFI system table

Jose Marinho jose.marinho at arm.com
Mon Feb 8 13:52:45 CET 2021


The ESRT is initialised during efi_init_objlist after
efi_initialize_system_table().

The ESRT is initially created with size for 50 FW image entries.
The ESRT is resized when it runs out of space. Every resize adds 50
additional entries.
The ESRT is populated from information provided by FMP instances only.

Signed-off-by: Jose Marinho <jose.marinho at arm.com>

CC: Heinrich Schuchardt	<xypron.glpk at gmx.de>
CC: Sughosh Ganu <sughosh.ganu at linaro.org>
CC: AKASHI Takahiro <takahiro.akashi at linaro.org>
CC: Ilias Apalodimas <ilias.apalodimas at linaro.org>
CC: Andre Przywara <andre.przywara at arm.com>
CC: Alexander Graf <agraf at csgraf.de>
CC: nd at arm.com

---
 cmd/efidebug.c                |   4 +
 include/efi_api.h             |  21 ++
 include/efi_loader.h          |  19 ++
 lib/efi_loader/Kconfig        |   7 +
 lib/efi_loader/Makefile       |   1 +
 lib/efi_loader/efi_boottime.c |   2 -
 lib/efi_loader/efi_capsule.c  |   7 +
 lib/efi_loader/efi_esrt.c     | 439 ++++++++++++++++++++++++++++++++++
 lib/efi_loader/efi_setup.c    |   6 +
 9 files changed, 504 insertions(+), 2 deletions(-)
 create mode 100644 lib/efi_loader/efi_esrt.c

diff --git a/cmd/efidebug.c b/cmd/efidebug.c
index 83bc2196a5..4160dde1cf 100644
--- a/cmd/efidebug.c
+++ b/cmd/efidebug.c
@@ -458,6 +458,10 @@ static const struct {
 		"Block IO",
 		EFI_BLOCK_IO_PROTOCOL_GUID,
 	},
+	{
+		"EFI System Resource Table",
+		EFI_SYSTEM_RESOURCE_TABLE_GUID,
+	},
 	{
 		"Simple File System",
 		EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID,
diff --git a/include/efi_api.h b/include/efi_api.h
index 48e48a6263..7eb15bd44c 100644
--- a/include/efi_api.h
+++ b/include/efi_api.h
@@ -1722,6 +1722,23 @@ struct efi_load_file_protocol {
 					 void *buffer);
 };
 
+struct efi_system_resource_entry {
+	efi_guid_t fw_class;
+	uint32_t fw_type;
+	uint32_t fw_version;
+	uint32_t lowest_supported_fw_version;
+	uint32_t capsule_flags;
+	uint32_t last_attempt_version;
+	uint32_t last_attempt_status;
+} __packed;
+
+struct efi_system_resource_table {
+	uint32_t fw_resource_count;
+	uint32_t fw_resource_count_max;
+	uint64_t fw_resource_version;
+	struct efi_system_resource_entry entries[];
+} __packed;
+
 /* Boot manager load options */
 #define LOAD_OPTION_ACTIVE		0x00000001
 #define LOAD_OPTION_FORCE_RECONNECT	0x00000002
@@ -1740,6 +1757,10 @@ struct efi_load_file_protocol {
 #define ESRT_FW_TYPE_DEVICEFIRMWARE	0x00000002
 #define ESRT_FW_TYPE_UEFIDRIVER		0x00000003
 
+#define EFI_SYSTEM_RESOURCE_TABLE_GUID\
+	EFI_GUID(0xb122a263, 0x3661, 0x4f68,\
+		0x99, 0x29, 0x78, 0xf8, 0xb0, 0xd6, 0x21, 0x80 )
+
 /* Last Attempt Status Values */
 #define LAST_ATTEMPT_STATUS_SUCCESS			0x00000000
 #define LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL		0x00000001
diff --git a/include/efi_loader.h b/include/efi_loader.h
index f470bbd636..c85c540041 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -884,4 +884,23 @@ static inline efi_status_t efi_launch_capsules(void)
 
 #endif /* CONFIG_IS_ENABLED(EFI_LOADER) */
 
+/*
+ * Install the ESRT system table.
+ *
+ * @return	status code
+ */
+efi_status_t efi_esrt_register(void);
+
+/**
+ * efi_esrt_add_from_fmp() - Populates a sequence of ESRT entries from the FW
+ * images in the FMP.
+ *
+ * @fmp:        the fmp 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
+ */
+efi_status_t efi_esrt_add_from_fmp(struct efi_firmware_management_protocol *fmp);
+
 #endif /* _EFI_LOADER_H */
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
index e729f727df..12b29180a0 100644
--- a/lib/efi_loader/Kconfig
+++ b/lib/efi_loader/Kconfig
@@ -347,4 +347,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_LOADER
+	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 10b42e8847..dec791b310 100644
--- a/lib/efi_loader/Makefile
+++ b/lib/efi_loader/Makefile
@@ -52,6 +52,7 @@ obj-y += efi_variable.o
 obj-$(CONFIG_EFI_VARIABLES_PRESEED) += efi_var_seed.o
 endif
 obj-y += efi_watchdog.o
+obj-y += 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_boottime.c b/lib/efi_loader/efi_boottime.c
index ce658a8e73..9b0b15571a 100644
--- a/lib/efi_loader/efi_boottime.c
+++ b/lib/efi_loader/efi_boottime.c
@@ -1148,8 +1148,6 @@ efi_status_t efi_add_protocol(const efi_handle_t handle,
 		}
 	}
 
-	if (!guidcmp(&efi_guid_device_path, protocol))
-		EFI_PRINT("installed device path '%pD'\n", protocol_interface);
 	return EFI_SUCCESS;
 }
 
diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
index 0d5a7b63ec..60703bcdaa 100644
--- a/lib/efi_loader/efi_capsule.c
+++ b/lib/efi_loader/efi_capsule.c
@@ -404,6 +404,13 @@ static efi_status_t efi_capsule_update_firmware(
 			efi_free_pool(abort_reason);
 			goto out;
 		}
+
+#ifdef CONFIG_EFI_ESRT
+		/* Update the ESRT entries corresponding to fmp. */
+		ret = efi_esrt_add_from_fmp(fmp);
+		if (ret != EFI_SUCCESS)
+			log_warning("EFI Capsule: failed to populate ESRT entry\n");
+#endif
 	}
 
 out:
diff --git a/lib/efi_loader/efi_esrt.c b/lib/efi_loader/efi_esrt.c
new file mode 100644
index 0000000000..34133346ca
--- /dev/null
+++ b/lib/efi_loader/efi_esrt.c
@@ -0,0 +1,439 @@
+// 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>
+
+static efi_guid_t esrt_guid = EFI_SYSTEM_RESOURCE_TABLE_GUID;
+
+struct efi_system_resource_table *esrt;
+
+#define EFI_ESRT_MIN_RESIZE_ENTIRES 50
+#define EFI_ESRT_VERSION 1
+
+/**
+ * efi_esrt_image_info_to_entry() - copy the information present in a fw image
+ * descriptor to a ESRT entry.
+ *
+ * @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
+ *
+ */
+void
+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)
+{
+	guidcpy(&entry->fw_class, &img_info->image_type_id);
+	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 = EFI_SUCCESS;
+	}
+}
+
+/**
+ * efi_esrt_entries_to_size() - Obtain the bytes used by an ESRT
+ * datastructure with @num_entries.
+ *
+ * @num_entries: the ESRT which we obtain the filled size in bytes.
+ *
+ * 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.
+ *
+ * @bt	       : pointer to the boottime services structure.
+ * @num_entries: the number of entries that the ESRT will hold.
+ *
+ * Return:
+ * - pointer to the ESRT if successful.
+ * - NULL otherwise.
+ */
+static
+struct efi_system_resource_table *efi_esrt_allocate_install(struct efi_boot_services *bt,
+							    u32 num_entries)
+{
+	efi_status_t ret;
+	struct efi_system_resource_table *new_esrt;
+	u32 size = efi_esrt_entries_to_size(num_entries);
+
+	/* Allocated pages must be on the lower 32bit address space. */
+	new_esrt = (struct efi_system_resource_table *)(uintptr_t)U32_MAX;
+
+	/* Reserve num_pages for ESRT */
+	ret = EFI_CALL(bt->allocate_pool(EFI_RUNTIME_SERVICES_DATA,
+					 size,
+					 (void **)&new_esrt));
+
+	if (ret != EFI_SUCCESS) {
+		EFI_PRINT("ESRT Failed to allocate memory for ESRT with %d entries (%d bytes)\n",
+			  num_entries, efi_esrt_entries_to_size(num_entries));
+
+		return NULL;
+	}
+
+	new_esrt->fw_resource_count_max = num_entries;
+	new_esrt->fw_resource_version = EFI_ESRT_VERSION;
+
+	/* Install the ESRT in the system configuration table. */
+	ret = EFI_CALL(bt->install_configuration_table(&esrt_guid, (void *)new_esrt));
+	if (ret != EFI_SUCCESS)
+		EFI_PRINT("ESRT failed to install the ESRT\n");
+
+	return new_esrt;
+}
+
+/**
+ * efi_esrt_resize() - Increments the ESRT to hold an addition @inc_entries FW
+ * image entries.
+ *
+ * @inc_entries: the additional number of FW image entries to add to the ESRT.
+ *
+ * Return:
+ * - EFI_SUCCESS if ESRT correctly resized.
+ * - Error code otherwise.
+ */
+static efi_status_t efi_esrt_resize(u32 inc_entries)
+{
+	struct efi_boot_services *bt = systab.boottime;
+	struct efi_system_resource_table *old_esrt = esrt;
+	struct efi_system_resource_table *new_esrt;
+
+	u32 old_max_entries = old_esrt->fw_resource_count_max;
+	u32 old_filled_size;
+	efi_status_t ret;
+
+	if (!bt) {
+		EFI_PRINT("ESRT cannot obtain pointer to BS\n");
+		return EFI_NOT_READY;
+	}
+
+	/* Allocate and install the bigger ESRT. */
+	new_esrt = efi_esrt_allocate_install(bt, old_max_entries + inc_entries);
+	if (!esrt) {
+		EFI_PRINT("ESRT failed to allocate and install resized ESRT\n");
+		esrt = old_esrt;
+		return EFI_OUT_OF_RESOURCES;
+	}
+	esrt = new_esrt;
+
+	old_filled_size = efi_esrt_entries_to_size(old_esrt->fw_resource_count_max);
+
+	/* Copy the old ESRT entries onto the new table. */
+	memcpy(new_esrt->entries, old_esrt->entries, old_filled_size
+		- sizeof(struct efi_system_resource_table));
+
+	new_esrt->fw_resource_count =  old_esrt->fw_resource_count;
+
+	ret = EFI_CALL(bt->free_pool(old_esrt));
+	if (ret != EFI_SUCCESS) {
+		/*
+		 * This should never happen.
+		 *
+		 * if ret!= EFI_SUCCESS then old_esrt is invalid.
+		 */
+		panic("EFI ESRT: could not deallocate old ESRT at %p\n", old_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,
+ *   - boot services are not present.
+ */
+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_status_t ret;
+
+		/* ESRT is full, attempt to extend the ESRT entries. */
+		ret = efi_esrt_resize(EFI_ESRT_MIN_RESIZE_ENTIRES);
+		if (ret != EFI_SUCCESS) {
+			EFI_PRINT("ESRT full, failed to resize\n");
+			return NULL;
+		}
+
+		entry = esrt->entries;
+		EFI_PRINT("ESRT table resized\n");
+	}
+
+	/*
+	 * 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
+ */
+efi_status_t efi_esrt_add_from_fmp(struct efi_firmware_management_protocol *fmp)
+{
+	struct efi_boot_services *bt = systab.boottime;
+	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;
+	efi_status_t ret = EFI_SUCCESS;
+
+	if (!bt) {
+		EFI_PRINT("ESRT cannot obtain pointer to BS\n");
+		return EFI_NOT_READY;
+	}
+
+	/*
+	 * 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 = 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(bt->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 = fmp->get_image_info(fmp, &info_size, img_info,
+			&desc_version, &desc_count,
+			&desc_size, NULL, NULL);
+	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) {
+			efi_esrt_image_info_to_entry(cur_img_info, entry,
+						     desc_version, image_type,
+						     flags);
+		} else {
+			EFI_PRINT("ESRT failed to add entry for %pUl\n",
+				  &cur_img_info->image_type_id);
+			continue;
+		}
+	}
+
+out:
+	EFI_CALL(bt->free_pool(img_info));
+	return EFI_SUCCESS;
+}
+
+static void EFIAPI efi_esrt_new_fmp_notify(struct efi_event *event,
+					   void *context)
+{
+	efi_status_t ret;
+	efi_handle_t handle = NULL;
+	struct efi_firmware_management_protocol *fmp;
+	struct efi_boot_services *bt = systab.boottime;
+	size_t handle_buff_size = sizeof(handle);
+
+	if (!bt) {
+		EFI_PRINT("ESRT cannot obtain pointer to BS\n");
+		return;
+	}
+
+	/* Obtain a single handle of the FMP type. */
+	ret = EFI_CALL(bt->locate_handle(BY_PROTOCOL,
+					 &efi_guid_firmware_management_protocol,
+					 NULL,
+					 &handle_buff_size,
+					 &handle
+					 ));
+	if (ret != EFI_SUCCESS) {
+		EFI_PRINT("ESRT cannot find FMP handle\n");
+		return;
+	}
+
+	/* Translate the received handle to a FMP object. */
+	ret = EFI_CALL(bt->handle_protocol(handle,
+					   &efi_guid_firmware_management_protocol,
+					   (void **)&fmp));
+	if (ret != EFI_SUCCESS) {
+		EFI_PRINT(" ESRT cannot obtain FMP\n");
+		return;
+	}
+
+	/* Add all the FW images managed by FMP into the ESRT. */
+	ret = efi_esrt_add_from_fmp(fmp);
+	if (ret != EFI_SUCCESS) {
+		EFI_PRINT("ESRT failed to populate ESRT entry\n");
+		return;
+	}
+}
+
+/**
+ * efi_esrt_register() - Install the ESRT system table.
+ *
+ * Return: status code
+ */
+efi_status_t efi_esrt_register(void)
+{
+	struct efi_boot_services *bt = systab.boottime;
+	struct efi_event *ev = NULL;
+	void *registration;
+	u32 num_entries = EFI_ESRT_MIN_RESIZE_ENTIRES;
+	efi_status_t ret;
+
+	if (!bt) {
+		EFI_PRINT("ESRT cannot obtain pointer to BS\n");
+		return EFI_NOT_READY;
+	}
+
+	EFI_PRINT("ESRT creation start\n");
+
+	esrt = efi_esrt_allocate_install(bt, num_entries);
+	if (!esrt) {
+		EFI_PRINT("ESRT failed to initialize ESRT\n");
+		return EFI_NOT_READY;
+	}
+
+	ret = EFI_CALL(bt->create_event(EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
+					efi_esrt_new_fmp_notify, NULL, &ev));
+	if (ret != EFI_SUCCESS) {
+		EFI_PRINT("ESRT failed to create event\n");
+		return ret;
+	}
+
+	ret = EFI_CALL(bt->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_setup.c b/lib/efi_loader/efi_setup.c
index 5800cbf6d4..0183abd09e 100644
--- a/lib/efi_loader/efi_setup.c
+++ b/lib/efi_loader/efi_setup.c
@@ -231,6 +231,12 @@ efi_status_t efi_init_obj_list(void)
 	if (ret != EFI_SUCCESS)
 		goto out;
 
+#ifdef CONFIG_EFI_ESRT
+	ret = efi_esrt_register();
+	if (ret != EFI_SUCCESS)
+		goto out;
+#endif
+
 	if (IS_ENABLED(CONFIG_EFI_TCG2_PROTOCOL)) {
 		ret = efi_tcg2_register();
 		if (ret != EFI_SUCCESS)
-- 
2.17.1



More information about the U-Boot mailing list