aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/psci
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/psci')
-rw-r--r--sys/dev/psci/psci.c26
-rw-r--r--sys/dev/psci/smccc.c55
-rw-r--r--sys/dev/psci/smccc.h33
-rw-r--r--sys/dev/psci/smccc_arm64.S7
-rw-r--r--sys/dev/psci/smccc_errata.c139
-rw-r--r--sys/dev/psci/smccc_trng.c143
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);