SSE delivery is independent of normal S-mode interrupts. Firmware may also retain an event registration until Linux explicitly unregisters it. A hart must therefore stop accepting SSE events before Linux stops servicing the registered handler.
Mask SSE on the local hart before panic stop, CPU stop, restart, poweroff, and crash shutdown paths. This prevents firmware from entering Linux-owned handler state after the corresponding CPU or kernel context is no longer valid. A crash kernel cannot identify or take ownership of registrations inherited from the crashed kernel. Reject a later normal kexec while such SSE state may still exist, rather than transferring unknown firmware state to another kernel. Signed-off-by: Zhanpeng Zhang <[email protected]> --- arch/riscv/kernel/machine_kexec.c | 11 +++++++++++ arch/riscv/kernel/reset.c | 18 ++++++++++++++++++ arch/riscv/kernel/smp.c | 17 +++++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/arch/riscv/kernel/machine_kexec.c b/arch/riscv/kernel/machine_kexec.c index 738df176ff6f..24e7affae70b 100644 --- a/arch/riscv/kernel/machine_kexec.c +++ b/arch/riscv/kernel/machine_kexec.c @@ -14,10 +14,13 @@ #include <asm/set_memory.h> /* For set_memory_x() */ #include <linux/compiler.h> /* For unreachable() */ #include <linux/cpu.h> /* For cpu_down() */ +#include <linux/crash_dump.h> #include <linux/reboot.h> #include <linux/interrupt.h> #include <linux/irq.h> +#include <asm/sse.h> + /* * machine_kexec_prepare - Initialize kexec * @@ -36,6 +39,13 @@ machine_kexec_prepare(struct kimage *image) unsigned int control_code_buffer_sz = 0; int i = 0; + /* A crash kernel cannot tear down registrations inherited from firmware. */ + if (is_kdump_kernel() && image->type != KEXEC_TYPE_CRASH && + riscv_sse_available()) { + pr_err("Normal kexec from a crash kernel is unsupported with SSE\n"); + return -EOPNOTSUPP; + } + /* Find the Flattened Device Tree and save its physical address */ for (i = 0; i < image->nr_segments; i++) { if (image->segment[i].memsz <= sizeof(fdt)) @@ -127,6 +137,7 @@ void machine_crash_shutdown(struct pt_regs *regs) { local_irq_disable(); + riscv_sse_mask_current_hart(); /* shutdown non-crashing cpus */ crash_smp_send_stop(); diff --git a/arch/riscv/kernel/reset.c b/arch/riscv/kernel/reset.c index 14eb08a6db85..fdab37e7ae52 100644 --- a/arch/riscv/kernel/reset.c +++ b/arch/riscv/kernel/reset.c @@ -6,6 +6,20 @@ #include <linux/efi.h> #include <linux/reboot.h> #include <linux/pm.h> +#include <linux/smp.h> + +#include <asm/sse.h> + +#ifndef CONFIG_SMP +void __noreturn panic_smp_self_stop(void) +{ + riscv_sse_mask_current_hart(); + local_irq_disable(); + + for (;;) + cpu_relax(); +} +#endif static void __noreturn default_power_off(void) { @@ -18,6 +32,8 @@ EXPORT_SYMBOL(pm_power_off); void machine_restart(char *cmd) { + riscv_sse_mask_current_hart(); + /* * UpdateCapsule() depends on the system being reset via ResetSystem(). */ @@ -30,12 +46,14 @@ void machine_restart(char *cmd) void machine_halt(void) { + riscv_sse_mask_current_hart(); do_kernel_power_off(); default_power_off(); } void machine_power_off(void) { + riscv_sse_mask_current_hart(); do_kernel_power_off(); default_power_off(); } diff --git a/arch/riscv/kernel/smp.c b/arch/riscv/kernel/smp.c index fa66f9c97d74..7f0d4e7332f1 100644 --- a/arch/riscv/kernel/smp.c +++ b/arch/riscv/kernel/smp.c @@ -23,10 +23,13 @@ #include <linux/irq.h> #include <linux/irq_work.h> #include <linux/nmi.h> +#include <linux/panic.h> #include <asm/tlbflush.h> #include <asm/cacheflush.h> #include <asm/cpu_ops.h> +#include <asm/sbi.h> +#include <asm/sse.h> enum ipi_message_type { IPI_RESCHEDULE, @@ -79,8 +82,18 @@ int riscv_hartid_to_cpuid(unsigned long hartid) return -ENOENT; } +void __noreturn panic_smp_self_stop(void) +{ + riscv_sse_mask_current_hart(); + local_irq_disable(); + + for (;;) + cpu_relax(); +} + static void ipi_stop(void) { + riscv_sse_mask_current_hart(); set_cpu_online(smp_processor_id(), false); while (1) wait_for_interrupt(); @@ -91,6 +104,7 @@ static atomic_t waiting_for_crash_ipi = ATOMIC_INIT(0); static inline void ipi_cpu_crash_stop(unsigned int cpu, struct pt_regs *regs) { + riscv_sse_mask_current_hart(); crash_save_cpu(regs, cpu); atomic_dec(&waiting_for_crash_ipi); @@ -254,6 +268,8 @@ void smp_send_stop(void) { unsigned long timeout; + riscv_sse_mask_current_hart(); + if (num_online_cpus() > 1) { cpumask_t mask; @@ -301,6 +317,7 @@ void crash_smp_send_stop(void) return; cpus_stopped = 1; + riscv_sse_mask_current_hart(); /* * If this cpu is the only one alive at this point in time, online or -- 2.50.1 (Apple Git-155)

