feat: perform checks for partition runtime model

Perform state transition checks across invocations of various
FF-A ABIs based on the partition runtime model.

Partition manager must return DENIED as the error code if an
invalid transition is attempted by an endpoint execution context.

Change-Id: I7bb9c23f7c50288c8ccfcc9c8928045c0168276f
Signed-off-by: Madhukar Pappireddy <madhukar.pappireddy@arm.com>
diff --git a/src/api.c b/src/api.c
index 8ae4e4a..ceb7934 100644
--- a/src/api.c
+++ b/src/api.c
@@ -270,7 +270,8 @@
 {
 	struct ffa_value ret = (struct ffa_value){.func = FFA_SUCCESS_32};
 	struct vcpu_locked current_locked;
-	bool is_direct_request_ongoing;
+	bool transition_allowed;
+	enum vcpu_state next_state = VCPU_STATE_BLOCKED;
 
 	if (current->vm->id == HF_PRIMARY_VM_ID) {
 		/* NOOP on the primary as it makes the scheduling decisions. */
@@ -278,20 +279,23 @@
 	}
 
 	current_locked = vcpu_lock(current);
-	is_direct_request_ongoing =
-		is_ffa_direct_msg_request_ongoing(current_locked);
+	transition_allowed = plat_ffa_check_runtime_state_transition(
+		current, current->vm->id, HF_INVALID_VM_ID, NULL, FFA_YIELD_32,
+		&next_state);
 	vcpu_unlock(&current_locked);
 
-	if (is_direct_request_ongoing) {
+	if (!transition_allowed) {
 		return ffa_error(FFA_DENIED);
 	}
 
+	assert(next_state == VCPU_STATE_BLOCKED);
+
 	*next = api_switch_to_primary(
 		current,
 		(struct ffa_value){.func = FFA_YIELD_32,
 				   .arg1 = ffa_vm_vcpu(current->vm->id,
 						       vcpu_index(current))},
-		VCPU_STATE_BLOCKED);
+		next_state);
 
 	return ret;
 }
