feat: power management messages subscription

Move PE cold and warm boot code paths to arch and platform specific
functions. Hypervisor and SPMC implement different flows for PE power
up/down by two flavors of the plat_psci_cpu_resume function.
SPMC leverages vcpu_secondary_reset_and_start when notified by SPMD of a
PE being brought out of reset.
Consume the power management field in partition manifests. For now it
permits entering a partition after warm boot (or restricting it e.g. for
UP SP partitions when queried). If the power management field is omitted
in the manifest, then it defaults to entering the first partition vCPU
on warm boots (legacy behavior maintained for MP SPs).
Provide an early normal world exit path for the SPMC when a partition
doesn't require handling warm reboot events (api_switch_to_other_world
then other_world_exit). Other minor refactorings:
plat_ffa_sri_init is only called by the SPMC on a PE boot up (the
Hypervisor shouldn't have to call it as done currently).

Signed-off-by: Olivier Deprez <olivier.deprez@arm.com>
Change-Id: I144341cd542387214697dfc4ae2f487dd260fe7b
diff --git a/inc/hf/arch/cpu.h b/inc/hf/arch/cpu.h
index 135b222..65ec8d5 100644
--- a/inc/hf/arch/cpu.h
+++ b/inc/hf/arch/cpu.h
@@ -65,4 +65,6 @@
 /**
  * Initialize and reset CPU-wide register values.
  */
-void arch_cpu_init(struct cpu *c, ipaddr_t entry_point);
+void arch_cpu_init(struct cpu *c);
+
+struct vcpu *arch_vcpu_resume(struct cpu *c);
diff --git a/inc/hf/arch/plat/psci.h b/inc/hf/arch/plat/psci.h
index 5cbef53..77e73cb 100644
--- a/inc/hf/arch/plat/psci.h
+++ b/inc/hf/arch/plat/psci.h
@@ -28,4 +28,4 @@
 void plat_psci_cpu_suspend(uint32_t power_state);
 
 /** Called when a CPU resumes from being off or suspended. */
-void plat_psci_cpu_resume(struct cpu *c, ipaddr_t entry_point);
+struct vcpu *plat_psci_cpu_resume(struct cpu *c);
diff --git a/src/arch/aarch64/hypervisor/cpu.c b/src/arch/aarch64/hypervisor/cpu.c
index 5e025b5..41d5284 100644
--- a/src/arch/aarch64/hypervisor/cpu.c
+++ b/src/arch/aarch64/hypervisor/cpu.c
@@ -245,10 +245,8 @@
 	isb();
 }
 
