summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGunnar Beutner <gbeutner@serenityos.org>2021-06-29 10:31:25 +0200
committerAndreas Kling <kling@serenityos.org>2021-06-29 20:03:36 +0200
commit6dde7dac8f7540c8f17e85cc3193832611f1bcc8 (patch)
tree9fe873d4e2477cfcf816cdb9db4cca0478dee8ff
parent36ce2a2b765a54c2548475134060d0a89081c2fa (diff)
downloadserenity-6dde7dac8f7540c8f17e85cc3193832611f1bcc8.zip
Kernel: Implement signal handling for x86_64
-rw-r--r--Kernel/Process.cpp22
-rw-r--r--Kernel/Syscalls/sigaction.cpp23
-rw-r--r--Kernel/Thread.cpp36
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<FlatPtr> 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(&registers.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) {