FF-A: Respond to PSCI_CPU_OFF from SPMD.

At run-time when a PSCI call is made from NWd, the corresponding SPMD PM
hooks gets called and is eventually routed to the SPMC.  This change
implements a generic "SPMD handler" processing PM events that may be
conveyed from SPMD to SPMC such as core suspend, core off.

Change-Id: Id288e26d9fbb8328acc71f5cf68c39e095a0e3da
Signed-off-by: Max Shvetsov <maksims.svecovs@arm.com>
Signed-off-by: Olivier Deprez <olivier.deprez@arm.com>
diff --git a/src/arch/aarch64/hypervisor/handler.c b/src/arch/aarch64/hypervisor/handler.c
index f7851d9..d923a68 100644
--- a/src/arch/aarch64/hypervisor/handler.c
+++ b/src/arch/aarch64/hypervisor/handler.c
@@ -260,6 +260,7 @@
 }
 
 #if SECURE_WORLD == 1
+
 static bool sp_boot_next(struct vcpu *current, struct vcpu **next,
 			 struct ffa_value *ffa_ret)
 {
@@ -302,6 +303,54 @@
 	vm_unlock(&current_vm_locked);
 	return ret;
 }
+
+/**
+ * Handle special direct messages from SPMD to SPMC. For now related to power
+ * management only.
+ */
+static bool spmd_handler(struct ffa_value *args, struct vcpu *current)
+{
+	ffa_vm_id_t sender = ffa_msg_send_sender(*args);
+	ffa_vm_id_t receiver = ffa_msg_send_receiver(*args);
+	ffa_vm_id_t current_vm_id = current->vm->id;
+
+	/*
+	 * Check if direct message request is originating from the SPMD and
+	 * directed to the SPMC.
+	 */
+	if (!(sender == HF_SPMD_VM_ID && receiver == HF_SPMC_VM_ID &&
+	      current_vm_id == HF_OTHER_WORLD_ID)) {
+		return false;
+	}
+
+	switch (args->arg3) {
+	case PSCI_CPU_OFF: {
+		struct vm *vm = vm_get_first_boot();
+		struct vcpu *vcpu = vm_get_vcpu(vm, vcpu_index(current));
+
+		/*
+		 * TODO: the PM event reached the SPMC. In a later iteration,
+		 * the PM event can be passed to the SP by resuming it.
+		 */
+		*args = (struct ffa_value){
+			.func = FFA_MSG_SEND_DIRECT_RESP_32,
+			.arg1 = ((uint64_t)HF_SPMC_VM_ID << 16) | HF_SPMD_VM_ID,
+			.arg2 = 0U};
+
+		dlog_verbose("%s cpu off notification cpuid %#x\n", __func__,
+			     vcpu->cpu->id);
+		cpu_off(vcpu->cpu);
+		break;
+	}
+	default:
+		dlog_verbose("%s message not handled %#x\n", __func__,
+			     args->arg3);
+		return false;
+	}
+
+	return true;
+}
+
 #endif
 
 /**
@@ -460,11 +509,17 @@
 					    (args->arg4 >> 16) & 0xffff,
 					    current);
 		return true;
-	case FFA_MSG_SEND_DIRECT_REQ_32:
+	case FFA_MSG_SEND_DIRECT_REQ_32: {
+#if SECURE_WORLD == 1
+		if (spmd_handler(args, current)) {
+			return true;
+		}
+#endif
 		*args = api_ffa_msg_send_direct_req(
 			ffa_msg_send_sender(*args),
 			ffa_msg_send_receiver(*args), *args, current, next);
 		return true;
+	}
 	case FFA_MSG_SEND_DIRECT_RESP_32:
 		*args = api_ffa_msg_send_direct_resp(
 			ffa_msg_send_sender(*args),