From 372b693b0d9778fc5696512f13d39d0090b9b0d9 Mon Sep 17 00:00:00 2001
From: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
Date: Fri, 15 Nov 2024 19:24:50 +0000
Subject: [PATCH 11/36] fwu_arm_psa: Read the FWU directory through
 get_image_info()

Implement get_image_info()

get_image_info() is implemented by fwu_arm_psa_get_image_info()
which reads the images information from the cached directory in Secure
world then populates the images descriptors.

The FWU Arm PSA setup ABIs have been implemented to allow
reading the directory data.

The ABIs are specified by the Platform Security Firmware Update for
the A-profile Arm Architecture specification [1].

For more details about PSA please see [2].

The implemented ABIs are as follows:

FWU_DISCOVER
FWU_OPEN
FWU_READ_STREAM
FWU_COMMIT

[1]: DEN0118, 1.0 A: https://developer.arm.com/documentation/den0118/latest/
[2]: https://www.psacertified.org/

Signed-off-by: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
Signed-off-by: Davidson kumaresan <davidson.kumaresan@arm.com>
Upstream-Status: Submitted [cover letter: https://lore.kernel.org/all/20250702152528.1180414-1-abdellatif.elkhlifi@arm.com/]
---
 include/fwu_arm_psa.h         | 206 ++++++++++++
 lib/fwu_updates/fwu_arm_psa.c | 580 +++++++++++++++++++++++++++++++++-
 2 files changed, 785 insertions(+), 1 deletion(-)

diff --git a/include/fwu_arm_psa.h b/include/fwu_arm_psa.h
index f1d42f9ef24..ba294359b5e 100644
--- a/include/fwu_arm_psa.h
+++ b/include/fwu_arm_psa.h
@@ -10,9 +10,16 @@
 #ifndef __FWU_ARM_PSA_H
 #define __FWU_ARM_PSA_H
 
+#include <efi_loader.h>
 #include <linux/bitfield.h>
 #include <u-boot/uuid.h>
 
+#define DEFAULT_HW_INSTANCE		(1)
+
+/* Default values of the ESRT fields which are not supported at this stage */
+#define PACKAGE_VERSION_NOT_SUP		(0xffffffff)
+#define LAST_ATTEMPT_NOT_SUP		(0)
+
 #define FWU_BUFFER_PAGES		(1024)
 
 /* 4 MB buffer shared with secure world */
@@ -26,6 +33,13 @@
 #define TS_FWU_SERVICE_UUID		"38a82368-061b-0e47-7497-fd53fb8bce0c"
 /* In little-endian equivalent to:  6823a838-1b06-470e-9774-0cce8bfb53fd */
 
+/* TS FWU directory UUID string (in big-endian format) */
+#define FWU_DIRECTORY_CANONICAL_UUID	"d958eede-4751-d34a-90a2-a541236e6677"
+/* In little-endian equivalent to:  deee58d9-5147-4ad3-a290-77666e2341a5 */
+
+/* The entries correspond to the payloads in the storage device and the fake ESRT image */
+#define FWU_DIRECTORY_IMAGE_ENTRIES_COUNT (CONFIG_FWU_NUM_IMAGES_PER_BANK + 1)
+
 #define TS_RPC_MEM_RETRIEVE		(0xff0001)
 #define TS_RPC_SERVICE_INFO_GET		(0xff0003)
 #define RPC_SUCCESS			(0)
@@ -34,6 +48,11 @@
 #define GET_SVC_IFACE_ID(x)		\
 			 ((u8)(FIELD_GET(SVC_IFACE_ID_GET_MASK, (x))))
 
+#define SVC_ID_MASK			GENMASK(15, 0)
+#define SVC_IFACE_ID_SET_MASK		GENMASK(23, 16)
+#define PACK_SVC_IFACE_ID(svc, iface)	(FIELD_PREP(SVC_ID_MASK, (svc)) | \
+					FIELD_PREP(SVC_IFACE_ID_SET_MASK, (iface)))
+
 #define HANDLE_MSW_MASK			GENMASK(63, 32)
 #define HANDLE_LSW_MASK			GENMASK(31, 0)
 #define GET_FWU_BUF_MSW(x)		\
