1fcf5ef2aSThomas Huth /*
2fcf5ef2aSThomas Huth * QEMU support -- ARM Power Control specific functions.
3fcf5ef2aSThomas Huth *
4fcf5ef2aSThomas Huth * Copyright (c) 2016 Jean-Christophe Dubois
5fcf5ef2aSThomas Huth *
6fcf5ef2aSThomas Huth * This work is licensed under the terms of the GNU GPL, version 2 or later.
7fcf5ef2aSThomas Huth * See the COPYING file in the top-level directory.
8fcf5ef2aSThomas Huth *
9fcf5ef2aSThomas Huth */
10fcf5ef2aSThomas Huth
11fcf5ef2aSThomas Huth #include "qemu/osdep.h"
12fcf5ef2aSThomas Huth #include "cpu.h"
13fcf5ef2aSThomas Huth #include "cpu-qom.h"
14fcf5ef2aSThomas Huth #include "internals.h"
15fcf5ef2aSThomas Huth #include "arm-powerctl.h"
16fcf5ef2aSThomas Huth #include "qemu/log.h"
17062ba099SAlex Bennée #include "qemu/main-loop.h"
182b77ad4dSFabiano Rosas #include "sysemu/tcg.h"
19*e2d8cf9bSPhilippe Mathieu-Daudé #include "target/arm/multiprocessing.h"
20fcf5ef2aSThomas Huth
21fcf5ef2aSThomas Huth #ifndef DEBUG_ARM_POWERCTL
22fcf5ef2aSThomas Huth #define DEBUG_ARM_POWERCTL 0
23fcf5ef2aSThomas Huth #endif
24fcf5ef2aSThomas Huth
25fcf5ef2aSThomas Huth #define DPRINTF(fmt, args...) \
26fcf5ef2aSThomas Huth do { \
27fcf5ef2aSThomas Huth if (DEBUG_ARM_POWERCTL) { \
28fcf5ef2aSThomas Huth fprintf(stderr, "[ARM]%s: " fmt , __func__, ##args); \
29fcf5ef2aSThomas Huth } \
30fcf5ef2aSThomas Huth } while (0)
31fcf5ef2aSThomas Huth
arm_get_cpu_by_id(uint64_t id)32fcf5ef2aSThomas Huth CPUState *arm_get_cpu_by_id(uint64_t id)
33fcf5ef2aSThomas Huth {
34fcf5ef2aSThomas Huth CPUState *cpu;
35fcf5ef2aSThomas Huth
36fcf5ef2aSThomas Huth DPRINTF("cpu %" PRId64 "\n", id);
37fcf5ef2aSThomas Huth
38fcf5ef2aSThomas Huth CPU_FOREACH(cpu) {
39fcf5ef2aSThomas Huth ARMCPU *armcpu = ARM_CPU(cpu);
40fcf5ef2aSThomas Huth
41c4380f7bSRichard Henderson if (arm_cpu_mp_affinity(armcpu) == id) {
42fcf5ef2aSThomas Huth return cpu;
43fcf5ef2aSThomas Huth }
44fcf5ef2aSThomas Huth }
45fcf5ef2aSThomas Huth
46fcf5ef2aSThomas Huth qemu_log_mask(LOG_GUEST_ERROR,
47fcf5ef2aSThomas Huth "[ARM]%s: Requesting unknown CPU %" PRId64 "\n",
48fcf5ef2aSThomas Huth __func__, id);
49fcf5ef2aSThomas Huth
50fcf5ef2aSThomas Huth return NULL;
51fcf5ef2aSThomas Huth }
52fcf5ef2aSThomas Huth
53062ba099SAlex Bennée struct CpuOnInfo {
54062ba099SAlex Bennée uint64_t entry;
55062ba099SAlex Bennée uint64_t context_id;
56062ba099SAlex Bennée uint32_t target_el;
57062ba099SAlex Bennée bool target_aa64;
58062ba099SAlex Bennée };
59062ba099SAlex Bennée
60062ba099SAlex Bennée
arm_set_cpu_on_async_work(CPUState * target_cpu_state,run_on_cpu_data data)61062ba099SAlex Bennée static void arm_set_cpu_on_async_work(CPUState *target_cpu_state,
62062ba099SAlex Bennée run_on_cpu_data data)
63062ba099SAlex Bennée {
64062ba099SAlex Bennée ARMCPU *target_cpu = ARM_CPU(target_cpu_state);
65062ba099SAlex Bennée struct CpuOnInfo *info = (struct CpuOnInfo *) data.host_ptr;
66062ba099SAlex Bennée
67062ba099SAlex Bennée /* Initialize the cpu we are turning on */
68062ba099SAlex Bennée cpu_reset(target_cpu_state);
693a45f4f5SPeter Maydell arm_emulate_firmware_reset(target_cpu_state, info->target_el);
70062ba099SAlex Bennée target_cpu_state->halted = 0;
71062ba099SAlex Bennée
72062ba099SAlex Bennée /* We check if the started CPU is now at the correct level */
73062ba099SAlex Bennée assert(info->target_el == arm_current_el(&target_cpu->env));
74062ba099SAlex Bennée
75062ba099SAlex Bennée if (info->target_aa64) {
76062ba099SAlex Bennée target_cpu->env.xregs[0] = info->context_id;
77062ba099SAlex Bennée } else {
78062ba099SAlex Bennée target_cpu->env.regs[0] = info->context_id;
79062ba099SAlex Bennée }
80062ba099SAlex Bennée
812b77ad4dSFabiano Rosas if (tcg_enabled()) {
82c8fa6079SNiek Linnenbank /* CP15 update requires rebuilding hflags */
83c8fa6079SNiek Linnenbank arm_rebuild_hflags(&target_cpu->env);
842b77ad4dSFabiano Rosas }
85c8fa6079SNiek Linnenbank
86062ba099SAlex Bennée /* Start the new CPU at the requested address */
87062ba099SAlex Bennée cpu_set_pc(target_cpu_state, info->entry);
88062ba099SAlex Bennée
89062ba099SAlex Bennée g_free(info);
90062ba099SAlex Bennée
91062ba099SAlex Bennée /* Finally set the power status */
92195801d7SStefan Hajnoczi assert(bql_locked());
93062ba099SAlex Bennée target_cpu->power_state = PSCI_ON;
94062ba099SAlex Bennée }
95062ba099SAlex Bennée
arm_set_cpu_on(uint64_t cpuid,uint64_t entry,uint64_t context_id,uint32_t target_el,bool target_aa64)96fcf5ef2aSThomas Huth int arm_set_cpu_on(uint64_t cpuid, uint64_t entry, uint64_t context_id,
97fcf5ef2aSThomas Huth uint32_t target_el, bool target_aa64)
98fcf5ef2aSThomas Huth {
99fcf5ef2aSThomas Huth CPUState *target_cpu_state;
100fcf5ef2aSThomas Huth ARMCPU *target_cpu;
101062ba099SAlex Bennée struct CpuOnInfo *info;
102062ba099SAlex Bennée
103195801d7SStefan Hajnoczi assert(bql_locked());
104fcf5ef2aSThomas Huth
105fcf5ef2aSThomas Huth DPRINTF("cpu %" PRId64 " (EL %d, %s) @ 0x%" PRIx64 " with R0 = 0x%" PRIx64
106fcf5ef2aSThomas Huth "\n", cpuid, target_el, target_aa64 ? "aarch64" : "aarch32", entry,
107fcf5ef2aSThomas Huth context_id);
108fcf5ef2aSThomas Huth
109fcf5ef2aSThomas Huth /* requested EL level need to be in the 1 to 3 range */
110fcf5ef2aSThomas Huth assert((target_el > 0) && (target_el < 4));
111fcf5ef2aSThomas Huth
112fcf5ef2aSThomas Huth if (target_aa64 && (entry & 3)) {
113fcf5ef2aSThomas Huth /*
114fcf5ef2aSThomas Huth * if we are booting in AArch64 mode then "entry" needs to be 4 bytes
115fcf5ef2aSThomas Huth * aligned.
116fcf5ef2aSThomas Huth */
117fcf5ef2aSThomas Huth return QEMU_ARM_POWERCTL_INVALID_PARAM;
118fcf5ef2aSThomas Huth }
119fcf5ef2aSThomas Huth
120fcf5ef2aSThomas Huth /* Retrieve the cpu we are powering up */
121fcf5ef2aSThomas Huth target_cpu_state = arm_get_cpu_by_id(cpuid);
122fcf5ef2aSThomas Huth if (!target_cpu_state) {
123fcf5ef2aSThomas Huth /* The cpu was not found */
124fcf5ef2aSThomas Huth return QEMU_ARM_POWERCTL_INVALID_PARAM;
125fcf5ef2aSThomas Huth }
126fcf5ef2aSThomas Huth
127fcf5ef2aSThomas Huth target_cpu = ARM_CPU(target_cpu_state);
128062ba099SAlex Bennée if (target_cpu->power_state == PSCI_ON) {
129fcf5ef2aSThomas Huth qemu_log_mask(LOG_GUEST_ERROR,
130fcf5ef2aSThomas Huth "[ARM]%s: CPU %" PRId64 " is already on\n",
131fcf5ef2aSThomas Huth __func__, cpuid);
132fcf5ef2aSThomas Huth return QEMU_ARM_POWERCTL_ALREADY_ON;
133fcf5ef2aSThomas Huth }
134fcf5ef2aSThomas Huth
135fcf5ef2aSThomas Huth /*
136fcf5ef2aSThomas Huth * The newly brought CPU is requested to enter the exception level
137fcf5ef2aSThomas Huth * "target_el" and be in the requested mode (AArch64 or AArch32).
138fcf5ef2aSThomas Huth */
139fcf5ef2aSThomas Huth
140fcf5ef2aSThomas Huth if (((target_el == 3) && !arm_feature(&target_cpu->env, ARM_FEATURE_EL3)) ||
141fcf5ef2aSThomas Huth ((target_el == 2) && !arm_feature(&target_cpu->env, ARM_FEATURE_EL2))) {
142fcf5ef2aSThomas Huth /*
143fcf5ef2aSThomas Huth * The CPU does not support requested level
144fcf5ef2aSThomas Huth */
145fcf5ef2aSThomas Huth return QEMU_ARM_POWERCTL_INVALID_PARAM;
146fcf5ef2aSThomas Huth }
147fcf5ef2aSThomas Huth
148fcf5ef2aSThomas Huth if (!target_aa64 && arm_feature(&target_cpu->env, ARM_FEATURE_AARCH64)) {
149fcf5ef2aSThomas Huth /*
150fcf5ef2aSThomas Huth * For now we don't support booting an AArch64 CPU in AArch32 mode
151fcf5ef2aSThomas Huth * TODO: We should add this support later
152fcf5ef2aSThomas Huth */
153fcf5ef2aSThomas Huth qemu_log_mask(LOG_UNIMP,
154fcf5ef2aSThomas Huth "[ARM]%s: Starting AArch64 CPU %" PRId64
155fcf5ef2aSThomas Huth " in AArch32 mode is not supported yet\n",
156fcf5ef2aSThomas Huth __func__, cpuid);
157fcf5ef2aSThomas Huth return QEMU_ARM_POWERCTL_INVALID_PARAM;
158fcf5ef2aSThomas Huth }
159fcf5ef2aSThomas Huth
160fcf5ef2aSThomas Huth /*
161062ba099SAlex Bennée * If another CPU has powered the target on we are in the state
162062ba099SAlex Bennée * ON_PENDING and additional attempts to power on the CPU should
163062ba099SAlex Bennée * fail (see 6.6 Implementation CPU_ON/CPU_OFF races in the PSCI
164062ba099SAlex Bennée * spec)
165fcf5ef2aSThomas Huth */
166062ba099SAlex Bennée if (target_cpu->power_state == PSCI_ON_PENDING) {
167062ba099SAlex Bennée qemu_log_mask(LOG_GUEST_ERROR,
168062ba099SAlex Bennée "[ARM]%s: CPU %" PRId64 " is already powering on\n",
169062ba099SAlex Bennée __func__, cpuid);
170062ba099SAlex Bennée return QEMU_ARM_POWERCTL_ON_PENDING;
171fcf5ef2aSThomas Huth }
172fcf5ef2aSThomas Huth
173062ba099SAlex Bennée /* To avoid racing with a CPU we are just kicking off we do the
174062ba099SAlex Bennée * final bit of preparation for the work in the target CPUs
175062ba099SAlex Bennée * context.
176fcf5ef2aSThomas Huth */
177062ba099SAlex Bennée info = g_new(struct CpuOnInfo, 1);
178062ba099SAlex Bennée info->entry = entry;
179062ba099SAlex Bennée info->context_id = context_id;
180062ba099SAlex Bennée info->target_el = target_el;
181062ba099SAlex Bennée info->target_aa64 = target_aa64;
182fcf5ef2aSThomas Huth
183062ba099SAlex Bennée async_run_on_cpu(target_cpu_state, arm_set_cpu_on_async_work,
184062ba099SAlex Bennée RUN_ON_CPU_HOST_PTR(info));
185fcf5ef2aSThomas Huth
186fcf5ef2aSThomas Huth /* We are good to go */
187fcf5ef2aSThomas Huth return QEMU_ARM_POWERCTL_RET_SUCCESS;
188fcf5ef2aSThomas Huth }
189fcf5ef2aSThomas Huth
arm_set_cpu_on_and_reset_async_work(CPUState * target_cpu_state,run_on_cpu_data data)190ea824b97SPeter Maydell static void arm_set_cpu_on_and_reset_async_work(CPUState *target_cpu_state,
191ea824b97SPeter Maydell run_on_cpu_data data)
192ea824b97SPeter Maydell {
193ea824b97SPeter Maydell ARMCPU *target_cpu = ARM_CPU(target_cpu_state);
194ea824b97SPeter Maydell
195ea824b97SPeter Maydell /* Initialize the cpu we are turning on */
196ea824b97SPeter Maydell cpu_reset(target_cpu_state);
197ea824b97SPeter Maydell target_cpu_state->halted = 0;
198ea824b97SPeter Maydell
199ea824b97SPeter Maydell /* Finally set the power status */
200195801d7SStefan Hajnoczi assert(bql_locked());
201ea824b97SPeter Maydell target_cpu->power_state = PSCI_ON;
202ea824b97SPeter Maydell }
203ea824b97SPeter Maydell
arm_set_cpu_on_and_reset(uint64_t cpuid)204ea824b97SPeter Maydell int arm_set_cpu_on_and_reset(uint64_t cpuid)
205ea824b97SPeter Maydell {
206ea824b97SPeter Maydell CPUState *target_cpu_state;
207ea824b97SPeter Maydell ARMCPU *target_cpu;
208ea824b97SPeter Maydell
209195801d7SStefan Hajnoczi assert(bql_locked());
210ea824b97SPeter Maydell
211ea824b97SPeter Maydell /* Retrieve the cpu we are powering up */
212ea824b97SPeter Maydell target_cpu_state = arm_get_cpu_by_id(cpuid);
213ea824b97SPeter Maydell if (!target_cpu_state) {
214ea824b97SPeter Maydell /* The cpu was not found */
215ea824b97SPeter Maydell return QEMU_ARM_POWERCTL_INVALID_PARAM;
216ea824b97SPeter Maydell }
217ea824b97SPeter Maydell
218ea824b97SPeter Maydell target_cpu = ARM_CPU(target_cpu_state);
219ea824b97SPeter Maydell if (target_cpu->power_state == PSCI_ON) {
220ea824b97SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR,
221ea824b97SPeter Maydell "[ARM]%s: CPU %" PRId64 " is already on\n",
222ea824b97SPeter Maydell __func__, cpuid);
223ea824b97SPeter Maydell return QEMU_ARM_POWERCTL_ALREADY_ON;
224ea824b97SPeter Maydell }
225ea824b97SPeter Maydell
226ea824b97SPeter Maydell /*
227ea824b97SPeter Maydell * If another CPU has powered the target on we are in the state
228ea824b97SPeter Maydell * ON_PENDING and additional attempts to power on the CPU should
229ea824b97SPeter Maydell * fail (see 6.6 Implementation CPU_ON/CPU_OFF races in the PSCI
230ea824b97SPeter Maydell * spec)
231ea824b97SPeter Maydell */
232ea824b97SPeter Maydell if (target_cpu->power_state == PSCI_ON_PENDING) {
233ea824b97SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR,
234ea824b97SPeter Maydell "[ARM]%s: CPU %" PRId64 " is already powering on\n",
235ea824b97SPeter Maydell __func__, cpuid);
236ea824b97SPeter Maydell return QEMU_ARM_POWERCTL_ON_PENDING;
237ea824b97SPeter Maydell }
238ea824b97SPeter Maydell
239ea824b97SPeter Maydell async_run_on_cpu(target_cpu_state, arm_set_cpu_on_and_reset_async_work,
240ea824b97SPeter Maydell RUN_ON_CPU_NULL);
241ea824b97SPeter Maydell
242ea824b97SPeter Maydell /* We are good to go */
243ea824b97SPeter Maydell return QEMU_ARM_POWERCTL_RET_SUCCESS;
244ea824b97SPeter Maydell }
245ea824b97SPeter Maydell
arm_set_cpu_off_async_work(CPUState * target_cpu_state,run_on_cpu_data data)246062ba099SAlex Bennée static void arm_set_cpu_off_async_work(CPUState *target_cpu_state,
247062ba099SAlex Bennée run_on_cpu_data data)
248062ba099SAlex Bennée {
249062ba099SAlex Bennée ARMCPU *target_cpu = ARM_CPU(target_cpu_state);
250062ba099SAlex Bennée
251195801d7SStefan Hajnoczi assert(bql_locked());
252062ba099SAlex Bennée target_cpu->power_state = PSCI_OFF;
253062ba099SAlex Bennée target_cpu_state->halted = 1;
254062ba099SAlex Bennée target_cpu_state->exception_index = EXCP_HLT;
255062ba099SAlex Bennée }
256062ba099SAlex Bennée
arm_set_cpu_off(uint64_t cpuid)257fcf5ef2aSThomas Huth int arm_set_cpu_off(uint64_t cpuid)
258fcf5ef2aSThomas Huth {
259fcf5ef2aSThomas Huth CPUState *target_cpu_state;
260fcf5ef2aSThomas Huth ARMCPU *target_cpu;
261fcf5ef2aSThomas Huth
262195801d7SStefan Hajnoczi assert(bql_locked());
263062ba099SAlex Bennée
264fcf5ef2aSThomas Huth DPRINTF("cpu %" PRId64 "\n", cpuid);
265fcf5ef2aSThomas Huth
266fcf5ef2aSThomas Huth /* change to the cpu we are powering up */
267fcf5ef2aSThomas Huth target_cpu_state = arm_get_cpu_by_id(cpuid);
268fcf5ef2aSThomas Huth if (!target_cpu_state) {
269fcf5ef2aSThomas Huth return QEMU_ARM_POWERCTL_INVALID_PARAM;
270fcf5ef2aSThomas Huth }
271fcf5ef2aSThomas Huth target_cpu = ARM_CPU(target_cpu_state);
272062ba099SAlex Bennée if (target_cpu->power_state == PSCI_OFF) {
273fcf5ef2aSThomas Huth qemu_log_mask(LOG_GUEST_ERROR,
274fcf5ef2aSThomas Huth "[ARM]%s: CPU %" PRId64 " is already off\n",
275fcf5ef2aSThomas Huth __func__, cpuid);
276fcf5ef2aSThomas Huth return QEMU_ARM_POWERCTL_IS_OFF;
277fcf5ef2aSThomas Huth }
278fcf5ef2aSThomas Huth
279062ba099SAlex Bennée /* Queue work to run under the target vCPUs context */
280062ba099SAlex Bennée async_run_on_cpu(target_cpu_state, arm_set_cpu_off_async_work,
281062ba099SAlex Bennée RUN_ON_CPU_NULL);
282fcf5ef2aSThomas Huth
283fcf5ef2aSThomas Huth return QEMU_ARM_POWERCTL_RET_SUCCESS;
284fcf5ef2aSThomas Huth }
285fcf5ef2aSThomas Huth
arm_reset_cpu_async_work(CPUState * target_cpu_state,run_on_cpu_data data)286062ba099SAlex Bennée static void arm_reset_cpu_async_work(CPUState *target_cpu_state,
287062ba099SAlex Bennée run_on_cpu_data data)
288062ba099SAlex Bennée {
289062ba099SAlex Bennée /* Reset the cpu */
290062ba099SAlex Bennée cpu_reset(target_cpu_state);
291062ba099SAlex Bennée }
292062ba099SAlex Bennée
arm_reset_cpu(uint64_t cpuid)293fcf5ef2aSThomas Huth int arm_reset_cpu(uint64_t cpuid)
294fcf5ef2aSThomas Huth {
295fcf5ef2aSThomas Huth CPUState *target_cpu_state;
296fcf5ef2aSThomas Huth ARMCPU *target_cpu;
297fcf5ef2aSThomas Huth
298195801d7SStefan Hajnoczi assert(bql_locked());
299062ba099SAlex Bennée
300fcf5ef2aSThomas Huth DPRINTF("cpu %" PRId64 "\n", cpuid);
301fcf5ef2aSThomas Huth
302fcf5ef2aSThomas Huth /* change to the cpu we are resetting */
303fcf5ef2aSThomas Huth target_cpu_state = arm_get_cpu_by_id(cpuid);
304fcf5ef2aSThomas Huth if (!target_cpu_state) {
305fcf5ef2aSThomas Huth return QEMU_ARM_POWERCTL_INVALID_PARAM;
306fcf5ef2aSThomas Huth }
307fcf5ef2aSThomas Huth target_cpu = ARM_CPU(target_cpu_state);
308062ba099SAlex Bennée
309062ba099SAlex Bennée if (target_cpu->power_state == PSCI_OFF) {
310fcf5ef2aSThomas Huth qemu_log_mask(LOG_GUEST_ERROR,
311fcf5ef2aSThomas Huth "[ARM]%s: CPU %" PRId64 " is off\n",
312fcf5ef2aSThomas Huth __func__, cpuid);
313fcf5ef2aSThomas Huth return QEMU_ARM_POWERCTL_IS_OFF;
314fcf5ef2aSThomas Huth }
315fcf5ef2aSThomas Huth
316062ba099SAlex Bennée /* Queue work to run under the target vCPUs context */
317062ba099SAlex Bennée async_run_on_cpu(target_cpu_state, arm_reset_cpu_async_work,
318062ba099SAlex Bennée RUN_ON_CPU_NULL);
319fcf5ef2aSThomas Huth
320fcf5ef2aSThomas Huth return QEMU_ARM_POWERCTL_RET_SUCCESS;
321fcf5ef2aSThomas Huth }
322