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