Add banked_fw_store with FWU metadata management
Adds an A/B banked fw_store that manages update installation
and metadata in-line with the Arm FWU-A specification.
Signed-off-by: Julian Hall <julian.hall@arm.com>
Change-Id: Icf7af243081ba9edd208ddb87597ce35f46a924b
diff --git a/components/service/fwu/fw_store/banked/bank_scheme.h b/components/service/fwu/fw_store/banked/bank_scheme.h
new file mode 100644
index 0000000..83a9f0d
--- /dev/null
+++ b/components/service/fwu/fw_store/banked/bank_scheme.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef BANKED_FW_STORE_BANK_SCHEME_H
+#define BANKED_FW_STORE_BANK_SCHEME_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Definitions for an A/B banked scheme.
+ */
+#define BANK_SCHEME_NUM_BANKS (2)
+
+/**
+ * \brief Returns the index of the next bank to use
+ *
+ * Given a bank index, returns the index of the next bank to use.
+ *
+ * \param[in] bank_index
+ *
+ * \return Index of next bank to use
+ */
+static inline uint32_t bank_scheme_next_index(uint32_t bank_index)
+{
+ return (bank_index == 0) ? 1 : 0;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BANKED_FW_STORE_BANK_SCHEME_H */
diff --git a/components/service/fwu/fw_store/banked/bank_tracker.c b/components/service/fwu/fw_store/banked/bank_tracker.c
new file mode 100644
index 0000000..239be37
--- /dev/null
+++ b/components/service/fwu/fw_store/banked/bank_tracker.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2022-2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <assert.h>
+#include <stddef.h>
+#include <string.h>
+#include "bank_tracker.h"
+
+
+void bank_tracker_init(
+ struct bank_tracker *subject)
+{
+ memset(subject, 0, sizeof(struct bank_tracker));
+}
+
+void bank_tracker_deinit(
+ struct bank_tracker *subject)
+{
+ (void)subject;
+}
+
+void bank_tracker_accept(
+ struct bank_tracker *subject,
+ unsigned int bank_index,
+ unsigned int image_index)
+{
+ assert(bank_index < BANK_SCHEME_NUM_BANKS);
+ assert(image_index < FWU_MAX_FW_DIRECTORY_ENTRIES);
+
+ subject->bank_state[bank_index].is_accepted[image_index] = true;
+
+}
+
+void bank_tracker_copy_accept(
+ struct bank_tracker *subject,
+ unsigned int from_bank_index,
+ unsigned int to_bank_index,
+ unsigned int image_index)
+{
+ assert(from_bank_index < BANK_SCHEME_NUM_BANKS);
+ assert(to_bank_index < BANK_SCHEME_NUM_BANKS);
+ assert(image_index < FWU_MAX_FW_DIRECTORY_ENTRIES);
+
+ subject->bank_state[to_bank_index].is_accepted[image_index] =
+ subject->bank_state[from_bank_index].is_accepted[image_index];
+}
+
+void bank_tracker_set_no_content(
+ struct bank_tracker *subject,
+ unsigned int bank_index)
+{
+ assert(bank_index < BANK_SCHEME_NUM_BANKS);
+
+ subject->bank_state[bank_index].is_content = false;
+
+ for (unsigned int i = 0; i < FWU_MAX_FW_DIRECTORY_ENTRIES; i++)
+ subject->bank_state[bank_index].is_accepted[i] = false;
+}
+
+void bank_tracker_set_holds_content(
+ struct bank_tracker *subject,
+ unsigned int bank_index)
+{
+ assert(bank_index < BANK_SCHEME_NUM_BANKS);
+
+ subject->bank_state[bank_index].is_content = true;
+}
+
+void bank_tracker_set_holds_accepted_content(
+ struct bank_tracker *subject,
+ unsigned int bank_index)
+{
+ assert(bank_index < BANK_SCHEME_NUM_BANKS);
+
+ subject->bank_state[bank_index].is_content = true;
+
+ for (unsigned int i = 0; i < FWU_MAX_FW_DIRECTORY_ENTRIES; i++)
+ subject->bank_state[bank_index].is_accepted[i] = true;
+}
+
+bool bank_tracker_is_content(
+ const struct bank_tracker *subject,
+ unsigned int bank_index)
+{
+ assert(bank_index < BANK_SCHEME_NUM_BANKS);
+
+ return subject->bank_state[bank_index].is_content;
+}
+
+bool bank_tracker_is_accepted(
+ const struct bank_tracker *subject,
+ unsigned int bank_index,
+ unsigned int image_index)
+{
+ assert(bank_index < BANK_SCHEME_NUM_BANKS);
+ assert(image_index < FWU_MAX_FW_DIRECTORY_ENTRIES);
+
+ return subject->bank_state[bank_index].is_accepted[image_index];
+}
+
+bool bank_tracker_is_all_accepted(
+ const struct bank_tracker *subject,
+ unsigned int bank_index,
+ unsigned int num_images)
+{
+ assert(bank_index < BANK_SCHEME_NUM_BANKS);
+ assert(num_images <= FWU_MAX_FW_DIRECTORY_ENTRIES);
+
+ for (unsigned int image_index = 0; image_index < num_images; image_index++) {
+
+ if (!subject->bank_state[bank_index].is_accepted[image_index])
+ return false;
+ }
+
+ return true;
+}
diff --git a/components/service/fwu/fw_store/banked/bank_tracker.h b/components/service/fwu/fw_store/banked/bank_tracker.h
new file mode 100644
index 0000000..cf3279d
--- /dev/null
+++ b/components/service/fwu/fw_store/banked/bank_tracker.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2022-2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef BANK_TRACKER_H
+#define BANK_TRACKER_H
+
+#include <stdbool.h>
+#include <service/fwu/agent/fw_directory.h>
+#include "bank_scheme.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief bank_tracker structure definition
+ *
+ * Tracks the state of fw_store banks.
+ */
+struct bank_tracker {
+
+ struct {
+
+ /* True if bank holds content */
+ bool is_content;
+
+ /* Image accepted state for images contained in the bank */
+ bool is_accepted[FWU_MAX_FW_DIRECTORY_ENTRIES];
+
+ } bank_state[BANK_SCHEME_NUM_BANKS];
+
+};
+
+/**
+ * \brief Initialize the bank_tracker
+ *
+ * \param[in] subject This instance
+ */
+void bank_tracker_init(
+ struct bank_tracker *subject);
+
+/**
+ * \brief De-initialize the bank_tracker
+ *
+ * \param[in] subject This instance
+ */
+void bank_tracker_deinit(
+ struct bank_tracker *subject);
+
+/**
+ * \brief Mark image as accepted
+ *
+ * \param[in] subject This instance
+ * \param[in] bank_index The firmware bank
+ * \param[in] image_index The image index (from fw_directory)
+ */
+void bank_tracker_accept(
+ struct bank_tracker *subject,
+ unsigned int bank_index,
+ unsigned int image_index);
+
+/**
+ * \brief Copy image accept state
+ *
+ * \param[in] subject This instance
+ * \param[in] from_bank_index Copy accepted state from bank index
+ * \param[in] to_bank_index Copy accepted state to bank index
+ * \param[in] image_index The image index (from fw_directory)
+ */
+void bank_tracker_copy_accept(
+ struct bank_tracker *subject,
+ unsigned int from_bank_index,
+ unsigned int to_bank_index,
+ unsigned int image_index);
+
+/**
+ * \brief Sets bank as holding no content
+ *
+ * \param[in] subject This instance
+ * \param[in] bank_index The firmware bank
+ */
+void bank_tracker_set_no_content(
+ struct bank_tracker *subject,
+ unsigned int bank_index);
+
+/**
+ * \brief Set bank as holding content
+ *
+ * \param[in] subject This instance
+ * \param[in] bank_index The firmware bank
+ */
+void bank_tracker_set_holds_content(
+ struct bank_tracker *subject,
+ unsigned int bank_index);
+
+/**
+ * \brief Set bank as holding fully accepted content
+ *
+ * \param[in] subject This instance
+ * \param[in] bank_index The firmware bank
+ */
+void bank_tracker_set_holds_accepted_content(
+ struct bank_tracker *subject,
+ unsigned int bank_index);
+
+/**
+ * \brief Check if bank holds contents
+ *
+ * \param[in] subject This instance
+ * \param[in] bank_index The firmware bank
+ *
+ * \return True if bank holds content
+ */
+bool bank_tracker_is_content(
+ const struct bank_tracker *subject,
+ unsigned int bank_index);
+
+/**
+ * \brief Check if image is accepted
+ *
+ * \param[in] subject This instance
+ * \param[in] bank_index The firmware bank
+ * \param[in] image_index The image index
+ *
+ * \return True if an image has been accepted
+ */
+bool bank_tracker_is_accepted(
+ const struct bank_tracker *subject,
+ unsigned int bank_index,
+ unsigned int image_index);
+
+/**
+ * \brief Check if all images are accepted
+ *
+ * \param[in] subject This instance
+ * \param[in] bank_index The firmware bank
+ * \param[in] num_images Number of images to consider
+ *
+ * \return True if all images have been accepted
+ */
+bool bank_tracker_is_all_accepted(
+ const struct bank_tracker *subject,
+ unsigned int bank_index,
+ unsigned int num_images);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BANK_TRACKER_H */
diff --git a/components/service/fwu/fw_store/banked/banked_fw_store.c b/components/service/fwu/fw_store/banked/banked_fw_store.c
new file mode 100644
index 0000000..dec576a
--- /dev/null
+++ b/components/service/fwu/fw_store/banked/banked_fw_store.c
@@ -0,0 +1,595 @@
+/*
+ * Copyright (c) 2022-2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <assert.h>
+#include <stddef.h>
+#include <string.h>
+#include <common/uuid/uuid.h>
+#include <protocols/service/fwu/packed-c/fwu_proto.h>
+#include <protocols/service/fwu/packed-c/status.h>
+#include <service/fwu/installer/installer.h>
+#include <service/fwu/installer/installer_index.h>
+#include "banked_fw_store.h"
+#include "bank_scheme.h"
+#include "volume_id.h"
+
+static int activate_installer(
+ struct fw_store *fw_store,
+ struct installer *installer,
+ uint32_t location_id)
+{
+ int status = FWU_STATUS_UNKNOWN;
+
+ /* An image has been presented that requires a new installer to
+ * be activated. This involves associating the installer with
+ * the correct storage volumes that reflect the current bank
+ * state. Active installers are held in a linked list during
+ * the installation phase of an update transaction.
+ */
+ unsigned int update_volume_id = banked_volume_id(
+ location_id, banked_usage_id(fw_store->update_index));
+
+ unsigned int current_volume_id = banked_volume_id(
+ location_id, banked_usage_id(fw_store->boot_index));
+
+ status = installer_begin(installer, current_volume_id, update_volume_id);
+
+ if (status == FWU_STATUS_SUCCESS) {
+
+ /* Add to list of active installers */
+ installer->next = fw_store->active_installers;
+ fw_store->active_installers = installer;
+ }
+
+ return status;
+}
+
+static void copy_accepted_state_for_location(
+ struct fw_store *fw_store,
+ uint32_t location_id)
+{
+ size_t index = 0;
+
+ while (1) {
+
+ const struct image_info *image_info = fw_directory_get_image_info(
+ fw_store->fw_directory, index);
+
+ if (image_info) {
+
+ if (image_info->location_id == location_id)
+ bank_tracker_copy_accept(
+ &fw_store->bank_tracker,
+ fw_store->boot_index,
+ fw_store->update_index,
+ image_info->image_index);
+ } else
+ break;
+
+ ++index;
+ }
+}
+
+static int install_unchanged_images(
+ struct fw_store *fw_store)
+{
+ int status = FWU_STATUS_SUCCESS;
+ size_t num_locations = 0;
+ const uint32_t *location_ids = installer_index_get_location_ids(&num_locations);
+
+ /* Iterate over each of the location_ids that require updating for a viable update.
+ * If there are locations without an active installer, attempt to copy from the
+ * currently active bank.
+ */
+ for (size_t i = 0; !status && i < num_locations; i++) {
+
+ bool is_installer_found = false;
+ uint32_t location_id = location_ids[i];
+ struct installer *installer = fw_store->active_installers;
+
+ while (installer) {
+
+ if ((installer->location_id == location_id) &&
+ !installer_status(installer)) {
+
+ /* There was an installer for this location and the installation
+ * went without errors.
+ */
+ is_installer_found = true;
+ break;
+ }
+
+ installer = installer->next;
+ }
+
+ if (!is_installer_found) {
+
+ /* There is no installer for the location so assume the active bank
+ * should be copied. This relies on the platform integrator providing
+ * a suitable installer to do the copying. It's a legitimate platform
+ * configuration to not provide one and only allow updates that consist
+ * of images for all locations.
+ */
+ struct installer *copy_installer = installer_index_find(
+ INSTALL_TYPE_WHOLE_VOLUME_COPY, location_id);
+
+ if (copy_installer) {
+
+ /* A copy installer doesn't accept any external data. Instead, it
+ * copies from the current volume to the update volume during the
+ * finalize operation.
+ */
+ unsigned int update_volume_id = banked_volume_id(
+ location_id, banked_usage_id(fw_store->update_index));
+
+ unsigned int current_volume_id = banked_volume_id(
+ location_id, banked_usage_id(fw_store->boot_index));
+
+ status = installer_begin(copy_installer,
+ current_volume_id, update_volume_id);
+
+ if (status == FWU_STATUS_SUCCESS) {
+
+ status = installer_finalize(copy_installer);
+
+ /* If a whole volume image was successfully copied from
+ * the currently active bank to the update bank, also copy the
+ * corresponding accepted state for all images associated with the
+ * location. This saves a client from having to re-accept images
+ * that have already been accepted during a previous update.
+ */
+ if (status == FWU_STATUS_SUCCESS)
+ copy_accepted_state_for_location(fw_store, location_id);
+ }
+
+ } else {
+
+ /* Platform configuration doesn't include a suitable copy installer
+ * so treat this update attempt as not viable.
+ */
+ status = FWU_STATUS_NOT_AVAILABLE;
+ }
+ }
+ }
+
+ return status;
+}
+
+int banked_fw_store_init(
+ struct fw_store *fw_store,
+ const struct metadata_serializer *serializer)
+{
+ fw_store->fw_directory = NULL;
+ fw_store->active_installers = NULL;
+
+ fw_store->update_index = 0;
+ fw_store->boot_index = 0;
+
+ bank_tracker_init(&fw_store->bank_tracker);
+
+ return metadata_manager_init(&fw_store->metadata_manager, serializer);
+}
+
+void banked_fw_store_deinit(
+ struct fw_store *fw_store)
+{
+ bank_tracker_deinit(&fw_store->bank_tracker);
+ metadata_manager_deinit(&fw_store->metadata_manager);
+}
+
+int fw_store_synchronize(
+ struct fw_store *fw_store,
+ struct fw_directory *fw_dir,
+ unsigned int boot_index)
+{
+ int status = FWU_STATUS_UNKNOWN;
+
+ /* Associate with the fw_directory */
+ fw_store->fw_directory = fw_dir;
+
+ /* Note the boot index decision made by the boot loader and choose an
+ * alternative index for a prospective update.
+ */
+ fw_store->boot_index = boot_index;
+ fw_store->update_index = bank_scheme_next_index(boot_index);
+
+ /* Start building a view of the boot_info that will be advertised by the fw_directory */
+ struct boot_info boot_info = {0};
+
+ boot_info.boot_index = boot_index;
+
+ /* Ensure that FWU metadata is in a good state. Corruption can occur
+ * due to power failures. The following steps will repair a corrupted
+ * metadata copy or generate fresh metadata if necessary.
+ */
+ status = metadata_manager_check_and_repair(
+ &fw_store->metadata_manager, fw_dir);
+
+ if (status != FWU_STATUS_SUCCESS) {
+
+ /* No viable metadata exists so a fresh version needs to be created. We
+ * can only assume that the boot_index is good. Also assume that it is
+ * fully accepted as there is no knowledge that a rollback to a previous
+ * bank is possible.
+ */
+ boot_info.active_index = boot_index;
+ boot_info.previous_active_index = boot_index;
+
+ bank_tracker_set_holds_accepted_content(
+ &fw_store->bank_tracker,
+ fw_store->boot_index);
+
+ status = metadata_manager_update(
+ &fw_store->metadata_manager,
+ boot_info.active_index,
+ boot_info.previous_active_index,
+ fw_dir,
+ &fw_store->bank_tracker);
+
+ } else {
+
+ /* Metadata was successfully loaded from storage so synchronize local
+ * state to the NV state reflected by the metadata.
+ */
+ status = metadata_manager_get_active_indices(
+ &fw_store->metadata_manager,
+ &boot_info.active_index,
+ &boot_info.previous_active_index);
+
+ /* Synchronize image approval state with NV information contained in the metadata */
+ metadata_manager_preload_bank_tracker(
+ &fw_store->metadata_manager,
+ &fw_store->bank_tracker);
+
+ /* Check for the case where the bootloader has not booted from the active bank.
+ * This will occur when boot loader conditions for a successful boot are not
+ * met and it was necessary to fallback to a previous bank. To prevent
+ * repeated failed boots, the metadata is updated in-line with the
+ * bootloader's decision.
+ */
+ if ((status == FWU_STATUS_SUCCESS) &&
+ (boot_index != boot_info.active_index)) {
+
+ boot_info.active_index = boot_index;
+ boot_info.previous_active_index = boot_index;
+
+ status = metadata_manager_update(
+ &fw_store->metadata_manager,
+ boot_info.active_index,
+ boot_info.previous_active_index,
+ fw_dir,
+ &fw_store->bank_tracker);
+ }
+ }
+
+ /* Synchronize the fw_directory's view of the boot info */
+ fw_directory_set_boot_info(fw_dir, &boot_info);
+
+ return status;
+}
+
+int fw_store_begin_install(
+ struct fw_store *fw_store)
+{
+ assert(!fw_store->active_installers);
+
+ /* Begin the update transaction with the update bank marked as holding
+ * no content and all updatable images in the unaccepted state. Installed
+ * images may be committed as accepted after installation or accepted
+ * during the trial state.
+ */
+ bank_tracker_set_no_content(
+ &fw_store->bank_tracker,
+ fw_store->update_index);
+
+ /* Protect the update bank from use while the installation is in
+ * progress by setting the active and previous active indices to be equal.
+ * If a system restart occurs during installation, this prevents the
+ * possibility of the boot loader attempting to boot from a bank in a partially
+ * installed state.
+ */
+ int status = metadata_manager_update(
+ &fw_store->metadata_manager,
+ fw_store->boot_index,
+ fw_store->boot_index,
+ fw_store->fw_directory,
+ &fw_store->bank_tracker);
+
+ return status;
+}
+
+void fw_store_cancel_install(
+ struct fw_store *fw_store)
+{
+ /* Abort all active installers - each installer will do its best to
+ * clean-up to a state where a follow-in installation is possible.
+ */
+ while (fw_store->active_installers) {
+
+ struct installer *installer = fw_store->active_installers;
+
+ fw_store->active_installers = installer->next;
+
+ installer_abort(installer);
+ }
+}
+
+int fw_store_finalize_install(
+ struct fw_store *fw_store)
+{
+ int status = FWU_STATUS_NOT_AVAILABLE;
+
+ /* Treat no active installers as an error. This would occur if no
+ * images where actually installed during the transaction.
+ */
+ bool is_error = !fw_store->active_installers;
+
+ /* If there are firmware locations that are unchanged after
+ * installation, it may be necessary to copy active images to
+ * the update bank to ensure that the update bank is fully
+ * populated. This will be needed if an incoming update package
+ * only contains images for some locations.
+ */
+ if (!is_error) {
+
+ status = install_unchanged_images(fw_store);
+ is_error = (status != FWU_STATUS_SUCCESS);
+ }
+
+ /* Finalize all active installers - each installer will perform any
+ * actions needed to make installed images ready for use. For example,
+ * for a component installer where only a subset of images were updated,
+ * the finalize step can be used to copy any images that weren't updated
+ * from the currently active storage volume.
+ */
+ while (fw_store->active_installers) {
+
+ struct installer *installer = fw_store->active_installers;
+
+ fw_store->active_installers = installer->next;
+
+ if (!is_error) {
+
+ status = installer_finalize(installer);
+ is_error = (status != FWU_STATUS_SUCCESS);
+
+ } else
+ installer_abort(installer);
+ }
+
+ if (is_error) {
+
+ return (status != FWU_STATUS_SUCCESS) ?
+ status : FWU_STATUS_NOT_AVAILABLE;
+ }
+
+ /* All installers finalized successfully so mark update bank as holding
+ * content and signal to boot loader that the update is ready for a trial
+ * by promoting the update bank to being the active bank.
+ */
+ bank_tracker_set_holds_content(
+ &fw_store->bank_tracker,
+ fw_store->update_index);
+
+ status = metadata_manager_update(
+ &fw_store->metadata_manager,
+ fw_store->update_index,
+ fw_store->boot_index,
+ fw_store->fw_directory,
+ &fw_store->bank_tracker);
+
+ return status;
+}
+
+int fw_store_select_installer(
+ struct fw_store *fw_store,
+ const struct image_info *image_info,
+ struct installer **installer)
+{
+ int status = FWU_STATUS_UNKNOWN;
+
+ *installer = NULL;
+
+ struct installer *selected_installer =
+ installer_index_find(image_info->install_type, image_info->location_id);
+
+ if (selected_installer) {
+
+ /* An installer is available to handle the incoming image. An installer
+ * will potentially handle multiple images as part of an update transaction.
+ * If this is the first, the installer needs to be activated.
+ */
+ if (installer_is_active(selected_installer) ||
+ (status = activate_installer(fw_store,
+ selected_installer, image_info->location_id),
+ status == FWU_STATUS_SUCCESS)) {
+
+ status = installer_open(selected_installer, image_info);
+
+ if (status == FWU_STATUS_SUCCESS)
+ *installer = selected_installer;
+ }
+ }
+
+ return status;
+}
+
+int fw_store_write_image(
+ struct fw_store *fw_store,
+ struct installer *installer,
+ const uint8_t *data,
+ size_t data_len)
+{
+ if (!installer_is_active(installer))
+ /* Attempting to write to an inactive installer */
+ return FWU_STATUS_DENIED;
+
+ int status = installer_write(installer, data, data_len);
+
+ return status;
+}
+
+int fw_store_commit_image(
+ struct fw_store *fw_store,
+ struct installer *installer,
+ const struct image_info *image_info,
+ bool accepted)
+{
+ if (!installer_is_active(installer))
+ /* Attempting to commit an inactive installer */
+ return FWU_STATUS_DENIED;
+
+ int status = installer_commit(installer);
+
+ if ((status == FWU_STATUS_SUCCESS) && accepted)
+ bank_tracker_accept(
+ &fw_store->bank_tracker,
+ fw_store->update_index,
+ image_info->image_index);
+
+ return status;
+}
+
+bool fw_store_notify_accepted(
+ struct fw_store *fw_store,
+ const struct image_info *image_info)
+{
+ unsigned int num_images = fw_directory_num_images(fw_store->fw_directory);
+
+ bank_tracker_accept(
+ &fw_store->bank_tracker,
+ fw_store->boot_index,
+ image_info->image_index);
+
+ int status = metadata_manager_update(
+ &fw_store->metadata_manager,
+ fw_store->boot_index,
+ fw_store->update_index,
+ fw_store->fw_directory,
+ &fw_store->bank_tracker);
+
+ return (status == FWU_STATUS_SUCCESS) &&
+ bank_tracker_is_all_accepted(
+ &fw_store->bank_tracker,
+ fw_store->boot_index,
+ num_images);
+}
+
+bool fw_store_is_accepted(
+ const struct fw_store *fw_store,
+ const struct image_info *image_info)
+{
+ return bank_tracker_is_accepted(
+ &fw_store->bank_tracker,
+ fw_store->boot_index,
+ image_info->image_index);
+}
+
+bool fw_store_is_trial(
+ const struct fw_store *fw_store)
+{
+ const struct boot_info *boot_info = fw_directory_get_boot_info(fw_store->fw_directory);
+ unsigned int num_images = fw_directory_num_images(fw_store->fw_directory);
+
+ return
+ (boot_info->boot_index == boot_info->active_index) &&
+ !bank_tracker_is_all_accepted(
+ &fw_store->bank_tracker,
+ fw_store->boot_index,
+ num_images);
+}
+
+int fw_store_commit_to_update(
+ struct fw_store *fw_store)
+{
+ (void)fw_store;
+
+ /* Currently, the final commitment to an update is made by the boot loader
+ * when anti-rollback counters are advanced after a reboot. For deployments
+ * where anti-rollback counters are managed by the update agent, the necessary
+ * management logic should be add here.
+ */
+ return FWU_STATUS_SUCCESS;
+}
+
+int fw_store_revert_to_previous(
+ struct fw_store *fw_store)
+{
+ uint32_t active_index;
+ uint32_t previous_active_index;
+
+ int status = metadata_manager_get_active_indices(
+ &fw_store->metadata_manager,
+ &active_index,
+ &previous_active_index);
+
+ if (status)
+ return status;
+
+ if (active_index == fw_store->boot_index) {
+
+ /* Update has been activated via a reboot */
+ active_index = previous_active_index;
+ previous_active_index = fw_store->boot_index;
+ fw_store->update_index = bank_scheme_next_index(active_index);
+
+ } else {
+
+ /* Update has not yet been activated */
+ previous_active_index = active_index;
+ active_index = fw_store->boot_index;
+ fw_store->update_index = bank_scheme_next_index(active_index);
+ }
+
+ /* Ensure all images for the new active bank are marked as accepted */
+ bank_tracker_set_holds_accepted_content(
+ &fw_store->bank_tracker,
+ active_index);
+
+ /* Update the FWU metadata to the pre-update state */
+ status = metadata_manager_update(
+ &fw_store->metadata_manager,
+ active_index,
+ previous_active_index,
+ fw_store->fw_directory,
+ &fw_store->bank_tracker);
+
+ return status;
+}
+
+bool fw_store_export(
+ struct fw_store *fw_store,
+ const struct uuid_octets *uuid,
+ const uint8_t **data,
+ size_t *data_len,
+ int *status)
+{
+ struct uuid_octets target_uuid;
+
+ /* Check for request to export the FWU metadata */
+ uuid_guid_octets_from_canonical(&target_uuid, FWU_METADATA_CANONICAL_UUID);
+
+ if (uuid_is_equal(uuid->octets, target_uuid.octets)) {
+
+ bool is_dirty;
+
+ *status = metadata_manager_fetch(
+ &fw_store->metadata_manager,
+ data, data_len,
+ &is_dirty);
+
+ /* is_dirty value is not yet returned when exporting the FWU Metadata but this
+ * may be added to allow for deployments where metadata is written by the client
+ * to allow unnecessary writes to be avoided.
+ */
+
+ return true;
+ }
+
+ return false;
+}
+
diff --git a/components/service/fwu/fw_store/banked/banked_fw_store.h b/components/service/fwu/fw_store/banked/banked_fw_store.h
new file mode 100644
index 0000000..43849ec
--- /dev/null
+++ b/components/service/fwu/fw_store/banked/banked_fw_store.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2022-2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef BANKED_FW_STORE_H
+#define BANKED_FW_STORE_H
+
+#include <stdint.h>
+#include <service/fwu/fw_store/fw_store.h>
+#include "metadata_manager.h"
+#include "bank_tracker.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Interface dependencies
+ */
+struct installer;
+struct metadata_serializer;
+
+/**
+ * \brief Banked fw_store structure definition
+ *
+ * A banked fw_store manages updates to an A/B banked firmware store. The
+ * implementation realizes the common fw_store interface. The banked fw_store
+ * can handle both Nwd and Swd image installation.
+ */
+struct fw_store {
+ const struct fw_directory *fw_directory;
+ struct installer *active_installers;
+ struct metadata_manager metadata_manager;
+ struct bank_tracker bank_tracker;
+ uint32_t update_index;
+ uint32_t boot_index;
+};
+
+/**
+ * \brief Initialize a banked fw_store
+ *
+ * Initializes a banked fw_store that manages updates according to the Arm
+ * FWU-A specification. The provided metadata_serializer should have been
+ * selected for compatibility with the bootloader.
+ *
+ * \param[in] fw_store The subject fw_store
+ * \param[in] serializer The metadata_serializer to use
+ *
+ * \return FWU status code
+ */
+int banked_fw_store_init(
+ struct fw_store *fw_store,
+ const struct metadata_serializer *serializer);
+
+/**
+ * \brief De-initialize a banked_fw_store
+ *
+ * \param[in] fw_store The subject fw_store
+ */
+void banked_fw_store_deinit(
+ struct fw_store *fw_store);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BANKED_FW_STORE_H */
diff --git a/components/service/fwu/fw_store/banked/component.cmake b/components/service/fwu/fw_store/banked/component.cmake
new file mode 100644
index 0000000..2eaafce
--- /dev/null
+++ b/components/service/fwu/fw_store/banked/component.cmake
@@ -0,0 +1,15 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2022-2023, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+if (NOT DEFINED TGT)
+ message(FATAL_ERROR "mandatory parameter TGT is not defined.")
+endif()
+
+target_sources(${TGT} PRIVATE
+ "${CMAKE_CURRENT_LIST_DIR}/banked_fw_store.c"
+ "${CMAKE_CURRENT_LIST_DIR}/metadata_manager.c"
+ "${CMAKE_CURRENT_LIST_DIR}/bank_tracker.c"
+ )
diff --git a/components/service/fwu/fw_store/banked/metadata_manager.c b/components/service/fwu/fw_store/banked/metadata_manager.c
new file mode 100644
index 0000000..f2d49f8
--- /dev/null
+++ b/components/service/fwu/fw_store/banked/metadata_manager.c
@@ -0,0 +1,352 @@
+/*
+ * Copyright (c) 2022-2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <trace.h>
+#include <protocols/service/fwu/packed-c/status.h>
+#include <media/volume/index/volume_index.h>
+#include <media/volume/volume.h>
+#include <common/crc32/crc32.h>
+#include <service/fwu/fw_store/banked/metadata_serializer/metadata_serializer.h>
+#include "metadata_manager.h"
+#include "volume_id.h"
+
+static int load_and_check_metadata(
+ struct volume *volume,
+ uint8_t *buf,
+ size_t expected_len)
+{
+ int status = FWU_STATUS_UNKNOWN;
+
+ status = volume_open(volume);
+
+ if (status == 0) {
+
+ size_t len = 0;
+
+ status = volume_read(volume, (uintptr_t)buf, expected_len, &len);
+
+ if (status == 0) {
+
+ uint32_t calc_crc = crc32(0U,
+ buf + sizeof(uint32_t),
+ expected_len - sizeof(uint32_t));
+
+ uint32_t expected_crc = *((uint32_t *)buf);
+
+ status = (calc_crc == expected_crc) ?
+ FWU_STATUS_SUCCESS : FWU_STATUS_UNKNOWN;
+ }
+
+ volume_close(volume);
+ }
+
+ return status;
+}
+
+static int store_metadata(
+ struct volume *volume,
+ const uint8_t *data,
+ size_t data_len)
+{
+ int status = volume_open(volume);
+
+ if (status == 0) {
+
+ status = volume_erase(volume);
+
+ if (status == 0) {
+
+ size_t written_len = 0;
+
+ status = volume_write(
+ volume, (const uintptr_t)data,
+ data_len, &written_len);
+ }
+
+ volume_close(volume);
+ }
+
+ return status;
+}
+
+int metadata_manager_init(
+ struct metadata_manager *subject,
+ const struct metadata_serializer *serializer)
+{
+ assert(subject);
+ assert(serializer);
+
+ subject->serializer = serializer;
+
+ /* Depending on the class of device, the storage volumes that hold FWU
+ * metadata may or may not be accessible by the metadata_manager. This
+ * should be reflected by which volumes have been added to the volume_index
+ * by deployment specific configuration code.
+ */
+ subject->primary_metadata_volume = NULL;
+ subject->backup_metadata_volume = NULL;
+
+ volume_index_find(BANKED_VOLUME_ID_PRIMARY_METADATA,
+ &subject->primary_metadata_volume);
+ volume_index_find(BANKED_VOLUME_ID_BACKUP_METADATA,
+ &subject->backup_metadata_volume);
+
+ /* A cached copy of the metadata is held in memory. It will not initially
+ * hold a valid copy until one has been loaded from storage or a fresh
+ * version has been written.
+ */
+ subject->is_dirty = false;
+ subject->is_valid = false;
+ subject->stored_crc = 0;
+ subject->metadata_len = 0;
+ subject->metadata_max_len = subject->serializer->max_size();
+ subject->metadata_cache = malloc(subject->metadata_max_len);
+
+ return (subject->metadata_cache) ?
+ FWU_STATUS_SUCCESS : FWU_STATUS_UNKNOWN;
+}
+
+void metadata_manager_deinit(
+ struct metadata_manager *subject)
+{
+ free(subject->metadata_cache);
+}
+
+int metadata_manager_check_and_repair(
+ struct metadata_manager *subject,
+ const struct fw_directory *fw_dir)
+{
+ int primary_status = FWU_STATUS_UNKNOWN;
+ int backup_status = FWU_STATUS_UNKNOWN;
+
+ /* No need to perform operation if valid data is already held.*/
+ if (subject->is_valid)
+ return FWU_STATUS_SUCCESS;
+
+ /* If no storage volume is accessible (e.g. with a single flash configuration),
+ * the operation can never succeed.
+ */
+ if (!subject->primary_metadata_volume && !subject->backup_metadata_volume) {
+
+ IMSG("FWU volume not accessible");
+ return FWU_STATUS_UNKNOWN;
+ }
+
+ /* Loaded metadata length should be consistent with the view of firmware held
+ * by the fw_directory.
+ */
+ subject->metadata_len = subject->serializer->size(fw_dir);
+
+ if (subject->primary_metadata_volume) {
+
+ primary_status = load_and_check_metadata(subject->primary_metadata_volume,
+ subject->metadata_cache,
+ subject->metadata_len);
+
+ subject->is_valid = (primary_status == FWU_STATUS_SUCCESS);
+ }
+
+ if (subject->backup_metadata_volume) {
+
+ if (subject->is_valid) {
+
+ /* Already successfully loaded the primary copy. Just need to check
+ * the backup and repair it if necessary. During an update operation,
+ * the primary metadata is always written first. A hazard exists where
+ * both primary and backup are valid but they contain different data
+ * due to a power failure just before writing the backup. This
+ * condition needs to be checked for.
+ */
+ uint8_t *backup_buf = malloc(subject->metadata_len);
+
+ if (backup_buf) {
+
+ backup_status = load_and_check_metadata(
+ subject->backup_metadata_volume,
+ backup_buf,
+ subject->metadata_len);
+
+ if ((backup_status == FWU_STATUS_SUCCESS) &&
+ (*(uint32_t *)backup_buf !=
+ *(uint32_t *)subject->metadata_cache)) {
+
+ /* Both copies have valid CRC but CRSs are different. Force
+ * the backup to be repaired.
+ */
+ backup_status = FWU_STATUS_UNKNOWN;
+ }
+
+ free(backup_buf);
+ }
+ } else {
+
+ /* Primary must have failed so use the backup copy. */
+ backup_status = load_and_check_metadata(subject->backup_metadata_volume,
+ subject->metadata_cache,
+ subject->metadata_len);
+
+ subject->is_valid = (backup_status == FWU_STATUS_SUCCESS);
+ }
+ }
+
+ /* Attempt a repair if necessary (and possible) */
+ if (subject->is_valid) {
+
+ if ((primary_status != FWU_STATUS_SUCCESS) && subject->primary_metadata_volume) {
+
+ IMSG("Repairing primary FWU metadata");
+
+ primary_status = store_metadata(
+ subject->primary_metadata_volume,
+ subject->metadata_cache,
+ subject->metadata_len);
+ }
+
+ if ((backup_status != FWU_STATUS_SUCCESS) && subject->backup_metadata_volume) {
+
+ IMSG("Repairing backup FWU metadata");
+
+ backup_status = store_metadata(
+ subject->backup_metadata_volume,
+ subject->metadata_cache,
+ subject->metadata_len);
+ }
+ }
+
+ /* Synchronize the view of the stored CRC */
+ if (subject->is_valid)
+ subject->stored_crc = *(uint32_t *)subject->metadata_cache;
+
+ return (subject->is_valid) ? FWU_STATUS_SUCCESS : FWU_STATUS_UNKNOWN;
+}
+
+int metadata_manager_update(
+ struct metadata_manager *subject,
+ uint32_t active_index,
+ uint32_t previous_active_index,
+ const struct fw_directory *fw_dir,
+ const struct bank_tracker *bank_tracker)
+{
+ int primary_status = FWU_STATUS_SUCCESS;
+ int backup_status = FWU_STATUS_SUCCESS;
+
+ subject->metadata_len = subject->serializer->size(fw_dir);
+
+ /* Serialize metadata into metadata cache */
+ subject->serializer->serialize(
+ active_index, previous_active_index,
+ fw_dir, bank_tracker,
+ subject->metadata_cache,
+ subject->metadata_max_len,
+ &subject->metadata_len);
+
+ /* Update cache copy with valid crc */
+ uint32_t calc_crc = crc32(0U,
+ subject->metadata_cache + sizeof(uint32_t),
+ subject->metadata_len - sizeof(uint32_t));
+ *(uint32_t *)subject->metadata_cache = calc_crc;
+
+ bool was_valid = subject->is_valid;
+
+ /* Cache has been updated so it now holds valid data */
+ subject->is_valid = true;
+ subject->is_dirty = true;
+
+ /* To prevent unnecessary flash writes, if after serialization, there
+ * is no change to the metadata, skip the store operation.
+ */
+ if (was_valid && (subject->stored_crc == *(uint32_t *)subject->metadata_cache))
+ return FWU_STATUS_SUCCESS;
+
+ /* Update NV storage - order of primary followed by backup is important to
+ * defend against a power failure after updating the primary but before the backup.
+ */
+ if (subject->primary_metadata_volume) {
+
+ primary_status = store_metadata(
+ subject->primary_metadata_volume,
+ subject->metadata_cache,
+ subject->metadata_len);
+ }
+
+ if (subject->backup_metadata_volume) {
+
+ backup_status = store_metadata(
+ subject->backup_metadata_volume,
+ subject->metadata_cache,
+ subject->metadata_len);
+ }
+
+ /* Updated the view of the stored data if successfully stored */
+ if ((primary_status == FWU_STATUS_SUCCESS) && (backup_status == FWU_STATUS_SUCCESS))
+ subject->stored_crc = *(uint32_t *)subject->metadata_cache;
+
+ return
+ (primary_status != FWU_STATUS_SUCCESS) ? primary_status :
+ (backup_status != FWU_STATUS_SUCCESS) ? backup_status :
+ FWU_STATUS_SUCCESS;
+}
+
+int metadata_manager_fetch(
+ struct metadata_manager *subject,
+ const uint8_t **data,
+ size_t *data_len,
+ bool *is_dirty)
+{
+ int status = FWU_STATUS_UNKNOWN;
+
+ if (subject->is_valid) {
+
+ *data = subject->metadata_cache;
+ *data_len = subject->metadata_len;
+ *is_dirty = subject->is_dirty;
+
+ subject->is_dirty = false;
+
+ status = FWU_STATUS_SUCCESS;
+ }
+
+ return status;
+}
+
+int metadata_manager_get_active_indices(
+ const struct metadata_manager *subject,
+ uint32_t *active_index,
+ uint32_t *previous_active_index)
+{
+ int status = FWU_STATUS_UNKNOWN;
+
+ if (subject->is_valid) {
+
+ subject->serializer->deserialize_active_indices(
+ active_index, previous_active_index,
+ subject->metadata_cache, subject->metadata_len);
+
+ status = FWU_STATUS_SUCCESS;
+ }
+
+ return status;
+}
+
+void metadata_manager_cache_invalidate(
+ struct metadata_manager *subject)
+{
+ subject->is_valid = false;
+}
+
+void metadata_manager_preload_bank_tracker(
+ const struct metadata_manager *subject,
+ struct bank_tracker *bank_tracker)
+{
+ subject->serializer->deserialize_bank_info(
+ bank_tracker,
+ subject->metadata_cache, subject->metadata_len);
+}
diff --git a/components/service/fwu/fw_store/banked/metadata_manager.h b/components/service/fwu/fw_store/banked/metadata_manager.h
new file mode 100644
index 0000000..67ad602
--- /dev/null
+++ b/components/service/fwu/fw_store/banked/metadata_manager.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2022-2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef METADATA_MANAGER_H
+#define METADATA_MANAGER_H
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Interface dependencies
+ */
+struct volume;
+struct fw_directory;
+struct bank_tracker;
+struct metadata_serializer;
+
+/**
+ * \brief metadata_manager structure definition
+ *
+ * Manages the FWU metadata seen by the boot loader.
+ */
+struct metadata_manager {
+
+ /* Volume objects for IO operations to NV storage */
+ struct volume *primary_metadata_volume;
+ struct volume *backup_metadata_volume;
+
+ /* Metadata serializer compatible with bootloader */
+ const struct metadata_serializer *serializer;
+
+ /* Cached copy of metadata */
+ bool is_dirty;
+ bool is_valid;
+ uint32_t stored_crc;
+ size_t metadata_len;
+ size_t metadata_max_len;
+ uint8_t *metadata_cache;
+};
+
+/**
+ * \brief Initialize the metadata_manager
+ *
+ * \param[in] subject This instance
+ * \param[in] serializer Metadata serializer to use
+ *
+ * \return Status 0 on success
+ */
+int metadata_manager_init(
+ struct metadata_manager *subject,
+ const struct metadata_serializer *serializer);
+
+/**
+ * \brief De-initialize the metadata_manager
+ *
+ * \param[in] subject This instance
+ */
+void metadata_manager_deinit(
+ struct metadata_manager *subject);
+
+/**
+ * \brief Check integrity of FWU metadata and repair if necessary
+ *
+ * FWU metadata is vulnerable to corruption due to power failure during a
+ * write to storage. To mitigate this risk, a replica is maintained which
+ * the boot loader will use if necessary. When a corruption occurs, the
+ * corrupted copy is repaired by copying the intact replica. Returns
+ * failure if a repair was not possible.
+ *
+ * \param[in] subject This instance
+ * \param[in] fw_dir The fw_directory
+ *
+ * \return Status 0 if intact or if repair was successful
+ */
+int metadata_manager_check_and_repair(
+ struct metadata_manager *subject,
+ const struct fw_directory *fw_dir);
+
+/**
+ * \brief Update the FWU metadata seen by the boot loader
+ *
+ * \param[in] subject This instance
+ * \param[in] active_index The active bank index
+ * \param[in] previous_active_index The previous active bank index
+ * \param[in] fw_dir Source firmware directory
+ * \param[in] bank_tracker Provides bank state
+ *
+ * \return Status 0 if successful
+ */
+int metadata_manager_update(
+ struct metadata_manager *subject,
+ uint32_t active_index,
+ uint32_t previous_active_index,
+ const struct fw_directory *fw_dir,
+ const struct bank_tracker *bank_tracker);
+
+/**
+ * \brief Get the active index values from the metadata
+ *
+ * \param[in] subject This instance
+ * \param[out] active_index The active bank index
+ * \param[out] previous_active_index The previous active bank index
+ *
+ * \return Status 0 if successful
+ */
+int metadata_manager_get_active_indices(
+ const struct metadata_manager *subject,
+ uint32_t *active_index,
+ uint32_t *previous_active_index);
+
+/**
+ * \brief Fetch the FWU metadata that should be seen by the boot loader
+ *
+ * In deployments where the metadata manager is unable to update the metadata
+ * seen by the boot loader directly, this function outputs the most recently
+ * updated view of the metadata to enable a Nwd component to perform the necessary
+ * write to storage.
+ *
+ * \param[in] subject This instance
+ * \param[out] data Outputs pointer to data
+ * \param[out] data_len Length of metadata
+ * \param[out] is_dirty True if updated since previous call
+ *
+ * \return Status 0 if successful
+ */
+int metadata_manager_fetch(
+ struct metadata_manager *subject,
+ const uint8_t **data,
+ size_t *data_len,
+ bool *is_dirty);
+
+/**
+ * \brief Invalidate the metadata cache
+ *
+ * \param[in] subject This instance
+ */
+void metadata_manager_cache_invalidate(
+ struct metadata_manager *subject);
+
+/**
+ * \brief Preload the bank_tracker with NV state from metadata
+ *
+ * \param[in] subject This instance
+ * \param[in] bank_tracker The bank_tracker to modify
+ *
+ * \return Status 0 if successful
+ */
+void metadata_manager_preload_bank_tracker(
+ const struct metadata_manager *subject,
+ struct bank_tracker *bank_tracker);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* METADATA_MANAGER_H */
diff --git a/components/service/fwu/fw_store/banked/metadata_serializer/metadata_serializer.h b/components/service/fwu/fw_store/banked/metadata_serializer/metadata_serializer.h
new file mode 100644
index 0000000..2e411e5
--- /dev/null
+++ b/components/service/fwu/fw_store/banked/metadata_serializer/metadata_serializer.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2022-2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef METADATA_SERIALIZER_H
+#define METADATA_SERIALIZER_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Interface dependencies
+ */
+struct fw_directory;
+struct bank_tracker;
+
+/**
+ * \brief FWU metadata serializer interface
+ *
+ * Defines a common interface for FWU serialization operations. Because
+ * updates to the bootloader and update agent are likely to occur at different
+ * times, it is necessary for the update agent to have some agility over the
+ * metadata version that it generates in order to maintain compatibility with
+ * the bootloader. The active serializer is selected based on the version
+ * reported by the bootloader at boot time. Note that the early stage bootloader
+ * that interprets the metadata cannot be updated using the banked update
+ * mechanism so it is not possible to rely on a simultaneous update strategy.
+ * To provide a pathway for transitioning to a new metadata version,
+ * support for runtime selection of the metadata serializer is possible.
+ */
+struct metadata_serializer {
+
+ /**
+ * \brief Serialize FWU metadata
+ *
+ * Serialize FWU metadata using the presented inputs.
+ *
+ * \param[in] active_index The active bank index
+ * \param[in] previous_active_index The previous active bank index
+ * \param[in] fw_dir Source firmware directory
+ * \param[in] bank_tracker source bank_tracker
+ * \param[in] buf Serialize into this buffer
+ * \param[in] buf_size Size of buffer
+ * \param[out] metadata_len Size of serialized metadata
+ *
+ * \return Status
+ */
+ int (*serialize)(
+ uint32_t active_index,
+ uint32_t previous_active_index,
+ const struct fw_directory *fw_dir,
+ const struct bank_tracker *bank_tracker,
+ uint8_t *buf,
+ size_t buf_size,
+ size_t *metadata_len);
+
+ /**
+ * \brief Return serialized FWU metadata size
+ *
+ * \param[in] fw_dir Source information
+ *
+ * \return Size in bytes
+ */
+ size_t (*size)(
+ const struct fw_directory *fw_dir);
+
+ /**
+ * \brief Return the maximum serialized FWU metadata size
+ *
+ * \return Size in bytes
+ */
+ size_t (*max_size)(void);
+
+ /**
+ * \brief De-serialize bank info information
+ *
+ * \param[in] bank_tracker Destination bank_tracker
+ * \param[in] serialized_metadata Serialized metadata
+ * \param[in] metadata_len Length of serialized metadata
+ */
+ void (*deserialize_bank_info)(
+ struct bank_tracker *bank_tracker,
+ const uint8_t *serialized_metadata,
+ size_t metadata_len);
+
+ /**
+ * \brief De-serialize active indices
+ *
+ * \param[out] active_index active_index value
+ * \param[out] previous_active_index previous_active_index value
+ * \param[in] serialized_metadata Serialized metadata
+ * \param[in] metadata_len Length of serialized metadata
+ */
+ void (*deserialize_active_indices)(
+ uint32_t *active_index,
+ uint32_t *previous_active_index,
+ const uint8_t *serialized_metadata,
+ size_t metadata_len);
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* METADATA_SERIALIZER_H */
diff --git a/components/service/fwu/fw_store/banked/metadata_serializer/v1/component.cmake b/components/service/fwu/fw_store/banked/metadata_serializer/v1/component.cmake
new file mode 100644
index 0000000..17c50f1
--- /dev/null
+++ b/components/service/fwu/fw_store/banked/metadata_serializer/v1/component.cmake
@@ -0,0 +1,13 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2023, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+if (NOT DEFINED TGT)
+ message(FATAL_ERROR "mandatory parameter TGT is not defined.")
+endif()
+
+target_sources(${TGT} PRIVATE
+ "${CMAKE_CURRENT_LIST_DIR}/metadata_serializer_v1.c"
+ )
diff --git a/components/service/fwu/fw_store/banked/metadata_serializer/v1/metadata_serializer_v1.c b/components/service/fwu/fw_store/banked/metadata_serializer/v1/metadata_serializer_v1.c
new file mode 100644
index 0000000..39e0fea
--- /dev/null
+++ b/components/service/fwu/fw_store/banked/metadata_serializer/v1/metadata_serializer_v1.c
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2022-2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <assert.h>
+#include <stddef.h>
+#include <string.h>
+#include <common/uuid/uuid.h>
+#include <protocols/service/fwu/packed-c/metadata_v1.h>
+#include <protocols/service/fwu/packed-c/status.h>
+#include <media/volume/volume.h>
+#include <media/volume/index/volume_index.h>
+#include <service/fwu/agent/fw_directory.h>
+#include <service/fwu/fw_store/banked/bank_tracker.h>
+#include <service/fwu/fw_store/banked/volume_id.h>
+#include <service/fwu/fw_store/banked/metadata_serializer/metadata_serializer.h>
+#include "metadata_serializer_v1.h"
+
+
+static int serialize_image_entries(
+ struct fwu_metadata *metadata,
+ const struct fw_directory *fw_dir,
+ const struct bank_tracker *bank_tracker)
+{
+ size_t image_index = 0;
+
+ do {
+ /* Image entry indices in the metadata correspond to the image index
+ * of the associate entry in the fw_directory.
+ */
+ const struct image_info *image_info =
+ fw_directory_get_image_info(fw_dir, image_index);
+
+ if (!image_info)
+ break;
+
+ /* Information about storage for the image is retrieved from the configured
+ * volume objects that provide access to the banked storage. Both volumes
+ * are assumed to have the same parent location, identified by the location
+ * uuid.
+ */
+ struct uuid_octets location_uuid = {0};
+ struct fwu_image_entry *entry = &metadata->img_entry[image_index];
+
+ /* Serialize bank storage info */
+ for (size_t bank_index = 0;
+ bank_index < BANK_SCHEME_NUM_BANKS; bank_index++) {
+
+ struct uuid_octets img_uuid = {0};
+ struct volume *volume = NULL;
+
+ int status = volume_index_find(
+ banked_volume_id(
+ image_info->location_id,
+ banked_usage_id(bank_index)),
+ &volume);
+
+ if (!status && volume)
+ volume_get_storage_ids(volume, &img_uuid, &location_uuid);
+
+ struct fwu_image_properties *properties = &entry->img_props[bank_index];
+
+ memcpy(properties->img_uuid, img_uuid.octets, OSF_UUID_OCTET_LEN);
+ properties->reserved = 0;
+ properties->accepted = bank_tracker_is_accepted(
+ bank_tracker, bank_index, image_index) ? 1 : 0;
+ }
+
+ /* Serialize per-image UUIDs */
+ memcpy(entry->img_type_uuid,
+ image_info->img_type_uuid.octets, OSF_UUID_OCTET_LEN);
+ memcpy(entry->location_uuid,
+ location_uuid.octets, OSF_UUID_OCTET_LEN);
+
+ ++image_index;
+
+ } while (true);
+
+ return FWU_STATUS_SUCCESS;
+}
+
+static size_t metadata_serializer_size(
+ const struct fw_directory *fw_dir)
+{
+ return
+ offsetof(struct fwu_metadata, img_entry) +
+ fw_directory_num_images(fw_dir) * sizeof(struct fwu_image_entry);
+}
+
+static size_t metadata_serializer_max_size(void)
+{
+ return
+ offsetof(struct fwu_metadata, img_entry) +
+ FWU_MAX_FW_DIRECTORY_ENTRIES * sizeof(struct fwu_image_entry);
+}
+
+static int metadata_serializer_serialize(
+ uint32_t active_index,
+ uint32_t previous_active_index,
+ const struct fw_directory *fw_dir,
+ const struct bank_tracker *bank_tracker,
+ uint8_t *buf,
+ size_t buf_size,
+ size_t *metadata_len)
+{
+ int status = FWU_STATUS_UNKNOWN;
+ size_t serialized_size = metadata_serializer_size(fw_dir);
+
+ *metadata_len = 0;
+
+ if (serialized_size <= buf_size) {
+
+ struct fwu_metadata *metadata = (struct fwu_metadata *)buf;
+
+ /* Serialize metadata header */
+ metadata->crc_32 = 0;
+ metadata->version = FWU_METADATA_VERSION;
+ metadata->active_index = active_index;
+ metadata->previous_active_index = previous_active_index;
+
+ /* Serialize image entries */
+ status = serialize_image_entries(metadata, fw_dir, bank_tracker);
+
+ if (status == FWU_STATUS_SUCCESS)
+ *metadata_len = serialized_size;
+ }
+
+ return status;
+}
+
+static void metadata_serializer_deserialize_bank_info(
+ struct bank_tracker *bank_tracker,
+ const uint8_t *serialized_metadata,
+ size_t metadata_len)
+{
+ const struct fwu_metadata *metadata = (const struct fwu_metadata *)serialized_metadata;
+
+ /* Assume referenced banks hold content */
+ if (metadata->active_index < BANK_SCHEME_NUM_BANKS)
+ bank_tracker_set_holds_content(bank_tracker, metadata->active_index);
+
+ if (metadata->previous_active_index < BANK_SCHEME_NUM_BANKS)
+ bank_tracker_set_holds_content(bank_tracker, metadata->previous_active_index);
+
+ /* Deserialize image accept state */
+ if (metadata_len >= offsetof(struct fwu_metadata, img_entry)) {
+
+ size_t num_images =
+ (metadata_len - offsetof(struct fwu_metadata, img_entry)) /
+ sizeof(struct fwu_image_entry);
+
+ for (size_t image_index = 0; image_index < num_images; image_index++) {
+
+ const struct fwu_image_entry *image_entry =
+ &metadata->img_entry[image_index];
+
+ for (size_t bank_index = 0;
+ bank_index < BANK_SCHEME_NUM_BANKS; bank_index++) {
+
+ if (image_entry->img_props[bank_index].accepted)
+ bank_tracker_accept(bank_tracker, bank_index, image_index);
+ }
+ }
+ }
+}
+
+static void metadata_serializer_deserialize_active_indices(
+ uint32_t *active_index,
+ uint32_t *previous_active_index,
+ const uint8_t *serialized_metadata,
+ size_t metadata_len)
+{
+ const struct fwu_metadata *metadata = (const struct fwu_metadata *)serialized_metadata;
+
+ assert(metadata_len >= offsetof(struct fwu_metadata, img_entry));
+
+ *active_index = metadata->active_index;
+ *previous_active_index = metadata->previous_active_index;
+}
+
+const struct metadata_serializer *metadata_serializer_v1(void)
+{
+ static const struct metadata_serializer serializer = {
+ metadata_serializer_serialize,
+ metadata_serializer_size,
+ metadata_serializer_max_size,
+ metadata_serializer_deserialize_bank_info,
+ metadata_serializer_deserialize_active_indices
+ };
+
+ return &serializer;
+}
diff --git a/components/service/fwu/fw_store/banked/metadata_serializer/v1/metadata_serializer_v1.h b/components/service/fwu/fw_store/banked/metadata_serializer/v1/metadata_serializer_v1.h
new file mode 100644
index 0000000..e64712b
--- /dev/null
+++ b/components/service/fwu/fw_store/banked/metadata_serializer/v1/metadata_serializer_v1.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef METADATA_SERIALIZER_V1_H
+#define METADATA_SERIALIZER_V1_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Interface dependencies
+ */
+struct metadata_serializer;
+
+/**
+ * \brief Return a metadata_serializer for version 1 serialization
+ *
+ */
+const struct metadata_serializer *metadata_serializer_v1(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* METADATA_SERIALIZER_V1_H */
diff --git a/components/service/fwu/fw_store/banked/test/component.cmake b/components/service/fwu/fw_store/banked/test/component.cmake
new file mode 100644
index 0000000..4eb11f0
--- /dev/null
+++ b/components/service/fwu/fw_store/banked/test/component.cmake
@@ -0,0 +1,13 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2022, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+if (NOT DEFINED TGT)
+ message(FATAL_ERROR "mandatory parameter TGT is not defined.")
+endif()
+
+target_sources(${TGT} PRIVATE
+ "${CMAKE_CURRENT_LIST_DIR}/metadata_manager_tests.cpp"
+ )
diff --git a/components/service/fwu/fw_store/banked/test/metadata_manager_tests.cpp b/components/service/fwu/fw_store/banked/test/metadata_manager_tests.cpp
new file mode 100644
index 0000000..0bc4310
--- /dev/null
+++ b/components/service/fwu/fw_store/banked/test/metadata_manager_tests.cpp
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2022-2023, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <stdlib.h>
+#include <common/uuid/uuid.h>
+#include <media/disk/guid.h>
+#include <media/volume/block_volume/block_volume.h>
+#include <media/volume/index/volume_index.h>
+#include <media/volume/volume.h>
+#include <service/block_storage/factory/ref_ram_gpt/block_store_factory.h>
+#include <service/fwu/fw_store/banked/metadata_manager.h>
+#include <service/fwu/fw_store/banked/metadata_serializer/metadata_serializer.h>
+#include <service/fwu/fw_store/banked/metadata_serializer/v1/metadata_serializer_v1.h>
+#include <service/fwu/fw_store/banked/bank_tracker.h>
+#include <service/fwu/fw_store/banked/volume_id.h>
+#include <service/fwu/agent/fw_directory.h>
+#include <service/fwu/inspector/mock/mock_fw_inspector.h>
+#include <CppUTest/TestHarness.h>
+
+TEST_GROUP(FwuMetadataManagerTests)
+{
+ void setup()
+ {
+ int result;
+ struct uuid_octets partition_guid;
+
+ /* Default to V1 metadata serializer */
+ m_serializer = metadata_serializer_v1();
+
+ /* Construct storage */
+ volume_index_init();
+ m_block_store = ref_ram_gpt_block_store_factory_create();
+
+ /* Construct primary metadata volume */
+ uuid_guid_octets_from_canonical(&partition_guid,
+ DISK_GUID_UNIQUE_PARTITION_PRIMARY_FWU_METADATA);
+
+ result = block_volume_init(&m_primary_block_volume,
+ m_block_store, &partition_guid,
+ &m_primary_volume);
+
+ LONGS_EQUAL(0, result);
+ CHECK_TRUE(m_primary_volume);
+
+ /* Construct backup metadata volume */
+ uuid_guid_octets_from_canonical(&partition_guid,
+ DISK_GUID_UNIQUE_PARTITION_BACKUP_FWU_METADATA);
+
+ result = block_volume_init(&m_backup_block_volume,
+ m_block_store, &partition_guid,
+ &m_backup_volume);
+
+ LONGS_EQUAL(0, result);
+ CHECK_TRUE(m_backup_volume);
+
+ bank_tracker_init(&m_bank_tracker);
+ fw_directory_init(&m_fw_directory);
+
+ result = mock_fw_inspector_inspect(&m_fw_directory, BOOT_INDEX);
+ LONGS_EQUAL(0, result);
+ }
+
+ void teardown()
+ {
+ metadata_manager_deinit(&m_metadata_manager);
+ fw_directory_deinit(&m_fw_directory);
+ bank_tracker_deinit(&m_bank_tracker);
+
+ volume_index_clear();
+ block_volume_deinit(&m_primary_block_volume);
+ block_volume_deinit(&m_backup_block_volume);
+ ref_ram_gpt_block_store_factory_destroy(m_block_store);
+ }
+
+ void corrupt_metadata(struct volume *volume)
+ {
+ int status;
+ size_t metadata_size = m_serializer->size(&m_fw_directory);
+ uint8_t metadata_buf[metadata_size];
+ size_t actual_len = 0;
+
+ status = volume_open(volume);
+ LONGS_EQUAL(0, status);
+
+ status = volume_read(volume,
+ (uintptr_t)metadata_buf, metadata_size, &actual_len);
+ LONGS_EQUAL(0, status);
+ UNSIGNED_LONGS_EQUAL(metadata_size, actual_len);
+
+ /* Corrupt the first byte */
+ metadata_buf[0] ^= 0xff;
+
+ /* Erase contents and write back the corrupted copy */
+ status = volume_erase(volume);
+ LONGS_EQUAL(0, status);
+
+ status = volume_seek(volume, IO_SEEK_SET, 0);
+ LONGS_EQUAL(0, status);
+
+ status = volume_write(volume,
+ (const uintptr_t)metadata_buf, metadata_size, &actual_len);
+ LONGS_EQUAL(0, status);
+ UNSIGNED_LONGS_EQUAL(metadata_size, actual_len);
+
+ volume_close(volume);
+ }
+
+ static const unsigned int BOOT_INDEX = 1;
+
+ struct block_store *m_block_store;
+ struct block_volume m_primary_block_volume;
+ struct block_volume m_backup_block_volume;
+ struct volume *m_primary_volume;
+ struct volume *m_backup_volume;
+ struct metadata_manager m_metadata_manager;
+ struct fw_directory m_fw_directory;
+ struct bank_tracker m_bank_tracker;
+ const struct metadata_serializer *m_serializer;
+};
+
+TEST(FwuMetadataManagerTests, checkAndRepairAccessibleStorage)
+{
+ int result = 0;
+
+ /* Check configuration where metadata storage is accessible. Because neither
+ * metadata copy has been initialized, initially expect the check and repair
+ * operation to fail. */
+ result = volume_index_add(BANKED_VOLUME_ID_PRIMARY_METADATA, m_primary_volume);
+ LONGS_EQUAL(0, result);
+ result = volume_index_add(BANKED_VOLUME_ID_BACKUP_METADATA, m_backup_volume);
+ LONGS_EQUAL(0, result);
+
+ result = metadata_manager_init(&m_metadata_manager, m_serializer);
+ LONGS_EQUAL(0, result);
+
+ result = metadata_manager_check_and_repair(&m_metadata_manager, &m_fw_directory);
+ CHECK_TRUE(result != 0);
+
+ /* An update to the metadata should result in both primary and backup copies
+ * being initialized. */
+ result = metadata_manager_update(&m_metadata_manager, 0, 1,
+ &m_fw_directory, &m_bank_tracker);
+ LONGS_EQUAL(0, result);
+
+ /* If the update was successful, check_and_repair shouldn't have anything to do. */
+ result = metadata_manager_check_and_repair(&m_metadata_manager, &m_fw_directory);
+ LONGS_EQUAL(0, result);
+
+ /* Invalidating the cache should force a reload but expect both volumes to hold
+ * valid data.*/
+ metadata_manager_cache_invalidate(&m_metadata_manager);
+ result = metadata_manager_check_and_repair(&m_metadata_manager, &m_fw_directory);
+ LONGS_EQUAL(0, result);
+
+ /* Corrupt either copy randomly a few times and expect to always repair */
+ for (size_t i = 0; i < 100; ++i) {
+
+ struct volume *volume = (rand() & 1) ?
+ m_primary_volume :
+ m_backup_volume;
+
+ corrupt_metadata(volume);
+ metadata_manager_cache_invalidate(&m_metadata_manager);
+ result = metadata_manager_check_and_repair(&m_metadata_manager, &m_fw_directory);
+ LONGS_EQUAL(0, result);
+ }
+
+ /* Corrupt both copies - repair should not be possible */
+ corrupt_metadata(m_primary_volume);
+ corrupt_metadata(m_backup_volume);
+ metadata_manager_cache_invalidate(&m_metadata_manager);
+ result = metadata_manager_check_and_repair(&m_metadata_manager, &m_fw_directory);
+ CHECK_TRUE(result != 0);
+}
+
+TEST(FwuMetadataManagerTests, checkAndRepairInaccessibleStorage)
+{
+ int result = 0;
+
+ /* Check configuration where metadata storage is inaccessible (i.e. no
+ * volumes added to volume_index). Expect the check to fail, indicating
+ * that an update is required to initialise the cache. */
+ result = metadata_manager_init(&m_metadata_manager, m_serializer);
+ LONGS_EQUAL(0, result);
+
+ result = metadata_manager_check_and_repair(&m_metadata_manager, &m_fw_directory);
+ CHECK_TRUE(result != 0);
+}
\ No newline at end of file
diff --git a/components/service/fwu/fw_store/banked/volume_id.h b/components/service/fwu/fw_store/banked/volume_id.h
new file mode 100644
index 0000000..d29bccf
--- /dev/null
+++ b/components/service/fwu/fw_store/banked/volume_id.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef BANKED_FW_STORE_VOLUME_ID_H
+#define BANKED_FW_STORE_VOLUME_ID_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Storage volume IDs used by a banked_fw_store.
+ *
+ * These IDs are used to obtain deployment specific volume objects from
+ * the volume_index to enable storage IO operations to be performed. Use
+ * of volume IDs allows generic components to be decoupled from deployment
+ * specific code. A set of volume objects will be constructed for a deployment,
+ * each configured to provide access to each area of storage that holds
+ * NV data that will be modified during the FWU process.
+ *
+ * To allow images to be installed into different storage locations (e.g.
+ * different storage partitions), a volume ID numbering scheme is used for
+ * the banked_fw_store that combines a location ID with usage ID.
+ */
+
+/* FWU metadata volume IDs. A single location is assumed. */
+#define BANKED_VOLUME_ID_PRIMARY_METADATA (0xffff0000)
+#define BANKED_VOLUME_ID_BACKUP_METADATA (0xffff0001)
+
+/* Per-location usage IDs for the banked_fw store */
+#define BANKED_USAGE_ID_FW_BANK_A (0)
+#define BANKED_USAGE_ID_FW_BANK_B (1)
+
+/**
+ * \brief Return a volume id constructed from a usage and location id
+ *
+ * Banked storage may be distributed across multiple locations. This
+ * function creates a volume ID made up of a location ID and a usage ID.
+ * A platform integrator is free define as many location IDs as is
+ * necessary to enable different areas of firmware storage to be
+ * updated. A location could correspond to say a storage partition or
+ * storage managed by a separate MCU.
+ *
+ * \param[in] location_id Platform specific location id
+ * \param[in] usage_id The requires usage for the volume
+ *
+ * \return volume id
+ */
+static inline unsigned int banked_volume_id(
+ unsigned int location_id,
+ unsigned int usage_id)
+{
+ return (location_id << 16) | (usage_id & 0xffff);
+}
+
+/**
+ * \brief Return the usage id for the specified bank index
+ *
+ * \param[in] bank_index The bank index [0..1]
+ *
+ * \return Usage ID
+ */
+static inline unsigned int banked_usage_id(unsigned int bank_index)
+{
+ return (bank_index == 0) ?
+ BANKED_USAGE_ID_FW_BANK_A : BANKED_USAGE_ID_FW_BANK_B;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BANKED_FW_STORE_VOLUME_ID_H */
diff --git a/components/service/fwu/fw_store/fw_store.h b/components/service/fwu/fw_store/fw_store.h
new file mode 100644
index 0000000..b4eeade
--- /dev/null
+++ b/components/service/fwu/fw_store/fw_store.h
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef FW_STORE_H
+#define FW_STORE_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Interface dependencies
+ */
+struct fw_store;
+struct fw_directory;
+struct image_info;
+struct installer;
+struct uuid_octets;
+
+/**
+ * \brief Synchronize fw_store state to the active state of the firmware
+ *
+ * Synchronizes the state of the fw_store to reflect the state of the
+ * booted firmware obtained from the firmware directory. The firmware directory
+ * will have been populated with information obtained from trusted
+ * sources such as the attestation service.
+ *
+ * \param[in] fw_store The subject fw_store
+ * \param[in] fw_dir The firmware directory to synchronize to
+ * \param[in] boot_index The boot_index reported by the bootloader
+ *
+ * \return FWU status code
+ */
+int fw_store_synchronize(
+ struct fw_store *fw_store,
+ struct fw_directory *fw_dir,
+ unsigned int boot_index);
+
+/**
+ * \brief Begin to install one or more images
+ *
+ * Should be called prior to installing one or more images into this fw_store.
+ * As long as success is returned, installation operations may follow.
+ *
+ * \param[in] fw_store The subject fw_store
+ *
+ * \return FWU status code
+ */
+int fw_store_begin_install(
+ struct fw_store *fw_store);
+
+/**
+ * \brief Cancel any install operation in progress
+ *
+ * \param[in] fw_store The subject fw_store
+ */
+void fw_store_cancel_install(
+ struct fw_store *fw_store);
+
+/**
+ * \brief Finalize the installation of a set of images
+ *
+ * Should be called after installing all images. Once finalized, the new
+ * installation may be activated in preparation for a trial of the update.
+ *
+ * \param[in] fw_store The subject fw_store
+ *
+ * \return FWU status code
+ */
+int fw_store_finalize_install(
+ struct fw_store *fw_store);
+
+/**
+ * \brief Select an installer
+ *
+ * Select an installer to handle installation of the image described by
+ * the image_info.
+ *
+ * \param[in] fw_store The subject fw_store
+ * \param[in] image_info Image info that describes the image to install
+ * \param[out] installer Selected installer
+ *
+ * \return FWU status code
+ */
+int fw_store_select_installer(
+ struct fw_store *fw_store,
+ const struct image_info *image_info,
+ struct installer **installer);
+
+/**
+ * \brief Write image data during image installation
+ *
+ * \param[in] fw_store The subject fw_store
+ * \param[in] installer The selected installer
+ * \param[in] data Pointer to data
+ * \param[in] data_len The data length
+ *
+ * \return FWU status code
+ */
+int fw_store_write_image(
+ struct fw_store *fw_store,
+ struct installer *installer,
+ const uint8_t *data,
+ size_t data_len);
+
+/**
+ * \brief Commit image data
+ *
+ * Called after fw_store_write_image to commit all image data written for an image.
+ *
+ * \param[in] fw_store The subject fw_store
+ * \param[in] installer The selected installer
+ * \param[in] image_info Info about the image
+ * \param[in] accepted Initial accepted state
+ *
+ * \return FWU status code
+ */
+int fw_store_commit_image(
+ struct fw_store *fw_store,
+ struct installer *installer,
+ const struct image_info *image_info,
+ bool accepted);
+
+/**
+ * \brief Notify that an updated image has been accepted
+ *
+ * \param[in] fw_store The subject fw_store
+ * \param[in] image_info Information about the accepted image
+ *
+ * \return True if all necessary images have been accepted
+ */
+bool fw_store_notify_accepted(
+ struct fw_store *fw_store,
+ const struct image_info *image_info);
+
+/**
+ * \brief Check if image is accepted
+ *
+ * \param[in] fw_store The subject fw_store
+ * \param[in] image_info Information about the image
+ *
+ * \return True if image has been accepted
+ */
+bool fw_store_is_accepted(
+ const struct fw_store *fw_store,
+ const struct image_info *image_info);
+
+/**
+ * \brief Check if the booted firmware is being trialed
+ *
+ * \param[in] fw_store The subject fw_store
+ *
+ * \return True if trialed
+ */
+bool fw_store_is_trial(
+ const struct fw_store *fw_store);
+
+/**
+ * \brief Commit to the complete update
+ *
+ * \param[in] fw_store The subject fw_store
+ *
+ * \return FWU status code
+ */
+int fw_store_commit_to_update(
+ struct fw_store *fw_store);
+
+/**
+ * \brief Revert back to the previous good version (if possible)
+ *
+ * \param[in] fw_store The subject fw_store
+ *
+ * \return FWU status code
+ */
+int fw_store_revert_to_previous(
+ struct fw_store *fw_store);
+
+/**
+ * \brief Export fw_store specific objects
+ *
+ * Provides a way of exporting objects from a concrete fw_store e.g.
+ * for coordinating with a Nwd client. A concrete fw_store may export
+ * [0..*] types of object.
+ *
+ * \param[in] fw_store The subject fw_store
+ * \param[in] uuid Identifies the object
+ * \param[out] data Pointer to data to the exported object
+ * \param[out] data_len Length of the exported object
+ * \param[out] status Status of the export operation
+ *
+ * \return True if UUID identifies an object held by the fw_store
+ */
+bool fw_store_export(
+ struct fw_store *fw_store,
+ const struct uuid_octets *uuid,
+ const uint8_t **data,
+ size_t *data_len,
+ int *status);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FW_STORE_H */
diff --git a/deployments/component-test/component-test.cmake b/deployments/component-test/component-test.cmake
index bd9e2cb..e6a8cef 100644
--- a/deployments/component-test/component-test.cmake
+++ b/deployments/component-test/component-test.cmake
@@ -1,5 +1,5 @@
#-------------------------------------------------------------------------------
-# Copyright (c) 2020-2022, Arm Limited and Contributors. All rights reserved.
+# Copyright (c) 2020-2023, Arm Limited and Contributors. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
@@ -109,6 +109,9 @@
"components/service/block_storage/factory/ref_ram_gpt"
"components/service/block_storage/factory/client"
"components/service/fwu/agent"
+ "components/service/fwu/fw_store/banked"
+ "components/service/fwu/fw_store/banked/metadata_serializer/v1"
+ "components/service/fwu/fw_store/banked/test"
"components/service/fwu/installer"
"components/service/fwu/inspector/mock"
"components/service/crypto/client/cpp"