@@ -41,6 +60,174 @@
 #define GET_FWU_BUF_LSW(x)		\
 				((u32)(FIELD_GET(HANDLE_LSW_MASK, (x))))
 
+enum fwu_abis {
+	FWU_DISCOVER = 0,
+	FWU_OPEN = 19,
+	FWU_READ_STREAM = 21,
+	FWU_COMMIT = 22,
+	/* To be updated when adding new FWU IDs */
+	FWU_FIRST_ID = FWU_DISCOVER, /* Lowest number ID */
+	FWU_LAST_ID = FWU_COMMIT, /* Highest number ID */
+};
+
+enum fwu_abi_errcode {
+	FWU_UNKNOWN = 1,
+	FWU_BUSY,
+	FWU_OUT_OF_BOUNDS,
+	FWU_AUTH_FAIL,
+	FWU_NO_PERMISSION,
+	FWU_DENIED,
+	FWU_RESUME,
+	FWU_NOT_AVAILABLE,
+	MAX_NUMBER_FWU_ERR
+};
+
+/* Container structure and helper macros to map between an FF-A error and relevant error log */
+struct fwu_abi_errmap {
+	char *err_str[MAX_NUMBER_FWU_ERR];
+};
+
+#define FWU_ERRMAP_COUNT (FWU_LAST_ID - FWU_FIRST_ID + 1)
+#define FWU_ID_TO_ERRMAP_ID(fwu_id) ((fwu_id) - FWU_FIRST_ID)
+
+/*
+ * struct fwu_open_args - fwu_open ABI arguments
+ * @function_id: fwu_open service ID
+ * @image_type_guid: GUID of the image to be opened
+ * @op_type: The operation that the Client will perform on the image
+ */
+struct __packed fwu_open_args {
+	u32 function_id;
+	efi_guid_t image_type_guid;
+#define FWU_OP_TYPE_READ 0
+#define FWU_OP_TYPE_WRITE 1
+	u8  op_type;
+};
+
+/*
+ * struct fwu_open_resp - fwu_open ABI returns
+ * @status: The ABI return status
+ * @handle: Staging context identifier
+ */
+struct __packed fwu_open_resp {
+	int status;
+	u32  handle;
+};
+
+/*
+ * struct fwu_discover_args - fwu_discover ABI arguments
+ * @function_id: fwu_discover service ID
+ */
+struct __packed fwu_discover_args {
+	u32 function_id;
+};
+
+/*
+ * struct fwu_discover_resp - fwu_discover ABI returns
+ * @status: The ABI return status
+ * @service_status: the status of the service provider
+ * @version_major: the ABI major version
+ * @version_minor: the ABI minor version
+ * @off_function_presence: the offset (in bytes) of the function_presence array
+ *                         relative to the start of this data structure
+ * @num_func: the number of entries in the function_presence array
+ * @max_payload_size: the maximum number of bytes that a payload can contain
+ * @flags: flags listing the update capabilities
+ * @vendor_specific_flags: Vendor specific update capabilities flags
+ * @function_presence: array of bytes indicating functions that are implemented
+ */
+struct __packed fwu_discover_resp {
+	int status;
+	u16 service_status;
+	u8 version_major;
+	u8 version_minor;
+	u16 off_function_presence;
+	u16 num_func;
+	u64 max_payload_size;
+	u32 flags;
+	u32 vendor_specific_flags;
+	void *function_presence;
+};
+
+/*
+ * struct fwu_read_stream_args - fwu_read_stream ABI arguments
+ * @function_id: fwu_read_stream service ID
+ * @handle: The handle of the context being read from
+ */
+struct __packed fwu_read_stream_args {
+	u32 function_id;
+	u32  handle;
+};
+
+/*
+ * struct fwu_read_stream_resp - fwu_read_stream ABI returns
+ * @status: The ABI return status
+ * @read_bytes: Number of bytes read by the current ABI call
+ * @total_bytes: Total number of bytes that can be read
+ * @payload: The read data by the current ABI call
+ */
+struct __packed fwu_read_stream_resp {
+	int status;
+	u32  read_bytes;
+	u32  total_bytes;
+	u8  payload[];
+};
+
+/*
+ * struct fwu_commit_args - fwu_commit ABI arguments
+ * @function_id: fwu_commit service ID
+ * @handle: The handle of the context being closed
+ * @acceptance_req: If positive, the Client requests the image to be marked as
+ *                  unaccepted
+ * @max_atomic_len: Hint, maximum time (in ns) that the Update Agent can execute
+ *                  continuously without yielding back to the Client state
+ */
+struct __packed fwu_commit_args {
+	u32 function_id;
+	u32 handle;
+#define FWU_IMG_ACCEPTED 0
+#define FWU_IMG_NOT_ACCEPTED 1
+	u32 acceptance_req;
+	u32 max_atomic_len;
+};
+
+/*
+ * struct fwu_commit_resp - fwu_commit ABI returns
+ * @status: The ABI return status
+ * @progress: Units of work already completed by the Update Agent
+ * @total_work: Units of work the Update Agent must perform until fwu_commit
+ *              returns successfully
+ */
+struct __packed fwu_commit_resp {
+	int status;
+	u32 progress;
+	u32 total_work;
+};
+
+/*
+ * FWU directory information structures
+ */
+
+struct __packed fwu_image_info_entry {
+	efi_guid_t image_guid;
+	u32 client_permissions;
+	u32 img_max_size;
+	u32 lowest_acceptable_version;
+	u32 img_version;
+	u32 accepted;
+	u32 reserved;
+};
+
+struct __packed fwu_image_directory {
+	u32 directory_version;
+	u32 img_info_offset;
+	u32 num_images;
+	u32 correct_boot;
+	u32 img_info_size;
+	u32 reserved;
+	struct fwu_image_info_entry entries[FWU_DIRECTORY_IMAGE_ENTRIES_COUNT];
+};
+
 /**
  * fwu_agent_init() - Setup the FWU agent
  *
@@ -55,4 +242,23 @@
  */
 int fwu_agent_init(void);
 
