Add fwu configurator component
An FWU service deployment can be configured in different ways to
meet platform requirements. This commit adds a standard interface
for creating alternative configuartions with an underlying directory
structure that allows additional configurators to be added. A GPT
based configurator is included that creates the FWU configuartion
using information read from partition entries read from the GPT.
This is expected to be a common configuration method.
Signed-off-by: Julian Hall <julian.hall@arm.com>
Change-Id: Idbe8daf198bb36b8b1de340df838cc82ec450f75
diff --git a/components/media/disk/guid.h b/components/media/disk/guid.h
index cf7f836..6968708 100644
--- a/components/media/disk/guid.h
+++ b/components/media/disk/guid.h
@@ -36,4 +36,10 @@
#define DISK_GUID_UNIQUE_PARTITION_DISK_HEADER \
"5cb130b7-a138-4d08-b0be-c2d4eff57870"
+#define DISK_GUID_UNIQUE_PARTITION_BOOT_BANK_A \
+ "27365ff7-90fe-410b-9fb8-4595fdc27867"
+
+#define DISK_GUID_UNIQUE_PARTITION_BOOT_BANK_B \
+ "3a87713e-4b0b-4361-b6d4-019f0ccfe41a"
+
#endif /* MEDIA_DISK_GUID_H */
diff --git a/components/service/fwu/config/component.cmake b/components/service/fwu/config/component.cmake
new file mode 100644
index 0000000..c653a41
--- /dev/null
+++ b/components/service/fwu/config/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}/fwu_configure.c"
+ )
diff --git a/components/service/fwu/config/fwu_configure.c b/components/service/fwu/config/fwu_configure.c
new file mode 100644
index 0000000..0a30518
--- /dev/null
+++ b/components/service/fwu/config/fwu_configure.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <media/volume/index/volume_index.h>
+#include <media/volume/factory/volume_factory.h>
+#include <service/fwu/installer/installer_index.h>
+#include <service/fwu/installer/factory/installer_factory.h>
+#include <service/fwu/config/gpt/gpt_fwu_configure.h>
+#include "fwu_configure.h"
+
+int fwu_configure(const struct uuid_octets *device_uuids, size_t num_device_uuids)
+{
+ unsigned int location_id = 0;
+
+ volume_index_init();
+ installer_index_init();
+
+ for (size_t i = 0; i < num_device_uuids; i++) {
+
+ unsigned int new_location_count = 0;
+
+ int status = gpt_fwu_configure(&device_uuids[i],
+ location_id, &new_location_count);
+
+ if (status)
+ return status;
+
+ location_id += new_location_count;
+ }
+
+ return 0;
+}
+
+void fwu_deconfigure(void)
+{
+ unsigned int index = 0;
+
+ /* Destroy installers */
+ while (1) {
+ struct installer *installer = installer_index_get(index);
+
+ if (installer)
+ installer_factory_destroy_installer(installer);
+ else
+ break;
+
+ ++index;
+ }
+
+ /* Destroy volumes */
+ index = 0;
+
+ while (1) {
+ struct volume *volume = volume_index_get(index);
+
+ if (volume)
+ volume_factory_destroy_volume(volume);
+ else
+ break;
+
+ ++index;
+ }
+
+ installer_index_clear();
+ volume_index_clear();
+}
diff --git a/components/service/fwu/config/fwu_configure.h b/components/service/fwu/config/fwu_configure.h
new file mode 100644
index 0000000..8aa98fe
--- /dev/null
+++ b/components/service/fwu/config/fwu_configure.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef FWU_CONFIGURE_H
+#define FWU_CONFIGURE_H
+
+#include <common/uuid/uuid.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Configure installers and volumes for FWU
+ *
+ * Creates an FWU configuration consisting of a set of installers and volumes
+ * that provide the capabilities needed to update images residing on the set
+ * of storage devices identified by the input set of device UUIDs. Created
+ * installers are added to the singleton installer_index and created volumes
+ * are added to the volume_index. Related installers and volumes are grouped
+ * by assigning location IDs.
+ *
+ * \param[in] device_uuids Array of device UUIDs
+ * \param[in] num_device_uuids Number of UUIDs in the array
+ *
+ * \return Configuration status (0 for success)
+ */
+int fwu_configure(const struct uuid_octets *device_uuids, size_t num_device_uuids);
+
+/**
+ * \brief De-configure the FWU configuration
+ *
+ * Deregisters and destroys all installers and volumes.
+ */
+void fwu_deconfigure(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FWU_CONFIGURE_H */
diff --git a/components/service/fwu/config/gpt/component.cmake b/components/service/fwu/config/gpt/component.cmake
new file mode 100644
index 0000000..4950aa2
--- /dev/null
+++ b/components/service/fwu/config/gpt/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}/gpt_fwu_configure.c"
+ )
diff --git a/components/service/fwu/config/gpt/gpt_fwu_configure.c b/components/service/fwu/config/gpt/gpt_fwu_configure.c
new file mode 100644
index 0000000..6f065ef
--- /dev/null
+++ b/components/service/fwu/config/gpt/gpt_fwu_configure.c
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <errno.h>
+#include <trace.h>
+#include <stdint.h>
+#include <media/disk/guid.h>
+#include <media/disk/gpt_iterator/gpt_iterator.h>
+#include <media/volume/factory/volume_factory.h>
+#include <media/volume/index/volume_index.h>
+#include <service/fwu/fw_store/banked/volume_id.h>
+#include <service/fwu/installer/factory/installer_factory.h>
+#include <service/fwu/installer/installer_index.h>
+#include <service/fwu/installer/installer.h>
+#include "gpt_fwu_configure.h"
+
+static struct installer *create_and_register_installers(
+ const struct uuid_octets *partition_type_uuid,
+ unsigned int location_id);
+
+static int create_and_register_metadata_volume(
+ const struct uuid_octets *device_uuid,
+ gpt_entry_t *entry,
+ unsigned *metadata_count);
+
+
+int gpt_fwu_configure(const struct uuid_octets *device_uuid,
+ unsigned int initial_location_id,
+ unsigned int *location_count)
+{
+ struct uuid_octets gpt_guid;
+ unsigned int next_location_id = initial_location_id;
+ unsigned int metadata_partition_count = 0;
+
+ *location_count = 0;
+
+ uuid_guid_octets_from_canonical(&gpt_guid, DISK_GUID_UNIQUE_PARTITION_DISK_HEADER);
+ struct volume *gpt_volume = volume_factory_create_volume( &gpt_guid, device_uuid);
+
+ if (!gpt_volume) {
+ /* The case where there is no GPT volume is not necessarily an error.
+ * It could just mean that an alternative configuration method is required
+ * for this storage device.
+ */
+ IMSG("No disk header volume");
+ return 0;
+ }
+
+ /* Attempt to initialise a gpt_iterator. This requires a well-formed GPT to
+ * be present. Again, if there's not one, this is shouldn't be treated as
+ * an error as an alternative configuration method may be used,
+ */
+ struct gpt_iterator gpt_iter;
+ int status = gpt_iterator_init(&gpt_iter, gpt_volume);
+
+ if (status) {
+ IMSG("No GPT found");
+ status = 0;
+ goto abnormal_exit;
+ }
+
+ /* Iterate over partition table and extend configuration for updatable partitions */
+ gpt_iterator_first(&gpt_iter);
+
+ while (!gpt_iterator_is_done(&gpt_iter)) {
+
+ gpt_entry_t entry;
+
+ status = gpt_iterator_current(&gpt_iter, &entry);
+
+ if (status) {
+ EMSG("Failed to read GPT entry: %d", status);
+ goto abnormal_exit;
+ }
+
+ const struct uuid_octets *partition_type_guid =
+ (const struct uuid_octets *)&entry.type_uuid;
+
+ /* Skip unused entry */
+ if (uuid_is_nil(partition_type_guid->octets)) {
+
+ gpt_iterator_next(&gpt_iter);
+ continue;
+ }
+
+ /* Determine if any installers are available to handle some form of
+ * image installation for the location reflected by the partition type
+ * GUID. If a partition of the same type has already been encountered
+ * and the installation is supported, there will already be one or more
+ * registered installers.
+ */
+ struct installer *installer =
+ installer_index_find_by_location_uuid(partition_type_guid);
+
+ if (!installer) {
+
+ /* No installer for the partition type has yet been registered. This
+ * is either because this is the first partition of this type to be
+ * encountered or because the partition is not updatable. Find out by
+ * attempting to construct one or more installers.
+ */
+ installer =
+ create_and_register_installers(partition_type_guid, next_location_id);
+
+ if (installer)
+ ++next_location_id;
+
+ }
+
+ if (installer) {
+
+ /* This configurator relies on the partition name to identify the bank
+ * index that the partition corresponds to. This convention also needs
+ * to be used by the bootloader to ensure that there is a consistent
+ * view of bank index. The name should include the bank index as the
+ * first character e.g. 0:AP-FW.
+ */
+ unsigned int bank_index;
+
+ if (entry.name[0] == '0')
+ bank_index = 0;
+ else if (entry.name[0] == '1')
+ bank_index = 1;
+ else {
+
+ EMSG("Invalid bank index in partition name");
+ goto abnormal_exit;
+ }
+
+ /* This partition is updatable so construct and register a volume
+ * to provide access to storage.
+ */
+ struct volume *volume = volume_factory_create_volume(
+ (const struct uuid_octets *)&entry.unique_uuid, device_uuid);
+
+ if (!volume) {
+
+ EMSG("Failed to create volume");
+ goto abnormal_exit;
+ }
+
+ status = volume_index_add(
+ banked_volume_id(installer->location_id,
+ banked_usage_id(bank_index)), volume);
+
+ if (status) {
+
+ volume_factory_destroy_volume(volume);
+ EMSG("Failed to register volume");
+ goto abnormal_exit;
+ }
+
+ } else {
+
+ /* Not an updatable partition but it might be for FWU metadata */
+ status = create_and_register_metadata_volume(device_uuid,
+ &entry, &metadata_partition_count);
+
+ if (status) {
+
+ EMSG("Failed to create metadata volume");
+ goto abnormal_exit;
+ }
+ }
+
+ gpt_iterator_next(&gpt_iter);
+ }
+
+abnormal_exit:
+ gpt_iterator_deinit(&gpt_iter);
+ volume_factory_destroy_volume(gpt_volume);
+
+ /* Count of the number of new locations added */
+ *location_count = next_location_id - initial_location_id;
+
+ return status;
+}
+
+static struct installer *create_and_register_installers(
+ const struct uuid_octets *partition_type_uuid,
+ unsigned int location_id)
+{
+ /* Attempt to create a complete set of installers for updating images
+ * contained within a partition of the specified type. The installer_factory
+ * holds the policy over which partitions are updatable and with what type
+ * of installer. If at least one installer ends up being constructed,
+ * a pointer to any of the constructed installers is returned.
+ */
+ struct installer *last_constructed = NULL;
+ struct installer *installer = NULL;
+
+ installer = installer_factory_create_installer(
+ INSTALL_TYPE_WHOLE_VOLUME, location_id, partition_type_uuid);
+
+ if (installer) {
+
+ installer_index_register(installer);
+ last_constructed = installer;
+ }
+
+ installer = installer_factory_create_installer(
+ INSTALL_TYPE_SUB_VOLUME, location_id, partition_type_uuid);
+
+ if (installer) {
+
+ installer_index_register(installer);
+ last_constructed = installer;
+ }
+
+ installer = installer_factory_create_installer(
+ INSTALL_TYPE_WHOLE_VOLUME_COPY, location_id, partition_type_uuid);
+
+ if (installer) {
+
+ installer_index_register(installer);
+ last_constructed = installer;
+ }
+
+ return last_constructed;
+}
+
+static int create_and_register_metadata_volume(
+ const struct uuid_octets *device_uuid,
+ gpt_entry_t *entry,
+ unsigned *metadata_count)
+{
+ struct uuid_octets guid;
+
+ uuid_guid_octets_from_canonical(&guid, DISK_GUID_PARTITION_TYPE_FWU_METADATA);
+
+ if (!uuid_is_equal(guid.octets, (const uint8_t *)&entry->type_uuid))
+ return 0;
+
+ uint32_t volume_id;
+
+ if (*metadata_count == 0)
+ volume_id = BANKED_VOLUME_ID_PRIMARY_METADATA;
+ else if (*metadata_count == 1)
+ volume_id = BANKED_VOLUME_ID_BACKUP_METADATA;
+ else
+ /* Ignore any additional metadata partitions */
+ return 0;
+
+ struct volume *volume = volume_factory_create_volume(
+ (const struct uuid_octets *)&entry->unique_uuid, device_uuid);
+
+ if (!volume)
+ return -EIO;
+
+ if (volume_index_add(volume_id, volume)) {
+
+ volume_factory_destroy_volume(volume);
+ return -EIO;
+ }
+
+ *metadata_count += 1;
+
+ return 0;
+}
\ No newline at end of file
diff --git a/components/service/fwu/config/gpt/gpt_fwu_configure.h b/components/service/fwu/config/gpt/gpt_fwu_configure.h
new file mode 100644
index 0000000..a9d591b
--- /dev/null
+++ b/components/service/fwu/config/gpt/gpt_fwu_configure.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef GPT_FWU_CONFIGURE_H
+#define GPT_FWU_CONFIGURE_H
+
+#include <common/uuid/uuid.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief GPT based FWU configure method
+ *
+ * An FWU configuration comprises a set of installers and volume objects that
+ * enables all updatable firmware images to be updated by the update agent. This
+ * configure method uses partition information contained within a UEFI formatted
+ * storage device to extend the FWU configuration to cover all updatable partitions
+ * on the device. The configurator iterates over the GPT and progressively adds
+ * the necessary installers and volumes to the configuration. To allow for multiple
+ * configuration steps (e.g. for multiple devices), the initial location ID is
+ * passed as a parameter and a count of the number of locations added is returned.
+ *
+ * \param[in] device_uuid Identifies the target device
+ * \param[in] initial_location_id Initial location ID
+ * \param[out] location_count The number of locations added
+ *
+ * \return Status code (0 for success)
+ */
+int gpt_fwu_configure(const struct uuid_octets *device_uuid,
+ unsigned int initial_location_id,
+ unsigned int *location_count);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GPT_FWU_CONFIGURE_H */