1 /* $OpenBSD: psci.c,v 1.16 2024/04/13 14:20:48 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(void) 217 { 218 struct psci_softc *sc = psci_sc; 219 struct cpu_info *ci = curcpu(); 220 221 /* 222 * SMCCC 1.1 allows us to detect if the workaround is 223 * implemented and needed. 224 */ 225 if (sc && sc->sc_smccc_version >= 0x10001 && 226 smccc_arch_features(SMCCC_ARCH_WORKAROUND_1) == 0) { 227 /* Workaround implemented and needed. */ 228 ci->ci_flush_bp = psci_flush_bp_smccc_arch_workaround_1; 229 ci->ci_flush_bp(); 230 } else { 231 /* Workaround isn't implemented or isn't needed. */ 232 ci->ci_flush_bp = psci_flush_bp_none; 233 } 234 } 235 236 void 237 smccc_enable_arch_workaround_2(void) 238 { 239 struct psci_softc *sc = psci_sc; 240 241 /* 242 * SMCCC 1.1 allows us to detect if the workaround is 243 * implemented and needed. 244 */ 245 if (sc && sc->sc_smccc_version >= 0x10001 && 246 smccc_arch_features(SMCCC_ARCH_WORKAROUND_2) == 0) { 247 /* Workaround implemented and needed. */ 248 (*sc->sc_callfn)(SMCCC_ARCH_WORKAROUND_2, 1, 0, 0); 249 } 250 } 251 252 int 253 smccc_needs_arch_workaround_3(void) 254 { 255 struct psci_softc *sc = psci_sc; 256 257 /* 258 * SMCCC 1.1 allows us to detect if the workaround is 259 * implemented and needed. 260 */ 261 if (sc && sc->sc_smccc_version >= 0x10001 && 262 smccc_arch_features(SMCCC_ARCH_WORKAROUND_3) == 0) { 263 /* Workaround implemented and needed. */ 264 return 1; 265 } 266 267 return 0; 268 } 269 270 int32_t 271 smccc_version(void) 272 { 273 struct psci_softc *sc = psci_sc; 274 int32_t version; 275 276 KASSERT(sc && sc->sc_callfn); 277 version = (*sc->sc_callfn)(SMCCC_VERSION, 0, 0, 0); 278 if (version != PSCI_NOT_SUPPORTED) 279 return version; 280 281 /* Treat NOT_SUPPORTED as 1.0 */ 282 return 0x10000; 283 } 284 285 int32_t 286 smccc(uint32_t func_id, register_t arg0, register_t arg1, register_t arg2) 287 { 288 struct psci_softc *sc = psci_sc; 289 290 if (sc && sc->sc_callfn) 291 return (*sc->sc_callfn)(func_id, arg0, arg1, arg2); 292 293 return PSCI_NOT_SUPPORTED; 294 } 295 296 int32_t 297 smccc_arch_features(uint32_t arch_func_id) 298 { 299 struct psci_softc *sc = psci_sc; 300 301 KASSERT(sc && sc->sc_callfn); 302 return (*sc->sc_callfn)(SMCCC_ARCH_FEATURES, arch_func_id, 0, 0); 303 } 304 305 uint32_t 306 psci_version(void) 307 { 308 struct psci_softc *sc = psci_sc; 309 310 if (sc && sc->sc_callfn && sc->sc_psci_version != 0) 311 return (*sc->sc_callfn)(sc->sc_psci_version, 0, 0, 0); 312 313 /* No version support; return 0.0. */ 314 return 0; 315 } 316 317 int32_t 318 psci_system_suspend(register_t entry_point_address, register_t context_id) 319 { 320 struct psci_softc *sc = psci_sc; 321 322 if (sc && sc->sc_callfn && sc->sc_system_suspend != 0) 323 return (*sc->sc_callfn)(sc->sc_system_suspend, 324 entry_point_address, context_id, 0); 325 326 return PSCI_NOT_SUPPORTED; 327 } 328 329 int32_t 330 psci_cpu_off(void) 331 { 332 struct psci_softc *sc = psci_sc; 333 334 if (sc && sc->sc_callfn && sc->sc_cpu_off != 0) 335 return (*sc->sc_callfn)(sc->sc_cpu_off, 0, 0, 0); 336 337 return PSCI_NOT_SUPPORTED; 338 } 339 340 int32_t 341 psci_cpu_on(register_t target_cpu, register_t entry_point_address, 342 register_t context_id) 343 { 344 struct psci_softc *sc = psci_sc; 345 346 if (sc && sc->sc_callfn && sc->sc_cpu_on != 0) 347 return (*sc->sc_callfn)(sc->sc_cpu_on, target_cpu, 348 entry_point_address, context_id); 349 350 return PSCI_NOT_SUPPORTED; 351 } 352 353 int32_t 354 psci_cpu_suspend(register_t power_state, register_t entry_point_address, 355 register_t context_id) 356 { 357 struct psci_softc *sc = psci_sc; 358 359 if (sc && sc->sc_callfn && sc->sc_cpu_suspend != 0) 360 return (*sc->sc_callfn)(sc->sc_cpu_suspend, power_state, 361 entry_point_address, context_id); 362 363 return PSCI_NOT_SUPPORTED; 364 } 365 366 int32_t 367 psci_features(uint32_t psci_func_id) 368 { 369 struct psci_softc *sc = psci_sc; 370 371 if (sc && sc->sc_callfn) 372 return (*sc->sc_callfn)(PSCI_FEATURES, psci_func_id, 0, 0); 373 374 return PSCI_NOT_SUPPORTED; 375 } 376 377 int 378 psci_can_suspend(void) 379 { 380 struct psci_softc *sc = psci_sc; 381 382 return (sc && sc->sc_system_suspend != 0); 383 } 384 385 int 386 psci_method(void) 387 { 388 struct psci_softc *sc = psci_sc; 389 390 return sc ? sc->sc_method : PSCI_METHOD_NONE; 391 } 392