feat(runtime/rmi): implement RMI_PDEV_DESTROY

Implement RMI handler to destroy a PDEV and release all PDEV resources.
This calls device specific deinit routine. Scrub and transition PDEV
AUX granules and PDEV granule to DELEGATED state.

Signed-off-by: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com>
Change-Id: I6c228930cb96c8a41671859a820d986a7b0bc6f6
diff --git a/app/device_assignment/el0_app/src/dev_assign_el0_app.c b/app/device_assignment/el0_app/src/dev_assign_el0_app.c
index 6e09607..e4fe1c3 100644
--- a/app/device_assignment/el0_app/src/dev_assign_el0_app.c
+++ b/app/device_assignment/el0_app/src/dev_assign_el0_app.c
@@ -159,6 +159,28 @@
 }
 
 /*
+ * Clear all assigned buffers address that are used by CMA SPDM and call
+ * libspdm_deinit_context to freeup the memory of contexts within the SPDM
+ * context.
+ */
+static int dev_assign_deinit(uintptr_t heap)
+{
+	void *spdm_ctx;
+	struct dev_assign_info *info;
+
+	info = heap_start_to_dev_assign_info(heap);
+
+	info->send_recv_buffer = NULL;
+	info->scratch_buffer = NULL;
+	info->mbedtls_heap_buf = NULL;
+
+	/* Connection state related cleanup is handled by connection_deinit */
+	spdm_ctx = info->libspdm_ctx;
+	libspdm_deinit_context(spdm_ctx);
+	return DEV_ASSIGN_STATUS_SUCCESS;
+}
+
+/*
  * Assigns buffers to various objects as mentioned in the below mapping starting
  * from start of EL0 heap. Note that send_recv_buffer must be first and
  * libspdm_context must be just before struct dsm as this is assumed in
@@ -403,6 +425,8 @@
 		return (unsigned long)dev_assign_init(heap, arg_0,
 			(struct dev_assign_params *)shared);
 	}
+	case DEVICE_ASSIGN_APP_FUNC_ID_DEINIT:
+		return (unsigned long)dev_assign_deinit(heap);
 	default:
 		assert(false);
 		return (unsigned long)DEV_ASSIGN_STATUS_ERROR;
diff --git a/app/device_assignment/rmm_stub/include/dev_assign_structs.h b/app/device_assignment/rmm_stub/include/dev_assign_structs.h
index 480b01d..5d8d9bb 100644
--- a/app/device_assignment/rmm_stub/include/dev_assign_structs.h
+++ b/app/device_assignment/rmm_stub/include/dev_assign_structs.h
@@ -49,4 +49,12 @@
 	uint8_t sendrecv_buf[GRANULE_SIZE];
 };
 
+/*
+ * App function ID to de-initialise. App uses heap available via
+ * tpidrro_el0.
+ *
+ * ret0 == DEV_ASSIGN_STATUS_SUCCESS
+ */
+#define DEVICE_ASSIGN_APP_FUNC_ID_DEINIT		4
+
 #endif /* DEV_ASSIGN_STRUCTS_H */
diff --git a/runtime/core/handler.c b/runtime/core/handler.c
index 6aaed49..54c1aef 100644
--- a/runtime/core/handler.c
+++ b/runtime/core/handler.c
@@ -164,7 +164,7 @@
 	HANDLER(PDEV_ABORT,		0, 0, NULL,			 true, true),
 	HANDLER(PDEV_COMMUNICATE,	2, 0, NULL,			 true, true),
 	HANDLER(PDEV_CREATE,		2, 0, smc_pdev_create,		 true, true),
-	HANDLER(PDEV_DESTROY,		0, 0, NULL,			 true, true),
+	HANDLER(PDEV_DESTROY,		1, 0, smc_pdev_destroy,		 true, true),
 	HANDLER(PDEV_GET_STATE,		1, 1, smc_pdev_get_state,	 true, true),
 	HANDLER(PDEV_IDE_RESET,		0, 0, NULL,			 true, true),
 	HANDLER(PDEV_NOTIFY,		0, 0, NULL,			 true, true),
diff --git a/runtime/include/smc-handler.h b/runtime/include/smc-handler.h
index 436de84..33557d4 100644
--- a/runtime/include/smc-handler.h
+++ b/runtime/include/smc-handler.h
@@ -110,4 +110,7 @@
 			      unsigned long pdev_params_ptr);
 
 void smc_pdev_get_state(unsigned long pdev_ptr, struct smc_result *res);
+
+unsigned long smc_pdev_destroy(unsigned long pdev_ptr);
+
 #endif /* SMC_HANDLER_H */
diff --git a/runtime/rmi/pdev.c b/runtime/rmi/pdev.c
index 6e91db2..12d3380 100644
--- a/runtime/rmi/pdev.c
+++ b/runtime/rmi/pdev.c
@@ -3,6 +3,7 @@
  * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
  */
 
+#include <app.h>
 #include <arch.h>
 #include <arch_features.h>
 #include <buffer.h>
@@ -325,3 +326,69 @@
 out_err_input:
 	res->x[0] = RMI_ERROR_INPUT;
 }
+
+/*
+ * Destroy a PDEV. Host can reclaim PDEV resources when the PDEV state is STOPPED
+ * using RMI PDEV_DESTROY.
+ *
+ * pdev_ptr	- PA of the PDEV
+ */
+unsigned long smc_pdev_destroy(unsigned long pdev_ptr)
+{
+	int rc __unused;
+	struct granule *g_pdev;
+	void *aux_mapped_addr;
+	struct pdev *pd;
+
+	if (!is_rmi_feat_da_enabled()) {
+		return SMC_NOT_SUPPORTED;
+	}
+
+	if (!GRANULE_ALIGNED(pdev_ptr)) {
+		return RMI_ERROR_INPUT;
+	}
+
+	/* Lock pdev granule and map it */
+	g_pdev = find_lock_granule(pdev_ptr, GRANULE_STATE_PDEV);
+	if (g_pdev == NULL) {
+		return RMI_ERROR_INPUT;
+	}
+
+	pd = buffer_granule_map(g_pdev, SLOT_PDEV);
+	if (pd == NULL) {
+		granule_unlock(g_pdev);
+		return RMI_ERROR_INPUT;
+	}
+
+	if (pd->rmi_state != RMI_PDEV_STATE_STOPPED) {
+		buffer_unmap(pd);
+		granule_unlock(g_pdev);
+		return RMI_ERROR_DEVICE;
+	}
+
+	/* Map PDEV aux granules and map PDEV heap */
+	aux_mapped_addr = buffer_pdev_aux_granules_map(pd->g_aux, pd->num_aux);
+	assert(aux_mapped_addr != NULL);
+
+	/* Deinit the DSM context state */
+	rc = (int)app_run(&pd->da_app_data, DEVICE_ASSIGN_APP_FUNC_ID_DEINIT,
+				0, 0, 0, 0);
+	assert(rc == DEV_ASSIGN_STATUS_SUCCESS);
+
+	/* Unmap all PDEV aux granules and heap */
+	buffer_pdev_aux_unmap(aux_mapped_addr, pd->num_aux);
+
+	/*
+	 * Scrub PDEV AUX granules and move its state from PDEV_AUX to
+	 * delegated.
+	 */
+	pdev_restore_aux_granules_state(pd->g_aux, pd->num_aux, true);
+
+	/* Move the PDEV granule from PDEV to delegated state */
+	granule_memzero_mapped(pd);
+	buffer_unmap(pd);
+
+	granule_unlock_transition(g_pdev, GRANULE_STATE_DELEGATED);
+
+	return RMI_SUCCESS;
+}