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