From 1b4044fde1523bb74256fe88899086e9993dbe46 Mon Sep 17 00:00:00 2001 From: Sam Roberts Date: Tue, 17 Mar 2026 15:37:26 -0400 Subject: [PATCH] arm64: Add signal handling and forking Change-Id: I8fd41bf5c540bcb2b9347aae47ad8bb86a6a6964 Reviewed-on: https://review.haiku-os.org/c/haiku/+/10605 Reviewed-by: waddlesplash Tested-by: Commit checker robot --- headers/private/kernel/arch/arm64/arch_cpu.h | 28 ------ .../kernel/arch/arm64/arch_thread_types.h | 35 +++++++- .../system/arch/arm64/arch_commpage_defs.h | 1 + src/system/kernel/arch/arm64/Jamfile | 2 +- .../kernel/arch/arm64/arch_commpage.cpp | 34 +++++++ src/system/kernel/arch/arm64/arch_int.cpp | 10 ++- src/system/kernel/arch/arm64/arch_thread.cpp | 90 +++++++++++++++++-- 7 files changed, 163 insertions(+), 37 deletions(-) diff --git a/headers/private/kernel/arch/arm64/arch_cpu.h b/headers/private/kernel/arch/arm64/arch_cpu.h index 475f3ecf18..e9dd312498 100644 --- a/headers/private/kernel/arch/arm64/arch_cpu.h +++ b/headers/private/kernel/arch/arm64/arch_cpu.h @@ -119,34 +119,6 @@ ADDRESS_TRANSLATE_FUNC(s1e1r) ADDRESS_TRANSLATE_FUNC(s1e1w) -struct aarch64_fpu_state -{ - uint64 regs[32 * 2]; - uint64 fpsr; - uint64 fpcr; -}; - - -/* raw exception frames */ -struct iframe { - // return info - uint64 elr; - uint64 spsr; - uint64 x[29]; - uint64 fp; - uint64 lr; - uint64 sp; - uint64 tpidr; - - // exception info - uint64 esr; - uint64 far; - - // fpu - struct aarch64_fpu_state fpu; -}; - - #ifdef __cplusplus namespace BKernel { struct Thread; diff --git a/headers/private/kernel/arch/arm64/arch_thread_types.h b/headers/private/kernel/arch/arm64/arch_thread_types.h index 56df2b48e9..2180cf268a 100644 --- a/headers/private/kernel/arch/arm64/arch_thread_types.h +++ b/headers/private/kernel/arch/arm64/arch_thread_types.h @@ -11,6 +11,35 @@ #define IFRAME_TRACE_DEPTH 4 + +struct aarch64_fpu_state +{ + uint64 regs[32 * 2]; + uint64 fpsr; + uint64 fpcr; +}; + + +/* raw exception frames */ +struct iframe { + // return info + uint64 elr; + uint64 spsr; + uint64 x[29]; + uint64 fp; + uint64 lr; + uint64 sp; + uint64 tpidr; + + // exception info + uint64 esr; + uint64 far; + + // fpu + struct aarch64_fpu_state fpu; +}; + + struct iframe_stack { struct iframe *frames[IFRAME_TRACE_DEPTH]; int32 index; @@ -20,17 +49,21 @@ struct iframe_stack { struct arch_thread { uint64 regs[14]; // x19-x30, sp, tpidr_el0 uint64 fp_regs[8]; // d8-d15 + uint64 old_x0; // used to track interrupts on this thread struct iframe_stack iframes; }; + struct arch_team { int dummy; }; + struct arch_fork_arg { - int dummy; + struct iframe frame; }; + #endif /* _KERNEL_ARCH_ARM64_ARCH_THREAD_TYPES_H_ */ diff --git a/headers/private/system/arch/arm64/arch_commpage_defs.h b/headers/private/system/arch/arm64/arch_commpage_defs.h index 2d2fd2502c..5f9c296988 100644 --- a/headers/private/system/arch/arm64/arch_commpage_defs.h +++ b/headers/private/system/arch/arm64/arch_commpage_defs.h @@ -11,5 +11,6 @@ #endif #define COMMPAGE_ENTRY_ARM64_THREAD_EXIT (COMMPAGE_ENTRY_FIRST_ARCH_SPECIFIC + 0) +#define COMMPAGE_ENTRY_ARM64_SIGNAL_HANDLER (COMMPAGE_ENTRY_FIRST_ARCH_SPECIFIC + 1) #endif /* _SYSTEM_ARCH_ARM64_COMMPAGE_DEFS_H */ diff --git a/src/system/kernel/arch/arm64/Jamfile b/src/system/kernel/arch/arm64/Jamfile index bfc3e3522e..dcc016113f 100644 --- a/src/system/kernel/arch/arm64/Jamfile +++ b/src/system/kernel/arch/arm64/Jamfile @@ -49,5 +49,5 @@ KernelMergeObject kernel_arch_arm64.o : CreateAsmStructOffsetsHeader asm_offsets.h : asm_offsets.cpp : $(TARGET_KERNEL_ARCH) ; # syscall headers are generated on the fly, so we need a explicit dependency. -Includes [ FGristFiles arch_int.cpp arch_asm.S ] +Includes [ FGristFiles arch_commpage.cpp arch_int.cpp arch_asm.S ] : syscall_numbers.h ; diff --git a/src/system/kernel/arch/arm64/arch_commpage.cpp b/src/system/kernel/arch/arm64/arch_commpage.cpp index 2e9cf151d0..e338c00ed2 100644 --- a/src/system/kernel/arch/arm64/arch_commpage.cpp +++ b/src/system/kernel/arch/arm64/arch_commpage.cpp @@ -12,10 +12,41 @@ #include #include +#include "syscall_numbers.h" + extern "C" void _thread_exit_syscall(); +extern "C" void __attribute__((noreturn)) +arch_user_signal_handler(signal_frame_data* data) +{ + if (data->siginfo_handler) { + auto handler = (void (*)(int, siginfo_t*, void*, void*))data->handler; + handler(data->info.si_signo, &data->info, &data->context, data->user_data); + } else { + auto handler = (void (*)(int, void*, vregs*))data->handler; + handler(data->info.si_signo, data->user_data, &data->context.uc_mcontext); + } + + #define TO_STRING_LITERAL_HELPER(number) #number + #define TO_STRING_LITERAL(number) TO_STRING_LITERAL_HELPER(number) + + // _kern_restore_signal_frame(data) + asm volatile( + "mov x0, %0;" + "svc #" TO_STRING_LITERAL(((SYSCALL_RESTORE_SIGNAL_FRAME << 5) | 1)) ";" + :: "r" (data) + ); + + #undef TO_STRING_LITERAL_HELPER + #undef TO_STRING_LITERAL + + __builtin_unreachable(); +} + + + static void register_commpage_function(const char* functionName, int32 commpageIndex, const char* commpageSymbolName, addr_t expectedAddress) @@ -53,5 +84,8 @@ arch_commpage_init_post_cpus(void) register_commpage_function("_thread_exit_syscall", COMMPAGE_ENTRY_ARM64_THREAD_EXIT, "commpage_thread_exit", (addr_t)&_thread_exit_syscall); + register_commpage_function("arch_user_signal_handler", COMMPAGE_ENTRY_ARM64_SIGNAL_HANDLER, + "commpage_signal_handler", (addr_t)&arch_user_signal_handler); + return B_OK; } diff --git a/src/system/kernel/arch/arm64/arch_int.cpp b/src/system/kernel/arch/arm64/arch_int.cpp index a0c6426e5f..8859f71313 100644 --- a/src/system/kernel/arch/arm64/arch_int.cpp +++ b/src/system/kernel/arch/arm64/arch_int.cpp @@ -311,7 +311,6 @@ do_sync_handler(iframe * frame) return; } } else if (thread->page_faults_allowed != 0) { - dprintf("PF: %lx\n", frame->far); enable_interrupts(); addr_t ret = 0; vm_page_fault(frame->far, frame->elr, write, isExec, isUser, &ret); @@ -352,6 +351,7 @@ do_sync_handler(iframe * frame) _fp_save(&frame->fpu); thread_at_kernel_entry(system_time()); + thread_get_current_thread()->arch_info.old_x0 = frame->x[0]; enable_interrupts(); syscall_dispatcher(syscall, (void*)args, &frame->x[0]); @@ -369,7 +369,13 @@ do_sync_handler(iframe * frame) thread_at_kernel_exit_no_signals(); } if ((THREAD_FLAGS_RESTART_SYSCALL & thread_get_current_thread()->flags) != 0) { - panic("syscall restart"); + atomic_and(&thread_get_current_thread()->flags, ~THREAD_FLAGS_RESTART_SYSCALL); + atomic_or(&thread_get_current_thread()->flags, THREAD_FLAGS_SYSCALL_RESTARTED); + + // Restore old syscall argument and go + // back an instruction + frame->x[0] = thread_get_current_thread()->arch_info.old_x0; + frame->elr -= 4; } } diff --git a/src/system/kernel/arch/arm64/arch_thread.cpp b/src/system/kernel/arch/arm64/arch_thread.cpp index 66b7b54e99..bc56b6f950 100644 --- a/src/system/kernel/arch/arm64/arch_thread.cpp +++ b/src/system/kernel/arch/arm64/arch_thread.cpp @@ -137,7 +137,32 @@ arch_thread_enter_userspace(Thread *thread, addr_t entry, bool arch_on_signal_stack(Thread *thread) { - return false; + iframe_stack* iframes = &thread->arch_info.iframes; + iframe* frame = iframes->frames[iframes->index - 1]; + + return frame->sp >= thread->signal_stack_base + && frame->sp < thread->signal_stack_base + + thread->signal_stack_size; +} + + +static uint8* +get_signal_stack(Thread* thread, struct iframe* frame, + struct sigaction* action, size_t spaceNeeded) +{ + // use the alternate signal stack if we should and can + if ( + thread->signal_stack_enabled && + (action->sa_flags & SA_ONSTACK) != 0 && ( + frame->sp < thread->signal_stack_base || + frame->sp >= thread->signal_stack_base + thread->signal_stack_size + ) + ) { + addr_t stackTop = thread->signal_stack_base + + thread->signal_stack_size; + return (uint8*)ROUNDDOWN(stackTop - spaceNeeded, 16); + } + return (uint8*)ROUNDDOWN(frame->sp - spaceNeeded, 16); } @@ -145,26 +170,81 @@ status_t arch_setup_signal_frame(Thread *thread, struct sigaction *sa, struct signal_frame_data *signalFrameData) { - panic("arch_setup_signal_frame"); - return B_ERROR; + iframe_stack* iframes = &thread->arch_info.iframes; + iframe *frame = iframes->frames[iframes->index - 1]; + + memcpy(signalFrameData->context.uc_mcontext.x, frame->x, sizeof(frame->x)); + signalFrameData->context.uc_mcontext.x[29] = frame->fp; + signalFrameData->context.uc_mcontext.lr = frame->lr; + signalFrameData->context.uc_mcontext.elr = frame->elr; + signalFrameData->context.uc_mcontext.spsr = frame->spsr; + signalFrameData->context.uc_mcontext.sp = frame->sp; + memcpy(signalFrameData->context.uc_mcontext.fp_q, frame->fpu.regs, + sizeof(signalFrameData->context.uc_mcontext.fp_q)); + signalFrameData->context.uc_mcontext.fpsr = frame->fpu.fpsr; + signalFrameData->context.uc_mcontext.fpcr = frame->fpu.fpcr; + signalFrameData->syscall_restart_return_value = thread->arch_info.old_x0; + + signal_get_user_stack(frame->sp, &signalFrameData->context.uc_stack); + + uint8* userStack = get_signal_stack(thread, frame, sa, + sizeof(*signalFrameData)); + status_t res = user_memcpy(userStack, signalFrameData, + sizeof(*signalFrameData)); + if (res < B_OK) + return res; + + addr_t commpageAddr = (addr_t)thread->team->commpage_address; + addr_t signalHandlerAddr; + ASSERT(user_memcpy(&signalHandlerAddr, + &((addr_t*)commpageAddr)[COMMPAGE_ENTRY_ARM64_SIGNAL_HANDLER], + sizeof(signalHandlerAddr)) >= B_OK); + signalHandlerAddr += commpageAddr; + + frame->lr = frame->elr; + frame->sp = (addr_t)userStack; + frame->elr = signalHandlerAddr; + frame->x[0] = frame->sp; + + return B_OK; } int64 arch_restore_signal_frame(struct signal_frame_data* signalFrameData) { - return 0; + iframe_stack* iframes = &thread_get_current_thread()->arch_info.iframes; + iframe *frame = iframes->frames[iframes->index - 1]; + + thread_get_current_thread()->arch_info.old_x0 + = signalFrameData->syscall_restart_return_value; + + memcpy(frame->x, signalFrameData->context.uc_mcontext.x, sizeof(frame->x)); + frame->fp = signalFrameData->context.uc_mcontext.x[29]; + frame->lr = signalFrameData->context.uc_mcontext.lr; + frame->elr = signalFrameData->context.uc_mcontext.elr; + frame->spsr = signalFrameData->context.uc_mcontext.spsr; + frame->sp = signalFrameData->context.uc_mcontext.sp; + memcpy(frame->fpu.regs, signalFrameData->context.uc_mcontext.fp_q, + sizeof(signalFrameData->context.uc_mcontext.fp_q)); + frame->fpu.fpsr = signalFrameData->context.uc_mcontext.fpsr; + frame->fpu.fpcr = signalFrameData->context.uc_mcontext.fpcr; + + return frame->x[0]; } void arch_store_fork_frame(struct arch_fork_arg *arg) { - panic("arch_store_fork_frame"); + iframe_stack* iframes = &thread_get_current_thread()->arch_info.iframes; + memcpy(&arg->frame, iframes->frames[iframes->index - 1], sizeof(iframe)); + arg->frame.x[0] = 0; } void arch_restore_fork_frame(struct arch_fork_arg *arg) { + _eret_with_iframe(&arg->frame); }