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