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