tcg: Rearrange tb_link_page() to avoid forward declaration
[qemu/cris-port.git] / target-arm / arm-powerctl.c
blobcb9919b465a981f304e18db6ad83202cbbcc14f7
1 /*
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.
9 */
11 #include "qemu/osdep.h"
12 #include <cpu.h>
13 #include <cpu-qom.h>
14 #include "internals.h"
15 #include "arm-powerctl.h"
17 #ifndef DEBUG_ARM_POWERCTL
18 #define DEBUG_ARM_POWERCTL 0
19 #endif
21 #define DPRINTF(fmt, args...) \
22 do { \
23 if (DEBUG_ARM_POWERCTL) { \
24 fprintf(stderr, "[ARM]%s: " fmt , __func__, ##args); \
25 } \
26 } while (0)
28 CPUState *arm_get_cpu_by_id(uint64_t id)
30 CPUState *cpu;
32 DPRINTF("cpu %" PRId64 "\n", id);
34 CPU_FOREACH(cpu) {
35 ARMCPU *armcpu = ARM_CPU(cpu);
37 if (armcpu->mp_affinity == id) {
38 return cpu;
42 qemu_log_mask(LOG_GUEST_ERROR,
43 "[ARM]%s: Requesting unknown CPU %" PRId64 "\n",
44 __func__, id);
46 return NULL;
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;
53 ARMCPU *target_cpu;
55 DPRINTF("cpu %" PRId64 " (EL %d, %s) @ 0x%" PRIx64 " with R0 = 0x%" PRIx64
56 "\n", cpuid, target_el, target_aa64 ? "aarch64" : "aarch32", entry,
57 context_id);
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
65 * aligned.
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",
81 __func__, cpuid);
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",
106 __func__, cpuid);
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;
115 if (target_aa64) {
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);
133 } else {
134 /* We are requested to boot in AArch32 mode */
135 static uint32_t mode_for_el[] = { 0,
136 ARM_CPU_MODE_SVC,
137 ARM_CPU_MODE_HYP,
138 ARM_CPU_MODE_SVC };
140 cpsr_write(&target_cpu->env, mode_for_el[target_el], CPSR_M,
141 CPSRWriteRaw);
144 if (target_el == 3) {
145 /* Processor is in secure mode */
146 target_cpu->env.cp15.scr_el3 &= ~SCR_NS;
147 } else {
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));
155 if (target_aa64) {
156 target_cpu->env.xregs[0] = context_id;
157 target_cpu->env.thumb = false;
158 } else {
159 target_cpu->env.regs[0] = context_id;
160 target_cpu->env.thumb = entry & 1;
161 entry &= 0xfffffffe;
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;
174 ARMCPU *target_cpu;
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",
187 __func__, cpuid);
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);
195 /* notreached */
197 return QEMU_ARM_POWERCTL_RET_SUCCESS;
200 int arm_reset_cpu(uint64_t cpuid)
202 CPUState *target_cpu_state;
203 ARMCPU *target_cpu;
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",
216 __func__, cpuid);
217 return QEMU_ARM_POWERCTL_IS_OFF;
220 /* Reset the cpu */
221 cpu_reset(target_cpu_state);
223 return QEMU_ARM_POWERCTL_RET_SUCCESS;