Restructure fwu-app deployment

Change fwu-app deployment to be more consistent to existing
deployments:
  - change fwu-app to be a dedicated deployment instead of a
    configuration of an SP.  Change the name to "fwu-tool".
	Change the name of the executable to the same.
  - delete posix environment and:
      - move posix trace implementation to linux-pc environment
	  - move deployment specific files to the deployment directory
  - move fwu-app common files from components/service/fwu/app to
    the new deployment, under src/app

Change-Id: Icf68d39bda34092807f33256f540389cd82d0a46
Signed-off-by: Gyorgy Szing <Gyorgy.Szing@arm.com>
diff --git a/components/app/fwu-tool/app/fwu_app.cpp b/components/app/fwu-tool/app/fwu_app.cpp
new file mode 100644
index 0000000..173afce
--- /dev/null
+++ b/components/app/fwu-tool/app/fwu_app.cpp
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "fwu_app.h"
+
+#include <cassert>
+#include <cstddef>
+#include <cstring>
+#include <errno.h>
+
+#include "media/volume/factory/volume_factory.h"
+#include "metadata_reader.h"
+#include "service/block_storage/factory/file/block_store_factory.h"
+#include "service/fwu/agent/update_agent.h"
+#include "service/fwu/config/fwu_configure.h"
+#include "service/fwu/fw_store/banked/bank_scheme.h"
+#include "service/fwu/fw_store/banked/banked_fw_store.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/installer/factory/installer_factory.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;
+}
diff --git a/components/app/fwu-tool/app/fwu_app.h b/components/app/fwu-tool/app/fwu_app.h
new file mode 100644
index 0000000..8bbaa9d
--- /dev/null
+++ b/components/app/fwu-tool/app/fwu_app.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef FWU_APP_H
+#define FWU_APP_H
+
+#include <stdint.h>
+#include <vector>
+
+#include "common/uuid/uuid.h"
+#include "service/fwu/agent/update_agent.h"
+#include "service/fwu/fw_store/banked/banked_fw_store.h"
+
+/*
+ * The fwu_app class is intended to provide the core for an application
+ * that uses the update_agent for updating the contents of a disk image
+ * file. The app uses standard fwu components, such as the update_agent,
+ * to manage updates. To support additional update methods (e.g.
+ * update_capsule), inherit from this class and add additional methods
+ * to the derived class. This allows the core app to be reused for
+ * different application areas without overloading the base app.
+ */
+class fwu_app {
+public:
+	fwu_app();
+	virtual ~fwu_app();
+
+	/**
+	 * \brief Configure storage and installers
+	 *
+	 * Configures a set of storage volumes and installers to manage the
+	 * contents of the specified disk image file.
+	 *
+	 * \param[in]  disk_img_filename   UEFI formatted disk image
+	 *
+	 * \return Status (0 on success)
+	 */
+	int configure(const char *disk_img_filename);
+
+	/**
+	 * \brief Get boot info from the FWU metadata
+	 *
+	 * \param[out]  active_index   The active index seen by the bootloader
+	 * \param[out]  metadata_version Current metadata version
+	 *
+	 * \return Status (0 on success)
+	 */
+	int get_boot_info(unsigned int &active_index, unsigned int &metadata_version);
+
+	/**
+	 * \brief Initialise the update agent
+	 *
+	 * \param[in]  boot_index   The boot_index chosen by the bootloader
+	 * \param[in]  metadata_version Current metadata version
+	 *
+	 * \return Status (0 on success)
+	 */
+	int init_update_agent(unsigned int boot_index, unsigned int metadata_version);
+
+	/**
+	 * \brief Update a single image
+	 *
+	 * Begins staging, writes the raw contents of the image file and ends
+	 * staging.
+	 *
+	 * \param[in]  img_type_uuid   UUID of image to update
+	 * \param[in]  img_data        Buffer containing image data
+	 * \param[in]  img_size        Size in bytes of image
+	 *
+	 * \return Status (0 on success)
+	 */
+	int update_image(const struct uuid_octets &img_type_uuid, const uint8_t *img_data,
+			 size_t img_size);
+
+	/**
+	 * \brief Read an object from the update agent
+	 *
+	 * \param[in]  object_uuid    UUID of object
+	 * \param[out] data           Read object data
+	 *
+	 * \return Status (0 on success)
+	 */
+	int read_object(const struct uuid_octets &object_uuid, std::vector<uint8_t> &data);
+
+protected:
+	/**
+	 * \brief Return pointer to update_agent struct
+	 *
+	 * \return Pointer to the core update_agent.
+	 */
+	struct update_agent *update_agent();
+
+private:
+	static const size_t MAX_STORAGE_DEVICES = 4;
+
+	static const struct metadata_serializer *select_metadata_serializer(unsigned int version);
+
+	struct update_agent m_update_agent;
+	struct fw_store m_fw_store;
+};
+
+#endif /* FWU_APP_H */
diff --git a/components/app/fwu-tool/app/metadata_reader.cpp b/components/app/fwu-tool/app/metadata_reader.cpp
new file mode 100644
index 0000000..2d38a2f
--- /dev/null
+++ b/components/app/fwu-tool/app/metadata_reader.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "metadata_reader.h"
+
+#include "media/volume/index/volume_index.h"
+#include "media/volume/volume.h"
+#include "service/fwu/fw_store/banked/volume_id.h"
+
+extern "C" {
+#include "trace.h"
+}
+
+metadata_reader::metadata_reader()
+	: registered_readers()
+{
+}
+
+metadata_reader::~metadata_reader()
+{
+}
+
+metadata_reader *metadata_reader::instance()
+{
+	static metadata_reader the_instance;
+	return &the_instance;
+}
+
+void metadata_reader::register_reader(metadata_version_specific_reader *reader)
+{
+	registered_readers.push_back(reader);
+}
+
+int metadata_reader::get_boot_info(unsigned int &active_index, unsigned int &metadata_version) const
+{
+	struct volume *volume;
+
+	int status = volume_index_find(BANKED_VOLUME_ID_PRIMARY_METADATA, &volume);
+
+	if (status) {
+		IMSG("Failed to find metadata volume");
+		return status;
+	}
+
+	status = volume_open(volume);
+
+	if (!status) {
+		/* Assume whatever metadata version is in-use, it will fit in the buffer */
+		size_t len_read = 0;
+		uint8_t buf[1000];
+
+		status = volume_read(volume, (uintptr_t)buf, sizeof(buf), &len_read);
+
+		if (!status) {
+			bool is_handled = false;
+
+			for (unsigned int i = 0; i < registered_readers.size(); i++) {
+				metadata_version_specific_reader *reader = registered_readers[i];
+
+				if (reader->is_supported(buf, len_read)) {
+					reader->get_version(buf, len_read, metadata_version);
+					reader->get_active_index(buf, len_read, active_index);
+
+					is_handled = true;
+					break;
+				}
+			}
+
+			if (!is_handled) {
+				/* This is normal on first-boot */
+				status = -1;
+			}
+
+		} else
+			IMSG("Failed to read metadata volume");
+
+		volume_close(volume);
+
+	} else
+		IMSG("Failed to open metadata volume");
+
+	return status;
+}
diff --git a/components/app/fwu-tool/app/metadata_reader.h b/components/app/fwu-tool/app/metadata_reader.h
new file mode 100644
index 0000000..19c3cf4
--- /dev/null
+++ b/components/app/fwu-tool/app/metadata_reader.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef FWU_METADATA_READER_H
+#define FWU_METADATA_READER_H
+
+#include <cstdint>
+#include <vector>
+
+/*
+ * A version specific metadata reader. Before using get methods to extract
+ * metadata attributes, is_supported() should be called to verify that the
+ * input metadata version is supported.
+ */
+class metadata_version_specific_reader {
+public:
+	virtual ~metadata_version_specific_reader()
+	{
+	}
+
+	virtual bool is_supported(const uint8_t *buf, size_t data_len) const = 0;
+
+	virtual void get_version(const uint8_t *buf, size_t data_len,
+				 unsigned int &version) const = 0;
+
+	virtual void get_active_index(const uint8_t *buf, size_t data_len,
+				      unsigned int &active_index) const = 0;
+};
+
+/*
+ * A singleton that provides a common interface for reading fwu metadata.
+ * The caller doesn't need to worry about the version of metadata being used.
+ */
+class metadata_reader {
+public:
+	static metadata_reader *instance();
+	~metadata_reader();
+
+	void register_reader(metadata_version_specific_reader *reader);
+
+	int get_boot_info(unsigned int &active_index, unsigned int &metadata_version) const;
+
+private:
+	metadata_reader();
+
+	std::vector<metadata_version_specific_reader *> registered_readers;
+};
+
+#endif /* FWU_METADATA_READER_H */
diff --git a/components/app/fwu-tool/app/metadata_v1_reader.cpp b/components/app/fwu-tool/app/metadata_v1_reader.cpp
new file mode 100644
index 0000000..195fa6e
--- /dev/null
+++ b/components/app/fwu-tool/app/metadata_v1_reader.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+
+#include "metadata_reader.h"
+#include "protocols/service/fwu/packed-c/metadata_v1.h"
+
+class metadata_v1_reader : public metadata_version_specific_reader {
+public:
+	metadata_v1_reader();
+	~metadata_v1_reader();
+
+	bool is_supported(const uint8_t *buf, size_t data_len) const override;
+
+	void get_version(const uint8_t *buf, size_t data_len, unsigned int &version) const override;
+
+	void get_active_index(const uint8_t *buf, size_t data_len,
+			      unsigned int &active_index) const override;
+};
+
+/* Registers on static construction */
+static metadata_v1_reader the_v1_reader;
+
+metadata_v1_reader::metadata_v1_reader()
+	: metadata_version_specific_reader()
+{
+	metadata_reader::instance()->register_reader(this);
+}
+
+metadata_v1_reader::~metadata_v1_reader()
+{
+}
+
+bool metadata_v1_reader::is_supported(const uint8_t *buf, size_t data_len) const
+{
+	assert(buf);
+
+	const struct fwu_metadata *metadata = (const struct fwu_metadata *)buf;
+
+	return (data_len >= sizeof(struct fwu_metadata)) && (metadata->version == 1);
+}
+
+void metadata_v1_reader::get_version(const uint8_t *buf, size_t data_len,
+				     unsigned int &version) const
+{
+	assert(buf);
+	assert(data_len >= sizeof(struct fwu_metadata));
+
+	const struct fwu_metadata *metadata = (const struct fwu_metadata *)buf;
+
+	version = metadata->version;
+}
+
+void metadata_v1_reader::get_active_index(const uint8_t *buf, size_t data_len,
+					  unsigned int &active_index) const
+{
+	assert(buf);
+	assert(data_len >= sizeof(struct fwu_metadata));
+
+	const struct fwu_metadata *metadata = (const struct fwu_metadata *)buf;
+
+	active_index = metadata->active_index;
+}
\ No newline at end of file
diff --git a/components/app/fwu-tool/app/metadata_v2_reader.cpp b/components/app/fwu-tool/app/metadata_v2_reader.cpp
new file mode 100644
index 0000000..61faa61
--- /dev/null
+++ b/components/app/fwu-tool/app/metadata_v2_reader.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+
+#include "metadata_reader.h"
+#include "protocols/service/fwu/packed-c/metadata_v2.h"
+
+class metadata_v2_reader : public metadata_version_specific_reader {
+public:
+	metadata_v2_reader();
+	~metadata_v2_reader();
+
+	bool is_supported(const uint8_t *buf, size_t data_len) const override;
+
+	void get_version(const uint8_t *buf, size_t data_len, unsigned int &version) const override;
+
+	void get_active_index(const uint8_t *buf, size_t data_len,
+			      unsigned int &active_index) const override;
+};
+
+/* Registers on static construction */
+static metadata_v2_reader the_v2_reader;
+
+metadata_v2_reader::metadata_v2_reader()
+	: metadata_version_specific_reader()
+{
+	metadata_reader::instance()->register_reader(this);
+}
+
+metadata_v2_reader::~metadata_v2_reader()
+{
+}
+
+bool metadata_v2_reader::is_supported(const uint8_t *buf, size_t data_len) const
+{
+	assert(buf);
+
+	const struct fwu_metadata *metadata = (const struct fwu_metadata *)buf;
+
+	return (data_len >= sizeof(struct fwu_metadata)) && (metadata->version == 2);
+}
+
+void metadata_v2_reader::get_version(const uint8_t *buf, size_t data_len,
+				     unsigned int &version) const
+{
+	assert(buf);
+	assert(data_len >= sizeof(struct fwu_metadata));
+
+	const struct fwu_metadata *metadata = (const struct fwu_metadata *)buf;
+
+	version = metadata->version;
+}
+
+void metadata_v2_reader::get_active_index(const uint8_t *buf, size_t data_len,
+					  unsigned int &active_index) const
+{
+	assert(buf);
+	assert(data_len >= sizeof(struct fwu_metadata));
+
+	const struct fwu_metadata *metadata = (const struct fwu_metadata *)buf;
+
+	active_index = metadata->active_index;
+}
\ No newline at end of file
diff --git a/components/app/fwu-tool/cmd_print_image_dir.cpp b/components/app/fwu-tool/cmd_print_image_dir.cpp
new file mode 100644
index 0000000..1e07027
--- /dev/null
+++ b/components/app/fwu-tool/cmd_print_image_dir.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "cmd_print_image_dir.h"
+
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <vector>
+
+#include "common/uuid/uuid.h"
+#include "print_uuid.h"
+#include "protocols/service/fwu/packed-c/fwu_proto.h"
+
+void cmd_print_image_dir(fwu_app &app)
+{
+	std::vector<uint8_t> fetched_object;
+	struct uuid_octets object_uuid;
+
+	uuid_guid_octets_from_canonical(&object_uuid, FWU_DIRECTORY_CANONICAL_UUID);
+
+	int status = app.read_object(object_uuid, fetched_object);
+
+	if (status) {
+		printf("Error: failed to read image directory\n");
+		return;
+	}
+
+	if (fetched_object.size() < offsetof(ts_fwu_image_directory, img_info_entry)) {
+		printf("Error: invalid image directory size\n");
+		return;
+	}
+
+	const struct ts_fwu_image_directory *img_dir =
+		(const struct ts_fwu_image_directory *)fetched_object.data();
+
+	printf("\nimage_directory (size %zu bytes) :\n", fetched_object.size());
+	printf("\tdirectory_version : %d\n", img_dir->directory_version);
+	printf("\tnum_images : %d\n", img_dir->num_images);
+	printf("\tcorrect_boot : %d\n", img_dir->correct_boot);
+
+	for (unsigned int i = 0; i < img_dir->num_images; i++) {
+		printf("\timg_info_entry[%u]:\n", i);
+		printf("\t\timg_type_uuid : %s\n",
+		       print_uuid(img_dir->img_info_entry[i].img_type_uuid).c_str());
+		printf("\t\tclient_permissions : 0x%x\n",
+		       img_dir->img_info_entry[i].client_permissions);
+		printf("\t\timg_max_size : %d\n", img_dir->img_info_entry[i].img_max_size);
+		printf("\t\tlowest_accepted_version : %d\n",
+		       img_dir->img_info_entry[i].lowest_accepted_version);
+		printf("\t\timg_version : %d\n", img_dir->img_info_entry[i].img_version);
+		printf("\t\taccepted : %d\n", img_dir->img_info_entry[i].accepted);
+	}
+}
diff --git a/components/app/fwu-tool/cmd_print_image_dir.h b/components/app/fwu-tool/cmd_print_image_dir.h
new file mode 100644
index 0000000..6a8f91e
--- /dev/null
+++ b/components/app/fwu-tool/cmd_print_image_dir.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef CMD_PRINT_IMAGE_DIR_H
+#define CMD_PRINT_IMAGE_DIR_H
+
+#include "app/fwu_app.h"
+
+void cmd_print_image_dir(fwu_app &app);
+
+#endif /* CMD_PRINT_IMAGE_DIR_H */
diff --git a/components/app/fwu-tool/cmd_print_metadata_v1.cpp b/components/app/fwu-tool/cmd_print_metadata_v1.cpp
new file mode 100644
index 0000000..6539e57
--- /dev/null
+++ b/components/app/fwu-tool/cmd_print_metadata_v1.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "cmd_print_metadata_v1.h"
+
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <vector>
+
+#include "common/uuid/uuid.h"
+#include "print_uuid.h"
+#include "protocols/service/fwu/packed-c/fwu_proto.h"
+#include "protocols/service/fwu/packed-c/metadata_v1.h"
+
+void cmd_print_metadata_v1(fwu_app &app)
+{
+	std::vector<uint8_t> fetched_object;
+	struct uuid_octets object_uuid;
+
+	uuid_guid_octets_from_canonical(&object_uuid, FWU_METADATA_CANONICAL_UUID);
+
+	int status = app.read_object(object_uuid, fetched_object);
+
+	if (status) {
+		printf("Error: failed to read metadata\n");
+		return;
+	}
+
+	if (fetched_object.size() < sizeof(struct fwu_metadata)) {
+		printf("Error: invalid metadata size\n");
+		return;
+	}
+
+	const struct fwu_metadata *metadata = (const struct fwu_metadata *)fetched_object.data();
+
+	printf("\nfwu_metadata (size %zu bytes) :\n", fetched_object.size());
+	printf("\tcrc_32 : 0x%x\n", metadata->crc_32);
+	printf("\tversion : %d\n", metadata->version);
+	printf("\tactive_index : %d\n", metadata->active_index);
+	printf("\tprevious_active_index : %d\n", metadata->previous_active_index);
+
+	for (unsigned int i = 0; i < FWU_METADATA_NUM_IMAGE_ENTRIES; i++) {
+		printf("\timg_entry[%u]:\n", i);
+		printf("\t\timg_type_uuid : %s\n",
+		       print_uuid(metadata->img_entry[i].img_type_uuid).c_str());
+		printf("\t\tlocation_uuid : %s\n",
+		       print_uuid(metadata->img_entry[i].location_uuid).c_str());
+
+		for (unsigned int bank_index = 0; bank_index < FWU_METADATA_NUM_BANKS;
+		     bank_index++) {
+			printf("\t\timg_props[%u]:\n", bank_index);
+			printf("\t\t\timg_uuid : %s\n",
+			       print_uuid(metadata->img_entry[i].img_props[bank_index].img_uuid)
+				       .c_str());
+			printf("\t\t\taccepted : %d\n",
+			       metadata->img_entry[i].img_props[bank_index].accepted);
+		}
+	}
+}
diff --git a/components/app/fwu-tool/cmd_print_metadata_v1.h b/components/app/fwu-tool/cmd_print_metadata_v1.h
new file mode 100644
index 0000000..83ac756
--- /dev/null
+++ b/components/app/fwu-tool/cmd_print_metadata_v1.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef CMD_PRINT_METADATA_V1_H
+#define CMD_PRINT_METADATA_V1_H
+
+#include "app/fwu_app.h"
+
+void cmd_print_metadata_v1(fwu_app &app);
+
+#endif /* CMD_PRINT_METADATA_V1_H */
diff --git a/components/app/fwu-tool/cmd_print_metadata_v2.cpp b/components/app/fwu-tool/cmd_print_metadata_v2.cpp
new file mode 100644
index 0000000..69e09d7
--- /dev/null
+++ b/components/app/fwu-tool/cmd_print_metadata_v2.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "cmd_print_metadata_v2.h"
+
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <vector>
+
+#include "common/uuid/uuid.h"
+#include "print_uuid.h"
+#include "protocols/service/fwu/packed-c/fwu_proto.h"
+#include "protocols/service/fwu/packed-c/metadata_v2.h"
+
+void cmd_print_metadata_v2(fwu_app &app)
+{
+	std::vector<uint8_t> fetched_object;
+	struct uuid_octets object_uuid;
+
+	uuid_guid_octets_from_canonical(&object_uuid, FWU_METADATA_CANONICAL_UUID);
+
+	int status = app.read_object(object_uuid, fetched_object);
+
+	if (status) {
+		printf("Error: failed to read metadata\n");
+		return;
+	}
+
+	if (fetched_object.size() < sizeof(struct fwu_metadata)) {
+		printf("Error: invalid metadata size\n");
+		return;
+	}
+
+	/* Print mandatory metadata header */
+	const struct fwu_metadata *metadata = (const struct fwu_metadata *)fetched_object.data();
+
+	printf("\nfwu_metadata (size %zu bytes) :\n", fetched_object.size());
+	printf("\tcrc_32 : 0x%x\n", metadata->crc_32);
+	printf("\tversion : %d\n", metadata->version);
+	printf("\tmetadata_size : %d\n", metadata->metadata_size);
+	printf("\theader_size : %d\n", metadata->header_size);
+	printf("\tactive_index : %d\n", metadata->active_index);
+	printf("\tprevious_active_index : %d\n", metadata->previous_active_index);
+	printf("\tbank_state : 0x%x 0x%x\n", metadata->bank_state[0], metadata->bank_state[1]);
+
+	if (metadata->metadata_size <= metadata->header_size)
+		return;
+
+	size_t fw_store_desc_size = metadata->metadata_size - metadata->header_size;
+
+	if (fw_store_desc_size < sizeof(fwu_fw_store_desc)) {
+		printf("\tInsufficient space for fw store descriptor\n");
+		return;
+	}
+
+	/* Print optional fw store descriptor */
+	struct fwu_fw_store_desc *fw_store_desc =
+		(struct fwu_fw_store_desc *)&fetched_object[metadata->header_size];
+
+	printf("\tfw_store_desc :\n");
+	printf("\t\tnum_banks : %d\n", fw_store_desc->num_banks);
+	printf("\t\tnum_images : %d\n", fw_store_desc->num_images);
+	printf("\t\timg_entry_size : %d\n", fw_store_desc->img_entry_size);
+	printf("\t\tbank_entry_size : %d\n", fw_store_desc->bank_entry_size);
+
+	for (unsigned int i = 0; i < fw_store_desc->num_images; i++) {
+		struct fwu_image_entry *img_entry = &fw_store_desc->img_entry[i];
+
+		printf("\t\timg_entry[%u] :\n", i);
+		printf("\t\t\timg_type_uuid : %s\n", print_uuid(img_entry->img_type_uuid).c_str());
+		printf("\t\t\tlocation_uuid : %s\n", print_uuid(img_entry->location_uuid).c_str());
+
+		for (unsigned int j = 0; j < fw_store_desc->num_banks; j++) {
+			struct fwu_img_bank_info *bank_info = &img_entry->img_bank_info[j];
+
+			printf("\t\t\timg_bank_info[%u] :\n", j);
+			printf("\t\t\t\timg_uuid : %s\n", print_uuid(bank_info->img_uuid).c_str());
+			printf("\t\t\t\taccepted : %d\n", bank_info->accepted);
+		}
+	}
+}
diff --git a/components/app/fwu-tool/cmd_print_metadata_v2.h b/components/app/fwu-tool/cmd_print_metadata_v2.h
new file mode 100644
index 0000000..fc25886
--- /dev/null
+++ b/components/app/fwu-tool/cmd_print_metadata_v2.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef CMD_PRINT_METADATA_V2_H
+#define CMD_PRINT_METADATA_V2_H
+
+#include "app/fwu_app.h"
+
+void cmd_print_metadata_v2(fwu_app &app);
+
+#endif /* CMD_PRINT_METADATA_V2_H */
diff --git a/components/app/fwu-tool/cmd_update_image.cpp b/components/app/fwu-tool/cmd_update_image.cpp
new file mode 100644
index 0000000..06c13a6
--- /dev/null
+++ b/components/app/fwu-tool/cmd_update_image.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "cmd_update_image.h"
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include "common/uuid/uuid.h"
+
+int cmd_update_image(fwu_app &app, const std::string &img_type_uuid,
+		     const std::string &img_filename)
+{
+	FILE *fp = fopen(img_filename.c_str(), "rb");
+
+	if (!fp) {
+		printf("Error: failed to open image file: %s\n", img_filename.c_str());
+		return -1;
+	}
+
+	/* Get file size */
+	fseek(fp, 0, SEEK_END);
+	size_t img_size = ftell(fp);
+	rewind(fp);
+
+	/* Allocate buffer for image data */
+	uint8_t *img_buf = (uint8_t *)malloc(img_size);
+
+	if (!img_buf) {
+		fclose(fp);
+		printf("Error: failed to allocate image buffer\n");
+		return -1;
+	}
+
+	/* Read file contents into buffer */
+	if (fread(img_buf, 1, img_size, fp)) {
+		fclose(fp);
+		free(img_buf);
+		printf("Error: failed to read image file\n");
+		return -1;
+	}
+
+	fclose(fp);
+
+	/* Apply update */
+	struct uuid_octets uuid;
+
+	uuid_guid_octets_from_canonical(&uuid, img_type_uuid.c_str());
+
+	int status = app.update_image(uuid, img_buf, img_size);
+
+	if (status)
+		printf("Error: update image failed\n");
+
+	free(img_buf);
+
+	return status;
+}
diff --git a/components/app/fwu-tool/cmd_update_image.h b/components/app/fwu-tool/cmd_update_image.h
new file mode 100644
index 0000000..20f71bc
--- /dev/null
+++ b/components/app/fwu-tool/cmd_update_image.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef CMD_UPDATE_IMAGE_H
+#define CMD_UPDATE_IMAGE_H
+
+#include <string>
+
+#include "app/fwu_app.h"
+
+int cmd_update_image(fwu_app &app, const std::string &img_type_uuid,
+		     const std::string &img_filename);
+
+#endif /* CMD_UPDATE_IMAGE_H */
diff --git a/components/app/fwu-tool/component.cmake b/components/app/fwu-tool/component.cmake
new file mode 100644
index 0000000..73a8524
--- /dev/null
+++ b/components/app/fwu-tool/component.cmake
@@ -0,0 +1,38 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2023, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+# Includes components needed for deploying the fwu update_agent within a
+# Posix environment as a command-line application. Can be used to apply an
+# update to a disk image file. Uses the same fwu components as a fw deployment
+# of the fwu service.
+#-------------------------------------------------------------------------------
+
+if (NOT DEFINED TGT)
+	message(FATAL_ERROR "Mandatory parameter TGT is not defined.")
+endif()
+#-------------------------------------------------------------------------------
+# Common components for fwu posix deployments
+#
+#-------------------------------------------------------------------------------
+add_components(TARGET ${TGT}
+	BASE_DIR ${TS_ROOT}
+	COMPONENTS
+		"components/common/crc32/native"
+		"components/common/trace"
+		"components/common/utils"
+)
+
+target_sources(${TGT} PRIVATE
+	${CMAKE_CURRENT_LIST_DIR}/fwu_main.cpp
+	${CMAKE_CURRENT_LIST_DIR}/cmd_update_image.cpp
+	${CMAKE_CURRENT_LIST_DIR}/cmd_print_image_dir.cpp
+	${CMAKE_CURRENT_LIST_DIR}/cmd_print_metadata_v1.cpp
+	${CMAKE_CURRENT_LIST_DIR}/cmd_print_metadata_v2.cpp
+	${CMAKE_CURRENT_LIST_DIR}/print_uuid.cpp
+	${CMAKE_CURRENT_LIST_DIR}/app/fwu_app.cpp
+	${CMAKE_CURRENT_LIST_DIR}/app/metadata_reader.cpp
+	${CMAKE_CURRENT_LIST_DIR}/app/metadata_v1_reader.cpp
+	${CMAKE_CURRENT_LIST_DIR}/app/metadata_v2_reader.cpp
+)
\ No newline at end of file
diff --git a/components/app/fwu-tool/fwu_main.cpp b/components/app/fwu-tool/fwu_main.cpp
new file mode 100644
index 0000000..ef0fe10
--- /dev/null
+++ b/components/app/fwu-tool/fwu_main.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <sstream>
+#include <string>
+#include <sys/stat.h>
+
+#include "cmd_print_image_dir.h"
+#include "cmd_print_metadata_v1.h"
+#include "cmd_print_metadata_v2.h"
+#include "cmd_update_image.h"
+#include "common/uuid/uuid.h"
+#include "app/fwu_app.h"
+
+static bool option_selected(const char *option_switch, int argc, char *argv[]);
+
+static std::string parse_string_option(const char *option_switch, int argc, char *argv[],
+				       const char *default_val);
+
+static int parse_numeric_option(const char *option_switch, int argc, char *argv[], int default_val);
+
+static bool file_exists(const std::string &filename);
+
+static void print_usage(void);
+static void print_help(void);
+
+int main(int argc, char *argv[])
+{
+	fwu_app app;
+	std::string disk_img_filename;
+	std::string update_img_filename;
+	std::string img_type_uuid;
+
+	/* Check for help */
+	if (option_selected("-h", argc, argv) || option_selected("-help", argc, argv) ||
+	    option_selected("--help", argc, argv)) {
+		print_help();
+		return 0;
+	}
+
+	/* Handle mandatory disk image filename. Must be first argument */
+	if (argc > 1)
+		disk_img_filename = std::string(argv[1]);
+	else {
+		printf("Error: missing disk-filename argument\n");
+		print_usage();
+		return -1;
+	}
+
+	/* Check if disk image file exists */
+	if (!file_exists(disk_img_filename)) {
+		printf("Error: %s does not exist\n", disk_img_filename.c_str());
+		return -1;
+	}
+
+	/* Create fwu configuration based on the input disk image */
+	int status = app.configure(disk_img_filename.c_str());
+
+	if (status) {
+		printf("Error: failed to configure with status: %d\n", status);
+		return -1;
+	}
+
+	/* Attempt to derive boot info from metadata. Assume bootloader booted from the
+	 * active index. This can be overridden via command-line parameter.
+	 */
+	unsigned int boot_index;
+	unsigned int metadata_version;
+
+	status = app.get_boot_info(boot_index, metadata_version);
+
+	if (status) {
+		printf("No recognised metadata, assume default boot index and version\n");
+
+		boot_index = 0;
+		metadata_version = 2;
+	}
+
+	/* Allow for command-line overrides */
+	boot_index = parse_numeric_option("-boot-index", argc, argv, boot_index);
+	metadata_version = parse_numeric_option("-meta-ver", argc, argv, metadata_version);
+
+	/* Options for printing fwu info */
+	bool is_print_img_dir = option_selected("-dir", argc, argv);
+	bool is_print_metadata = option_selected("-meta", argc, argv);
+
+	/* Parse input image related parameters*/
+	update_img_filename = parse_string_option("-img", argc, argv, "");
+	img_type_uuid = parse_string_option("-img-type", argc, argv, "");
+
+	/* Check if image file exists (if one was specified) */
+	if (!update_img_filename.empty() && !file_exists(update_img_filename)) {
+		printf("Error: %s does not exist\n", update_img_filename.c_str());
+		return -1;
+	}
+
+	/* Check if img type canonical uuid is well formed */
+	if (!img_type_uuid.empty() && !uuid_is_valid(img_type_uuid.c_str())) {
+		printf("Error: image type uuid invalid\n");
+		return -1;
+	}
+
+	/* Initialise the update_agent. Missing or corrupt metadata will get repaired
+	 */
+	status = app.init_update_agent(boot_index, metadata_version);
+
+	if (!status) {
+		printf("Update agent started: boot index: %u metadata ver: %u\n", boot_index,
+		       metadata_version);
+
+		if (is_print_img_dir)
+			cmd_print_image_dir(app);
+
+		if (is_print_metadata) {
+			if (metadata_version == 1)
+				cmd_print_metadata_v1(app);
+			else if (metadata_version == 2)
+				cmd_print_metadata_v2(app);
+			else
+				printf("Unsupported metadata version\n");
+		}
+
+		if (!update_img_filename.empty() && !img_type_uuid.empty()) {
+			status = cmd_update_image(app, img_type_uuid, update_img_filename);
+
+		} else if (!update_img_filename.empty() || !img_type_uuid.empty()) {
+			printf("Error: both image filename and uuid arguments are needed\n");
+			return -1;
+		}
+	}
+
+	if (!status)
+		printf("OK\n");
+	else
+		printf("Error status: %d\n", status);
+
+	return status;
+}
+
+static bool option_selected(const char *option_switch, int argc, char *argv[])
+{
+	bool is_selected = false;
+
+	for (int i = 1; (i < argc) && !is_selected; ++i) {
+		is_selected = (strcmp(argv[i], option_switch) == 0);
+	}
+
+	return is_selected;
+}
+
+static std::string parse_string_option(const char *option_switch, int argc, char *argv[],
+				       const char *default_val)
+{
+	std::string option = std::string(default_val);
+
+	for (int i = 1; i + 1 < argc; ++i) {
+		if (strcmp(argv[i], option_switch) == 0) {
+			option = std::string(argv[i + 1]);
+			break;
+		}
+	}
+
+	return option;
+}
+
+static int parse_numeric_option(const char *option_switch, int argc, char *argv[], int default_val)
+{
+	int option = default_val;
+
+	for (int i = 1; i + 1 < argc; ++i) {
+		if (strcmp(argv[i], option_switch) == 0) {
+			std::istringstream iss(argv[i + 1]);
+			int val;
+
+			iss >> val;
+
+			if (!iss.fail())
+				option = val;
+
+			break;
+		}
+	}
+
+	return option;
+}
+
+static bool file_exists(const std::string &filename)
+{
+	struct stat stat_buf;
+
+	return stat(filename.c_str(), &stat_buf) == 0;
+}
+
+static void print_usage(void)
+{
+	printf("Usage: fwu disk-filename [-dir -meta] [-boot-index number -meta-ver number] "
+	       "[-img filename -img-type uuid]\n");
+}
+
+static void print_help(void)
+{
+	print_usage();
+
+	printf("\n");
+	printf("\tdisk-filename\tDisk image file to update\n");
+	printf("\t-dir\t\tPrint image directory\n");
+	printf("\t-meta\t\tPrint FWU metadata\n");
+	printf("\t-boot-index\tOverride default boot index [0..n]\n");
+	printf("\t-meta-ver\tSpecify FWU metadata to use\n");
+	printf("\t-img\t\tFile containing image update\n");
+	printf("\t-img-type\tCanonical UUID of image to update\n");
+}
diff --git a/components/app/fwu-tool/print_uuid.cpp b/components/app/fwu-tool/print_uuid.cpp
new file mode 100644
index 0000000..a65bac1
--- /dev/null
+++ b/components/app/fwu-tool/print_uuid.cpp
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "print_uuid.h"
+
+#include "common/uuid/uuid.h"
+
+std::string print_uuid(const uint8_t *uuid_octets)
+{
+	struct uuid_canonical canonical_uuid;
+
+	uuid_canonical_from_guid_octets(&canonical_uuid, (const struct uuid_octets *)uuid_octets);
+
+	return std::string(canonical_uuid.characters);
+}
diff --git a/components/app/fwu-tool/print_uuid.h b/components/app/fwu-tool/print_uuid.h
new file mode 100644
index 0000000..2e94c85
--- /dev/null
+++ b/components/app/fwu-tool/print_uuid.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef PRINT_UUID_H
+#define PRINT_UUID_H
+
+#include <cstdint>
+#include <string>
+
+std::string print_uuid(const uint8_t *uuid_octets);
+
+#endif /* PRINT_UUID_H */