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__
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_ */
@@ -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);
@@ -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 */
+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 -
@@ -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);
+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
// 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
+15 -2
View File
@@ -15,12 +15,25 @@
#include <elf.h>
#include <smp.h>
#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);
}
@@ -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 <arch_cpu.h>
#include <commpage.h>
#include <cpu.h>
#include <debug.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 -
@@ -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;
}
+1
View File
@@ -26,6 +26,7 @@ if $(TARGET_ARCH) = x86_64 {
interrupts.S
stubs.cpp
signals.cpp
signals_asm.S
syscalls.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 -
@@ -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);
+2
View File
@@ -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