From c7df14add690e18645fdfea2c7e8ee5f2b8239c1 Mon Sep 17 00:00:00 2001
From: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
Date: Thu, 20 Mar 2025 15:09:27 +0000
Subject: [PATCH 17/36] fwu_arm_psa: Add FWU acceptance mechanism

Accept the FWU at ExitBootServices()

Provide the FWU_ACCEPT_IMAGE ABI and setup an event triggered
on ExitBootServices().
This mechanism notifies Secure world that the system booted
successfully by accepting the images in trial state.

Also, add FWU_ARM_PSA_ACCEPT_IMAGES config
to allow platforms to switch off image acceptance
in ExitBootServices().

Signed-off-by: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
Upstream-Status: Submitted [cover letter: https://lore.kernel.org/all/20250702152528.1180414-1-abdellatif.elkhlifi@arm.com/]
---
 include/fwu_arm_psa.h         |  22 +++-
 lib/fwu_updates/Kconfig       |   8 ++
 lib/fwu_updates/fwu_arm_psa.c | 183 ++++++++++++++++++++++++++++++++++
 3 files changed, 212 insertions(+), 1 deletion(-)

diff --git a/include/fwu_arm_psa.h b/include/fwu_arm_psa.h
index a8f0ff93d51..451d8b614e3 100644
--- a/include/fwu_arm_psa.h
+++ b/include/fwu_arm_psa.h
@@ -84,9 +84,10 @@ enum fwu_abis {
 	FWU_WRITE_STREAM = 20,
 	FWU_READ_STREAM = 21,
 	FWU_COMMIT = 22,
+	FWU_ACCEPT_IMAGE = 23,
 	/* To be updated when adding new FWU IDs */
 	FWU_FIRST_ID = FWU_DISCOVER, /* Lowest number ID */
-	FWU_LAST_ID = FWU_COMMIT, /* Highest number ID */
+	FWU_LAST_ID = FWU_ACCEPT_IMAGE, /* Highest number ID */
 };
 
 enum fwu_abi_errcode {
@@ -311,6 +312,25 @@ struct __packed fwu_write_stream_resp {
 	int status;
 };
 
+/*
+ * struct fwu_accept_image_args - fwu_accept_image ABI arguments
+ * @function_id: fwu_accept_image service ID
+ * @image_type_guid: GUID of the image to be accepted
+ */
+struct __packed fwu_accept_image_args {
+	u32 function_id;
+	u32 reserved;
+	efi_guid_t image_type_guid;
+};
+
+/*
+ * struct fwu_accept_image_resp - fwu_accept_image ABI returns
+ * @status: The ABI return status
+ */
+struct __packed fwu_accept_image_resp {
+	int status;
+};
+
 /*
  * FWU directory information structures
  */
diff --git a/lib/fwu_updates/Kconfig b/lib/fwu_updates/Kconfig
index cdc96109f0a..958e54dec37 100644
--- a/lib/fwu_updates/Kconfig
+++ b/lib/fwu_updates/Kconfig
@@ -57,6 +57,14 @@ config FWU_ARM_PSA
 	  driver that supports the Arm PSA firmware update specification as
 	  mentioned in https://developer.arm.com/documentation/den0118/a/
 
+config FWU_ARM_PSA_ACCEPT_IMAGES
+	bool "Accept images at EFI ExitBootServices() level"
+	depends on FWU_ARM_PSA
+	default y
+	help
+	  Select this option if reaching ExitBootServices() level means the boot succeeded
+	  This option will add a callback at ExitBootServices() that accepts all the images.
+
 config FWU_BUFFER_PAGES
 	int "Number of 4KB pages in the FWU shared buffer"
 	depends on FWU_ARM_PSA
diff --git a/lib/fwu_updates/fwu_arm_psa.c b/lib/fwu_updates/fwu_arm_psa.c
index 042bf7a0898..40746eee6ce 100644
--- a/lib/fwu_updates/fwu_arm_psa.c
+++ b/lib/fwu_updates/fwu_arm_psa.c
@@ -8,6 +8,7 @@
  */
 #include <arm_ffa.h>
 #include <dm.h>
+#include <fwu.h>
 #include <fwu_arm_psa.h>
 #include <fwu.h>
 #include <log.h>
@@ -113,6 +114,14 @@ static struct fwu_abi_errmap err_msg_map[FWU_ERRMAP_COUNT] = {
 			"FWU_DENIED: The system is not in a Staging state",
 		},
 	},
+	[FWU_ID_TO_ERRMAP_ID(FWU_ACCEPT_IMAGE)] = {
+		{
+			[FWU_UNKNOWN] =
+			"FWU_UNKNOWN: Image with type=image_type_guid is not managed by the Update Agent",
+			[FWU_DENIED] =
+			"FWU_DENIED: The system has not booted with the active bank, or the image cannot be accepted before being activated",
+		},
+	},
 };
 
 /**
@@ -708,6 +717,36 @@ static int fwu_write_stream(u32 handle, const u8 *payload, u32 payload_size)
 	return ret;
 }
 
+/**
+ * fwu_accept() -  fwu_accept_image ABI
+ * @guid: GUID of the image to be accepted
+ *
+ * Sets the status of the firmware image, with a given GUID
+ * to "accepted" in the active firmware bank.
+ *
+ * Return:
+ *
+ * 0 on success. Otherwise, failure
+ */
+static int fwu_accept(const efi_guid_t *guid)
+{
+	struct fwu_accept_image_args *args = g_fwu_buf;
+	struct fwu_accept_image_resp *resp = g_fwu_buf;
+	char *svc_name = "FWU_ACCEPT_IMAGE";
+
+	if (!guid)
+		return -EINVAL;
+
+	/* Filling the arguments in the shared buffer */
+	args->function_id = FWU_ACCEPT_IMAGE;
+
+	guidcpy(&args->image_type_guid, guid);
+
+	/* Executing the FWU ABI through the FF-A bus */
+	return fwu_invoke_svc(args->function_id, svc_name,
+			     sizeof(*args), sizeof(*resp), NULL);
+}
+
 /**
  * fwu_update_image() - Update an image
  *
@@ -1051,6 +1090,144 @@ failure:
 	return ret;
 }
 
+/**
+ * fwu_one_image_accepted() - Accept one image in trial state
+ * @img_entry: Pointer to the image entry.
+ * @active_idx: Active bank index.
+ * @image_number: Image number for logging purposes.
+ *
+ * Invoke FWU accept image ABI to accept the image.
+ *
+ * Return:
+ *
+ * true is returned on success, false on failure.
+ */
+static bool fwu_one_image_accepted(const struct fwu_image_entry *img_entry,
+				   u32 active_idx,
+				   u32 image_number)
+{
+	const struct fwu_image_bank_info *bank_info =
+		&img_entry->img_bank_info[active_idx];
+	int fwu_ret;
+
+	if (!bank_info->accepted) {
+		fwu_ret = fwu_accept(&bank_info->image_guid);
+		if (fwu_ret) {
+			log_err("FWU: Failed to accept image #%d\n",
+				image_number + 1);
+			return false;
+		}
+		log_debug("FWU: Image #%d accepted\n", image_number + 1);
+	}
+
+	return true;
+}
+
+/**
+ * fwu_all_images_accepted() - Accept any pending firmware update images
+ * @fwu_data: Pointer to FWU data structure
+ *
+ * Read from the metadata the acceptance state of each image.
+ * Then, accept the images which are not accepted yet.
+ *
+ * Return:
+ *
+ * true is returned on success, false on failure.
+ */
+static bool fwu_all_images_accepted(const struct fwu_data *fwu_data)
+{
+	int fwu_ret;
+	u32 active_idx;
+	u32 i;
+	bool accepted;
+
+	fwu_ret = fwu_get_active_index(&active_idx);
+	if (fwu_ret) {
+		log_err("FWU: Failed to read boot index, err (%d)\n",
+			fwu_ret);
+		return false;
+	}
+
+	for (i = 0 ; i < CONFIG_FWU_NUM_IMAGES_PER_BANK ; i++) {
+		accepted = fwu_one_image_accepted(&fwu_data->fwu_images[i], active_idx, i);
+		if (!accepted)
+			return false;
+	}
+
+	return true;
+}
+
+/**
+ * fwu_accept_notify_exit_boot_services() - ExitBootServices callback
+ *
+ * @event:	callback event
+ * @context:	callback context
+ *
+ * Reaching ExitBootServices() level means the boot succeeded.
+ * So, accept all the images.
+ *
+ * Return:
+ *
+ * EFI_SUCCESS on success. Otherwise, failure
+ */
+static void EFIAPI fwu_accept_notify_exit_boot_services(struct efi_event *event,
+							void *context)
+{
+	efi_status_t efi_ret = EFI_SUCCESS;
+	bool all_accepted;
+	struct fwu_data *fwu_data;
+
+	EFI_ENTRY("%p, %p", event, context);
+
+	fwu_data = fwu_get_data();
+	if (!fwu_data) {
+		log_err("FWU: Cannot get FWU data\n");
+		efi_ret = EFI_INVALID_PARAMETER;
+		goto out;
+	}
+
+	if (fwu_data->trial_state) {
+		all_accepted = fwu_all_images_accepted(fwu_data);
+		if (!all_accepted) {
+			efi_ret = EFI_ACCESS_DENIED;
+			goto out;
+		}
+
+	} else {
+		log_info("FWU: ExitBootServices: Booting in regular state\n");
+	}
+
+out:
+	EFI_EXIT(efi_ret);
+}
+
+/**
+ * fwu_setup_accept_event() - Setup the FWU accept event
+ *
+ * Create a FWU accept event triggered on ExitBootServices().
+ *
+ * Return:
+ *
+ * 0 is returned on success. Otherwise, failure
+ */
+static int fwu_setup_accept_event(void)
+{
+	efi_status_t efi_ret;
+	struct efi_event *evt = NULL;
+
+	efi_ret = efi_create_event(EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK,
+				   fwu_accept_notify_exit_boot_services, NULL,
+				   &efi_guid_event_group_exit_boot_services,
+				   &evt);
+	if (efi_ret != EFI_SUCCESS) {
+		log_err("FWU: Cannot install accept event %p, err (%lu)\n", evt,
+			efi_ret);
+		return -EPERM;
+	}
+
+	return 0;
+}
+
 /**
  * fwu_agent_init() - Setup the FWU agent
  *
@@ -1105,6 +1282,12 @@ int fwu_agent_init(void)
 	if (ret)
 		goto failure;
 
+	if (IS_ENABLED(CONFIG_FWU_ARM_PSA_ACCEPT_IMAGES)) {
+		ret = fwu_setup_accept_event();
+		if (ret)
+			goto failure;
+	}
+
 	g_fwu_initialized = true;
 
 	return 0;
-- 
2.25.1