-void arch_cpu_init(struct cpu *c, ipaddr_t entry_point)
+void arch_cpu_init(struct cpu *c)
 {
-	plat_psci_cpu_resume(c, entry_point);
-
 	/*
 	 * Linux expects LORegions to be disabled, hence if the current system
 	 * supports them, Hafnium ensures that they are disabled.
@@ -267,3 +265,8 @@
 
 	plat_interrupts_controller_hw_init(c);
 }
+
+struct vcpu *arch_vcpu_resume(struct cpu *c)
+{
+	return plat_psci_cpu_resume(c);
+}
diff --git a/src/arch/aarch64/hypervisor/exceptions.S b/src/arch/aarch64/hypervisor/exceptions.S
index aa24b6d..f547bb6 100644
--- a/src/arch/aarch64/hypervisor/exceptions.S
+++ b/src/arch/aarch64/hypervisor/exceptions.S
@@ -499,6 +499,12 @@
 	add x20, x18, #SVE_CTX_VECTORS
 	sve_op_vectors ldr, x20
 .arch_extension nosve
+	b sve_skip_context_restore
+
+/* Called after cpu_main. */
+.globl other_world_exit
+other_world_exit:
+	mov x19, x0
 
 sve_skip_context_restore:
 	/*
diff --git a/src/arch/aarch64/hypervisor/hypervisor_entry.S b/src/arch/aarch64/hypervisor/hypervisor_entry.S
index a8dfffa..cec8b3f 100644
--- a/src/arch/aarch64/hypervisor/hypervisor_entry.S
+++ b/src/arch/aarch64/hypervisor/hypervisor_entry.S
@@ -197,6 +197,15 @@
 	/* Call into C code, x0 holds the CPU pointer. */
 	bl cpu_main
 
+#if SECURE_WORLD == 1
+	ldr x1, [x0, #VCPU_VM]
+	ldrh w1, [x1, #VM_ID]
+
+	/* Exit to normal world if VM is HF_OTHER_WORLD_ID. */
+	cmp w1, #0
+	beq other_world_exit
+#endif
+
 	/* Run the vCPU returned by cpu_main. */
 	bl vcpu_restore_all_and_run
 
diff --git a/src/arch/aarch64/plat/ffa/hypervisor.c b/src/arch/aarch64/plat/ffa/hypervisor.c
index 9b601f6..5bf3e0e 100644
--- a/src/arch/aarch64/plat/ffa/hypervisor.c
+++ b/src/arch/aarch64/plat/ffa/hypervisor.c
@@ -841,11 +841,6 @@
 	(void)cpu;
 }
 
-void plat_ffa_sri_init(struct cpu *cpu)
-{
-	(void)cpu;
-}
-
 bool plat_ffa_inject_notification_pending_interrupt(
 	struct vcpu_locked target_locked, struct vcpu *current,
 	struct vm_locked receiver_locked)
diff --git a/src/arch/aarch64/plat/psci/BUILD.gn b/src/arch/aarch64/plat/psci/BUILD.gn
index 8adb5d1..b044b4a 100644
--- a/src/arch/aarch64/plat/psci/BUILD.gn
+++ b/src/arch/aarch64/plat/psci/BUILD.gn
@@ -7,11 +7,17 @@
 import("//build/toolchain/platform.gni")
 
 source_set("hypervisor") {
-  public_configs = [ "//src/arch/${plat_arch}:config" ]
+  public_configs = [
+    "//src/arch/${plat_arch}:config",
+    "//src/arch/${plat_arch}:arch_config",
+  ]
   sources = [ "hypervisor.c" ]
 }
 
 source_set("spmc") {
-  public_configs = [ "//src/arch/${plat_arch}:config" ]
+  public_configs = [
+    "//src/arch/${plat_arch}:config",
+    "//src/arch/${plat_arch}:arch_config",
+  ]
   sources = [ "spmc.c" ]
 }
diff --git a/src/arch/aarch64/plat/psci/hypervisor.c b/src/arch/aarch64/plat/psci/hypervisor.c
index d647bf3..b7506d6 100644
--- a/src/arch/aarch64/plat/psci/hypervisor.c
+++ b/src/arch/aarch64/plat/psci/hypervisor.c
@@ -6,8 +6,11 @@
  * https://opensource.org/licenses/BSD-3-Clause.
  */
 
+#include "hf/arch/plat/psci.h"
+
 #include "hf/cpu.h"
 #include "hf/dlog.h"
+#include "hf/vm.h"
 
 #include "psci.h"
 
@@ -55,8 +58,19 @@
 	(void)power_state;
 }
 
-void plat_psci_cpu_resume(struct cpu *c, ipaddr_t entry_point)
+struct vcpu *plat_psci_cpu_resume(struct cpu *c)
 {
-	(void)c;
-	(void)entry_point;
+	struct vcpu *vcpu = vcpu_get_boot_vcpu();
+
+	vcpu = vm_get_vcpu(vcpu->vm, cpu_index(c));
+	vcpu->cpu = c;
+
+	arch_cpu_init(c);
+
+	/* Reset the registers to give a clean start for vCPU. */
+	arch_regs_reset(vcpu);
+
+	/* TODO: call plat_ffa_sri_init? */
+
+	return vcpu;
 }
diff --git a/src/arch/aarch64/plat/psci/spmc.c b/src/arch/aarch64/plat/psci/spmc.c
index d99e744..d48224d 100644
--- a/src/arch/aarch64/plat/psci/spmc.c
+++ b/src/arch/aarch64/plat/psci/spmc.c
@@ -6,10 +6,17 @@
  * https://opensource.org/licenses/BSD-3-Clause.
  */
 
+#include "hf/arch/plat/ffa.h"
+#include "hf/arch/plat/psci.h"
+
+#include "hf/api.h"
+#include "hf/check.h"
 #include "hf/cpu.h"
 #include "hf/dlog.h"
 #include "hf/vm.h"
 
+#include "vmapi/hf/types.h"
+
 #include "psci.h"
 
 void cpu_entry(struct cpu *c);
@@ -50,16 +57,49 @@
 	(void)power_state;
 }
 
