blob: 358c8275315e383ca8e02ed8861255e3d8520f40 [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"
Andrew Walbran33645652019-04-15 12:29:31 +010024
25void cpu_entry(struct cpu *c);
26
27/* Performs arch specific boot time initialisation. */
28void arch_one_time_init(void)
29{
Olivier Deprezf17eadc2021-01-25 15:33:38 +010030 plat_psci_init();
Andrew Walbran33645652019-04-15 12:29:31 +010031}
32
33/**
34 * Handles PSCI requests received via HVC or SMC instructions from the primary
35 * VM.
36 *
37 * A minimal PSCI 1.1 interface is offered which can make use of the
38 * implementation of PSCI in EL3 by acting as an adapter.
39 *
40 * Returns true if the request was a PSCI one, false otherwise.
41 */
42bool psci_primary_vm_handler(struct vcpu *vcpu, uint32_t func, uintreg_t arg0,
43 uintreg_t arg1, uintreg_t arg2, uintreg_t *ret)
44{
45 struct cpu *c;
Andrew Walbranb5ab43c2020-04-30 11:32:54 +010046 struct ffa_value smc_res;
Andrew Walbran33645652019-04-15 12:29:31 +010047
48 /*
49 * If there's a problem with the EL3 PSCI, block standard secure service
50 * calls by marking them as unknown. Other calls will be allowed to pass
51 * through.
52 *
53 * This blocks more calls than just PSCI so it may need to be made more
54 * lenient in future.
55 */
Olivier Deprezf17eadc2021-01-25 15:33:38 +010056 if (plat_psci_version_get() == 0) {
Andrew Walbran33645652019-04-15 12:29:31 +010057 *ret = SMCCC_ERROR_UNKNOWN;
58 return (func & SMCCC_SERVICE_CALL_MASK) ==
59 SMCCC_STANDARD_SECURE_SERVICE_CALL;
60 }
61
62 switch (func & ~SMCCC_CONVENTION_MASK) {
63 case PSCI_VERSION:
64 *ret = PSCI_VERSION_1_1;
65 break;
66
67 case PSCI_FEATURES:
68 switch (arg0 & ~SMCCC_CONVENTION_MASK) {
69 case PSCI_CPU_SUSPEND:
Olivier Deprezf17eadc2021-01-25 15:33:38 +010070 if (plat_psci_version_get() == PSCI_VERSION_0_2) {
Andrew Walbran33645652019-04-15 12:29:31 +010071 /*
72 * PSCI 0.2 doesn't support PSCI_FEATURES so
73 * report PSCI 0.2 compatible features.
74 */
75 *ret = 0;
76 } else {
77 /* PSCI 1.x only defines two feature bits. */
Fuad Tabba8176e3e2019-08-01 10:40:36 +010078 smc_res = smc32(func, arg0, 0, 0, 0, 0, 0,
79 SMCCC_CALLER_HYPERVISOR);
Andrew Walbran6606d862019-11-15 16:43:18 +000080 *ret = smc_res.func & 0x3;
Andrew Walbran33645652019-04-15 12:29:31 +010081 }
82 break;
83
84 case PSCI_VERSION:
85 case PSCI_FEATURES:
86 case PSCI_SYSTEM_OFF:
87 case PSCI_SYSTEM_RESET:
88 case PSCI_AFFINITY_INFO:
89 case PSCI_CPU_OFF:
90 case PSCI_CPU_ON:
91 /* These are supported without special features. */
92 *ret = 0;
93 break;
94
95 default:
96 /* Everything else is unsupported. */
97 *ret = PSCI_ERROR_NOT_SUPPORTED;
98 break;
99 }
100 break;
101
102 case PSCI_SYSTEM_OFF:
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100103 smc32(PSCI_SYSTEM_OFF, 0, 0, 0, 0, 0, 0,
104 SMCCC_CALLER_HYPERVISOR);
Andrew Walbran33645652019-04-15 12:29:31 +0100105 panic("System off failed");
106 break;
107
108 case PSCI_SYSTEM_RESET:
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100109 smc32(PSCI_SYSTEM_RESET, 0, 0, 0, 0, 0, 0,
110 SMCCC_CALLER_HYPERVISOR);
Andrew Walbran33645652019-04-15 12:29:31 +0100111 panic("System reset failed");
112 break;
113
114 case PSCI_AFFINITY_INFO:
115 c = cpu_find(arg0);
116 if (!c) {
117 *ret = PSCI_ERROR_INVALID_PARAMETERS;
118 break;
119 }
120
121 if (arg1 != 0) {
122 *ret = PSCI_ERROR_NOT_SUPPORTED;
123 break;
124 }
125
126 sl_lock(&c->lock);
127 if (c->is_on) {
128 *ret = PSCI_RETURN_ON;
129 } else {
130 *ret = PSCI_RETURN_OFF;
131 }
132 sl_unlock(&c->lock);
133 break;
134
135 case PSCI_CPU_SUSPEND: {
Andrew Scull550d99b2020-02-10 13:55:00 +0000136 plat_psci_cpu_suspend(arg0);
Andrew Walbran33645652019-04-15 12:29:31 +0100137 /*
Fuad Tabbaed294af2019-12-20 10:43:01 +0000138 * Update vCPU state to wake from the provided entry point but
Andrew Walbran33645652019-04-15 12:29:31 +0100139 * if suspend returns, for example because it failed or was a
140 * standby power state, the SMC will return and the updated
Fuad Tabbaed294af2019-12-20 10:43:01 +0000141 * vCPU registers will be ignored.
Andrew Walbran33645652019-04-15 12:29:31 +0100142 */
143 arch_regs_set_pc_arg(&vcpu->regs, ipa_init(arg1), arg2);
Fuad Tabba1889a73c2019-07-31 10:27:47 +0100144 smc_res = smc64(PSCI_CPU_SUSPEND, arg0, (uintreg_t)&cpu_entry,
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100145 (uintreg_t)vcpu->cpu, 0, 0, 0,
146 SMCCC_CALLER_HYPERVISOR);
Andrew Walbran6606d862019-11-15 16:43:18 +0000147 *ret = smc_res.func;
Andrew Walbran33645652019-04-15 12:29:31 +0100148 break;
149 }
150
151 case PSCI_CPU_OFF:
152 cpu_off(vcpu->cpu);
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100153 smc32(PSCI_CPU_OFF, 0, 0, 0, 0, 0, 0, SMCCC_CALLER_HYPERVISOR);
Andrew Walbran33645652019-04-15 12:29:31 +0100154 panic("CPU off failed");
155 break;
156
157 case PSCI_CPU_ON:
158 c = cpu_find(arg0);
159 if (!c) {
160 *ret = PSCI_ERROR_INVALID_PARAMETERS;
161 break;
162 }
163
164 if (cpu_on(c, ipa_init(arg1), arg2)) {
165 *ret = PSCI_ERROR_ALREADY_ON;
166 break;
167 }
168
169 /*
170 * There's a race when turning a CPU on when it's in the
171 * process of turning off. We need to loop here while it is
172 * reported that the CPU is on (because it's about to turn
173 * itself off).
174 */
175 do {
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100176 smc_res = smc64(PSCI_CPU_ON, arg0,
177 (uintreg_t)&cpu_entry, (uintreg_t)c, 0,
178 0, 0, SMCCC_CALLER_HYPERVISOR);
Andrew Walbran6606d862019-11-15 16:43:18 +0000179 *ret = smc_res.func;
Andrew Walbran33645652019-04-15 12:29:31 +0100180 } while (*ret == PSCI_ERROR_ALREADY_ON);
181
182 if (*ret != PSCI_RETURN_SUCCESS) {
183 cpu_off(c);
184 }
185 break;
186
187 case PSCI_MIGRATE:
188 case PSCI_MIGRATE_INFO_TYPE:
189 case PSCI_MIGRATE_INFO_UP_CPU:
190 case PSCI_CPU_FREEZE:
191 case PSCI_CPU_DEFAULT_SUSPEND:
192 case PSCI_NODE_HW_STATE:
193 case PSCI_SYSTEM_SUSPEND:
194 case PSCI_SET_SYSPEND_MODE:
195 case PSCI_STAT_RESIDENCY:
196 case PSCI_STAT_COUNT:
197 case PSCI_SYSTEM_RESET2:
198 case PSCI_MEM_PROTECT:
199 case PSCI_MEM_PROTECT_CHECK_RANGE:
200 /* Block all other known PSCI calls. */
201 *ret = PSCI_ERROR_NOT_SUPPORTED;
202 break;
203
204 default:
205 return false;
206 }
207
208 return true;
209}
210
211/**
212 * Convert a PSCI CPU / affinity ID for a secondary VM to the corresponding vCPU
213 * index.
214 */
Andrew Walbranb5ab43c2020-04-30 11:32:54 +0100215ffa_vcpu_index_t vcpu_id_to_index(cpu_id_t vcpu_id)
Andrew Walbran33645652019-04-15 12:29:31 +0100216{
217 /* For now we use indices as IDs for the purposes of PSCI. */
218 return vcpu_id;
219}
220
221/**
222 * Handles PSCI requests received via HVC or SMC instructions from a secondary
223 * VM.
224 *
225 * A minimal PSCI 1.1 interface is offered which can start and stop vCPUs in
226 * collaboration with the scheduler in the primary VM.
227 *
228 * Returns true if the request was a PSCI one, false otherwise.
229 */
230bool psci_secondary_vm_handler(struct vcpu *vcpu, uint32_t func, uintreg_t arg0,
231 uintreg_t arg1, uintreg_t arg2, uintreg_t *ret,
232 struct vcpu **next)
233{
234 switch (func & ~SMCCC_CONVENTION_MASK) {
235 case PSCI_VERSION:
236 *ret = PSCI_VERSION_1_1;
237 break;
238
239 case PSCI_FEATURES:
240 switch (arg0 & ~SMCCC_CONVENTION_MASK) {
241 case PSCI_CPU_SUSPEND:
242 /*
243 * Does not offer OS-initiated mode but does use
244 * extended StateID Format.
245 */
246 *ret = 0x2;
247 break;
248
249 case PSCI_VERSION:
250 case PSCI_FEATURES:
251 case PSCI_AFFINITY_INFO:
252 case PSCI_CPU_OFF:
253 case PSCI_CPU_ON:
254 /* These are supported without special features. */
255 *ret = 0;
256 break;
257
258 default:
259 /* Everything else is unsupported. */
260 *ret = PSCI_ERROR_NOT_SUPPORTED;
261 break;
262 }
263 break;
264
265 case PSCI_AFFINITY_INFO: {
Andrew Walbran4d3fa282019-06-26 13:31:15 +0100266 cpu_id_t target_affinity = arg0;
Andrew Walbran33645652019-04-15 12:29:31 +0100267 uint32_t lowest_affinity_level = arg1;
268 struct vm *vm = vcpu->vm;
269 struct vcpu_locked target_vcpu;
Andrew Walbranb5ab43c2020-04-30 11:32:54 +0100270 ffa_vcpu_index_t target_vcpu_index =
Andrew Walbranb037d5b2019-06-25 17:19:41 +0100271 vcpu_id_to_index(target_affinity);
Andrew Walbran33645652019-04-15 12:29:31 +0100272
273 if (lowest_affinity_level != 0) {
274 /* Affinity levels greater than 0 not supported. */
275 *ret = PSCI_ERROR_INVALID_PARAMETERS;
276 break;
277 }
278
279 if (target_vcpu_index >= vm->vcpu_count) {
280 *ret = PSCI_ERROR_INVALID_PARAMETERS;
281 break;
282 }
283
284 target_vcpu = vcpu_lock(vm_get_vcpu(vm, target_vcpu_index));
285 *ret = vcpu_is_off(target_vcpu) ? PSCI_RETURN_OFF
286 : PSCI_RETURN_ON;
287 vcpu_unlock(&target_vcpu);
288 break;
289 }
290
291 case PSCI_CPU_SUSPEND: {
292 /*
293 * Downgrade suspend request to WFI and return SUCCESS, as
294 * allowed by the specification.
295 */
296 *next = api_wait_for_interrupt(vcpu);
297 *ret = PSCI_RETURN_SUCCESS;
298 break;
299 }
300
301 case PSCI_CPU_OFF:
302 /*
303 * Should never return to the caller, but in case it somehow
304 * does.
305 */
306 *ret = PSCI_ERROR_DENIED;
307 /* Tell the scheduler not to run the vCPU again. */
308 *next = api_vcpu_off(vcpu);
309 break;
310
311 case PSCI_CPU_ON: {
312 /* Parameter names as per PSCI specification. */
Andrew Walbran4d3fa282019-06-26 13:31:15 +0100313 cpu_id_t target_cpu = arg0;
Andrew Walbran33645652019-04-15 12:29:31 +0100314 ipaddr_t entry_point_address = ipa_init(arg1);
315 uint64_t context_id = arg2;
Andrew Walbranb5ab43c2020-04-30 11:32:54 +0100316 ffa_vcpu_index_t target_vcpu_index =
Andrew Walbranb037d5b2019-06-25 17:19:41 +0100317 vcpu_id_to_index(target_cpu);
Andrew Walbran33645652019-04-15 12:29:31 +0100318 struct vm *vm = vcpu->vm;
319 struct vcpu *target_vcpu;
Max Shvetsov40108e72020-08-27 12:39:50 +0100320 struct vcpu_locked vcpu_locked;
321 bool vcpu_was_off;
Andrew Walbran33645652019-04-15 12:29:31 +0100322
323 if (target_vcpu_index >= vm->vcpu_count) {
324 *ret = PSCI_ERROR_INVALID_PARAMETERS;
325 break;
326 }
327
328 target_vcpu = vm_get_vcpu(vm, target_vcpu_index);
Max Shvetsov40108e72020-08-27 12:39:50 +0100329 vcpu_locked = vcpu_lock(target_vcpu);
330 vcpu_was_off = vcpu_secondary_reset_and_start(
331 vcpu_locked, entry_point_address, context_id);
332 vcpu_unlock(&vcpu_locked);
Andrew Walbran33645652019-04-15 12:29:31 +0100333
Max Shvetsov40108e72020-08-27 12:39:50 +0100334 if (vcpu_was_off) {
Andrew Walbran33645652019-04-15 12:29:31 +0100335 /*
336 * Tell the scheduler that it can start running the new
337 * vCPU now.
338 */
339 *next = api_wake_up(vcpu, target_vcpu);
340 *ret = PSCI_RETURN_SUCCESS;
341 } else {
342 *ret = PSCI_ERROR_ALREADY_ON;
343 }
344
345 break;
346 }
347
348 case PSCI_SYSTEM_OFF:
349 case PSCI_SYSTEM_RESET:
350 case PSCI_MIGRATE:
351 case PSCI_MIGRATE_INFO_TYPE:
352 case PSCI_MIGRATE_INFO_UP_CPU:
353 case PSCI_CPU_FREEZE:
354 case PSCI_CPU_DEFAULT_SUSPEND:
355 case PSCI_NODE_HW_STATE:
356 case PSCI_SYSTEM_SUSPEND:
357 case PSCI_SET_SYSPEND_MODE:
358 case PSCI_STAT_RESIDENCY:
359 case PSCI_STAT_COUNT:
360 case PSCI_SYSTEM_RESET2:
361 case PSCI_MEM_PROTECT:
362 case PSCI_MEM_PROTECT_CHECK_RANGE:
363 /* Block all other known PSCI calls. */
364 *ret = PSCI_ERROR_NOT_SUPPORTED;
365 break;
366
367 default:
368 return false;
369 }
370
371 return true;
372}
373
374/**
375 * Handles PSCI requests received via HVC or SMC instructions from a VM.
376 * Requests from primary and secondary VMs are dealt with differently.
377 *
378 * Returns true if the request was a PSCI one, false otherwise.
379 */
380bool psci_handler(struct vcpu *vcpu, uint32_t func, uintreg_t arg0,
381 uintreg_t arg1, uintreg_t arg2, uintreg_t *ret,
382 struct vcpu **next)
383{
384 if (vcpu->vm->id == HF_PRIMARY_VM_ID) {
385 return psci_primary_vm_handler(vcpu, func, arg0, arg1, arg2,
386 ret);
387 }
388 return psci_secondary_vm_handler(vcpu, func, arg0, arg1, arg2, ret,
389 next);
390}