feat(runtime/rmi): implement RMI_PDEV_STOP

RMI:
This RMI handler set the PDEV state to STOPPING, this allows NS Host to
invoke PDEV communicate call to do deinit the device connection and
transition the PDEV to STOPPED state.

DEV_ASSIGN App:
Added command to deinit the connection. This terminates the secure
session. This command also unset the public key context and certificate
chain hash in the libspdm connection state.

Signed-off-by: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com>
Change-Id: I7d0d9b1654137f739793005cf290b8bb4c74957a
diff --git a/runtime/core/handler.c b/runtime/core/handler.c
index 140a746..4076efb 100644
--- a/runtime/core/handler.c
+++ b/runtime/core/handler.c
@@ -169,7 +169,7 @@
 	HANDLER(PDEV_IDE_RESET,		0, 0, NULL,			 true, true),
 	HANDLER(PDEV_NOTIFY,		0, 0, NULL,			 true, true),
 	HANDLER(PDEV_SET_PUBKEY,	4, 0, NULL,			 true, true),
-	HANDLER(PDEV_STOP,		0, 0, NULL,			 true, true),
+	HANDLER(PDEV_STOP,		1, 0, smc_pdev_stop,		 true, true),
 	HANDLER(RTT_AUX_CREATE,		0, 0, NULL,			 true, true),
 	HANDLER(RTT_AUX_DESTROY,	0, 0, NULL,			 true, true),
 	HANDLER(RTT_AUX_FOLD,		0, 0, NULL,			 true, true),
diff --git a/runtime/include/smc-handler.h b/runtime/include/smc-handler.h
index 75bb669..0ea36f5 100644
--- a/runtime/include/smc-handler.h
+++ b/runtime/include/smc-handler.h
@@ -118,6 +118,8 @@
 
 unsigned long smc_pdev_abort(unsigned long pdev_ptr);
 
+unsigned long smc_pdev_stop(unsigned long pdev_ptr);
+
 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 f2afd06..a52bc20 100644
--- a/runtime/rmi/pdev.c
+++ b/runtime/rmi/pdev.c
@@ -434,6 +434,10 @@
 		rc = dev_assign_dev_communicate(&pd->da_app_data, enter_args,
 			exit_args, DEVICE_ASSIGN_APP_FUNC_ID_CONNECT_INIT);
 		break;
+	case RMI_PDEV_STATE_STOPPING:
+		rc = dev_assign_dev_communicate(&pd->da_app_data, enter_args,
+			exit_args, DEVICE_ASSIGN_APP_FUNC_ID_STOP_CONNECTION);
+		break;
 	default:
 		assert(false);
 		rc = -1;
@@ -546,11 +550,17 @@
 	 */
 	switch (pd->dev_comm_state) {
 	case DEV_COMM_ERROR:
-		pd->rmi_state = RMI_PDEV_STATE_ERROR;
+		if (pd->rmi_state == RMI_PDEV_STATE_STOPPING) {
+			pd->rmi_state = RMI_PDEV_STATE_STOPPED;
+		} else {
+			pd->rmi_state = RMI_PDEV_STATE_ERROR;
+		}
 		break;
 	case DEV_COMM_IDLE:
 		if (pd->rmi_state == RMI_PDEV_STATE_NEW) {
 			pd->rmi_state = RMI_PDEV_STATE_NEEDS_KEY;
+		} else if (pd->rmi_state == RMI_PDEV_STATE_STOPPING) {
+			pd->rmi_state = RMI_PDEV_STATE_STOPPED;
 		} else {
 			pd->rmi_state = RMI_PDEV_STATE_ERROR;
 		}
@@ -618,6 +628,62 @@
 }
 
 /*
+ * Stop the PDEV. This sets the PDEV state to STOPPING, and a PDEV communicate
+ * call can do device specific cleanup like terminating a secure session.
+ *
+ * pdev_ptr	- PA of the PDEV
+ */
+unsigned long smc_pdev_stop(unsigned long pdev_ptr)
+{
+	struct granule *g_pdev;
+	unsigned long smc_rc;
+	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_COMMUNICATING) ||
+	    (pd->rmi_state == RMI_PDEV_STATE_STOPPING) ||
+	    (pd->rmi_state == RMI_PDEV_STATE_STOPPED)) {
+		smc_rc = RMI_ERROR_DEVICE;
+		goto out_pdev_buf_unmap;
+	}
+
+	if (pd->num_vdevs != 0U) {
+		smc_rc = RMI_ERROR_DEVICE;
+		goto out_pdev_buf_unmap;
+	}
+
+	pd->rmi_state = RMI_PDEV_STATE_STOPPING;
+	pd->dev_comm_state = DEV_COMM_PENDING;
+	smc_rc = RMI_SUCCESS;
+
+out_pdev_buf_unmap:
+	buffer_unmap(pd);
+
+	granule_unlock(g_pdev);
+
+	return smc_rc;
+}
+
+/*
  * Abort device communication associated with a PDEV.
  *
  * pdev_ptr	- PA of the PDEV