master
c 311 lines 9.04 KB
Raw
1 /*
2 * QEMU support -- ARM Power Control specific functions.
3 *
4 * Copyright (c) 2016 Jean-Christophe Dubois
5 *
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.
8 *
9 */
10
11 #include "qemu/osdep.h"
12 #include "cpu.h"
13 #include "cpu-qom.h"
14 #include "internals.h"
15 #include "arm-powerctl.h"
16 #include "qemu/log.h"
17 #include "qemu/main-loop.h"
18 #include "system/tcg.h"
19 #include "target/arm/multiprocessing.h"
20 #include "trace.h"
21
22 CPUState *arm_get_cpu_by_id(uint64_t id)
23 {
24 CPUState *cpu;
25
26 CPU_FOREACH(cpu) {
27 ARMCPU *armcpu = ARM_CPU(cpu);
28
29 if (arm_cpu_mp_affinity(armcpu) == id) {
30 return cpu;
31 }
32 }
33
34 qemu_log_mask(LOG_GUEST_ERROR,
35 "[ARM]%s: Requesting unknown CPU %" PRId64 "\n",
36 __func__, id);
37
38 return NULL;
39 }
40
41 struct CpuOnInfo {
42 uint64_t entry;
43 uint64_t context_id;
44 uint32_t target_el;
45 bool target_aa64;
46 };
47
48
49 static void arm_set_cpu_on_async_work(CPUState *target_cpu_state,
50 run_on_cpu_data data)
51 {
52 ARMCPU *target_cpu = ARM_CPU(target_cpu_state);
53 struct CpuOnInfo *info = (struct CpuOnInfo *) data.host_ptr;
54
55 /* Initialize the cpu we are turning on */
56 cpu_reset(target_cpu_state);
57 arm_emulate_firmware_reset(target_cpu_state, info->target_el);
58 target_cpu_state->halted = 0;
59
60 /* We check if the started CPU is now at the correct level */
61 assert(info->target_el == arm_current_el(&target_cpu->env));
62
63 if (info->target_aa64) {
64 target_cpu->env.xregs[0] = info->context_id;
65 } else {
66 target_cpu->env.regs[0] = info->context_id;
67 }
68
69 if (tcg_enabled()) {
70 /* CP15 update requires rebuilding hflags */
71 arm_rebuild_hflags(&target_cpu->env);
72 }
73
74 /* Start the new CPU at the requested address */
75 cpu_set_pc(target_cpu_state, info->entry);
76
77 g_free(info);
78
79 /* Finally set the power status */
80 assert(bql_locked());
81 arm_set_cpu_power_state(target_cpu, PSCI_ON);
82 }
83
84 int arm_set_cpu_on(uint64_t cpuid, uint64_t entry, uint64_t context_id,
85 uint32_t target_el, bool target_aa64)
86 {
87 CPUState *target_cpu_state;
88 ARMCPU *target_cpu;
89 struct CpuOnInfo *info;
90
91 assert(bql_locked());
92
93 trace_arm_powerctl_set_cpu_on(cpuid, target_el,
94 target_aa64 ? "aarch64" : "aarch32",
95 entry, context_id);
96
97 /* requested EL level need to be in the 1 to 3 range */
98 assert((target_el > 0) && (target_el < 4));
99
100 if (target_aa64 && (entry & 3)) {
101 /*
102 * if we are booting in AArch64 mode then "entry" needs to be 4 bytes
103 * aligned.
104 */
105 return QEMU_ARM_POWERCTL_INVALID_PARAM;
106 }
107
108 /* Retrieve the cpu we are powering up */
109 target_cpu_state = arm_get_cpu_by_id(cpuid);
110 if (!target_cpu_state) {
111 /* The cpu was not found */
112 return QEMU_ARM_POWERCTL_INVALID_PARAM;
113 }
114
115 target_cpu = ARM_CPU(target_cpu_state);
116 if (target_cpu->power_state == PSCI_ON) {
117 qemu_log_mask(LOG_GUEST_ERROR,
118 "[ARM]%s: CPU %" PRId64 " is already on\n",
119 __func__, cpuid);
120 return QEMU_ARM_POWERCTL_ALREADY_ON;
121 }
122
123 /*
124 * The newly brought CPU is requested to enter the exception level
125 * "target_el" and be in the requested mode (AArch64 or AArch32).
126 */
127
128 if (((target_el == 3) && !arm_feature(&target_cpu->env, ARM_FEATURE_EL3)) ||
129 ((target_el == 2) && !arm_feature(&target_cpu->env, ARM_FEATURE_EL2))) {
130 /*
131 * The CPU does not support requested level
132 */
133 return QEMU_ARM_POWERCTL_INVALID_PARAM;
134 }
135
136 if (!target_aa64 && arm_feature(&target_cpu->env, ARM_FEATURE_AARCH64)) {
137 /*
138 * For now we don't support booting an AArch64 CPU in AArch32 mode
139 * TODO: We should add this support later
140 */
141 qemu_log_mask(LOG_UNIMP,
142 "[ARM]%s: Starting AArch64 CPU %" PRId64
143 " in AArch32 mode is not supported yet\n",
144 __func__, cpuid);
145 return QEMU_ARM_POWERCTL_INVALID_PARAM;
146 }
147
148 /*
149 * If another CPU has powered the target on we are in the state
150 * ON_PENDING and additional attempts to power on the CPU should
151 * fail (see 6.6 Implementation CPU_ON/CPU_OFF races in the PSCI
152 * spec)
153 */
154 if (target_cpu->power_state == PSCI_ON_PENDING) {
155 qemu_log_mask(LOG_GUEST_ERROR,
156 "[ARM]%s: CPU %" PRId64 " is already powering on\n",
157 __func__, cpuid);
158 return QEMU_ARM_POWERCTL_ON_PENDING;
159 }
160
161 /* To avoid racing with a CPU we are just kicking off we do the
162 * final bit of preparation for the work in the target CPUs
163 * context.
164 */
165 info = g_new(struct CpuOnInfo, 1);
166 info->entry = entry;
167 info->context_id = context_id;
168 info->target_el = target_el;
169 info->target_aa64 = target_aa64;
170
171 async_run_on_cpu(target_cpu_state, arm_set_cpu_on_async_work,
172 RUN_ON_CPU_HOST_PTR(info));
173
174 /* We are good to go */
175 return QEMU_ARM_POWERCTL_RET_SUCCESS;
176 }
177
178 static void arm_set_cpu_on_and_reset_async_work(CPUState *target_cpu_state,
179 run_on_cpu_data data)
180 {
181 ARMCPU *target_cpu = ARM_CPU(target_cpu_state);
182
183 /* Initialize the cpu we are turning on */
184 cpu_reset(target_cpu_state);
185 target_cpu_state->halted = 0;
186
187 /* Finally set the power status */
188 assert(bql_locked());
189 arm_set_cpu_power_state(target_cpu, PSCI_ON);
190 }
191
192 int arm_set_cpu_on_and_reset(uint64_t cpuid)
193 {
194 CPUState *target_cpu_state;
195 ARMCPU *target_cpu;
196
197 assert(bql_locked());
198
199 trace_arm_powerctl_set_cpu_on_and_reset(cpuid);
200
201 /* Retrieve the cpu we are powering up */
202 target_cpu_state = arm_get_cpu_by_id(cpuid);
203 if (!target_cpu_state) {
204 /* The cpu was not found */
205 return QEMU_ARM_POWERCTL_INVALID_PARAM;
206 }
207
208 target_cpu = ARM_CPU(target_cpu_state);
209 if (target_cpu->power_state == PSCI_ON) {
210 qemu_log_mask(LOG_GUEST_ERROR,
211 "[ARM]%s: CPU %" PRId64 " is already on\n",
212 __func__, cpuid);
213 return QEMU_ARM_POWERCTL_ALREADY_ON;
214 }
215
216 /*
217 * If another CPU has powered the target on we are in the state
218 * ON_PENDING and additional attempts to power on the CPU should
219 * fail (see 6.6 Implementation CPU_ON/CPU_OFF races in the PSCI
220 * spec)
221 */
222 if (target_cpu->power_state == PSCI_ON_PENDING) {
223 qemu_log_mask(LOG_GUEST_ERROR,
224 "[ARM]%s: CPU %" PRId64 " is already powering on\n",
225 __func__, cpuid);
226 return QEMU_ARM_POWERCTL_ON_PENDING;
227 }
228
229 async_run_on_cpu(target_cpu_state, arm_set_cpu_on_and_reset_async_work,
230 RUN_ON_CPU_NULL);
231
232 /* We are good to go */
233 return QEMU_ARM_POWERCTL_RET_SUCCESS;
234 }
235
236 static void arm_set_cpu_off_async_work(CPUState *target_cpu_state,
237 run_on_cpu_data data)
238 {
239 ARMCPU *target_cpu = ARM_CPU(target_cpu_state);
240
241 assert(bql_locked());
242 arm_set_cpu_power_state(target_cpu, PSCI_OFF);
243 target_cpu_state->halted = 1;
244 target_cpu_state->exception_index = EXCP_HLT;
245 }
246
247 int arm_set_cpu_off(uint64_t cpuid)
248 {
249 CPUState *target_cpu_state;
250 ARMCPU *target_cpu;
251
252 assert(bql_locked());
253
254 trace_arm_powerctl_set_cpu_off(cpuid);
255
256 /* change to the cpu we are powering up */
257 target_cpu_state = arm_get_cpu_by_id(cpuid);
258 if (!target_cpu_state) {
259 return QEMU_ARM_POWERCTL_INVALID_PARAM;
260 }
261 target_cpu = ARM_CPU(target_cpu_state);
262 if (target_cpu->power_state == PSCI_OFF) {
263 qemu_log_mask(LOG_GUEST_ERROR,
264 "[ARM]%s: CPU %" PRId64 " is already off\n",
265 __func__, cpuid);
266 return QEMU_ARM_POWERCTL_IS_OFF;
267 }
268
269 /* Queue work to run under the target vCPUs context */
270 async_run_on_cpu(target_cpu_state, arm_set_cpu_off_async_work,
271 RUN_ON_CPU_NULL);
272
273 return QEMU_ARM_POWERCTL_RET_SUCCESS;
274 }
275
276 static void arm_reset_cpu_async_work(CPUState *target_cpu_state,
277 run_on_cpu_data data)
278 {
279 /* Reset the cpu */
280 cpu_reset(target_cpu_state);
281 }
282
283 int arm_reset_cpu(uint64_t cpuid)
284 {
285 CPUState *target_cpu_state;
286 ARMCPU *target_cpu;
287
288 assert(bql_locked());
289
290 trace_arm_powerctl_set_cpu_off(cpuid);
291
292 /* change to the cpu we are resetting */
293 target_cpu_state = arm_get_cpu_by_id(cpuid);
294 if (!target_cpu_state) {
295 return QEMU_ARM_POWERCTL_INVALID_PARAM;
296 }
297 target_cpu = ARM_CPU(target_cpu_state);
298
299 if (target_cpu->power_state == PSCI_OFF) {
300 qemu_log_mask(LOG_GUEST_ERROR,
301 "[ARM]%s: CPU %" PRId64 " is off\n",
302 __func__, cpuid);
303 return QEMU_ARM_POWERCTL_IS_OFF;
304 }
305
306 /* Queue work to run under the target vCPUs context */
307 async_run_on_cpu(target_cpu_state, arm_reset_cpu_async_work,
308 RUN_ON_CPU_NULL);
309
310 return QEMU_ARM_POWERCTL_RET_SUCCESS;
311 }