FF-A: secondary EC cold boot

When the SPMC boots, all Secure Partitions are initialized
on their primary Execution Context. A Secure Partition calls
FFA_SECONDARY_EP_REGISTER at virtual FF-A instance from
its first EC passing the entry point address for secondary
ECs. A secondary EC is first resumed either upon invocation
of PSCI_CPU_ON from the NWd to which a SP is registered
(currently the first SP) or by ffa_run invocation.

Change-Id: Ic6050af16d4081ca31729744995fbb999b170e11
Signed-off-by: Max Shvetsov <maksims.svecovs@arm.com>
Signed-off-by: Olivier Deprez <olivier.deprez@arm.com>
diff --git a/src/api.c b/src/api.c
index 75a06b7..fdb395f 100644
--- a/src/api.c
+++ b/src/api.c
@@ -545,6 +545,8 @@
 static bool api_vcpu_prepare_run(const struct vcpu *current, struct vcpu *vcpu,
 				 struct ffa_value *run_ret)
 {
+	struct vcpu_locked vcpu_locked;
+	struct vm_locked vm_locked;
 	bool need_vm_lock;
 	bool ret;
 
@@ -556,14 +558,24 @@
 	 * dependencies in the common run case meaning the sensitive context
 	 * switch performance is consistent.
 	 */
-	sl_lock(&vcpu->lock);
+	vcpu_locked = vcpu_lock(vcpu);
+
+#if SECURE_WORLD == 1
+
+	if (vcpu_secondary_reset_and_start(vcpu_locked, vcpu->vm->secondary_ep,
+					   0)) {
+		dlog_verbose("%s secondary cold boot vmid %#x vcpu id %#x\n",
+			     __func__, vcpu->vm->id, current->cpu->id);
+	}
+
+#endif
 
 	/* The VM needs to be locked to deliver mailbox messages. */
 	need_vm_lock = vcpu->state == VCPU_STATE_BLOCKED_MAILBOX;
 	if (need_vm_lock) {
-		sl_unlock(&vcpu->lock);
-		sl_lock(&vcpu->vm->lock);
-		sl_lock(&vcpu->lock);
+		vcpu_unlock(&vcpu_locked);
+		vm_locked = vm_lock(vcpu->vm);
+		vcpu_locked = vcpu_lock(vcpu);
 	}
 
 	/*
@@ -671,9 +683,9 @@
 	ret = true;
 
 out:
-	sl_unlock(&vcpu->lock);
+	vcpu_unlock(&vcpu_locked);
 	if (need_vm_lock) {
-		sl_unlock(&vcpu->vm->lock);
+		vm_unlock(&vm_locked);
 	}
 
 	return ret;
@@ -2278,3 +2290,15 @@
 
 	return ret;
 }
+
+struct ffa_value api_ffa_secondary_ep_register(ipaddr_t entry_point,
+					       struct vcpu *current)
+{
+	struct vm_locked vm_locked;
+
+	vm_locked = vm_lock(current->vm);
+	vm_locked.vm->secondary_ep = entry_point;
+	vm_unlock(&vm_locked);
+
+	return (struct ffa_value){.func = FFA_SUCCESS_32};
+}
diff --git a/src/arch/aarch64/hypervisor/handler.c b/src/arch/aarch64/hypervisor/handler.c
index 1552e17..f7851d9 100644
--- a/src/arch/aarch64/hypervisor/handler.c
+++ b/src/arch/aarch64/hypervisor/handler.c
@@ -470,6 +470,10 @@
 			ffa_msg_send_sender(*args),
 			ffa_msg_send_receiver(*args), *args, current, next);
 		return true;
+	case FFA_SECONDARY_EP_REGISTER_32:
+		*args = api_ffa_secondary_ep_register(ipa_init(args->arg1),
+						      current);
+		return true;
 	}
 
 	return false;
diff --git a/src/arch/aarch64/hypervisor/psci_handler.c b/src/arch/aarch64/hypervisor/psci_handler.c
index 825ec9f..358c827 100644
--- a/src/arch/aarch64/hypervisor/psci_handler.c
+++ b/src/arch/aarch64/hypervisor/psci_handler.c
@@ -317,6 +317,8 @@
 			vcpu_id_to_index(target_cpu);
 		struct vm *vm = vcpu->vm;
 		struct vcpu *target_vcpu;
+		struct vcpu_locked vcpu_locked;
+		bool vcpu_was_off;
 
 		if (target_vcpu_index >= vm->vcpu_count) {
 			*ret = PSCI_ERROR_INVALID_PARAMETERS;
@@ -324,9 +326,12 @@
 		}
 
 		target_vcpu = vm_get_vcpu(vm, target_vcpu_index);
+		vcpu_locked = vcpu_lock(target_vcpu);
+		vcpu_was_off = vcpu_secondary_reset_and_start(
+			vcpu_locked, entry_point_address, context_id);
+		vcpu_unlock(&vcpu_locked);
 
-		if (vcpu_secondary_reset_and_start(
-			    target_vcpu, entry_point_address, context_id)) {
+		if (vcpu_was_off) {
 			/*
 			 * Tell the scheduler that it can start running the new
 			 * vCPU now.
diff --git a/src/load.c b/src/load.c
index 7accc65..d31b67a 100644
--- a/src/load.c
+++ b/src/load.c
@@ -365,6 +365,7 @@
 {
 	struct vm *vm;
 	struct vm_locked vm_locked;
+	struct vcpu_locked vcpu_locked;
 	struct vcpu *vcpu;
 	ipaddr_t secondary_entry;
 	bool ret;
@@ -561,8 +562,9 @@
 
 	vcpu = vm_get_vcpu(vm, 0);
 
+	vcpu_locked = vcpu_lock(vcpu);
 	if (has_fdt) {
-		vcpu_secondary_reset_and_start(vcpu, secondary_entry,
+		vcpu_secondary_reset_and_start(vcpu_locked, secondary_entry,
 					       pa_addr(fdt_addr));
 	} else {
 		/*
@@ -570,9 +572,12 @@
 		 * passed in register x0, which is what
 		 * vcpu_secondary_reset_and_start does in this case.
 		 */
-		vcpu_secondary_reset_and_start(vcpu, secondary_entry, mem_size);
+		vcpu_secondary_reset_and_start(vcpu_locked, secondary_entry,
+					       mem_size);
 	}
 
