Przeglądaj źródła

Kernel: Save and restore FPU state on signal dispatch on i386/x86_64

Ali Mohammad Pur 3 lat temu
rodzic
commit
88d7bf7362
3 zmienionych plików z 19 dodań i 7 usunięć
  1. 6 6
      Kernel/Process.cpp
  2. 8 1
      Kernel/Syscalls/sigaction.cpp
  3. 5 0
      Kernel/Thread.cpp

+ 6 - 6
Kernel/Process.cpp

@@ -293,12 +293,12 @@ void signal_trampoline_dummy()
     // 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.
-    constexpr static auto offset_to_first_register_slot = sizeof(__ucontext) + sizeof(siginfo) + 5 * sizeof(FlatPtr);
+    constexpr static auto offset_to_first_register_slot = sizeof(__ucontext) + sizeof(siginfo) + sizeof(FPUState) + 4 * sizeof(FlatPtr);
     asm(
         ".intel_syntax noprefix\n"
         ".globl asm_signal_trampoline\n"
         "asm_signal_trampoline:\n"
-        // stack state: 0, ucontext, signal_info, (alignment = 16), 0, ucontext*, siginfo*, signal, (alignment = 16), handler
+        // stack state: 0, ucontext, signal_info, (alignment = 16), fpu_state (alignment = 16), 0, ucontext*, siginfo*, signal, (alignment = 16), handler
 
         // Pop the handler into ecx
         "pop ecx\n" // save handler
@@ -310,7 +310,7 @@ void signal_trampoline_dummy()
         "call ecx\n"
         // drop the 4 arguments
         "add esp, 16\n"
-        // Current stack state is just saved_eax, ucontext, signal_info.
+        // Current stack state is just saved_eax, ucontext, signal_info, fpu_state?.
         // syscall SC_sigreturn
         "mov eax, %P0\n"
         "int 0x82\n"
@@ -326,12 +326,12 @@ void signal_trampoline_dummy()
     // 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.
-    constexpr static auto offset_to_first_register_slot = sizeof(__ucontext) + sizeof(siginfo) + 4 * sizeof(FlatPtr);
+    constexpr static auto offset_to_first_register_slot = sizeof(__ucontext) + sizeof(siginfo) + sizeof(FPUState) + 3 * sizeof(FlatPtr);
     asm(
         ".intel_syntax noprefix\n"
         ".globl asm_signal_trampoline\n"
         "asm_signal_trampoline:\n"
-        // stack state: 0, ucontext, signal_info (alignment = 16), ucontext*, siginfo*, signal, handler
+        // stack state: 0, ucontext, signal_info (alignment = 16), fpu_state (alignment = 16), ucontext*, siginfo*, signal, handler
 
         // Pop the handler into rcx
         "pop rcx\n" // save handler
@@ -346,7 +346,7 @@ void signal_trampoline_dummy()
         // Note that the stack is currently aligned to 16 bytes as we popped the extra entries above.
         // call the signal handler
         "call rcx\n"
-        // Current stack state is just saved_rax, ucontext, signal_info.
+        // Current stack state is just saved_rax, ucontext, signal_info, fpu_state.
         // syscall SC_sigreturn
         "mov rax, %P0\n"
         "int 0x82\n"

+ 8 - 1
Kernel/Syscalls/sigaction.cpp

@@ -85,7 +85,14 @@ ErrorOr<FlatPtr> Process::sys$sigreturn([[maybe_unused]] RegisterState& register
     auto stack_ptr = registers.userspace_sp();
 
     // Stack state (created by the signal trampoline):
-    // saved_ax, ucontext, signal_info.
+    // saved_ax, ucontext, signal_info, fpu_state?.
+
+#if ARCH(I386) || ARCH(X86_64)
+    // The FPU state is at the top here, pop it off and restore it.
+    Thread::current()->fpu_state() = TRY(copy_typed_from_user<FPUState>(stack_ptr));
+    stack_ptr += sizeof(FPUState);
+#endif
+
     stack_ptr += sizeof(siginfo); // We don't need this here.
 
     auto ucontext = TRY(copy_typed_from_user<__ucontext>(stack_ptr));

+ 5 - 0
Kernel/Thread.cpp

@@ -1190,6 +1190,11 @@ DispatchSignalResult Thread::dispatch_signal(u8 signal)
 
         VERIFY(stack % 16 == 0);
 
+#if ARCH(I386) || ARCH(X86_64)
+        // Save the FPU/SSE state
+        TRY(copy_value_on_user_stack(stack, fpu_state()));
+#endif
+
 #if ARCH(I386)
         // Leave one empty slot to align the stack for a handler call.
         TRY(push_value_on_user_stack(stack, 0));