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/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;
+}