Add FWU installer framework
Adds the base installer and installer_index to allow specialized
installers to be added to handle different types of update
image.
Signed-off-by: Julian Hall <julian.hall@arm.com>
Change-Id: I18fa376106dae36a36f0078ed76c7fab5fb83a16
diff --git a/components/service/fwu/installer/component.cmake b/components/service/fwu/installer/component.cmake
new file mode 100644
index 0000000..5f124f1
--- /dev/null
+++ b/components/service/fwu/installer/component.cmake
@@ -0,0 +1,27 @@
+#-------------------------------------------------------------------------------
+# 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}/installer.c"
+ "${CMAKE_CURRENT_LIST_DIR}/installer_index.c"
+ )
+
+# Configurable build parameters
+# TS_CFG_FWU_MAX_INSTALLERS Maximum number of installers allowed
+# TS_CFG_FWU_MAX_LOCATIONS Maximum number of updatable locations
+if(DEFINED TS_CFG_FWU_MAX_INSTALLERS)
+ target_compile_definitions(${TGT} PRIVATE
+ INSTALLER_INDEX_LIMIT=${TS_CFG_FWU_MAX_INSTALLERS})
+endif()
+
+if(DEFINED TS_CFG_FWU_MAX_LOCATIONS)
+ target_compile_definitions(${TGT} PRIVATE
+ INSTALLER_INDEX_LOCATION_ID_LIMIT=${TS_CFG_FWU_MAX_LOCATIONS})
+endif()
diff --git a/components/service/fwu/installer/installer.c b/components/service/fwu/installer/installer.c
new file mode 100644
index 0000000..3c78a65
--- /dev/null
+++ b/components/service/fwu/installer/installer.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2022-2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <protocols/service/fwu/packed-c/status.h>
+#include "installer.h"
+
+void installer_init(
+ struct installer *installer,
+ enum install_type install_type,
+ uint32_t location_id,
+ const struct uuid_octets *location_uuid,
+ void *context,
+ const struct installer_interface *interface)
+{
+ assert(installer);
+ assert(location_uuid);
+ assert(context);
+ assert(interface);
+
+ installer->install_type = install_type;
+ installer->location_id = location_id;
+ installer->location_uuid = *location_uuid;
+ installer->context = context;
+ installer->interface = interface;
+
+ installer->install_status = FWU_STATUS_SUCCESS;
+ installer->is_active = false;
+ installer->next = NULL;
+}
+
+int installer_begin(
+ struct installer *installer,
+ uint32_t current_volume_id,
+ uint32_t update_volume_id)
+{
+ assert(installer);
+ assert(installer->interface);
+ assert(installer->interface->begin);
+
+ installer->install_status = FWU_STATUS_SUCCESS;
+ installer->is_active = true;
+
+ return installer->interface->begin(
+ installer->context,
+ current_volume_id,
+ update_volume_id);
+}
+
+int installer_finalize(
+ struct installer *installer)
+{
+ assert(installer);
+ assert(installer->interface);
+ assert(installer->interface->finalize);
+
+ installer->is_active = false;
+
+ return installer->interface->finalize(
+ installer->context);
+}
+
+void installer_abort(
+ struct installer *installer)
+{
+ assert(installer);
+ assert(installer->interface);
+ assert(installer->interface->abort);
+
+ installer->is_active = false;
+
+ installer->interface->abort(
+ installer->context);
+}
+
+int installer_open(
+ struct installer *installer,
+ const struct image_info *image_info)
+{
+ assert(installer);
+ assert(installer->interface);
+ assert(installer->interface->open);
+
+ int status = installer->interface->open(
+ installer->context,
+ image_info);
+
+ if (status && !installer->install_status)
+ installer->install_status = status;
+
+ return status;
+}
+
+int installer_commit(
+ struct installer *installer)
+{
+ assert(installer);
+ assert(installer->interface);
+ assert(installer->interface->commit);
+
+ int status = installer->interface->commit(
+ installer->context);
+
+ if (status && !installer->install_status)
+ installer->install_status = status;
+
+ return status;
+}
+
+int installer_write(
+ struct installer *installer,
+ const uint8_t *data,
+ size_t data_len)
+{
+ assert(installer);
+ assert(installer->interface);
+ assert(installer->interface->write);
+
+ int status = installer->interface->write(
+ installer->context,
+ data, data_len);
+
+ if (status && !installer->install_status)
+ installer->install_status = status;
+
+ return status;
+}
+
+int installer_enumerate(
+ struct installer *installer,
+ uint32_t volume_id,
+ struct fw_directory *fw_directory)
+{
+ assert(installer);
+ assert(installer->interface);
+ assert(installer->interface->enumerate);
+
+ return installer->interface->enumerate(
+ installer->context,
+ volume_id,
+ fw_directory);
+}
diff --git a/components/service/fwu/installer/installer.h b/components/service/fwu/installer/installer.h
new file mode 100644
index 0000000..49c822c
--- /dev/null
+++ b/components/service/fwu/installer/installer.h
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2022-2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef FWU_INSTALLATION_INSTALLER_H
+#define FWU_INSTALLATION_INSTALLER_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <common/uuid/uuid.h>
+#include <service/fwu/agent/install_type.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Interface dependencies
+ */
+struct image_info;
+struct fw_directory;
+
+/**
+ * \brief Image installer interface
+ *
+ * The installer_interface structure provides a common interface for
+ * any image installer. Concrete installers will implement installation
+ * strategies that match the class of image being installed (e.g. component,
+ * whole firmware).
+ */
+struct installer_interface {
+ /**
+ * \brief Begin a transaction of one or more image install operations
+ *
+ * \param[in] context The concrete installer context
+ * \param[in] current_volume_id Where the active fw was loaded from
+ * \param[out] update_volume_id Where the update should be installed
+ *
+ * \return FWU status
+ */
+ int (*begin)(void *context,
+ uint32_t current_volume_id,
+ uint32_t update_volume_id);
+
+ /**
+ * \brief Finalize a transaction of one or more image install operations
+ *
+ * \param[in] context The concrete installer context
+ *
+ * \return FWU status
+ */
+ int (*finalize)(void *context);
+
+ /**
+ * \brief Abort a transaction
+ *
+ * \param[in] context The concrete installer context
+ */
+ void (*abort)(void *context);
+
+ /**
+ * \brief Open a stream for writing installation data
+ *
+ * \param[in] context The concrete installer context
+ * \param[in] image_info Describes the image to install
+ *
+ * \return FWU status
+ */
+ int (*open)(void *context,
+ const struct image_info *image_info);
+
+ /**
+ * \brief Commit installed data (called once per open)
+ *
+ * \param[in] context The concrete installer context
+ *
+ * \return FWU status
+ */
+ int (*commit)(void *context);
+
+ /**
+ * \brief Write installation data to an opened installer
+ *
+ * \param[in] context The concrete installer context
+ * \param[in] data Data to write
+ * \param[in] data_len Length of data
+ *
+ * \return FWU status
+ */
+ int (*write)(void *context,
+ const uint8_t *data,
+ size_t data_len);
+
+ /**
+ * \brief Enumerate the collection of images that can be handled by the installer
+ *
+ * A concrete installer will use its specialized knowledge of the associated
+ * storage volume to enumerate the set of images that can be handled by the
+ * installer.
+ *
+ * \param[in] context The concrete installer context
+ * \param[in] volume_id Identifies the target volume
+ * \param[in] fw_directory Add results to this fw_directory
+ *
+ * \return FWU status
+ */
+ int (*enumerate)(void *context,
+ uint32_t volume_id,
+ struct fw_directory *fw_directory);
+};
+
+/**
+ * \brief Base installer structure
+ *
+ */
+struct installer {
+
+ /* The installation type handled by the installer */
+ enum install_type install_type;
+
+ /* The location id is a short ID that identifies an updatable logical
+ * portion of the firmware store. The location id only needs to be
+ * unique within the device and will have been assigned dynamically
+ * during FWU configuration. The location id is used to bind a set of
+ * installers to the intended set of target volumes.
+ */
+ uint32_t location_id;
+
+ /* The location UUID is a globally unique ID that reflects the same
+ * logical scope as a location id. It is used to associate incoming
+ * update images, either directly or indirectly, to the corresponding
+ * location within the firmware store. For images contained within a
+ * disk partition, this will correspond to the partition type UUID.
+ */
+ struct uuid_octets location_uuid;
+
+ /* The opaque context for the concrete installer */
+ void *context;
+
+ /* Pointer to a concrete installer_interface */
+ const struct installer_interface *interface;
+
+ /* Error status encountered during an update transaction. During an update,
+ * an installer will be called multiple times. If one or more errors are encountered
+ * during the update transaction, the first error status is saved to allow for
+ * appropriate handling.
+ */
+ uint32_t install_status;
+
+ /**
+ * During a multi-image update, images may be delegated to different concrete
+ * installers to allow for alternative installation strategies and install destinations.
+ * Each active installer must be sequenced through a begin->finalize transaction to
+ * ensure that installation operations are completed for all images handled by an
+ * installer and by all installers used during the update.
+ */
+ bool is_active;
+ struct installer *next;
+};
+
+static inline bool installer_is_active(const struct installer *installer)
+{
+ return installer->is_active;
+}
+
+static inline uint32_t installer_status(const struct installer *installer)
+{
+ return installer->install_status;
+}
+
+void installer_init(
+ struct installer *installer,
+ enum install_type install_type,
+ uint32_t location_id,
+ const struct uuid_octets *location_uuid,
+ void *context,
+ const struct installer_interface *interface);
+
+int installer_begin(
+ struct installer *installer,
+ uint32_t current_volume_id,
+ uint32_t update_volume_id);
+
+int installer_finalize(
+ struct installer *installer);
+
+void installer_abort(
+ struct installer *installer);
+
+int installer_open(
+ struct installer *installer,
+ const struct image_info *image_info);
+
+int installer_commit(
+ struct installer *installer);
+
+int installer_write(
+ struct installer *installer,
+ const uint8_t *data,
+ size_t data_len);
+
+int installer_enumerate(
+ struct installer *installer,
+ uint32_t volume_id,
+ struct fw_directory *fw_directory);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FWU_INSTALLATION_INSTALLER_H */
diff --git a/components/service/fwu/installer/installer_index.c b/components/service/fwu/installer/installer_index.c
new file mode 100644
index 0000000..b2b24ef
--- /dev/null
+++ b/components/service/fwu/installer/installer_index.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2022-2023, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <stdint.h>
+#include <trace.h>
+#include "installer.h"
+#include "installer_index.h"
+
+#ifndef INSTALLER_INDEX_LIMIT
+#define INSTALLER_INDEX_LIMIT (8)
+#endif
+
+#ifndef INSTALLER_INDEX_LOCATION_ID_LIMIT
+#define INSTALLER_INDEX_LOCATION_ID_LIMIT (8)
+#endif
+
+/**
+ * Singleton index of installers to use for different classes of image.
+ */
+static struct {
+
+ /* An index for registered installers to handle update installation
+ * for the platform. The set of registered installers will have been
+ * selected for compatibility with the class of update image handled
+ * by the installer.
+ */
+ size_t num_registered;
+ struct installer *installers[INSTALLER_INDEX_LIMIT];
+
+ /* Each installer is associated with a location via the assigned
+ * location_id. This is used as a logical identifier for the part
+ * of the device firmware that the installer handles. Multiple
+ * installers may be associated with the same location.
+ */
+ size_t num_location_ids;
+ uint32_t location_ids[INSTALLER_INDEX_LOCATION_ID_LIMIT];
+
+} installer_index;
+
+static void add_location_id(uint32_t location_id)
+{
+ /* Check if location_id already added. Because 1..* installers
+ * may be associated with the same location_id, we can ignore
+ * location_ids that have already been added.
+ */
+ for (size_t i = 0; i < installer_index.num_location_ids; i++) {
+
+ if (location_id == installer_index.location_ids[i])
+ return;
+ }
+
+ /* It's a new location_id so add it */
+ if (installer_index.num_location_ids < INSTALLER_INDEX_LOCATION_ID_LIMIT) {
+
+ installer_index.location_ids[installer_index.num_location_ids] = location_id;
+ ++installer_index.num_location_ids;
+ } else {
+ EMSG("Too many FWU locations");
+ }
+}
+
+void installer_index_init(void)
+{
+ installer_index_clear();
+}
+
+void installer_index_clear(void)
+{
+ memset(&installer_index, 0, sizeof(installer_index));
+}
+
+void installer_index_register(
+ struct installer *installer)
+{
+ assert(installer);
+
+ if (installer_index.num_registered < INSTALLER_INDEX_LIMIT) {
+
+ installer_index.installers[installer_index.num_registered] = installer;
+ ++installer_index.num_registered;
+
+
+ add_location_id(installer->location_id);
+ } else {
+ EMSG("FWU configuration exceeds installer limit");
+ }
+}
+
+struct installer *installer_index_find(
+ enum install_type install_type,
+ uint32_t location_id)
+{
+ struct installer *result = NULL;
+
+ for (size_t i = 0; i < installer_index.num_registered; i++) {
+
+ struct installer *installer = installer_index.installers[i];
+
+ if ((installer->install_type == install_type) &&
+ (installer->location_id == location_id)) {
+
+ result = installer;
+ break;
+ }
+ }
+
+ return result;
+}
+
+struct installer *installer_index_find_by_location_uuid(
+ const struct uuid_octets *location_uuid)
+{
+ struct installer *result = NULL;
+
+ for (size_t i = 0; i < installer_index.num_registered; i++) {
+
+ struct installer *installer = installer_index.installers[i];
+
+ if (uuid_is_equal(location_uuid->octets,
+ installer->location_uuid.octets)) {
+
+ result = installer;
+ break;
+ }
+ }
+
+ return result;
+}
+
+struct installer *installer_index_get(
+ unsigned int index)
+{
+ struct installer *result = NULL;
+
+ if (index < installer_index.num_registered)
+ result = installer_index.installers[index];
+
+ return result;
+}
+
+const uint32_t *installer_index_get_location_ids(size_t *num_ids)
+{
+ *num_ids = installer_index.num_location_ids;
+ return installer_index.location_ids;
+}
diff --git a/components/service/fwu/installer/installer_index.h b/components/service/fwu/installer/installer_index.h
new file mode 100644
index 0000000..1626257
--- /dev/null
+++ b/components/service/fwu/installer/installer_index.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2022-2023, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef INSTALLER_INDEX_H
+#define INSTALLER_INDEX_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <service/fwu/agent/install_type.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Interface dependencies
+ */
+struct installer;
+
+/**
+ * @brief Initialize the installer index
+ *
+ * The installer_index is a singleton that holds the set of installers that are suitable
+ * for the deployment. The mix of installers will depend on factors such as the image
+ * container used by firmware or whether images are encrypted etc.
+ */
+void installer_index_init(void);
+
+/**
+ * @brief Clears the installer index
+ *
+ * Clears all mappings.
+ */
+void installer_index_clear(void);
+
+/**
+ * @brief Register an installer
+ *
+ * Registers an installer to handle image installation for a particular install_type
+ * and location.
+ *
+ * @param[in] installer The installer to use
+ */
+void installer_index_register(
+ struct installer *installer);
+
+/**
+ * @brief Find an installer for the specified type and location
+ *
+ * @param[in] install_type The type of installer
+ * @param[in] location_id The location it serves
+ *
+ * @return Pointer to a concrete installer or NULL if none found
+ */
+struct installer *installer_index_find(
+ enum install_type install_type,
+ uint32_t location_id);
+
+/**
+ * @brief Find a registered installer associated with the specified location UUID
+ *
+ * @param[in] location_uuid The associated location UUID
+ *
+ * @return Pointer to a concrete installer or NULL if none found
+ */
+struct installer *installer_index_find_by_location_uuid(
+ const struct uuid_octets *location_uuid);
+
+/**
+ * @brief Iterator function
+ *
+ * @param[in] index 0..n
+ *
+ * @return Pointer to a concrete installer or NULL if iterated beyond final entry
+ */
+struct installer *installer_index_get(unsigned int index);
+
+/**
+ * @brief Get an array of location_ids
+ *
+ * Updatable firmware may be distributed over multiple locations. Each location is
+ * assigned a location_id when the set of installers are registered for the platform.
+ * This function returns a variable length array of locations_ids for the platform.
+ * This is useful for checking if an incoming update has included components that
+ * update all locations or not.
+ *
+ * @param[out] num_ids 0..n
+ *
+ * @return Pointer to a static array of location ids
+ */
+const uint32_t *installer_index_get_location_ids(size_t *num_ids);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* INSTALLER_INDEX_H */
diff --git a/deployments/component-test/component-test.cmake b/deployments/component-test/component-test.cmake
index c1e40fb..4fd0009 100644
--- a/deployments/component-test/component-test.cmake
+++ b/deployments/component-test/component-test.cmake
@@ -109,6 +109,7 @@
"components/service/block_storage/factory/ref_ram_gpt"
"components/service/block_storage/factory/client"
"components/service/fwu/agent"
+ "components/service/fwu/installer"
"components/service/crypto/client/cpp"
"components/service/crypto/client/cpp/protocol/protobuf"
"components/service/crypto/client/cpp/protocol/packed-c"