From 6a73345662d53ebaa9750c70aa8fc7f40d3f8524 Mon Sep 17 00:00:00 2001
From: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
Date: Tue, 29 Nov 2022 15:11:27 +0000
Subject: [PATCH 11/27] efi: corstone1000: introduce EFI capsule update

This commit provides capsule update feature for Corstone1000.

This feature is available before and after ExitBootServices().

A platform specific capsule buffer is allocated. This buffer
is physically contiguous and allocated at the start of the DDR
memory after u-boot relocation to the end of DDR.

The capsule buffer is shared between u-boot and the secure world.
On UpdateCapsule() , capsule data is copied to the buffer
and a buffer ready event is generated using FF-A transport.

On ExitBootServices() a kernel started event is sent to the
SE Proxy FW update service. This event is generated on each boot.

Signed-off-by: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org>
Upstream-Status: Pending [Not submitted to upstream yet]
---
 board/armltd/corstone1000/corstone1000.c |   4 +
 configs/corstone1000_defconfig           |   3 +
 include/configs/corstone1000.h           |  18 ++++
 include/efi_loader.h                     |   4 +-
 lib/efi_loader/efi_boottime.c            |  36 +++++++
 lib/efi_loader/efi_capsule.c             | 124 ++++++++++++++++++++++-
 lib/efi_loader/efi_setup.c               |  15 +++
 7 files changed, 200 insertions(+), 4 deletions(-)