-void plat_psci_cpu_resume(struct cpu *c, ipaddr_t entry_point)
+struct vcpu *plat_psci_cpu_resume(struct cpu *c)
 {
+	struct vcpu_locked vcpu_locked;
 	struct vcpu *vcpu = vcpu_get_boot_vcpu();
 	struct vm *vm = vcpu->vm;
-	struct vcpu_locked vcpu_locked;
+	struct vm *other_world_vm;
 
-	if (!cpu_on(c)) {
-		vcpu = vm_get_vcpu(vm, cpu_index(c));
-		vcpu_locked = vcpu_lock(vcpu);
-		vcpu_on(vcpu_locked, entry_point, 0LL);
-		vcpu_unlock(&vcpu_locked);
+	cpu_on(c);
+
+	arch_cpu_init(c);
+
+	/* Initialize SRI for running core. */
+	plat_ffa_sri_init(c);
+
+	vcpu = vm_get_vcpu(vm, (vm->vcpu_count == 1) ? 0 : cpu_index(c));
+	vcpu_locked = vcpu_lock(vcpu);
+
+	if (vcpu->rt_model != RTM_SP_INIT &&
+	    vm_power_management_cpu_on_requested(vm) == false) {
+		other_world_vm = vm_find(HF_OTHER_WORLD_ID);
+		CHECK(other_world_vm != NULL);
+		vcpu = api_switch_to_other_world(
+			vm_get_vcpu(other_world_vm, cpu_index(c)),
+			(struct ffa_value){.func = FFA_MSG_WAIT_32},
+			VCPU_STATE_WAITING);
+		goto exit;
 	}
+
+	vcpu->cpu = c;
+
+	vcpu_secondary_reset_and_start(vcpu_locked, vcpu->vm->secondary_ep,
+				       0ULL);
+
+	/* vCPU restarts in runtime model for SP initialization. */
+	vcpu->rt_model = RTM_SP_INIT;
+
+	/* Set the designated GP register with the core linear id. */
+	vcpu_set_phys_core_idx(vcpu);
+
+	vcpu_set_boot_info_gp_reg(vcpu);
+
+exit:
+	vcpu_unlock(&vcpu_locked);
+
+	return vcpu;
 }
diff --git a/src/arch/fake/hypervisor/cpu.c b/src/arch/fake/hypervisor/cpu.c
index 244bc5e..33b6807 100644
--- a/src/arch/fake/hypervisor/cpu.c
+++ b/src/arch/fake/hypervisor/cpu.c
@@ -60,10 +60,7 @@
 	r->arg[7] = v.arg7;
 }
 
-void arch_cpu_init(struct cpu *c, ipaddr_t entry_point)
+void arch_cpu_init(struct cpu *c)
 {
-	(void)c;
-	(void)entry_point;
-
 	plat_interrupts_controller_hw_init(c);
 }
diff --git a/src/arch/fake/hypervisor/ffa.c b/src/arch/fake/hypervisor/ffa.c
index 9b74a98..34e224a 100644
--- a/src/arch/fake/hypervisor/ffa.c
+++ b/src/arch/fake/hypervisor/ffa.c
@@ -382,11 +382,6 @@
 	(void)cpu;
 }
 
-void plat_ffa_sri_init(struct cpu *cpu)
-{
-	(void)cpu;
-}
-
 bool plat_ffa_inject_notification_pending_interrupt(
 	struct vcpu_locked target_locked, struct vcpu *current,
 	struct vm_locked receiver_locked)
diff --git a/src/main.c b/src/main.c
index 3c14329..6cb2e50 100644
--- a/src/main.c
+++ b/src/main.c
@@ -6,13 +6,7 @@
  * https://opensource.org/licenses/BSD-3-Clause.
  */
 
-#include "hf/arch/plat/ffa.h"
-
 #include "hf/cpu.h"
-#include "hf/dlog.h"
-#include "hf/vm.h"
-
-#include "vmapi/hf/ffa.h"
 
 /**
  * The entry point of CPUs when they are turned on. It is supposed to initialise
@@ -20,28 +14,5 @@
  */
 struct vcpu *cpu_main(struct cpu *c)
 {
-	struct vcpu *boot_vcpu = vcpu_get_boot_vcpu();
-	struct vcpu *vcpu;
-
-	/*
-	 * Get the pinned vCPU from which Hafnium booted.
-	 * This is the boot vCPU from PVM in the normal world and
-	 * the first booted Secure Partition in the secure world.
-	 */
-	vcpu = vm_get_vcpu(boot_vcpu->vm, cpu_index(c));
-
-	vcpu->cpu = c;
-
-	/* Reset the registers to give a clean start for vCPU. */
-	vcpu_reset(vcpu);
-
-	/* Set the designated GP with the physical core index. */
-	vcpu_set_phys_core_idx(vcpu);
-
-	/* Initialize SRI for running core. */
-	plat_ffa_sri_init(c);
-
-	vcpu_set_boot_info_gp_reg(vcpu);
-
-	return vcpu;
+	return arch_vcpu_resume(c);
 }
diff --git a/src/vcpu.c b/src/vcpu.c
index 1875974..7830ceb 100644
--- a/src/vcpu.c
+++ b/src/vcpu.c
@@ -200,7 +200,7 @@
 
 void vcpu_reset(struct vcpu *vcpu)
 {
-	arch_cpu_init(vcpu->cpu, vcpu->vm->secondary_ep);
+	arch_cpu_init(vcpu->cpu);
 
 	/* Reset the registers to give a clean start for vCPU. */
 	arch_regs_reset(vcpu);