1 /* $OpenBSD: psci.c,v 1.15 2024/03/18 21:57:22 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_2 0x80007fff 38 #define SMCCC_ARCH_WORKAROUND_3 0x80003fff 39 40 #define PSCI_VERSION 0x84000000 41 #ifdef __LP64__ 42 #define CPU_SUSPEND 0xc4000001 43 #else 44 #define CPU_SUSPEND 0x84000001 45 #endif 46 #define CPU_OFF 0x84000002 47 #ifdef __LP64__ 48 #define CPU_ON 0xc4000003 49 #else 50 #define CPU_ON 0x84000003 51 #endif 52 #define SYSTEM_OFF 0x84000008 53 #define SYSTEM_RESET 0x84000009 54 #define PSCI_FEATURES 0x8400000a 55 #ifdef __LP64__ 56 #define SYSTEM_SUSPEND 0xc400000e 57 #else 58 #define SYSTEM_SUSPEND 0x8400000e 59 #endif 60 61 struct psci_softc { 62 struct device sc_dev; 63 register_t (*sc_callfn)(register_t, register_t, register_t, 64 register_t); 65 uint32_t sc_psci_version; 66 uint32_t sc_system_off; 67 uint32_t sc_system_reset; 68 uint32_t sc_system_suspend; 69 uint32_t sc_cpu_on; 70 uint32_t sc_cpu_off; 71 uint32_t sc_cpu_suspend; 72 73 uint32_t sc_smccc_version; 74 uint32_t sc_method; 75 }; 76 77 struct psci_softc *psci_sc; 78 79 int psci_match(struct device *, void *, void *); 80 void psci_attach(struct device *, struct device *, void *); 81 void psci_reset(void); 82 void psci_powerdown(void); 83 84 extern register_t hvc_call(register_t, register_t, register_t, register_t); 85 extern register_t smc_call(register_t, register_t, register_t, register_t); 86 87 int32_t smccc_version(void); 88 int32_t smccc_arch_features(uint32_t); 89 90 uint32_t psci_version(void); 91 int32_t psci_features(uint32_t); 92 93 const struct cfattach psci_ca = { 94 sizeof(struct psci_softc), psci_match, psci_attach 95 }; 96 97 struct cfdriver psci_cd = { 98 NULL, "psci", DV_DULL 99 }; 100 101 int 102 psci_match(struct device *parent, void *match, void *aux) 103 { 104 struct fdt_attach_args *faa = aux; 105 106 return OF_is_compatible(faa->fa_node, "arm,psci") || 107 OF_is_compatible(faa->fa_node, "arm,psci-0.2") || 108 OF_is_compatible(faa->fa_node, "arm,psci-1.0"); 109 } 110 111 void 112 psci_attach(struct device *parent, struct device *self, void *aux) 113 { 114 struct psci_softc *sc = (struct psci_softc *)self; 115 struct fdt_attach_args *faa = aux; 116 char method[128]; 117 uint32_t version; 118 119 if (OF_getprop(faa->fa_node, "method", method, sizeof(method))) { 120 if (strcmp(method, "hvc") == 0) { 121 sc->sc_callfn = hvc_call; 122 sc->sc_method = PSCI_METHOD_HVC; 123 } else if (strcmp(method, "smc") == 0) { 124 sc->sc_callfn = smc_call; 125 sc->sc_method = PSCI_METHOD_SMC; 126 } 127 } 128 129 /* 130 * The function IDs are only to be parsed for the old specification 131 * (as in version 0.1). All newer implementations are supposed to 132 * use the specified values. 133 */ 134 if (OF_is_compatible(faa->fa_node, "arm,psci-0.2") || 135 OF_is_compatible(faa->fa_node, "arm,psci-1.0")) { 136 sc->sc_psci_version = PSCI_VERSION; 137 sc->sc_system_off = SYSTEM_OFF; 138 sc->sc_system_reset = SYSTEM_RESET; 139 sc->sc_cpu_on = CPU_ON; 140 sc->sc_cpu_off = CPU_OFF; 141 sc->sc_cpu_suspend = CPU_SUSPEND; 142 } else if (OF_is_compatible(faa->fa_node, "arm,psci")) { 143 sc->sc_system_off = OF_getpropint(faa->fa_node, 144 "system_off", 0); 145 sc->sc_system_reset = OF_getpropint(faa->fa_node, 146 "system_reset", 0); 147 sc->sc_cpu_on = OF_getpropint(faa->fa_node, "cpu_on", 0); 148 sc->sc_cpu_off = OF_getpropint(faa->fa_node, "cpu_off", 0); 149 sc->sc_cpu_suspend = OF_getpropint(faa->fa_node, 150 "cpu_suspend", 0); 151 } 152 153 psci_sc = sc; 154 155 version = psci_version(); 156 printf(": PSCI %d.%d", version >> 16, version & 0xffff); 157 158 if (version >= 0x10000) { 159 if (psci_features(SMCCC_VERSION) == PSCI_SUCCESS) { 160 sc->sc_smccc_version = smccc_version(); 161 printf(", SMCCC %d.%d", sc->sc_smccc_version >> 16, 162 sc->sc_smccc_version & 0xffff); 163 } 164 if (psci_features(SYSTEM_SUSPEND) == PSCI_SUCCESS) { 165 sc->sc_system_suspend = SYSTEM_SUSPEND; 166 printf(", SYSTEM_SUSPEND"); 167 } 168 } 169 170 printf("\n"); 171 172 if (sc->sc_system_off != 0) 173 powerdownfn = psci_powerdown; 174 if (sc->sc_system_reset != 0) 175 cpuresetfn = psci_reset; 176 } 177 178 void 179 psci_reset(void) 180 { 181 struct psci_softc *sc = psci_sc; 182 183 if (sc->sc_callfn) 184 (*sc->sc_callfn)(sc->sc_system_reset, 0, 0, 0); 185 } 186 187 void 188 psci_powerdown(void) 189 { 190 struct psci_softc *sc = psci_sc; 191 192 if (sc->sc_callfn) 193 (*sc->sc_callfn)(sc->sc_system_off, 0, 0, 0); 194 } 195 196 /* 197 * Firmware-based workaround for CVE-2017-5715. We determine whether 198 * the workaround is actually implemented and needed the first time we 199 * are invoked such that we only make the firmware call when appropriate. 200 */ 201 202 void 203 psci_flush_bp_none(void) 204 { 205 } 206 207 void 208 psci_flush_bp_smccc_arch_workaround_1(void) 209 { 210 struct psci_softc *sc = psci_sc; 211 212 (*sc->sc_callfn)(SMCCC_ARCH_WORKAROUND_1, 0, 0, 0); 213 } 214 215 void 216 psci_flush_bp_smccc_arch_workaround_3(void) 217 { 218 struct psci_softc *sc = psci_sc; 219 220 (*sc->sc_callfn)(SMCCC_ARCH_WORKAROUND_3, 0, 0, 0); 221 } 222 223 void 224 psci_flush_bp(void) 225 { 226 struct psci_softc *sc = psci_sc; 227 struct cpu_info *ci = curcpu(); 228 229 /* 230 * SMCCC 1.1 allows us to detect if the workaround is 231 * implemented and needed. 232 */ 233 if (sc && sc->sc_smccc_version >= 0x10001 && 234 smccc_arch_features(SMCCC_ARCH_WORKAROUND_1) == 0) { 235 /* Workaround implemented and needed. */ 236 ci->ci_flush_bp = psci_flush_bp_smccc_arch_workaround_1; 237 ci->ci_flush_bp(); 238 } else { 239 /* Workaround isn't implemented or isn't needed. */ 240 ci->ci_flush_bp = psci_flush_bp_none; 241 } 242 } 243 244 void 245 smccc_enable_arch_workaround_2(void) 246 { 247 struct psci_softc *sc = psci_sc; 248 249 /* 250 * SMCCC 1.1 allows us to detect if the workaround is 251 * implemented and needed. 252 */ 253 if (sc && sc->sc_smccc_version >= 0x10001 && 254 smccc_arch_features(SMCCC_ARCH_WORKAROUND_2) == 0) { 255 /* Workaround implemented and needed. */ 256 (*sc->sc_callfn)(SMCCC_ARCH_WORKAROUND_2, 1, 0, 0); 257 } 258 } 259 260 int 261 smccc_needs_arch_workaround_3(void) 262 { 263 struct psci_softc *sc = psci_sc; 264 265 /* 266 * SMCCC 1.1 allows us to detect if the workaround is 267 * implemented and needed. 268 */ 269 if (sc && sc->sc_smccc_version >= 0x10001 && 270 smccc_arch_features(SMCCC_ARCH_WORKAROUND_3) == 0) { 271 /* Workaround implemented and needed. */ 272 return 1; 273 } 274 275 return 0; 276 } 277 278 int32_t 279 smccc_version(void) 280 { 281 struct psci_softc *sc = psci_sc; 282 int32_t version; 283 284 KASSERT(sc && sc->sc_callfn); 285 version = (*sc->sc_callfn)(SMCCC_VERSION, 0, 0, 0); 286 if (version != PSCI_NOT_SUPPORTED) 287 return version; 288 289 /* Treat NOT_SUPPORTED as 1.0 */ 290 return 0x10000; 291 } 292 293 int32_t 294 smccc(uint32_t func_id, register_t arg0, register_t arg1, register_t arg2) 295 { 296 struct psci_softc *sc = psci_sc; 297 298 if (sc && sc->sc_callfn) 299 return (*sc->sc_callfn)(func_id, arg0, arg1, arg2); 300 301 return PSCI_NOT_SUPPORTED; 302 } 303 304 int32_t 305 smccc_arch_features(uint32_t arch_func_id) 306 { 307 struct psci_softc *sc = psci_sc; 308 309 KASSERT(sc && sc->sc_callfn); 310 return (*sc->sc_callfn)(SMCCC_ARCH_FEATURES, arch_func_id, 0, 0); 311 } 312 313 uint32_t 314 psci_version(void) 315 { 316 struct psci_softc *sc = psci_sc; 317 318 if (sc && sc->sc_callfn && sc->sc_psci_version != 0) 319 return (*sc->sc_callfn)(sc->sc_psci_version, 0, 0, 0); 320 321 /* No version support; return 0.0. */ 322 return 0; 323 } 324 325 int32_t 326 psci_system_suspend(register_t entry_point_address, register_t context_id) 327 { 328 struct psci_softc *sc = psci_sc; 329 330 if (sc && sc->sc_callfn && sc->sc_system_suspend != 0) 331 return (*sc->sc_callfn)(sc->sc_system_suspend, 332 entry_point_address, context_id, 0); 333 334 return PSCI_NOT_SUPPORTED; 335 } 336 337 int32_t 338 psci_cpu_off(void) 339 { 340 struct psci_softc *sc = psci_sc; 341 342 if (sc && sc->sc_callfn && sc->sc_cpu_off != 0) 343 return (*sc->sc_callfn)(sc->sc_cpu_off, 0, 0, 0); 344 345 return PSCI_NOT_SUPPORTED; 346 } 347 348 int32_t 349 psci_cpu_on(register_t target_cpu, register_t entry_point_address, 350 register_t context_id) 351 { 352 struct psci_softc *sc = psci_sc; 353 354 if (sc && sc->sc_callfn && sc->sc_cpu_on != 0) 355 return (*sc->sc_callfn)(sc->sc_cpu_on, target_cpu, 356 entry_point_address, context_id); 357 358 return PSCI_NOT_SUPPORTED; 359 } 360 361 int32_t 362 psci_cpu_suspend(register_t power_state, register_t entry_point_address, 363 register_t context_id) 364 { 365 struct psci_softc *sc = psci_sc; 366 367 if (sc && sc->sc_callfn && sc->sc_cpu_suspend != 0) 368 return (*sc->sc_callfn)(sc->sc_cpu_suspend, power_state, 369 entry_point_address, context_id); 370 371 return PSCI_NOT_SUPPORTED; 372 } 373 374 int32_t 375 psci_features(uint32_t psci_func_id) 376 { 377 struct psci_softc *sc = psci_sc; 378 379 if (sc && sc->sc_callfn) 380 return (*sc->sc_callfn)(PSCI_FEATURES, psci_func_id, 0, 0); 381 382 return PSCI_NOT_SUPPORTED; 383 } 384 385 int 386 psci_can_suspend(void) 387 { 388 struct psci_softc *sc = psci_sc; 389 390 return (sc && sc->sc_system_suspend != 0); 391 } 392 393 int 394 psci_method(void) 395 { 396 struct psci_softc *sc = psci_sc; 397 398 return sc ? sc->sc_method : PSCI_METHOD_NONE; 399 } 400