blob: 9aa85701e24a68f11db3d000c9c82ac3ad6ee111 [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
21#include "hf/arch/types.h"
22
23#include "hf/api.h"
24#include "hf/cpu.h"
25#include "hf/dlog.h"
26#include "hf/panic.h"
Andrew Walbranb037d5b2019-06-25 17:19:41 +010027#include "hf/spci.h"
Andrew Walbran33645652019-04-15 12:29:31 +010028#include "hf/vm.h"
29
30#include "psci.h"
31#include "smc.h"
32
33static uint32_t el3_psci_version;
34
35void cpu_entry(struct cpu *c);
36
37/* Performs arch specific boot time initialisation. */
38void arch_one_time_init(void)
39{
Andrew Scullce688f02019-09-30 12:54:14 +010040 struct smc_result smc_res =
Fuad Tabba8176e3e2019-08-01 10:40:36 +010041 smc32(PSCI_VERSION, 0, 0, 0, 0, 0, 0, SMCCC_CALLER_HYPERVISOR);
Fuad Tabba1889a73c2019-07-31 10:27:47 +010042
43 el3_psci_version = smc_res.res0;
Andrew Walbran33645652019-04-15 12:29:31 +010044
45 /* Check there's nothing unexpected about PSCI. */
46 switch (el3_psci_version) {
47 case PSCI_VERSION_0_2:
48 case PSCI_VERSION_1_0:
49 case PSCI_VERSION_1_1:
50 /* Supported EL3 PSCI version. */
Andrew Walbranac5b2612019-07-12 16:44:19 +010051 dlog("Found PSCI version: %#x\n", el3_psci_version);
Andrew Walbran33645652019-04-15 12:29:31 +010052 break;
53
54 default:
55 /* Unsupported EL3 PSCI version. Log a warning but continue. */
Andrew Walbranac5b2612019-07-12 16:44:19 +010056 dlog("Warning: unknown PSCI version: %#x\n", el3_psci_version);
Andrew Walbran33645652019-04-15 12:29:31 +010057 el3_psci_version = 0;
58 break;
59 }
60}
61
62/**
63 * Handles PSCI requests received via HVC or SMC instructions from the primary
64 * VM.
65 *
66 * A minimal PSCI 1.1 interface is offered which can make use of the
67 * implementation of PSCI in EL3 by acting as an adapter.
68 *
69 * Returns true if the request was a PSCI one, false otherwise.
70 */
71bool psci_primary_vm_handler(struct vcpu *vcpu, uint32_t func, uintreg_t arg0,
72 uintreg_t arg1, uintreg_t arg2, uintreg_t *ret)
73{
74 struct cpu *c;
Andrew Scullce688f02019-09-30 12:54:14 +010075 struct smc_result smc_res;
Andrew Walbran33645652019-04-15 12:29:31 +010076
77 /*
78 * If there's a problem with the EL3 PSCI, block standard secure service
79 * calls by marking them as unknown. Other calls will be allowed to pass
80 * through.
81 *
82 * This blocks more calls than just PSCI so it may need to be made more
83 * lenient in future.
84 */
85 if (el3_psci_version == 0) {
86 *ret = SMCCC_ERROR_UNKNOWN;
87 return (func & SMCCC_SERVICE_CALL_MASK) ==
88 SMCCC_STANDARD_SECURE_SERVICE_CALL;
89 }
90
91 switch (func & ~SMCCC_CONVENTION_MASK) {
92 case PSCI_VERSION:
93 *ret = PSCI_VERSION_1_1;
94 break;
95
96 case PSCI_FEATURES:
97 switch (arg0 & ~SMCCC_CONVENTION_MASK) {
98 case PSCI_CPU_SUSPEND:
99 if (el3_psci_version == PSCI_VERSION_0_2) {
100 /*
101 * PSCI 0.2 doesn't support PSCI_FEATURES so
102 * report PSCI 0.2 compatible features.
103 */
104 *ret = 0;
105 } else {
106 /* PSCI 1.x only defines two feature bits. */
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100107 smc_res = smc32(func, arg0, 0, 0, 0, 0, 0,
108 SMCCC_CALLER_HYPERVISOR);
Fuad Tabba1889a73c2019-07-31 10:27:47 +0100109 *ret = smc_res.res0 & 0x3;
Andrew Walbran33645652019-04-15 12:29:31 +0100110 }
111 break;
112
113 case PSCI_VERSION:
114 case PSCI_FEATURES:
115 case PSCI_SYSTEM_OFF:
116 case PSCI_SYSTEM_RESET:
117 case PSCI_AFFINITY_INFO:
118 case PSCI_CPU_OFF:
119 case PSCI_CPU_ON:
120 /* These are supported without special features. */
121 *ret = 0;
122 break;
123
124 default:
125 /* Everything else is unsupported. */
126 *ret = PSCI_ERROR_NOT_SUPPORTED;
127 break;
128 }
129 break;
130
131 case PSCI_SYSTEM_OFF:
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100132 smc32(PSCI_SYSTEM_OFF, 0, 0, 0, 0, 0, 0,
133 SMCCC_CALLER_HYPERVISOR);
Andrew Walbran33645652019-04-15 12:29:31 +0100134 panic("System off failed");
135 break;
136
137 case PSCI_SYSTEM_RESET:
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100138 smc32(PSCI_SYSTEM_RESET, 0, 0, 0, 0, 0, 0,
139 SMCCC_CALLER_HYPERVISOR);
Andrew Walbran33645652019-04-15 12:29:31 +0100140 panic("System reset failed");
141 break;
142
143 case PSCI_AFFINITY_INFO:
144 c = cpu_find(arg0);
145 if (!c) {
146 *ret = PSCI_ERROR_INVALID_PARAMETERS;
147 break;
148 }
149
150 if (arg1 != 0) {
151 *ret = PSCI_ERROR_NOT_SUPPORTED;
152 break;
153 }
154
155 sl_lock(&c->lock);
156 if (c->is_on) {
157 *ret = PSCI_RETURN_ON;
158 } else {
159 *ret = PSCI_RETURN_OFF;
160 }
161 sl_unlock(&c->lock);
162 break;
163
164 case PSCI_CPU_SUSPEND: {
165 /*
166 * Update vcpu state to wake from the provided entry point but
167 * if suspend returns, for example because it failed or was a
168 * standby power state, the SMC will return and the updated
169 * vcpu registers will be ignored.
170 */
171 arch_regs_set_pc_arg(&vcpu->regs, ipa_init(arg1), arg2);
Fuad Tabba1889a73c2019-07-31 10:27:47 +0100172 smc_res = smc64(PSCI_CPU_SUSPEND, arg0, (uintreg_t)&cpu_entry,
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100173 (uintreg_t)vcpu->cpu, 0, 0, 0,
174 SMCCC_CALLER_HYPERVISOR);
Fuad Tabba1889a73c2019-07-31 10:27:47 +0100175 *ret = smc_res.res0;
Andrew Walbran33645652019-04-15 12:29:31 +0100176 break;
177 }
178
179 case PSCI_CPU_OFF:
180 cpu_off(vcpu->cpu);
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100181 smc32(PSCI_CPU_OFF, 0, 0, 0, 0, 0, 0, SMCCC_CALLER_HYPERVISOR);
Andrew Walbran33645652019-04-15 12:29:31 +0100182 panic("CPU off failed");
183 break;
184
185 case PSCI_CPU_ON:
186 c = cpu_find(arg0);
187 if (!c) {
188 *ret = PSCI_ERROR_INVALID_PARAMETERS;
189 break;
190 }
191
192 if (cpu_on(c, ipa_init(arg1), arg2)) {
193 *ret = PSCI_ERROR_ALREADY_ON;
194 break;
195 }
196
197 /*
198 * There's a race when turning a CPU on when it's in the
199 * process of turning off. We need to loop here while it is
200 * reported that the CPU is on (because it's about to turn
201 * itself off).
202 */
203 do {
Fuad Tabba8176e3e2019-08-01 10:40:36 +0100204 smc_res = smc64(PSCI_CPU_ON, arg0,
205 (uintreg_t)&cpu_entry, (uintreg_t)c, 0,
206 0, 0, SMCCC_CALLER_HYPERVISOR);
Fuad Tabba1889a73c2019-07-31 10:27:47 +0100207 *ret = smc_res.res0;
Andrew Walbran33645652019-04-15 12:29:31 +0100208 } while (*ret == PSCI_ERROR_ALREADY_ON);
209
210 if (*ret != PSCI_RETURN_SUCCESS) {
211 cpu_off(c);
212 }
213 break;
214
215 case PSCI_MIGRATE:
216 case PSCI_MIGRATE_INFO_TYPE:
217 case PSCI_MIGRATE_INFO_UP_CPU:
218 case PSCI_CPU_FREEZE:
219 case PSCI_CPU_DEFAULT_SUSPEND:
220 case PSCI_NODE_HW_STATE:
221 case PSCI_SYSTEM_SUSPEND:
222 case PSCI_SET_SYSPEND_MODE:
223 case PSCI_STAT_RESIDENCY:
224 case PSCI_STAT_COUNT:
225 case PSCI_SYSTEM_RESET2:
226 case PSCI_MEM_PROTECT:
227 case PSCI_MEM_PROTECT_CHECK_RANGE:
228 /* Block all other known PSCI calls. */
229 *ret = PSCI_ERROR_NOT_SUPPORTED;
230 break;
231
232 default:
233 return false;
234 }
235
236 return true;
237}
238
239/**
240 * Convert a PSCI CPU / affinity ID for a secondary VM to the corresponding vCPU
241 * index.
242 */
Andrew Walbran4d3fa282019-06-26 13:31:15 +0100243spci_vcpu_index_t vcpu_id_to_index(cpu_id_t vcpu_id)
Andrew Walbran33645652019-04-15 12:29:31 +0100244{
245 /* For now we use indices as IDs for the purposes of PSCI. */
246 return vcpu_id;
247}
248
249/**
250 * Handles PSCI requests received via HVC or SMC instructions from a secondary
251 * VM.
252 *
253 * A minimal PSCI 1.1 interface is offered which can start and stop vCPUs in
254 * collaboration with the scheduler in the primary VM.
255 *
256 * Returns true if the request was a PSCI one, false otherwise.
257 */
258bool psci_secondary_vm_handler(struct vcpu *vcpu, uint32_t func, uintreg_t arg0,
259 uintreg_t arg1, uintreg_t arg2, uintreg_t *ret,
260 struct vcpu **next)
261{
262 switch (func & ~SMCCC_CONVENTION_MASK) {
263 case PSCI_VERSION:
264 *ret = PSCI_VERSION_1_1;
265 break;
266
267 case PSCI_FEATURES:
268 switch (arg0 & ~SMCCC_CONVENTION_MASK) {
269 case PSCI_CPU_SUSPEND:
270 /*
271 * Does not offer OS-initiated mode but does use
272 * extended StateID Format.
273 */
274 *ret = 0x2;
275 break;
276
277 case PSCI_VERSION:
278 case PSCI_FEATURES:
279 case PSCI_AFFINITY_INFO:
280 case PSCI_CPU_OFF:
281 case PSCI_CPU_ON:
282 /* These are supported without special features. */
283 *ret = 0;
284 break;
285
286 default:
287 /* Everything else is unsupported. */
288 *ret = PSCI_ERROR_NOT_SUPPORTED;
289 break;
290 }
291 break;
292
293 case PSCI_AFFINITY_INFO: {
Andrew Walbran4d3fa282019-06-26 13:31:15 +0100294 cpu_id_t target_affinity = arg0;
Andrew Walbran33645652019-04-15 12:29:31 +0100295 uint32_t lowest_affinity_level = arg1;
296 struct vm *vm = vcpu->vm;
297 struct vcpu_locked target_vcpu;
Andrew Walbranb037d5b2019-06-25 17:19:41 +0100298 spci_vcpu_index_t target_vcpu_index =
299 vcpu_id_to_index(target_affinity);
Andrew Walbran33645652019-04-15 12:29:31 +0100300
301 if (lowest_affinity_level != 0) {
302 /* Affinity levels greater than 0 not supported. */
303 *ret = PSCI_ERROR_INVALID_PARAMETERS;
304 break;
305 }
306
307 if (target_vcpu_index >= vm->vcpu_count) {
308 *ret = PSCI_ERROR_INVALID_PARAMETERS;
309 break;
310 }
311
312 target_vcpu = vcpu_lock(vm_get_vcpu(vm, target_vcpu_index));
313 *ret = vcpu_is_off(target_vcpu) ? PSCI_RETURN_OFF
314 : PSCI_RETURN_ON;
315 vcpu_unlock(&target_vcpu);
316 break;
317 }
318
319 case PSCI_CPU_SUSPEND: {
320 /*
321 * Downgrade suspend request to WFI and return SUCCESS, as
322 * allowed by the specification.
323 */
324 *next = api_wait_for_interrupt(vcpu);
325 *ret = PSCI_RETURN_SUCCESS;
326 break;
327 }
328
329 case PSCI_CPU_OFF:
330 /*
331 * Should never return to the caller, but in case it somehow
332 * does.
333 */
334 *ret = PSCI_ERROR_DENIED;
335 /* Tell the scheduler not to run the vCPU again. */
336 *next = api_vcpu_off(vcpu);
337 break;
338
339 case PSCI_CPU_ON: {
340 /* Parameter names as per PSCI specification. */
Andrew Walbran4d3fa282019-06-26 13:31:15 +0100341 cpu_id_t target_cpu = arg0;
Andrew Walbran33645652019-04-15 12:29:31 +0100342 ipaddr_t entry_point_address = ipa_init(arg1);
343 uint64_t context_id = arg2;
Andrew Walbranb037d5b2019-06-25 17:19:41 +0100344 spci_vcpu_index_t target_vcpu_index =
345 vcpu_id_to_index(target_cpu);
Andrew Walbran33645652019-04-15 12:29:31 +0100346 struct vm *vm = vcpu->vm;
347 struct vcpu *target_vcpu;
348
349 if (target_vcpu_index >= vm->vcpu_count) {
350 *ret = PSCI_ERROR_INVALID_PARAMETERS;
351 break;
352 }
353
354 target_vcpu = vm_get_vcpu(vm, target_vcpu_index);
355
356 if (vcpu_secondary_reset_and_start(
357 target_vcpu, entry_point_address, context_id)) {
358 /*
359 * Tell the scheduler that it can start running the new
360 * vCPU now.
361 */
362 *next = api_wake_up(vcpu, target_vcpu);
363 *ret = PSCI_RETURN_SUCCESS;
364 } else {
365 *ret = PSCI_ERROR_ALREADY_ON;
366 }
367
368 break;
369 }
370
371 case PSCI_SYSTEM_OFF:
372 case PSCI_SYSTEM_RESET:
373 case PSCI_MIGRATE:
374 case PSCI_MIGRATE_INFO_TYPE:
375 case PSCI_MIGRATE_INFO_UP_CPU:
376 case PSCI_CPU_FREEZE:
377 case PSCI_CPU_DEFAULT_SUSPEND:
378 case PSCI_NODE_HW_STATE:
379 case PSCI_SYSTEM_SUSPEND:
380 case PSCI_SET_SYSPEND_MODE:
381 case PSCI_STAT_RESIDENCY:
382 case PSCI_STAT_COUNT:
383 case PSCI_SYSTEM_RESET2:
384 case PSCI_MEM_PROTECT:
385 case PSCI_MEM_PROTECT_CHECK_RANGE:
386 /* Block all other known PSCI calls. */
387 *ret = PSCI_ERROR_NOT_SUPPORTED;
388 break;
389
390 default:
391 return false;
392 }
393
394 return true;
395}
396
397/**
398 * Handles PSCI requests received via HVC or SMC instructions from a VM.
399 * Requests from primary and secondary VMs are dealt with differently.
400 *
401 * Returns true if the request was a PSCI one, false otherwise.
402 */
403bool psci_handler(struct vcpu *vcpu, uint32_t func, uintreg_t arg0,
404 uintreg_t arg1, uintreg_t arg2, uintreg_t *ret,
405 struct vcpu **next)
406{
407 if (vcpu->vm->id == HF_PRIMARY_VM_ID) {
408 return psci_primary_vm_handler(vcpu, func, arg0, arg1, arg2,
409 ret);
410 }
411 return psci_secondary_vm_handler(vcpu, func, arg0, arg1, arg2, ret,
412 next);
413}