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;
+}