1
2
3
4
5
6
7
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
19#ifndef DEBUG_ARM_POWERCTL
20#define DEBUG_ARM_POWERCTL 0
21#endif
22
23#define DPRINTF(fmt, args...) \
24 do { \
25 if (DEBUG_ARM_POWERCTL) { \
26 fprintf(stderr, "[ARM]%s: " fmt , __func__, ##args); \
27 } \
28 } while (0)
29
30CPUState *arm_get_cpu_by_id(uint64_t id)
31{
32 CPUState *cpu;
33
34 DPRINTF("cpu %" PRId64 "\n", id);
35
36 CPU_FOREACH(cpu) {
37 ARMCPU *armcpu = ARM_CPU(cpu);
38
39 if (armcpu->mp_affinity == id) {
40 return cpu;
41 }
42 }
43
44 qemu_log_mask(LOG_GUEST_ERROR,
45 "[ARM]%s: Requesting unknown CPU %" PRId64 "\n",
46 __func__, id);
47
48 return NULL;
49}
50
51struct CpuOnInfo {
52 uint64_t entry;
53 uint64_t context_id;
54 uint32_t target_el;
55 bool target_aa64;
56};
57
58
59static void arm_set_cpu_on_async_work(CPUState *target_cpu_state,
60 run_on_cpu_data data)
61{
62 ARMCPU *target_cpu = ARM_CPU(target_cpu_state);
63 struct CpuOnInfo *info = (struct CpuOnInfo *) data.host_ptr;
64
65
66 cpu_reset(target_cpu_state);
67 target_cpu_state->halted = 0;
68
69 if (info->target_aa64) {
70 if ((info->target_el < 3) && arm_feature(&target_cpu->env,
71 ARM_FEATURE_EL3)) {
72
73
74
75
76 target_cpu->env.cp15.scr_el3 |= SCR_RW;
77 }
78
79 if ((info->target_el < 2) && arm_feature(&target_cpu->env,
80 ARM_FEATURE_EL2)) {
81
82
83
84
85 target_cpu->env.cp15.hcr_el2 |= HCR_RW;
86 }
87
88 target_cpu->env.pstate = aarch64_pstate_mode(info->target_el, true);
89 } else {
90
91 static const uint32_t mode_for_el[] = { 0,
92 ARM_CPU_MODE_SVC,
93 ARM_CPU_MODE_HYP,
94 ARM_CPU_MODE_SVC };
95
96 cpsr_write(&target_cpu->env, mode_for_el[info->target_el], CPSR_M,
97 CPSRWriteRaw);
98 }
99
100 if (info->target_el == 3) {
101
102 target_cpu->env.cp15.scr_el3 &= ~SCR_NS;
103 } else {
104
105 target_cpu->env.cp15.scr_el3 |= SCR_NS;
106 }
107
108
109 assert(info->target_el == arm_current_el(&target_cpu->env));
110
111 if (info->target_aa64) {
112 target_cpu->env.xregs[0] = info->context_id;
113 target_cpu->env.thumb = false;
114 } else {
115 target_cpu->env.regs[0] = info->context_id;
116 target_cpu->env.thumb = info->entry & 1;
117 info->entry &= 0xfffffffe;
118 }
119
120
121 cpu_set_pc(target_cpu_state, info->entry);
122
123 g_free(info);
124
125
126 assert(qemu_mutex_iothread_locked());
127 target_cpu->power_state = PSCI_ON;
128}
129
130int arm_set_cpu_on(uint64_t cpuid, uint64_t entry, uint64_t context_id,
131 uint32_t target_el, bool target_aa64)
132{
133 CPUState *target_cpu_state;
134 ARMCPU *target_cpu;
135 struct CpuOnInfo *info;
136
137 assert(qemu_mutex_iothread_locked());
138
139 DPRINTF("cpu %" PRId64 " (EL %d, %s) @ 0x%" PRIx64 " with R0 = 0x%" PRIx64
140 "\n", cpuid, target_el, target_aa64 ? "aarch64" : "aarch32", entry,
141 context_id);
142
143
144 assert((target_el > 0) && (target_el < 4));
145
146 if (target_aa64 && (entry & 3)) {
147
148
149
150
151 return QEMU_ARM_POWERCTL_INVALID_PARAM;
152 }
153
154
155 target_cpu_state = arm_get_cpu_by_id(cpuid);
156 if (!target_cpu_state) {
157
158 return QEMU_ARM_POWERCTL_INVALID_PARAM;
159 }
160
161 target_cpu = ARM_CPU(target_cpu_state);
162 if (target_cpu->power_state == PSCI_ON) {
163 qemu_log_mask(LOG_GUEST_ERROR,
164 "[ARM]%s: CPU %" PRId64 " is already on\n",
165 __func__, cpuid);
166 return QEMU_ARM_POWERCTL_ALREADY_ON;
167 }
168
169
170
171
172
173
174 if (((target_el == 3) && !arm_feature(&target_cpu->env, ARM_FEATURE_EL3)) ||
175 ((target_el == 2) && !arm_feature(&target_cpu->env, ARM_FEATURE_EL2))) {
176
177
178
179 return QEMU_ARM_POWERCTL_INVALID_PARAM;
180 }
181
182 if (!target_aa64 && arm_feature(&target_cpu->env, ARM_FEATURE_AARCH64)) {
183
184
185
186
187 qemu_log_mask(LOG_UNIMP,
188 "[ARM]%s: Starting AArch64 CPU %" PRId64
189 " in AArch32 mode is not supported yet\n",
190 __func__, cpuid);
191 return QEMU_ARM_POWERCTL_INVALID_PARAM;
192 }
193
194
195
196
197
198
199
200 if (target_cpu->power_state == PSCI_ON_PENDING) {
201 qemu_log_mask(LOG_GUEST_ERROR,
202 "[ARM]%s: CPU %" PRId64 " is already powering on\n",
203 __func__, cpuid);
204 return QEMU_ARM_POWERCTL_ON_PENDING;
205 }
206
207
208
209
210
211 info = g_new(struct CpuOnInfo, 1);
212 info->entry = entry;
213 info->context_id = context_id;
214 info->target_el = target_el;
215 info->target_aa64 = target_aa64;
216
217 async_run_on_cpu(target_cpu_state, arm_set_cpu_on_async_work,
218 RUN_ON_CPU_HOST_PTR(info));
219
220
221 return QEMU_ARM_POWERCTL_RET_SUCCESS;
222}
223
224static void arm_set_cpu_off_async_work(CPUState *target_cpu_state,
225 run_on_cpu_data data)
226{
227 ARMCPU *target_cpu = ARM_CPU(target_cpu_state);
228
229 assert(qemu_mutex_iothread_locked());
230 target_cpu->power_state = PSCI_OFF;
231 target_cpu_state->halted = 1;
232 target_cpu_state->exception_index = EXCP_HLT;
233}
234
235int arm_set_cpu_off(uint64_t cpuid)
236{
237 CPUState *target_cpu_state;
238 ARMCPU *target_cpu;
239
240 assert(qemu_mutex_iothread_locked());
241
242 DPRINTF("cpu %" PRId64 "\n", cpuid);
243
244
245 target_cpu_state = arm_get_cpu_by_id(cpuid);
246 if (!target_cpu_state) {
247 return QEMU_ARM_POWERCTL_INVALID_PARAM;
248 }
249 target_cpu = ARM_CPU(target_cpu_state);
250 if (target_cpu->power_state == PSCI_OFF) {
251 qemu_log_mask(LOG_GUEST_ERROR,
252 "[ARM]%s: CPU %" PRId64 " is already off\n",
253 __func__, cpuid);
254 return QEMU_ARM_POWERCTL_IS_OFF;
255 }
256
257
258 async_run_on_cpu(target_cpu_state, arm_set_cpu_off_async_work,
259 RUN_ON_CPU_NULL);
260
261 return QEMU_ARM_POWERCTL_RET_SUCCESS;
262}
263
264static void arm_reset_cpu_async_work(CPUState *target_cpu_state,
265 run_on_cpu_data data)
266{
267
268 cpu_reset(target_cpu_state);
269}
270
271int arm_reset_cpu(uint64_t cpuid)
272{
273 CPUState *target_cpu_state;
274 ARMCPU *target_cpu;
275
276 assert(qemu_mutex_iothread_locked());
277
278 DPRINTF("cpu %" PRId64 "\n", cpuid);
279
280
281 target_cpu_state = arm_get_cpu_by_id(cpuid);
282 if (!target_cpu_state) {
283 return QEMU_ARM_POWERCTL_INVALID_PARAM;
284 }
285 target_cpu = ARM_CPU(target_cpu_state);
286
287 if (target_cpu->power_state == PSCI_OFF) {
288 qemu_log_mask(LOG_GUEST_ERROR,
289 "[ARM]%s: CPU %" PRId64 " is off\n",
290 __func__, cpuid);
291 return QEMU_ARM_POWERCTL_IS_OFF;
292 }
293
294
295 async_run_on_cpu(target_cpu_state, arm_reset_cpu_async_work,
296 RUN_ON_CPU_NULL);
297
298 return QEMU_ARM_POWERCTL_RET_SUCCESS;
299}
300