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