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 #ifndef DEBUG_ARM_POWERCTL
18 #define DEBUG_ARM_POWERCTL 0
21 #define DPRINTF(fmt, args...) \
23 if (DEBUG_ARM_POWERCTL) { \
24 fprintf(stderr, "[ARM]%s: " fmt , __func__, ##args); \
28 CPUState
*arm_get_cpu_by_id(uint64_t id
)
32 DPRINTF("cpu %" PRId64
"\n", id
);
35 ARMCPU
*armcpu
= ARM_CPU(cpu
);
37 if (armcpu
->mp_affinity
== id
) {
42 qemu_log_mask(LOG_GUEST_ERROR
,
43 "[ARM]%s: Requesting unknown CPU %" PRId64
"\n",
49 int arm_set_cpu_on(uint64_t cpuid
, uint64_t entry
, uint64_t context_id
,
50 uint32_t target_el
, bool target_aa64
)
52 CPUState
*target_cpu_state
;
55 DPRINTF("cpu %" PRId64
" (EL %d, %s) @ 0x%" PRIx64
" with R0 = 0x%" PRIx64
56 "\n", cpuid
, target_el
, target_aa64
? "aarch64" : "aarch32", entry
,
59 /* requested EL level need to be in the 1 to 3 range */
60 assert((target_el
> 0) && (target_el
< 4));
62 if (target_aa64
&& (entry
& 3)) {
64 * if we are booting in AArch64 mode then "entry" needs to be 4 bytes
67 return QEMU_ARM_POWERCTL_INVALID_PARAM
;
70 /* Retrieve the cpu we are powering up */
71 target_cpu_state
= arm_get_cpu_by_id(cpuid
);
72 if (!target_cpu_state
) {
73 /* The cpu was not found */
74 return QEMU_ARM_POWERCTL_INVALID_PARAM
;
77 target_cpu
= ARM_CPU(target_cpu_state
);
78 if (!target_cpu
->powered_off
) {
79 qemu_log_mask(LOG_GUEST_ERROR
,
80 "[ARM]%s: CPU %" PRId64
" is already on\n",
82 return QEMU_ARM_POWERCTL_ALREADY_ON
;
86 * The newly brought CPU is requested to enter the exception level
87 * "target_el" and be in the requested mode (AArch64 or AArch32).
90 if (((target_el
== 3) && !arm_feature(&target_cpu
->env
, ARM_FEATURE_EL3
)) ||
91 ((target_el
== 2) && !arm_feature(&target_cpu
->env
, ARM_FEATURE_EL2
))) {
93 * The CPU does not support requested level
95 return QEMU_ARM_POWERCTL_INVALID_PARAM
;
98 if (!target_aa64
&& arm_feature(&target_cpu
->env
, ARM_FEATURE_AARCH64
)) {
100 * For now we don't support booting an AArch64 CPU in AArch32 mode
101 * TODO: We should add this support later
103 qemu_log_mask(LOG_UNIMP
,
104 "[ARM]%s: Starting AArch64 CPU %" PRId64
105 " in AArch32 mode is not supported yet\n",
107 return QEMU_ARM_POWERCTL_INVALID_PARAM
;
110 /* Initialize the cpu we are turning on */
111 cpu_reset(target_cpu_state
);
112 target_cpu
->powered_off
= false;
113 target_cpu_state
->halted
= 0;
116 if ((target_el
< 3) && arm_feature(&target_cpu
->env
, ARM_FEATURE_EL3
)) {
118 * As target mode is AArch64, we need to set lower
119 * exception level (the requested level 2) to AArch64
121 target_cpu
->env
.cp15
.scr_el3
|= SCR_RW
;
124 if ((target_el
< 2) && arm_feature(&target_cpu
->env
, ARM_FEATURE_EL2
)) {
126 * As target mode is AArch64, we need to set lower
127 * exception level (the requested level 1) to AArch64
129 target_cpu
->env
.cp15
.hcr_el2
|= HCR_RW
;
132 target_cpu
->env
.pstate
= aarch64_pstate_mode(target_el
, true);
134 /* We are requested to boot in AArch32 mode */
135 static uint32_t mode_for_el
[] = { 0,
140 cpsr_write(&target_cpu
->env
, mode_for_el
[target_el
], CPSR_M
,
144 if (target_el
== 3) {
145 /* Processor is in secure mode */
146 target_cpu
->env
.cp15
.scr_el3
&= ~SCR_NS
;
148 /* Processor is not in secure mode */
149 target_cpu
->env
.cp15
.scr_el3
|= SCR_NS
;
152 /* We check if the started CPU is now at the correct level */
153 assert(target_el
== arm_current_el(&target_cpu
->env
));
156 target_cpu
->env
.xregs
[0] = context_id
;
157 target_cpu
->env
.thumb
= false;
159 target_cpu
->env
.regs
[0] = context_id
;
160 target_cpu
->env
.thumb
= entry
& 1;
164 /* Start the new CPU at the requested address */
165 cpu_set_pc(target_cpu_state
, entry
);
167 /* We are good to go */
168 return QEMU_ARM_POWERCTL_RET_SUCCESS
;
171 int arm_set_cpu_off(uint64_t cpuid
)
173 CPUState
*target_cpu_state
;
176 DPRINTF("cpu %" PRId64
"\n", cpuid
);
178 /* change to the cpu we are powering up */
179 target_cpu_state
= arm_get_cpu_by_id(cpuid
);
180 if (!target_cpu_state
) {
181 return QEMU_ARM_POWERCTL_INVALID_PARAM
;
183 target_cpu
= ARM_CPU(target_cpu_state
);
184 if (target_cpu
->powered_off
) {
185 qemu_log_mask(LOG_GUEST_ERROR
,
186 "[ARM]%s: CPU %" PRId64
" is already off\n",
188 return QEMU_ARM_POWERCTL_IS_OFF
;
191 target_cpu
->powered_off
= true;
192 target_cpu_state
->halted
= 1;
193 target_cpu_state
->exception_index
= EXCP_HLT
;
194 cpu_loop_exit(target_cpu_state
);
197 return QEMU_ARM_POWERCTL_RET_SUCCESS
;
200 int arm_reset_cpu(uint64_t cpuid
)
202 CPUState
*target_cpu_state
;
205 DPRINTF("cpu %" PRId64
"\n", cpuid
);
207 /* change to the cpu we are resetting */
208 target_cpu_state
= arm_get_cpu_by_id(cpuid
);
209 if (!target_cpu_state
) {
210 return QEMU_ARM_POWERCTL_INVALID_PARAM
;
212 target_cpu
= ARM_CPU(target_cpu_state
);
213 if (target_cpu
->powered_off
) {
214 qemu_log_mask(LOG_GUEST_ERROR
,
215 "[ARM]%s: CPU %" PRId64
" is off\n",
217 return QEMU_ARM_POWERCTL_IS_OFF
;
221 cpu_reset(target_cpu_state
);
223 return QEMU_ARM_POWERCTL_RET_SUCCESS
;