#include "thread/Thread.h" #include "Log.h" #include "memory/MemoryManager.h" #include #include #include bool is_in_kernel(Registers* regs) { return regs->cs == 8; } void Thread::set_ip(u64 ip) { regs.rip = ip; } u64 Thread::ip() { return regs.rip; } void Thread::set_sp(u64 sp) { regs.rsp = sp; } u64 Thread::sp() { return regs.rsp; } void Thread::set_return(u64 ret) { regs.rax = ret; } u64 Thread::return_register() { return regs.rax; } void Thread::init_regs_kernel() { memset(®s, 0, sizeof(Registers)); regs.cs = 0x08; regs.ss = 0x10; regs.rflags = 1 << 9; // IF (Interrupt enable flag) } void Thread::init_regs_user() { memset(®s, 0, sizeof(Registers)); regs.cs = 0x18 | 3; regs.ss = 0x20 | 3; regs.rflags = 1 << 9; // IF (Interrupt enable flag) } void Thread::set_arguments(u64 arg1, u64 arg2, u64 arg3, u64 arg4) { regs.rdi = arg1; regs.rsi = arg2; regs.rdx = arg3; regs.rcx = arg4; } void switch_context(Thread* old_thread, Thread* new_thread, Registers* regs) { if (!old_thread->is_idle()) memcpy(&old_thread->regs, regs, sizeof(Registers)); memcpy(regs, &new_thread->regs, sizeof(Registers)); } // FIXME: Move this function to a common location (also used in ThreadImage) Result Thread::push_mem_on_stack(const u8* mem, usize size) { if ((regs.rsp - size) < stack.bottom()) return err(E2BIG); if (!MemoryManager::validate_user_write((void*)(regs.rsp - size), size)) return err(EFAULT); regs.rsp -= size; memcpy((void*)regs.rsp, mem, size); return regs.rsp; } Result Thread::pop_mem_from_stack(u8* mem, usize size) { if ((regs.rsp + size) > stack.top()) return err(E2BIG); if (!MemoryManager::validate_user_read((void*)regs.rsp, size)) return err(EFAULT); memcpy(mem, (void*)regs.rsp, size); regs.rsp += size; return regs.rsp; } bool Thread::deliver_signal(int signo, Registers* current_regs) { auto handler = signal_handlers[signo - 1]; check(handler.sa_handler != SIG_IGN); check(handler.sa_handler != SIG_DFL); memcpy(®s, current_regs, sizeof(regs)); kinfoln("signal: delivering signal %d for thread %ld, ip=%p, sp=%p, handler=%p, sigreturn=%p", signo, id, (void*)regs.rip, (void*)regs.rsp, (void*)handler.sa_handler, (void*)handler.__sa_sigreturn); regs.rsp -= 128; // Skip the red zone if (push_mem_on_stack((u8*)current_regs, sizeof(*current_regs)).has_error()) return false; if (push_mem_on_stack((u8*)&signal_mask, sizeof(signal_mask)).has_error()) return false; u64 rsp = regs.rsp; regs.rsp = align_down<16>(regs.rsp); if (push_mem_on_stack((u8*)&rsp, sizeof(u64)).has_error()) return false; if (push_mem_on_stack((u8*)&handler.__sa_sigreturn, sizeof(void*)).has_error()) return false; // FIXME: Do not add the current signal to the signal mask if SA_NODEFER is set. signal_mask = handler.sa_mask | (1 << (signo - 1)); rsp = regs.rsp; init_regs_user(); regs.rsp = rsp; regs.rip = (u64)handler.sa_handler; regs.rdi = signo; memcpy(current_regs, ®s, sizeof(regs)); return true; } void Thread::sigreturn(Registers* current_regs) { memcpy(®s, current_regs, sizeof(regs)); u64 rsp; pop_mem_from_stack((u8*)&rsp, sizeof(rsp)); regs.rsp = rsp; pop_mem_from_stack((u8*)&signal_mask, sizeof(signal_mask)); pop_mem_from_stack((u8*)current_regs, sizeof(*current_regs)); memcpy(®s, current_regs, sizeof(regs)); regs.cs = 0x18 | 3; regs.ss = 0x20 | 3; // FIXME: Using this, a program can craft a special RFLAGS that gives them a higher IOPL or other stuff. Find out // exactly what bits to block from modifying. kinfoln("sigreturn: restored program state, sp=%p, ip=%p", (void*)regs.rsp, (void*)regs.rip); memcpy(current_regs, ®s, sizeof(regs)); }