blob: f89a00baa8018cf8a0401f1f8860981b17827a8a [file] [log] [blame]
Andrew Walbran33645652019-04-15 12:29:31 +01001/*
2 * Copyright 2019 The Hafnium Authors.
3 *
Andrew Walbrane959ec12020-06-17 15:01:09 +01004 * 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.
Andrew Walbran33645652019-04-15 12:29:31 +01007 */
8
9#include "psci_handler.h"
10
11#include <stdint.h>
12
Andrew Scull550d99b2020-02-10 13:55:00 +000013#include "hf/arch/plat/psci.h"
Andrew Walbran33645652019-04-15 12:29:31 +010014#include "hf/arch/types.h"
15
16#include "hf/api.h"
17#include "hf/cpu.h"
18#include "hf/dlog.h"
Andrew Walbranb5ab43c2020-04-30 11:32:54 +010019#include "hf/ffa.h"
Andrew Walbran33645652019-04-15 12:29:31 +010020#include "hf/panic.h"
21#include "hf/vm.h"
22
23#include "psci.h"
24#include "smc.h"
25
26static uint32_t el3_psci_version;
27
28void cpu_entry(struct cpu *c);
29
30/* Performs arch specific boot time initialisation. */
31void arch_one_time_init(void)
32{
Olivier Deprez56a62c92020-03-03 16:25:45 +010033#if SECURE_WORLD == 0
Andrew Walbranb5ab43c2020-04-30 11:32:54 +010034 struct ffa_value smc_res =
Fuad Tabba8176e3e2019-08-01 10:40:36 +010035 smc32(PSCI_VERSION, 0, 0, 0, 0, 0, 0, SMCCC_CALLER_HYPERVISOR);
Fuad Tabba1889a73c2019-07-31 10:27:47 +010036
Andrew Walbran6606d862019-11-15 16:43:18 +000037 el3_psci_version = smc_res.func;
Andrew Walbran33645652019-04-15 12:29:31 +010038
39 /* Check there's nothing unexpected about PSCI. */
40 switch (el3_psci_version) {
41 case PSCI_VERSION_0_2:
42 case PSCI_VERSION_1_0:
43 case PSCI_VERSION_1_1:
44 /* Supported EL3 PSCI version. */
Andrew Walbran17eebf92020-02-05 16:35:49 +000045 dlog_info("Found PSCI version: %#x\n", el3_psci_version);
Andrew Walbran33645652019-04-15 12:29:31 +010046 break;
47
48 default:
49 /* Unsupported EL3 PSCI version. Log a warning but continue. */
Andrew Walbran17eebf92020-02-05 16:35:49 +000050 dlog_warning("Unknown PSCI version: %#x\n", el3_psci_version);
Andrew Walbran33645652019-04-15 12:29:31 +010051 el3_psci_version = 0;
52 break;
53 }
Olivier Deprez56a62c92020-03-03 16:25:45 +010054#else
55 el3_psci_version = PSCI_VERSION_1_1;
56#endif
Andrew Walbran33645652019-04-15 12:29:31 +010057}
58
59/**
60 * Handles PSCI requests received via HVC or SMC instructions from the primary
61 * VM.
62 *
63 * A minimal PSCI 1.1 interface is offered which can make use of the
64 * implementation of PSCI in EL3 by acting as an adapter.
65 *
66 * Returns true if the request was a PSCI one, false otherwise.
67 */
68bool psci_primary_vm_handler(struct vcpu *vcpu, uint32_t func, uintreg_t arg0,
69 uintreg_t arg1, uintreg_t arg2, uintreg_t *ret)
70{
71 struct cpu *c;
Andrew Walbranb5ab43c2020-04-30 11:32:54 +010072 struct ffa_value smc_res;
Andrew Walbran33645652019-04-15 12:29:31 +010073
74 /*
75 * If there's a problem with the EL3 PSCI, block standard secure service
76 * calls by marking them as unknown. Other calls will be allowed to pass
77 * through.
78 *
79 * This blocks more calls than just PSCI so it may need to be made more
80 * lenient in future.
81 */
82 if (el3_psci_version == 0) {
83 *ret = SMCCC_ERROR_UNKNOWN;
84 return (func & SMCCC_SERVICE_CALL_MASK) ==
85 SMCCC_STANDARD_SECURE_SERVICE_CALL;
86 }
87
88 switch (func & ~SMCCC_CONVENTION_MASK) {
89 case PSCI_VERSION:
90 *ret = PSCI_VERSION_1_1;
91 break;
92
93 case PSCI_FEATURES:
94 switch (arg0 & ~SMCCC_CONVENTION_MASK) {
95 case PSCI_CPU_SUSPEND:
96 if (el3_psci_version == PSCI_VERSION_0_2) {
97 /*
98 * PSCI 0.2 doesn't support PSCI_FEATURES so
99 * report PSCI 0.2 compatible features.
100 */
101 *ret = 0;
102 } else {
103 /* PSCI 1.x only defines two feature bits. */
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100104 smc_res = smc32(func, arg0, 0, 0, 0, 0, 0,
105 SMCCC_CALLER_HYPERVISOR);
Andrew Walbran6606d862019-11-15 16:43:18 +0000106 *ret = smc_res.func & 0x3;
Andrew Walbran33645652019-04-15 12:29:31 +0100107 }
108 break;
109
110 case PSCI_VERSION:
111 case PSCI_FEATURES:
112 case PSCI_SYSTEM_OFF:
113 case PSCI_SYSTEM_RESET:
114 case PSCI_AFFINITY_INFO:
115 case PSCI_CPU_OFF:
116 case PSCI_CPU_ON:
117 /* These are supported without special features. */
118 *ret = 0;
119 break;
120
121 default:
122 /* Everything else is unsupported. */
123 *ret = PSCI_ERROR_NOT_SUPPORTED;
124 break;
125 }
126 break;
127
128 case PSCI_SYSTEM_OFF:
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100129 smc32(PSCI_SYSTEM_OFF, 0, 0, 0, 0, 0, 0,
130 SMCCC_CALLER_HYPERVISOR);
Andrew Walbran33645652019-04-15 12:29:31 +0100131 panic("System off failed");
132 break;
133
134 case PSCI_SYSTEM_RESET:
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100135 smc32(PSCI_SYSTEM_RESET, 0, 0, 0, 0, 0, 0,
136 SMCCC_CALLER_HYPERVISOR);
Andrew Walbran33645652019-04-15 12:29:31 +0100137 panic("System reset failed");
138 break;
139
140 case PSCI_AFFINITY_INFO:
141 c = cpu_find(arg0);
142 if (!c) {
143 *ret = PSCI_ERROR_INVALID_PARAMETERS;
144 break;
145 }
146
147 if (arg1 != 0) {
148 *ret = PSCI_ERROR_NOT_SUPPORTED;
149 break;
150 }
151
152 sl_lock(&c->lock);
153 if (c->is_on) {
154 *ret = PSCI_RETURN_ON;
155 } else {
156 *ret = PSCI_RETURN_OFF;
157 }
158 sl_unlock(&c->lock);
159 break;
160
161 case PSCI_CPU_SUSPEND: {
Andrew Scull550d99b2020-02-10 13:55:00 +0000162 plat_psci_cpu_suspend(arg0);
Andrew Walbran33645652019-04-15 12:29:31 +0100163 /*
Fuad Tabbaed294af2019-12-20 10:43:01 +0000164 * Update vCPU state to wake from the provided entry point but
Andrew Walbran33645652019-04-15 12:29:31 +0100165 * if suspend returns, for example because it failed or was a
166 * standby power state, the SMC will return and the updated
Fuad Tabbaed294af2019-12-20 10:43:01 +0000167 * vCPU registers will be ignored.
Andrew Walbran33645652019-04-15 12:29:31 +0100168 */
169 arch_regs_set_pc_arg(&vcpu->regs, ipa_init(arg1), arg2);
Fuad Tabba1889a73c2019-07-31 10:27:47 +0100170 smc_res = smc64(PSCI_CPU_SUSPEND, arg0, (uintreg_t)&cpu_entry,
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100171 (uintreg_t)vcpu->cpu, 0, 0, 0,
172 SMCCC_CALLER_HYPERVISOR);
Andrew Walbran6606d862019-11-15 16:43:18 +0000173 *ret = smc_res.func;
Andrew Walbran33645652019-04-15 12:29:31 +0100174 break;
175 }
176
177 case PSCI_CPU_OFF:
178 cpu_off(vcpu->cpu);
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100179 smc32(PSCI_CPU_OFF, 0, 0, 0, 0, 0, 0, SMCCC_CALLER_HYPERVISOR);
Andrew Walbran33645652019-04-15 12:29:31 +0100180 panic("CPU off failed");
181 break;
182
183 case PSCI_CPU_ON:
184 c = cpu_find(arg0);
185 if (!c) {
186 *ret = PSCI_ERROR_INVALID_PARAMETERS;
187 break;
188 }
189
190 if (cpu_on(c, ipa_init(arg1), arg2)) {
191 *ret = PSCI_ERROR_ALREADY_ON;
192 break;
193 }
194
195 /*
196 * There's a race when turning a CPU on when it's in the
197 * process of turning off. We need to loop here while it is
198 * reported that the CPU is on (because it's about to turn
199 * itself off).
200 */
201 do {
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100202 smc_res = smc64(PSCI_CPU_ON, arg0,
203 (uintreg_t)&cpu_entry, (uintreg_t)c, 0,
204 0, 0, SMCCC_CALLER_HYPERVISOR);
Andrew Walbran6606d862019-11-15 16:43:18 +0000205 *ret = smc_res.func;
Andrew Walbran33645652019-04-15 12:29:31 +0100206 } while (*ret == PSCI_ERROR_ALREADY_ON);
207
208 if (*ret != PSCI_RETURN_SUCCESS) {
209 cpu_off(c);
210 }
211 break;
212
213 case PSCI_MIGRATE:
214 case PSCI_MIGRATE_INFO_TYPE:
215 case PSCI_MIGRATE_INFO_UP_CPU:
216 case PSCI_CPU_FREEZE:
217 case PSCI_CPU_DEFAULT_SUSPEND:
218 case PSCI_NODE_HW_STATE:
219 case PSCI_SYSTEM_SUSPEND:
220 case PSCI_SET_SYSPEND_MODE:
221 case PSCI_STAT_RESIDENCY:
222 case PSCI_STAT_COUNT:
223 case PSCI_SYSTEM_RESET2:
224 case PSCI_MEM_PROTECT:
225 case PSCI_MEM_PROTECT_CHECK_RANGE:
226 /* Block all other known PSCI calls. */
227 *ret = PSCI_ERROR_NOT_SUPPORTED;
228 break;
229
230 default:
231 return false;
232 }
233
234 return true;
235}
236
237/**
238 * Convert a PSCI CPU / affinity ID for a secondary VM to the corresponding vCPU
239 * index.
240 */
Andrew Walbranb5ab43c2020-04-30 11:32:54 +0100241ffa_vcpu_index_t vcpu_id_to_index(cpu_id_t vcpu_id)
Andrew Walbran33645652019-04-15 12:29:31 +0100242{
243 /* For now we use indices as IDs for the purposes of PSCI. */
244 return vcpu_id;
245}
246
247/**
248 * Handles PSCI requests received via HVC or SMC instructions from a secondary
249 * VM.
250 *
251 * A minimal PSCI 1.1 interface is offered which can start and stop vCPUs in
252 * collaboration with the scheduler in the primary VM.
253 *
254 * Returns true if the request was a PSCI one, false otherwise.
255 */
256bool psci_secondary_vm_handler(struct vcpu *vcpu, uint32_t func, uintreg_t arg0,
257 uintreg_t arg1, uintreg_t arg2, uintreg_t *ret,
258 struct vcpu **next)
259{
260 switch (func & ~SMCCC_CONVENTION_MASK) {
261 case PSCI_VERSION:
262 *ret = PSCI_VERSION_1_1;
263 break;
264
265 case PSCI_FEATURES:
266 switch (arg0 & ~SMCCC_CONVENTION_MASK) {
267 case PSCI_CPU_SUSPEND:
268 /*
269 * Does not offer OS-initiated mode but does use
270 * extended StateID Format.
271 */
272 *ret = 0x2;
273 break;
274
275 case PSCI_VERSION:
276 case PSCI_FEATURES:
277 case PSCI_AFFINITY_INFO:
278 case PSCI_CPU_OFF:
279 case PSCI_CPU_ON:
280 /* These are supported without special features. */
281 *ret = 0;
282 break;
283
284 default:
285 /* Everything else is unsupported. */
286 *ret = PSCI_ERROR_NOT_SUPPORTED;
287 break;
288 }
289 break;
290
291 case PSCI_AFFINITY_INFO: {
Andrew Walbran4d3fa282019-06-26 13:31:15 +0100292 cpu_id_t target_affinity = arg0;
Andrew Walbran33645652019-04-15 12:29:31 +0100293 uint32_t lowest_affinity_level = arg1;
294 struct vm *vm = vcpu->vm;
295 struct vcpu_locked target_vcpu;
Andrew Walbranb5ab43c2020-04-30 11:32:54 +0100296 ffa_vcpu_index_t target_vcpu_index =
Andrew Walbranb037d5b2019-06-25 17:19:41 +0100297 vcpu_id_to_index(target_affinity);
Andrew Walbran33645652019-04-15 12:29:31 +0100298
299 if (lowest_affinity_level != 0) {
300 /* Affinity levels greater than 0 not supported. */
301 *ret = PSCI_ERROR_INVALID_PARAMETERS;
302 break;
303 }
304
305 if (target_vcpu_index >= vm->vcpu_count) {
306 *ret = PSCI_ERROR_INVALID_PARAMETERS;
307 break;
308 }
309
310 target_vcpu = vcpu_lock(vm_get_vcpu(vm, target_vcpu_index));
311 *ret = vcpu_is_off(target_vcpu) ? PSCI_RETURN_OFF
312 : PSCI_RETURN_ON;
313 vcpu_unlock(&target_vcpu);
314 break;
315 }
316
317 case PSCI_CPU_SUSPEND: {
318 /*
319 * Downgrade suspend request to WFI and return SUCCESS, as
320 * allowed by the specification.
321 */
322 *next = api_wait_for_interrupt(vcpu);
323 *ret = PSCI_RETURN_SUCCESS;
324 break;
325 }
326
327 case PSCI_CPU_OFF:
328 /*
329 * Should never return to the caller, but in case it somehow
330 * does.
331 */
332 *ret = PSCI_ERROR_DENIED;
333 /* Tell the scheduler not to run the vCPU again. */
334 *next = api_vcpu_off(vcpu);
335 break;
336
337 case PSCI_CPU_ON: {
338 /* Parameter names as per PSCI specification. */
Andrew Walbran4d3fa282019-06-26 13:31:15 +0100339 cpu_id_t target_cpu = arg0;
Andrew Walbran33645652019-04-15 12:29:31 +0100340 ipaddr_t entry_point_address = ipa_init(arg1);
341 uint64_t context_id = arg2;
Andrew Walbranb5ab43c2020-04-30 11:32:54 +0100342 ffa_vcpu_index_t target_vcpu_index =
Andrew Walbranb037d5b2019-06-25 17:19:41 +0100343 vcpu_id_to_index(target_cpu);
Andrew Walbran33645652019-04-15 12:29:31 +0100344 struct vm *vm = vcpu->vm;
345 struct vcpu *target_vcpu;
346
347 if (target_vcpu_index >= vm->vcpu_count) {
348 *ret = PSCI_ERROR_INVALID_PARAMETERS;
349 break;
350 }
351
352 target_vcpu = vm_get_vcpu(vm, target_vcpu_index);
353
354 if (vcpu_secondary_reset_and_start(
355 target_vcpu, entry_point_address, context_id)) {
356 /*
357 * Tell the scheduler that it can start running the new
358 * vCPU now.
359 */
360 *next = api_wake_up(vcpu, target_vcpu);
361 *ret = PSCI_RETURN_SUCCESS;
362 } else {
363 *ret = PSCI_ERROR_ALREADY_ON;
364 }
365
366 break;
367 }
368
369 case PSCI_SYSTEM_OFF:
370 case PSCI_SYSTEM_RESET:
371 case PSCI_MIGRATE:
372 case PSCI_MIGRATE_INFO_TYPE:
373 case PSCI_MIGRATE_INFO_UP_CPU:
374 case PSCI_CPU_FREEZE:
375 case PSCI_CPU_DEFAULT_SUSPEND:
376 case PSCI_NODE_HW_STATE:
377 case PSCI_SYSTEM_SUSPEND:
378 case PSCI_SET_SYSPEND_MODE:
379 case PSCI_STAT_RESIDENCY:
380 case PSCI_STAT_COUNT:
381 case PSCI_SYSTEM_RESET2:
382 case PSCI_MEM_PROTECT:
383 case PSCI_MEM_PROTECT_CHECK_RANGE:
384 /* Block all other known PSCI calls. */
385 *ret = PSCI_ERROR_NOT_SUPPORTED;
386 break;
387
388 default:
389 return false;
390 }
391
392 return true;
393}
394
395/**
396 * Handles PSCI requests received via HVC or SMC instructions from a VM.
397 * Requests from primary and secondary VMs are dealt with differently.
398 *
399 * Returns true if the request was a PSCI one, false otherwise.
400 */
401bool psci_handler(struct vcpu *vcpu, uint32_t func, uintreg_t arg0,
402 uintreg_t arg1, uintreg_t arg2, uintreg_t *ret,
403 struct vcpu **next)
404{
405 if (vcpu->vm->id == HF_PRIMARY_VM_ID) {
406 return psci_primary_vm_handler(vcpu, func, arg0, arg1, arg2,
407 ret);
408 }
409 return psci_secondary_vm_handler(vcpu, func, arg0, arg1, arg2, ret,
410 next);
411}