So after adding a quick hack to mitigate Spectre variant 2 to ARM Trusted Firmware (ATF), ARM actually designed a proper solution that minimizes the performance loss and makes the presence of the workaround detectable. This is all documented in an update of the SMC Calling Convention (SMCCC) standard.
The diff below implements support for this solution while keeping support for the hack. While ARM strongly suggests vendors to update to a version of ATF that implements SMCCC 1.1 the current ATF for the Marvell ARMADA 8040 hasn't been updated yet (but does include the initial hack). Unfortunately the SMCCC 1.1 implementation in ATF doesn't quite implement the spec. As a result we have to check whether the workaround is implemented by issuing the relevant calls on each of the CPUs that might be affected. This is important for big.LITTLE designs such as the RK3399 that include both Cortex-A53 cores that aren't vulnerable and Cortex-A72 cores that are. ok? Index: dev/fdt/psci.c =================================================================== RCS file: /cvs/src/sys/dev/fdt/psci.c,v retrieving revision 1.6 diff -u -p -r1.6 psci.c --- dev/fdt/psci.c 23 Feb 2018 19:08:56 -0000 1.6 +++ dev/fdt/psci.c 1 May 2018 09:35:14 -0000 @@ -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, struc 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, struc 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, struc 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,111 @@ 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. Note + * that ARM Trusted Firmware violates the SMCCC 1.1 specification and + * only reports the presence of the appropriate workaround on CPUs + * that are actually vulnerable. That's why we determine the + * appropriate workaround the first time around. + */ + +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. + */ + if (smccc_arch_features(SMCCC_ARCH_WORKAROUND_1) == PSCI_SUCCESS) { + /* Workaround implemented. */ + 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) @@ -160,6 +273,17 @@ psci_cpu_on(register_t target_cpu, regis if (sc && sc->sc_callfn && sc->sc_cpu_on != 0) return (*sc->sc_callfn)(sc->sc_cpu_on, target_cpu, entry_point_address, context_id); + + 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; } Index: dev/fdt/pscivar.h =================================================================== RCS file: /cvs/src/sys/dev/fdt/pscivar.h,v retrieving revision 1.2 diff -u -p -r1.2 pscivar.h --- dev/fdt/pscivar.h 23 Feb 2018 19:08:56 -0000 1.2 +++ dev/fdt/pscivar.h 1 May 2018 09:35:14 -0000 @@ -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_ */ Index: arch/arm64/arm64/cpu.c =================================================================== RCS file: /cvs/src/sys/arch/arm64/arm64/cpu.c,v retrieving revision 1.17 diff -u -p -r1.17 cpu.c --- arch/arm64/arm64/cpu.c 29 Mar 2018 19:48:14 -0000 1.17 +++ arch/arm64/arm64/cpu.c 1 May 2018 09:35:14 -0000 @@ -165,7 +165,7 @@ cpu_identify(struct cpu_info *ci) /* * Some ARM processors are vulnerable to branch target - * injection attacks. + * injection attacks (CVE-2017-5715). */ switch (impl) { case CPU_IMPL_ARM: @@ -182,7 +182,7 @@ cpu_identify(struct cpu_info *ci) case CPU_PART_CORTEX_A75: default: /* - * Vulnerable; call PSCI_VERSION and hope + * Vulnerable; call into the firmware and hope * we're running on top of Arm Trusted * Firmware with a fix for Security Advisory * TFV 6. @@ -305,7 +305,7 @@ void cpu_flush_bp_psci(void) { #if NPSCI > 0 - psci_version(); + psci_flush_bp(); #endif }