FF-A: SPMC direct messaging interface.

Adaptation of direct messaging interface to the SPMC.

Change-Id: Ic27d19908d2071be57483a76da61c7f948b86bdb
Signed-off-by: Olivier Deprez <olivier.deprez@arm.com>
diff --git a/src/api.c b/src/api.c
index bdf60d3..a514bf6 100644
--- a/src/api.c
+++ b/src/api.c
@@ -123,6 +123,34 @@
 }
 
 /**
+ * Choose next vCPU to run to be the counterpart vCPU in the other
+ * world (run the normal world if currently running in the secure
+ * world). Set current vCPU state to the given vcpu_state parameter.
+ * Set FF-A return values to the target vCPU in the other world.
+ *
+ * Called in context of a direct message response from a secure
+ * partition to a VM.
+ */
+static struct vcpu *api_switch_to_other_world(struct vcpu *current,
+					      struct ffa_value other_world_ret,
+					      enum vcpu_state vcpu_state)
+{
+	struct vcpu *next = vcpu_get_other_world_counterpart(current);
+
+	CHECK(next != NULL);
+
+	/* Set the return value for the other world's VM. */
+	arch_regs_set_retval(&next->regs, other_world_ret);
+
+	/* Set the current vCPU state. */
+	sl_lock(&current->lock);
+	current->state = vcpu_state;
+	sl_unlock(&current->lock);
+
+	return next;
+}
+
+/**
  * Checks whether the given `to` VM's mailbox is currently busy, and optionally
  * registers the `from` VM to be notified when it becomes available.
  */