diff --git a/board/armltd/corstone1000/corstone1000.c b/board/armltd/corstone1000/corstone1000.c
index 4f4b96a095c2..76816f8f4ea1 100644
--- a/board/armltd/corstone1000/corstone1000.c
+++ b/board/armltd/corstone1000/corstone1000.c
@@ -66,6 +66,10 @@ static struct mm_region corstone1000_mem_map[] = {
 
 struct mm_region *mem_map = corstone1000_mem_map;
 
+void set_dfu_alt_info(char *interface, char *devstr)
+{
+}
+
 int board_init(void)
 {
 	return 0;
diff --git a/configs/corstone1000_defconfig b/configs/corstone1000_defconfig
index d1dc06c86c2c..06eac3e041fe 100644
--- a/configs/corstone1000_defconfig
+++ b/configs/corstone1000_defconfig
@@ -54,3 +54,6 @@ CONFIG_USB_ISP1760=y
 CONFIG_ERRNO_STR=y
 CONFIG_EFI_MM_COMM_TEE=y
 CONFIG_ARM_FFA_TRANSPORT=y
+CONFIG_EFI_RUNTIME_UPDATE_CAPSULE=y
+CONFIG_EFI_CAPSULE_FIRMWARE_FIT=y
+CONFIG_EFI_CAPSULE_FIRMWARE_RAW=y
diff --git a/include/configs/corstone1000.h b/include/configs/corstone1000.h
index b6226fa12af3..cd30499e3c9c 100644
--- a/include/configs/corstone1000.h
+++ b/include/configs/corstone1000.h
@@ -14,6 +14,24 @@
 
 #include <linux/sizes.h>
 
+/* The SE Proxy partition ID*/
+#define CORSTONE1000_SEPROXY_PART_ID		(0x8002)
+
+/* Update service ID provided by  the SE Proxy secure partition*/
+#define CORSTONE1000_SEPROXY_UPDATE_SVC_ID	(0x4)
+
+/* Notification events used with SE Proxy update service */
+#define CORSTONE1000_BUFFER_READY_EVT		(0x1)
+#define CORSTONE1000_KERNEL_STARTED_EVT		(0x2)
+
+/* Size in 4KB pages of the EFI capsule buffer */
+#define CORSTONE1000_CAPSULE_BUFFER_SIZE	(8192) /* 32 MB */
+
+/* Capsule GUID */
+#define EFI_CORSTONE1000_CAPSULE_ID_GUID \
+	EFI_GUID(0x3a770ddc, 0x409b, 0x48b2, 0x81, 0x41, \
+		 0x93, 0xb7, 0xc6, 0x0b, 0x20, 0x9e)
+
 #define FFA_SHARED_MM_BUFFER_SIZE	SZ_4K /* 4 KB */
 
 /*
diff --git a/include/efi_loader.h b/include/efi_loader.h
index f9e427f09059..26981141c228 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -1032,11 +1032,11 @@ extern const struct efi_firmware_management_protocol efi_fmp_fit;
 extern const struct efi_firmware_management_protocol efi_fmp_raw;
 
 /* Capsule update */
-efi_status_t EFIAPI efi_update_capsule(
+efi_status_t __efi_runtime EFIAPI efi_update_capsule(
 		struct efi_capsule_header **capsule_header_array,
 		efi_uintn_t capsule_count,
 		u64 scatter_gather_list);
-efi_status_t EFIAPI efi_query_capsule_caps(
+efi_status_t __efi_runtime EFIAPI efi_query_capsule_caps(
 		struct efi_capsule_header **capsule_header_array,
 		efi_uintn_t capsule_count,
 		u64 *maximum_capsule_size,
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
index fea4eb7a342e..faab74474d77 100644
--- a/lib/efi_loader/efi_boottime.c
+++ b/lib/efi_loader/efi_boottime.c
@@ -2103,6 +2103,33 @@ static void efi_exit_caches(void)
 #endif
 }
 
+#if IS_ENABLED(CONFIG_TARGET_CORSTONE1000)
+/**
+ * efi_corstone1000_kernel_started_event - notifies SE Proxy FW update service
+ *
+ * This function notifies the SE Proxy update service that the kernel has already started
+ *
+ * Return:
+ *
+ * 0: on success, otherwise failure
+ */
+static int efi_corstone1000_kernel_started_event(void)
+{
+	struct ffa_send_direct_data msg = {0};
+
+	log_debug("[%s]\n", __func__);
+
+	/*
+	 * setting the kernel started  event arguments
+	 */
+	msg.data0 = CORSTONE1000_SEPROXY_UPDATE_SVC_ID; /* x3 */
+	msg.data2 = CORSTONE1000_KERNEL_STARTED_EVT; /* x5 */
+
+	return ffa_bus_ops_get()->sync_send_receive(CORSTONE1000_SEPROXY_PART_ID, &msg, 0);
+}
+
+#endif
+
 /**
  * efi_exit_boot_services() - stop all boot services
  * @image_handle: handle of the loaded image
@@ -2209,6 +2236,15 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
 	/* Recalculate CRC32 */
 	efi_update_table_header_crc32(&systab.hdr);
 
+#if IS_ENABLED(CONFIG_TARGET_CORSTONE1000)
+	/* Notifying SE Proxy FW update service */
+	ffa_ret = efi_corstone1000_kernel_started_event();
+	if (ffa_ret)
+		debug("[efi_boottime][ERROR]: Failure to notify SE Proxy FW update service\n");
+	else
+		debug("[efi_boottime][INFO]: SE Proxy FW update service notified\n");
+#endif
+
 	/* Give the payload some time to boot */
 	efi_set_watchdog(0);
 	schedule();
diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
index 0997cd248fe3..9e8ddaac7f03 100644
--- a/lib/efi_loader/efi_capsule.c
+++ b/lib/efi_loader/efi_capsule.c
@@ -26,6 +26,14 @@
 #include <crypto/pkcs7_parser.h>
 #include <linux/err.h>
 
+#ifdef CONFIG_TARGET_CORSTONE1000
+#include <arm_ffa.h>
+#include <cpu_func.h>
+
+void *__efi_runtime_data corstone1000_capsule_buf; /* capsule shared buffer virtual address */
+efi_guid_t corstone1000_capsule_guid = EFI_CORSTONE1000_CAPSULE_ID_GUID;
+#endif
+
 DECLARE_GLOBAL_DATA_PTR;
 
 const efi_guid_t efi_guid_capsule_report = EFI_CAPSULE_REPORT_GUID;
@@ -696,6 +704,78 @@ static efi_status_t efi_capsule_update_firmware(
 }
 #endif /* CONFIG_EFI_CAPSULE_FIRMWARE_MANAGEMENT */
 
+#if IS_ENABLED(CONFIG_TARGET_CORSTONE1000)
+
+/**
+ * efi_corstone1000_alloc_capsule_shared_buf - allocate capsule shared buffer
+ * @capsule_image_size:	The capsule data (header + payload)
+ *
+ * This function allocates the physically contiguous buffer shared between u-boot
+ * and the secure world. On UpdateCapsule() capsule data is copied to the buffer
+ * and a door bell event is generated.
+ * The buffer is allocated at the start of the DDR memory after u-boot has been relocated
+ * to the end of DDR.
+ *
+ * Return:
+ *
+ * 0: on success, otherwise failure
+ */
+efi_status_t efi_corstone1000_alloc_capsule_shared_buf(void)
+{
+	efi_status_t efi_ret;
+	u64 ram_base = CFG_SYS_SDRAM_BASE;
+
+	log_debug("[%s]\n", __func__);
+
+	efi_ret = efi_allocate_pages(EFI_ALLOCATE_ADDRESS,
+				     EFI_RUNTIME_SERVICES_DATA,
+				     CORSTONE1000_CAPSULE_BUFFER_SIZE,
+				     &ram_base);
+
+	if (efi_ret != EFI_SUCCESS) {
+		corstone1000_capsule_buf = NULL;
+		log_err("EFI: Corstone1000: Allocating capsule shared buffer error (%d)\n"
+			, (int)efi_ret);
+		return efi_ret;
+	}
+
+	log_info("EFI: Corstone1000: Capsule shared buffer at 0x%x , size %d pages\n"
+		, (unsigned int)ram_base,
+		CORSTONE1000_CAPSULE_BUFFER_SIZE);
+
+	corstone1000_capsule_buf = (void *)map_sysmem((phys_addr_t)ram_base, 0);
+
+	return EFI_SUCCESS;
+}
+
+/**
+ * efi_corstone1000_buffer_ready_event - issue door bell event
+ * @capsule_image_size:	The capsule data (header + payload)
+ *
+ * This function notifies the SE Proxy update service that capsule data is available
+ * in the capsule shared buffer.
+ *
+ * Return:
+ *
+ * 0: on success, otherwise failure
+ */
+static int __efi_runtime efi_corstone1000_buffer_ready_event(u32 capsule_image_size)
+{
+	struct ffa_send_direct_data msg = {0};
+
+	log_debug("[%s]\n", __func__);
+
+	/*
+	 * setting the buffer ready event arguments
+	 */
+	msg.data0 = CORSTONE1000_SEPROXY_UPDATE_SVC_ID; /* x3 */
+	msg.data1 = capsule_image_size; /* x4 */
+	msg.data2 = CORSTONE1000_BUFFER_READY_EVT; /* x5 */
+
+	return ffa_bus_ops_get()->sync_send_receive(NULL, CORSTONE1000_SEPROXY_PART_ID, &msg, 0);
+}
+#endif
+
 /**
  * efi_update_capsule() - process information from operating system
  * @capsule_header_array:	Array of virtual address pointers
@@ -709,7 +789,7 @@ static efi_status_t efi_capsule_update_firmware(
  *
  * Return:			status code
  */
-efi_status_t EFIAPI efi_update_capsule(
+efi_status_t __efi_runtime EFIAPI efi_update_capsule(
 		struct efi_capsule_header **capsule_header_array,
 		efi_uintn_t capsule_count,
 		u64 scatter_gather_list)
@@ -726,6 +806,13 @@ efi_status_t EFIAPI efi_update_capsule(
 		goto out;
 	}
 
+#if IS_ENABLED(CONFIG_TARGET_CORSTONE1000)
+		if (capsule_count != 1 || !corstone1000_capsule_buf) {
+			ret = EFI_INVALID_PARAMETER;
+			goto out;
+		}
+#endif
+
 	ret = EFI_SUCCESS;
 	for (i = 0, capsule = *capsule_header_array; i < capsule_count;
 	     i++, capsule = *(++capsule_header_array)) {
@@ -738,6 +825,39 @@ efi_status_t EFIAPI efi_update_capsule(
 
 		log_debug("Capsule[%d] (guid:%pUs)\n",
 			  i, &capsule->capsule_guid);
+
+#if CONFIG_IS_ENABLED(TARGET_CORSTONE1000)
+		if (guidcmp(&corstone1000_capsule_guid, &capsule->capsule_guid)) {
+			ret = EFI_INVALID_PARAMETER;
+			log_err("Corstone1000: Invalid capsule GUID\n");
+			goto out;
+		}
+
+		if (efi_size_in_pages(capsule->capsule_image_size) >
+		    CORSTONE1000_CAPSULE_BUFFER_SIZE) {
+			log_err("Corstone1000: Capsule data size exceeds the shared buffer size\n");
+			ret = EFI_BUFFER_TOO_SMALL;
+			goto out;
+		}
+
+		/* copy the data to the contiguous buffer */
+		efi_memcpy_runtime(corstone1000_capsule_buf, capsule, capsule->capsule_image_size);
+
+		/* invalidate the data cache */
+		invalidate_dcache_all();
+
+		/* issue buffer ready event */
+		ret = efi_corstone1000_buffer_ready_event(capsule->capsule_image_size);
+		if (ret) {
+			log_err("EFI: Corstone1000: Buffer ready event error (%d)\n", (int)ret);
+			ret = EFI_DEVICE_ERROR;
+		} else {
+			ret = EFI_SUCCESS;
+		}
+
+		goto out;
+#endif
+
 		if (!guidcmp(&capsule->capsule_guid,
 			     &efi_guid_firmware_management_capsule_id)) {
 			ret  = efi_capsule_update_firmware(capsule);
@@ -776,7 +896,7 @@ out:
  *
  * Return:			status code
  */
-efi_status_t EFIAPI efi_query_capsule_caps(
+efi_status_t __efi_runtime EFIAPI efi_query_capsule_caps(
 		struct efi_capsule_header **capsule_header_array,
 		efi_uintn_t capsule_count,
 		u64 *maximum_capsule_size,
diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c
index 543764113530..1ad2fa52d7e9 100644
--- a/lib/efi_loader/efi_setup.c
+++ b/lib/efi_loader/efi_setup.c
@@ -16,6 +16,13 @@
 
 efi_status_t efi_obj_list_initialized = OBJ_LIST_NOT_INITIALIZED;
 
+#if IS_ENABLED(CONFIG_TARGET_CORSTONE1000)
+/**
+ * efi_corstone1000_alloc_capsule_shared_buf - allocate capsule shared buffer
+ */
+extern efi_status_t efi_corstone1000_alloc_capsule_shared_buf(void);
+#endif
+
 /*
  * Allow unaligned memory access.
  *
@@ -128,6 +135,14 @@ static efi_status_t efi_init_capsule(void)
 {
 	efi_status_t ret = EFI_SUCCESS;
 
+#if IS_ENABLED(CONFIG_TARGET_CORSTONE1000)
+	ret = efi_corstone1000_alloc_capsule_shared_buf();
+	if (ret != EFI_SUCCESS) {
+		printf("EFI: Corstone-1000: cannot allocate caspsule shared buffer\n");
+		return ret;
+	}
+#endif
+
 	if (IS_ENABLED(CONFIG_EFI_HAVE_CAPSULE_UPDATE)) {
 		ret = efi_set_variable_int(u"CapsuleMax",
 					   &efi_guid_capsule_report,
-- 
2.39.1

