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