blob: aabe979baef7370a41c93680bfd8fbb32aed1dd6 [file] [log] [blame]
Andrew Walbran33645652019-04-15 12:29:31 +01001/*
2 * Copyright 2019 The Hafnium Authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "psci_handler.h"
18
19#include <stdint.h>
20
Andrew Scull550d99b2020-02-10 13:55:00 +000021#include "hf/arch/plat/psci.h"
Andrew Walbran33645652019-04-15 12:29:31 +010022#include "hf/arch/types.h"
23
24#include "hf/api.h"
25#include "hf/cpu.h"
26#include "hf/dlog.h"
27#include "hf/panic.h"
Andrew Walbranb037d5b2019-06-25 17:19:41 +010028#include "hf/spci.h"
Andrew Walbran33645652019-04-15 12:29:31 +010029#include "hf/vm.h"
30
31#include "psci.h"
32#include "smc.h"
33
34static uint32_t el3_psci_version;
35
36void cpu_entry(struct cpu *c);
37
38/* Performs arch specific boot time initialisation. */
39void arch_one_time_init(void)
40{
Andrew Walbran6606d862019-11-15 16:43:18 +000041 struct spci_value smc_res =
Fuad Tabba8176e3e2019-08-01 10:40:36 +010042 smc32(PSCI_VERSION, 0, 0, 0, 0, 0, 0, SMCCC_CALLER_HYPERVISOR);
Fuad Tabba1889a73c2019-07-31 10:27:47 +010043
Andrew Walbran6606d862019-11-15 16:43:18 +000044 el3_psci_version = smc_res.func;
Andrew Walbran33645652019-04-15 12:29:31 +010045
46 /* Check there's nothing unexpected about PSCI. */
47 switch (el3_psci_version) {
48 case PSCI_VERSION_0_2:
49 case PSCI_VERSION_1_0:
50 case PSCI_VERSION_1_1:
51 /* Supported EL3 PSCI version. */
Andrew Walbran17eebf92020-02-05 16:35:49 +000052 dlog_info("Found PSCI version: %#x\n", el3_psci_version);
Andrew Walbran33645652019-04-15 12:29:31 +010053 break;
54
55 default:
56 /* Unsupported EL3 PSCI version. Log a warning but continue. */
Andrew Walbran17eebf92020-02-05 16:35:49 +000057 dlog_warning("Unknown PSCI version: %#x\n", el3_psci_version);
Andrew Walbran33645652019-04-15 12:29:31 +010058 el3_psci_version = 0;
59 break;
60 }
61}
62
63/**
64 * Handles PSCI requests received via HVC or SMC instructions from the primary
65 * VM.
66 *
67 * A minimal PSCI 1.1 interface is offered which can make use of the
68 * implementation of PSCI in EL3 by acting as an adapter.
69 *
70 * Returns true if the request was a PSCI one, false otherwise.
71 */
72bool psci_primary_vm_handler(struct vcpu *vcpu, uint32_t func, uintreg_t arg0,
73 uintreg_t arg1, uintreg_t arg2, uintreg_t *ret)
74{
75 struct cpu *c;
Andrew Walbran6606d862019-11-15 16:43:18 +000076 struct spci_value smc_res;
Andrew Walbran33645652019-04-15 12:29:31 +010077
78 /*
79 * If there's a problem with the EL3 PSCI, block standard secure service
80 * calls by marking them as unknown. Other calls will be allowed to pass
81 * through.
82 *
83 * This blocks more calls than just PSCI so it may need to be made more
84 * lenient in future.
85 */
86 if (el3_psci_version == 0) {
87 *ret = SMCCC_ERROR_UNKNOWN;
88 return (func & SMCCC_SERVICE_CALL_MASK) ==
89 SMCCC_STANDARD_SECURE_SERVICE_CALL;
90 }
91
92 switch (func & ~SMCCC_CONVENTION_MASK) {
93 case PSCI_VERSION:
94 *ret = PSCI_VERSION_1_1;
95 break;
96
97 case PSCI_FEATURES:
98 switch (arg0 & ~SMCCC_CONVENTION_MASK) {
99 case PSCI_CPU_SUSPEND:
100 if (el3_psci_version == PSCI_VERSION_0_2) {
101 /*
102 * PSCI 0.2 doesn't support PSCI_FEATURES so
103 * report PSCI 0.2 compatible features.
104 */
105 *ret = 0;
106 } else {
107 /* PSCI 1.x only defines two feature bits. */
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100108 smc_res = smc32(func, arg0, 0, 0, 0, 0, 0,
109 SMCCC_CALLER_HYPERVISOR);
Andrew Walbran6606d862019-11-15 16:43:18 +0000110 *ret = smc_res.func & 0x3;
Andrew Walbran33645652019-04-15 12:29:31 +0100111 }
112 break;
113
114 case PSCI_VERSION:
115 case PSCI_FEATURES:
116 case PSCI_SYSTEM_OFF:
117 case PSCI_SYSTEM_RESET:
118 case PSCI_AFFINITY_INFO:
119 case PSCI_CPU_OFF:
120 case PSCI_CPU_ON:
121 /* These are supported without special features. */
122 *ret = 0;
123 break;
124
125 default:
126 /* Everything else is unsupported. */
127 *ret = PSCI_ERROR_NOT_SUPPORTED;
128 break;
129 }
130 break;
131
132 case PSCI_SYSTEM_OFF:
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100133 smc32(PSCI_SYSTEM_OFF, 0, 0, 0, 0, 0, 0,
134 SMCCC_CALLER_HYPERVISOR);
Andrew Walbran33645652019-04-15 12:29:31 +0100135 panic("System off failed");
136 break;
137
138 case PSCI_SYSTEM_RESET:
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100139 smc32(PSCI_SYSTEM_RESET, 0, 0, 0, 0, 0, 0,
140 SMCCC_CALLER_HYPERVISOR);
Andrew Walbran33645652019-04-15 12:29:31 +0100141 panic("System reset failed");
142 break;
143
144 case PSCI_AFFINITY_INFO:
145 c = cpu_find(arg0);
146 if (!c) {
147 *ret = PSCI_ERROR_INVALID_PARAMETERS;
148 break;
149 }
150
151 if (arg1 != 0) {
152 *ret = PSCI_ERROR_NOT_SUPPORTED;
153 break;
154 }
155
156 sl_lock(&c->lock);
157 if (c->is_on) {
158 *ret = PSCI_RETURN_ON;
159 } else {
160 *ret = PSCI_RETURN_OFF;
161 }
162 sl_unlock(&c->lock);
163 break;
164
165 case PSCI_CPU_SUSPEND: {
Andrew Scull550d99b2020-02-10 13:55:00 +0000166 plat_psci_cpu_suspend(arg0);
Andrew Walbran33645652019-04-15 12:29:31 +0100167 /*
Fuad Tabbaed294af2019-12-20 10:43:01 +0000168 * Update vCPU state to wake from the provided entry point but
Andrew Walbran33645652019-04-15 12:29:31 +0100169 * if suspend returns, for example because it failed or was a
170 * standby power state, the SMC will return and the updated
Fuad Tabbaed294af2019-12-20 10:43:01 +0000171 * vCPU registers will be ignored.
Andrew Walbran33645652019-04-15 12:29:31 +0100172 */
173 arch_regs_set_pc_arg(&vcpu->regs, ipa_init(arg1), arg2);
Fuad Tabba1889a73c2019-07-31 10:27:47 +0100174 smc_res = smc64(PSCI_CPU_SUSPEND, arg0, (uintreg_t)&cpu_entry,
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100175 (uintreg_t)vcpu->cpu, 0, 0, 0,
176 SMCCC_CALLER_HYPERVISOR);
Andrew Walbran6606d862019-11-15 16:43:18 +0000177 *ret = smc_res.func;
Andrew Walbran33645652019-04-15 12:29:31 +0100178 break;
179 }
180
181 case PSCI_CPU_OFF:
182 cpu_off(vcpu->cpu);
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100183 smc32(PSCI_CPU_OFF, 0, 0, 0, 0, 0, 0, SMCCC_CALLER_HYPERVISOR);
Andrew Walbran33645652019-04-15 12:29:31 +0100184 panic("CPU off failed");
185 break;
186
187 case PSCI_CPU_ON:
188 c = cpu_find(arg0);
189 if (!c) {
190 *ret = PSCI_ERROR_INVALID_PARAMETERS;
191 break;
192 }
193
194 if (cpu_on(c, ipa_init(arg1), arg2)) {
195 *ret = PSCI_ERROR_ALREADY_ON;
196 break;
197 }
198
199 /*
200 * There's a race when turning a CPU on when it's in the
201 * process of turning off. We need to loop here while it is
202 * reported that the CPU is on (because it's about to turn
203 * itself off).
204 */
205 do {
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100206 smc_res = smc64(PSCI_CPU_ON, arg0,
207 (uintreg_t)&cpu_entry, (uintreg_t)c, 0,
208 0, 0, SMCCC_CALLER_HYPERVISOR);
Andrew Walbran6606d862019-11-15 16:43:18 +0000209 *ret = smc_res.func;
Andrew Walbran33645652019-04-15 12:29:31 +0100210 } while (*ret == PSCI_ERROR_ALREADY_ON);
211
212 if (*ret != PSCI_RETURN_SUCCESS) {
213 cpu_off(c);
214 }
215 break;
216
217 case PSCI_MIGRATE:
218 case PSCI_MIGRATE_INFO_TYPE:
219 case PSCI_MIGRATE_INFO_UP_CPU:
220 case PSCI_CPU_FREEZE:
221 case PSCI_CPU_DEFAULT_SUSPEND:
222 case PSCI_NODE_HW_STATE:
223 case PSCI_SYSTEM_SUSPEND:
224 case PSCI_SET_SYSPEND_MODE:
225 case PSCI_STAT_RESIDENCY:
226 case PSCI_STAT_COUNT:
227 case PSCI_SYSTEM_RESET2:
228 case PSCI_MEM_PROTECT:
229 case PSCI_MEM_PROTECT_CHECK_RANGE:
230 /* Block all other known PSCI calls. */
231 *ret = PSCI_ERROR_NOT_SUPPORTED;
232 break;
233
234 default:
235 return false;
236 }
237
238 return true;
239}
240
241/**
242 * Convert a PSCI CPU / affinity ID for a secondary VM to the corresponding vCPU
243 * index.
244 */
Andrew Walbran4d3fa282019-06-26 13:31:15 +0100245spci_vcpu_index_t vcpu_id_to_index(cpu_id_t vcpu_id)
Andrew Walbran33645652019-04-15 12:29:31 +0100246{
247 /* For now we use indices as IDs for the purposes of PSCI. */
248 return vcpu_id;
249}
250
251/**
252 * Handles PSCI requests received via HVC or SMC instructions from a secondary
253 * VM.
254 *
255 * A minimal PSCI 1.1 interface is offered which can start and stop vCPUs in
256 * collaboration with the scheduler in the primary VM.
257 *
258 * Returns true if the request was a PSCI one, false otherwise.
259 */
260bool psci_secondary_vm_handler(struct vcpu *vcpu, uint32_t func, uintreg_t arg0,
261 uintreg_t arg1, uintreg_t arg2, uintreg_t *ret,
262 struct vcpu **next)
263{
264 switch (func & ~SMCCC_CONVENTION_MASK) {
265 case PSCI_VERSION:
266 *ret = PSCI_VERSION_1_1;
267 break;
268
269 case PSCI_FEATURES:
270 switch (arg0 & ~SMCCC_CONVENTION_MASK) {
271 case PSCI_CPU_SUSPEND:
272 /*
273 * Does not offer OS-initiated mode but does use
274 * extended StateID Format.
275 */
276 *ret = 0x2;
277 break;
278
279 case PSCI_VERSION:
280 case PSCI_FEATURES:
281 case PSCI_AFFINITY_INFO:
282 case PSCI_CPU_OFF:
283 case PSCI_CPU_ON:
284 /* These are supported without special features. */
285 *ret = 0;
286 break;
287
288 default:
289 /* Everything else is unsupported. */
290 *ret = PSCI_ERROR_NOT_SUPPORTED;
291 break;
292 }
293 break;
294
295 case PSCI_AFFINITY_INFO: {
Andrew Walbran4d3fa282019-06-26 13:31:15 +0100296 cpu_id_t target_affinity = arg0;
Andrew Walbran33645652019-04-15 12:29:31 +0100297 uint32_t lowest_affinity_level = arg1;
298 struct vm *vm = vcpu->vm;
299 struct vcpu_locked target_vcpu;
Andrew Walbranb037d5b2019-06-25 17:19:41 +0100300 spci_vcpu_index_t target_vcpu_index =
301 vcpu_id_to_index(target_affinity);
Andrew Walbran33645652019-04-15 12:29:31 +0100302
303 if (lowest_affinity_level != 0) {
304 /* Affinity levels greater than 0 not supported. */
305 *ret = PSCI_ERROR_INVALID_PARAMETERS;
306 break;
307 }
308
309 if (target_vcpu_index >= vm->vcpu_count) {
310 *ret = PSCI_ERROR_INVALID_PARAMETERS;
311 break;
312 }
313
314 target_vcpu = vcpu_lock(vm_get_vcpu(vm, target_vcpu_index));
315 *ret = vcpu_is_off(target_vcpu) ? PSCI_RETURN_OFF
316 : PSCI_RETURN_ON;
317 vcpu_unlock(&target_vcpu);
318 break;
319 }
320
321 case PSCI_CPU_SUSPEND: {
322 /*
323 * Downgrade suspend request to WFI and return SUCCESS, as
324 * allowed by the specification.
325 */
326 *next = api_wait_for_interrupt(vcpu);
327 *ret = PSCI_RETURN_SUCCESS;
328 break;
329 }
330
331 case PSCI_CPU_OFF:
332 /*
333 * Should never return to the caller, but in case it somehow
334 * does.
335 */
336 *ret = PSCI_ERROR_DENIED;
337 /* Tell the scheduler not to run the vCPU again. */
338 *next = api_vcpu_off(vcpu);
339 break;
340
341 case PSCI_CPU_ON: {
342 /* Parameter names as per PSCI specification. */
Andrew Walbran4d3fa282019-06-26 13:31:15 +0100343 cpu_id_t target_cpu = arg0;
Andrew Walbran33645652019-04-15 12:29:31 +0100344 ipaddr_t entry_point_address = ipa_init(arg1);
345 uint64_t context_id = arg2;
Andrew Walbranb037d5b2019-06-25 17:19:41 +0100346 spci_vcpu_index_t target_vcpu_index =
347 vcpu_id_to_index(target_cpu);
Andrew Walbran33645652019-04-15 12:29:31 +0100348 struct vm *vm = vcpu->vm;
349 struct vcpu *target_vcpu;
350
351 if (target_vcpu_index >= vm->vcpu_count) {
352 *ret = PSCI_ERROR_INVALID_PARAMETERS;
353 break;
354 }
355
356 target_vcpu = vm_get_vcpu(vm, target_vcpu_index);
357
358 if (vcpu_secondary_reset_and_start(
359 target_vcpu, entry_point_address, context_id)) {
360 /*
361 * Tell the scheduler that it can start running the new
362 * vCPU now.
363 */
364 *next = api_wake_up(vcpu, target_vcpu);
365 *ret = PSCI_RETURN_SUCCESS;
366 } else {
367 *ret = PSCI_ERROR_ALREADY_ON;
368 }
369
370 break;
371 }
372
373 case PSCI_SYSTEM_OFF:
374 case PSCI_SYSTEM_RESET:
375 case PSCI_MIGRATE:
376 case PSCI_MIGRATE_INFO_TYPE:
377 case PSCI_MIGRATE_INFO_UP_CPU:
378 case PSCI_CPU_FREEZE:
379 case PSCI_CPU_DEFAULT_SUSPEND:
380 case PSCI_NODE_HW_STATE:
381 case PSCI_SYSTEM_SUSPEND:
382 case PSCI_SET_SYSPEND_MODE:
383 case PSCI_STAT_RESIDENCY:
384 case PSCI_STAT_COUNT:
385 case PSCI_SYSTEM_RESET2:
386 case PSCI_MEM_PROTECT:
387 case PSCI_MEM_PROTECT_CHECK_RANGE:
388 /* Block all other known PSCI calls. */
389 *ret = PSCI_ERROR_NOT_SUPPORTED;
390 break;
391
392 default:
393 return false;
394 }
395
396 return true;
397}
398
399/**
400 * Handles PSCI requests received via HVC or SMC instructions from a VM.
401 * Requests from primary and secondary VMs are dealt with differently.
402 *
403 * Returns true if the request was a PSCI one, false otherwise.
404 */
405bool psci_handler(struct vcpu *vcpu, uint32_t func, uintreg_t arg0,
406 uintreg_t arg1, uintreg_t arg2, uintreg_t *ret,
407 struct vcpu **next)
408{
409 if (vcpu->vm->id == HF_PRIMARY_VM_ID) {
410 return psci_primary_vm_handler(vcpu, func, arg0, arg1, arg2,
411 ret);
412 }
413 return psci_secondary_vm_handler(vcpu, func, arg0, arg1, arg2, ret,
414 next);
415}