Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F101269204
D47248.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
26 KB
Referenced Files
None
Subscribers
None
D47248.diff
View Options
diff --git a/sys/arm64/arm64/genassym.c b/sys/arm64/arm64/genassym.c
--- a/sys/arm64/arm64/genassym.c
+++ b/sys/arm64/arm64/genassym.c
@@ -47,6 +47,8 @@
ASSYM(PC_CURPCB, offsetof(struct pcpu, pc_curpcb));
ASSYM(PC_CURTHREAD, offsetof(struct pcpu, pc_curthread));
ASSYM(PC_SSBD, offsetof(struct pcpu, pc_ssbd));
+ASSYM(PC_SDEI_NORMAL_STACK, offsetof(struct pcpu, pc_sdei_normal_stack));
+ASSYM(PC_SDEI_CRITICAL_STACK, offsetof(struct pcpu, pc_sdei_critical_stack));
/* Size of pcb, rounded to keep stack alignment */
ASSYM(PCB_SIZE, roundup2(sizeof(struct pcb), STACKALIGNBYTES + 1));
diff --git a/sys/arm64/conf/std.dev b/sys/arm64/conf/std.dev
--- a/sys/arm64/conf/std.dev
+++ b/sys/arm64/conf/std.dev
@@ -118,6 +118,7 @@
# Firmware
device mmio_sram # Generic on-chip SRAM
+device sdei # Software defined exception interface
# Wireless options
options IEEE80211_DEBUG # enable debug msgs
diff --git a/sys/arm64/include/intr.h b/sys/arm64/include/intr.h
--- a/sys/arm64/include/intr.h
+++ b/sys/arm64/include/intr.h
@@ -47,11 +47,13 @@
#define ACPI_INTR_XREF 1
#define ACPI_MSI_XREF 2
#define ACPI_GPIO_XREF 3
+#define ACPI_SDEI_XREF 4
#endif
#endif /* !LOCORE */
#define INTR_ROOT_FIQ 1
-#define INTR_ROOT_NUM 2
+#define INTR_ROOT_SDEI 2
+#define INTR_ROOT_NUM 3
#endif /* _MACHINE_INTR_H */
diff --git a/sys/arm64/include/pcpu.h b/sys/arm64/include/pcpu.h
--- a/sys/arm64/include/pcpu.h
+++ b/sys/arm64/include/pcpu.h
@@ -50,7 +50,9 @@
struct pmap *pc_curvmpmap; \
uint64_t pc_mpidr; \
u_int pc_bcast_tlbi_workaround; \
- char __pad[197]
+ void *pc_sdei_normal_stack; \
+ void *pc_sdei_critical_stack; \
+ char __pad[181]
#ifdef _KERNEL
diff --git a/sys/arm64/include/stack.h b/sys/arm64/include/stack.h
--- a/sys/arm64/include/stack.h
+++ b/sys/arm64/include/stack.h
@@ -50,9 +50,35 @@
static __inline bool
kstack_contains(struct thread *td, vm_offset_t va, size_t len)
{
- return (va >= td->td_kstack && va + len >= va &&
- va + len <= td->td_kstack + td->td_kstack_pages * PAGE_SIZE -
- sizeof(struct pcb));
+ extern u_int mp_maxid;
+ u_int cpu;
+
+ if (va + len < va)
+ return (false);
+
+ if (va >= td->td_kstack && va + len <= td->td_kstack +
+ td->td_kstack_pages * PAGE_SIZE - sizeof(struct pcb))
+ return (true);
+
+ for (cpu = 0; cpu <= mp_maxid; cpu++) {
+ struct pcpu *pcpu;
+
+ pcpu = pcpu_find(cpu);
+ if (pcpu == NULL)
+ continue;
+ if (pcpu->pc_sdei_normal_stack != NULL &&
+ va >= (uintptr_t)pcpu->pc_sdei_normal_stack &&
+ va + len <= (uintptr_t)pcpu->pc_sdei_normal_stack +
+ PAGE_SIZE)
+ return (true);
+ if (pcpu->pc_sdei_critical_stack != NULL &&
+ va >= (uintptr_t)pcpu->pc_sdei_critical_stack &&
+ va + len <= (uintptr_t)pcpu->pc_sdei_critical_stack +
+ PAGE_SIZE)
+ return (true);
+ }
+
+ return (false);
}
#endif /* _SYS_PROC_H_ */
diff --git a/sys/conf/files.arm64 b/sys/conf/files.arm64
--- a/sys/conf/files.arm64
+++ b/sys/conf/files.arm64
@@ -417,6 +417,9 @@
dev/pci/pci_dw_if.m optional pci fdt
dev/psci/psci.c standard
+dev/psci/sdei.c optional sdei
+dev/psci/sdei_acpi.c optional sdei acpi
+dev/psci/sdei_arm64.S optional sdei
dev/psci/smccc_arm64.S standard
dev/psci/smccc_trng.c standard
dev/psci/smccc.c standard
diff --git a/sys/dev/psci/sdei.h b/sys/dev/psci/sdei.h
new file mode 100644
--- /dev/null
+++ b/sys/dev/psci/sdei.h
@@ -0,0 +1,65 @@
+/*-
+ * 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.
+ */
+
+#ifndef _PSCI_SDEI_H_
+#define _PSCI_SDEI_H_
+
+#define SDEI_FUNC(id) SMCCC_FUNC_ID(SMCCC_FAST_CALL, SMCCC_64BIT_CALL, \
+ SMCCC_STD_SECURE_SERVICE_CALLS, id)
+
+#define SDEI_VERSION SDEI_FUNC(0x20)
+#define SDEI_EVENT_REGISTER SDEI_FUNC(0x21)
+#define SDEI_EVENT_ENABLE SDEI_FUNC(0x22)
+#define SDEI_EVENT_DISABLE SDEI_FUNC(0x23)
+#define SDEI_EVENT_CONTEXT SDEI_FUNC(0x24)
+#define SDEI_EVENT_COMPLETE SDEI_FUNC(0x25)
+#define SDEI_EVENT_COMPLETE_AND_RESUME SDEI_FUNC(0x26)
+#define SDEI_EVENT_UNREGISTER SDEI_FUNC(0x27)
+#define SDEI_EVENT_STATUS SDEI_FUNC(0x28)
+#define SDEI_EVENT_GET_INFO SDEI_FUNC(0x29)
+#define SDEI_EVENT_ROUTING_SET SDEI_FUNC(0x2a)
+#define SDEI_PE_MASK SDEI_FUNC(0x2b)
+#define SDEI_PE_UNMASK SDEI_FUNC(0x2c)
+#define SDEI_INTERRUPT_BIND SDEI_FUNC(0x2d)
+#define SDEI_INTERRUPT_RELEASE SDEI_FUNC(0x2e)
+#define SDEI_EVENT_SIGNAL SDEI_FUNC(0x2f)
+#define SDEI_FEATURES SDEI_FUNC(0x30)
+#define SDEI_PRIVATE_RESET SDEI_FUNC(0x31)
+#define SDEI_SHARED_RESET SDEI_FUNC(0x32)
+
+/* SDEI_EVENT_GET_INFO */
+#define SDEI_INFO_EV_TYPE 0
+#define SDEI_INFO_EV_TYPE_PRIVATE 0
+#define SDEI_INFO_EV_TYPE_SHARED 1
+#define SDEI_INFO_EV_SIGNALED 1
+#define SDEI_INFO_EV_PRIORITY 2
+#define SDEI_INFO_EV_PRIORITY_NORMAL 0
+#define SDEI_INFO_EV_PRIORITY_CRITICAL 1
+#define SDEI_INFO_EV_ROUTING_MODE 3
+#define SDEI_INFO_EV_ROUTING_AFF 4
+
+#endif
diff --git a/sys/dev/psci/sdei.c b/sys/dev/psci/sdei.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/psci/sdei.c
@@ -0,0 +1,416 @@
+/*-
+ * 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.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/domainset.h>
+#include <sys/bus.h>
+#include <sys/interrupt.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/proc.h>
+#include <sys/smp.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <machine/pmap.h>
+
+#include <machine/machdep.h>
+#include <machine/smp.h>
+
+#include <dev/psci/psci.h>
+#include <dev/psci/sdei.h>
+#include <dev/psci/sdeivar.h>
+
+#include "pic_if.h"
+
+#define SDEI_EVENT_0 0
+
+static MALLOC_DEFINE(M_SDEI, "sdei", "SDEI buffers");
+
+extern void sdei_handler_hvc(void);
+extern void sdei_handler_smc(void);
+
+#ifdef SMP
+static pic_init_secondary_t sdei_init_secondary;
+static pic_ipi_send_t sdei_ipi_send;
+static pic_ipi_setup_t sdei_ipi_setup;
+#endif
+
+static struct sdei_event **sdei_alloc_private_event(struct sdei_softc *,
+ uint32_t);
+static void sdei_free_private_event(struct sdei_event **);
+static bool sdei_attach_event(struct sdei_softc *, struct sdei_event *);
+static int sdei_intr(void *);
+
+static device_method_t sdei_methods[] = {
+#ifdef SMP
+ /* Interrupt controller interface */
+ DEVMETHOD(pic_init_secondary, sdei_init_secondary),
+ DEVMETHOD(pic_ipi_send, sdei_ipi_send),
+ DEVMETHOD(pic_ipi_setup, sdei_ipi_setup),
+#endif
+
+ /* End */
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_0(sdei, sdei_driver, sdei_methods, sizeof(struct sdei_softc));
+
+static int
+_sdei_smccc_invoke(struct sdei_softc *sc, register_t a0, register_t a1,
+ register_t a2, register_t a3, register_t a4, register_t a5, register_t a6,
+ struct arm_smccc_1_2_regs *res)
+{
+ struct arm_smccc_1_2_regs args;
+
+ args.a0 = a0;
+ args.a1 = a1;
+ args.a2 = a2;
+ args.a3 = a3;
+ args.a4 = a4;
+ args.a5 = a5;
+ args.a6 = a6;
+ args.a7 = 0;
+
+ return (sc->sc_smccc_call(&args, res));
+}
+#define sdei_smccc_invoke(sc, ...) \
+ _arm_smccc_invoke(_sdei_smccc_invoke, sc, __VA_ARGS__)
+
+static int
+sdei_get_info(struct sdei_softc *sc, uint32_t event, uint32_t info,
+ uint64_t *result)
+{
+ struct arm_smccc_1_2_regs res;
+ int ret;
+
+ ret = sdei_smccc_invoke(sc, SDEI_EVENT_GET_INFO, event, info, &res);
+ if (ret < 0)
+ return (ret);
+
+ *result = res.a0;
+ return (0);
+}
+
+static bool
+sdei_event_is_private(struct sdei_softc *sc, uint32_t event, bool *priv)
+{
+ uint64_t res;
+
+ if (sdei_get_info(sc, event, SDEI_INFO_EV_TYPE, &res) != 0)
+ return (false);
+
+ switch (res) {
+ case SDEI_INFO_EV_TYPE_PRIVATE:
+ *priv = true;
+ break;
+ case SDEI_INFO_EV_TYPE_SHARED:
+ *priv = false;
+ break;
+ default:
+ return (false);
+ }
+
+ return (true);
+}
+
+static bool
+sdei_event_is_critical(struct sdei_softc *sc, uint32_t event, bool *crit)
+{
+ uint64_t res;
+
+ if (sdei_get_info(sc, event, SDEI_INFO_EV_PRIORITY, &res) != 0)
+ return (false);
+
+ switch (res) {
+ case SDEI_INFO_EV_PRIORITY_NORMAL:
+ *crit = false;
+ break;
+ case SDEI_INFO_EV_PRIORITY_CRITICAL:
+ *crit = true;
+ break;
+ default:
+ return (false);
+ }
+
+ return (true);
+}
+
+static struct sdei_event **
+sdei_alloc_private_event(struct sdei_softc *sc, uint32_t event_id)
+{
+ struct sdei_event **event;
+ u_int cpu;
+ bool critical;
+
+ /* Try reading if the event is critical */
+ if (!sdei_event_is_critical(sc, event_id, &critical))
+ return (NULL);
+
+ event = mallocarray(mp_maxid + 1, sizeof(*event), M_SDEI,
+ M_WAITOK | M_ZERO);
+ CPU_FOREACH(cpu) {
+ event[cpu] = malloc_domainset(sizeof(**event),
+ M_SDEI, DOMAINSET_PREF(pcpu_find(cpu)->pc_domain),
+ M_WAITOK);
+ event[cpu]->event_critical = critical;
+ event[cpu]->event_id = event_id;
+ }
+
+ return (event);
+}
+
+static void
+sdei_free_private_event(struct sdei_event **event)
+{
+ u_int cpu;
+
+ CPU_FOREACH(cpu) {
+ free(event[cpu], M_SDEI);
+ }
+ free(event, M_SDEI);
+}
+
+static bool
+sdei_attach_event(struct sdei_softc *sc, struct sdei_event *event)
+{
+ void (*handler)(void);
+ uint32_t event_id;
+
+ switch (sc->sc_method) {
+ case PSCI_METHOD_HVC:
+ handler = sdei_handler_hvc;
+ break;
+ case PSCI_METHOD_SMC:
+ handler = sdei_handler_smc;
+ break;
+ default:
+ return (false);
+ }
+
+ MPASS(event != NULL);
+ event_id = event->event_id;
+ if (sdei_smccc_invoke(sc, SDEI_EVENT_REGISTER, event_id,
+ (register_t)handler, (register_t)event, 0, 0, NULL) != 0)
+ return (false);
+
+ if (sdei_smccc_invoke(sc, SDEI_EVENT_ENABLE, event_id, NULL) != 0) {
+ /*
+ * Unregister on failure. Don't check for errors as there's
+ * nothing we could do other than report it to the user.
+ */
+ sdei_smccc_invoke(sc, SDEI_EVENT_UNREGISTER, event_id, NULL);
+ return (false);
+ }
+
+ return (true);
+}
+
+int
+sdei_attach(device_t dev)
+{
+ struct sdei_softc *sc;
+ int cpu, ret;
+#ifdef SMP
+ bool valid;
+#endif
+
+ /*
+ * The firmware will send exceptions to the highest implemented
+ * exception level below it, i.e. EL3 will send to EL2 if implemented
+ * otherwise EL1.
+ *
+ * If we are started in EL2, but are currently running in EL1 then
+ * this will break. Fail to probe in this case so we don't get in to
+ * a situation where SDEI exceptions are sent to an exception level
+ * that isn't expecting them.
+ */
+ if (has_hyp() && !in_vhe())
+ return (0);
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+
+ switch (sc->sc_method) {
+ case PSCI_METHOD_HVC:
+ sc->sc_smccc_call = &arm_smccc_1_2_hvc;
+ break;
+ case PSCI_METHOD_SMC:
+ sc->sc_smccc_call = &arm_smccc_1_2_smc;
+ break;
+ default:
+ return (ENXIO);
+ }
+
+ sc->sc_pic = intr_pic_register(dev, sc->sc_intr_xref);
+ if (sc->sc_pic == NULL) {
+ device_printf(dev, "Unable to register PIC\n");
+ return (ENXIO);
+ }
+
+ if (intr_pic_claim_root(dev, sc->sc_intr_xref, sdei_intr, sc,
+ INTR_ROOT_SDEI) != 0) {
+ device_printf(dev, "Unable to claim SDEI interrupt root\n");
+ return (ENXIO);
+ }
+
+ /* Allocate stacks for each CPU */
+ CPU_FOREACH(cpu) {
+ struct pcpu *pcpu;
+
+ pcpu = pcpu_find(cpu);
+ pcpu->pc_sdei_normal_stack = malloc_domainset_aligned(PAGE_SIZE,
+ PAGE_SIZE, M_SDEI, DOMAINSET_PREF(pcpu->pc_domain),
+ M_WAITOK);
+ pcpu->pc_sdei_critical_stack = malloc_domainset_aligned(
+ PAGE_SIZE, PAGE_SIZE, M_SDEI,
+ DOMAINSET_PREF(pcpu->pc_domain), M_WAITOK);
+ }
+
+#ifdef SMP
+ valid = sdei_event_is_private(sc, SDEI_EVENT_0,
+ &sc->sc_event_0_private);
+ if (valid && sc->sc_event_0_private) {
+ sc->sc_event0 = sdei_alloc_private_event(sc, SDEI_EVENT_0);
+ }
+
+ if (sc->sc_event0 != NULL) {
+ if (sdei_attach_event(sc, sc->sc_event0[PCPU_GET(cpuid)])) {
+ /*
+ * Handle hard stops as they come from panic
+ * so need to be a NMI.
+ */
+ intr_ipi_pic_register_ipi(IPI_STOP_HARD, dev, 1);
+ } else {
+ sdei_free_private_event(sc->sc_event0);
+ sc->sc_event0 = NULL;
+ }
+ }
+#endif
+
+ /*
+ * This can only fail if SDEI is unimplemented. As we've been calling
+ * into SDEI before now assert it succeeded assert this also succeeds.
+ */
+ ret = sdei_smccc_invoke(sc, SDEI_PE_UNMASK, NULL);
+ KASSERT(ret == 0, ("%s: Invalid response from SDEI_PE_UNMASK: %d",
+ device_get_nameunit(dev), ret));
+
+ return (0);
+}
+
+#ifdef SMP
+static void
+sdei_init_secondary(device_t dev, uint32_t rootnum)
+{
+ struct sdei_softc *sc;
+ int ret __diagused;
+ bool attached __diagused;
+
+ sc = device_get_softc(dev);
+ attached = sdei_attach_event(sc, sc->sc_event0[PCPU_GET(cpuid)]);
+ KASSERT(attached, ("%s: Unable to attach event 0",
+ device_get_nameunit(dev)));
+
+ /* As above assert SDEI_PE_UNMASK succeeds */
+ ret = sdei_smccc_invoke(sc, SDEI_PE_UNMASK, NULL);
+ KASSERT(ret == 0, ("%s: Invalid response from SDEI_PE_UNMASK: %d",
+ device_get_nameunit(dev), ret));
+}
+
+static void
+sdei_ipi_send(device_t dev, struct intr_irqsrc *isrc, cpuset_t cpus,
+ u_int ipi)
+{
+ struct sdei_softc *sc;
+ bool send_self;
+
+ KASSERT(ipi == IPI_STOP_HARD,
+ ("%s: Invalid IPI: %x != IPI_STOP_HARD (%x)", __func__, ipi,
+ IPI_STOP_HARD));
+
+ sc = device_get_softc(dev);
+ send_self = false;
+ for (int i = 0; i <= mp_maxid; i++) {
+ if (CPU_ISSET(i, &cpus)) {
+ if (PCPU_GET(cpuid) == i) {
+ send_self = true;
+ } else {
+ sdei_smccc_invoke(sc, SDEI_EVENT_SIGNAL,
+ SDEI_EVENT_0,
+ PCPU_GET_MPIDR(cpuid_to_pcpu[i]), NULL);
+ }
+ }
+ }
+ if (send_self) {
+ sdei_smccc_invoke(sc, SDEI_EVENT_SIGNAL, SDEI_EVENT_0,
+ get_mpidr(), NULL);
+ }
+}
+
+static int
+sdei_ipi_setup(device_t dev, u_int ipi, struct intr_irqsrc **isrcp)
+{
+ struct sdei_softc *sc;
+ struct intr_irqsrc *isrc;
+
+ KASSERT(ipi == IPI_STOP_HARD,
+ ("%s: Invalid IPI: %x != IPI_STOP_HARD (%x)", __func__, ipi,
+ IPI_STOP_HARD));
+
+ sc = device_get_softc(dev);
+
+ isrc = &sc->sc_ipi_isrc;
+ CPU_SET(PCPU_GET(cpuid), &isrc->isrc_cpu);
+ *isrcp = isrc;
+
+ return (0);
+}
+#endif
+
+static int
+sdei_intr(void *arg)
+{
+ struct sdei_softc *sc;
+ uint32_t event_id;
+
+ sc = arg;
+ event_id = curthread->td_intr_frame->tf_x[0];
+ switch (event_id) {
+#ifdef SMP
+ case SDEI_EVENT_0:
+ intr_ipi_dispatch(IPI_STOP_HARD);
+ break;
+#endif
+ default:
+ device_printf(sc->sc_dev, "Stray SDEI event %u\n", event_id);
+ }
+
+ return (FILTER_HANDLED);
+}
diff --git a/sys/dev/psci/sdei_acpi.c b/sys/dev/psci/sdei_acpi.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/psci/sdei_acpi.c
@@ -0,0 +1,97 @@
+/*-
+ * 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.
+ */
+
+#include "opt_acpi.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/domainset.h>
+#include <sys/bus.h>
+#include <sys/interrupt.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+
+#include <contrib/dev/acpica/include/acpi.h>
+#include <dev/acpica/acpivar.h>
+
+#include <dev/psci/psci.h>
+#include <dev/psci/sdeivar.h>
+
+static device_identify_t sdei_acpi_identify;
+static device_probe_t sdei_acpi_probe;
+static device_attach_t sdei_acpi_attach;
+
+static device_method_t sdei_acpi_methods[] = {
+ DEVMETHOD(device_identify, sdei_acpi_identify),
+ DEVMETHOD(device_probe, sdei_acpi_probe),
+ DEVMETHOD(device_attach, sdei_acpi_attach),
+
+ /* End */
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(sdei, sdei_acpi_driver, sdei_acpi_methods,
+ sizeof(struct sdei_softc), sdei_driver);
+
+DRIVER_MODULE(sdei, acpi, sdei_acpi_driver, 0, 0);
+
+static void
+sdei_acpi_identify(driver_t *driver, device_t parent)
+{
+ ACPI_TABLE_HEADER *sdei;
+ device_t dev;
+ if (ACPI_FAILURE(AcpiGetTable(ACPI_SIG_SDEI, 0, &sdei)))
+ return;
+ AcpiPutTable(sdei);
+
+ dev = BUS_ADD_CHILD(parent, BUS_PASS_DEFAULT, "sdei", -1);
+ if (dev == NULL)
+ return;
+ acpi_set_private(dev, sdei_acpi_identify);
+}
+
+static int
+sdei_acpi_probe(device_t dev)
+{
+ if (acpi_get_private(dev) != sdei_acpi_identify)
+ return (ENXIO);
+
+ device_set_desc(dev, "Arm SDEI");
+ return (0);
+}
+
+static int
+sdei_acpi_attach(device_t dev)
+{
+ struct sdei_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->sc_intr_xref = ACPI_SDEI_XREF;
+ sc->sc_method = psci_get_method();
+
+ return (sdei_attach(dev));
+}
diff --git a/sys/dev/psci/sdei_arm64.S b/sys/dev/psci/sdei_arm64.S
new file mode 100644
--- /dev/null
+++ b/sys/dev/psci/sdei_arm64.S
@@ -0,0 +1,181 @@
+/*-
+ * 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.
+ */
+
+#include <sys/elf_common.h>
+#include <sys/intr.h>
+
+#include <machine/asm.h>
+#include <machine/armreg.h>
+#include <machine/intr.h>
+#include <machine/param.h>
+#include "assym.inc"
+
+#include <dev/psci/smccc.h>
+#include <dev/psci/sdei.h>
+
+/*
+ * x0: Event number
+ * x1: struct sdei_event_args address
+ * x2: Interrupted PC
+ * x3: Interrupted PSTATE
+ * x18-x29 must be preserved
+ */
+LENTRY(sdei_handler)
+ /* Save the GP registers in the tf_x array */
+ stp x0, x1, [x1, #(TF_X + (0 * 8))]
+ /* x2 and x3 are saved in the caller */
+ stp x4, x5, [x1, #(TF_X + (4 * 8))]
+ stp x6, x7, [x1, #(TF_X + (6 * 8))]
+ stp x8, x9, [x1, #(TF_X + (8 * 8))]
+ stp x10, x11, [x1, #(TF_X + (10 * 8))]
+ stp x12, x13, [x1, #(TF_X + (12 * 8))]
+ stp x14, x15, [x1, #(TF_X + (14 * 8))]
+ stp x16, x17, [x1, #(TF_X + (16 * 8))]
+ stp x18, x19, [x1, #(TF_X + (18 * 8))]
+ stp x20, x21, [x1, #(TF_X + (20 * 8))]
+ stp x22, x23, [x1, #(TF_X + (22 * 8))]
+ stp x24, x25, [x1, #(TF_X + (24 * 8))]
+ stp x26, x27, [x1, #(TF_X + (26 * 8))]
+ stp x28, x29, [x1, #(TF_X + (28 * 8))]
+ /* SP and LR are saved in the caller */
+
+ /* No ESR or FAR registers */
+ stp xzr, xzr, [x1, #(TF_ESR)]
+
+ /* The firmware may have not set the PAN flag correctly */
+ EXIT_USER_ACCESS_CHECK(w0, x2)
+
+ /*
+ * As the CPU may not have been in the kernel when the SDEI
+ * event was raised we need to restore the pcpu pointer and
+ * save the userspace stack pointer. Unfortunatly tpidr_el1
+ * may have been trashed. Work around this by using tpidr_el2
+ * to restore both x18 and tpidr_el1.
+ */
+ mrs x5, CurrentEL
+ and x5, x5, #(CURRENTEL_EL_MASK)
+ cmp x5, #(CURRENTEL_EL_EL1)
+ b.eq .Lel1
+
+ /* The kernel is in EL2 */
+ mrs x18, tpidr_el2
+ msr tpidr_el1, x18
+ isb
+ b .Ldone_el
+
+.Lel1:
+ /* The kernel is in EL1 */
+ mrs x18, tpidr_el1
+
+.Ldone_el:
+
+ /* Store sp_el0 in a callee-saved register so we can restore it later */
+ mrs x19, sp_el0
+#if defined(PERTHREAD_SSP)
+ /* Load the SSP canary to sp_el0 */
+ ldr x6, [x18, #(PC_CURTHREAD)]
+ add x6, x6, #(TD_MD_CANARY)
+ msr sp_el0, x6
+#endif
+
+ /* Load the correct stack pointer */
+ ldp x20, x21, [x18, #PC_SDEI_NORMAL_STACK]
+ ldrb w22, [x1, #TF_SIZE]
+ cmp w22, #0
+ csel x20, x20, x21, eq
+ add sp, x20, #PAGE_SIZE
+
+ /*
+ * Create a fake stack frame for unwinding to use to get onto the
+ * correct stack. If we entered* from the kernel link to its stack,
+ * otherwise store zeros to the stack to stop unwinding.
+ */
+ and x6, x3, PSR_M_EL_MASK
+ cmp x5, x6
+ csel x29, x29, xzr, eq
+ csel x2, x2, xzr, eq
+ stp x29, x2, [sp, #(-16)]!
+ mov x29, sp
+
+ /*
+ * Create the stack frame either pointing to the kernel frame or
+ * stoping a stacktrace.
+ */
+ stp x29, x30, [sp, #(-16)]!
+ mov x29, sp
+ mov x20, x1
+
+ // KMSAN_ENTER
+ mov x0, x1
+ mov x1, #INTR_ROOT_SDEI
+ bl intr_irq_handler
+ // KMSAN_LEAVE
+
+ /* Load the return address, skipping fp as it will be loaded below */
+ ldp xzr, x30, [sp], #32
+
+ msr sp_el0, x19
+
+ /* Restore registers we need to preserve */
+ mov x1, x20
+ ldp x18, x19, [x1, #(TF_X + (18 * 8))]
+ ldp x20, x21, [x1, #(TF_X + (20 * 8))]
+ ldp x22, x23, [x1, #(TF_X + (22 * 8))]
+ ldp x24, x25, [x1, #(TF_X + (24 * 8))]
+ ldp x26, x27, [x1, #(TF_X + (26 * 8))]
+ ldp x28, x29, [x1, #(TF_X + (28 * 8))]
+
+ ret
+LEND(sdei_handler)
+
+.macro sdei_handler_impl insn
+EENTRY(sdei_handler_\insn)
+ /*
+ * Free up a register to use. x2 and x3 hold elr and spsr.
+ * x3 is used later to check what exception level was interrupted.
+ */
+ stp x2, x3, [x1, #(TF_ELR)]
+ stp x2, x3, [x1, #(TF_X + (2 * 8))]
+ /* Save SP and LR */
+ mov x2, sp
+ stp x2, lr, [x1, #(TF_SP)]
+
+ bl sdei_handler
+
+ ldp x2, lr, [x1, #(TF_SP)]
+ mov sp, x2
+
+ ldr x0, =(SDEI_EVENT_COMPLETE)
+ mov x1, xzr
+ \insn #0
+EEND(sdei_handler_\insn)
+.endm
+
+sdei_handler_impl hvc
+sdei_handler_impl smc
+
+GNU_PROPERTY_AARCH64_FEATURE_1_NOTE(GNU_PROPERTY_AARCH64_FEATURE_1_VAL)
diff --git a/sys/arm64/include/intr.h b/sys/dev/psci/sdeivar.h
copy from sys/arm64/include/intr.h
copy to sys/dev/psci/sdeivar.h
--- a/sys/arm64/include/intr.h
+++ b/sys/dev/psci/sdeivar.h
@@ -1,6 +1,7 @@
/*-
- * Copyright (c) 2014 Andrew Turner <andrew@FreeBSD.org>
- * All rights reserved.
+ * 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
@@ -24,34 +25,38 @@
* SUCH DAMAGE.
*/
-#ifndef _MACHINE_INTR_H_
-#define _MACHINE_INTR_H_
-
-#ifndef LOCORE
-#ifdef FDT
-#include <dev/ofw/openfirm.h>
+#ifndef _PSCI_SDEIVAR_H_
+#define _PSCI_SDEIVAR_H_
+
+#include <machine/intr.h>
+#include <dev/psci/smccc.h>
+
+struct sdei_event {
+ struct trapframe tf;
+ /* This must be straight after the trapframe as it's read from asm */
+ bool event_critical;
+
+ /* These should not be accessed by asm so can be reordered as needed */
+ uint32_t event_id;
+};
+
+struct sdei_softc {
+ device_t sc_dev;
+ int (*sc_smccc_call)(const struct arm_smccc_1_2_regs *,
+ struct arm_smccc_1_2_regs *);
+ psci_method_t sc_method;
+
+ intptr_t sc_intr_xref;
+ struct intr_pic *sc_pic;
+#ifdef SMP
+ struct intr_irqsrc sc_ipi_isrc;
+ bool sc_event_0_private;
+ struct sdei_event **sc_event0;
#endif
+};
-#include <sys/intr.h>
-
-#ifndef NIRQ
-#define NIRQ 16384 /* XXX - It should be an option. */
-#endif
+DECLARE_CLASS(sdei_driver);
-static inline void
-arm_irq_memory_barrier(uintptr_t irq)
-{
-}
+int sdei_attach(device_t);
-#ifdef DEV_ACPI
-#define ACPI_INTR_XREF 1
-#define ACPI_MSI_XREF 2
-#define ACPI_GPIO_XREF 3
#endif
-
-#endif /* !LOCORE */
-
-#define INTR_ROOT_FIQ 1
-#define INTR_ROOT_NUM 2
-
-#endif /* _MACHINE_INTR_H */
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Oct 27, 11:17 PM (19 h, 52 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
14362538
Default Alt Text
D47248.diff (26 KB)
Attached To
Mode
D47248: dev/psci: Add initial SDEI support
Attached
Detach File
Event Timeline
Log In to Comment