xref: /openbsd/sys/dev/fdt/psci.c (revision f6aab3d8)
1 /*	$OpenBSD: psci.c,v 1.14 2023/02/19 17:16:13 kettenis Exp $	*/
2 
3 /*
4  * Copyright (c) 2016 Jonathan Gray <jsg@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>
20 #include <sys/device.h>
21 #include <sys/systm.h>
22 
23 #include <machine/bus.h>
24 #include <machine/fdt.h>
25 
26 #include <dev/ofw/openfirm.h>
27 #include <dev/ofw/fdt.h>
28 
29 #include <dev/fdt/pscivar.h>
30 
31 extern void (*cpuresetfn)(void);
32 extern void (*powerdownfn)(void);
33 
34 #define SMCCC_VERSION		0x80000000
35 #define SMCCC_ARCH_FEATURES	0x80000001
36 #define SMCCC_ARCH_WORKAROUND_1	0x80008000
37 #define SMCCC_ARCH_WORKAROUND_3	0x80003fff
38 
39 #define PSCI_VERSION		0x84000000
40 #ifdef __LP64__
41 #define CPU_SUSPEND		0xc4000001
42 #else
43 #define CPU_SUSPEND		0x84000001
44 #endif
45 #define CPU_OFF			0x84000002
46 #ifdef __LP64__
47 #define CPU_ON			0xc4000003
48 #else
49 #define CPU_ON			0x84000003
50 #endif
51 #define SYSTEM_OFF		0x84000008
52 #define SYSTEM_RESET		0x84000009
53 #define PSCI_FEATURES		0x8400000a
54 #ifdef __LP64__
55 #define SYSTEM_SUSPEND		0xc400000e
56 #else
57 #define SYSTEM_SUSPEND		0x8400000e
58 #endif
59 
60 struct psci_softc {
61 	struct device	 sc_dev;
62 	register_t	 (*sc_callfn)(register_t, register_t, register_t,
63 			     register_t);
64 	uint32_t	 sc_psci_version;
65 	uint32_t	 sc_system_off;
66 	uint32_t	 sc_system_reset;
67 	uint32_t	 sc_system_suspend;
68 	uint32_t	 sc_cpu_on;
69 	uint32_t	 sc_cpu_off;
70 	uint32_t	 sc_cpu_suspend;
71 
72 	uint32_t	 sc_smccc_version;
73 	uint32_t	 sc_method;
74 };
75 
76 struct psci_softc *psci_sc;
77 
78 int	psci_match(struct device *, void *, void *);
79 void	psci_attach(struct device *, struct device *, void *);
80 void	psci_reset(void);
81 void	psci_powerdown(void);
82 
83 extern register_t hvc_call(register_t, register_t, register_t, register_t);
84 extern register_t smc_call(register_t, register_t, register_t, register_t);
85 
86 int32_t smccc_version(void);
87 int32_t smccc_arch_features(uint32_t);
88 
89 uint32_t psci_version(void);
90 int32_t psci_features(uint32_t);
91 
92 const struct cfattach psci_ca = {
93 	sizeof(struct psci_softc), psci_match, psci_attach
94 };
95 
96 struct cfdriver psci_cd = {
97 	NULL, "psci", DV_DULL
98 };
99 
100 int
101 psci_match(struct device *parent, void *match, void *aux)
102 {
103 	struct fdt_attach_args *faa = aux;
104 
105 	return OF_is_compatible(faa->fa_node, "arm,psci") ||
106 	    OF_is_compatible(faa->fa_node, "arm,psci-0.2") ||
107 	    OF_is_compatible(faa->fa_node, "arm,psci-1.0");
108 }
109 
110 void
111 psci_attach(struct device *parent, struct device *self, void *aux)
112 {
113 	struct psci_softc *sc = (struct psci_softc *)self;
114 	struct fdt_attach_args *faa = aux;
115 	char method[128];
116 	uint32_t version;
117 
118 	if (OF_getprop(faa->fa_node, "method", method, sizeof(method))) {
119 		if (strcmp(method, "hvc") == 0) {
120 			sc->sc_callfn = hvc_call;
121 			sc->sc_method = PSCI_METHOD_HVC;
122 		} else if (strcmp(method, "smc") == 0) {
123 			sc->sc_callfn = smc_call;
124 			sc->sc_method = PSCI_METHOD_SMC;
125 		}
126 	}
127 
128 	/*
129 	 * The function IDs are only to be parsed for the old specification
130 	 * (as in version 0.1).  All newer implementations are supposed to
131 	 * use the specified values.
132 	 */
133 	if (OF_is_compatible(faa->fa_node, "arm,psci-0.2") ||
134 	    OF_is_compatible(faa->fa_node, "arm,psci-1.0")) {
135 		sc->sc_psci_version = PSCI_VERSION;
136 		sc->sc_system_off = SYSTEM_OFF;
137 		sc->sc_system_reset = SYSTEM_RESET;
138 		sc->sc_cpu_on = CPU_ON;
139 		sc->sc_cpu_off = CPU_OFF;
140 		sc->sc_cpu_suspend = CPU_SUSPEND;
141 	} else if (OF_is_compatible(faa->fa_node, "arm,psci")) {
142 		sc->sc_system_off = OF_getpropint(faa->fa_node,
143 		    "system_off", 0);
144 		sc->sc_system_reset = OF_getpropint(faa->fa_node,
145 		    "system_reset", 0);
146 		sc->sc_cpu_on = OF_getpropint(faa->fa_node, "cpu_on", 0);
147 		sc->sc_cpu_off = OF_getpropint(faa->fa_node, "cpu_off", 0);
148 		sc->sc_cpu_suspend = OF_getpropint(faa->fa_node,
149 		    "cpu_suspend", 0);
150 	}
151 
152 	psci_sc = sc;
153 
154 	version = psci_version();
155 	printf(": PSCI %d.%d", version >> 16, version & 0xffff);
156 
157 	if (version >= 0x10000) {
158 		if (psci_features(SMCCC_VERSION) == PSCI_SUCCESS) {
159 			sc->sc_smccc_version = smccc_version();
160 			printf(", SMCCC %d.%d", sc->sc_smccc_version >> 16,
161 			    sc->sc_smccc_version & 0xffff);
162 		}
163 		if (psci_features(SYSTEM_SUSPEND) == PSCI_SUCCESS) {
164 			sc->sc_system_suspend = SYSTEM_SUSPEND;
165 			printf(", SYSTEM_SUSPEND");
166 		}
167 	}
168 
169 	printf("\n");
170 
171 	if (sc->sc_system_off != 0)
172 		powerdownfn = psci_powerdown;
173 	if (sc->sc_system_reset != 0)
174 		cpuresetfn = psci_reset;
175 }
176 
177 void
178 psci_reset(void)
179 {
180 	struct psci_softc *sc = psci_sc;
181 
182 	if (sc->sc_callfn)
183 		(*sc->sc_callfn)(sc->sc_system_reset, 0, 0, 0);
184 }
185 
186 void
187 psci_powerdown(void)
188 {
189 	struct psci_softc *sc = psci_sc;
190 
191 	if (sc->sc_callfn)
192 		(*sc->sc_callfn)(sc->sc_system_off, 0, 0, 0);
193 }
194 
195 /*
196  * Firmware-based workaround for CVE-2017-5715.  We determine whether
197  * the workaround is actually implemented and needed the first time we
198  * are invoked such that we only make the firmware call when appropriate.
199  */
200 
201 void
202 psci_flush_bp_none(void)
203 {
204 }
205 
206 void
207 psci_flush_bp_smccc_arch_workaround_1(void)
208 {
209 	struct psci_softc *sc = psci_sc;
210 
211 	(*sc->sc_callfn)(SMCCC_ARCH_WORKAROUND_1, 0, 0, 0);
212 }
213 
214 void
215 psci_flush_bp_smccc_arch_workaround_3(void)
216 {
217 	struct psci_softc *sc = psci_sc;
218 
219 	(*sc->sc_callfn)(SMCCC_ARCH_WORKAROUND_3, 0, 0, 0);
220 }
221 
222 void
223 psci_flush_bp(void)
224 {
225 	struct psci_softc *sc = psci_sc;
226 	struct cpu_info *ci = curcpu();
227 
228 	/*
229 	 * SMCCC 1.1 allows us to detect if the workaround is
230 	 * implemented and needed.
231 	 */
232 	if (sc && sc->sc_smccc_version >= 0x10001 &&
233 	    smccc_arch_features(SMCCC_ARCH_WORKAROUND_1) == 0) {
234 		/* Workaround implemented and needed. */
235 		ci->ci_flush_bp = psci_flush_bp_smccc_arch_workaround_1;
236 		ci->ci_flush_bp();
237 	} else {
238 		/* Workaround isn't implemented or isn't needed. */
239 		ci->ci_flush_bp = psci_flush_bp_none;
240 	}
241 }
242 
243 int
244 psci_flush_bp_has_bhb(void)
245 {
246 	struct psci_softc *sc = psci_sc;
247 
248 	/*
249 	 * SMCCC 1.1 allows us to detect if the workaround is
250 	 * implemented and needed.
251 	 */
252 	if (sc && sc->sc_smccc_version >= 0x10001 &&
253 	    smccc_arch_features(SMCCC_ARCH_WORKAROUND_3) == 0) {
254 		/* Workaround implemented and needed. */
255 		return 1;
256 	}
257 
258 	return 0;
259 }
260 
261 int32_t
262 smccc_version(void)
263 {
264 	struct psci_softc *sc = psci_sc;
265 	int32_t version;
266 
267 	KASSERT(sc && sc->sc_callfn);
268 	version = (*sc->sc_callfn)(SMCCC_VERSION, 0, 0, 0);
269 	if (version != PSCI_NOT_SUPPORTED)
270 		return version;
271 
272 	/* Treat NOT_SUPPORTED as 1.0 */
273 	return 0x10000;
274 }
275 
276 int32_t
277 smccc(uint32_t func_id, register_t arg0, register_t arg1, register_t arg2)
278 {
279 	struct psci_softc *sc = psci_sc;
280 
281 	if (sc && sc->sc_callfn)
282 		return (*sc->sc_callfn)(func_id, arg0, arg1, arg2);
283 
284 	return PSCI_NOT_SUPPORTED;
285 }
286 
287 int32_t
288 smccc_arch_features(uint32_t arch_func_id)
289 {
290 	struct psci_softc *sc = psci_sc;
291 
292 	KASSERT(sc && sc->sc_callfn);
293 	return (*sc->sc_callfn)(SMCCC_ARCH_FEATURES, arch_func_id, 0, 0);
294 }
295 
296 uint32_t
297 psci_version(void)
298 {
299 	struct psci_softc *sc = psci_sc;
300 
301 	if (sc && sc->sc_callfn && sc->sc_psci_version != 0)
302 		return (*sc->sc_callfn)(sc->sc_psci_version, 0, 0, 0);
303 
304 	/* No version support; return 0.0. */
305 	return 0;
306 }
307 
308 int32_t
309 psci_system_suspend(register_t entry_point_address, register_t context_id)
310 {
311 	struct psci_softc *sc = psci_sc;
312 
313 	if (sc && sc->sc_callfn && sc->sc_system_suspend != 0)
314 		return (*sc->sc_callfn)(sc->sc_system_suspend,
315 		    entry_point_address, context_id, 0);
316 
317 	return PSCI_NOT_SUPPORTED;
318 }
319 
320 int32_t
321 psci_cpu_off(void)
322 {
323 	struct psci_softc *sc = psci_sc;
324 
325 	if (sc && sc->sc_callfn && sc->sc_cpu_off != 0)
326 		return (*sc->sc_callfn)(sc->sc_cpu_off, 0, 0, 0);
327 
328 	return PSCI_NOT_SUPPORTED;
329 }
330 
331 int32_t
332 psci_cpu_on(register_t target_cpu, register_t entry_point_address,
333     register_t context_id)
334 {
335 	struct psci_softc *sc = psci_sc;
336 
337 	if (sc && sc->sc_callfn && sc->sc_cpu_on != 0)
338 		return (*sc->sc_callfn)(sc->sc_cpu_on, target_cpu,
339 		    entry_point_address, context_id);
340 
341 	return PSCI_NOT_SUPPORTED;
342 }
343 
344 int32_t
345 psci_cpu_suspend(register_t power_state, register_t entry_point_address,
346     register_t context_id)
347 {
348 	struct psci_softc *sc = psci_sc;
349 
350 	if (sc && sc->sc_callfn && sc->sc_cpu_suspend != 0)
351 		return (*sc->sc_callfn)(sc->sc_cpu_suspend, power_state,
352 		    entry_point_address, context_id);
353 
354 	return PSCI_NOT_SUPPORTED;
355 }
356 
357 int32_t
358 psci_features(uint32_t psci_func_id)
359 {
360 	struct psci_softc *sc = psci_sc;
361 
362 	if (sc && sc->sc_callfn)
363 		return (*sc->sc_callfn)(PSCI_FEATURES, psci_func_id, 0, 0);
364 
365 	return PSCI_NOT_SUPPORTED;
366 }
367 
368 int
369 psci_can_suspend(void)
370 {
371 	struct psci_softc *sc = psci_sc;
372 
373 	return (sc && sc->sc_system_suspend != 0);
374 }
375 
376 int
377 psci_method(void)
378 {
379 	struct psci_softc *sc = psci_sc;
380 
381 	return sc ? sc->sc_method : PSCI_METHOD_NONE;
382 }
383