1 /*
2 * Copyright 2019 The Hafnium Authors.
3 *
4 * Use of this source code is governed by a BSD-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/BSD-3-Clause.
7 */
8
9 #include "psci_handler.h"
10
11 #include <stdint.h>
12
13 #include "hf/arch/plat/psci.h"
14 #include "hf/arch/types.h"
15
16 #include "hf/api.h"
17 #include "hf/cpu.h"
18 #include "hf/dlog.h"
19 #include "hf/ffa.h"
20 #include "hf/panic.h"
21 #include "hf/vm.h"
22
23 #include "psci.h"
24
25 void cpu_entry(struct cpu *c);
26
27 /**
28 * Handles PSCI requests received via HVC or SMC instructions from the primary
29 * VM.
30 *
31 * A minimal PSCI 1.1 interface is offered which can make use of the
32 * implementation of PSCI in EL3 by acting as an adapter.
33 *
34 * Returns true if the request was a PSCI one, false otherwise.
35 */
psci_primary_vm_handler(struct vcpu * vcpu,uint32_t func,uintreg_t arg0,uintreg_t arg1,uintreg_t arg2,uintreg_t * ret)36 bool psci_primary_vm_handler(struct vcpu *vcpu, uint32_t func, uintreg_t arg0,
37 uintreg_t arg1, uintreg_t arg2, uintreg_t *ret)
38 {
39 struct cpu *c;
40 struct ffa_value smc_res;
41 struct vcpu *vcpu_target;
42 struct vcpu_locked vcpu_locked;
43
44 /*
45 * If there's a problem with the EL3 PSCI, block standard secure service
46 * calls by marking them as unknown. Other calls will be allowed to pass
47 * through.
48 *
49 * This blocks more calls than just PSCI so it may need to be made more
50 * lenient in future.
51 */
52
53 if (plat_psci_version_get() == 0) {
54 *ret = SMCCC_ERROR_UNKNOWN;
55 return (func & SMCCC_SERVICE_CALL_MASK) ==
56 SMCCC_STANDARD_SECURE_SERVICE_CALL;
57 }
58
59 switch (func & ~SMCCC_CONVENTION_MASK) {
60 case PSCI_VERSION:
61 *ret = PSCI_VERSION_1_1;
62 break;
63
64 case PSCI_FEATURES:
65 switch (arg0 & ~SMCCC_CONVENTION_MASK) {
66 case SMCCC_VERSION_FUNC_ID:
67 *ret = SMCCC_VERSION_1_2;
68 break;
69
70 case PSCI_CPU_SUSPEND:
71 if (plat_psci_version_get() == PSCI_VERSION_0_2) {
72 /*
73 * PSCI 0.2 doesn't support PSCI_FEATURES so
74 * report PSCI 0.2 compatible features.
75 */
76 *ret = 0;
77 } else {
78 /* PSCI 1.x only defines two feature bits. */
79 smc_res = smc32(func, arg0, 0, 0, 0, 0, 0,
80 SMCCC_CALLER_HYPERVISOR);
81 *ret = smc_res.func & 0x3;
82 }
83 break;
84
85 case PSCI_VERSION:
86 case PSCI_FEATURES:
87 case PSCI_SYSTEM_OFF:
88 case PSCI_SYSTEM_RESET:
89 case PSCI_AFFINITY_INFO:
90 case PSCI_CPU_OFF:
91 case PSCI_CPU_ON:
92 /* These are supported without special features. */
93 *ret = 0;
94 break;
95
96 default:
97 /* Everything else is unsupported. */
98 *ret = PSCI_ERROR_NOT_SUPPORTED;
99 break;
100 }
101 break;
102
103 case PSCI_SYSTEM_OFF:
104 smc32(PSCI_SYSTEM_OFF, 0, 0, 0, 0, 0, 0,
105 SMCCC_CALLER_HYPERVISOR);
106 panic("System off failed");
107 break;
108
109 case PSCI_SYSTEM_RESET:
110 smc32(PSCI_SYSTEM_RESET, 0, 0, 0, 0, 0, 0,
111 SMCCC_CALLER_HYPERVISOR);
112 panic("System reset failed");
113 break;
114
115 case PSCI_AFFINITY_INFO:
116 c = cpu_find(arg0);
117 if (!c) {
118 *ret = PSCI_ERROR_INVALID_PARAMETERS;
119 break;
120 }
121
122 if (arg1 != 0) {
123 *ret = PSCI_ERROR_NOT_SUPPORTED;
124 break;
125 }
126
127 sl_lock(&c->lock);
128 if (c->is_on) {
129 *ret = PSCI_RETURN_ON;
130 } else {
131 *ret = PSCI_RETURN_OFF;
132 }
133 sl_unlock(&c->lock);
134 break;
135
136 case PSCI_CPU_SUSPEND: {
137 plat_psci_cpu_suspend(arg0);
138 /*
139 * Update vCPU state to wake from the provided entry point but
140 * if suspend returns, for example because it failed or was a
141 * standby power state, the SMC will return and the updated
142 * vCPU registers will be ignored.
143 */
144 arch_regs_set_pc_arg(&vcpu->regs, ipa_init(arg1), arg2);
145 smc_res = smc64(PSCI_CPU_SUSPEND, arg0, (uintreg_t)&cpu_entry,
146 (uintreg_t)vcpu->cpu, 0, 0, 0,
147 SMCCC_CALLER_HYPERVISOR);
148 *ret = smc_res.func;
149 break;
150 }
151
152 case PSCI_CPU_OFF:
153 cpu_off(vcpu->cpu);
154 smc32(PSCI_CPU_OFF, 0, 0, 0, 0, 0, 0, SMCCC_CALLER_HYPERVISOR);
155 panic("CPU off failed");
156 break;
157
158 case PSCI_CPU_ON:
159 c = cpu_find(arg0);
160 if (!c) {
161 *ret = PSCI_ERROR_INVALID_PARAMETERS;
162 break;
163 }
164
165 if (cpu_on(c)) {
166 *ret = PSCI_ERROR_ALREADY_ON;
167 break;
168 }
169
170 vcpu_target = vm_get_vcpu(vcpu->vm, cpu_index(c));
171 vcpu_locked = vcpu_lock(vcpu_target);
172 vcpu_on(vcpu_locked, ipa_init(arg1), arg2);
173 vcpu_unlock(&vcpu_locked);
174
175 /*
176 * There's a race when turning a CPU on when it's in the
177 * process of turning off. We need to loop here while it is
178 * reported that the CPU is on (because it's about to turn
179 * itself off).
180 */
181 do {
182 smc_res = smc64(PSCI_CPU_ON, arg0,
183 (uintreg_t)&cpu_entry, (uintreg_t)c, 0,
184 0, 0, SMCCC_CALLER_HYPERVISOR);
185 *ret = smc_res.func;
186 } while (*ret == (uintreg_t)PSCI_ERROR_ALREADY_ON);
187
188 if (*ret != PSCI_RETURN_SUCCESS) {
189 cpu_off(c);
190 }
191 break;
192
193 case PSCI_MIGRATE:
194 case PSCI_MIGRATE_INFO_TYPE:
195 case PSCI_MIGRATE_INFO_UP_CPU:
196 case PSCI_CPU_FREEZE:
197 case PSCI_CPU_DEFAULT_SUSPEND:
198 case PSCI_NODE_HW_STATE:
199 case PSCI_SYSTEM_SUSPEND:
200 case PSCI_SET_SYSPEND_MODE:
201 case PSCI_STAT_RESIDENCY:
202 case PSCI_STAT_COUNT:
203 case PSCI_SYSTEM_RESET2:
204 case PSCI_MEM_PROTECT:
205 case PSCI_MEM_PROTECT_CHECK_RANGE:
206 /* Block all other known PSCI calls. */
207 *ret = PSCI_ERROR_NOT_SUPPORTED;
208 break;
209
210 default:
211 return false;
212 }
213
214 return true;
215 }
216
217 /**
218 * Handles PSCI requests received via HVC or SMC instructions from a secondary
219 * VM.
220 *
221 * A minimal PSCI 1.1 interface is offered which can start and stop vCPUs in
222 * collaboration with the scheduler in the primary VM.
223 *
224 * Returns true if the request was a PSCI one, false otherwise.
225 */
psci_secondary_vm_handler(struct vcpu * vcpu,uint32_t func,uintreg_t arg0,uintreg_t arg1,uintreg_t arg2,uintreg_t * ret,struct vcpu ** next)226 bool psci_secondary_vm_handler(struct vcpu *vcpu, uint32_t func, uintreg_t arg0,
227 uintreg_t arg1, uintreg_t arg2, uintreg_t *ret,
228 struct vcpu **next)
229 {
230 switch (func & ~SMCCC_CONVENTION_MASK) {
231 case PSCI_VERSION:
232 *ret = PSCI_VERSION_1_1;
233 break;
234
235 case PSCI_FEATURES:
236 switch (arg0 & ~SMCCC_CONVENTION_MASK) {
237 case PSCI_CPU_SUSPEND:
238 /*
239 * Does not offer OS-initiated mode but does use
240 * extended StateID Format.
241 */
242 *ret = 0x2;
243 break;
244
245 case PSCI_VERSION:
246 case PSCI_FEATURES:
247 case PSCI_AFFINITY_INFO:
248 case PSCI_CPU_OFF:
249 case PSCI_CPU_ON:
250 /* These are supported without special features. */
251 *ret = 0;
252 break;
253
254 default:
255 /* Everything else is unsupported. */
256 *ret = PSCI_ERROR_NOT_SUPPORTED;
257 break;
258 }
259 break;
260
261 case PSCI_AFFINITY_INFO: {
262 cpu_id_t target_affinity = arg0;
263 uint32_t lowest_affinity_level = arg1;
264 struct vm *vm = vcpu->vm;
265 struct vcpu_locked target_vcpu;
266 ffa_vcpu_index_t target_vcpu_index =
267 vcpu_id_to_index(target_affinity);
268
269 if (lowest_affinity_level != 0) {
270 /* Affinity levels greater than 0 not supported. */
271 *ret = PSCI_ERROR_INVALID_PARAMETERS;
272 break;
273 }
274
275 if (target_vcpu_index >= vm->vcpu_count) {
276 *ret = PSCI_ERROR_INVALID_PARAMETERS;
277 break;
278 }
279
280 target_vcpu = vcpu_lock(vm_get_vcpu(vm, target_vcpu_index));
281 *ret = vcpu_is_off(target_vcpu) ? PSCI_RETURN_OFF
282 : PSCI_RETURN_ON;
283 vcpu_unlock(&target_vcpu);
284 break;
285 }
286
287 case PSCI_CPU_SUSPEND: {
288 /*
289 * Downgrade suspend request to WFI and return SUCCESS, as
290 * allowed by the specification.
291 */
292 *next = api_wait_for_interrupt(vcpu);
293 *ret = PSCI_RETURN_SUCCESS;
294 break;
295 }
296
297 case PSCI_CPU_OFF:
298 /*
299 * Should never return to the caller, but in case it somehow
300 * does.
301 */
302 *ret = PSCI_ERROR_DENIED;
303 /* Tell the scheduler not to run the vCPU again. */
304 *next = api_vcpu_off(vcpu);
305 break;
306
307 case PSCI_CPU_ON: {
308 /* Parameter names as per PSCI specification. */
309 cpu_id_t target_cpu = arg0;
310 ipaddr_t entry_point_address = ipa_init(arg1);
311 uint64_t context_id = arg2;
312 ffa_vcpu_index_t target_vcpu_index =
313 vcpu_id_to_index(target_cpu);
314 struct vm *vm = vcpu->vm;
315 struct vcpu *target_vcpu;
316 struct vcpu_locked vcpu_locked;
317 bool vcpu_was_off;
318
319 if (target_vcpu_index >= vm->vcpu_count) {
320 *ret = PSCI_ERROR_INVALID_PARAMETERS;
321 break;
322 }
323
324 target_vcpu = vm_get_vcpu(vm, target_vcpu_index);
325 vcpu_locked = vcpu_lock(target_vcpu);
326 vcpu_was_off = vcpu_secondary_reset_and_start(
327 vcpu_locked, entry_point_address, context_id);
328 vcpu_unlock(&vcpu_locked);
329
330 if (vcpu_was_off) {
331 /*
332 * Tell the scheduler that it can start running the new
333 * vCPU now.
334 */
335 *next = api_wake_up(vcpu, target_vcpu);
336 *ret = PSCI_RETURN_SUCCESS;
337 } else {
338 *ret = PSCI_ERROR_ALREADY_ON;
339 }
340
341 break;
342 }
343
344 case PSCI_SYSTEM_OFF:
345 case PSCI_SYSTEM_RESET:
346 case PSCI_MIGRATE:
347 case PSCI_MIGRATE_INFO_TYPE:
348 case PSCI_MIGRATE_INFO_UP_CPU:
349 case PSCI_CPU_FREEZE:
350 case PSCI_CPU_DEFAULT_SUSPEND:
351 case PSCI_NODE_HW_STATE:
352 case PSCI_SYSTEM_SUSPEND:
353 case PSCI_SET_SYSPEND_MODE:
354 case PSCI_STAT_RESIDENCY:
355 case PSCI_STAT_COUNT:
356 case PSCI_SYSTEM_RESET2:
357 case PSCI_MEM_PROTECT:
358 case PSCI_MEM_PROTECT_CHECK_RANGE:
359 /* Block all other known PSCI calls. */
360 *ret = PSCI_ERROR_NOT_SUPPORTED;
361 break;
362
363 default:
364 return false;
365 }
366
367 return true;
368 }
369
370 /**
371 * Handles PSCI requests received via HVC or SMC instructions from a VM.
372 * Requests from primary and secondary VMs are dealt with differently.
373 *
374 * Returns true if the request was a PSCI one, false otherwise.
375 */
psci_handler(struct vcpu * vcpu,uint32_t func,uintreg_t arg0,uintreg_t arg1,uintreg_t arg2,uintreg_t * ret,struct vcpu ** next)376 bool psci_handler(struct vcpu *vcpu, uint32_t func, uintreg_t arg0,
377 uintreg_t arg1, uintreg_t arg2, uintreg_t *ret,
378 struct vcpu **next)
379 {
380 if (vm_is_primary(vcpu->vm)) {
381 return psci_primary_vm_handler(vcpu, func, arg0, arg1, arg2,
382 ret);
383 }
384 return psci_secondary_vm_handler(vcpu, func, arg0, arg1, arg2, ret,
385 next);
386 }
387