SPM: Prevent simultaneous blocking calls

Blocking calls can only succeed if the target Secure Partition is idle.

Change-Id: Iabeaa0b8d3e653fd8581fa086758936abfc1c772
Signed-off-by: Antonio Nino Diaz <antonio.ninodiaz@arm.com>
diff --git a/services/std_svc/spm/spci.c b/services/std_svc/spm/spci.c
index cbb0f3c..1544ae1 100644
--- a/services/std_svc/spm/spci.c
+++ b/services/std_svc/spm/spci.c
@@ -291,6 +291,12 @@
 		SMC_RET1(handle, SPCI_BUSY);
 	}
 
+	if (spm_sp_request_increase_if_zero(sp_ctx) == -1) {
+		spin_unlock(&spci_handles_lock);
+
+		SMC_RET1(handle, SPCI_BUSY);
+	}
+
 	/* Prevent this handle from being closed */
 	handle_info->num_active_requests += 1;
 
@@ -348,6 +354,7 @@
 	spin_lock(&spci_handles_lock);
 	handle_info->num_active_requests -= 1;
 	spin_unlock(&spci_handles_lock);
+	spm_sp_request_decrease(sp_ctx);
 
 	/* Restore non-secure state */
 	cm_el1_sysregs_context_restore(NON_SECURE);
diff --git a/services/std_svc/spm/spm_main.c b/services/std_svc/spm/spm_main.c
index b1d5dd8..050c66c 100644
--- a/services/std_svc/spm/spm_main.c
+++ b/services/std_svc/spm/spm_main.c
@@ -47,6 +47,39 @@
 }
 
 /*******************************************************************************
+ * Functions to keep track of how many requests a Secure Partition has received
+ * and hasn't finished.
+ ******************************************************************************/
+void spm_sp_request_increase(sp_context_t *sp_ctx)
+{
+	spin_lock(&(sp_ctx->request_count_lock));
+	sp_ctx->request_count++;
+	spin_unlock(&(sp_ctx->request_count_lock));
+}
+
+void spm_sp_request_decrease(sp_context_t *sp_ctx)
+{
+	spin_lock(&(sp_ctx->request_count_lock));
+	sp_ctx->request_count--;
+	spin_unlock(&(sp_ctx->request_count_lock));
+}
+
+/* Returns 0 if it was originally 0, -1 otherwise. */
+int spm_sp_request_increase_if_zero(sp_context_t *sp_ctx)
+{
+	int ret = -1;
+
+	spin_lock(&(sp_ctx->request_count_lock));
+	if (sp_ctx->request_count == 0U) {
+		sp_ctx->request_count++;
+		ret = 0U;
+	}
+	spin_unlock(&(sp_ctx->request_count_lock));
+
+	return ret;
+}
+
+/*******************************************************************************
  * This function returns a pointer to the context of the Secure Partition that
  * handles the service specified by an UUID. It returns NULL if the UUID wasn't
  * found.
diff --git a/services/std_svc/spm/spm_private.h b/services/std_svc/spm/spm_private.h
index a8234c3..a7bd760 100644
--- a/services/std_svc/spm/spm_private.h
+++ b/services/std_svc/spm/spm_private.h
@@ -58,6 +58,9 @@
 	sp_state_t state;
 	spinlock_t state_lock;
 
+	unsigned int request_count;
+	spinlock_t request_count_lock;
+
 	/* Base and size of the shared SPM<->SP buffer */
 	uintptr_t spm_sp_buffer_base;
 	size_t spm_sp_buffer_size;
@@ -80,6 +83,11 @@
 void sp_state_wait_switch(sp_context_t *sp_ptr, sp_state_t from, sp_state_t to);
 int sp_state_try_switch(sp_context_t *sp_ptr, sp_state_t from, sp_state_t to);
 
+/* Functions to keep track of the number of active requests per SP */
+void spm_sp_request_increase(sp_context_t *sp_ctx);
+void spm_sp_request_decrease(sp_context_t *sp_ctx);
+int spm_sp_request_increase_if_zero(sp_context_t *sp_ctx);
+
 /* Functions related to the translation tables management */
 xlat_ctx_t *spm_sp_xlat_context_alloc(void);
 void sp_map_memory_regions(sp_context_t *sp_ctx);