From 6dde7dac8f7540c8f17e85cc3193832611f1bcc8 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Tue, 29 Jun 2021 10:31:25 +0200 Subject: Kernel: Implement signal handling for x86_64 --- Kernel/Process.cpp | 22 +++++++++++++++++++--- Kernel/Syscalls/sigaction.cpp | 23 ++++++++++++++++++++++- Kernel/Thread.cpp | 36 +++++++++++++++++++++++++++++++----- 3 files changed, 72 insertions(+), 9 deletions(-) diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index 7aaa1e911d..a96383d4c6 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -308,9 +308,25 @@ void signal_trampoline_dummy() "asm_signal_trampoline_end:\n" ".att_syntax" ::"i"(Syscall::SC_sigreturn)); #elif ARCH(X86_64) - asm("asm_signal_trampoline:\n" - "cli;hlt\n" - "asm_signal_trampoline_end:\n"); + // The trampoline preserves the current rax, pushes the signal code and + // then calls the signal handler. We do this because, when interrupting a + // blocking syscall, that syscall may return some special error code in eax; + // This error code would likely be overwritten by the signal handler, so it's + // necessary to preserve it here. + asm( + ".intel_syntax noprefix\n" + "asm_signal_trampoline:\n" + "push rbp\n" + "mov rbp, rsp\n" + "push rax\n" // we have to store rax 'cause it might be the return value from a syscall + "sub rsp, 8\n" // align the stack to 16 bytes + "mov rdi, [rbp+24]\n" // push the signal code + "call [rbp+16]\n" // call the signal handler + "add rsp, 8\n" + "mov rax, %P0\n" + "int 0x82\n" // sigreturn syscall + "asm_signal_trampoline_end:\n" + ".att_syntax" ::"i"(Syscall::SC_sigreturn)); #endif } diff --git a/Kernel/Syscalls/sigaction.cpp b/Kernel/Syscalls/sigaction.cpp index 1b07db25a9..1c0a413b3a 100644 --- a/Kernel/Syscalls/sigaction.cpp +++ b/Kernel/Syscalls/sigaction.cpp @@ -104,7 +104,28 @@ KResultOr Process::sys$sigreturn([[maybe_unused]] RegisterState& regist registers.userspace_esp = registers.esp; return smuggled_eax; #else - PANIC("sys$sigreturn() not implemented."); + //Here, we restore the state pushed by dispatch signal and asm_signal_trampoline. + FlatPtr* stack_ptr = (FlatPtr*)registers.userspace_rsp; + FlatPtr smuggled_rax = *stack_ptr; + + //pop the stored rax, rbp, return address, handler and signal code + stack_ptr += 5; + + Thread::current()->m_signal_mask = *stack_ptr; + stack_ptr++; + + //pop rdi, rsi, rbp, rsp, rbx, rdx, rcx, rax, r8, r9, r10, r11, r12, r13, r14 and r15 + memcpy(®isters.rdi, stack_ptr, 16 * sizeof(FlatPtr)); + stack_ptr += 16; + + registers.rip = *stack_ptr; + stack_ptr++; + + registers.rflags = (registers.rflags & ~safe_eflags_mask) | (*stack_ptr & safe_eflags_mask); + stack_ptr++; + + registers.userspace_rsp = registers.rsp; + return smuggled_rax; #endif } diff --git a/Kernel/Thread.cpp b/Kernel/Thread.cpp index 7e5cb0fe50..e18dd2c782 100644 --- a/Kernel/Thread.cpp +++ b/Kernel/Thread.cpp @@ -822,7 +822,11 @@ DispatchSignalResult Thread::dispatch_signal(u8 signal) dbgln_if(SIGNAL_DEBUG, "Setting up user stack to return to EIP {:p}, ESP {:p}", ret_eip, old_esp); #elif ARCH(X86_64) FlatPtr* stack = &state.userspace_rsp; - TODO(); + FlatPtr old_rsp = *stack; + FlatPtr ret_rip = state.rip; + FlatPtr ret_rflags = state.rflags; + + dbgln_if(SIGNAL_DEBUG, "Setting up user stack to return to RIP {:p}, RSP {:p}", ret_rip, old_rsp); #endif #if ARCH(I386) @@ -843,10 +847,32 @@ DispatchSignalResult Thread::dispatch_signal(u8 signal) push_value_on_user_stack(stack, state.ebp); push_value_on_user_stack(stack, state.esi); push_value_on_user_stack(stack, state.edi); - #else - // FIXME - PANIC("Thread:dispatch_signal() not implemented"); + // Align the stack to 16 bytes. + // Note that we push 176 bytes (8 * 22) on to the stack, + // so we need to account for this here. + FlatPtr stack_alignment = (*stack - 112) % 16; + *stack -= stack_alignment; + + push_value_on_user_stack(stack, ret_rflags); + + push_value_on_user_stack(stack, ret_rip); + push_value_on_user_stack(stack, state.r15); + push_value_on_user_stack(stack, state.r14); + push_value_on_user_stack(stack, state.r13); + push_value_on_user_stack(stack, state.r12); + push_value_on_user_stack(stack, state.r11); + push_value_on_user_stack(stack, state.r10); + push_value_on_user_stack(stack, state.r9); + push_value_on_user_stack(stack, state.r8); + push_value_on_user_stack(stack, state.rax); + push_value_on_user_stack(stack, state.rcx); + push_value_on_user_stack(stack, state.rdx); + push_value_on_user_stack(stack, state.rbx); + push_value_on_user_stack(stack, old_rsp); + push_value_on_user_stack(stack, state.rbp); + push_value_on_user_stack(stack, state.rsi); + push_value_on_user_stack(stack, state.rdi); #endif // PUSH old_signal_mask @@ -887,7 +913,7 @@ RegisterState& Thread::get_register_dump_from_stack() // We should *always* have a trap. If we don't we're probably a kernel // thread that hasn't been pre-empted. If we want to support this, we - // need to capture the registers probably into m_tss and return it + // need to capture the registers probably into m_regs and return it VERIFY(trap); while (trap) { -- cgit v1.2.3