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