Implemented signals for x86_64.

This commit is contained in:
Alex Smith
2012-07-30 13:52:51 +01:00
parent 5568c5055c
commit c0d28c0199
12 changed files with 331 additions and 59 deletions
+99 -26
View File
@@ -12,35 +12,108 @@
#if __x86_64__ #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 /* __x86_64__ */
#endif /* _ARCH_SIGNAL_H_ */ #endif /* _ARCH_SIGNAL_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); phys_addr_t x86_next_page_directory(Thread* from, Thread* to);
void x86_initial_return_to_userland(Thread* thread, struct iframe* iframe); 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_restart_syscall(struct iframe* frame);
void x86_set_tls_context(Thread* thread); void x86_set_tls_context(Thread* thread);
@@ -11,7 +11,9 @@
#define COMMPAGE_ENTRY_X86_MEMCPY (COMMPAGE_ENTRY_FIRST_ARCH_SPECIFIC + 0) #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_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 */ #endif /* _SYSTEM_ARCH_x86_64_COMMPAGE_DEFS_H */
+18 -2
View File
@@ -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 - // #pragma mark -
@@ -290,7 +306,7 @@ arch_setup_signal_frame(Thread* thread, struct sigaction* action,
signalFrameData->context.uc_mcontext.ebx = frame->bx; signalFrameData->context.uc_mcontext.ebx = frame->bx;
x86_fnsave((void *)(&signalFrameData->context.uc_mcontext.xregs)); 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); signal_get_user_stack(frame->user_sp, &signalFrameData->context.uc_stack);
// store orig_eax/orig_edx in syscall_restart_return_value // 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 // get the stack to use -- that's either the current one or a special signal
// stack // 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 // copy the signal frame data onto the stack
userStack -= sizeof(*signalFrameData); userStack -= sizeof(*signalFrameData);
+7 -2
View File
@@ -337,8 +337,9 @@ FUNCTION(x86_64_syscall_entry):
// Get the system call table entry. Note I'm hardcoding the shift because // 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, // sizeof(syscall_info) is 16 and scale factors of 16 aren't supported,
// so can't just do leaq kSyscallInfos(, %rax, SYSCALL_INFO_sizeof). // so can't just do leaq kSyscallInfos(, %rax, SYSCALL_INFO_sizeof).
shlq $4, %r14 movq %r14, %rax
leaq kSyscallInfos(, %r14, 1), %rax shlq $4, %rax
leaq kSyscallInfos(, %rax, 1), %rax
// Check the number of call arguments, greater than 6 (6 * 8 = 48) requires // Check the number of call arguments, greater than 6 (6 * 8 = 48) requires
// a stack copy. // a stack copy.
@@ -388,6 +389,10 @@ FUNCTION(x86_64_syscall_entry):
UPDATE_THREAD_KERNEL_TIME() 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. // If the return address is not canonical, return using the IRET path.
// On Intel's implementation of SYSRET, the canonical address check for the // 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 // return address is performed before the switch to user mode, so a fault
+15 -2
View File
@@ -15,12 +15,25 @@
#include <elf.h> #include <elf.h>
#include <smp.h> #include <smp.h>
#include "syscall_numbers.h"
extern "C" void _user_signal_handler(void);
extern int _user_signal_handler_end;
void void
x86_initialize_commpage_signal_handler() 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);
} }
@@ -0,0 +1,56 @@
/*
* Copyright 2012, Alex Smith, alex@alex-smith.me.uk.
* Distributed under the terms of the MIT License.
*/
#include <asm_defs.h>
#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):
+129 -7
View File
@@ -12,7 +12,7 @@
#include <string.h> #include <string.h>
#include <arch_cpu.h> #include <commpage.h>
#include <cpu.h> #include <cpu.h>
#include <debug.h> #include <debug.h>
#include <kernel.h> #include <kernel.h>
@@ -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 - // #pragma mark -
@@ -203,8 +223,8 @@ arch_thread_enter_userspace(Thread* thread, addr_t entry, void* args1,
filled in: filled in:
- \c context.uc_stack: The stack currently used by the thread. - \c context.uc_stack: The stack currently used by the thread.
- \c context.uc_mcontext: The current userland state of the registers. - \c context.uc_mcontext: The current userland state of the registers.
- \c syscall_restart_return_value: Architecture specific use. On x86 the - \c syscall_restart_return_value: Architecture specific use. On x86_64 the
value of eax and edx which are overwritten by the syscall return value. value of rax which is overwritten by the syscall return value.
Furthermore the function needs to set \c thread->user_signal_context to the Furthermore the function needs to set \c thread->user_signal_context to the
userland pointer to the \c ucontext_t on the user stack. 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, arch_setup_signal_frame(Thread* thread, struct sigaction* action,
struct signal_frame_data* signalFrameData) struct signal_frame_data* signalFrameData)
{ {
panic("arch_setup_signal_frame: TODO\n"); iframe* frame = x86_get_current_iframe();
return B_ERROR; 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 int64
arch_restore_signal_frame(struct signal_frame_data* signalFrameData) arch_restore_signal_frame(struct signal_frame_data* signalFrameData)
{ {
panic("arch_restore_signal_frame: TODO\n"); iframe* frame = x86_get_current_iframe();
return B_ERROR;
// 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;
} }
+1
View File
@@ -26,6 +26,7 @@ if $(TARGET_ARCH) = x86_64 {
interrupts.S interrupts.S
stubs.cpp stubs.cpp
signals.cpp signals.cpp
signals_asm.S
syscalls.cpp syscalls.cpp
thread.cpp 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 - // #pragma mark -
@@ -91,6 +91,7 @@ dummy()
DEFINE_OFFSET_MACRO(SIGNAL_FRAME_DATA, signal_frame_data, context); 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, user_data);
DEFINE_OFFSET_MACRO(SIGNAL_FRAME_DATA, signal_frame_data, handler); DEFINE_OFFSET_MACRO(SIGNAL_FRAME_DATA, signal_frame_data, handler);
DEFINE_OFFSET_MACRO(SIGNAL_FRAME_DATA, signal_frame_data, siginfo_handler);
// struct ucontext_t // struct ucontext_t
DEFINE_OFFSET_MACRO(UCONTEXT_T, __ucontext_t, uc_mcontext); DEFINE_OFFSET_MACRO(UCONTEXT_T, __ucontext_t, uc_mcontext);
+2
View File
@@ -10,7 +10,9 @@
void x86_initialize_commpage_signal_handler(); void x86_initialize_commpage_signal_handler();
#ifndef __x86_64__
addr_t x86_get_user_signal_handler_wrapper(bool beosHandler); addr_t x86_get_user_signal_handler_wrapper(bool beosHandler);
#endif
#endif // _KERNEL_ARCH_X86_SIGNALS_H #endif // _KERNEL_ARCH_X86_SIGNALS_H