blob: 3c78e57ba0b5675464ff6c8ffce712f9fe4f8f7c [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 Deprezf17eadc2021-01-25 15:33:38 +010050 if (plat_psci_version_get() == 0) {
Andrew Walbran33645652019-04-15 12:29:31 +010051 *ret = SMCCC_ERROR_UNKNOWN;
52 return (func & SMCCC_SERVICE_CALL_MASK) ==
53 SMCCC_STANDARD_SECURE_SERVICE_CALL;
54 }
55
56 switch (func & ~SMCCC_CONVENTION_MASK) {
57 case PSCI_VERSION:
58 *ret = PSCI_VERSION_1_1;
59 break;
60
61 case PSCI_FEATURES:
62 switch (arg0 & ~SMCCC_CONVENTION_MASK) {
63 case PSCI_CPU_SUSPEND:
Olivier Deprezf17eadc2021-01-25 15:33:38 +010064 if (plat_psci_version_get() == PSCI_VERSION_0_2) {
Andrew Walbran33645652019-04-15 12:29:31 +010065 /*
66 * PSCI 0.2 doesn't support PSCI_FEATURES so
67 * report PSCI 0.2 compatible features.
68 */
69 *ret = 0;
70 } else {
71 /* PSCI 1.x only defines two feature bits. */
Fuad Tabba8176e3e2019-08-01 10:40:36 +010072 smc_res = smc32(func, arg0, 0, 0, 0, 0, 0,
73 SMCCC_CALLER_HYPERVISOR);
Andrew Walbran6606d862019-11-15 16:43:18 +000074 *ret = smc_res.func & 0x3;
Andrew Walbran33645652019-04-15 12:29:31 +010075 }
76 break;
77
78 case PSCI_VERSION:
79 case PSCI_FEATURES:
80 case PSCI_SYSTEM_OFF:
81 case PSCI_SYSTEM_RESET:
82 case PSCI_AFFINITY_INFO:
83 case PSCI_CPU_OFF:
84 case PSCI_CPU_ON:
85 /* These are supported without special features. */
86 *ret = 0;
87 break;
88
89 default:
90 /* Everything else is unsupported. */
91 *ret = PSCI_ERROR_NOT_SUPPORTED;
92 break;
93 }
94 break;
95
96 case PSCI_SYSTEM_OFF:
Fuad Tabba8176e3e2019-08-01 10:40:36 +010097 smc32(PSCI_SYSTEM_OFF, 0, 0, 0, 0, 0, 0,
98 SMCCC_CALLER_HYPERVISOR);
Andrew Walbran33645652019-04-15 12:29:31 +010099 panic("System off failed");
100 break;
101
102 case PSCI_SYSTEM_RESET:
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100103 smc32(PSCI_SYSTEM_RESET, 0, 0, 0, 0, 0, 0,
104 SMCCC_CALLER_HYPERVISOR);
Andrew Walbran33645652019-04-15 12:29:31 +0100105 panic("System reset failed");
106 break;
107
108 case PSCI_AFFINITY_INFO:
109 c = cpu_find(arg0);
110 if (!c) {
111 *ret = PSCI_ERROR_INVALID_PARAMETERS;
112 break;
113 }
114
115 if (arg1 != 0) {
116 *ret = PSCI_ERROR_NOT_SUPPORTED;
117 break;
118 }
119
120 sl_lock(&c->lock);
121 if (c->is_on) {
122 *ret = PSCI_RETURN_ON;
123 } else {
124 *ret = PSCI_RETURN_OFF;
125 }
126 sl_unlock(&c->lock);
127 break;
128
129 case PSCI_CPU_SUSPEND: {
Andrew Scull550d99b2020-02-10 13:55:00 +0000130 plat_psci_cpu_suspend(arg0);
Andrew Walbran33645652019-04-15 12:29:31 +0100131 /*
Fuad Tabbaed294af2019-12-20 10:43:01 +0000132 * Update vCPU state to wake from the provided entry point but
Andrew Walbran33645652019-04-15 12:29:31 +0100133 * if suspend returns, for example because it failed or was a
134 * standby power state, the SMC will return and the updated
Fuad Tabbaed294af2019-12-20 10:43:01 +0000135 * vCPU registers will be ignored.
Andrew Walbran33645652019-04-15 12:29:31 +0100136 */
137 arch_regs_set_pc_arg(&vcpu->regs, ipa_init(arg1), arg2);
Fuad Tabba1889a73c2019-07-31 10:27:47 +0100138 smc_res = smc64(PSCI_CPU_SUSPEND, arg0, (uintreg_t)&cpu_entry,
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100139 (uintreg_t)vcpu->cpu, 0, 0, 0,
140 SMCCC_CALLER_HYPERVISOR);
Andrew Walbran6606d862019-11-15 16:43:18 +0000141 *ret = smc_res.func;
Andrew Walbran33645652019-04-15 12:29:31 +0100142 break;
143 }
144
145 case PSCI_CPU_OFF:
146 cpu_off(vcpu->cpu);
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100147 smc32(PSCI_CPU_OFF, 0, 0, 0, 0, 0, 0, SMCCC_CALLER_HYPERVISOR);
Andrew Walbran33645652019-04-15 12:29:31 +0100148 panic("CPU off failed");
149 break;
150
151 case PSCI_CPU_ON:
152 c = cpu_find(arg0);
153 if (!c) {
154 *ret = PSCI_ERROR_INVALID_PARAMETERS;
155 break;
156 }
157
158 if (cpu_on(c, ipa_init(arg1), arg2)) {
159 *ret = PSCI_ERROR_ALREADY_ON;
160 break;
161 }
162
163 /*
164 * There's a race when turning a CPU on when it's in the
165 * process of turning off. We need to loop here while it is
166 * reported that the CPU is on (because it's about to turn
167 * itself off).
168 */
169 do {
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100170 smc_res = smc64(PSCI_CPU_ON, arg0,
171 (uintreg_t)&cpu_entry, (uintreg_t)c, 0,
172 0, 0, SMCCC_CALLER_HYPERVISOR);
Andrew Walbran6606d862019-11-15 16:43:18 +0000173 *ret = smc_res.func;
Andrew Walbran33645652019-04-15 12:29:31 +0100174 } while (*ret == PSCI_ERROR_ALREADY_ON);
175
176 if (*ret != PSCI_RETURN_SUCCESS) {
177 cpu_off(c);
178 }
179 break;
180
181 case PSCI_MIGRATE:
182 case PSCI_MIGRATE_INFO_TYPE:
183 case PSCI_MIGRATE_INFO_UP_CPU:
184 case PSCI_CPU_FREEZE:
185 case PSCI_CPU_DEFAULT_SUSPEND:
186 case PSCI_NODE_HW_STATE:
187 case PSCI_SYSTEM_SUSPEND:
188 case PSCI_SET_SYSPEND_MODE:
189 case PSCI_STAT_RESIDENCY:
190 case PSCI_STAT_COUNT:
191 case PSCI_SYSTEM_RESET2:
192 case PSCI_MEM_PROTECT:
193 case PSCI_MEM_PROTECT_CHECK_RANGE:
194 /* Block all other known PSCI calls. */
195 *ret = PSCI_ERROR_NOT_SUPPORTED;
196 break;
197
198 default:
199 return false;
200 }
201
202 return true;
203}
204
205/**
206 * Convert a PSCI CPU / affinity ID for a secondary VM to the corresponding vCPU
207 * index.
208 */
Andrew Walbranb5ab43c2020-04-30 11:32:54 +0100209ffa_vcpu_index_t vcpu_id_to_index(cpu_id_t vcpu_id)
Andrew Walbran33645652019-04-15 12:29:31 +0100210{
211 /* For now we use indices as IDs for the purposes of PSCI. */
212 return vcpu_id;
213}
214
215/**
216 * Handles PSCI requests received via HVC or SMC instructions from a secondary
217 * VM.
218 *
219 * A minimal PSCI 1.1 interface is offered which can start and stop vCPUs in
220 * collaboration with the scheduler in the primary VM.
221 *
222 * Returns true if the request was a PSCI one, false otherwise.
223 */
224bool psci_secondary_vm_handler(struct vcpu *vcpu, uint32_t func, uintreg_t arg0,
225 uintreg_t arg1, uintreg_t arg2, uintreg_t *ret,
226 struct vcpu **next)
227{
228 switch (func & ~SMCCC_CONVENTION_MASK) {
229 case PSCI_VERSION:
230 *ret = PSCI_VERSION_1_1;
231 break;
232
233 case PSCI_FEATURES:
234 switch (arg0 & ~SMCCC_CONVENTION_MASK) {
235 case PSCI_CPU_SUSPEND:
236 /*
237 * Does not offer OS-initiated mode but does use
238 * extended StateID Format.
239 */
240 *ret = 0x2;
241 break;
242
243 case PSCI_VERSION:
244 case PSCI_FEATURES:
245 case PSCI_AFFINITY_INFO:
246 case PSCI_CPU_OFF:
247 case PSCI_CPU_ON:
248 /* These are supported without special features. */
249 *ret = 0;
250 break;
251
252 default:
253 /* Everything else is unsupported. */
254 *ret = PSCI_ERROR_NOT_SUPPORTED;
255 break;
256 }
257 break;
258
259 case PSCI_AFFINITY_INFO: {
Andrew Walbran4d3fa282019-06-26 13:31:15 +0100260 cpu_id_t target_affinity = arg0;
Andrew Walbran33645652019-04-15 12:29:31 +0100261 uint32_t lowest_affinity_level = arg1;
262 struct vm *vm = vcpu->vm;
263 struct vcpu_locked target_vcpu;
Andrew Walbranb5ab43c2020-04-30 11:32:54 +0100264 ffa_vcpu_index_t target_vcpu_index =
Andrew Walbranb037d5b2019-06-25 17:19:41 +0100265 vcpu_id_to_index(target_affinity);
Andrew Walbran33645652019-04-15 12:29:31 +0100266
267 if (lowest_affinity_level != 0) {
268 /* Affinity levels greater than 0 not supported. */
269 *ret = PSCI_ERROR_INVALID_PARAMETERS;
270 break;
271 }
272
273 if (target_vcpu_index >= vm->vcpu_count) {
274 *ret = PSCI_ERROR_INVALID_PARAMETERS;
275 break;
276 }
277
278 target_vcpu = vcpu_lock(vm_get_vcpu(vm, target_vcpu_index));
279 *ret = vcpu_is_off(target_vcpu) ? PSCI_RETURN_OFF
280 : PSCI_RETURN_ON;
281 vcpu_unlock(&target_vcpu);
282 break;
283 }
284
285 case PSCI_CPU_SUSPEND: {
286 /*
287 * Downgrade suspend request to WFI and return SUCCESS, as
288 * allowed by the specification.
289 */
290 *next = api_wait_for_interrupt(vcpu);
291 *ret = PSCI_RETURN_SUCCESS;
292 break;
293 }
294
295 case PSCI_CPU_OFF:
296 /*
297 * Should never return to the caller, but in case it somehow
298 * does.
299 */
300 *ret = PSCI_ERROR_DENIED;
301 /* Tell the scheduler not to run the vCPU again. */
302 *next = api_vcpu_off(vcpu);
303 break;
304
305 case PSCI_CPU_ON: {
306 /* Parameter names as per PSCI specification. */
Andrew Walbran4d3fa282019-06-26 13:31:15 +0100307 cpu_id_t target_cpu = arg0;
Andrew Walbran33645652019-04-15 12:29:31 +0100308 ipaddr_t entry_point_address = ipa_init(arg1);
309 uint64_t context_id = arg2;
Andrew Walbranb5ab43c2020-04-30 11:32:54 +0100310 ffa_vcpu_index_t target_vcpu_index =
Andrew Walbranb037d5b2019-06-25 17:19:41 +0100311 vcpu_id_to_index(target_cpu);
Andrew Walbran33645652019-04-15 12:29:31 +0100312 struct vm *vm = vcpu->vm;
313 struct vcpu *target_vcpu;
Max Shvetsov40108e72020-08-27 12:39:50 +0100314 struct vcpu_locked vcpu_locked;
315 bool vcpu_was_off;
Andrew Walbran33645652019-04-15 12:29:31 +0100316
317 if (target_vcpu_index >= vm->vcpu_count) {
318 *ret = PSCI_ERROR_INVALID_PARAMETERS;
319 break;
320 }
321
322 target_vcpu = vm_get_vcpu(vm, target_vcpu_index);
Max Shvetsov40108e72020-08-27 12:39:50 +0100323 vcpu_locked = vcpu_lock(target_vcpu);
324 vcpu_was_off = vcpu_secondary_reset_and_start(
325 vcpu_locked, entry_point_address, context_id);
326 vcpu_unlock(&vcpu_locked);
Andrew Walbran33645652019-04-15 12:29:31 +0100327
Max Shvetsov40108e72020-08-27 12:39:50 +0100328 if (vcpu_was_off) {
Andrew Walbran33645652019-04-15 12:29:31 +0100329 /*
330 * Tell the scheduler that it can start running the new
331 * vCPU now.
332 */
333 *next = api_wake_up(vcpu, target_vcpu);
334 *ret = PSCI_RETURN_SUCCESS;
335 } else {
336 *ret = PSCI_ERROR_ALREADY_ON;
337 }
338
339 break;
340 }
341
342 case PSCI_SYSTEM_OFF:
343 case PSCI_SYSTEM_RESET:
344 case PSCI_MIGRATE:
345 case PSCI_MIGRATE_INFO_TYPE:
346 case PSCI_MIGRATE_INFO_UP_CPU:
347 case PSCI_CPU_FREEZE:
348 case PSCI_CPU_DEFAULT_SUSPEND:
349 case PSCI_NODE_HW_STATE:
350 case PSCI_SYSTEM_SUSPEND:
351 case PSCI_SET_SYSPEND_MODE:
352 case PSCI_STAT_RESIDENCY:
353 case PSCI_STAT_COUNT:
354 case PSCI_SYSTEM_RESET2:
355 case PSCI_MEM_PROTECT:
356 case PSCI_MEM_PROTECT_CHECK_RANGE:
357 /* Block all other known PSCI calls. */
358 *ret = PSCI_ERROR_NOT_SUPPORTED;
359 break;
360
361 default:
362 return false;
363 }
364
365 return true;
366}
367
368/**
369 * Handles PSCI requests received via HVC or SMC instructions from a VM.
370 * Requests from primary and secondary VMs are dealt with differently.
371 *
372 * Returns true if the request was a PSCI one, false otherwise.
373 */
374bool psci_handler(struct vcpu *vcpu, uint32_t func, uintreg_t arg0,
375 uintreg_t arg1, uintreg_t arg2, uintreg_t *ret,
376 struct vcpu **next)
377{
378 if (vcpu->vm->id == HF_PRIMARY_VM_ID) {
379 return psci_primary_vm_handler(vcpu, func, arg0, arg1, arg2,
380 ret);
381 }
382 return psci_secondary_vm_handler(vcpu, func, arg0, arg1, arg2, ret,
383 next);
384}