@@ -689,6 +693,7 @@
 				  struct ffa_value *args)
 {
 	struct ffa_value ret;
+	enum vcpu_state next_state = VCPU_STATE_WAITING;
 
 	if (args->arg1 != 0U || args->arg2 != 0U || args->arg3 != 0U ||
 	    args->arg4 != 0U || args->arg5 != 0U || args->arg6 != 0U ||
@@ -696,6 +701,14 @@
 		return ffa_error(FFA_INVALID_PARAMETERS);
 	}
 
+	if (!plat_ffa_check_runtime_state_transition(
+		    current, current->vm->id, HF_INVALID_VM_ID, NULL,
+		    FFA_MSG_WAIT_32, &next_state)) {
+		return ffa_error(FFA_DENIED);
+	}
+
+	assert(next_state == VCPU_STATE_WAITING);
+
 	if (plat_ffa_msg_wait_prepare(current, next, &ret)) {
 		return ret;
 	}
@@ -941,6 +954,8 @@
 	struct vm *vm;
 	struct vcpu *vcpu;
 	struct ffa_value ret = ffa_error(FFA_INVALID_PARAMETERS);
+	enum vcpu_state next_state = VCPU_STATE_BLOCKED;
+	struct vcpu_locked current_locked;
 
 	if (!plat_ffa_run_checks(current, vm_id, vcpu_idx, &ret, next)) {
 		return ret;
@@ -961,8 +976,23 @@
 		goto out;
 	}
 
-	/* Update state if allowed. */
-	vcpu = vm_get_vcpu(vm, vcpu_idx);
+	/*
+	 * Refer Figure 8.13 Scenario 1 of the FF-A v1.1 EAC spec. SPMC
+	 * bypasses the intermediate execution contexts and resumes the
+	 * SP execution context that was originally preempted.
+	 */
+	if (*next != NULL) {
+		vcpu = *next;
+	} else {
+		vcpu = vm_get_vcpu(vm, vcpu_idx);
+	}
+
+	if (!plat_ffa_check_runtime_state_transition(current, current->vm->id,
+						     HF_INVALID_VM_ID, vcpu,
+						     FFA_RUN_32, &next_state)) {
+		return ffa_error(FFA_DENIED);
+	}
+
 	if (!api_vcpu_prepare_run(current, vcpu, &ret)) {
 		goto out;
 	}
@@ -991,6 +1021,11 @@
 	/* Switch to the vCPU. */
 	*next = vcpu;
 
+	assert(next_state == VCPU_STATE_BLOCKED);
+	current_locked = vcpu_lock(current);
+	current->state = VCPU_STATE_BLOCKED;
+	vcpu_unlock(&current_locked);
+
 	/*
 	 * Set a placeholder return code to the scheduler. This will be
 	 * overwritten when the switch back to the primary occurs.
@@ -2445,6 +2480,7 @@
 	struct vm_locked receiver_locked;
 	struct vcpu *receiver_vcpu;
 	struct two_vcpu_locked vcpus_locked;
+	enum vcpu_state next_state = VCPU_STATE_BLOCKED;
 
 	if (!api_ffa_dir_msg_is_arg2_zero(args)) {
 		return ffa_error(FFA_INVALID_PARAMETERS);
@@ -2487,6 +2523,12 @@
 		return ffa_error(FFA_INVALID_PARAMETERS);
 	}
 
+	if (!plat_ffa_check_runtime_state_transition(
+		    current, sender_vm_id, HF_INVALID_VM_ID, receiver_vcpu,
+		    args.func, &next_state)) {
+		return ffa_error(FFA_DENIED);
+	}
+
 	receiver_locked = vm_lock(receiver_vm);
 	vcpus_locked = vcpu_lock_both(receiver_vcpu, current);
 
@@ -2554,6 +2596,7 @@
 
 	arch_regs_set_retval(&receiver_vcpu->regs, api_ffa_dir_msg_value(args));
 
+	assert(next_state == VCPU_STATE_BLOCKED);
 	current->state = VCPU_STATE_BLOCKED;
 
 	/* Switch to receiver vCPU targeted to by direct msg request */
@@ -2596,6 +2639,7 @@
 					      struct vcpu **next)
 {
 	struct vcpu_locked current_locked;
+	enum vcpu_state next_state = VCPU_STATE_WAITING;
 
 	if (!api_ffa_dir_msg_is_arg2_zero(args)) {
 		return ffa_error(FFA_INVALID_PARAMETERS);
@@ -2608,6 +2652,13 @@
 		return ffa_error(FFA_INVALID_PARAMETERS);
 	}
 
+	if (!plat_ffa_check_runtime_state_transition(current, sender_vm_id,
+						     receiver_vm_id, NULL,
+						     args.func, &next_state)) {
+		return ffa_error(FFA_DENIED);
+	}
+
+	assert(next_state == VCPU_STATE_WAITING);
 	current_locked = vcpu_lock(current);
 	if (api_ffa_is_managed_exit_ongoing(current_locked)) {
 		/*
diff --git a/test/vmapi/ffa_secure_partitions/dir_msg.c b/test/vmapi/ffa_secure_partitions/dir_msg.c
index 7610849..dce67b8 100644
--- a/test/vmapi/ffa_secure_partitions/dir_msg.c
+++ b/test/vmapi/ffa_secure_partitions/dir_msg.c
@@ -56,7 +56,7 @@
 
 /**
  * Test that if a direct message request is sent to an SP that is already
- * waiting for a direct message response an BUSY error code is returned.
+ * waiting for a direct message response an DENIED error code is returned.
  */
 TEST(ffa_msg_send_direct_req, fails_direct_req_to_waiting_sp)
 {
@@ -64,7 +64,7 @@
 	struct ffa_value res;
 	ffa_vm_id_t own_id = hf_vm_get_id();
 
-	res = sp_req_echo_busy_cmd_send(own_id, receiver_id);
+	res = sp_req_echo_denied_cmd_send(own_id, receiver_id);
 
 	EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP_32);
 	EXPECT_EQ(sp_resp(res), SP_SUCCESS);
diff --git a/test/vmapi/ffa_secure_partitions/services/inc/partition_services.h b/test/vmapi/ffa_secure_partitions/services/inc/partition_services.h
index 941e31e..0d91fd3 100644
--- a/test/vmapi/ffa_secure_partitions/services/inc/partition_services.h
+++ b/test/vmapi/ffa_secure_partitions/services/inc/partition_services.h
@@ -69,18 +69,18 @@
 				 uint32_t val2, uint32_t val3, uint32_t val4);
 
 /**
- * Command to request SP to run echo busy test with second SP.
+ * Command to request SP to run echo denied test with second SP.
  */
-#define SP_REQ_ECHO_BUSY_CMD 0x65636871
+#define SP_REQ_ECHO_DENIED_CMD 0x65636871
 
-static inline struct ffa_value sp_req_echo_busy_cmd_send(ffa_vm_id_t sender,
-							 ffa_vm_id_t receiver)
+static inline struct ffa_value sp_req_echo_denied_cmd_send(ffa_vm_id_t sender,
+							   ffa_vm_id_t receiver)
 {
-	return ffa_msg_send_direct_req(sender, receiver, SP_REQ_ECHO_BUSY_CMD,
+	return ffa_msg_send_direct_req(sender, receiver, SP_REQ_ECHO_DENIED_CMD,
 				       0, 0, 0, 0);
 }
 
-struct ffa_value sp_req_echo_busy_cmd(ffa_vm_id_t test_source);
+struct ffa_value sp_req_echo_denied_cmd(ffa_vm_id_t test_source);
 
 /**
  * Command to request SP to set notifications.
diff --git a/test/vmapi/ffa_secure_partitions/services/message_loop.c b/test/vmapi/ffa_secure_partitions/services/message_loop.c
index 8b5bf37..d907b90 100644
--- a/test/vmapi/ffa_secure_partitions/services/message_loop.c
+++ b/test/vmapi/ffa_secure_partitions/services/message_loop.c
@@ -37,8 +37,8 @@
 			res = sp_req_echo_cmd(ffa_sender(res), res.arg4,
 					      res.arg5, res.arg6, res.arg7);
 			break;
-		case SP_REQ_ECHO_BUSY_CMD:
-			res = sp_req_echo_busy_cmd(ffa_sender(res));
+		case SP_REQ_ECHO_DENIED_CMD:
+			res = sp_req_echo_denied_cmd(ffa_sender(res));
 			break;
 		case SP_NOTIF_SET_CMD:
 			res = sp_notif_set_cmd(
diff --git a/test/vmapi/ffa_secure_partitions/services/partition_services.c b/test/vmapi/ffa_secure_partitions/services/partition_services.c
index 9a3262b..3fa669a 100644
--- a/test/vmapi/ffa_secure_partitions/services/partition_services.c
+++ b/test/vmapi/ffa_secure_partitions/services/partition_services.c
@@ -46,7 +46,7 @@
 	return sp_success(own_id, test_source, 0);
 }
 
-struct ffa_value sp_req_echo_busy_cmd(ffa_vm_id_t test_source)
+struct ffa_value sp_req_echo_denied_cmd(ffa_vm_id_t test_source)
 {
 	ffa_vm_id_t own_id = hf_vm_get_id();
 	struct ffa_value res;
@@ -54,9 +54,9 @@
 	if (IS_SP_ID(test_source)) {
 		res = ffa_msg_send_direct_req(own_id, test_source, 0, 0, 0, 0,
 					      0);
-		EXPECT_FFA_ERROR(res, FFA_BUSY);
+		EXPECT_FFA_ERROR(res, FFA_DENIED);
 	} else {
-		res = sp_req_echo_busy_cmd_send(own_id, own_id + 1);
+		res = sp_req_echo_denied_cmd_send(own_id, own_id + 1);
 
 		EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP_32);
 		EXPECT_EQ(sp_resp(res), SP_SUCCESS);