Add support for handling Non-Maskable Interrupts (NMIs) through the
RISC-V Supervisor Software Events (SSE) framework. Add basic NMI
functionality via SBI_SSE_EVENT_LOCAL_SOFTWARE_INJECTED registration
and enabling.
Signed-off-by: Yunhui Cui <cuiyunhui@bytedance.com>
---
MAINTAINERS | 8 +++
drivers/firmware/riscv/Kconfig | 10 +++
drivers/firmware/riscv/Makefile | 1 +
drivers/firmware/riscv/riscv_sse_nmi.c | 89 ++++++++++++++++++++++++++
include/linux/riscv_sse_nmi.h | 26 ++++++++
5 files changed, 134 insertions(+)
create mode 100644 drivers/firmware/riscv/riscv_sse_nmi.c
create mode 100644 include/linux/riscv_sse_nmi.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 8bf5416953f45..c06658da8af96 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22057,6 +22057,14 @@ S: Maintained
F: drivers/firmware/riscv/riscv_sse.c
F: include/linux/riscv_sse.h
+RISC-V SSE NMI SUPPORT
+M: Yunhui Cui <cuiyunhui@bytedance.com>
+R: Xu Lu <luxu.kernel@bytedance.com>
+L: linux-riscv@lists.infradead.org
+S: Maintained
+F: drivers/firmware/riscv/riscv_sse_nmi.c
+F: include/linux/riscv_sse_nmi.h
+
RISC-V THEAD SoC SUPPORT
M: Drew Fustini <fustini@kernel.org>
M: Guo Ren <guoren@kernel.org>
diff --git a/drivers/firmware/riscv/Kconfig b/drivers/firmware/riscv/Kconfig
index ed5b663ac5f91..6c77c7823571a 100644
--- a/drivers/firmware/riscv/Kconfig
+++ b/drivers/firmware/riscv/Kconfig
@@ -12,4 +12,14 @@ config RISCV_SBI_SSE
this option provides support to register callbacks on specific SSE
events.
+config RISCV_SSE_NMI
+ bool "Enable SBI Supervisor Software Events NMI support"
+ depends on RISCV_SBI_SSE && SMP
+ default y
+ help
+ This option enables support for delivering Non-Maskable Interrupt
+ (NMI) notifications through the Supervisor Software Events (SSE)
+ framework. When enabled, the system can deliver local, unknown and
+ other types of NMIs.
+
endmenu
diff --git a/drivers/firmware/riscv/Makefile b/drivers/firmware/riscv/Makefile
index c8795d4bbb2ea..fbc182b53ae53 100644
--- a/drivers/firmware/riscv/Makefile
+++ b/drivers/firmware/riscv/Makefile
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_RISCV_SBI_SSE) += riscv_sbi_sse.o
+obj-$(CONFIG_RISCV_SSE_NMI) += riscv_sse_nmi.o
diff --git a/drivers/firmware/riscv/riscv_sse_nmi.c b/drivers/firmware/riscv/riscv_sse_nmi.c
new file mode 100644
index 0000000000000..1763f43961ab6
--- /dev/null
+++ b/drivers/firmware/riscv/riscv_sse_nmi.c
@@ -0,0 +1,89 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#define pr_fmt(fmt) "SSE NMI: " fmt
+
+#include <linux/atomic.h>
+#include <linux/riscv_sbi_sse.h>
+#include <linux/riscv_sse_nmi.h>
+
+#include <asm/irq_regs.h>
+#include <asm/sbi.h>
+#include <asm/smp.h>
+
+bool nmi_available;
+static struct sse_event *local_nmi_evt;
+static atomic_t local_nmi_arg = ATOMIC_INIT(LOCAL_NMI_NONE);
+
+bool nmi_support(void)
+{
+ return READ_ONCE(nmi_available);
+}
+
+static inline struct sbiret sbi_sse_ecall(int fid, unsigned long arg0,
+ unsigned long arg1)
+{
+ return sbi_ecall(SBI_EXT_SSE, fid, arg0, arg1, 0, 0, 0, 0);
+}
+
+void send_nmi_single(unsigned int cpu, enum local_nmi_type type)
+{
+ unsigned int hart_id = cpuid_to_hartid_map(cpu);
+ u32 evt = SBI_SSE_EVENT_LOCAL_SOFTWARE_INJECTED;
+ struct sbiret ret;
+
+ atomic_or(type, &local_nmi_arg);
+ ret = sbi_sse_ecall(SBI_SSE_EVENT_INJECT, evt, hart_id);
+ if (ret.error)
+ pr_err("Failed to signal event %x to hartid %d, error %ld\n",
+ evt, hart_id, ret.error);
+}
+
+void send_nmi_mask(cpumask_t *mask, enum local_nmi_type type)
+{
+ unsigned int cpu;
+
+ for_each_cpu(cpu, mask)
+ send_nmi_single(cpu, type);
+}
+
+static int local_nmi_handler(u32 evt, void *arg, struct pt_regs *regs)
+{
+ return 0;
+}
+
+static int __init local_nmi_init(void)
+{
+ int ret;
+
+ local_nmi_evt = sse_event_register(SBI_SSE_EVENT_LOCAL_SOFTWARE_INJECTED, 0,
+ local_nmi_handler, &local_nmi_arg);
+ if (IS_ERR(local_nmi_evt))
+ return PTR_ERR(local_nmi_evt);
+
+ ret = sse_event_enable(local_nmi_evt);
+ if (ret) {
+ sse_event_unregister(local_nmi_evt);
+ return ret;
+ }
+
+ pr_info("Using SSE for Local NMI event delivery\n");
+
+ return 0;
+}
+
+static int __init sse_nmi_init(void)
+{
+ int ret;
+
+ ret = local_nmi_init();
+ if (ret) {
+ pr_err("Local_nmi_init failed with error %d\n", ret);
+ return ret;
+ }
+
+ WRITE_ONCE(nmi_available, true);
+
+ return 0;
+}
+
+late_initcall(sse_nmi_init);
diff --git a/include/linux/riscv_sse_nmi.h b/include/linux/riscv_sse_nmi.h
new file mode 100644
index 0000000000000..16db85c5162f5
--- /dev/null
+++ b/include/linux/riscv_sse_nmi.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef __LINUX_RISCV_SSE_NMI_H
+#define __LINUX_RISCV_SSE_NMI_H
+
+#include <linux/cpumask.h>
+
+enum local_nmi_type {
+ LOCAL_NMI_NONE = 0U,
+ LOCAL_NMI_STOP = BIT(0),
+ LOCAL_NMI_CRASH = BIT(1),
+ LOCAL_NMI_BACKTRACE = BIT(2),
+ LOCAL_NMI_KGDB = BIT(3),
+};
+
+#ifdef CONFIG_RISCV_SSE_NMI
+bool nmi_support(void);
+void send_nmi_mask(cpumask_t *mask, enum local_nmi_type type);
+void send_nmi_single(unsigned int cpu, enum local_nmi_type type);
+#else
+static inline bool nmi_support(void) { return false; }
+static inline void send_nmi_mask(cpumask_t *mask) { };
+static inline void send_nmi_single(unsigned int cpu) { };
+#endif
+
+#endif
--
2.39.5
Hi All,
On Tue, Nov 18, 2025 at 10:50 AM Yunhui Cui <cuiyunhui@bytedance.com> wrote:
>
> Add support for handling Non-Maskable Interrupts (NMIs) through the
> RISC-V Supervisor Software Events (SSE) framework. Add basic NMI
> functionality via SBI_SSE_EVENT_LOCAL_SOFTWARE_INJECTED registration
> and enabling.
>
> Signed-off-by: Yunhui Cui <cuiyunhui@bytedance.com>
> ---
> MAINTAINERS | 8 +++
> drivers/firmware/riscv/Kconfig | 10 +++
> drivers/firmware/riscv/Makefile | 1 +
> drivers/firmware/riscv/riscv_sse_nmi.c | 89 ++++++++++++++++++++++++++
> include/linux/riscv_sse_nmi.h | 26 ++++++++
> 5 files changed, 134 insertions(+)
> create mode 100644 drivers/firmware/riscv/riscv_sse_nmi.c
> create mode 100644 include/linux/riscv_sse_nmi.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 8bf5416953f45..c06658da8af96 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -22057,6 +22057,14 @@ S: Maintained
> F: drivers/firmware/riscv/riscv_sse.c
> F: include/linux/riscv_sse.h
>
> +RISC-V SSE NMI SUPPORT
> +M: Yunhui Cui <cuiyunhui@bytedance.com>
> +R: Xu Lu <luxu.kernel@bytedance.com>
> +L: linux-riscv@lists.infradead.org
> +S: Maintained
> +F: drivers/firmware/riscv/riscv_sse_nmi.c
> +F: include/linux/riscv_sse_nmi.h
> +
> RISC-V THEAD SoC SUPPORT
> M: Drew Fustini <fustini@kernel.org>
> M: Guo Ren <guoren@kernel.org>
> diff --git a/drivers/firmware/riscv/Kconfig b/drivers/firmware/riscv/Kconfig
> index ed5b663ac5f91..6c77c7823571a 100644
> --- a/drivers/firmware/riscv/Kconfig
> +++ b/drivers/firmware/riscv/Kconfig
> @@ -12,4 +12,14 @@ config RISCV_SBI_SSE
> this option provides support to register callbacks on specific SSE
> events.
>
> +config RISCV_SSE_NMI
> + bool "Enable SBI Supervisor Software Events NMI support"
> + depends on RISCV_SBI_SSE && SMP
> + default y
> + help
> + This option enables support for delivering Non-Maskable Interrupt
> + (NMI) notifications through the Supervisor Software Events (SSE)
> + framework. When enabled, the system can deliver local, unknown and
> + other types of NMIs.
> +
> endmenu
> diff --git a/drivers/firmware/riscv/Makefile b/drivers/firmware/riscv/Makefile
> index c8795d4bbb2ea..fbc182b53ae53 100644
> --- a/drivers/firmware/riscv/Makefile
> +++ b/drivers/firmware/riscv/Makefile
> @@ -1,3 +1,4 @@
> # SPDX-License-Identifier: GPL-2.0
>
> obj-$(CONFIG_RISCV_SBI_SSE) += riscv_sbi_sse.o
> +obj-$(CONFIG_RISCV_SSE_NMI) += riscv_sse_nmi.o
> diff --git a/drivers/firmware/riscv/riscv_sse_nmi.c b/drivers/firmware/riscv/riscv_sse_nmi.c
> new file mode 100644
> index 0000000000000..1763f43961ab6
> --- /dev/null
> +++ b/drivers/firmware/riscv/riscv_sse_nmi.c
> @@ -0,0 +1,89 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +
> +#define pr_fmt(fmt) "SSE NMI: " fmt
> +
> +#include <linux/atomic.h>
> +#include <linux/riscv_sbi_sse.h>
> +#include <linux/riscv_sse_nmi.h>
> +
> +#include <asm/irq_regs.h>
> +#include <asm/sbi.h>
> +#include <asm/smp.h>
> +
> +bool nmi_available;
> +static struct sse_event *local_nmi_evt;
> +static atomic_t local_nmi_arg = ATOMIC_INIT(LOCAL_NMI_NONE);
> +
> +bool nmi_support(void)
> +{
> + return READ_ONCE(nmi_available);
> +}
> +
> +static inline struct sbiret sbi_sse_ecall(int fid, unsigned long arg0,
> + unsigned long arg1)
> +{
> + return sbi_ecall(SBI_EXT_SSE, fid, arg0, arg1, 0, 0, 0, 0);
> +}
> +
> +void send_nmi_single(unsigned int cpu, enum local_nmi_type type)
> +{
> + unsigned int hart_id = cpuid_to_hartid_map(cpu);
> + u32 evt = SBI_SSE_EVENT_LOCAL_SOFTWARE_INJECTED;
> + struct sbiret ret;
> +
> + atomic_or(type, &local_nmi_arg);
> + ret = sbi_sse_ecall(SBI_SSE_EVENT_INJECT, evt, hart_id);
> + if (ret.error)
> + pr_err("Failed to signal event %x to hartid %d, error %ld\n",
> + evt, hart_id, ret.error);
> +}
> +
> +void send_nmi_mask(cpumask_t *mask, enum local_nmi_type type)
> +{
> + unsigned int cpu;
> +
> + for_each_cpu(cpu, mask)
> + send_nmi_single(cpu, type);
> +}
> +
> +static int local_nmi_handler(u32 evt, void *arg, struct pt_regs *regs)
> +{
> + return 0;
> +}
> +
> +static int __init local_nmi_init(void)
> +{
> + int ret;
> +
> + local_nmi_evt = sse_event_register(SBI_SSE_EVENT_LOCAL_SOFTWARE_INJECTED, 0,
> + local_nmi_handler, &local_nmi_arg);
local_nmi_arg is used to represent the local NMI types (Crash, Stop,
Backtrace, Kgdb) for multiple CPUs, which may lead to certain issues:
for example, if CPU A sends a Backtrace to CPU C and CPU B sends a
Stop to CPU D, both CPU C and CPU D may end up responding to both
Backtrace and Stop. It is preferable to use the per-CPU variable
local_nmi_type instead of local_nmi_arg.
> + if (IS_ERR(local_nmi_evt))
> + return PTR_ERR(local_nmi_evt);
> +
> + ret = sse_event_enable(local_nmi_evt);
> + if (ret) {
> + sse_event_unregister(local_nmi_evt);
> + return ret;
> + }
> +
> + pr_info("Using SSE for Local NMI event delivery\n");
> +
> + return 0;
> +}
> +
> +static int __init sse_nmi_init(void)
> +{
> + int ret;
> +
> + ret = local_nmi_init();
> + if (ret) {
> + pr_err("Local_nmi_init failed with error %d\n", ret);
> + return ret;
> + }
> +
> + WRITE_ONCE(nmi_available, true);
> +
> + return 0;
> +}
> +
> +late_initcall(sse_nmi_init);
> diff --git a/include/linux/riscv_sse_nmi.h b/include/linux/riscv_sse_nmi.h
> new file mode 100644
> index 0000000000000..16db85c5162f5
> --- /dev/null
> +++ b/include/linux/riscv_sse_nmi.h
> @@ -0,0 +1,26 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +
> +#ifndef __LINUX_RISCV_SSE_NMI_H
> +#define __LINUX_RISCV_SSE_NMI_H
> +
> +#include <linux/cpumask.h>
> +
> +enum local_nmi_type {
> + LOCAL_NMI_NONE = 0U,
> + LOCAL_NMI_STOP = BIT(0),
> + LOCAL_NMI_CRASH = BIT(1),
> + LOCAL_NMI_BACKTRACE = BIT(2),
> + LOCAL_NMI_KGDB = BIT(3),
> +};
> +
> +#ifdef CONFIG_RISCV_SSE_NMI
> +bool nmi_support(void);
> +void send_nmi_mask(cpumask_t *mask, enum local_nmi_type type);
> +void send_nmi_single(unsigned int cpu, enum local_nmi_type type);
> +#else
> +static inline bool nmi_support(void) { return false; }
> +static inline void send_nmi_mask(cpumask_t *mask) { };
> +static inline void send_nmi_single(unsigned int cpu) { };
> +#endif
> +
> +#endif
> --
> 2.39.5
>
Thanks,
Yunhui
© 2016 - 2025 Red Hat, Inc.