@@ -1601,26 +1629,15 @@
 					     struct vcpu **next)
 {
 	struct ffa_value ret = (struct ffa_value){.func = FFA_INTERRUPT_32};
-	ffa_vm_id_t current_vm_id = current->vm->id;
 	struct vm *receiver_vm;
 	struct vcpu *receiver_vcpu;
 	struct two_vcpu_locked vcpus_locked;
 
-	/* Only allow primary VM to send direct message requests. */
-	if (current_vm_id != HF_PRIMARY_VM_ID) {
+	if (!arch_other_world_is_direct_request_valid(current, sender_vm_id,
+						      receiver_vm_id)) {
 		return ffa_error(FFA_NOT_SUPPORTED);
 	}
 
-	/* Prevent sender_vm_id spoofing. */
-	if (current_vm_id != sender_vm_id) {
-		return ffa_error(FFA_INVALID_PARAMETERS);
-	}
-
-	/* Prevent a VM from sending messages to itself. */
-	if (current_vm_id == receiver_vm_id) {
-		return ffa_error(FFA_INVALID_PARAMETERS);
-	}
-
 	receiver_vm = vm_find(receiver_vm_id);
 	if (receiver_vm == NULL) {
 		return ffa_error(FFA_INVALID_PARAMETERS);
@@ -1696,7 +1713,7 @@
 	receiver_vcpu->cpu = current->cpu;
 	receiver_vcpu->state = VCPU_STATE_RUNNING;
 	receiver_vcpu->regs_available = false;
-	receiver_vcpu->direct_request_origin_vm_id = current_vm_id;
+	receiver_vcpu->direct_request_origin_vm_id = sender_vm_id;
 
 	arch_regs_set_retval(&receiver_vcpu->regs, (struct ffa_value){
 							   .func = args.func,
@@ -1735,61 +1752,67 @@
 					      struct vcpu *current,
 					      struct vcpu **next)
 {
-	ffa_vm_id_t current_vm_id = current->vm->id;
-	struct vcpu_locked vcpu_locked;
+	struct vcpu_locked current_locked;
 
-	/* Only allow secondary VMs to send direct message responses. */
-	if (current_vm_id == HF_PRIMARY_VM_ID) {
+	if (!arch_other_world_is_direct_response_valid(current, sender_vm_id,
+						       receiver_vm_id)) {
 		return ffa_error(FFA_NOT_SUPPORTED);
 	}
 
-	/* Prevent sender_vm_id spoofing. */
-	if (current_vm_id != sender_vm_id) {
-		return ffa_error(FFA_INVALID_PARAMETERS);
-	}
-
-	/* Prevent a VM from sending messages to itself. */
-	if (current_vm_id == receiver_vm_id) {
-		return ffa_error(FFA_INVALID_PARAMETERS);
-	}
-
-	vcpu_locked = vcpu_lock(current);
+	current_locked = vcpu_lock(current);
 
 	/*
 	 * Ensure the terminating FFA_MSG_SEND_DIRECT_REQ had a
 	 * defined originator.
 	 */
-	if (!is_ffa_direct_msg_request_ongoing(vcpu_locked)) {
+	if (!is_ffa_direct_msg_request_ongoing(current_locked)) {
 		/*
 		 * Sending direct response but direct request origin vCPU is
 		 * not set.
 		 */
-		vcpu_unlock(&vcpu_locked);
+		vcpu_unlock(&current_locked);
 		return ffa_error(FFA_DENIED);
 	}
 
 	if (current->direct_request_origin_vm_id != receiver_vm_id) {
-		vcpu_unlock(&vcpu_locked);
+		vcpu_unlock(&current_locked);
 		return ffa_error(FFA_DENIED);
 	}
 
 	/* Clear direct request origin for the caller. */
 	current->direct_request_origin_vm_id = HF_INVALID_VM_ID;
 
-	vcpu_unlock(&vcpu_locked);
+	vcpu_unlock(&current_locked);
 
-	*next = api_switch_to_primary(current,
-				      (struct ffa_value){
-					      .func = args.func,
-					      .arg1 = args.arg1,
-					      .arg2 = 0,
-					      .arg3 = args.arg3,
-					      .arg4 = args.arg4,
-					      .arg5 = args.arg5,
-					      .arg6 = args.arg6,
-					      .arg7 = args.arg7,
-				      },
-				      VCPU_STATE_BLOCKED_MAILBOX);
+	if (!vm_id_is_current_world(receiver_vm_id)) {
+		*next = api_switch_to_other_world(current,
+						  (struct ffa_value){
+							  .func = args.func,
+							  .arg1 = args.arg1,
+							  .arg2 = 0,
+							  .arg3 = args.arg3,
+							  .arg4 = args.arg4,
+							  .arg5 = args.arg5,
+							  .arg6 = args.arg6,
+							  .arg7 = args.arg7,
+						  },
+						  VCPU_STATE_BLOCKED_MAILBOX);
+	} else if (receiver_vm_id == HF_PRIMARY_VM_ID) {
+		*next = api_switch_to_primary(current,
+					      (struct ffa_value){
+						      .func = args.func,
+						      .arg1 = args.arg1,
+						      .arg2 = 0,
+						      .arg3 = args.arg3,
+						      .arg4 = args.arg4,
+						      .arg5 = args.arg5,
+						      .arg6 = args.arg6,
+						      .arg7 = args.arg7,
+					      },
+					      VCPU_STATE_BLOCKED_MAILBOX);
+	} else {
+		panic("Invalid direct message response invocation");
+	}
 
 	return (struct ffa_value){.func = FFA_INTERRUPT_32};
 }
diff --git a/src/arch/aarch64/hypervisor/handler.c b/src/arch/aarch64/hypervisor/handler.c
index 624899e..4d3a462 100644
--- a/src/arch/aarch64/hypervisor/handler.c
+++ b/src/arch/aarch64/hypervisor/handler.c
@@ -427,14 +427,6 @@
 
 #if SECURE_WORLD == 1
 
-static struct vcpu *get_other_world_vcpu(struct vcpu *current)
-{
-	struct vm *vm = vm_find(HF_OTHER_WORLD_ID);
-	ffa_vcpu_index_t current_cpu_index = cpu_index(current->cpu);
-
-	return vm_get_vcpu(vm, current_cpu_index);
-}
-
 /**
  * Called to switch to the other world and handle FF-A calls from it. Returns
  * when it is ready to run a secure partition again.
@@ -539,7 +531,8 @@
 		arch_regs_set_retval(&vcpu->regs, args);
 
 #if SECURE_WORLD == 1
-		struct vcpu *other_world_vcpu = get_other_world_vcpu(current());
+		struct vcpu *other_world_vcpu =
+			vcpu_get_other_world_counterpart(current());
 
 		if (*next == other_world_vcpu) {
 			/*
diff --git a/src/arch/aarch64/hypervisor/other_world.c b/src/arch/aarch64/hypervisor/other_world.c
index 39b5752..8868873 100644
--- a/src/arch/aarch64/hypervisor/other_world.c
+++ b/src/arch/aarch64/hypervisor/other_world.c
@@ -107,6 +107,77 @@
 #endif
 }
 
+/**
+ * Check validity of a FF-A direct message request.
+ */
+bool arch_other_world_is_direct_request_valid(struct vcpu *current,
+					      ffa_vm_id_t sender_vm_id,
+					      ffa_vm_id_t receiver_vm_id)
+{
+	ffa_vm_id_t current_vm_id = current->vm->id;
+
+#if SECURE_WORLD == 1
+
+	/*
+	 * The normal world can send direct message requests
+	 * via the Hypervisor to any SP.
+	 */
+	return sender_vm_id != receiver_vm_id &&
+	       current_vm_id == HF_HYPERVISOR_VM_ID &&
+	       vm_id_is_current_world(receiver_vm_id) &&
+	       !vm_id_is_current_world(sender_vm_id);
+
+#else
+
+	/*
+	 * The primary VM can send direct message request to
+	 * any other VM (but itself) or SP, but can't spoof
+	 * a different sender.
+	 */
+	return sender_vm_id != receiver_vm_id &&
+	       sender_vm_id == current_vm_id &&
+	       current_vm_id == HF_PRIMARY_VM_ID;
+
+#endif
+
+	return false;
+}
+
+/**
+ * Check validity of a FF-A direct message response.
+ */
+bool arch_other_world_is_direct_response_valid(struct vcpu *current,
+					       ffa_vm_id_t sender_vm_id,
+					       ffa_vm_id_t receiver_vm_id)
+{
+	ffa_vm_id_t current_vm_id = current->vm->id;
+
+#if SECURE_WORLD == 1
+
+	/*
+	 * Direct message responses emitted from a SP
+	 * target a VM in NWd.
+	 */
+	return sender_vm_id != receiver_vm_id &&
+	       sender_vm_id == current_vm_id &&
+	       vm_id_is_current_world(sender_vm_id) &&
+	       !vm_id_is_current_world(receiver_vm_id);
+
+#else
+
+	/*
+	 * Secondary VMs can send direct message responses to
+	 * the PVM, but can't spoof a different sender.
+	 */
+	return sender_vm_id != receiver_vm_id &&
+	       sender_vm_id == current_vm_id &&
+	       receiver_vm_id == HF_PRIMARY_VM_ID;
+
+#endif
+
+	return false;
+}
+
 struct ffa_value arch_other_world_call(struct ffa_value args)
 {
 	return smc_ffa_call(args);
diff --git a/src/arch/fake/hypervisor/other_world.c b/src/arch/fake/hypervisor/other_world.c
index 57df952..c41e1d6 100644
--- a/src/arch/fake/hypervisor/other_world.c
+++ b/src/arch/fake/hypervisor/other_world.c
@@ -17,3 +17,25 @@
 	dlog_error("Attempted to call TEE function %#x\n", args.func);
 	return ffa_error(FFA_NOT_SUPPORTED);
 }
+
+bool arch_other_world_is_direct_request_valid(struct vcpu *current,
+					      ffa_vm_id_t sender_vm_id,
+					      ffa_vm_id_t receiver_vm_id)
+{
+	(void)current;
+	(void)sender_vm_id;
+	(void)receiver_vm_id;
+
+	return true;
+}
+
+bool arch_other_world_is_direct_response_valid(struct vcpu *current,
+					       ffa_vm_id_t sender_vm_id,
+					       ffa_vm_id_t receiver_vm_id)
+{
+	(void)current;
+	(void)sender_vm_id;
+	(void)receiver_vm_id;
+
+	return true;
+}
diff --git a/src/manifest.c b/src/manifest.c
index 273d445..60b0fa3 100644
--- a/src/manifest.c
+++ b/src/manifest.c
@@ -617,7 +617,8 @@
 		ret_code = MANIFEST_ERROR_NOT_COMPATIBLE;
 	}
 
-	if (vm->sp.messaging_method != INDIRECT_MESSAGING) {
+	if (vm->sp.messaging_method != INDIRECT_MESSAGING &&
+	    vm->sp.messaging_method != DIRECT_MESSAGING) {
 		dlog_error("Messaging method %s: %x\n", error_string,
 			   vm->sp.messaging_method);
 		ret_code = MANIFEST_ERROR_NOT_COMPATIBLE;
diff --git a/src/vcpu.c b/src/vcpu.c
index 7f1746f..1e4d777 100644
--- a/src/vcpu.c
+++ b/src/vcpu.c
@@ -181,3 +181,15 @@
 
 	return resume;
 }
+
+/**
+ * Return the vCPU corresponding to the other world vCPU whose index
+ * matches current vCPU index.
+ */
+struct vcpu *vcpu_get_other_world_counterpart(struct vcpu *current)
+{
+	struct vm *vm = vm_find(HF_OTHER_WORLD_ID);
+	ffa_vcpu_index_t current_cpu_index = cpu_index(current->cpu);
+
+	return vm_get_vcpu(vm, current_cpu_index);
+}