aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2022-11-01 09:32:21 -0400
committerTom Rini <trini@konsulko.com>2022-11-01 09:32:21 -0400
commitc8d9ff634fc429db5acf2f5386ea937f0fef1ae7 (patch)
tree5ee0a5efa73f325b172a16db7551af87725fdb1f /lib
parenta90afc6730e6c67ad37f4c98a02891a93b4ff971 (diff)
parent75f11c3bfdcfbadad0265eda74c372e52423ae4c (diff)
Merge branch '2022-10-31-FWU-add-FWU-multi-bank-update-feature-support'
To quote the author: The patchset adds support for the FWU Multi Bank Update[1] feature. Certain aspects of the Dependable Boot[2] specification have also been implemented. The FWU multi bank update feature is used for supporting multiple sets(also called banks) of firmware image(s), allowing the platform to boot from a different bank, in case it fails to boot from the active bank. This functionality is supported by keeping the relevant information in a structure called metadata, which provides information on the images. Among other parameters, the metadata structure contains information on the currect active bank that is being used to boot image(s). Functionality is being added to work with the UEFI capsule driver in u-boot. The metadata is read to gather information on the update bank, which is the bank to which the firmware images would be flashed to. On a successful completion of the update of all components, the active bank field in the metadata is updated, to reflect the bank from which the platform will boot on the subsequent boots. Currently, the feature is being enabled on the STM32MP157C-DK2 and Synquacer boards. The DK2 board boots a FIP image from a uSD card partitioned with the GPT partioning scheme, while the Synquacer board boots a FIP image from a MTD partitioned SPI NOR flash device. This feature also requires changes in a previous stage of bootloader, which parses the metadata and selects the bank to boot the image(s) from. Support has being added in tf-a(BL2 stage) for the STM32MP157C-DK2 board to boot the active bank images. These changes have been merged to the upstream tf-a repository. The patch for adding a python test for the feature has been developed, and was sent in the version 5 of the patches[3]. However, the test script depends on adding support for the feature on MTD SPI NOR devices, and that is being done as part of the Synquacer patches. Hence these set of patches do not have the test script for the feature. That will be added through the patches for adding support for the feauture on Synquacer platform. [1] - https://developer.arm.com/documentation/den0118/a [2] - https://git.codelinaro.org/linaro/dependable-boot/mbfw/uploads/6f7ddfe3be24e18d4319e108a758d02e/mbfw.pdf [3] - https://lists.denx.de/pipermail/u-boot/2022-June/485992.html
Diffstat (limited to 'lib')
-rw-r--r--lib/Kconfig6
-rw-r--r--lib/Makefile1
-rw-r--r--lib/efi_loader/efi_capsule.c210
-rw-r--r--lib/efi_loader/efi_firmware.c14
-rw-r--r--lib/fwu_updates/Kconfig33
-rw-r--r--lib/fwu_updates/Makefile7
-rw-r--r--lib/fwu_updates/fwu.c719
-rw-r--r--lib/fwu_updates/fwu_gpt.c123
8 files changed, 1111 insertions, 2 deletions
diff --git a/lib/Kconfig b/lib/Kconfig
index 6121c80dc8..6abe1d0a86 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -978,3 +978,9 @@ config LMB_RESERVED_REGIONS
memory blocks.
endmenu
+
+menu "FWU Multi Bank Updates"
+
+source lib/fwu_updates/Kconfig
+
+endmenu
diff --git a/lib/Makefile b/lib/Makefile
index e3deb15287..f2cfd1e428 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_EFI) += efi/
obj-$(CONFIG_EFI_LOADER) += efi_driver/
obj-$(CONFIG_EFI_LOADER) += efi_loader/
obj-$(CONFIG_CMD_BOOTEFI_SELFTEST) += efi_selftest/
+obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu_updates/
obj-$(CONFIG_LZMA) += lzma/
obj-$(CONFIG_BZIP2) += bzip2/
obj-$(CONFIG_FIT) += libfdt/
diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
index 397e393a18..1163a2ee30 100644
--- a/lib/efi_loader/efi_capsule.c
+++ b/lib/efi_loader/efi_capsule.c
@@ -14,6 +14,7 @@
#include <env.h>
#include <fdtdec.h>
#include <fs.h>
+#include <fwu.h>
#include <hang.h>
#include <malloc.h>
#include <mapmem.h>
@@ -32,6 +33,12 @@ static const efi_guid_t efi_guid_firmware_management_capsule_id =
EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
const efi_guid_t efi_guid_firmware_management_protocol =
EFI_FIRMWARE_MANAGEMENT_PROTOCOL_GUID;
+const efi_guid_t fwu_guid_os_request_fw_revert =
+ FWU_OS_REQUEST_FW_REVERT_GUID;
+const efi_guid_t fwu_guid_os_request_fw_accept =
+ FWU_OS_REQUEST_FW_ACCEPT_GUID;
+
+#define FW_ACCEPT_OS (u32)0x8000
#ifdef CONFIG_EFI_CAPSULE_ON_DISK
/* for file system access */
@@ -386,6 +393,132 @@ efi_status_t efi_capsule_authenticate(const void *capsule, efi_uintn_t capsule_s
}
#endif /* CONFIG_EFI_CAPSULE_AUTHENTICATE */
+static __maybe_unused bool fwu_empty_capsule(struct efi_capsule_header *capsule)
+{
+ return !guidcmp(&capsule->capsule_guid,
+ &fwu_guid_os_request_fw_revert) ||
+ !guidcmp(&capsule->capsule_guid,
+ &fwu_guid_os_request_fw_accept);
+}
+
+static __maybe_unused efi_status_t fwu_to_efi_error(int err)
+{
+ efi_status_t ret;
+
+ switch(err) {
+ case 0:
+ ret = EFI_SUCCESS;
+ break;
+ case -ERANGE:
+ case -EIO:
+ ret = EFI_DEVICE_ERROR;
+ break;
+ case -EINVAL:
+ ret = EFI_INVALID_PARAMETER;
+ break;
+ case -ENODEV:
+ ret = EFI_NOT_FOUND;
+ break;
+ default:
+ ret = EFI_OUT_OF_RESOURCES;
+ }
+
+ return ret;
+}
+
+static __maybe_unused efi_status_t fwu_empty_capsule_process(
+ struct efi_capsule_header *capsule)
+{
+ int status;
+ u32 active_idx;
+ efi_guid_t *image_guid;
+ efi_status_t ret = EFI_INVALID_PARAMETER;
+
+ if (!guidcmp(&capsule->capsule_guid,
+ &fwu_guid_os_request_fw_revert)) {
+ /*
+ * One of the previously updated image has
+ * failed the OS acceptance test. OS has
+ * requested to revert back to the earlier
+ * boot index
+ */
+ status = fwu_revert_boot_index();
+ ret = fwu_to_efi_error(status);
+ if (ret == EFI_SUCCESS)
+ log_debug("Reverted the FWU active_index. Recommend rebooting the system\n");
+ else
+ log_err("Failed to revert the FWU boot index\n");
+ } else if (!guidcmp(&capsule->capsule_guid,
+ &fwu_guid_os_request_fw_accept)) {
+ /*
+ * Image accepted by the OS. Set the acceptance
+ * status for the image.
+ */
+ image_guid = (void *)(char *)capsule +
+ capsule->header_size;
+
+ status = fwu_get_active_index(&active_idx);
+ ret = fwu_to_efi_error(status);
+ if (ret != EFI_SUCCESS) {
+ log_err("Unable to get the active_index from the FWU metadata\n");
+ return ret;
+ }
+
+ status = fwu_accept_image(image_guid, active_idx);
+ ret = fwu_to_efi_error(status);
+ if (ret != EFI_SUCCESS)
+ log_err("Unable to set the Accept bit for the image %pUs\n",
+ image_guid);
+ }
+
+ return ret;
+}
+
+static __maybe_unused void fwu_post_update_checks(
+ struct efi_capsule_header *capsule,
+ bool *fw_accept_os, bool *capsule_update)
+{
+ if (fwu_empty_capsule(capsule))
+ *capsule_update = false;
+ else
+ if (!*fw_accept_os)
+ *fw_accept_os =
+ capsule->flags & FW_ACCEPT_OS ? true : false;
+}
+
+static __maybe_unused efi_status_t fwu_post_update_process(bool fw_accept_os)
+{
+ int status;
+ uint update_index;
+ efi_status_t ret;
+
+ status = fwu_plat_get_update_index(&update_index);
+ if (status < 0) {
+ log_err("Failed to get the FWU update_index value\n");
+ return EFI_DEVICE_ERROR;
+ }
+
+ /*
+ * All the capsules have been updated successfully,
+ * update the FWU metadata.
+ */
+ log_debug("Update Complete. Now updating active_index to %u\n",
+ update_index);
+ status = fwu_set_active_index(update_index);
+ ret = fwu_to_efi_error(status);
+ if (ret != EFI_SUCCESS) {
+ log_err("Failed to update FWU metadata index values\n");
+ } else {
+ log_debug("Successfully updated the active_index\n");
+ if (fw_accept_os) {
+ status = fwu_trial_state_ctr_start();
+ if (status < 0)
+ ret = EFI_DEVICE_ERROR;
+ }
+ }
+
+ return ret;
+}
/**
* efi_capsule_update_firmware - update firmware from capsule
@@ -408,7 +541,32 @@ static efi_status_t efi_capsule_update_firmware(
int item;
struct efi_firmware_management_protocol *fmp;
u16 *abort_reason;
+ efi_guid_t *image_type_id;
efi_status_t ret = EFI_SUCCESS;
+ int status;
+ uint update_index;
+ bool fw_accept_os;
+
+ if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {
+ if (fwu_empty_capsule_checks_pass() &&
+ fwu_empty_capsule(capsule_data))
+ return fwu_empty_capsule_process(capsule_data);
+
+ if (!fwu_update_checks_pass()) {
+ log_err("FWU checks failed. Cannot start update\n");
+ return EFI_INVALID_PARAMETER;
+ }
+
+
+ /* Obtain the update_index from the platform */
+ status = fwu_plat_get_update_index(&update_index);
+ if (status < 0) {
+ log_err("Failed to get the FWU update_index value\n");
+ return EFI_DEVICE_ERROR;
+ }
+
+ fw_accept_os = capsule_data->flags & FW_ACCEPT_OS ? 0x1 : 0x0;
+ }
/* sanity check */
if (capsule_data->header_size < sizeof(*capsule) ||
@@ -495,6 +653,34 @@ static efi_status_t efi_capsule_update_firmware(
efi_free_pool(abort_reason);
goto out;
}
+
+ if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {
+ image_type_id = &image->update_image_type_id;
+ if (!fw_accept_os) {
+ /*
+ * The OS will not be accepting the firmware
+ * images. Set the accept bit of all the
+ * images contained in this capsule.
+ */
+ status = fwu_accept_image(image_type_id,
+ update_index);
+ } else {
+ status = fwu_clear_accept_image(image_type_id,
+ update_index);
+ }
+ ret = fwu_to_efi_error(status);
+ if (ret != EFI_SUCCESS) {
+ log_err("Unable to %s the accept bit for the image %pUs\n",
+ fw_accept_os ? "clear" : "set",
+ image_type_id);
+ goto out;
+ }
+
+ log_debug("%s the accepted bit for Image %pUs\n",
+ fw_accept_os ? "Cleared" : "Set",
+ image_type_id);
+ }
+
}
out:
@@ -1103,6 +1289,9 @@ efi_status_t efi_launch_capsules(void)
u16 **files;
unsigned int nfiles, index, i;
efi_status_t ret;
+ bool capsule_update = true;
+ bool update_status = true;
+ bool fw_accept_os = false;
if (check_run_capsules() != EFI_SUCCESS)
return EFI_SUCCESS;
@@ -1130,12 +1319,19 @@ efi_status_t efi_launch_capsules(void)
ret = efi_capsule_read_file(files[i], &capsule);
if (ret == EFI_SUCCESS) {
ret = efi_capsule_update_firmware(capsule);
- if (ret != EFI_SUCCESS)
+ if (ret != EFI_SUCCESS) {
log_err("Applying capsule %ls failed.\n",
files[i]);
- else
+ update_status = false;
+ } else {
log_info("Applying capsule %ls succeeded.\n",
files[i]);
+ if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {
+ fwu_post_update_checks(capsule,
+ &fw_accept_os,
+ &capsule_update);
+ }
+ }
/* create CapsuleXXXX */
set_capsule_result(index, capsule, ret);
@@ -1143,6 +1339,7 @@ efi_status_t efi_launch_capsules(void)
free(capsule);
} else {
log_err("Reading capsule %ls failed\n", files[i]);
+ update_status = false;
}
/* delete a capsule either in case of success or failure */
ret = efi_capsule_delete_file(files[i]);
@@ -1150,8 +1347,17 @@ efi_status_t efi_launch_capsules(void)
log_err("Deleting capsule %ls failed\n",
files[i]);
}
+
efi_capsule_scan_done();
+ if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {
+ if (capsule_update == true && update_status == true) {
+ ret = fwu_post_update_process(fw_accept_os);
+ } else if (capsule_update == true && update_status == false) {
+ log_err("All capsules were not updated. Not updating FWU metadata\n");
+ }
+ }
+
for (i = 0; i < nfiles; i++)
free(files[i]);
free(files);
diff --git a/lib/efi_loader/efi_firmware.c b/lib/efi_loader/efi_firmware.c
index 30cafd15ca..93e2b01c07 100644
--- a/lib/efi_loader/efi_firmware.c
+++ b/lib/efi_loader/efi_firmware.c
@@ -10,6 +10,7 @@
#include <charset.h>
#include <dfu.h>
#include <efi_loader.h>
+#include <fwu.h>
#include <image.h>
#include <signatures.h>
@@ -389,6 +390,7 @@ efi_status_t EFIAPI efi_firmware_raw_set_image(
efi_status_t (*progress)(efi_uintn_t completion),
u16 **abort_reason)
{
+ int ret;
efi_status_t status;
EFI_ENTRY("%p %d %p %zu %p %p %p\n", this, image_index, image,
@@ -401,6 +403,18 @@ efi_status_t EFIAPI efi_firmware_raw_set_image(
if (status != EFI_SUCCESS)
return EFI_EXIT(status);
+ if (IS_ENABLED(CONFIG_FWU_MULTI_BANK_UPDATE)) {
+ /*
+ * Based on the value of update bank, derive the
+ * image index value.
+ */
+ ret = fwu_get_image_index(&image_index);
+ if (ret) {
+ log_debug("Unable to get FWU image_index\n");
+ return EFI_EXIT(EFI_DEVICE_ERROR);
+ }
+ }
+
if (dfu_write_by_alt(image_index - 1, (void *)image, image_size,
NULL, NULL))
return EFI_EXIT(EFI_DEVICE_ERROR);
diff --git a/lib/fwu_updates/Kconfig b/lib/fwu_updates/Kconfig
new file mode 100644
index 0000000000..78759e6618
--- /dev/null
+++ b/lib/fwu_updates/Kconfig
@@ -0,0 +1,33 @@
+config FWU_MULTI_BANK_UPDATE
+ bool "Enable FWU Multi Bank Update Feature"
+ depends on EFI_CAPSULE_ON_DISK
+ select PARTITION_TYPE_GUID
+ select EFI_SETUP_EARLY
+ imply EFI_CAPSULE_ON_DISK_EARLY
+ select EVENT
+ help
+ Feature for updating firmware images on platforms having
+ multiple banks(copies) of the firmware images. One of the
+ bank is selected for updating all the firmware components
+
+config FWU_NUM_BANKS
+ int "Number of Banks defined by the platform"
+ depends on FWU_MULTI_BANK_UPDATE
+ help
+ Define the number of banks of firmware images on a platform
+
+config FWU_NUM_IMAGES_PER_BANK
+ int "Number of firmware images per bank"
+ depends on FWU_MULTI_BANK_UPDATE
+ help
+ Define the number of firmware images per bank. This value
+ should be the same for all the banks.
+
+config FWU_TRIAL_STATE_CNT
+ int "Number of times system boots in Trial State"
+ depends on FWU_MULTI_BANK_UPDATE
+ default 3
+ help
+ With FWU Multi Bank Update feature enabled, number of times
+ the platform is allowed to boot in Trial State after an
+ update.
diff --git a/lib/fwu_updates/Makefile b/lib/fwu_updates/Makefile
new file mode 100644
index 0000000000..1993088e5b
--- /dev/null
+++ b/lib/fwu_updates/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright (c) 2022, Linaro Limited
+#
+
+obj-$(CONFIG_FWU_MULTI_BANK_UPDATE) += fwu.o
+obj-$(CONFIG_FWU_MDATA_GPT_BLK) += fwu_gpt.o
diff --git a/lib/fwu_updates/fwu.c b/lib/fwu_updates/fwu.c
new file mode 100644
index 0000000000..5313d07302
--- /dev/null
+++ b/lib/fwu_updates/fwu.c
@@ -0,0 +1,719 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2022, Linaro Limited
+ */
+
+#include <dm.h>
+#include <efi.h>
+#include <efi_loader.h>
+#include <efi_variable.h>
+#include <event.h>
+#include <fwu.h>
+#include <fwu_mdata.h>
+#include <malloc.h>
+
+#include <linux/errno.h>
+#include <linux/types.h>
+
+static u8 in_trial;
+static u8 boottime_check;
+
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <u-boot/crc.h>
+
+enum {
+ IMAGE_ACCEPT_SET = 1,
+ IMAGE_ACCEPT_CLEAR,
+};
+
+enum {
+ PRIMARY_PART = 1,
+ SECONDARY_PART,
+ BOTH_PARTS,
+};
+
+static int fwu_get_dev_mdata(struct udevice **dev, struct fwu_mdata *mdata)
+{
+ int ret;
+
+ ret = uclass_first_device_err(UCLASS_FWU_MDATA, dev);
+ if (ret) {
+ log_debug("Cannot find fwu device\n");
+ return ret;
+ }
+
+ if (!mdata)
+ return 0;
+
+ ret = fwu_get_mdata(*dev, mdata);
+ if (ret < 0)
+ log_debug("Unable to get valid FWU metadata\n");
+
+ return ret;
+}
+
+static int trial_counter_update(u16 *trial_state_ctr)
+{
+ bool delete;
+ u32 var_attr;
+ efi_status_t status;
+ efi_uintn_t var_size;
+
+ delete = !trial_state_ctr ? true : false;
+ var_size = !trial_state_ctr ? 0 : (efi_uintn_t)sizeof(*trial_state_ctr);
+ var_attr = !trial_state_ctr ? 0 : EFI_VARIABLE_NON_VOLATILE |
+ EFI_VARIABLE_BOOTSERVICE_ACCESS;
+ status = efi_set_variable_int(u"TrialStateCtr",
+ &efi_global_variable_guid,
+ var_attr,
+ var_size, trial_state_ctr, false);
+
+ if ((delete && (status != EFI_NOT_FOUND &&
+ status != EFI_SUCCESS)) ||
+ (!delete && status != EFI_SUCCESS))
+ return -1;
+
+ return 0;
+}
+
+static int trial_counter_read(u16 *trial_state_ctr)
+{
+ efi_status_t status;
+ efi_uintn_t var_size;
+
+ var_size = (efi_uintn_t)sizeof(trial_state_ctr);
+ status = efi_get_variable_int(u"TrialStateCtr",
+ &efi_global_variable_guid,
+ NULL,
+ &var_size, trial_state_ctr,
+ NULL);
+ if (status != EFI_SUCCESS) {
+ log_err("Unable to read TrialStateCtr variable\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int fwu_trial_count_update(void)
+{
+ int ret;
+ u16 trial_state_ctr;
+
+ ret = trial_counter_read(&trial_state_ctr);
+ if (ret) {
+ log_debug("Unable to read trial_state_ctr\n");
+ goto out;
+ }
+
+ ++trial_state_ctr;
+ if (trial_state_ctr > CONFIG_FWU_TRIAL_STATE_CNT) {
+ log_info("Trial State count exceeded. Revert back to previous_active_index\n");
+ ret = fwu_revert_boot_index();
+ if (ret)
+ log_err("Unable to revert active_index\n");
+ ret = 1;
+ } else {
+ ret = trial_counter_update(&trial_state_ctr);
+ if (ret)
+ log_err("Unable to increment TrialStateCtr variable\n");
+ }
+
+out:
+ return ret;
+}
+
+static int in_trial_state(struct fwu_mdata *mdata)
+{
+ u32 i, active_bank;
+ struct fwu_image_entry *img_entry;
+ struct fwu_image_bank_info *img_bank_info;
+
+ active_bank = mdata->active_index;
+ img_entry = &mdata->img_entry[0];
+ for (i = 0; i < CONFIG_FWU_NUM_IMAGES_PER_BANK; i++) {
+ img_bank_info = &img_entry[i].img_bank_info[active_bank];
+ if (!img_bank_info->accepted) {
+ log_info("System booting in Trial State\n");
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int fwu_get_image_type_id(u8 *image_index, efi_guid_t *image_type_id)
+{
+ u8 index;
+ int i;
+ struct efi_fw_image *image;
+
+ index = *image_index;
+ image = update_info.images;
+ for (i = 0; i < num_image_type_guids; i++) {
+ if (index == image[i].image_index) {
+ guidcpy(image_type_id, &image[i].image_type_id);
+ return 0;
+ }
+ }
+
+ return -ENOENT;
+}
+
+/**
+ * fwu_verify_mdata() - Verify the FWU metadata
+ * @mdata: FWU metadata structure
+ * @pri_part: FWU metadata partition is primary or secondary
+ *
+ * Verify the FWU metadata by computing the CRC32 for the metadata
+ * structure and comparing it against the CRC32 value stored as part
+ * of the structure.
+ *
+ * Return: 0 if OK, -ve on error
+ *
+ */
+int fwu_verify_mdata(struct fwu_mdata *mdata, bool pri_part)
+{
+ u32 calc_crc32;
+ void *buf;
+
+ buf = &mdata->version;
+ calc_crc32 = crc32(0, buf, sizeof(*mdata) - sizeof(u32));
+
+ if (calc_crc32 != mdata->crc32) {
+ log_debug("crc32 check failed for %s FWU metadata partition\n",
+ pri_part ? "primary" : "secondary");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * fwu_check_mdata_validity() - Check for validity of the FWU metadata copies
+ *
+ * Read both the metadata copies from the storage media, verify their checksum,
+ * and ascertain that both copies match. If one of the copies has gone bad,
+ * restore it from the good copy.
+ *
+ * Return: 0 if OK, -ve on error
+ *
+ */
+int fwu_check_mdata_validity(void)
+{
+ int ret;
+ struct udevice *dev;
+ struct fwu_mdata pri_mdata;
+ struct fwu_mdata secondary_mdata;
+ uint mdata_parts[2];
+ uint valid_partitions, invalid_partitions;
+
+ ret = fwu_get_dev_mdata(&dev, NULL);
+ if (ret)
+ return ret;
+
+ /*
+ * Check if the platform has defined its own
+ * function to check the metadata partitions'
+ * validity. If so, that takes precedence.
+ */
+ ret = fwu_mdata_check(dev);
+ if (!ret || ret != -ENOSYS)
+ return ret;
+
+ /*
+ * Two FWU metadata partitions are expected.
+ * If we don't have two, user needs to create
+ * them first
+ */
+ valid_partitions = 0;
+ ret = fwu_get_mdata_part_num(dev, mdata_parts);
+ if (ret < 0) {
+ log_debug("Error getting the FWU metadata partitions\n");
+ return -ENOENT;
+ }
+
+ ret = fwu_read_mdata_partition(dev, &pri_mdata, mdata_parts[0]);
+ if (!ret) {
+ ret = fwu_verify_mdata(&pri_mdata, 1);
+ if (!ret)
+ valid_partitions |= PRIMARY_PART;
+ }
+
+ ret = fwu_read_mdata_partition(dev, &secondary_mdata, mdata_parts[1]);
+ if (!ret) {
+ ret = fwu_verify_mdata(&secondary_mdata, 0);
+ if (!ret)
+ valid_partitions |= SECONDARY_PART;
+ }
+
+ if (valid_partitions == (PRIMARY_PART | SECONDARY_PART)) {
+ /*
+ * Before returning, check that both the
+ * FWU metadata copies are the same. If not,
+ * populate the secondary partition from the
+ * primary partition copy.
+ */
+ if (!memcmp(&pri_mdata, &secondary_mdata,
+ sizeof(struct fwu_mdata))) {
+ ret = 0;
+ } else {
+ log_info("Both FWU metadata copies are valid but do not match.");
+ log_info(" Restoring the secondary partition from the primary\n");
+ ret = fwu_write_mdata_partition(dev, &pri_mdata,
+ mdata_parts[1]);
+ if (ret)
+ log_debug("Restoring secondary FWU metadata partition failed\n");
+ }
+ goto out;
+ }
+
+ if (!(valid_partitions & BOTH_PARTS)) {
+ log_info("Both FWU metadata partitions invalid\n");
+ ret = -EBADMSG;
+ goto out;
+ }
+
+ invalid_partitions = valid_partitions ^ BOTH_PARTS;
+ ret = fwu_write_mdata_partition(dev,
+ (invalid_partitions == PRIMARY_PART) ?
+ &secondary_mdata : &pri_mdata,
+ (invalid_partitions == PRIMARY_PART) ?
+ mdata_parts[0] : mdata_parts[1]);
+
+ if (ret)
+ log_debug("Restoring %s FWU metadata partition failed\n",
+ (invalid_partitions == PRIMARY_PART) ?
+ "primary" : "secondary");
+
+out:
+ return ret;
+}
+
+/**
+ * fwu_get_active_index() - Get active_index from the FWU metadata
+ * @active_idx: active_index value to be read
+ *
+ * Read the active_index field from the FWU metadata and place it in
+ * the variable pointed to be the function argument.
+ *
+ * Return: 0 if OK, -ve on error
+ *
+ */
+int fwu_get_active_index(uint *active_idx)
+{
+ int ret;
+ struct udevice *dev;
+ struct fwu_mdata mdata = { 0 };
+
+ ret = fwu_get_dev_mdata(&dev, &mdata);
+ if (ret)
+ return ret;
+
+ /*
+ * Found the FWU metadata partition, now read the active_index
+ * value
+ */
+ *active_idx = mdata.active_index;
+ if (*active_idx >= CONFIG_FWU_NUM_BANKS) {
+ log_debug("Active index value read is incorrect\n");
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+/**
+ * fwu_set_active_index() - Set active_index in the FWU metadata
+ * @active_idx: active_index value to be set
+ *
+ * Update the active_index field in the FWU metadata
+ *
+ * Return: 0 if OK, -ve on error
+ *
+ */
+int fwu_set_active_index(uint active_idx)
+{
+ int ret;
+ struct udevice *dev;
+ struct fwu_mdata mdata = { 0 };
+
+ if (active_idx >= CONFIG_FWU_NUM_BANKS) {
+ log_debug("Invalid active index value\n");
+ return -EINVAL;
+ }
+
+ ret = fwu_get_dev_mdata(&dev, &mdata);
+ if (ret)
+ return ret;
+
+ /*
+ * Update the active index and previous_active_index fields
+ * in the FWU metadata
+ */
+ mdata.previous_active_index = mdata.active_index;
+ mdata.active_index = active_idx;
+
+ /*
+ * Now write this updated FWU metadata to both the
+ * FWU metadata partitions
+ */
+ ret = fwu_update_mdata(dev, &mdata);
+ if (ret) {
+ log_debug("Failed to update FWU metadata partitions\n");
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+/**
+ * fwu_get_image_index() - Get the Image Index to be used for capsule update
+ * @image_index: The Image Index for the image
+ *
+ * The FWU multi bank update feature computes the value of image_index at
+ * runtime, based on the bank to which the image needs to be written to.
+ * Derive the image_index value for the image.
+ *
+ * Currently, the capsule update driver uses the DFU framework for
+ * the updates. This function gets the DFU alt number which is to
+ * be used as the Image Index
+ *
+ * Return: 0 if OK, -ve on error
+ *
+ */
+int fwu_get_image_index(u8 *image_index)
+{
+ int ret, i;
+ u8 alt_num;
+ uint update_bank;
+ efi_guid_t *image_guid, image_type_id;
+ struct udevice *dev;
+ struct fwu_mdata mdata = { 0 };
+ struct fwu_image_entry *img_entry;
+ struct fwu_image_bank_info *img_bank_info;
+
+ ret = fwu_get_dev_mdata(&dev, &mdata);
+ if (ret)
+ return ret;
+
+ ret = fwu_plat_get_update_index(&update_bank);
+ if (ret) {
+ log_debug("Failed to get the FWU update bank\n");
+ goto out;
+ }
+
+ ret = fwu_get_image_type_id(image_index, &image_type_id);
+ if (ret) {
+ log_debug("Unable to get image_type_id for image_index %u\n",
+ *image_index);
+ goto out;
+ }
+
+ ret = -EINVAL;
+ /*
+ * The FWU metadata has been read. Now get the image_uuid for the
+ * image with the update_bank.
+ */
+ for (i = 0; i < CONFIG_FWU_NUM_IMAGES_PER_BANK; i++) {
+ if (!guidcmp(&image_type_id,
+ &mdata.img_entry[i].image_type_uuid)) {
+ img_entry = &mdata.img_entry[i];
+ img_bank_info = &img_entry->img_bank_info[update_bank];
+ image_guid = &img_bank_info->image_uuid;
+ ret = fwu_plat_get_alt_num(dev, image_guid, &alt_num);
+ if (ret) {
+ log_debug("alt_num not found for partition with GUID %pUs\n",
+ image_guid);
+ } else {
+ log_debug("alt_num %d for partition %pUs\n",
+ alt_num, image_guid);
+ *image_index = alt_num + 1;
+ }
+
+ goto out;
+ }
+ }
+
+ log_debug("Partition with the image type %pUs not found\n",
+ &image_type_id);
+
+out:
+ return ret;
+}
+
+/**
+ * fwu_revert_boot_index() - Revert the active index in the FWU metadata
+ *
+ * Revert the active_index value in the FWU metadata, by swapping the values
+ * of active_index and previous_active_index in both copies of the
+ * FWU metadata.
+ *
+ * Return: 0 if OK, -ve on error
+ *
+ */
+int fwu_revert_boot_index(void)
+{
+ int ret;
+ u32 cur_active_index;
+ struct udevice *dev;
+ struct fwu_mdata mdata = { 0 };
+
+ ret = fwu_get_dev_mdata(&dev, &mdata);
+ if (ret)
+ return ret;
+
+ /*
+ * Swap the active index and previous_active_index fields
+ * in the FWU metadata
+ */
+ cur_active_index = mdata.active_index;
+ mdata.active_index = mdata.previous_active_index;
+ mdata.previous_active_index = cur_active_index;
+
+ /*
+ * Now write this updated FWU metadata to both the
+ * FWU metadata partitions
+ */
+ ret = fwu_update_mdata(dev, &mdata);
+ if (ret) {
+ log_debug("Failed to update FWU metadata partitions\n");
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+/**
+ * fwu_clrset_image_accept() - Set or Clear the Acceptance bit for the image
+ * @img_type_id: GUID of the image type for which the accepted bit is to be
+ * set or cleared
+ * @bank: Bank of which the image's Accept bit is to be set or cleared
+ * @action: Action which specifies whether image's Accept bit is to be set or
+ * cleared
+ *
+ * Set/Clear the accepted bit for the image specified by the img_guid parameter.
+ * This indicates acceptance or rejection of image for subsequent boots by some
+ * governing component like OS(or firmware).
+ *
+ * Return: 0 if OK, -ve on error
+ *
+ */
+static int fwu_clrset_image_accept(efi_guid_t *img_type_id, u32 bank, u8 action)
+{
+ int ret, i;
+ struct udevice *dev;
+ struct fwu_mdata mdata = { 0 };
+ struct fwu_image_entry *img_entry;
+ struct fwu_image_bank_info *img_bank_info;
+
+ ret = fwu_get_dev_mdata(&dev, &mdata);
+ if (ret)
+ return ret;
+
+ img_entry = &mdata.img_entry[0];
+ for (i = 0; i < CONFIG_FWU_NUM_IMAGES_PER_BANK; i++) {
+ if (!guidcmp(&img_entry[i].image_type_uuid, img_type_id)) {
+ img_bank_info = &img_entry[i].img_bank_info[bank];
+ if (action == IMAGE_ACCEPT_SET)
+ img_bank_info->accepted |= FWU_IMAGE_ACCEPTED;
+ else
+ img_bank_info->accepted = 0;
+
+ ret = fwu_update_mdata(dev, &mdata);
+ goto out;
+ }
+ }
+
+ /* Image not found */
+ ret = -ENOENT;
+
+out:
+ return ret;
+}
+
+/**
+ * fwu_accept_image() - Set the Acceptance bit for the image
+ * @img_type_id: GUID of the image type for which the accepted bit is to be
+ * cleared
+ * @bank: Bank of which the image's Accept bit is to be set
+ *
+ * Set the accepted bit for the image specified by the img_guid parameter. This
+ * indicates acceptance of image for subsequent boots by some governing component
+ * like OS(or firmware).
+ *
+ * Return: 0 if OK, -ve on error
+ *
+ */
+int fwu_accept_image(efi_guid_t *img_type_id, u32 bank)
+{
+ return fwu_clrset_image_accept(img_type_id, bank,
+ IMAGE_ACCEPT_SET);
+}
+
+/**
+ * fwu_clear_accept_image() - Clear the Acceptance bit for the image
+ * @img_type_id: GUID of the image type for which the accepted bit is to be
+ * cleared
+ * @bank: Bank of which the image's Accept bit is to be cleared
+ *
+ * Clear the accepted bit for the image type specified by the img_type_id parameter.
+ * This function is called after the image has been updated. The accepted bit is
+ * cleared to be set subsequently after passing the image acceptance criteria, by
+ * either the OS(or firmware)
+ *
+ * Return: 0 if OK, -ve on error
+ *
+ */
+int fwu_clear_accept_image(efi_guid_t *img_type_id, u32 bank)
+{
+ return fwu_clrset_image_accept(img_type_id, bank,
+ IMAGE_ACCEPT_CLEAR);
+}
+
+/**
+ * fwu_plat_get_update_index() - Get the value of the update bank
+ * @update_idx: Bank number to which images are to be updated
+ *
+ * Get the value of the bank(partition) to which the update needs to be
+ * made.
+ *
+ * Note: This is a weak function and platforms can override this with
+ * their own implementation for selection of the update bank.
+ *
+ * Return: 0 if OK, -ve on error
+ *
+ */
+__weak int fwu_plat_get_update_index(uint *update_idx)
+{
+ int ret;
+ u32 active_idx;
+
+ ret = fwu_get_active_index(&active_idx);
+ if (ret < 0)
+ return -1;
+
+ *update_idx = (active_idx + 1) % CONFIG_FWU_NUM_BANKS;
+
+ return ret;
+}
+
+/**
+ * fwu_update_checks_pass() - Check if FWU update can be done
+ *
+ * Check if the FWU update can be executed. The updates are
+ * allowed only when the platform is not in Trial State and
+ * the boot time checks have passed
+ *
+ * Return: 1 if OK, 0 if checks do not pass
+ *
+ */
+u8 fwu_update_checks_pass(void)
+{
+ return !in_trial && boottime_check;
+}
+
+/**
+ * fwu_empty_capsule_checks_pass() - Check if empty capsule can be processed
+ *
+ * Check if the empty capsule can be processed to either accept or revert
+ * an earlier executed update. The empty capsules need to be processed
+ * only when the platform is in Trial State and the boot time checks have
+ * passed
+ *
+ * Return: 1 if OK, 0 if not to be allowed
+ *
+ */
+u8 fwu_empty_capsule_checks_pass(void)
+{
+ return in_trial && boottime_check;
+}
+
+/**
+ * fwu_trial_state_ctr_start() - Start the Trial State counter
+ *
+ * Start the counter to identify the platform booting in the
+ * Trial State. The counter is implemented as an EFI variable.
+ *
+ * Return: 0 if OK, -ve on error
+ *
+ */
+int fwu_trial_state_ctr_start(void)
+{
+ int ret;
+ u16 trial_state_ctr;
+
+ trial_state_ctr = 0;
+ ret = trial_counter_update(&trial_state_ctr);
+ if (ret)
+ log_err("Unable to initialise TrialStateCtr\n");
+
+ return ret;
+}
+
+static int fwu_boottime_checks(void *ctx, struct event *event)
+{
+ int ret;
+ u32 boot_idx, active_idx;
+ struct udevice *dev;
+ struct fwu_mdata mdata = { 0 };
+
+ /* Don't have boot time checks on sandbox */
+ if (IS_ENABLED(CONFIG_SANDBOX)) {
+ boottime_check = 1;
+ return 0;
+ }
+
+ ret = fwu_check_mdata_validity();
+ if (ret)
+ return 0;
+
+ /*
+ * Get the Boot Index, i.e. the bank from
+ * which the platform has booted. This value
+ * gets passed from the ealier stage bootloader
+ * which booted u-boot, e.g. tf-a. If the
+ * boot index is not the same as the
+ * active_index read from the FWU metadata,
+ * update the active_index.
+ */
+ fwu_plat_get_bootidx(&boot_idx);
+ if (boot_idx >= CONFIG_FWU_NUM_BANKS) {
+ log_err("Received incorrect value of boot_index\n");
+ return 0;
+ }
+
+ ret = fwu_get_active_index(&active_idx);
+ if (ret) {
+ log_err("Unable to read active_index\n");
+ return 0;
+ }
+
+ if (boot_idx != active_idx) {
+ log_info("Boot idx %u is not matching active idx %u, changing active_idx\n",
+ boot_idx, active_idx);
+ ret = fwu_set_active_index(boot_idx);
+ if (!ret)
+ boottime_check = 1;
+
+ return 0;
+ }
+
+ if (efi_init_obj_list() != EFI_SUCCESS)
+ return 0;
+
+ ret = fwu_get_dev_mdata(&dev, &mdata);
+ if (ret)
+ return ret;
+
+ in_trial = in_trial_state(&mdata);
+ if (!in_trial || (ret = fwu_trial_count_update()) > 0)
+ ret = trial_counter_update(NULL);
+
+ if (!ret)
+ boottime_check = 1;
+
+ return 0;
+}
+EVENT_SPY(EVT_MAIN_LOOP, fwu_boottime_checks);
diff --git a/lib/fwu_updates/fwu_gpt.c b/lib/fwu_updates/fwu_gpt.c
new file mode 100644
index 0000000000..21a573c934
--- /dev/null
+++ b/lib/fwu_updates/fwu_gpt.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2022, Linaro Limited
+ */
+
+#include <blk.h>
+#include <dfu.h>
+#include <efi.h>
+#include <efi_loader.h>
+#include <fwu.h>
+#include <log.h>
+#include <part.h>
+
+#include <linux/errno.h>
+
+static int get_gpt_dfu_identifier(struct blk_desc *desc, efi_guid_t *image_guid)
+{
+ int i;
+ struct disk_partition info;
+ efi_guid_t unique_part_guid;
+
+ for (i = 1; i < MAX_SEARCH_PARTITIONS; i++) {
+ if (part_get_info(desc, i, &info))
+ continue;
+ uuid_str_to_bin(info.uuid, unique_part_guid.b,
+ UUID_STR_FORMAT_GUID);
+
+ if (!guidcmp(&unique_part_guid, image_guid))
+ return i;
+ }
+
+ log_err("No partition found with image_guid %pUs\n", image_guid);
+ return -ENOENT;
+}
+
+static int fwu_alt_num_for_dfu_dev(struct dfu_entity *dfu, int dev_num,
+ int part, unsigned char dfu_dev,
+ u8 *alt_num)
+{
+ int ret;
+
+ switch(dfu_dev) {
+ case DFU_DEV_MMC:
+ if (dfu->layout == DFU_RAW_ADDR &&
+ dfu->data.mmc.dev_num == dev_num &&
+ dfu->data.mmc.part == part) {
+ *alt_num = dfu->alt;
+ ret = 0;
+ } else {
+ ret = -ENOENT;
+ }
+ break;
+ default:
+ ret = -ENOENT;
+ }
+
+ return ret;
+}
+
+static int fwu_gpt_get_alt_num(struct blk_desc *desc, efi_guid_t *image_guid,
+ u8 *alt_num, unsigned char dfu_dev)
+{
+ int ret = -1;
+ int i, part, dev_num;
+ struct dfu_entity *dfu;
+
+ dev_num = desc->devnum;
+ part = get_gpt_dfu_identifier(desc, image_guid);
+ if (part < 0)
+ return -ENOENT;
+
+ ret = dfu_init_env_entities(NULL, NULL);
+ if (ret)
+ goto out;
+
+ i = 0;
+ while (true) {
+ dfu = dfu_get_entity(i++);
+ if (!dfu) {
+ ret = -ENOENT;
+ break;
+ }
+
+ if (dfu->dev_type != dfu_dev)
+ continue;
+
+ ret = fwu_alt_num_for_dfu_dev(dfu, dev_num, part, dfu_dev,
+ alt_num);
+ if (!ret)
+ break;
+ }
+
+out:
+ dfu_free_entities();
+
+ return ret;
+}
+
+/**
+ * fwu_plat_get_alt_num() - Get the DFU alt number
+ * @dev: FWU metadata device
+ * @image_guid: GUID value of the image for which the alt num is to
+ * be obtained
+ * @alt_num: The DFU alt number for the image that is to be updated
+ *
+ * Get the DFU alt number for the image that is to be updated. The
+ * image is identified with the image_guid parameter that is passed
+ * to the function.
+ *
+ * Note: This is a weak function and platforms can override this with
+ * their own implementation for obtaining the alt number value.
+ *
+ * Return: 0 if OK, -ve on error
+ *
+ */
+__weak int fwu_plat_get_alt_num(struct udevice *dev, efi_guid_t *image_guid,
+ u8 *alt_num)
+{
+ struct fwu_mdata_gpt_blk_priv *priv = dev_get_priv(dev);
+
+ return fwu_gpt_get_alt_num(dev_get_uclass_plat(priv->blk_dev),
+ image_guid, alt_num, DFU_DEV_MMC);
+}