+	vcpu_unlock(&vcpu_locked);
+
 	ret = true;
 
 out:
diff --git a/src/vcpu.c b/src/vcpu.c
index 7fd2e75..b3b3f5a 100644
--- a/src/vcpu.c
+++ b/src/vcpu.c
@@ -114,16 +114,14 @@
  * Returns true if the secondary was reset and started, or false if it was
  * already on and so nothing was done.
  */
-bool vcpu_secondary_reset_and_start(struct vcpu *vcpu, ipaddr_t entry,
-				    uintreg_t arg)
+bool vcpu_secondary_reset_and_start(struct vcpu_locked vcpu_locked,
+				    ipaddr_t entry, uintreg_t arg)
 {
-	struct vcpu_locked vcpu_locked;
-	struct vm *vm = vcpu->vm;
+	struct vm *vm = vcpu_locked.vcpu->vm;
 	bool vcpu_was_off;
 
 	CHECK(vm->id != HF_PRIMARY_VM_ID);
 
-	vcpu_locked = vcpu_lock(vcpu);
 	vcpu_was_off = vcpu_is_off(vcpu_locked);
 	if (vcpu_was_off) {
 		/*
@@ -132,10 +130,9 @@
 		 * vCPU is defined as the index and does not match the ID of the
 		 * pCPU it is running on.
 		 */
-		arch_regs_reset(vcpu);
+		arch_regs_reset(vcpu_locked.vcpu);
 		vcpu_on(vcpu_locked, entry, arg);
 	}
-	vcpu_unlock(&vcpu_locked);
 
 	return vcpu_was_off;
 }
@@ -186,7 +183,7 @@
 
 void vcpu_reset(struct vcpu *vcpu)
 {
-	arch_cpu_init(vcpu->cpu, ipa_init(0));
+	arch_cpu_init(vcpu->cpu, vcpu->vm->secondary_ep);
 
 	/* Reset the registers to give a clean start for vCPU. */
 	arch_regs_reset(vcpu);