+/**
+ * fwu_arm_psa_get_image_info() - Arm PSA implementation for GetImageInfo()
+ *
+ * Initialize the update agent in secure world if not initialized.
+ * Then, read the FWU directory information including the current
+ * images information. For more details refer to fwu_image_directory structure.
+ *
+ * Return:
+ *
+ * EFI_SUCCESS is returned on success. Otherwise, failure
+ */
+efi_status_t fwu_arm_psa_get_image_info(efi_uintn_t *image_info_size,
+					struct efi_firmware_image_descriptor *image_info,
+					u32 *descriptor_version,
+					u8 *descriptor_count,
+					efi_uintn_t *descriptor_size,
+					u32 *package_version,
+					u16 **package_version_name);
+
 #endif
diff --git a/lib/fwu_updates/fwu_arm_psa.c b/lib/fwu_updates/fwu_arm_psa.c
index 4a01c5ac672..b42d9f90e1e 100644
--- a/lib/fwu_updates/fwu_arm_psa.c
+++ b/lib/fwu_updates/fwu_arm_psa.c
@@ -9,7 +9,6 @@
 #include <arm_ffa.h>
 #include <dm.h>
 #include <fwu_arm_psa.h>
-#include <efi_loader.h>
 #include <fwu.h>
 #include <log.h>
 #include <malloc.h>
@@ -21,7 +20,481 @@ static u64 g_fwu_buf_handle;
 static u16 g_fwu_sp_id;
 static struct udevice *g_dev;
 static u16 g_svc_interface_id;
+static u64 g_max_payload_size;
+static u8 g_fwu_version_major;
+static u8 g_fwu_version_minor;
 static bool g_fwu_initialized;
