summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorMark Kettenis <kettenis@cvs.openbsd.org>2018-05-03 09:45:58 +0000
committerMark Kettenis <kettenis@cvs.openbsd.org>2018-05-03 09:45:58 +0000
commit91ff3e48a9c5d1aa2ce2f37d310b41f48e0071c9 (patch)
treebbb97314b7d0e2a226df7b4e3e2e1e6bf0bd43ae /sys/dev
parent257bfdf21b7fd3340cd6c034f22afb51f9116ca8 (diff)
Add support for SMCCC 1.1 which provides proper support for the firmware-based
workaround for branch target injection attacks (CVE 2017-5715). ok patrick@, jsg@
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/fdt/psci.c165
-rw-r--r--sys/dev/fdt/pscivar.h2
2 files changed, 145 insertions, 22 deletions
diff --git a/sys/dev/fdt/psci.c b/sys/dev/fdt/psci.c
index 774b15ff89f..ccee49cd10a 100644
--- a/sys/dev/fdt/psci.c
+++ b/sys/dev/fdt/psci.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: psci.c,v 1.6 2018/02/23 19:08:56 kettenis Exp $ */
+/* $OpenBSD: psci.c,v 1.7 2018/05/03 09:45:57 kettenis Exp $ */
/*
* Copyright (c) 2016 Jonathan Gray <jsg@openbsd.org>
@@ -31,14 +31,19 @@
extern void (*cpuresetfn)(void);
extern void (*powerdownfn)(void);
-#define PSCI_VERSION 0x84000000
-#define SYSTEM_OFF 0x84000008
-#define SYSTEM_RESET 0x84000009
+#define SMCCC_VERSION 0x80000000
+#define SMCCC_ARCH_FEATURES 0x80000001
+#define SMCCC_ARCH_WORKAROUND_1 0x80008000
+
+#define PSCI_VERSION 0x84000000
#ifdef __LP64__
-#define CPU_ON 0xc4000003
+#define CPU_ON 0xc4000003
#else
-#define CPU_ON 0x84000003
+#define CPU_ON 0x84000003
#endif
+#define SYSTEM_OFF 0x84000008
+#define SYSTEM_RESET 0x84000009
+#define PSCI_FEATURES 0x8400000a
struct psci_softc {
struct device sc_dev;
@@ -48,6 +53,9 @@ struct psci_softc {
uint32_t sc_system_off;
uint32_t sc_system_reset;
uint32_t sc_cpu_on;
+
+ uint32_t sc_version;
+ uint32_t sc_smccc_version;
};
struct psci_softc *psci_sc;
@@ -60,6 +68,12 @@ void psci_powerdown(void);
extern register_t hvc_call(register_t, register_t, register_t, register_t);
extern register_t smc_call(register_t, register_t, register_t, register_t);
+int32_t smccc_version(void);
+int32_t smccc_arch_features(uint32_t);
+
+uint32_t psci_version(void);
+int32_t psci_features(uint32_t);
+
struct cfattach psci_ca = {
sizeof(struct psci_softc), psci_match, psci_attach
};
@@ -84,7 +98,6 @@ psci_attach(struct device *parent, struct device *self, void *aux)
struct psci_softc *sc = (struct psci_softc *)self;
struct fdt_attach_args *faa = aux;
char method[128];
- uint32_t version;
if (OF_getprop(faa->fa_node, "method", method, sizeof(method))) {
if (strcmp(method, "hvc") == 0)
@@ -114,8 +127,18 @@ psci_attach(struct device *parent, struct device *self, void *aux)
psci_sc = sc;
- version = psci_version();
- printf(": PSCI %d.%d\n", version >> 16, version & 0xffff);
+ sc->sc_version = psci_version();
+ printf(": PSCI %d.%d", sc->sc_version >> 16, sc->sc_version & 0xffff);
+
+ if (sc->sc_version >= 0x10000) {
+ if (psci_features(SMCCC_VERSION) == PSCI_SUCCESS) {
+ sc->sc_smccc_version = smccc_version();
+ printf(", SMCCC %d.%d", sc->sc_smccc_version >> 16,
+ sc->sc_smccc_version & 0xffff);
+ }
+ }
+
+ printf("\n");
if (sc->sc_system_off != 0)
powerdownfn = psci_powerdown;
@@ -123,22 +146,11 @@ psci_attach(struct device *parent, struct device *self, void *aux)
cpuresetfn = psci_reset;
}
-uint32_t
-psci_version(void)
-{
- struct psci_softc *sc = psci_sc;
-
- if (sc && sc->sc_callfn && sc->sc_psci_version != 0)
- return (*sc->sc_callfn)(sc->sc_psci_version, 0, 0, 0);
-
- /* No version support; return 0.0. */
- return 0;
-}
-
void
psci_reset(void)
{
struct psci_softc *sc = psci_sc;
+
if (sc->sc_callfn)
(*sc->sc_callfn)(sc->sc_system_reset, 0, 0, 0);
}
@@ -147,10 +159,110 @@ void
psci_powerdown(void)
{
struct psci_softc *sc = psci_sc;
+
if (sc->sc_callfn)
(*sc->sc_callfn)(sc->sc_system_off, 0, 0, 0);
}
+/*
+ * Firmware-based workaround for CVE-2017-5715. We pick the
+ * appropriate mechanism based on the PSCI and SMCCC versions. We
+ * determine whether the workaround is actually needed the first time
+ * we are invoked such that we only make the firmware call if we
+ * really need to.
+ */
+
+void
+psci_flush_bp_none(void)
+{
+}
+
+void
+psci_flush_bp_psci_version(void)
+{
+ struct psci_softc *sc = psci_sc;
+
+ (*sc->sc_callfn)(PSCI_VERSION, 0, 0, 0);
+}
+
+void
+psci_flush_bp_smccc_arch_workaround_1(void)
+{
+ struct psci_softc *sc = psci_sc;
+
+ (*sc->sc_callfn)(SMCCC_ARCH_WORKAROUND_1, 0, 0, 0);
+}
+
+void
+psci_flush_bp(void)
+{
+ struct psci_softc *sc = psci_sc;
+ struct cpu_info *ci = curcpu();
+
+ /* No PSCI or an old version of PSCI; nothing we can do. */
+ if (sc == NULL || sc->sc_version < 0x10000) {
+ ci->ci_flush_bp = psci_flush_bp_none;
+ return;
+ }
+
+ /*
+ * PSCI 1.0 or later with SMCCC 1.0; invoke PSCI_VERSION and
+ * hope for the best.
+ */
+ if (sc->sc_smccc_version < 0x10001) {
+ ci->ci_flush_bp = psci_flush_bp_psci_version;
+ ci->ci_flush_bp();
+ return;
+ }
+
+ /*
+ * SMCCC 1.1 or later; we can actually detect if the
+ * workaround is implemented and needed.
+ */
+ if (smccc_arch_features(SMCCC_ARCH_WORKAROUND_1) == 0) {
+ /* Workaround implemented and needed. */
+ ci->ci_flush_bp = psci_flush_bp_smccc_arch_workaround_1;
+ ci->ci_flush_bp();
+ } else {
+ /* No workaround needed. */
+ ci->ci_flush_bp = psci_flush_bp_none;
+ }
+}
+
+int32_t
+smccc_version(void)
+{
+ struct psci_softc *sc = psci_sc;
+
+ if (sc && sc->sc_callfn)
+ return (*sc->sc_callfn)(SMCCC_VERSION, 0, 0, 0);
+
+ return PSCI_NOT_SUPPORTED;
+}
+
+int32_t
+smccc_arch_features(uint32_t arch_func_id)
+{
+ struct psci_softc *sc = psci_sc;
+
+ if (sc && sc->sc_callfn)
+ return (*sc->sc_callfn)(SMCCC_ARCH_FEATURES, arch_func_id, 0, 0);
+
+ return PSCI_NOT_SUPPORTED;
+}
+
+uint32_t
+psci_version(void)
+{
+ struct psci_softc *sc = psci_sc;
+
+ if (sc && sc->sc_callfn && sc->sc_psci_version != 0)
+ return (*sc->sc_callfn)(sc->sc_psci_version, 0, 0, 0);
+
+ /* No version support; return 0.0. */
+ return 0;
+}
+
int32_t
psci_cpu_on(register_t target_cpu, register_t entry_point_address,
register_t context_id)
@@ -163,3 +275,14 @@ psci_cpu_on(register_t target_cpu, register_t entry_point_address,
return PSCI_NOT_SUPPORTED;
}
+
+int32_t
+psci_features(uint32_t psci_func_id)
+{
+ struct psci_softc *sc = psci_sc;
+
+ if (sc && sc->sc_callfn)
+ return (*sc->sc_callfn)(PSCI_FEATURES, psci_func_id, 0, 0);
+
+ return PSCI_NOT_SUPPORTED;
+}
diff --git a/sys/dev/fdt/pscivar.h b/sys/dev/fdt/pscivar.h
index 59d3702670e..717ca142e8e 100644
--- a/sys/dev/fdt/pscivar.h
+++ b/sys/dev/fdt/pscivar.h
@@ -6,7 +6,7 @@
#define PSCI_SUCCESS 0
#define PSCI_NOT_SUPPORTED -1
-uint32_t psci_version(void);
int32_t psci_cpu_on(register_t, register_t, register_t);
+void psci_flush_bp(void);
#endif /* _SYS_DEV_FDT_PSCIVAR_H_ */