blob: b1e18438ed154377c7f5d7b620c6c03f02a12f75 [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;
Olivier Deprez70f8a4a2022-09-26 09:17:56 +020041 struct vcpu *vcpu_target;
42 struct vcpu_locked vcpu_locked;
Andrew Walbran33645652019-04-15 12:29:31 +010043
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 */
Olivier Deprezcf5310c2021-10-28 15:31:55 +020052
Olivier Deprezf17eadc2021-01-25 15:33:38 +010053 if (plat_psci_version_get() == 0) {
Andrew Walbran33645652019-04-15 12:29:31 +010054 *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) {
Olivier Deprezcf5310c2021-10-28 15:31:55 +020066 case SMCCC_VERSION_FUNC_ID:
67 *ret = SMCCC_VERSION_1_2;
68 break;
69
Andrew Walbran33645652019-04-15 12:29:31 +010070 case PSCI_CPU_SUSPEND:
Olivier Deprezf17eadc2021-01-25 15:33:38 +010071 if (plat_psci_version_get() == PSCI_VERSION_0_2) {
Andrew Walbran33645652019-04-15 12:29:31 +010072 /*
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. */
Fuad Tabba8176e3e2019-08-01 10:40:36 +010079 smc_res = smc32(func, arg0, 0, 0, 0, 0, 0,
80 SMCCC_CALLER_HYPERVISOR);
Andrew Walbran6606d862019-11-15 16:43:18 +000081 *ret = smc_res.func & 0x3;
Andrew Walbran33645652019-04-15 12:29:31 +010082 }
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:
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100104 smc32(PSCI_SYSTEM_OFF, 0, 0, 0, 0, 0, 0,
105 SMCCC_CALLER_HYPERVISOR);
Andrew Walbran33645652019-04-15 12:29:31 +0100106 panic("System off failed");
107 break;
108
109 case PSCI_SYSTEM_RESET:
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100110 smc32(PSCI_SYSTEM_RESET, 0, 0, 0, 0, 0, 0,
111 SMCCC_CALLER_HYPERVISOR);
Andrew Walbran33645652019-04-15 12:29:31 +0100112 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: {
Andrew Scull550d99b2020-02-10 13:55:00 +0000137 plat_psci_cpu_suspend(arg0);
Andrew Walbran33645652019-04-15 12:29:31 +0100138 /*
Fuad Tabbaed294af2019-12-20 10:43:01 +0000139 * Update vCPU state to wake from the provided entry point but
Andrew Walbran33645652019-04-15 12:29:31 +0100140 * if suspend returns, for example because it failed or was a
141 * standby power state, the SMC will return and the updated
Fuad Tabbaed294af2019-12-20 10:43:01 +0000142 * vCPU registers will be ignored.
Andrew Walbran33645652019-04-15 12:29:31 +0100143 */
144 arch_regs_set_pc_arg(&vcpu->regs, ipa_init(arg1), arg2);
Fuad Tabba1889a73c2019-07-31 10:27:47 +0100145 smc_res = smc64(PSCI_CPU_SUSPEND, arg0, (uintreg_t)&cpu_entry,
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100146 (uintreg_t)vcpu->cpu, 0, 0, 0,
147 SMCCC_CALLER_HYPERVISOR);
Andrew Walbran6606d862019-11-15 16:43:18 +0000148 *ret = smc_res.func;
Andrew Walbran33645652019-04-15 12:29:31 +0100149 break;
150 }
151
152 case PSCI_CPU_OFF:
153 cpu_off(vcpu->cpu);
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100154 smc32(PSCI_CPU_OFF, 0, 0, 0, 0, 0, 0, SMCCC_CALLER_HYPERVISOR);
Andrew Walbran33645652019-04-15 12:29:31 +0100155 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
Olivier Deprez70f8a4a2022-09-26 09:17:56 +0200165 if (cpu_on(c)) {
Andrew Walbran33645652019-04-15 12:29:31 +0100166 *ret = PSCI_ERROR_ALREADY_ON;
167 break;
168 }
169
Olivier Deprez70f8a4a2022-09-26 09:17:56 +0200170 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
Andrew Walbran33645652019-04-15 12:29:31 +0100175 /*
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 {
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100182 smc_res = smc64(PSCI_CPU_ON, arg0,
183 (uintreg_t)&cpu_entry, (uintreg_t)c, 0,
184 0, 0, SMCCC_CALLER_HYPERVISOR);
Andrew Walbran6606d862019-11-15 16:43:18 +0000185 *ret = smc_res.func;
Andrew Walbran33645652019-04-15 12:29:31 +0100186 } while (*ret == 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 * Convert a PSCI CPU / affinity ID for a secondary VM to the corresponding vCPU
219 * index.
220 */
Andrew Walbranb5ab43c2020-04-30 11:32:54 +0100221ffa_vcpu_index_t vcpu_id_to_index(cpu_id_t vcpu_id)
Andrew Walbran33645652019-04-15 12:29:31 +0100222{
223 /* For now we use indices as IDs for the purposes of PSCI. */
224 return vcpu_id;
225}
226
227/**
228 * Handles PSCI requests received via HVC or SMC instructions from a secondary
229 * VM.
230 *
231 * A minimal PSCI 1.1 interface is offered which can start and stop vCPUs in
232 * collaboration with the scheduler in the primary VM.
233 *
234 * Returns true if the request was a PSCI one, false otherwise.
235 */
236bool psci_secondary_vm_handler(struct vcpu *vcpu, uint32_t func, uintreg_t arg0,
237 uintreg_t arg1, uintreg_t arg2, uintreg_t *ret,
238 struct vcpu **next)
239{
240 switch (func & ~SMCCC_CONVENTION_MASK) {
241 case PSCI_VERSION:
242 *ret = PSCI_VERSION_1_1;
243 break;
244
245 case PSCI_FEATURES:
246 switch (arg0 & ~SMCCC_CONVENTION_MASK) {
247 case PSCI_CPU_SUSPEND:
248 /*
249 * Does not offer OS-initiated mode but does use
250 * extended StateID Format.
251 */
252 *ret = 0x2;
253 break;
254
255 case PSCI_VERSION:
256 case PSCI_FEATURES:
257 case PSCI_AFFINITY_INFO:
258 case PSCI_CPU_OFF:
259 case PSCI_CPU_ON:
260 /* These are supported without special features. */
261 *ret = 0;
262 break;
263
264 default:
265 /* Everything else is unsupported. */
266 *ret = PSCI_ERROR_NOT_SUPPORTED;
267 break;
268 }
269 break;
270
271 case PSCI_AFFINITY_INFO: {
Andrew Walbran4d3fa282019-06-26 13:31:15 +0100272 cpu_id_t target_affinity = arg0;
Andrew Walbran33645652019-04-15 12:29:31 +0100273 uint32_t lowest_affinity_level = arg1;
274 struct vm *vm = vcpu->vm;
275 struct vcpu_locked target_vcpu;
Andrew Walbranb5ab43c2020-04-30 11:32:54 +0100276 ffa_vcpu_index_t target_vcpu_index =
Andrew Walbranb037d5b2019-06-25 17:19:41 +0100277 vcpu_id_to_index(target_affinity);
Andrew Walbran33645652019-04-15 12:29:31 +0100278
279 if (lowest_affinity_level != 0) {
280 /* Affinity levels greater than 0 not supported. */
281 *ret = PSCI_ERROR_INVALID_PARAMETERS;
282 break;
283 }
284
285 if (target_vcpu_index >= vm->vcpu_count) {
286 *ret = PSCI_ERROR_INVALID_PARAMETERS;
287 break;
288 }
289
290 target_vcpu = vcpu_lock(vm_get_vcpu(vm, target_vcpu_index));
291 *ret = vcpu_is_off(target_vcpu) ? PSCI_RETURN_OFF
292 : PSCI_RETURN_ON;
293 vcpu_unlock(&target_vcpu);
294 break;
295 }
296
297 case PSCI_CPU_SUSPEND: {
298 /*
299 * Downgrade suspend request to WFI and return SUCCESS, as
300 * allowed by the specification.
301 */
302 *next = api_wait_for_interrupt(vcpu);
303 *ret = PSCI_RETURN_SUCCESS;
304 break;
305 }
306
307 case PSCI_CPU_OFF:
308 /*
309 * Should never return to the caller, but in case it somehow
310 * does.
311 */
312 *ret = PSCI_ERROR_DENIED;
313 /* Tell the scheduler not to run the vCPU again. */
314 *next = api_vcpu_off(vcpu);
315 break;
316
317 case PSCI_CPU_ON: {
318 /* Parameter names as per PSCI specification. */
Andrew Walbran4d3fa282019-06-26 13:31:15 +0100319 cpu_id_t target_cpu = arg0;
Andrew Walbran33645652019-04-15 12:29:31 +0100320 ipaddr_t entry_point_address = ipa_init(arg1);
321 uint64_t context_id = arg2;
Andrew Walbranb5ab43c2020-04-30 11:32:54 +0100322 ffa_vcpu_index_t target_vcpu_index =
Andrew Walbranb037d5b2019-06-25 17:19:41 +0100323 vcpu_id_to_index(target_cpu);
Andrew Walbran33645652019-04-15 12:29:31 +0100324 struct vm *vm = vcpu->vm;
325 struct vcpu *target_vcpu;
Max Shvetsov40108e72020-08-27 12:39:50 +0100326 struct vcpu_locked vcpu_locked;
327 bool vcpu_was_off;
Andrew Walbran33645652019-04-15 12:29:31 +0100328
329 if (target_vcpu_index >= vm->vcpu_count) {
330 *ret = PSCI_ERROR_INVALID_PARAMETERS;
331 break;
332 }
333
334 target_vcpu = vm_get_vcpu(vm, target_vcpu_index);
Max Shvetsov40108e72020-08-27 12:39:50 +0100335 vcpu_locked = vcpu_lock(target_vcpu);
336 vcpu_was_off = vcpu_secondary_reset_and_start(
337 vcpu_locked, entry_point_address, context_id);
338 vcpu_unlock(&vcpu_locked);
Andrew Walbran33645652019-04-15 12:29:31 +0100339
Max Shvetsov40108e72020-08-27 12:39:50 +0100340 if (vcpu_was_off) {
Andrew Walbran33645652019-04-15 12:29:31 +0100341 /*
342 * Tell the scheduler that it can start running the new
343 * vCPU now.
344 */
345 *next = api_wake_up(vcpu, target_vcpu);
346 *ret = PSCI_RETURN_SUCCESS;
347 } else {
348 *ret = PSCI_ERROR_ALREADY_ON;
349 }
350
351 break;
352 }
353
354 case PSCI_SYSTEM_OFF:
355 case PSCI_SYSTEM_RESET:
356 case PSCI_MIGRATE:
357 case PSCI_MIGRATE_INFO_TYPE:
358 case PSCI_MIGRATE_INFO_UP_CPU:
359 case PSCI_CPU_FREEZE:
360 case PSCI_CPU_DEFAULT_SUSPEND:
361 case PSCI_NODE_HW_STATE:
362 case PSCI_SYSTEM_SUSPEND:
363 case PSCI_SET_SYSPEND_MODE:
364 case PSCI_STAT_RESIDENCY:
365 case PSCI_STAT_COUNT:
366 case PSCI_SYSTEM_RESET2:
367 case PSCI_MEM_PROTECT:
368 case PSCI_MEM_PROTECT_CHECK_RANGE:
369 /* Block all other known PSCI calls. */
370 *ret = PSCI_ERROR_NOT_SUPPORTED;
371 break;
372
373 default:
374 return false;
375 }
376
377 return true;
378}
379
380/**
381 * Handles PSCI requests received via HVC or SMC instructions from a VM.
382 * Requests from primary and secondary VMs are dealt with differently.
383 *
384 * Returns true if the request was a PSCI one, false otherwise.
385 */
386bool psci_handler(struct vcpu *vcpu, uint32_t func, uintreg_t arg0,
387 uintreg_t arg1, uintreg_t arg2, uintreg_t *ret,
388 struct vcpu **next)
389{
390 if (vcpu->vm->id == HF_PRIMARY_VM_ID) {
391 return psci_primary_vm_handler(vcpu, func, arg0, arg1, arg2,
392 ret);
393 }
394 return psci_secondary_vm_handler(vcpu, func, arg0, arg1, arg2, ret,
395 next);
396}