diff --git a/headers/posix/arch/x86_64/signal.h b/headers/posix/arch/x86_64/signal.h index f48a72f5fa..871d1f59b6 100644 --- a/headers/posix/arch/x86_64/signal.h +++ b/headers/posix/arch/x86_64/signal.h @@ -12,35 +12,108 @@ #if __x86_64__ -struct vregs { - unsigned long rax; /* gp regs */ - unsigned long rdx; - unsigned long rcx; - unsigned long rbx; - unsigned long rsi; - unsigned long rdi; - unsigned long rbp; - unsigned long rsp; - - unsigned long r8; /* egp regs */ - unsigned long r9; - unsigned long r10; - unsigned long r11; - unsigned long r12; - unsigned long r13; - unsigned long r14; - unsigned long r15; - - unsigned long rip; - -/*TODO: add -* Floatpoint -* MMX -* SSE -*/ +struct fp_stack { + unsigned char st0[10]; + unsigned char _reserved_42_47[6]; + unsigned char st1[10]; + unsigned char _reserved_58_63[6]; + unsigned char st2[10]; + unsigned char _reserved_74_79[6]; + unsigned char st3[10]; + unsigned char _reserved_90_95[6]; + unsigned char st4[10]; + unsigned char _reserved_106_111[6]; + unsigned char st5[10]; + unsigned char _reserved_122_127[6]; + unsigned char st6[10]; + unsigned char _reserved_138_143[6]; + unsigned char st7[10]; + unsigned char _reserved_154_159[6]; }; +struct mmx_regs { + unsigned char mm0[10]; + unsigned char _reserved_42_47[6]; + unsigned char mm1[10]; + unsigned char _reserved_58_63[6]; + unsigned char mm2[10]; + unsigned char _reserved_74_79[6]; + unsigned char mm3[10]; + unsigned char _reserved_90_95[6]; + unsigned char mm4[10]; + unsigned char _reserved_106_111[6]; + unsigned char mm5[10]; + unsigned char _reserved_122_127[6]; + unsigned char mm6[10]; + unsigned char _reserved_138_143[6]; + unsigned char mm7[10]; + unsigned char _reserved_154_159[6]; +}; + +struct xmm_regs { + unsigned char xmm0[16]; + unsigned char xmm1[16]; + unsigned char xmm2[16]; + unsigned char xmm3[16]; + unsigned char xmm4[16]; + unsigned char xmm5[16]; + unsigned char xmm6[16]; + unsigned char xmm7[16]; + unsigned char xmm8[16]; + unsigned char xmm9[16]; + unsigned char xmm10[16]; + unsigned char xmm11[16]; + unsigned char xmm12[16]; + unsigned char xmm13[16]; + unsigned char xmm14[16]; + unsigned char xmm15[16]; +}; + +struct fpu_state { + unsigned short control; + unsigned short status; + unsigned short tag; + unsigned short opcode; + unsigned long rip; + unsigned long rdp; + unsigned int mxcsr; + unsigned int mscsr_mask; + + union { + struct fp_stack fp; + struct mmx_regs mmx; + }; + + struct xmm_regs xmm; + unsigned char _reserved_416_511[96]; +}; + +struct vregs { + unsigned long rax; + unsigned long rbx; + unsigned long rcx; + unsigned long rdx; + unsigned long rdi; + unsigned long rsi; + unsigned long rbp; + unsigned long r8; + unsigned long r9; + unsigned long r10; + unsigned long r11; + unsigned long r12; + unsigned long r13; + unsigned long r14; + unsigned long r15; + + unsigned long rsp; + unsigned long rip; + unsigned long rflags; + + struct fpu_state fpu; +}; + + #endif /* __x86_64__ */ #endif /* _ARCH_SIGNAL_H_ */ diff --git a/headers/private/kernel/arch/x86/arch_thread.h b/headers/private/kernel/arch/x86/arch_thread.h index 850cfc9973..0769195543 100644 --- a/headers/private/kernel/arch/x86/arch_thread.h +++ b/headers/private/kernel/arch/x86/arch_thread.h @@ -25,8 +25,6 @@ struct iframe* x86_get_thread_user_iframe(Thread* thread); phys_addr_t x86_next_page_directory(Thread* from, Thread* to); void x86_initial_return_to_userland(Thread* thread, struct iframe* iframe); -uint8* x86_get_signal_stack(Thread* thread, struct iframe* frame, - struct sigaction* action); void x86_restart_syscall(struct iframe* frame); void x86_set_tls_context(Thread* thread); diff --git a/headers/private/system/arch/x86_64/arch_commpage_defs.h b/headers/private/system/arch/x86_64/arch_commpage_defs.h index f298a32678..bf7809e38a 100644 --- a/headers/private/system/arch/x86_64/arch_commpage_defs.h +++ b/headers/private/system/arch/x86_64/arch_commpage_defs.h @@ -11,7 +11,9 @@ #define COMMPAGE_ENTRY_X86_MEMCPY (COMMPAGE_ENTRY_FIRST_ARCH_SPECIFIC + 0) #define COMMPAGE_ENTRY_X86_MEMSET (COMMPAGE_ENTRY_FIRST_ARCH_SPECIFIC + 1) +#define COMMPAGE_ENTRY_X86_SIGNAL_HANDLER \ + (COMMPAGE_ENTRY_FIRST_ARCH_SPECIFIC + 2) -#define ARCH_USER_COMMPAGE_ADDR (0xffffffffffff0000) +#define ARCH_USER_COMMPAGE_ADDR (0xffffffffffff0000) #endif /* _SYSTEM_ARCH_x86_64_COMMPAGE_DEFS_H */ diff --git a/src/system/kernel/arch/x86/32/thread.cpp b/src/system/kernel/arch/x86/32/thread.cpp index 71b484c370..c4eaf9737a 100644 --- a/src/system/kernel/arch/x86/32/thread.cpp +++ b/src/system/kernel/arch/x86/32/thread.cpp @@ -107,6 +107,22 @@ x86_set_tls_context(Thread *thread) } +static uint8* +get_signal_stack(Thread* thread, struct iframe* frame, struct sigaction* action) +{ + // use the alternate signal stack if we should and can + if (thread->signal_stack_enabled + && (action->sa_flags & SA_ONSTACK) != 0 + && (frame->user_sp < thread->signal_stack_base + || frame->user_sp >= thread->signal_stack_base + + thread->signal_stack_size)) { + return (uint8*)(thread->signal_stack_base + thread->signal_stack_size); + } + + return (uint8*)frame->user_sp; +} + + // #pragma mark - @@ -290,7 +306,7 @@ arch_setup_signal_frame(Thread* thread, struct sigaction* action, signalFrameData->context.uc_mcontext.ebx = frame->bx; x86_fnsave((void *)(&signalFrameData->context.uc_mcontext.xregs)); - // fill in signalFrameData->context.uc_stack + // Fill in signalFrameData->context.uc_stack signal_get_user_stack(frame->user_sp, &signalFrameData->context.uc_stack); // store orig_eax/orig_edx in syscall_restart_return_value @@ -299,7 +315,7 @@ arch_setup_signal_frame(Thread* thread, struct sigaction* action, // get the stack to use -- that's either the current one or a special signal // stack - uint8* userStack = x86_get_signal_stack(thread, frame, action); + uint8* userStack = get_signal_stack(thread, frame, action); // copy the signal frame data onto the stack userStack -= sizeof(*signalFrameData); diff --git a/src/system/kernel/arch/x86/64/interrupts.S b/src/system/kernel/arch/x86/64/interrupts.S index 3f2d621ea9..acc82a083b 100644 --- a/src/system/kernel/arch/x86/64/interrupts.S +++ b/src/system/kernel/arch/x86/64/interrupts.S @@ -337,8 +337,9 @@ FUNCTION(x86_64_syscall_entry): // Get the system call table entry. Note I'm hardcoding the shift because // sizeof(syscall_info) is 16 and scale factors of 16 aren't supported, // so can't just do leaq kSyscallInfos(, %rax, SYSCALL_INFO_sizeof). - shlq $4, %r14 - leaq kSyscallInfos(, %r14, 1), %rax + movq %r14, %rax + shlq $4, %rax + leaq kSyscallInfos(, %rax, 1), %rax // Check the number of call arguments, greater than 6 (6 * 8 = 48) requires // a stack copy. @@ -388,6 +389,10 @@ FUNCTION(x86_64_syscall_entry): UPDATE_THREAD_KERNEL_TIME() + // If we've just restored a signal frame, use the IRET path. + cmpq $SYSCALL_RESTORE_SIGNAL_FRAME, %r14 + je .Liret + // If the return address is not canonical, return using the IRET path. // On Intel's implementation of SYSRET, the canonical address check for the // return address is performed before the switch to user mode, so a fault diff --git a/src/system/kernel/arch/x86/64/signals.cpp b/src/system/kernel/arch/x86/64/signals.cpp index 0b07851096..b8a29f1a39 100644 --- a/src/system/kernel/arch/x86/64/signals.cpp +++ b/src/system/kernel/arch/x86/64/signals.cpp @@ -15,12 +15,25 @@ #include #include -#include "syscall_numbers.h" + +extern "C" void _user_signal_handler(void); +extern int _user_signal_handler_end; void x86_initialize_commpage_signal_handler() { - // TODO x86_64 + void* handlerCode = (void*)&_user_signal_handler; + void* handlerCodeEnd = &_user_signal_handler_end; + + // Copy the signal handler code to the commpage. + size_t len = (size_t)((addr_t)handlerCodeEnd - (addr_t)handlerCode); + fill_commpage_entry(COMMPAGE_ENTRY_X86_SIGNAL_HANDLER, handlerCode, len); + + // Add symbol to the commpage image. + image_id image = get_commpage_image(); + elf_add_memory_image_symbol(image, "commpage_signal_handler", + ((addr_t*)USER_COMMPAGE_ADDR)[COMMPAGE_ENTRY_X86_SIGNAL_HANDLER], + len, B_SYMBOL_TYPE_TEXT); } diff --git a/src/system/kernel/arch/x86/64/signals_asm.S b/src/system/kernel/arch/x86/64/signals_asm.S new file mode 100644 index 0000000000..078a4c16af --- /dev/null +++ b/src/system/kernel/arch/x86/64/signals_asm.S @@ -0,0 +1,56 @@ +/* + * Copyright 2012, Alex Smith, alex@alex-smith.me.uk. + * Distributed under the terms of the MIT License. + */ + + +#include + +#include "asm_offsets.h" +#include "syscall_numbers.h" + + +.text + + +// Userspace signal handler wrapper, copied to the commpage. +FUNCTION(_user_signal_handler): + push %rbp + movq %rsp, %rbp + + // RDI points to the signal_frame_data structure, however we'll overwrite + // that with the function arguments, so move it somewhere else. We can use + // callee-save registers here without preserving them because the old value + // will be restored when the frame is restored. + movq %rdi, %r12 + + // Check the handler type. + cmpb $0, SIGNAL_FRAME_DATA_siginfo_handler(%r12) + jne .Lsiginfo_handler + +.Lsimple_handler: + // Fetch other arguments (user data, vregs). + movq SIGNAL_FRAME_DATA_user_data(%r12), %rsi + leaq SIGNAL_FRAME_DATA_context + UCONTEXT_T_uc_mcontext(%r12), %rdx + +.Lcall_handler: + // Get the handler address and the signal number first argument. + movq SIGNAL_FRAME_DATA_handler(%r12), %rax + movl SIGNAL_FRAME_DATA_info + SIGINFO_T_si_signo(%r12), %edi + + // Call the handler. + callq *%rax + + // Perform the restore_signal_frame() syscall, should not return. + movq $SYSCALL_RESTORE_SIGNAL_FRAME, %rax + movq %r12, %rdi + syscall + +.Lsiginfo_handler: + // Fetch other arguments (info pointer, context, user data). + leaq SIGNAL_FRAME_DATA_info(%r12), %rsi + leaq SIGNAL_FRAME_DATA_context(%r12), %rdx + movq SIGNAL_FRAME_DATA_user_data(%r12), %rcx + jmp .Lcall_handler +FUNCTION_END(_user_signal_handler) +SYMBOL(_user_signal_handler_end): diff --git a/src/system/kernel/arch/x86/64/thread.cpp b/src/system/kernel/arch/x86/64/thread.cpp index 51f4676e82..79434ac458 100644 --- a/src/system/kernel/arch/x86/64/thread.cpp +++ b/src/system/kernel/arch/x86/64/thread.cpp @@ -12,7 +12,7 @@ #include -#include +#include #include #include #include @@ -57,6 +57,26 @@ x86_set_tls_context(Thread* thread) } +static uint8* +get_signal_stack(Thread* thread, iframe* frame, struct sigaction* action) +{ + // Use the alternate signal stack if we should and can. + if (thread->signal_stack_enabled + && (action->sa_flags & SA_ONSTACK) != 0 + && (frame->user_sp < thread->signal_stack_base + || frame->user_sp >= thread->signal_stack_base + + thread->signal_stack_size)) { + return (uint8*)(thread->signal_stack_base + thread->signal_stack_size); + } + + // We are going to use the stack that we are already on. We must not touch + // the red zone (128 byte area below the stack pointer, reserved for use + // by functions to store temporary data and guaranteed not to be modified + // by signal handlers). + return (uint8*)(frame->user_sp - 128); +} + + // #pragma mark - @@ -203,8 +223,8 @@ arch_thread_enter_userspace(Thread* thread, addr_t entry, void* args1, filled in: - \c context.uc_stack: The stack currently used by the thread. - \c context.uc_mcontext: The current userland state of the registers. - - \c syscall_restart_return_value: Architecture specific use. On x86 the - value of eax and edx which are overwritten by the syscall return value. + - \c syscall_restart_return_value: Architecture specific use. On x86_64 the + value of rax which is overwritten by the syscall return value. Furthermore the function needs to set \c thread->user_signal_context to the userland pointer to the \c ucontext_t on the user stack. @@ -219,14 +239,116 @@ status_t arch_setup_signal_frame(Thread* thread, struct sigaction* action, struct signal_frame_data* signalFrameData) { - panic("arch_setup_signal_frame: TODO\n"); - return B_ERROR; + iframe* frame = x86_get_current_iframe(); + if (!IFRAME_IS_USER(frame)) { + panic("arch_setup_signal_frame(): No user iframe!"); + return B_BAD_VALUE; + } + + // Store the register state. + signalFrameData->context.uc_mcontext.rax = frame->ax; + signalFrameData->context.uc_mcontext.rbx = frame->bx; + signalFrameData->context.uc_mcontext.rcx = frame->cx; + signalFrameData->context.uc_mcontext.rdx = frame->dx; + signalFrameData->context.uc_mcontext.rdi = frame->di; + signalFrameData->context.uc_mcontext.rsi = frame->si; + signalFrameData->context.uc_mcontext.rbp = frame->bp; + signalFrameData->context.uc_mcontext.r8 = frame->r8; + signalFrameData->context.uc_mcontext.r9 = frame->r9; + signalFrameData->context.uc_mcontext.r10 = frame->r10; + signalFrameData->context.uc_mcontext.r11 = frame->r11; + signalFrameData->context.uc_mcontext.r12 = frame->r12; + signalFrameData->context.uc_mcontext.r13 = frame->r13; + signalFrameData->context.uc_mcontext.r14 = frame->r14; + signalFrameData->context.uc_mcontext.r15 = frame->r15; + signalFrameData->context.uc_mcontext.rsp = frame->user_sp; + signalFrameData->context.uc_mcontext.rip = frame->ip; + signalFrameData->context.uc_mcontext.rflags = frame->flags; + + // Store the FPU state. There appears to be a bug in GCC where the aligned + // attribute on a structure is being ignored when the structure is allocated + // on the stack, so even if the fpu_state struct has aligned(16) it may not + // get aligned correctly. Instead, use the current thread's FPU save area + // and then memcpy() to the frame structure. + x86_fxsave(thread->arch_info.fpu_state); + memcpy((void*)&signalFrameData->context.uc_mcontext.fpu, + thread->arch_info.fpu_state, + sizeof(signalFrameData->context.uc_mcontext.fpu)); + + // Fill in signalFrameData->context.uc_stack. + signal_get_user_stack(frame->user_sp, &signalFrameData->context.uc_stack); + + // Store syscall_restart_return_value (TODO). + //signalFrameData->syscall_restart_return_value = frame->orig_rax; + + // Get the stack to use and copy the frame data to it. + uint8* userStack = get_signal_stack(thread, frame, action); + + userStack -= sizeof(*signalFrameData); + signal_frame_data* userSignalFrameData = (signal_frame_data*)userStack; + + if (user_memcpy(userSignalFrameData, signalFrameData, + sizeof(*signalFrameData)) != B_OK) { + return B_BAD_ADDRESS; + } + + // Copy a return address to the stack so that backtraces will be correct. + userStack -= sizeof(frame->ip); + if (user_memcpy(userStack, &frame->ip, sizeof(frame->ip)) != B_OK) + return B_BAD_ADDRESS; + + // Update Thread::user_signal_context, now that everything seems to have + // gone fine. + thread->user_signal_context = &userSignalFrameData->context; + + // Set up the iframe to execute the signal handler wrapper on our prepared + // stack. First argument points to the frame data. + frame->user_sp = (addr_t)userStack; + frame->ip = ((addr_t*)USER_COMMPAGE_ADDR)[COMMPAGE_ENTRY_X86_SIGNAL_HANDLER]; + frame->di = (addr_t)userSignalFrameData; + + return B_OK; } int64 arch_restore_signal_frame(struct signal_frame_data* signalFrameData) { - panic("arch_restore_signal_frame: TODO\n"); - return B_ERROR; + iframe* frame = x86_get_current_iframe(); + + // TODO + //frame->orig_rax = signalFrameData->syscall_restart_return_value; + + frame->ax = signalFrameData->context.uc_mcontext.rax; + frame->bx = signalFrameData->context.uc_mcontext.rbx; + frame->cx = signalFrameData->context.uc_mcontext.rcx; + frame->dx = signalFrameData->context.uc_mcontext.rdx; + frame->di = signalFrameData->context.uc_mcontext.rdi; + frame->si = signalFrameData->context.uc_mcontext.rsi; + frame->bp = signalFrameData->context.uc_mcontext.rbp; + frame->r8 = signalFrameData->context.uc_mcontext.r8; + frame->r9 = signalFrameData->context.uc_mcontext.r9; + frame->r10 = signalFrameData->context.uc_mcontext.r10; + frame->r11 = signalFrameData->context.uc_mcontext.r11; + frame->r12 = signalFrameData->context.uc_mcontext.r12; + frame->r13 = signalFrameData->context.uc_mcontext.r13; + frame->r14 = signalFrameData->context.uc_mcontext.r14; + frame->r15 = signalFrameData->context.uc_mcontext.r15; + frame->user_sp = signalFrameData->context.uc_mcontext.rsp; + frame->ip = signalFrameData->context.uc_mcontext.rip; + frame->flags = (frame->flags & ~(uint64)X86_EFLAGS_USER_FLAGS) + | (signalFrameData->context.uc_mcontext.rflags & X86_EFLAGS_USER_FLAGS); + + // Same as above, alignment may not be correct. Copy to thread and restore + // from there. + Thread* thread = thread_get_current_thread(); + memcpy(thread->arch_info.fpu_state, + (void*)&signalFrameData->context.uc_mcontext.fpu, + sizeof(thread->arch_info.fpu_state)); + x86_fxrstor(thread->arch_info.fpu_state); + + // The syscall return code overwrites frame->ax with the return value of + // the syscall, need to return it here to ensure the correct value is + // restored. + return frame->ax; } diff --git a/src/system/kernel/arch/x86/Jamfile b/src/system/kernel/arch/x86/Jamfile index 579dda7d65..ccab5f8f02 100644 --- a/src/system/kernel/arch/x86/Jamfile +++ b/src/system/kernel/arch/x86/Jamfile @@ -26,6 +26,7 @@ if $(TARGET_ARCH) = x86_64 { interrupts.S stubs.cpp signals.cpp + signals_asm.S syscalls.cpp thread.cpp diff --git a/src/system/kernel/arch/x86/arch_thread.cpp b/src/system/kernel/arch/x86/arch_thread.cpp index 8b17b1d015..459ee03e3a 100644 --- a/src/system/kernel/arch/x86/arch_thread.cpp +++ b/src/system/kernel/arch/x86/arch_thread.cpp @@ -172,23 +172,6 @@ x86_initial_return_to_userland(Thread* thread, iframe* frame) } -uint8* -x86_get_signal_stack(Thread* thread, struct iframe* frame, - struct sigaction* action) -{ - // use the alternate signal stack if we should and can - if (thread->signal_stack_enabled - && (action->sa_flags & SA_ONSTACK) != 0 - && (frame->user_sp < thread->signal_stack_base - || frame->user_sp >= thread->signal_stack_base - + thread->signal_stack_size)) { - return (uint8*)(thread->signal_stack_base + thread->signal_stack_size); - } - - return (uint8*)frame->user_sp; -} - - // #pragma mark - diff --git a/src/system/kernel/arch/x86/asm_offsets.cpp b/src/system/kernel/arch/x86/asm_offsets.cpp index 65339ce38d..42f7bc9a48 100644 --- a/src/system/kernel/arch/x86/asm_offsets.cpp +++ b/src/system/kernel/arch/x86/asm_offsets.cpp @@ -91,6 +91,7 @@ dummy() DEFINE_OFFSET_MACRO(SIGNAL_FRAME_DATA, signal_frame_data, context); DEFINE_OFFSET_MACRO(SIGNAL_FRAME_DATA, signal_frame_data, user_data); DEFINE_OFFSET_MACRO(SIGNAL_FRAME_DATA, signal_frame_data, handler); + DEFINE_OFFSET_MACRO(SIGNAL_FRAME_DATA, signal_frame_data, siginfo_handler); // struct ucontext_t DEFINE_OFFSET_MACRO(UCONTEXT_T, __ucontext_t, uc_mcontext); diff --git a/src/system/kernel/arch/x86/x86_signals.h b/src/system/kernel/arch/x86/x86_signals.h index 282d4afec1..0e6cb50801 100644 --- a/src/system/kernel/arch/x86/x86_signals.h +++ b/src/system/kernel/arch/x86/x86_signals.h @@ -10,7 +10,9 @@ void x86_initialize_commpage_signal_handler(); +#ifndef __x86_64__ addr_t x86_get_user_signal_handler_wrapper(bool beosHandler); +#endif #endif // _KERNEL_ARCH_X86_SIGNALS_H