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