#include <arch/thread.h>
#include <string.h>
#include <arch/user_debugger.h>
#include <arch_cpu.h>
#include <commpage.h>
#include <cpu.h>
#include <debug.h>
#include <kernel.h>
#include <ksignal.h>
#include <interrupts.h>
#include <team.h>
#include <thread.h>
#include <tls.h>
#include <tracing.h>
#include <util/AutoLock.h>
#include <util/Random.h>
#include <vm/vm_types.h>
#include <vm/VMAddressSpace.h>
#include "paging/X86PagingStructures.h"
#include "paging/X86VMTranslationMap.h"
#include "x86_signals.h"
#ifdef TRACE_ARCH_THREAD
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
#ifdef SYSCALL_TRACING
namespace SyscallTracing {
class RestartSyscall : public AbstractTraceEntry {
public:
RestartSyscall()
{
Initialized();
}
virtual void AddDump(TraceOutput& out)
{
out.Print("syscall restart");
}
};
}
# define TSYSCALL(x) new(std::nothrow) SyscallTracing::x
#else
# define TSYSCALL(x)
#endif
extern bool gHasSSE;
static struct arch_thread sInitialState _ALIGNED(16);
static inline void
set_fs_register(uint32 segment)
{
asm("movl %0,%%fs" :: "r" (segment));
}
void
x86_restart_syscall(struct iframe* frame)
{
Thread* thread = thread_get_current_thread();
atomic_and(&thread->flags, ~THREAD_FLAGS_RESTART_SYSCALL);
atomic_or(&thread->flags, THREAD_FLAGS_SYSCALL_RESTARTED);
frame->ax = frame->orig_eax;
frame->dx = frame->orig_edx;
frame->ip -= 2;
TSYSCALL(RestartSyscall());
}
void
x86_set_tls_context(Thread *thread)
{
segment_descriptor* gdt = get_gdt(smp_get_current_cpu());
set_segment_descriptor_base(&gdt[USER_TLS_SEGMENT],
thread->user_local_storage);
set_fs_register((USER_TLS_SEGMENT << 3) | DPL_USER);
}
static addr_t
arch_randomize_stack_pointer(addr_t value)
{
STATIC_ASSERT(MAX_RANDOM_VALUE >= B_PAGE_SIZE - 1);
value -= random_value() & (B_PAGE_SIZE - 1);
return (value & ~addr_t(0xf)) - 4;
}
static uint8*
get_signal_stack(Thread* thread, struct iframe* frame, struct sigaction* action,
size_t spaceNeeded)
{
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)) {
addr_t stackTop = thread->signal_stack_base + thread->signal_stack_size;
return (uint8*)arch_randomize_stack_pointer(stackTop - spaceNeeded);
}
return (uint8*)((frame->user_sp - spaceNeeded) & ~addr_t(0xf)) - 4;
}
status_t
arch_thread_init(struct kernel_args *args)
{
asm volatile ("clts; fninit; fnclex;");
if (gHasSSE)
x86_fxsave(sInitialState.fpu_state);
else
x86_fnsave(sInitialState.fpu_state);
return B_OK;
}
status_t
arch_thread_init_thread_struct(Thread *thread)
{
memcpy(&thread->arch_info, &sInitialState, sizeof(struct arch_thread));
return B_OK;
}
void
arch_thread_init_kthread_stack(Thread* thread, void* _stack, void* _stackTop,
void (*function)(void*), const void* data)
{
addr_t* stackTop = (addr_t*)_stackTop;
TRACE(("arch_thread_init_kthread_stack: stack top %p, function %p, data: "
"%p\n", stackTop, function, data));
*--stackTop = (addr_t)data;
*--stackTop = 0;
*--stackTop = (addr_t)function;
for (int i = 0; i < 8; i++)
*--stackTop = 0;
thread->arch_info.current_stack.esp = (uint32*)stackTop;
thread->arch_info.current_stack.ss = (uint32*)KERNEL_DATA_SELECTOR;
}
void
arch_thread_dump_info(void *info)
{
struct arch_thread *at = (struct arch_thread *)info;
kprintf("\tesp: %p\n", at->current_stack.esp);
kprintf("\tss: %p\n", at->current_stack.ss);
kprintf("\tfpu_state at %p\n", at->fpu_state);
}
status_t
arch_thread_enter_userspace(Thread* thread, addr_t entry, void* args1,
void* args2)
{
addr_t stackTop = thread->user_stack_base + thread->user_stack_size;
uint32 args[3];
TRACE(("arch_thread_enter_userspace: entry 0x%lx, args %p %p, "
"ustack_top 0x%lx\n", entry, args1, args2, stackTop));
stackTop = arch_randomize_stack_pointer(stackTop - sizeof(args));
addr_t commPageAddress = (addr_t)thread->team->commpage_address;
args[0] = ((addr_t*)commPageAddress)[COMMPAGE_ENTRY_X86_THREAD_EXIT]
+ commPageAddress;
args[1] = (uint32)args1;
args[2] = (uint32)args2;
if (user_memcpy((void *)stackTop, args, sizeof(args)) < B_OK)
return B_BAD_ADDRESS;
iframe frame = {};
frame.type = IFRAME_TYPE_SYSCALL;
frame.gs = USER_DATA_SELECTOR;
frame.es = USER_DATA_SELECTOR;
frame.ds = USER_DATA_SELECTOR;
frame.ip = entry;
frame.cs = USER_CODE_SELECTOR;
frame.flags = X86_EFLAGS_RESERVED1 | X86_EFLAGS_INTERRUPT;
frame.user_sp = stackTop;
frame.user_ss = USER_DATA_SELECTOR;
x86_initial_return_to_userland(thread, &frame);
return B_OK;
}
status_t
arch_setup_signal_frame(Thread* thread, struct sigaction* action,
struct signal_frame_data* signalFrameData)
{
struct iframe *frame = x86_get_current_iframe();
if (!IFRAME_IS_USER(frame)) {
panic("arch_setup_signal_frame(): No user iframe!");
return B_BAD_VALUE;
}
if ((action->sa_flags & SA_BEOS_COMPATIBLE_HANDLER) != 0
&& signalFrameData->info.si_signo == SIGBUS) {
signalFrameData->info.si_signo = SIGSEGV;
}
signalFrameData->context.uc_mcontext.eip = frame->ip;
signalFrameData->context.uc_mcontext.eflags = frame->flags;
signalFrameData->context.uc_mcontext.eax = frame->ax;
signalFrameData->context.uc_mcontext.ecx = frame->cx;
signalFrameData->context.uc_mcontext.edx = frame->dx;
signalFrameData->context.uc_mcontext.ebp = frame->bp;
signalFrameData->context.uc_mcontext.esp = frame->user_sp;
signalFrameData->context.uc_mcontext.edi = frame->di;
signalFrameData->context.uc_mcontext.esi = frame->si;
signalFrameData->context.uc_mcontext.ebx = frame->bx;
x86_fnsave((void *)(&signalFrameData->context.uc_mcontext.xregs));
signal_get_user_stack(frame->user_sp, &signalFrameData->context.uc_stack);
signalFrameData->context.uc_mcontext.xregs.state.new_format.fault_address = x86_read_cr2();
signalFrameData->context.uc_mcontext.xregs.state.new_format.error_code = frame->error_code;
signalFrameData->context.uc_mcontext.xregs.state.new_format.cs = frame->cs;
signalFrameData->context.uc_mcontext.xregs.state.new_format.ds = frame->ds;
signalFrameData->context.uc_mcontext.xregs.state.new_format.es = frame->es;
signalFrameData->context.uc_mcontext.xregs.state.new_format.fs = frame->fs;
signalFrameData->context.uc_mcontext.xregs.state.new_format.gs = frame->gs;
signalFrameData->context.uc_mcontext.xregs.state.new_format.ss = frame->user_ss;
signalFrameData->context.uc_mcontext.xregs.state.new_format.trap_number = frame->vector;
signalFrameData->syscall_restart_return_value
= (uint64)frame->orig_edx << 32 | frame->orig_eax;
uint32 stackFrame[2];
uint8* userStack = get_signal_stack(thread, frame, action,
sizeof(*signalFrameData) + sizeof(stackFrame));
signal_frame_data* userSignalFrameData
= (signal_frame_data*)(userStack + sizeof(stackFrame));
if (user_memcpy(userSignalFrameData, signalFrameData,
sizeof(*signalFrameData)) != B_OK) {
return B_BAD_ADDRESS;
}
stackFrame[0] = frame->ip;
stackFrame[1] = (addr_t)userSignalFrameData;
if (user_memcpy(userStack, stackFrame, sizeof(stackFrame)) != B_OK)
return B_BAD_ADDRESS;
thread->user_signal_context = &userSignalFrameData->context;
frame->user_sp = (addr_t)userStack;
frame->ip = x86_get_user_signal_handler_wrapper(
(action->sa_flags & SA_BEOS_COMPATIBLE_HANDLER) != 0,
thread->team->commpage_address);
frame->flags &= ~(X86_EFLAGS_TRAP | X86_EFLAGS_DIRECTION);
return B_OK;
}
int64
arch_restore_signal_frame(struct signal_frame_data* signalFrameData)
{
struct iframe* frame = x86_get_current_iframe();
TRACE(("### arch_restore_signal_frame: entry\n"));
frame->orig_eax = (uint32)signalFrameData->syscall_restart_return_value;
frame->orig_edx
= (uint32)(signalFrameData->syscall_restart_return_value >> 32);
frame->ip = signalFrameData->context.uc_mcontext.eip;
frame->flags = (frame->flags & ~(uint32)X86_EFLAGS_USER_FLAGS)
| (signalFrameData->context.uc_mcontext.eflags & X86_EFLAGS_USER_FLAGS);
frame->ax = signalFrameData->context.uc_mcontext.eax;
frame->cx = signalFrameData->context.uc_mcontext.ecx;
frame->dx = signalFrameData->context.uc_mcontext.edx;
frame->bp = signalFrameData->context.uc_mcontext.ebp;
frame->user_sp = signalFrameData->context.uc_mcontext.esp;
frame->di = signalFrameData->context.uc_mcontext.edi;
frame->si = signalFrameData->context.uc_mcontext.esi;
frame->bx = signalFrameData->context.uc_mcontext.ebx;
frame->cs = signalFrameData->context.uc_mcontext.xregs.state.new_format.cs;
frame->ds = signalFrameData->context.uc_mcontext.xregs.state.new_format.ds;
frame->es = signalFrameData->context.uc_mcontext.xregs.state.new_format.es;
frame->fs = signalFrameData->context.uc_mcontext.xregs.state.new_format.fs;
frame->gs = signalFrameData->context.uc_mcontext.xregs.state.new_format.gs;
frame->user_ss = signalFrameData->context.uc_mcontext.xregs.state.new_format.ss;
x86_frstor((void*)(&signalFrameData->context.uc_mcontext.xregs));
TRACE(("### arch_restore_signal_frame: exit\n"));
return (int64)frame->ax | ((int64)frame->dx << 32);
}
void
arch_syscall_64_bit_return_value(void)
{
Thread* thread = thread_get_current_thread();
atomic_or(&thread->flags, THREAD_FLAGS_64_BIT_SYSCALL_RETURN);
}