blob: 55704d49d5391c6654e7ad91fb1029429ca67b4c [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{
Andrew Walbranb5ab43c2020-04-30 11:32:54 +010033 struct ffa_value smc_res =
Fuad Tabba8176e3e2019-08-01 10:40:36 +010034 smc32(PSCI_VERSION, 0, 0, 0, 0, 0, 0, SMCCC_CALLER_HYPERVISOR);
Fuad Tabba1889a73c2019-07-31 10:27:47 +010035
Andrew Walbran6606d862019-11-15 16:43:18 +000036 el3_psci_version = smc_res.func;
Andrew Walbran33645652019-04-15 12:29:31 +010037
38 /* Check there's nothing unexpected about PSCI. */
39 switch (el3_psci_version) {
40 case PSCI_VERSION_0_2:
41 case PSCI_VERSION_1_0:
42 case PSCI_VERSION_1_1:
43 /* Supported EL3 PSCI version. */
Andrew Walbran17eebf92020-02-05 16:35:49 +000044 dlog_info("Found PSCI version: %#x\n", el3_psci_version);
Andrew Walbran33645652019-04-15 12:29:31 +010045 break;
46
47 default:
48 /* Unsupported EL3 PSCI version. Log a warning but continue. */
Andrew Walbran17eebf92020-02-05 16:35:49 +000049 dlog_warning("Unknown PSCI version: %#x\n", el3_psci_version);
Andrew Walbran33645652019-04-15 12:29:31 +010050 el3_psci_version = 0;
51 break;
52 }
53}
54
55/**
56 * Handles PSCI requests received via HVC or SMC instructions from the primary
57 * VM.
58 *
59 * A minimal PSCI 1.1 interface is offered which can make use of the
60 * implementation of PSCI in EL3 by acting as an adapter.
61 *
62 * Returns true if the request was a PSCI one, false otherwise.
63 */
64bool psci_primary_vm_handler(struct vcpu *vcpu, uint32_t func, uintreg_t arg0,
65 uintreg_t arg1, uintreg_t arg2, uintreg_t *ret)
66{
67 struct cpu *c;
Andrew Walbranb5ab43c2020-04-30 11:32:54 +010068 struct ffa_value smc_res;
Andrew Walbran33645652019-04-15 12:29:31 +010069
70 /*
71 * If there's a problem with the EL3 PSCI, block standard secure service
72 * calls by marking them as unknown. Other calls will be allowed to pass
73 * through.
74 *
75 * This blocks more calls than just PSCI so it may need to be made more
76 * lenient in future.
77 */
78 if (el3_psci_version == 0) {
79 *ret = SMCCC_ERROR_UNKNOWN;
80 return (func & SMCCC_SERVICE_CALL_MASK) ==
81 SMCCC_STANDARD_SECURE_SERVICE_CALL;
82 }
83
84 switch (func & ~SMCCC_CONVENTION_MASK) {
85 case PSCI_VERSION:
86 *ret = PSCI_VERSION_1_1;
87 break;
88
89 case PSCI_FEATURES:
90 switch (arg0 & ~SMCCC_CONVENTION_MASK) {
91 case PSCI_CPU_SUSPEND:
92 if (el3_psci_version == PSCI_VERSION_0_2) {
93 /*
94 * PSCI 0.2 doesn't support PSCI_FEATURES so
95 * report PSCI 0.2 compatible features.
96 */
97 *ret = 0;
98 } else {
99 /* PSCI 1.x only defines two feature bits. */
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100100 smc_res = smc32(func, arg0, 0, 0, 0, 0, 0,
101 SMCCC_CALLER_HYPERVISOR);
Andrew Walbran6606d862019-11-15 16:43:18 +0000102 *ret = smc_res.func & 0x3;
Andrew Walbran33645652019-04-15 12:29:31 +0100103 }
104 break;
105
106 case PSCI_VERSION:
107 case PSCI_FEATURES:
108 case PSCI_SYSTEM_OFF:
109 case PSCI_SYSTEM_RESET:
110 case PSCI_AFFINITY_INFO:
111 case PSCI_CPU_OFF:
112 case PSCI_CPU_ON:
113 /* These are supported without special features. */
114 *ret = 0;
115 break;
116
117 default:
118 /* Everything else is unsupported. */
119 *ret = PSCI_ERROR_NOT_SUPPORTED;
120 break;
121 }
122 break;
123
124 case PSCI_SYSTEM_OFF:
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100125 smc32(PSCI_SYSTEM_OFF, 0, 0, 0, 0, 0, 0,
126 SMCCC_CALLER_HYPERVISOR);
Andrew Walbran33645652019-04-15 12:29:31 +0100127 panic("System off failed");
128 break;
129
130 case PSCI_SYSTEM_RESET:
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100131 smc32(PSCI_SYSTEM_RESET, 0, 0, 0, 0, 0, 0,
132 SMCCC_CALLER_HYPERVISOR);
Andrew Walbran33645652019-04-15 12:29:31 +0100133 panic("System reset failed");
134 break;
135
136 case PSCI_AFFINITY_INFO:
137 c = cpu_find(arg0);
138 if (!c) {
139 *ret = PSCI_ERROR_INVALID_PARAMETERS;
140 break;
141 }
142
143 if (arg1 != 0) {
144 *ret = PSCI_ERROR_NOT_SUPPORTED;
145 break;
146 }
147
148 sl_lock(&c->lock);
149 if (c->is_on) {
150 *ret = PSCI_RETURN_ON;
151 } else {
152 *ret = PSCI_RETURN_OFF;
153 }
154 sl_unlock(&c->lock);
155 break;
156
157 case PSCI_CPU_SUSPEND: {
Andrew Scull550d99b2020-02-10 13:55:00 +0000158 plat_psci_cpu_suspend(arg0);
Andrew Walbran33645652019-04-15 12:29:31 +0100159 /*
Fuad Tabbaed294af2019-12-20 10:43:01 +0000160 * Update vCPU state to wake from the provided entry point but
Andrew Walbran33645652019-04-15 12:29:31 +0100161 * if suspend returns, for example because it failed or was a
162 * standby power state, the SMC will return and the updated
Fuad Tabbaed294af2019-12-20 10:43:01 +0000163 * vCPU registers will be ignored.
Andrew Walbran33645652019-04-15 12:29:31 +0100164 */
165 arch_regs_set_pc_arg(&vcpu->regs, ipa_init(arg1), arg2);
Fuad Tabba1889a73c2019-07-31 10:27:47 +0100166 smc_res = smc64(PSCI_CPU_SUSPEND, arg0, (uintreg_t)&cpu_entry,
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100167 (uintreg_t)vcpu->cpu, 0, 0, 0,
168 SMCCC_CALLER_HYPERVISOR);
Andrew Walbran6606d862019-11-15 16:43:18 +0000169 *ret = smc_res.func;
Andrew Walbran33645652019-04-15 12:29:31 +0100170 break;
171 }
172
173 case PSCI_CPU_OFF:
174 cpu_off(vcpu->cpu);
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100175 smc32(PSCI_CPU_OFF, 0, 0, 0, 0, 0, 0, SMCCC_CALLER_HYPERVISOR);
Andrew Walbran33645652019-04-15 12:29:31 +0100176 panic("CPU off failed");
177 break;
178
179 case PSCI_CPU_ON:
180 c = cpu_find(arg0);
181 if (!c) {
182 *ret = PSCI_ERROR_INVALID_PARAMETERS;
183 break;
184 }
185
186 if (cpu_on(c, ipa_init(arg1), arg2)) {
187 *ret = PSCI_ERROR_ALREADY_ON;
188 break;
189 }
190
191 /*
192 * There's a race when turning a CPU on when it's in the
193 * process of turning off. We need to loop here while it is
194 * reported that the CPU is on (because it's about to turn
195 * itself off).
196 */
197 do {
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100198 smc_res = smc64(PSCI_CPU_ON, arg0,
199 (uintreg_t)&cpu_entry, (uintreg_t)c, 0,
200 0, 0, SMCCC_CALLER_HYPERVISOR);
Andrew Walbran6606d862019-11-15 16:43:18 +0000201 *ret = smc_res.func;
Andrew Walbran33645652019-04-15 12:29:31 +0100202 } while (*ret == PSCI_ERROR_ALREADY_ON);
203
204 if (*ret != PSCI_RETURN_SUCCESS) {
205 cpu_off(c);
206 }
207 break;
208
209 case PSCI_MIGRATE:
210 case PSCI_MIGRATE_INFO_TYPE:
211 case PSCI_MIGRATE_INFO_UP_CPU:
212 case PSCI_CPU_FREEZE:
213 case PSCI_CPU_DEFAULT_SUSPEND:
214 case PSCI_NODE_HW_STATE:
215 case PSCI_SYSTEM_SUSPEND:
216 case PSCI_SET_SYSPEND_MODE:
217 case PSCI_STAT_RESIDENCY:
218 case PSCI_STAT_COUNT:
219 case PSCI_SYSTEM_RESET2:
220 case PSCI_MEM_PROTECT:
221 case PSCI_MEM_PROTECT_CHECK_RANGE:
222 /* Block all other known PSCI calls. */
223 *ret = PSCI_ERROR_NOT_SUPPORTED;
224 break;
225
226 default:
227 return false;
228 }
229
230 return true;
231}
232
233/**
234 * Convert a PSCI CPU / affinity ID for a secondary VM to the corresponding vCPU
235 * index.
236 */
Andrew Walbranb5ab43c2020-04-30 11:32:54 +0100237ffa_vcpu_index_t vcpu_id_to_index(cpu_id_t vcpu_id)
Andrew Walbran33645652019-04-15 12:29:31 +0100238{
239 /* For now we use indices as IDs for the purposes of PSCI. */
240 return vcpu_id;
241}
242
243/**
244 * Handles PSCI requests received via HVC or SMC instructions from a secondary
245 * VM.
246 *
247 * A minimal PSCI 1.1 interface is offered which can start and stop vCPUs in
248 * collaboration with the scheduler in the primary VM.
249 *
250 * Returns true if the request was a PSCI one, false otherwise.
251 */
252bool psci_secondary_vm_handler(struct vcpu *vcpu, uint32_t func, uintreg_t arg0,
253 uintreg_t arg1, uintreg_t arg2, uintreg_t *ret,
254 struct vcpu **next)
255{
256 switch (func & ~SMCCC_CONVENTION_MASK) {
257 case PSCI_VERSION:
258 *ret = PSCI_VERSION_1_1;
259 break;
260
261 case PSCI_FEATURES:
262 switch (arg0 & ~SMCCC_CONVENTION_MASK) {
263 case PSCI_CPU_SUSPEND:
264 /*
265 * Does not offer OS-initiated mode but does use
266 * extended StateID Format.
267 */
268 *ret = 0x2;
269 break;
270
271 case PSCI_VERSION:
272 case PSCI_FEATURES:
273 case PSCI_AFFINITY_INFO:
274 case PSCI_CPU_OFF:
275 case PSCI_CPU_ON:
276 /* These are supported without special features. */
277 *ret = 0;
278 break;
279
280 default:
281 /* Everything else is unsupported. */
282 *ret = PSCI_ERROR_NOT_SUPPORTED;
283 break;
284 }
285 break;
286
287 case PSCI_AFFINITY_INFO: {
Andrew Walbran4d3fa282019-06-26 13:31:15 +0100288 cpu_id_t target_affinity = arg0;
Andrew Walbran33645652019-04-15 12:29:31 +0100289 uint32_t lowest_affinity_level = arg1;
290 struct vm *vm = vcpu->vm;
291 struct vcpu_locked target_vcpu;
Andrew Walbranb5ab43c2020-04-30 11:32:54 +0100292 ffa_vcpu_index_t target_vcpu_index =
Andrew Walbranb037d5b2019-06-25 17:19:41 +0100293 vcpu_id_to_index(target_affinity);
Andrew Walbran33645652019-04-15 12:29:31 +0100294
295 if (lowest_affinity_level != 0) {
296 /* Affinity levels greater than 0 not supported. */
297 *ret = PSCI_ERROR_INVALID_PARAMETERS;
298 break;
299 }
300
301 if (target_vcpu_index >= vm->vcpu_count) {
302 *ret = PSCI_ERROR_INVALID_PARAMETERS;
303 break;
304 }
305
306 target_vcpu = vcpu_lock(vm_get_vcpu(vm, target_vcpu_index));
307 *ret = vcpu_is_off(target_vcpu) ? PSCI_RETURN_OFF
308 : PSCI_RETURN_ON;
309 vcpu_unlock(&target_vcpu);
310 break;
311 }
312
313 case PSCI_CPU_SUSPEND: {
314 /*
315 * Downgrade suspend request to WFI and return SUCCESS, as
316 * allowed by the specification.
317 */
318 *next = api_wait_for_interrupt(vcpu);
319 *ret = PSCI_RETURN_SUCCESS;
320 break;
321 }
322
323 case PSCI_CPU_OFF:
324 /*
325 * Should never return to the caller, but in case it somehow
326 * does.
327 */
328 *ret = PSCI_ERROR_DENIED;
329 /* Tell the scheduler not to run the vCPU again. */
330 *next = api_vcpu_off(vcpu);
331 break;
332
333 case PSCI_CPU_ON: {
334 /* Parameter names as per PSCI specification. */
Andrew Walbran4d3fa282019-06-26 13:31:15 +0100335 cpu_id_t target_cpu = arg0;
Andrew Walbran33645652019-04-15 12:29:31 +0100336 ipaddr_t entry_point_address = ipa_init(arg1);
337 uint64_t context_id = arg2;
Andrew Walbranb5ab43c2020-04-30 11:32:54 +0100338 ffa_vcpu_index_t target_vcpu_index =
Andrew Walbranb037d5b2019-06-25 17:19:41 +0100339 vcpu_id_to_index(target_cpu);
Andrew Walbran33645652019-04-15 12:29:31 +0100340 struct vm *vm = vcpu->vm;
341 struct vcpu *target_vcpu;
342
343 if (target_vcpu_index >= vm->vcpu_count) {
344 *ret = PSCI_ERROR_INVALID_PARAMETERS;
345 break;
346 }
347
348 target_vcpu = vm_get_vcpu(vm, target_vcpu_index);
349
350 if (vcpu_secondary_reset_and_start(
351 target_vcpu, entry_point_address, context_id)) {
352 /*
353 * Tell the scheduler that it can start running the new
354 * vCPU now.
355 */
356 *next = api_wake_up(vcpu, target_vcpu);
357 *ret = PSCI_RETURN_SUCCESS;
358 } else {
359 *ret = PSCI_ERROR_ALREADY_ON;
360 }
361
362 break;
363 }
364
365 case PSCI_SYSTEM_OFF:
366 case PSCI_SYSTEM_RESET:
367 case PSCI_MIGRATE:
368 case PSCI_MIGRATE_INFO_TYPE:
369 case PSCI_MIGRATE_INFO_UP_CPU:
370 case PSCI_CPU_FREEZE:
371 case PSCI_CPU_DEFAULT_SUSPEND:
372 case PSCI_NODE_HW_STATE:
373 case PSCI_SYSTEM_SUSPEND:
374 case PSCI_SET_SYSPEND_MODE:
375 case PSCI_STAT_RESIDENCY:
376 case PSCI_STAT_COUNT:
377 case PSCI_SYSTEM_RESET2:
378 case PSCI_MEM_PROTECT:
379 case PSCI_MEM_PROTECT_CHECK_RANGE:
380 /* Block all other known PSCI calls. */
381 *ret = PSCI_ERROR_NOT_SUPPORTED;
382 break;
383
384 default:
385 return false;
386 }
387
388 return true;
389}
390
391/**
392 * Handles PSCI requests received via HVC or SMC instructions from a VM.
393 * Requests from primary and secondary VMs are dealt with differently.
394 *
395 * Returns true if the request was a PSCI one, false otherwise.
396 */
397bool psci_handler(struct vcpu *vcpu, uint32_t func, uintreg_t arg0,
398 uintreg_t arg1, uintreg_t arg2, uintreg_t *ret,
399 struct vcpu **next)
400{
401 if (vcpu->vm->id == HF_PRIMARY_VM_ID) {
402 return psci_primary_vm_handler(vcpu, func, arg0, arg1, arg2,
403 ret);
404 }
405 return psci_secondary_vm_handler(vcpu, func, arg0, arg1, arg2, ret,
406 next);
407}