Add fwu_app for test and demonstration
Adds the fwu_app class to facilitate deployment of the update agent
as part of an application e.g. a user-space or UEFI app. It uses
the standard FWU service components but operates on a disk image
file rather than on a flash device. The disk image file needs to
be a UEFI formated image with MBR+GPT with partitions for metadata
and firmware banks. Will be initially used to test operation with
boot images generated from build tools.
Signed-off-by: Julian Hall <julian.hall@arm.com>
Change-Id: Ia4151841115010333a922c217352614062f8f86a
diff --git a/components/service/fwu/app/fwu_app.cpp b/components/service/fwu/app/fwu_app.cpp
new file mode 100644
index 0000000..70059d0
--- /dev/null
+++ b/components/service/fwu/app/fwu_app.cpp
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <cassert>
+#include <cstddef>
+#include <cstring>
+#include <errno.h>
+#include <media/volume/factory/volume_factory.h>
+#include <service/block_storage/factory/file/block_store_factory.h>
+#include <service/fwu/installer/factory/installer_factory.h>
+#include <service/fwu/fw_store/banked/bank_scheme.h>
+#include <service/fwu/fw_store/banked/metadata_serializer/v1/metadata_serializer_v1.h>
+#include <service/fwu/fw_store/banked/metadata_serializer/v2/metadata_serializer_v2.h>
+#include <service/fwu/inspector/direct/direct_fw_inspector.h>
+#include <service/fwu/agent/update_agent.h>
+#include <service/fwu/fw_store/banked/banked_fw_store.h>
+#include <service/fwu/config/fwu_configure.h>
+#include "metadata_reader.h"
+#include "fwu_app.h"
+
+extern "C" {
+#include <trace.h>
+}
+
+fwu_app::fwu_app() :
+ m_update_agent(),
+ m_fw_store()
+{
+ memset(&m_update_agent, 0, sizeof(m_update_agent));
+ memset(&m_fw_store, 0, sizeof(m_fw_store));
+}
+
+fwu_app::~fwu_app()
+{
+ update_agent_deinit(&m_update_agent);
+ banked_fw_store_deinit(&m_fw_store);
+
+ fwu_deconfigure();
+ volume_factory_deinit();
+}
+
+int fwu_app::configure(
+ const char *disk_img_filename)
+{
+ if (disk_img_filename)
+ file_block_store_factory_set_filename(disk_img_filename);
+
+ struct uuid_octets device_uuids[MAX_STORAGE_DEVICES];
+ size_t num_storage_devices = 0;
+
+ int status = volume_factory_init(device_uuids,
+ MAX_STORAGE_DEVICES, &num_storage_devices);
+
+ if (status) {
+
+ EMSG("Failed to init volume factory: %d", status);
+ return -EIO;
+ }
+
+ status = fwu_configure(device_uuids, num_storage_devices);
+
+ if (status) {
+
+ EMSG("Failed to setup FWU configuration: %d", status);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+int fwu_app::get_boot_info(
+ unsigned int &active_index,
+ unsigned int &metadata_version)
+{
+ return metadata_reader::instance()->get_boot_info(active_index, metadata_version);
+}
+
+int fwu_app::init_update_agent(
+ unsigned int boot_index,
+ unsigned int metadata_version)
+{
+ if (boot_index >= BANK_SCHEME_NUM_BANKS) {
+
+ IMSG("Invalid boot index");
+ return -1;
+ }
+
+ const struct metadata_serializer *serializer =
+ select_metadata_serializer(metadata_version);
+
+ if (!serializer) {
+
+ IMSG("Unsupported FWU metadata version");
+ return -1;
+ }
+
+ /* Initialise update_agent */
+ int status = banked_fw_store_init(&m_fw_store, serializer);
+
+ if (status) {
+
+ IMSG("fw store initialisation error %d", status);
+ return -1;
+ }
+
+ status = update_agent_init(&m_update_agent, boot_index,
+ direct_fw_inspector_inspect, &m_fw_store);
+
+ if (status) {
+
+ IMSG("update agent initialisation error %d", status);
+ return -1;
+ }
+
+ /* Success */
+ return 0;
+}
+
+int fwu_app::update_image(
+ const struct uuid_octets &img_type_uuid,
+ const uint8_t *img_data,
+ size_t img_size)
+{
+ int status = update_agent_begin_staging(&m_update_agent);
+
+ if (status)
+ return status;
+
+ uint32_t stream_handle = 0;
+
+ status = update_agent_open(&m_update_agent,
+ &img_type_uuid, &stream_handle);
+
+ if (!status) {
+
+ status = update_agent_write_stream(&m_update_agent,
+ stream_handle, img_data, img_size);
+
+ if (!status)
+ status = update_agent_commit(&m_update_agent,
+ stream_handle, false);
+ }
+
+ if (!status)
+ status = update_agent_end_staging(&m_update_agent);
+ else
+ update_agent_cancel_staging(&m_update_agent);
+
+ return status;
+}
+
+int fwu_app::read_object(
+ const struct uuid_octets &object_uuid,
+ std::vector<uint8_t> &data)
+{
+ uint32_t stream_handle = 0;
+ int status = update_agent_open(&m_update_agent, &object_uuid, &stream_handle);
+
+ if (status)
+ return status;
+
+ /* Don't yet know how big the object will be so allocate some space to get
+ * started with the initial read.
+ */
+ size_t reported_total_len = 0;
+ size_t read_so_far = 0;
+ size_t vector_capacity = 512;
+
+ data.resize(vector_capacity);
+
+ do {
+
+ size_t data_len_read = 0;
+ size_t requested_read_len = vector_capacity - read_so_far;
+
+ status = update_agent_read_stream(
+ &m_update_agent,
+ stream_handle,
+ &data[read_so_far],
+ requested_read_len,
+ &data_len_read,
+ &reported_total_len);
+
+ read_so_far += data_len_read;
+ data.resize(read_so_far);
+
+ if (reported_total_len > vector_capacity) {
+
+ vector_capacity = reported_total_len;
+ data.resize(vector_capacity);
+ }
+
+ assert(read_so_far <= reported_total_len);
+
+ if (read_so_far == reported_total_len) {
+
+ /* Read all the data */
+ if (vector_capacity > reported_total_len)
+ data.resize(reported_total_len);
+
+ break;
+ }
+
+ } while (!status);
+
+ status = update_agent_commit(&m_update_agent, stream_handle, false);
+
+ return status;
+}
+
+struct update_agent *fwu_app::update_agent()
+{
+ return &m_update_agent;
+}
+
+const struct metadata_serializer *fwu_app::select_metadata_serializer(
+ unsigned int version)
+{
+ if (version == 1)
+ return metadata_serializer_v1();
+
+ if (version == 2)
+ return metadata_serializer_v2();
+
+ return NULL;
+}