1 /* $OpenBSD: psci.c,v 1.10 2021/10/24 17:52:26 mpi 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 38 #define PSCI_VERSION 0x84000000 39 #ifdef __LP64__ 40 #define CPU_ON 0xc4000003 41 #else 42 #define CPU_ON 0x84000003 43 #endif 44 #define SYSTEM_OFF 0x84000008 45 #define SYSTEM_RESET 0x84000009 46 #define PSCI_FEATURES 0x8400000a 47 48 struct psci_softc { 49 struct device sc_dev; 50 register_t (*sc_callfn)(register_t, register_t, register_t, 51 register_t); 52 uint32_t sc_psci_version; 53 uint32_t sc_system_off; 54 uint32_t sc_system_reset; 55 uint32_t sc_cpu_on; 56 57 uint32_t sc_smccc_version; 58 }; 59 60 struct psci_softc *psci_sc; 61 62 int psci_match(struct device *, void *, void *); 63 void psci_attach(struct device *, struct device *, void *); 64 void psci_reset(void); 65 void psci_powerdown(void); 66 67 extern register_t hvc_call(register_t, register_t, register_t, register_t); 68 extern register_t smc_call(register_t, register_t, register_t, register_t); 69 70 int32_t smccc_version(void); 71 int32_t smccc_arch_features(uint32_t); 72 73 uint32_t psci_version(void); 74 int32_t psci_features(uint32_t); 75 76 const struct cfattach psci_ca = { 77 sizeof(struct psci_softc), psci_match, psci_attach 78 }; 79 80 struct cfdriver psci_cd = { 81 NULL, "psci", DV_DULL 82 }; 83 84 int 85 psci_match(struct device *parent, void *match, void *aux) 86 { 87 struct fdt_attach_args *faa = aux; 88 89 return OF_is_compatible(faa->fa_node, "arm,psci") || 90 OF_is_compatible(faa->fa_node, "arm,psci-0.2") || 91 OF_is_compatible(faa->fa_node, "arm,psci-1.0"); 92 } 93 94 void 95 psci_attach(struct device *parent, struct device *self, void *aux) 96 { 97 struct psci_softc *sc = (struct psci_softc *)self; 98 struct fdt_attach_args *faa = aux; 99 char method[128]; 100 uint32_t version; 101 102 if (OF_getprop(faa->fa_node, "method", method, sizeof(method))) { 103 if (strcmp(method, "hvc") == 0) 104 sc->sc_callfn = hvc_call; 105 else if (strcmp(method, "smc") == 0) 106 sc->sc_callfn = smc_call; 107 } 108 109 /* 110 * The function IDs are only to be parsed for the old specification 111 * (as in version 0.1). All newer implementations are supposed to 112 * use the specified values. 113 */ 114 if (OF_is_compatible(faa->fa_node, "arm,psci-0.2") || 115 OF_is_compatible(faa->fa_node, "arm,psci-1.0")) { 116 sc->sc_psci_version = PSCI_VERSION; 117 sc->sc_system_off = SYSTEM_OFF; 118 sc->sc_system_reset = SYSTEM_RESET; 119 sc->sc_cpu_on = CPU_ON; 120 } else if (OF_is_compatible(faa->fa_node, "arm,psci")) { 121 sc->sc_system_off = OF_getpropint(faa->fa_node, 122 "system_off", 0); 123 sc->sc_system_reset = OF_getpropint(faa->fa_node, 124 "system_reset", 0); 125 sc->sc_cpu_on = OF_getpropint(faa->fa_node, "cpu_on", 0); 126 } 127 128 psci_sc = sc; 129 130 version = psci_version(); 131 printf(": PSCI %d.%d", version >> 16, version & 0xffff); 132 133 if (version >= 0x10000) { 134 if (psci_features(SMCCC_VERSION) == PSCI_SUCCESS) { 135 sc->sc_smccc_version = smccc_version(); 136 printf(", SMCCC %d.%d", sc->sc_smccc_version >> 16, 137 sc->sc_smccc_version & 0xffff); 138 } 139 } 140 141 printf("\n"); 142 143 if (sc->sc_system_off != 0) 144 powerdownfn = psci_powerdown; 145 if (sc->sc_system_reset != 0) 146 cpuresetfn = psci_reset; 147 } 148 149 void 150 psci_reset(void) 151 { 152 struct psci_softc *sc = psci_sc; 153 154 if (sc->sc_callfn) 155 (*sc->sc_callfn)(sc->sc_system_reset, 0, 0, 0); 156 } 157 158 void 159 psci_powerdown(void) 160 { 161 struct psci_softc *sc = psci_sc; 162 163 if (sc->sc_callfn) 164 (*sc->sc_callfn)(sc->sc_system_off, 0, 0, 0); 165 } 166 167 /* 168 * Firmware-based workaround for CVE-2017-5715. We determine whether 169 * the workaround is actually implemented and needed the first time we 170 * are invoked such that we only make the firmware call when appropriate. 171 */ 172 173 void 174 psci_flush_bp_none(void) 175 { 176 } 177 178 void 179 psci_flush_bp_smccc_arch_workaround_1(void) 180 { 181 struct psci_softc *sc = psci_sc; 182 183 (*sc->sc_callfn)(SMCCC_ARCH_WORKAROUND_1, 0, 0, 0); 184 } 185 186 void 187 psci_flush_bp(void) 188 { 189 struct psci_softc *sc = psci_sc; 190 struct cpu_info *ci = curcpu(); 191 192 /* 193 * SMCCC 1.1 allows us to detect if the workaround is 194 * implemented and needed. 195 */ 196 if (sc && sc->sc_smccc_version >= 0x10001 && 197 smccc_arch_features(SMCCC_ARCH_WORKAROUND_1) == 0) { 198 /* Workaround implemented and needed. */ 199 ci->ci_flush_bp = psci_flush_bp_smccc_arch_workaround_1; 200 ci->ci_flush_bp(); 201 } else { 202 /* Workaround isn't implemented or isn't needed. */ 203 ci->ci_flush_bp = psci_flush_bp_none; 204 } 205 } 206 207 int32_t 208 smccc_version(void) 209 { 210 struct psci_softc *sc = psci_sc; 211 int32_t version; 212 213 KASSERT(sc && sc->sc_callfn); 214 version = (*sc->sc_callfn)(SMCCC_VERSION, 0, 0, 0); 215 if (version != PSCI_NOT_SUPPORTED) 216 return version; 217 218 /* Treat NOT_SUPPORTED as 1.0 */ 219 return 0x10000; 220 } 221 222 int32_t 223 smccc_arch_features(uint32_t arch_func_id) 224 { 225 struct psci_softc *sc = psci_sc; 226 227 KASSERT(sc && sc->sc_callfn); 228 return (*sc->sc_callfn)(SMCCC_ARCH_FEATURES, arch_func_id, 0, 0); 229 } 230 231 uint32_t 232 psci_version(void) 233 { 234 struct psci_softc *sc = psci_sc; 235 236 if (sc && sc->sc_callfn && sc->sc_psci_version != 0) 237 return (*sc->sc_callfn)(sc->sc_psci_version, 0, 0, 0); 238 239 /* No version support; return 0.0. */ 240 return 0; 241 } 242 243 int32_t 244 psci_cpu_on(register_t target_cpu, register_t entry_point_address, 245 register_t context_id) 246 { 247 struct psci_softc *sc = psci_sc; 248 249 if (sc && sc->sc_callfn && sc->sc_cpu_on != 0) 250 return (*sc->sc_callfn)(sc->sc_cpu_on, target_cpu, 251 entry_point_address, context_id); 252 253 return PSCI_NOT_SUPPORTED; 254 } 255 256 int32_t 257 psci_features(uint32_t psci_func_id) 258 { 259 struct psci_softc *sc = psci_sc; 260 261 if (sc && sc->sc_callfn) 262 return (*sc->sc_callfn)(PSCI_FEATURES, psci_func_id, 0, 0); 263 264 return PSCI_NOT_SUPPORTED; 265 } 266