+struct fwu_image_directory g_fwu_cached_directory;
+
+/* Error mapping declarations */
+
+int fwu_to_std_errmap[MAX_NUMBER_FWU_ERR] = {
+	[FWU_UNKNOWN] = -EOPNOTSUPP,
+	[FWU_OUT_OF_BOUNDS] = -ERANGE,
+	[FWU_AUTH_FAIL] = -EFAULT,
+	[FWU_BUSY] = -EBUSY,
+	[FWU_NO_PERMISSION] = -EPERM,
+	[FWU_DENIED] = -EACCES,
+	[FWU_RESUME] = -EAGAIN,
+	[FWU_NOT_AVAILABLE] = -ENAVAIL,
+};
+
+static struct fwu_abi_errmap err_msg_map[FWU_ERRMAP_COUNT] = {
+	[FWU_ID_TO_ERRMAP_ID(FWU_OPEN)] = {
+		{
+			[FWU_UNKNOWN] =
+			"FWU_UNKNOWN: Image type with GUID=image_type_guid does not exist",
+			[FWU_DENIED] =
+			"FWU_DENIED: An image cannot be opened for writing outside of this Staging state",
+			[FWU_NOT_AVAILABLE] =
+			"FWU_NOT_AVAILABLE: The Update Agent does not support the op_type for this image",
+		},
+	},
+	[FWU_ID_TO_ERRMAP_ID(FWU_READ_STREAM)] = {
+		{
+			[FWU_UNKNOWN] =
+			"FWU_UNKNOWN: Handle is not recognized",
+			[FWU_DENIED] =
+			"FWU_DENIED: The image cannot be temporarily read from",
+			[FWU_NO_PERMISSION] =
+			"FWU_NO_PERMISSION: The image cannot be read from",
+		},
+	},
+	[FWU_ID_TO_ERRMAP_ID(FWU_COMMIT)] = {
+		{
+			[FWU_UNKNOWN] =
+			"FWU_UNKNOWN: Handle is not recognized",
+			[FWU_DENIED] =
+			"FWU_DENIED: The image can only be accepted after activation",
+			[FWU_AUTH_FAIL] =
+			"FWU_AUTH_FAIL: Image closed, authentication failed",
+			[FWU_RESUME] =
+			"FWU_RESUME: The Update Agent yielded",
+		},
+	},
+};
+
+/**
+ * fwu_to_std_errno() - convert FWU error code to standard error code
+ * @fwu_errno:	Error code returned by the FWU ABI
+ *
+ * Map the given FWU error code as specified
+ * by the spec to a U-Boot standard error code.
+ *
+ * Return:
+ *
+ * The standard error code on success. . Otherwise, failure
+ */
+static int fwu_to_std_errno(int fwu_errno)
+{
+	int err_idx = -fwu_errno;
+
+	/* Map the FWU error code to the standard u-boot error code */
+	if (err_idx > 0 && err_idx < MAX_NUMBER_FWU_ERR)
+		return fwu_to_std_errmap[err_idx];
+	return -EINVAL;
+}
+
+/**
+ * fwu_print_error_log() - print the error log corresponding to the selected FWU ABI
+ * @fwu_id:	FWU ABI ID
+ * @fwu_errno:	Error code returned by the FWU ABI
+ *
+ * Map the FWU error code to the error log relevant to the
+ * selected FWU ABI. Then the error log is printed.
+ *
+ * Return:
+ *
+ * 0 on success. . Otherwise, failure
+ */
+static int fwu_print_error_log(u32 fwu_id, int fwu_errno)
+{
+	int err_idx = -fwu_errno, abi_idx = 0;
+
+	/* Map the FWU error code to the corresponding error log */
+
+	if (err_idx <= 0 || err_idx >= MAX_NUMBER_FWU_ERR)
+		return -EINVAL;
+
+	if (fwu_id < FWU_FIRST_ID || fwu_id > FWU_LAST_ID)
+		return -EINVAL;
+
+	abi_idx = FWU_ID_TO_ERRMAP_ID(fwu_id);
+
+	if (!err_msg_map[abi_idx].err_str[err_idx])
+		return -EINVAL;
+
+	log_err("%s\n", err_msg_map[abi_idx].err_str[err_idx]);
+
+	return 0;
+}
+
+/**
+ * fwu_invoke_svc() - FWU service call request
+ * @svc_id: FWU ABI function ID
+ * @svc_name: FWU ABI function name
+ * @req_args_sz: Size in bytes of the arguments of the FWU ABI function
+ * @expect_resp_sz: Size in bytes of the response of the FWU ABI function
+ * @extra_resp_bytes: Size in bytes of the extra response data
+ *
+ * Invoke a FWU ABI by issuing a TS service call request.
+ *
+ * Return:
+ *
+ * 0 on success
+ */
+static int fwu_invoke_svc(u32 svc_id, const char *svc_name,
+			  u32 req_args_sz, u32 expect_resp_sz,
+			  u32 *extra_resp_bytes)
+{
+	struct ffa_send_direct_data msg;
+	int ret;
+	int *svc_status_in_buf = g_fwu_buf, svc_status_in_reg;
+	u32 expect_total_resp_sz;
+
+	log_debug("FWU: Invoking %s\n", svc_name);
+
+	if (!expect_resp_sz || !svc_name || !req_args_sz) {
+		log_err("%s: Invalid invoke arguments\n", svc_name);
+		return -EINVAL;
+	}
+
+	msg.data0 = PACK_SVC_IFACE_ID(svc_id, g_svc_interface_id);
+	msg.data1 = GET_FWU_BUF_LSW(g_fwu_buf_handle);
+	msg.data2 = GET_FWU_BUF_MSW(g_fwu_buf_handle);
+	msg.data3 = req_args_sz;
+	msg.data4 = 0;
+
+	ret = ffa_sync_send_receive(g_dev, g_fwu_sp_id, &msg, 0);
+	if (ret) {
+		log_err("%s: FF-A error %d\n", svc_name, ret);
+		return ret;
+	}
+
+	if (msg.data0 != PACK_SVC_IFACE_ID(svc_id, g_svc_interface_id)) {
+		log_err("%s: Unexpected service/interface ID 0x%lx\n",
+			svc_name, msg.data0);
+		return -EINVAL;
+	}
+
+	if (msg.data1 != RPC_SUCCESS) {
+		log_err("%s: TS RPC error 0x%lx\n", svc_name, msg.data1);
+		return -ECOMM;
+	}
+
+	svc_status_in_reg = (int)msg.data2;
+	if (*svc_status_in_buf != svc_status_in_reg) {
+		log_err("%s: Status mismatch (reg %d , buf %d)\n",
+			svc_name, svc_status_in_reg, *svc_status_in_buf);
+		return -EINVAL;
+	}
+
+	if (svc_status_in_reg < 0) {
+		fwu_print_error_log(svc_id, svc_status_in_reg);
+		if (svc_status_in_reg != -FWU_RESUME)
+			return fwu_to_std_errno(svc_status_in_reg);
+	}
+
+	if (!extra_resp_bytes)
+		expect_total_resp_sz = expect_resp_sz;
+	else
+		expect_total_resp_sz = expect_resp_sz + *extra_resp_bytes;
+
+	if (msg.data3 != expect_total_resp_sz) {
+		log_err("%s: Unexpected response size (%ld , %d)\n",
+			svc_name, msg.data3, expect_total_resp_sz);
+		return -EINVAL;
+	}
+
+	if (svc_status_in_reg == -FWU_RESUME)
+		return fwu_to_std_errno(svc_status_in_reg);
+
+	return 0;
+}
+
+/**
+ * fwu_discover() -  fwu_discover ABI
+ *
+ * This call indicates the version of the ABI alongside a list of the
+ * implemented functions (aka services).
+ * Only max_payload_size is saved for future use.
+ *
+ * Return:
+ *
+ * 0 on success. Otherwise, failure
+ */
+static int fwu_discover(void)
+{
+	int ret;
+	struct fwu_discover_args *args = g_fwu_buf;
+	struct fwu_discover_resp *resp = g_fwu_buf;
+	char *svc_name = "FWU_DISCOVER";
+
+	/* Filling the arguments in the shared buffer */
+	args->function_id = FWU_DISCOVER;
+
+	/* Executing the FWU ABI through the FF-A bus */
+	ret = fwu_invoke_svc(args->function_id, svc_name, sizeof(*args),
+			     sizeof(int), NULL);
+	if (ret) {
+		log_debug("FWU_DISCOVER: error %d\n", ret);
+		return ret;
+	}
+
+	g_max_payload_size = resp->max_payload_size;
+	g_fwu_version_major = resp->version_major;
+	g_fwu_version_minor = resp->version_minor;
+
+	log_debug("FWU: max_payload_size %llu\n", g_max_payload_size);
+	log_info("FWU: ABI version %d.%d detected\n", g_fwu_version_major,
+		 g_fwu_version_minor);
+
+	return 0;
+}
+
+/**
+ * fwu_open() -  fwu_open ABI
+ * @guid: GUID of the image to be opened
+ * @op_type: The operation that the Client will perform on the image
+ * @handle: Staging context identifier
+ *
+ * Returns a handle to the image with a given GUID.
+ *
+ * Return:
+ *
+ * 0 on success. Otherwise, failure
+ */
+static int fwu_open(const efi_guid_t *guid, u8  op_type,
+		    u32 *handle)
+{
+	int ret;
+	struct fwu_open_args *args = g_fwu_buf;
+	struct fwu_open_resp *resp = g_fwu_buf;
+	char *svc_name = "FWU_OPEN";
+
+	if (!guid || !handle)
+		return -EINVAL;
+
+	/* Filling the arguments in the shared buffer */
+	args->function_id = FWU_OPEN;
+
+	guidcpy(&args->image_type_guid, guid);
+	args->op_type = op_type;
+
+	/* Executing the FWU ABI through the FF-A bus */
+	ret = fwu_invoke_svc(args->function_id, svc_name,
+			     sizeof(*args), sizeof(*resp), NULL);
+	if (ret)
+		return ret;
+
+	*handle = resp->handle;
+
+	return 0;
+}
+
+/**
+ * fwu_read_stream() -  fwu_read_stream ABI
+ * @handle: The handle of the context being read from
+ * @buffer: The destination buffer where the data will be copied to
+ * @buffer_size: The size of the destination buffer
+ *
+ * The call reads at most g_max_payload_size bytes from the Update Agent
+ *  context pointed to by handle.
+ *
+ * Return:
+ *
+ * 0 on success. Otherwise, failure
+ */
+static int fwu_read_stream(u32 handle, u8 *buffer, u32 buffer_size)
+{
+	int ret;
+	u32 curr_read_offset = 0, new_read_offset, fwu_buf_bytes_left;
+	struct fwu_read_stream_args *args = g_fwu_buf;
+	struct fwu_read_stream_resp *resp = g_fwu_buf;
+	char *svc_name = "FWU_READ_STREAM";
+
+	if (!buffer || !buffer_size)
+		return -EINVAL;
+
+	do {
+		/* Filling the arguments in the shared buffer */
+		args->function_id = FWU_READ_STREAM;
+		args->handle = handle;
+
+		/* Executing the FWU ABI through the FF-A bus */
+		ret = fwu_invoke_svc(args->function_id, svc_name, sizeof(*args),
+				     sizeof(*resp), &resp->read_bytes);
+		if (ret)
+			return ret;
+
+		if (resp->read_bytes > g_max_payload_size) {
+			log_err("%s: Bytes read > max_payload_size (%d , %llu)\n",
+				svc_name, resp->read_bytes,
+				g_max_payload_size);
+			return -EINVAL;
+		}
+
+		fwu_buf_bytes_left = FWU_BUFFER_SIZE - sizeof(*resp);
+
+		if (resp->read_bytes > fwu_buf_bytes_left) {
+			log_err("%s: Bytes read > shared buffer (%d , %d)\n",
+				svc_name, resp->read_bytes, fwu_buf_bytes_left);
+			return -EINVAL;
+		}
+
+		if (resp->total_bytes > buffer_size) {
+			log_err("%s: Total bytes > dest buffer (%d , %d)\n",
+				svc_name, resp->total_bytes, buffer_size);
+			return -EINVAL;
+		}
+
+		new_read_offset = resp->read_bytes + curr_read_offset;
+
+		if (new_read_offset > buffer_size) {
+			log_err("%s: Bytes read > dest buffer (%d , %d)\n",
+				svc_name, new_read_offset, buffer_size);
+			return -EINVAL;
+		}
+
+		memcpy(buffer + curr_read_offset, resp->payload,
+		       resp->read_bytes);
+
+		curr_read_offset = new_read_offset;
+
+		if (curr_read_offset > resp->total_bytes) {
+			log_err("%s: Offset bypassed total bytes (%d , %d)\n",
+				svc_name, curr_read_offset, resp->total_bytes);
+			return -EINVAL;
+		}
+
+	} while (curr_read_offset != resp->total_bytes);
+
+	return ret;
+}
+
+/**
+ * fwu_commit() -  fwu_commit ABI
+ * @handle: The handle of the context being closed
+ * @acceptance_req: Acceptance status set by the Client
+ * @max_atomic_len: Hint, maximum time (in ns) that the Update Agent can execute
+ *                  continuously without yielding back to the Client state
+ *
+ * The call closes the image pointed to by handle. The image can be any entity
+ * opened with fwu_open().
+ *
+ * Return:
+ *
+ * 0 on success. Otherwise, failure
+ */
+static int fwu_commit(u32 handle, u32 acceptance_req, u32 max_atomic_len)
+{
+	struct fwu_commit_args *args = g_fwu_buf;
+	struct fwu_commit_resp *resp = g_fwu_buf;
+	char *svc_name = "FWU_COMMIT";
+	int ret;
+
+	/* Filling the arguments in the shared buffer */
+	args->function_id = FWU_COMMIT;
+	args->handle = handle;
+	args->acceptance_req = acceptance_req;
+	args->max_atomic_len = max_atomic_len;
+
+	/* Executing the FWU ABI through the FF-A bus */
+	ret = fwu_invoke_svc(args->function_id, svc_name,
+			     sizeof(*args), sizeof(*resp), NULL);
+
+	while (resp->status == -FWU_RESUME)
+		ret = fwu_invoke_svc(args->function_id, svc_name, sizeof(*args),
+				     sizeof(*resp), NULL);
+
+	if (ret)
+		return ret;
+
+	log_debug("%s:  Progress %d/%d\n", svc_name, resp->progress,
+		  resp->total_work);
+
+	return 0;
+}
+
+/**
+ * fwu_read_directory() - Read FWU directory information
+ *
+ * Read FWU directory information.
+ * For more details see fwu_image_directory structure.
+ *
+ * Return:
+ *
+ * 0 on success
+ */
+static int fwu_read_directory(void)
+{
+	int ret, close_ret;
+	u32 handle = 0;
+	efi_guid_t dir_guid = {0};
+	char *uuid_str = FWU_DIRECTORY_CANONICAL_UUID;
+
+	if (!uuid_str) {
+		log_err("FWU: No directory UUID provided\n");
+		return -EINVAL;
+	}
+
+	if (uuid_str_to_le_bin(uuid_str, dir_guid.b)) {
+		log_err("FWU: Invalid directory UUID\n");
+		return -EINVAL;
+	}
+
+	ret = fwu_open(&dir_guid, FWU_OP_TYPE_READ, &handle);
+	if (ret) {
+		log_err("FWU: Open image directory failed (err: %d)\n",
+			ret);
+		return ret;
+	}
+
+	log_debug("FWU: Image directory handle (0x%x)\n", handle);
+
+	ret = fwu_read_stream(handle, (u8 *)&g_fwu_cached_directory,
+			      sizeof(g_fwu_cached_directory));
+	if (ret) {
+		log_err("FWU: Read image directory failed (err: %d)\n",
+			ret);
+		goto close_handle;
+	}
+
+	log_debug("FWU: directory_version (%d)\n",
+		  g_fwu_cached_directory.directory_version);
+
+	/*
+	 * Note: The expected images in the directory are:
+	 * - The images to be updated
+	 * - The ESRT image (an image containing ESRT data)
+	 * The ESRT image is not involved in the FWU.
+	 * It should be removed from the count.
+	 */
+	g_fwu_cached_directory.num_images -= 1;
+
+	if (g_fwu_cached_directory.num_images !=
+	    CONFIG_FWU_NUM_IMAGES_PER_BANK) {
+		log_err("FWU: Unexpected image count (%d , %d)\n",
+			g_fwu_cached_directory.num_images,
+			CONFIG_FWU_NUM_IMAGES_PER_BANK);
+		ret = -EINVAL;
+		goto close_handle;
+	}
+
+	log_debug("FWU: images to be updated (%d)\n",
+		  g_fwu_cached_directory.num_images);
+	log_debug("FWU: img_info_size (%d)\n",
+		  g_fwu_cached_directory.img_info_size);
+
+close_handle:
+	/* The Update Agent can execute for an unbounded time */
+	close_ret = fwu_commit(handle, FWU_IMG_NOT_ACCEPTED, 0);
+	if (close_ret)
+		log_err("FWU: Close image directory handle failed (err: %d)\n",
+			close_ret);
+
+	return ret;
+}
 
 /**
  * fwu_discover_ts_sp_id() - Query the FWU partition ID
@@ -265,7 +738,112 @@ int fwu_agent_init(void)
 	if (ret)
 		return ret;
 
+	ret = fwu_discover();
+	if (ret)
+		goto failure;
+
 	g_fwu_initialized = true;
 
 	return 0;
+
+failure:
+	fwu_shared_buf_reclaim();
+	return ret;
+}
+
+/**
+ * fwu_arm_psa_get_image_info() - Arm PSA implementation for GetImageInfo()
+ *
+ * Initialize the update agent in secure world if not initialized.
+ * Then, read the FWU directory information including the current
+ * images information. For more details refer to fwu_image_directory structure.
+ *
+ * Return:
+ *
+ * EFI_SUCCESS is returned on success. Otherwise, failure
+ */
+efi_status_t fwu_arm_psa_get_image_info(efi_uintn_t *image_info_size,
+					struct efi_firmware_image_descriptor *image_info,
+					u32 *descriptor_version,
+					u8 *descriptor_count,
+					efi_uintn_t *descriptor_size,
+					u32 *package_version,
+					u16 **package_version_name)
+{
+	int ret;
+	int required_image_info_size;
+	size_t image_info_desc_size = sizeof(*image_info);
+
+	if (!g_fwu_initialized) {
+		ret = fwu_agent_init();
+		if (ret) {
+			log_err("FWU: Update agent init failed, ret = %d\n",
+				ret);
+			return EFI_EXIT(EFI_DEVICE_ERROR);
+		}
+	}
+
+	ret = fwu_read_directory();
+	if (ret)
+		return EFI_NOT_READY;
+
+	required_image_info_size = g_fwu_cached_directory.num_images *
+		image_info_desc_size;
+
+	if (*image_info_size < required_image_info_size) {
+		*image_info_size = required_image_info_size;
+		return EFI_BUFFER_TOO_SMALL;
+	}
+
+	*descriptor_version = EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION;
+	*descriptor_count = g_fwu_cached_directory.num_images;
+	*descriptor_size = image_info_desc_size;
+	*package_version = PACKAGE_VERSION_NOT_SUP; /* Not supported */
+	*package_version_name = NULL; /* Not supported */
+
+	for (int i = 0; i < g_fwu_cached_directory.num_images; i++) {
+		/* Only image indexes starting from 1 are supported */
+		image_info[i].image_index = i + 1;
+
+		/* Corresponding ESRT field: FwClass */
+		guidcpy(&image_info[i].image_type_id,
+			&g_fwu_cached_directory.entries[i].image_guid);
+
+		image_info[i].image_id = image_info[i].image_index;
+		image_info[i].image_id_name = NULL; /* Not supported */
+
+		/* Corresponding ESRT field: FwVersion */
+		image_info[i].version =
+			g_fwu_cached_directory.entries[i].img_version;
+
+		image_info[i].version_name = NULL; /* Not supported */
+		image_info[i].size =
+			g_fwu_cached_directory.entries[i].img_max_size;
+
+		image_info[i].attributes_supported =
+			IMAGE_ATTRIBUTE_IMAGE_UPDATABLE |
+			IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED;
+		image_info[i].attributes_setting =
+				IMAGE_ATTRIBUTE_IMAGE_UPDATABLE;
+
+		/* Check if the capsule authentication is enabled */
+		if (IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE))
+			image_info[i].attributes_setting |=
+				IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED;
+
+		/* Corresponding ESRT field: LowestSupportedFwVersion */
+		image_info[i].lowest_supported_image_version =
+			g_fwu_cached_directory.entries[i].lowest_acceptable_version;
+
+		/* Corresponding ESRT field: LastAttemptVersion (not supported) */
+		image_info[i].last_attempt_version = LAST_ATTEMPT_NOT_SUP;
+
+		/* Corresponding ESRT field: LastAttemptStatus (not supported) */
+		image_info[i].last_attempt_status = LAST_ATTEMPT_NOT_SUP;
+
+		image_info[i].hardware_instance = DEFAULT_HW_INSTANCE;
+		image_info[i].dependencies = NULL; /* Not supported */
+	}
+
+	return EFI_SUCCESS;
 }
-- 
2.25.1

