SUSPEND (not tested)

Change-Id: Ibff4209091387d5f9c01b2dcc9a0ba2c1885bdeb
diff --git a/arch/arm64/kvm/hyp/nvhe/psci.c b/arch/arm64/kvm/hyp/nvhe/psci.c
index 5be1f0a..ad940c0 100644
--- a/arch/arm64/kvm/hyp/nvhe/psci.c
+++ b/arch/arm64/kvm/hyp/nvhe/psci.c
@@ -139,10 +139,42 @@
 	hyp_puts("CPU_OFF");
 	arm_smccc_1_1_smc(PSCI_0_2_FN_CPU_OFF, NULL);
 
-	/* XXX - do we want to panic? */
+	/* XXX - do we want to panic instead? */
+	nvhe_spin_lock(cpu_lock);
+	vcpu->arch.power_off = false;
+	nvhe_spin_unlock(cpu_lock);
 	return PSCI_RET_DENIED;
 }
 
+static int kvm_host_psci_cpu_suspend(unsigned long power_state,
+				     unsigned long pc, unsigned long r0)
+{
+	struct arm_smccc_res res;
+	nvhe_spinlock_t *cpu_lock;
+	struct kvm_vcpu *vcpu;
+
+	hyp_puts("CPU_SUSPEND");
+
+	cpu_lock = this_cpu_ptr(&kvm_psci_cpu_lock);
+	vcpu = this_cpu_ptr(&kvm_host_vcpu);
+
+	nvhe_spin_lock(cpu_lock);
+	vcpu->arch.reset_state.reset = true;
+	vcpu->arch.reset_state.pc = pc;
+	vcpu->arch.reset_state.r0 = r0;
+
+	/* Unlock here? There would be a race with CPU_ON... */
+	nvhe_spin_unlock(cpu_lock);
+
+	hyp_puts("ENTER");
+	arm_smccc_1_1_smc(PSCI_0_2_FN64_CPU_SUSPEND,
+			  power_state, kvm_cpu_start_pa(),
+			  this_cpu_ptr(&kvm_cpu_params)->this_phys_addr,
+			  &res);
+	hyp_puts("EXIT");
+	return res.a0;
+}
+
 static int kvm_host_psci_affinity_info(unsigned long target_affinity,
 				       unsigned long lowest_affinity_level)
 {
@@ -207,6 +239,13 @@
 		return kvm_host_psci_cpu_on(smccc_get_arg1(host_vcpu),
 					    smccc_get_arg2(host_vcpu),
 					    smccc_get_arg3(host_vcpu));
+	case PSCI_0_2_FN_CPU_SUSPEND:
+		kvm_psci_narrow_to_32bit(host_vcpu);
+		fallthrough;
+	case PSCI_0_2_FN64_CPU_SUSPEND:
+		return kvm_host_psci_cpu_suspend(smccc_get_arg1(host_vcpu),
+						 smccc_get_arg2(host_vcpu),
+						 smccc_get_arg3(host_vcpu));
 	case PSCI_0_2_FN_SYSTEM_OFF:
 		kvm_host_psci_system_off();
 		unreachable();
diff --git a/drivers/firmware/psci/psci.c b/drivers/firmware/psci/psci.c
index 0ceba72..d808100 100644
--- a/drivers/firmware/psci/psci.c
+++ b/drivers/firmware/psci/psci.c
@@ -166,7 +166,9 @@
 	u32 fn;
 
 	fn = psci_function_id[PSCI_FN_CPU_SUSPEND];
+	pr_info("CPU_SUSPEND %lx...\n", smp_processor_id());
 	err = invoke_psci_fn(fn, state, entry_point, 0);
+	pr_info("CPU_SUSPEND %lx => %d\n", err);
 	return psci_to_linux_errno(err);
 }