1 /*
2  * Copyright 2018 The Hafnium Authors.
3  *
4  * Use of this source code is governed by a BSD-style
5  * license that can be found in the LICENSE file or at
6  * https://opensource.org/licenses/BSD-3-Clause.
7  */
8 
9 #include "hf/arch/vm/power_mgmt.h"
10 
11 #include "hf/static_assert.h"
12 
13 #include "vmapi/hf/call.h"
14 
15 #include "psci.h"
16 #include "smc.h"
17 
18 /**
19  * Starts the CPU with the given ID. It will set the stack pointer according to
20  * the provided `state` and jump to the entry point with the given argument
21  * specified in it.
22  *
23  * Note: The caller of this function must guarantee that the contents of `state`
24  * do not change until the new CPU has branched to the given entry point, and
25  * that it was written-back to memory (that it is not waiting in a data cache)
26  * because the new CPU is started with caching disabled.
27  */
arch_cpu_start(uintptr_t id,struct arch_cpu_start_state * state)28 bool arch_cpu_start(uintptr_t id, struct arch_cpu_start_state *state)
29 {
30 	void vm_cpu_entry(uintptr_t arg);
31 	struct ffa_value smc_res;
32 
33 	/* Try to start the CPU. */
34 	smc_res = smc64(PSCI_CPU_ON, id, (uintptr_t)&vm_cpu_entry,
35 			(uintptr_t)state, 0, 0, 0, SMCCC_CALLER_HYPERVISOR);
36 
37 	return smc_res.func == PSCI_RETURN_SUCCESS;
38 }
39 
40 /**
41  * Stops the current CPU.
42  */
arch_cpu_stop(void)43 noreturn void arch_cpu_stop(void)
44 {
45 	smc32(PSCI_CPU_OFF, 0, 0, 0, 0, 0, 0, SMCCC_CALLER_HYPERVISOR);
46 	for (;;) {
47 		/* This should never be reached. */
48 	}
49 }
50 
51 static_assert(POWER_STATUS_ON == PSCI_RETURN_ON,
52 	      "power_status enum values must match PSCI return values.");
53 static_assert(POWER_STATUS_OFF == PSCI_RETURN_OFF,
54 	      "power_status enum values must match PSCI return values.");
55 static_assert(POWER_STATUS_ON_PENDING == PSCI_RETURN_ON_PENDING,
56 	      "power_status enum values must match PSCI return values.");
57 
58 /**
59  * Returns the power status of the given CPU.
60  */
arch_cpu_status(cpu_id_t cpu_id)61 enum power_status arch_cpu_status(cpu_id_t cpu_id)
62 {
63 	uint32_t lowest_affinity_level = 0;
64 	struct ffa_value smc_res;
65 
66 	/*
67 	 * This works because the power_status enum values happen to be the same
68 	 * as the PSCI_RETURN_* values. The static_asserts above validate that
69 	 * this is the case.
70 	 */
71 	smc_res = smc32(PSCI_AFFINITY_INFO, cpu_id, lowest_affinity_level, 0, 0,
72 			0, 0, SMCCC_CALLER_HYPERVISOR);
73 	return smc_res.func;
74 }
75 
76 /**
77  * Shuts down the system or exits emulation.
78  */
arch_power_off(void)79 noreturn void arch_power_off(void)
80 {
81 	smc32(PSCI_SYSTEM_OFF, 0, 0, 0, 0, 0, 0, SMCCC_CALLER_HYPERVISOR);
82 	for (;;) {
83 		/* This should never be reached. */
84 	}
85 }
86 
87 /**
88  * Restarts the system.
89  */
arch_reboot(void)90 noreturn void arch_reboot(void)
91 {
92 	smc32(PSCI_SYSTEM_RESET, 0, 0, 0, 0, 0, 0, SMCCC_CALLER_HYPERVISOR);
93 	for (;;) {
94 		/* This should never be reached. */
95 	}
96 }
97