[PATCH dovetail v12 11/11] riscv: dovetail: add core support
Tobias Schaffner <[email protected]> Tue, 28 Jul 2026 13:12:22 +0200
| Newsgroups | dev.linux.lists.xenomai |
|---|---|
| Message-ID | <[email protected]> |
Add Dovetail co-kernel support for RISC-V, including, irq pipeline integration, out-of-band aware trap handling and memory management. Signed-off-by: Tobias Schaffner <[email protected]> --- arch/riscv/Kconfig | 2 + arch/riscv/include/asm/dovetail.h | 23 ++++++ arch/riscv/include/asm/mmu_context.h | 2 + arch/riscv/include/asm/syscall.h | 6 ++ arch/riscv/include/asm/thread_info.h | 8 ++ arch/riscv/kernel/traps.c | 114 +++++++++++++++++++++------ arch/riscv/mm/context.c | 20 ++++- 7 files changed, 147 insertions(+), 28 deletions(-) create mode 100644 arch/riscv/include/asm/dovetail.h diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 7c58654c4ece..912aada80962 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -151,6 +151,7 @@ config RISCV select HAVE_ARCH_USERFAULTFD_WP if 64BIT && MMU && USERFAULTFD && RISCV_ISA_SVRSW60T59B select HAVE_ARCH_VMAP_STACK if MMU && 64BIT select HAVE_IRQ_PIPELINE + select HAVE_DOVETAIL select HAVE_ASM_MODVERSIONS select HAVE_CONTEXT_TRACKING_USER select HAVE_DEBUG_KMEMLEAK @@ -376,6 +377,7 @@ config AS_HAS_OPTION_ARCH source "arch/riscv/Kconfig.socs" source "arch/riscv/Kconfig.errata" +source "kernel/Kconfig.dovetail" menu "Platform type" diff --git a/arch/riscv/include/asm/dovetail.h b/arch/riscv/include/asm/dovetail.h new file mode 100644 index 000000000000..bbdfee7c95a7 --- /dev/null +++ b/arch/riscv/include/asm/dovetail.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2024-2026 Siemens AG + * Author: Tobias Schaffner <[email protected]>. + */ +#ifndef _ASM_RISCV_DOVETAIL_H +#define _ASM_RISCV_DOVETAIL_H + +#if !defined(__ASSEMBLY__) +#ifdef CONFIG_DOVETAIL + +static inline void arch_dovetail_exec_prepare(void) +{ } + +static inline void arch_dovetail_switch_prepare(bool leave_inband) +{ } + +static inline void arch_dovetail_switch_finish(bool enter_inband) +{ } + +#endif /* CONFIG_DOVETAIL */ +#endif /* !__ASSEMBLY__ */ +#endif /* _ASM_RISCV_DOVETAIL_H */ diff --git a/arch/riscv/include/asm/mmu_context.h b/arch/riscv/include/asm/mmu_context.h index dbf27a78df6c..a1ac9192d51c 100644 --- a/arch/riscv/include/asm/mmu_context.h +++ b/arch/riscv/include/asm/mmu_context.h @@ -15,6 +15,8 @@ void switch_mm(struct mm_struct *prev, struct mm_struct *next, struct task_struct *task); +void switch_oob_mm(struct mm_struct *prev, struct mm_struct *next, + struct task_struct *task); #define activate_mm activate_mm static inline void activate_mm(struct mm_struct *prev, diff --git a/arch/riscv/include/asm/syscall.h b/arch/riscv/include/asm/syscall.h index 8067e666a4ca..12376b54268e 100644 --- a/arch/riscv/include/asm/syscall.h +++ b/arch/riscv/include/asm/syscall.h @@ -117,6 +117,12 @@ static inline bool arch_syscall_is_vdso_sigreturn(struct pt_regs *regs) return false; } +static inline unsigned long syscall_get_arg0(struct task_struct *task, + struct pt_regs *regs) +{ + return regs->orig_a0; +} + asmlinkage long sys_riscv_flush_icache(uintptr_t, uintptr_t, uintptr_t); asmlinkage long sys_riscv_hwprobe(struct riscv_hwprobe *, size_t, size_t, diff --git a/arch/riscv/include/asm/thread_info.h b/arch/riscv/include/asm/thread_info.h index 8bfb02064f48..4934b7c8f1f1 100644 --- a/arch/riscv/include/asm/thread_info.h +++ b/arch/riscv/include/asm/thread_info.h @@ -41,6 +41,7 @@ #include <asm/processor.h> #include <asm/csr.h> +#include <dovetail/thread_info.h> /* * low level task data that entry.S needs immediate access to @@ -80,6 +81,7 @@ struct thread_info { #ifdef CONFIG_RISCV_USER_CFI struct cfi_state user_cfi_state; #endif + struct oob_thread_state oob_state; /* co-kernel thread state */ }; #ifdef CONFIG_SHADOW_CALL_STACK @@ -123,6 +125,9 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src); #include <asm-generic/thread_info_tif.h> +#define TIF_MAYDAY 14 /* emergency trap pending */ +#define _TIF_MAYDAY (1 << TIF_MAYDAY) + #define TIF_32BIT 16 /* compat-mode 32bit process */ #define TIF_RISCV_V_DEFER_RESTORE 17 /* restore Vector before returning to user */ @@ -132,5 +137,8 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src); * Local (synchronous) thread flags. */ #define _TLF_OOB 0x0001 +#define _TLF_DOVETAIL 0x0002 +#define _TLF_OFFSTAGE 0x0004 +#define _TLF_OOBTRAP 0x0008 #endif /* _ASM_RISCV_THREAD_INFO_H */ diff --git a/arch/riscv/kernel/traps.c b/arch/riscv/kernel/traps.c index be87cd58e754..55be4272f0dc 100644 --- a/arch/riscv/kernel/traps.c +++ b/arch/riscv/kernel/traps.c @@ -22,6 +22,8 @@ #include <linux/irq.h> #include <linux/kexec.h> #include <linux/entry-common.h> +#include <linux/audit.h> +#include <linux/dovetail.h> #include <asm/asm-prototypes.h> #include <asm/bug.h> @@ -115,18 +117,24 @@ void die(struct pt_regs *regs, const char *str) static __always_inline bool mark_trap_entry(struct pt_regs *regs) { + oob_trap_notify(regs->cause, regs); + if (likely(running_inband())) { if (user_mode(regs)) hard_cond_local_irq_enable(); return true; } + oob_trap_unwind(regs->cause, regs); + return false; } static __always_inline void mark_trap_exit(struct pt_regs *regs) { + oob_trap_unwind(regs->cause, regs); + if (likely(running_inband()) && user_mode(regs)) hard_cond_local_irq_disable(); } @@ -172,13 +180,19 @@ asmlinkage __visible __trap_section void name(struct pt_regs *regs) \ { \ if (user_mode(regs)) { \ irqentry_enter_from_user_mode(regs); \ - local_irq_enable(); \ - do_trap_error(regs, signo, code, regs->epc, "Oops - " str); \ - local_irq_disable(); \ + if (mark_trap_entry(regs)) { \ + local_irq_enable(); \ + do_trap_error(regs, signo, code, regs->epc, "Oops - " str); \ + local_irq_disable(); \ + mark_trap_exit(regs); \ + } \ irqentry_exit_to_user_mode(regs); \ } else { \ irqentry_state_t state = irqentry_nmi_enter(regs); \ - do_trap_error(regs, signo, code, regs->epc, "Oops - " str); \ + if (mark_trap_entry(regs)) { \ + do_trap_error(regs, signo, code, regs->epc, "Oops - " str); \ + mark_trap_exit(regs); \ + } \ irqentry_nmi_exit(regs, state); \ } \ } @@ -198,21 +212,26 @@ asmlinkage __visible __trap_section void do_trap_insn_illegal(struct pt_regs *re if (user_mode(regs)) { irqentry_enter_from_user_mode(regs); - local_irq_enable(); + if (mark_trap_entry(regs)) { + local_irq_enable(); - handled = riscv_v_first_use_handler(regs); - if (!handled) - do_trap_error(regs, SIGILL, ILL_ILLOPC, regs->epc, - "Oops - illegal instruction"); + handled = riscv_v_first_use_handler(regs); - local_irq_disable(); + if (!handled) + do_trap_error(regs, SIGILL, ILL_ILLOPC, regs->epc, + "Oops - illegal instruction"); + + local_irq_disable(); + mark_trap_exit(regs); + } irqentry_exit_to_user_mode(regs); } else { irqentry_state_t state = irqentry_nmi_enter(regs); - - do_trap_error(regs, SIGILL, ILL_ILLOPC, regs->epc, - "Oops - illegal instruction"); - + if (mark_trap_entry(regs)) { + do_trap_error(regs, SIGILL, ILL_ILLOPC, regs->epc, + "Oops - illegal instruction"); + mark_trap_exit(regs); + } irqentry_nmi_exit(regs, state); } } @@ -244,17 +263,26 @@ static void do_trap_misaligned(struct pt_regs *regs, enum misaligned_access_type if (user_mode(regs)) { irqentry_enter_from_user_mode(regs); + if (!mark_trap_entry(regs)) + goto out; local_irq_enable(); } else { state = irqentry_nmi_enter(regs); + if (!mark_trap_entry(regs)) + goto out; } if (misaligned_handler[type].handler(regs)) do_trap_error(regs, SIGBUS, BUS_ADRALN, regs->epc, misaligned_handler[type].type_str); - if (user_mode(regs)) { + if (user_mode(regs)) local_irq_disable(); + + mark_trap_exit(regs); + +out: + if (user_mode(regs)) { irqentry_exit_to_user_mode(regs); } else { irqentry_nmi_exit(regs, state); @@ -330,17 +358,21 @@ asmlinkage __visible __trap_section void do_trap_break(struct pt_regs *regs) { if (user_mode(regs)) { irqentry_enter_from_user_mode(regs); - local_irq_enable(); + if (mark_trap_entry(regs)) { + local_irq_enable(); - handle_break(regs); + handle_break(regs); - local_irq_disable(); + local_irq_disable(); + mark_trap_exit(regs); + } irqentry_exit_to_user_mode(regs); } else { irqentry_state_t state = irqentry_nmi_enter(regs); - - handle_break(regs); - + if (mark_trap_entry(regs)) { + handle_break(regs); + mark_trap_exit(regs); + } irqentry_nmi_exit(regs, state); } } @@ -359,6 +391,15 @@ void do_trap_ecall_u(struct pt_regs *regs) syscall = syscall_enter_from_user_mode(regs, syscall); + if (dovetailing()) { + if (syscall == EXIT_SYSCALL_OOB) { + hard_local_irq_disable(); + return; + } + if (syscall == EXIT_SYSCALL_TAIL) + goto done_inband; + } + add_random_kstack_offset(); if (syscall >= 0 && syscall < NR_syscalls) { @@ -366,6 +407,13 @@ void do_trap_ecall_u(struct pt_regs *regs) syscall_handler(regs, syscall); } +done_inband: + /* + * Dovetail: balance audit entry that generic exit + * skips for in_oob_syscall(). + */ + if (dovetailing() && in_oob_syscall(regs)) + audit_syscall_exit(regs); syscall_exit_to_user_mode(regs); } else { irqentry_state_t state = irqentry_nmi_enter(regs); @@ -420,11 +468,13 @@ asmlinkage __visible __trap_section void do_trap_software_check(struct pt_regs * { if (user_mode(regs)) { irqentry_enter_from_user_mode(regs); + if (mark_trap_entry(regs)) { + /* not a cfi violation, then merge into flow of unknown trap handler */ + if (!handle_user_cfi_violation(regs)) + do_trap_unknown(regs); - /* not a cfi violation, then merge into flow of unknown trap handler */ - if (!handle_user_cfi_violation(regs)) - do_trap_unknown(regs); - + mark_trap_exit(regs); + } irqentry_exit_to_user_mode(regs); } else { /* sw check exception coming from kernel is a bug in kernel */ @@ -437,10 +487,22 @@ asmlinkage __visible noinstr void do_page_fault(struct pt_regs *regs) { irqentry_state_t state = irqentry_enter(regs); - handle_page_fault(regs); + mark_trap_entry(regs); + + BUG_ON(dovetail_debug() && !running_inband()); + + if (!user_mode(regs)) + hard_cond_local_irq_enable(); + + handle_page_fault(regs, state); local_irq_disable(); + mark_trap_exit(regs); + + if (!user_mode(regs)) + hard_cond_local_irq_disable(); + irqentry_exit(regs, state); } #endif diff --git a/arch/riscv/mm/context.c b/arch/riscv/mm/context.c index 55c20ad1f744..8f38b4a5eb26 100644 --- a/arch/riscv/mm/context.c +++ b/arch/riscv/mm/context.c @@ -25,7 +25,7 @@ static unsigned long num_asids; static atomic_long_t current_version; -static DEFINE_RAW_SPINLOCK(context_lock); +static DEFINE_HARD_SPINLOCK(context_lock); static cpumask_t context_tlb_flush_pending; static unsigned long *context_asid_map; @@ -315,7 +315,7 @@ static inline void flush_icache_deferred(struct mm_struct *mm, unsigned int cpu, #endif } -void switch_mm(struct mm_struct *prev, struct mm_struct *next, +static void do_switch_mm(struct mm_struct *prev, struct mm_struct *next, struct task_struct *task) { unsigned int cpu; @@ -336,3 +336,19 @@ void switch_mm(struct mm_struct *prev, struct mm_struct *next, flush_icache_deferred(next, cpu, task); } + +void switch_mm(struct mm_struct *prev, struct mm_struct *next, + struct task_struct *task) +{ + unsigned long flags; + + protect_inband_mm(flags); + do_switch_mm(prev, next, task); + unprotect_inband_mm(flags); +} + +void switch_oob_mm(struct mm_struct *prev, struct mm_struct *next, + struct task_struct *task) +{ + do_switch_mm(prev, next, task); +} -- 2.43.0