2 * QEMU support -- ARM Power Control specific functions.
4 * Copyright (c) 2016 Jean-Christophe Dubois
6 * This work is licensed under the terms of the GNU GPL, version 2 or later.
7 * See the COPYING file in the top-level directory.
11 #include "qemu/osdep.h"
14 #include "internals.h"
15 #include "arm-powerctl.h"
17 #include "qemu/main-loop.h"
18 #include "sysemu/tcg.h"
20 #ifndef DEBUG_ARM_POWERCTL
21 #define DEBUG_ARM_POWERCTL 0
24 #define DPRINTF(fmt, args...) \
26 if (DEBUG_ARM_POWERCTL) { \
27 fprintf(stderr, "[ARM]%s: " fmt , __func__, ##args); \
31 CPUState *arm_get_cpu_by_id(uint64_t id)
35 DPRINTF("cpu %" PRId64 "\n", id);
38 ARMCPU *armcpu = ARM_CPU(cpu);
40 if (armcpu->mp_affinity == id) {
45 qemu_log_mask(LOG_GUEST_ERROR,
46 "[ARM]%s: Requesting unknown CPU %" PRId64 "\n",
60 static void arm_set_cpu_on_async_work(CPUState *target_cpu_state,
63 ARMCPU *target_cpu = ARM_CPU(target_cpu_state);
64 struct CpuOnInfo *info = (struct CpuOnInfo *) data.host_ptr;
66 /* Initialize the cpu we are turning on */
67 cpu_reset(target_cpu_state);
68 arm_emulate_firmware_reset(target_cpu_state, info->target_el);
69 target_cpu_state->halted = 0;
71 /* We check if the started CPU is now at the correct level */
72 assert(info->target_el == arm_current_el(&target_cpu->env));
74 if (info->target_aa64) {
75 target_cpu->env.xregs[0] = info->context_id;
77 target_cpu->env.regs[0] = info->context_id;
81 /* CP15 update requires rebuilding hflags */
82 arm_rebuild_hflags(&target_cpu->env);
85 /* Start the new CPU at the requested address */
86 cpu_set_pc(target_cpu_state, info->entry);
90 /* Finally set the power status */
91 assert(qemu_mutex_iothread_locked());
92 target_cpu->power_state = PSCI_ON;
95 int arm_set_cpu_on(uint64_t cpuid, uint64_t entry, uint64_t context_id,
96 uint32_t target_el, bool target_aa64)
98 CPUState *target_cpu_state;
100 struct CpuOnInfo *info;
102 assert(qemu_mutex_iothread_locked());
104 DPRINTF("cpu %" PRId64 " (EL %d, %s) @ 0x%" PRIx64 " with R0 = 0x%" PRIx64
105 "\n", cpuid, target_el, target_aa64 ? "aarch64" : "aarch32", entry,
108 /* requested EL level need to be in the 1 to 3 range */
109 assert((target_el > 0) && (target_el < 4));
111 if (target_aa64 && (entry & 3)) {
113 * if we are booting in AArch64 mode then "entry" needs to be 4 bytes
116 return QEMU_ARM_POWERCTL_INVALID_PARAM;
119 /* Retrieve the cpu we are powering up */
120 target_cpu_state = arm_get_cpu_by_id(cpuid);
121 if (!target_cpu_state) {
122 /* The cpu was not found */
123 return QEMU_ARM_POWERCTL_INVALID_PARAM;
126 target_cpu = ARM_CPU(target_cpu_state);
127 if (target_cpu->power_state == PSCI_ON) {
128 qemu_log_mask(LOG_GUEST_ERROR,
129 "[ARM]%s: CPU %" PRId64 " is already on\n",
131 return QEMU_ARM_POWERCTL_ALREADY_ON;
135 * The newly brought CPU is requested to enter the exception level
136 * "target_el" and be in the requested mode (AArch64 or AArch32).
139 if (((target_el == 3) && !arm_feature(&target_cpu->env, ARM_FEATURE_EL3)) ||
140 ((target_el == 2) && !arm_feature(&target_cpu->env, ARM_FEATURE_EL2))) {
142 * The CPU does not support requested level
144 return QEMU_ARM_POWERCTL_INVALID_PARAM;
147 if (!target_aa64 && arm_feature(&target_cpu->env, ARM_FEATURE_AARCH64)) {
149 * For now we don't support booting an AArch64 CPU in AArch32 mode
150 * TODO: We should add this support later
152 qemu_log_mask(LOG_UNIMP,
153 "[ARM]%s: Starting AArch64 CPU %" PRId64
154 " in AArch32 mode is not supported yet\n",
156 return QEMU_ARM_POWERCTL_INVALID_PARAM;
160 * If another CPU has powered the target on we are in the state
161 * ON_PENDING and additional attempts to power on the CPU should
162 * fail (see 6.6 Implementation CPU_ON/CPU_OFF races in the PSCI
165 if (target_cpu->power_state == PSCI_ON_PENDING) {
166 qemu_log_mask(LOG_GUEST_ERROR,
167 "[ARM]%s: CPU %" PRId64 " is already powering on\n",
169 return QEMU_ARM_POWERCTL_ON_PENDING;
172 /* To avoid racing with a CPU we are just kicking off we do the
173 * final bit of preparation for the work in the target CPUs
176 info = g_new(struct CpuOnInfo, 1);
178 info->context_id = context_id;
179 info->target_el = target_el;
180 info->target_aa64 = target_aa64;
182 async_run_on_cpu(target_cpu_state, arm_set_cpu_on_async_work,
183 RUN_ON_CPU_HOST_PTR(info));
185 /* We are good to go */
186 return QEMU_ARM_POWERCTL_RET_SUCCESS;
189 static void arm_set_cpu_on_and_reset_async_work(CPUState *target_cpu_state,
190 run_on_cpu_data data)
192 ARMCPU *target_cpu = ARM_CPU(target_cpu_state);
194 /* Initialize the cpu we are turning on */
195 cpu_reset(target_cpu_state);
196 target_cpu_state->halted = 0;
198 /* Finally set the power status */
199 assert(qemu_mutex_iothread_locked());
200 target_cpu->power_state = PSCI_ON;
203 int arm_set_cpu_on_and_reset(uint64_t cpuid)
205 CPUState *target_cpu_state;
208 assert(qemu_mutex_iothread_locked());
210 /* Retrieve the cpu we are powering up */
211 target_cpu_state = arm_get_cpu_by_id(cpuid);
212 if (!target_cpu_state) {
213 /* The cpu was not found */
214 return QEMU_ARM_POWERCTL_INVALID_PARAM;
217 target_cpu = ARM_CPU(target_cpu_state);
218 if (target_cpu->power_state == PSCI_ON) {
219 qemu_log_mask(LOG_GUEST_ERROR,
220 "[ARM]%s: CPU %" PRId64 " is already on\n",
222 return QEMU_ARM_POWERCTL_ALREADY_ON;
226 * If another CPU has powered the target on we are in the state
227 * ON_PENDING and additional attempts to power on the CPU should
228 * fail (see 6.6 Implementation CPU_ON/CPU_OFF races in the PSCI
231 if (target_cpu->power_state == PSCI_ON_PENDING) {
232 qemu_log_mask(LOG_GUEST_ERROR,
233 "[ARM]%s: CPU %" PRId64 " is already powering on\n",
235 return QEMU_ARM_POWERCTL_ON_PENDING;
238 async_run_on_cpu(target_cpu_state, arm_set_cpu_on_and_reset_async_work,
241 /* We are good to go */
242 return QEMU_ARM_POWERCTL_RET_SUCCESS;
245 static void arm_set_cpu_off_async_work(CPUState *target_cpu_state,
246 run_on_cpu_data data)
248 ARMCPU *target_cpu = ARM_CPU(target_cpu_state);
250 assert(qemu_mutex_iothread_locked());
251 target_cpu->power_state = PSCI_OFF;
252 target_cpu_state->halted = 1;
253 target_cpu_state->exception_index = EXCP_HLT;
256 int arm_set_cpu_off(uint64_t cpuid)
258 CPUState *target_cpu_state;
261 assert(qemu_mutex_iothread_locked());
263 DPRINTF("cpu %" PRId64 "\n", cpuid);
265 /* change to the cpu we are powering up */
266 target_cpu_state = arm_get_cpu_by_id(cpuid);
267 if (!target_cpu_state) {
268 return QEMU_ARM_POWERCTL_INVALID_PARAM;
270 target_cpu = ARM_CPU(target_cpu_state);
271 if (target_cpu->power_state == PSCI_OFF) {
272 qemu_log_mask(LOG_GUEST_ERROR,
273 "[ARM]%s: CPU %" PRId64 " is already off\n",
275 return QEMU_ARM_POWERCTL_IS_OFF;
278 /* Queue work to run under the target vCPUs context */
279 async_run_on_cpu(target_cpu_state, arm_set_cpu_off_async_work,
282 return QEMU_ARM_POWERCTL_RET_SUCCESS;
285 static void arm_reset_cpu_async_work(CPUState *target_cpu_state,
286 run_on_cpu_data data)
289 cpu_reset(target_cpu_state);
292 int arm_reset_cpu(uint64_t cpuid)
294 CPUState *target_cpu_state;
297 assert(qemu_mutex_iothread_locked());
299 DPRINTF("cpu %" PRId64 "\n", cpuid);
301 /* change to the cpu we are resetting */
302 target_cpu_state = arm_get_cpu_by_id(cpuid);
303 if (!target_cpu_state) {
304 return QEMU_ARM_POWERCTL_INVALID_PARAM;
306 target_cpu = ARM_CPU(target_cpu_state);
308 if (target_cpu->power_state == PSCI_OFF) {
309 qemu_log_mask(LOG_GUEST_ERROR,
310 "[ARM]%s: CPU %" PRId64 " is off\n",
312 return QEMU_ARM_POWERCTL_IS_OFF;
315 /* Queue work to run under the target vCPUs context */
316 async_run_on_cpu(target_cpu_state, arm_reset_cpu_async_work,
319 return QEMU_ARM_POWERCTL_RET_SUCCESS;