[PATCH dovetail v8 11/11] riscv: dovetail: add core support
Tobias Schaffner <[email protected]>
| 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 | 24 ++++++++++++++ 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 | 48 +++++++++++++++++++++++++--- arch/riscv/mm/context.c | 20 ++++++++++-- arch/riscv/mm/fault.c | 28 +++++++++------- 8 files changed, 120 insertions(+), 18 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..e5a46cf1f67f --- /dev/null +++ b/arch/riscv/include/asm/dovetail.h @@ -0,0 +1,24 @@ +/* +* 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 a3f6dcc2eefa..32fcd33a1ed0 100644 --- a/arch/riscv/kernel/traps.c +++ b/arch/riscv/kernel/traps.c @@ -115,7 +115,10 @@ 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 (running_oob()) { + oob_trap_unwind(regs->cause, regs); return false; } @@ -128,17 +131,16 @@ bool mark_trap_entry(struct pt_regs *regs) static __always_inline void mark_trap_exit(struct pt_regs *regs) { + oob_trap_unwind(regs->cause, regs); + if (user_mode(regs)) hard_cond_local_irq_disable(); } -void do_trap(struct pt_regs *regs, int signo, int code, unsigned long addr) +static void do_trap_raw(struct pt_regs *regs, int signo, int code, unsigned long addr) { struct task_struct *tsk = current; - if (!mark_trap_entry(regs)) - return; - if (show_unhandled_signals && unhandled_signal(tsk, signo) && printk_ratelimit()) { pr_info("%s[%d]: unhandled signal %d code 0x%x at 0x" REG_FMT, @@ -150,6 +152,14 @@ void do_trap(struct pt_regs *regs, int signo, int code, unsigned long addr) } force_sig_fault(signo, code, (void __user *)addr); +} + +void do_trap(struct pt_regs *regs, int signo, int code, unsigned long addr) +{ + if(!mark_trap_entry(regs)) + return; + + do_trap_raw(regs, signo, code, addr); mark_trap_exit(regs); } @@ -160,7 +170,7 @@ static void do_trap_error(struct pt_regs *regs, int signo, int code, current->thread.bad_cause = regs->cause; if (user_mode(regs)) { - do_trap(regs, signo, code, addr); + do_trap_raw(regs, signo, code, addr); } else { /* * Dovetail: If we trapped from kernel space, either @@ -177,6 +187,8 @@ static void do_trap_error(struct pt_regs *regs, int signo, int code, #define DO_ERROR_INFO(name, signo, code, str) \ asmlinkage __visible __trap_section void name(struct pt_regs *regs) \ { \ + if (!mark_trap_entry(regs)) \ + return; \ if (user_mode(regs)) { \ irqentry_enter_from_user_mode(regs); \ local_irq_enable(); \ @@ -191,6 +203,7 @@ asmlinkage __visible __trap_section void name(struct pt_regs *regs) \ if (!stalled) \ unstall_inband_nocheck(); \ } \ + mark_trap_exit(regs); \ } DO_ERROR_INFO(do_trap_unknown, @@ -206,6 +219,9 @@ asmlinkage __visible __trap_section void do_trap_insn_illegal(struct pt_regs *re { bool handled; + if (!mark_trap_entry(regs)) + return; + if (user_mode(regs)) { irqentry_enter_from_user_mode(regs); unstall_inband_nocheck(); @@ -232,6 +248,8 @@ asmlinkage __visible __trap_section void do_trap_insn_illegal(struct pt_regs *re if (!stalled) unstall_inband_nocheck(); } + + mark_trap_exit(regs); } DO_ERROR_INFO(do_trap_load_fault, @@ -260,6 +278,9 @@ static void do_trap_misaligned(struct pt_regs *regs, enum misaligned_access_type irqentry_state_t state; int stalled; + if (!mark_trap_entry(regs)) + return; + if (user_mode(regs)) { irqentry_enter_from_user_mode(regs); local_irq_enable(); @@ -280,6 +301,8 @@ static void do_trap_misaligned(struct pt_regs *regs, enum misaligned_access_type if (!stalled) unstall_inband_nocheck(); } + + mark_trap_exit(regs); } asmlinkage __visible __trap_section void do_trap_load_misaligned(struct pt_regs *regs) @@ -349,6 +372,9 @@ void handle_break(struct pt_regs *regs) asmlinkage __visible __trap_section void do_trap_break(struct pt_regs *regs) { + if (!mark_trap_entry(regs)) + return; + if (user_mode(regs)) { irqentry_enter_from_user_mode(regs); unstall_inband_nocheck(); @@ -370,6 +396,8 @@ asmlinkage __visible __trap_section void do_trap_break(struct pt_regs *regs) if (!stalled) unstall_inband_nocheck(); } + + mark_trap_exit(regs); } asmlinkage __visible __trap_section __no_stack_protector @@ -386,6 +414,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) { @@ -393,6 +430,7 @@ void do_trap_ecall_u(struct pt_regs *regs) syscall_handler(regs, syscall); } +done_inband: syscall_exit_to_user_mode(regs); } else { int stalled = test_and_stall_inband_nocheck(); 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); +} diff --git a/arch/riscv/mm/fault.c b/arch/riscv/mm/fault.c index 885e2f66515e..32cb7285d6db 100644 --- a/arch/riscv/mm/fault.c +++ b/arch/riscv/mm/fault.c @@ -7,6 +7,7 @@ */ +#include <linux/preempt.h> #include <linux/mm.h> #include <linux/kernel.h> #include <linux/interrupt.h> @@ -315,8 +316,12 @@ void handle_page_fault(struct pt_regs *regs) return; } + oob_trap_notify(cause, regs); + if (!running_inband()) + goto out; + /* Enable interrupts if they were enabled in the parent context. */ - if (!regs_irqs_disabled(regs) && running_inband()) + if (!regs_irqs_disabled(regs)) local_irq_enable_full(); /* @@ -326,7 +331,7 @@ void handle_page_fault(struct pt_regs *regs) if (unlikely(faulthandler_disabled() || !mm)) { tsk->thread.bad_cause = cause; no_context(regs, addr); - return; + goto out; } if (user_mode(regs)) @@ -334,7 +339,7 @@ void handle_page_fault(struct pt_regs *regs) if (!user_mode(regs) && addr < TASK_SIZE && unlikely(!(regs->status & SR_SUM))) { if (fixup_exception(regs)) - return; + goto out; die_kernel_fault("access to user memory without uaccess routines", addr, regs); } @@ -357,7 +362,7 @@ void handle_page_fault(struct pt_regs *regs) count_vm_vma_lock_event(VMA_LOCK_SUCCESS); tsk->thread.bad_cause = cause; bad_area_nosemaphore(regs, SEGV_ACCERR, addr); - return; + goto out; } fault = handle_mm_fault(vma, addr, flags | FAULT_FLAG_VMA_LOCK, regs); @@ -375,7 +380,7 @@ void handle_page_fault(struct pt_regs *regs) if (fault_signal_pending(fault, regs)) { if (!user_mode(regs)) no_context(regs, addr); - return; + goto out; } lock_mmap: @@ -384,7 +389,7 @@ void handle_page_fault(struct pt_regs *regs) if (unlikely(!vma)) { tsk->thread.bad_cause = cause; bad_area_nosemaphore(regs, code, addr); - return; + goto out; } /* @@ -396,7 +401,7 @@ void handle_page_fault(struct pt_regs *regs) if (unlikely(access_error(cause, vma))) { tsk->thread.bad_cause = cause; bad_area(regs, mm, code, addr); - return; + goto out; } /* @@ -414,12 +419,12 @@ void handle_page_fault(struct pt_regs *regs) if (fault_signal_pending(fault, regs)) { if (!user_mode(regs)) no_context(regs, addr); - return; + goto out; } /* The fault is fully completed (including releasing mmap lock) */ if (fault & VM_FAULT_COMPLETED) - return; + goto out; if (unlikely(fault & VM_FAULT_RETRY)) { flags |= FAULT_FLAG_TRIED; @@ -438,7 +443,8 @@ void handle_page_fault(struct pt_regs *regs) if (unlikely(fault & VM_FAULT_ERROR)) { tsk->thread.bad_cause = cause; mm_fault_error(regs, addr, fault); - return; } - return; + +out: + oob_trap_unwind(cause, regs); } -- 2.43.0