diff options
Diffstat (limited to 'sys/dev/psci')
-rw-r--r-- | sys/dev/psci/psci.c | 26 | ||||
-rw-r--r-- | sys/dev/psci/smccc.c | 55 | ||||
-rw-r--r-- | sys/dev/psci/smccc.h | 33 | ||||
-rw-r--r-- | sys/dev/psci/smccc_arm64.S | 7 | ||||
-rw-r--r-- | sys/dev/psci/smccc_errata.c | 139 | ||||
-rw-r--r-- | sys/dev/psci/smccc_trng.c | 143 |
6 files changed, 391 insertions, 12 deletions
diff --git a/sys/dev/psci/psci.c b/sys/dev/psci/psci.c index e1e9c1880b54..497b23d2d4c3 100644 --- a/sys/dev/psci/psci.c +++ b/sys/dev/psci/psci.c @@ -71,6 +71,7 @@ struct psci_softc { device_t dev; + device_t smccc_dev; uint32_t psci_version; uint32_t psci_fnids[PSCI_FN_MAX]; @@ -341,11 +342,16 @@ psci_attach(device_t dev, psci_initfn_t psci_init, int default_version) if (psci_init(dev, default_version)) return (ENXIO); + psci_softc = sc; + #ifdef __aarch64__ smccc_init(); -#endif + sc->smccc_dev = device_add_child(dev, "smccc", DEVICE_UNIT_ANY); + if (sc->smccc_dev == NULL) + device_printf(dev, "Unable to add SMCCC device\n"); - psci_softc = sc; + bus_attach_children(dev); +#endif return (0); } @@ -378,12 +384,18 @@ psci_fdt_callfn(psci_callfn_t *callfn) { phandle_t node; - node = ofw_bus_find_compatible(OF_peer(0), "arm,psci-0.2"); - if (node == 0) { - node = ofw_bus_find_compatible(OF_peer(0), "arm,psci-1.0"); - if (node == 0) - return (PSCI_MISSING); + /* XXX: This is suboptimal, we should walk the tree & check each + * node against compat_data, but we only have a few entries so + * it's ok for now. + */ + for (int i = 0; compat_data[i].ocd_str != NULL; i++) { + node = ofw_bus_find_compatible(OF_peer(0), + compat_data[i].ocd_str); + if (node != 0) + break; } + if (node == 0) + return (PSCI_MISSING); if (!ofw_bus_node_status_okay(node)) return (PSCI_MISSING); diff --git a/sys/dev/psci/smccc.c b/sys/dev/psci/smccc.c index 08ad6d84fc3c..e40a60336d98 100644 --- a/sys/dev/psci/smccc.c +++ b/sys/dev/psci/smccc.c @@ -34,7 +34,9 @@ #include <sys/param.h> #include <sys/systm.h> +#include <sys/bus.h> #include <sys/kernel.h> +#include <sys/module.h> #include <dev/psci/psci.h> #include <dev/psci/smccc.h> @@ -66,6 +68,33 @@ smccc_init(void) } } +static int +smccc_probe(device_t dev) +{ + int32_t version; + + /* + * If the version is not implemented then we treat it as SMCCC 1.0 + */ + if (psci_features(SMCCC_VERSION) == PSCI_RETVAL_NOT_SUPPORTED || + (version = arm_smccc_invoke(SMCCC_VERSION, NULL)) <= 0) { + device_set_desc(dev, "ARM SMCCC v1.0"); + return (0); + } + + device_set_descf(dev, "ARM SMCCC v%d.%d", SMCCC_VERSION_MAJOR(version), + SMCCC_VERSION_MINOR(version)); + + return (0); +} + +static int +smccc_attach(device_t dev) +{ + bus_attach_children(dev); + return (0); +} + uint32_t smccc_get_version(void) { @@ -79,9 +108,9 @@ smccc_arch_features(uint32_t smccc_func_id) MPASS(smccc_version != 0); if (smccc_version == SMCCC_VERSION_1_0) - return (PSCI_RETVAL_NOT_SUPPORTED); + return (SMCCC_RET_NOT_SUPPORTED); - return (psci_call(SMCCC_ARCH_FEATURES, smccc_func_id, 0, 0)); + return (arm_smccc_invoke(SMCCC_ARCH_FEATURES, smccc_func_id, NULL)); } /* @@ -95,7 +124,7 @@ smccc_arch_workaround_1(void) MPASS(smccc_version != 0); KASSERT(smccc_version != SMCCC_VERSION_1_0, ("SMCCC arch workaround 1 called with an invalid SMCCC interface")); - return (psci_call(SMCCC_ARCH_WORKAROUND_1, 0, 0, 0)); + return (arm_smccc_invoke(SMCCC_ARCH_WORKAROUND_1, NULL)); } int @@ -105,5 +134,23 @@ smccc_arch_workaround_2(int enable) MPASS(smccc_version != 0); KASSERT(smccc_version != SMCCC_VERSION_1_0, ("SMCCC arch workaround 2 called with an invalid SMCCC interface")); - return (psci_call(SMCCC_ARCH_WORKAROUND_2, enable, 0, 0)); + return (arm_smccc_invoke(SMCCC_ARCH_WORKAROUND_2, enable, NULL)); } + +static device_method_t smccc_methods[] = { + DEVMETHOD(device_probe, smccc_probe), + DEVMETHOD(device_attach, smccc_attach), + + DEVMETHOD(bus_add_child, bus_generic_add_child), + + DEVMETHOD_END +}; + +static driver_t smccc_driver = { + "smccc", + smccc_methods, + 0, +}; + +EARLY_DRIVER_MODULE(smccc, psci, smccc_driver, 0, 0, + BUS_PASS_CPU + BUS_PASS_ORDER_FIRST); diff --git a/sys/dev/psci/smccc.h b/sys/dev/psci/smccc.h index 96527f037d78..b9e878d7c8ea 100644 --- a/sys/dev/psci/smccc.h +++ b/sys/dev/psci/smccc.h @@ -32,6 +32,7 @@ #ifndef _PSCI_SMCCC_H_ #define _PSCI_SMCCC_H_ +#define SMCCC_MAKE_VERSION(maj, min) ((maj) << 16 | (min)) #define SMCCC_VERSION_MAJOR(ver) (((ver) >> 16) & 0x7fff) #define SMCCC_VERSION_MINOR(ver) ((ver) & 0xffff) @@ -91,6 +92,38 @@ int arm_smccc_smc(register_t, register_t, register_t, register_t, register_t, int arm_smccc_hvc(register_t, register_t, register_t, register_t, register_t, register_t, register_t, register_t, struct arm_smccc_res *res); +#define arm_smccc_invoke_1(func, a0, res) \ + func(a0, 0, 0, 0, 0, 0, 0, 0, res) +#define arm_smccc_invoke_2(func, a0, a1, res) \ + func(a0, a1, 0, 0, 0, 0, 0, 0, res) +#define arm_smccc_invoke_3(func, a0, a1, a2, res) \ + func(a0, a1, a2, 0, 0, 0, 0, 0, res) +#define arm_smccc_invoke_4(func, a0, a1, a2, a3, res) \ + func(a0, a1, a2, a3, 0, 0, 0, 0, res) +#define arm_smccc_invoke_5(func, a0, a1, a2, a3, a4, res) \ + func(a0, a1, a2, a3, a4, 0, 0, 0, res) +#define arm_smccc_invoke_6(func, a0, a1, a2, a3, a4, a5, res) \ + func(a0, a1, a2, a3, a4, a5, 0, 0, res) +#define arm_smccc_invoke_7(func, a0, a1, a2, a3, a4, a5, a6, res) \ + func(a0, a1, a2, a3, a4, a5, a6, 0, res) +#define arm_smccc_invoke_8(func, a0, a1, a2, a3, a4, a5, a6, a7, res) \ + func(a0, a1, a2, a3, a4, a5, a6, a7, res) + +#define _arm_smccc_invoke_macro(_1, _2, _3, _4, _5, _6, _7, _8, NAME, ...) \ + NAME +#define _arm_smccc_invoke(func, a0, ...) \ + _arm_smccc_invoke_macro(__VA_ARGS__, arm_smccc_invoke_8, \ + arm_smccc_invoke_7, arm_smccc_invoke_6, arm_smccc_invoke_5, \ + arm_smccc_invoke_4, arm_smccc_invoke_3, arm_smccc_invoke_2, \ + arm_smccc_invoke_1)(func, a0, __VA_ARGS__) + +#define arm_smccc_invoke_hvc(a0, ...) \ + _arm_smccc_invoke(arm_smccc_hvc, a0, __VA_ARGS__) +#define arm_smccc_invoke_smc(a0, ...) \ + _arm_smccc_invoke(arm_smccc_smc, a0, __VA_ARGS__) +#define arm_smccc_invoke(a0, ...) \ + _arm_smccc_invoke(psci_callfn, a0, __VA_ARGS__) + struct arm_smccc_1_2_regs { register_t a0; register_t a1; diff --git a/sys/dev/psci/smccc_arm64.S b/sys/dev/psci/smccc_arm64.S index 3d3c9fc837b1..2a3c09ec26b2 100644 --- a/sys/dev/psci/smccc_arm64.S +++ b/sys/dev/psci/smccc_arm64.S @@ -30,7 +30,10 @@ * SUCH DAMAGE. */ +#include <sys/elf_common.h> + #include <machine/asm.h> + .macro arm_smccc_1_0 insn ENTRY(arm_smccc_\insn) \insn #0 @@ -77,10 +80,12 @@ ENTRY(arm_smccc_1_2_\insn) stp x16, x17, [x19, #16 * 8] 1: ldp xzr, x19, [sp], #16 ret -END(arm_smccc_1_2\insn) +END(arm_smccc_1_2_\insn) .endm /* int arm_smccc_1_2_*(const struct arm_smccc_1_2_regs *args, * struct arm_smccc_1_2_regs *res) */ arm_smccc_1_2 hvc arm_smccc_1_2 smc + +GNU_PROPERTY_AARCH64_FEATURE_1_NOTE(GNU_PROPERTY_AARCH64_FEATURE_1_VAL) diff --git a/sys/dev/psci/smccc_errata.c b/sys/dev/psci/smccc_errata.c new file mode 100644 index 000000000000..ebfc0f8b67ee --- /dev/null +++ b/sys/dev/psci/smccc_errata.c @@ -0,0 +1,139 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2024 Arm Ltd + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * A driver for the Arm Errata Management Firmware Interface (Errata ABI). + * This queries into the SMCCC firmware for the status of errata using the + * interface documented in den0100 [1]. + * + * [1] https://developer.arm.com/documentation/den0100/latest + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/eventhandler.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/random.h> + +#include <dev/psci/psci.h> +#include <dev/psci/smccc.h> + +#include <machine/cpu_feat.h> + +#define ERRATA_HIGHER_EL_MITIGATION 3 +#define ERRATA_NOT_AFFECTED 2 +#define ERRATA_AFFECTED 1 + +#define EM_VERSION SMCCC_FUNC_ID(SMCCC_FAST_CALL, \ + SMCCC_32BIT_CALL, SMCCC_STD_SECURE_SERVICE_CALLS, 0xf0u) +#define EM_VERSION_MIN 0x10000L +#define EM_FEATURES SMCCC_FUNC_ID(SMCCC_FAST_CALL, \ + SMCCC_32BIT_CALL, SMCCC_STD_SECURE_SERVICE_CALLS, 0xf1u) +#define EM_CPU_ERRATUM_FEATURES SMCCC_FUNC_ID(SMCCC_FAST_CALL, \ + SMCCC_32BIT_CALL, SMCCC_STD_SECURE_SERVICE_CALLS, 0xf2u) + +static device_identify_t errata_identify; +static device_probe_t errata_probe; +static device_attach_t errata_attach; +static cpu_feat_errata errata_cpu_feat_errata_check(const struct cpu_feat *, + u_int); + +static void +errata_identify(driver_t *driver, device_t parent) +{ + int32_t version; + + /* Check if Errata ABI is supported */ + if (smccc_arch_features(EM_VERSION) != SMCCC_RET_SUCCESS) + return; + + /* Check we have Errata 1.0 or later */ + version = psci_call(EM_VERSION, 0, 0, 0); + if (version < EM_VERSION_MIN) + return; + + if (BUS_ADD_CHILD(parent, 0, "errata", DEVICE_UNIT_ANY) == NULL) + device_printf(parent, "add errata child failed\n"); +} + +static int +errata_probe(device_t dev) +{ + device_set_desc(dev, "Arm SMCCC Errata Management"); + return (BUS_PROBE_NOWILDCARD); +} + +static int +errata_attach(device_t dev) +{ + /* Check for EM_CPU_ERRATUM_FEATURES. It's mandatory, so should exist */ + if (arm_smccc_invoke(EM_FEATURES, EM_CPU_ERRATUM_FEATURES, NULL) < 0) { + device_printf(dev, + "EM_CPU_ERRATUM_FEATURES is not implemented\n"); + return (ENXIO); + } + + cpu_feat_register_errata_check(errata_cpu_feat_errata_check); + + return (0); +} + +static cpu_feat_errata +errata_cpu_feat_errata_check(const struct cpu_feat *feat __unused, u_int errata_id) +{ + struct arm_smccc_res res; + + switch(arm_smccc_invoke(EM_CPU_ERRATUM_FEATURES, errata_id, 0, &res)) { + default: + return (ERRATA_UNKNOWN); + case ERRATA_NOT_AFFECTED: + return (ERRATA_NONE); + case ERRATA_AFFECTED: + return (ERRATA_AFFECTED); + case ERRATA_HIGHER_EL_MITIGATION: + return (ERRATA_FW_MITIGAION); + } +} + +static device_method_t errata_methods[] = { + DEVMETHOD(device_identify, errata_identify), + DEVMETHOD(device_probe, errata_probe), + DEVMETHOD(device_attach, errata_attach), + + DEVMETHOD_END +}; + +static driver_t errata_driver = { + "errata", + errata_methods, + 0 +}; + +DRIVER_MODULE(errata, smccc, errata_driver, 0, 0); diff --git a/sys/dev/psci/smccc_trng.c b/sys/dev/psci/smccc_trng.c new file mode 100644 index 000000000000..ab98837d3841 --- /dev/null +++ b/sys/dev/psci/smccc_trng.c @@ -0,0 +1,143 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2024 Arm Ltd + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * A driver for the Arm True Random Number Generator Firmware Interface. + * This queries into the SMCCC firmware for random numbers using the + * interface documented in den0098 [1]. + * + * [1] https://developer.arm.com/documentation/den0098/latest + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/random.h> + +#include <dev/psci/psci.h> +#include <dev/psci/smccc.h> + +#include <dev/random/randomdev.h> + +#define TRNG_VERSION SMCCC_FUNC_ID(SMCCC_FAST_CALL, \ + SMCCC_32BIT_CALL, SMCCC_STD_SECURE_SERVICE_CALLS, 0x50) +#define TRNG_VERSION_MIN 0x10000L +#define TRNG_RND64 SMCCC_FUNC_ID(SMCCC_FAST_CALL, \ + SMCCC_64BIT_CALL, SMCCC_STD_SECURE_SERVICE_CALLS, 0x53) + +static device_identify_t trng_identify; +static device_probe_t trng_probe; +static device_attach_t trng_attach; + +static unsigned trng_read(void *, unsigned); + +static struct random_source random_trng = { + .rs_ident = "Arm SMCCC TRNG", + .rs_source = RANDOM_PURE_ARM_TRNG, + .rs_read = trng_read, +}; + +static void +trng_identify(driver_t *driver, device_t parent) +{ + int32_t version; + + /* Check if TRNG is supported */ + if (smccc_arch_features(TRNG_VERSION) != SMCCC_RET_SUCCESS) + return; + + /* Check we have TRNG 1.0 or later */ + version = psci_call(TRNG_VERSION, 0, 0, 0); + if (version < TRNG_VERSION_MIN) + return; + + if (BUS_ADD_CHILD(parent, 0, "trng", DEVICE_UNIT_ANY) == NULL) + device_printf(parent, "add TRNG child failed\n"); +} + +static int +trng_probe(device_t dev) +{ + device_set_desc(dev, "Arm SMCCC TRNG"); + return (BUS_PROBE_NOWILDCARD); +} + +static int +trng_attach(device_t dev) +{ + struct arm_smccc_res res; + int32_t ret; + + ret = arm_smccc_invoke(TRNG_RND64, 192, &res); + if (ret < 0) { + device_printf(dev, "Failed to read fron TRNG\n"); + } else { + random_source_register(&random_trng); + } + + return (0); +} + +static unsigned +trng_read(void *buf, unsigned usz) +{ + struct arm_smccc_res res; + register_t len; + int32_t ret; + + len = usz; + if (len > sizeof(uint64_t)) + len = sizeof(uint64_t); + if (len == 0) + return (0); + + ret = arm_smccc_invoke(TRNG_RND64, len * 8, &res); + if (ret < 0) + return (0); + + memcpy(buf, &res.a3, len); + return (len); +} + +static device_method_t trng_methods[] = { + DEVMETHOD(device_identify, trng_identify), + DEVMETHOD(device_probe, trng_probe), + DEVMETHOD(device_attach, trng_attach), + + DEVMETHOD_END +}; + +static driver_t trng_driver = { + "trng", + trng_methods, + 0 +}; + +DRIVER_MODULE(trng, smccc, trng_driver, 0, 0); |