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