diff --git a/zion/CMakeLists.txt b/zion/CMakeLists.txt index 9479b7d..a76529a 100644 --- a/zion/CMakeLists.txt +++ b/zion/CMakeLists.txt @@ -12,6 +12,8 @@ add_executable(zion scheduler/process.cpp scheduler/scheduler.cpp scheduler/thread.cpp + syscall/syscall.cpp + syscall/syscall_enter.s zion.cpp) target_include_directories(zion diff --git a/zion/scheduler/thread.cpp b/zion/scheduler/thread.cpp index c4d893b..011a707 100644 --- a/zion/scheduler/thread.cpp +++ b/zion/scheduler/thread.cpp @@ -28,6 +28,7 @@ Thread::Thread(Process* proc, uint64_t tid) : process_(proc), id_(tid) { // 16: cr3 *(stack_ptr - 16) = proc->cr3(); rsp0_ = reinterpret_cast(stack_ptr - 16); + rsp0_start_ = reinterpret_cast(stack_ptr); } uint64_t Thread::pid() { return process_->id(); } diff --git a/zion/scheduler/thread.h b/zion/scheduler/thread.h index 33dc09c..89239bf 100644 --- a/zion/scheduler/thread.h +++ b/zion/scheduler/thread.h @@ -17,6 +17,7 @@ class Thread { Process& process() { return *process_; } uint64_t* Rsp0Ptr() { return &rsp0_; } + uint64_t Rsp0Start() { return rsp0_start_; } // Called the first time the thread starts up. void Init(); @@ -33,4 +34,7 @@ class Thread { // Stack pointer to take on resume. // Stack will contain the full thread context. uint64_t rsp0_; + // Stack pointer to take when returning from userspace. + // I don't think me mind clobbering the stack here. + uint64_t rsp0_start_; }; diff --git a/zion/syscall/syscall.cpp b/zion/syscall/syscall.cpp new file mode 100644 index 0000000..9576450 --- /dev/null +++ b/zion/syscall/syscall.cpp @@ -0,0 +1,57 @@ +#include "syscall/syscall.h" + +#include + +#include "debug/debug.h" +#include "scheduler/scheduler.h" + +#define EFER 0xC0000080 +#define STAR 0xC0000081 +#define LSTAR 0xC0000082 + +namespace { + +uint64_t GetMSR(uint32_t msr) { + uint32_t lo, hi; + asm("rdmsr" : "=a"(lo), "=d"(hi) : "c"(msr)); + return (static_cast(hi) << 32) | lo; +} + +void SetMSR(uint32_t msr, uint64_t val) { + uint32_t lo = static_cast(val); + uint32_t hi = val >> 32; + asm("wrmsr" ::"a"(lo), "d"(hi), "c"(msr)); +} + +extern "C" void syscall_enter(); + +} // namespace + +// Used by syscall_enter.s +extern "C" uint64_t GetKernelRsp() { + return sched::CurrentThread().Rsp0Start(); +} + +void InitSyscall() { + uint64_t efer_val = GetMSR(EFER); + efer_val |= 1; + SetMSR(EFER, efer_val); + if (GetMSR(EFER) != efer_val) { + panic("Failed to set EFER MSR"); + } + + uint64_t star_val = GetMSR(STAR); + // FIXME: Fix GDT such that we can properly set the user CS. + // Due to the ability to jump from a 64 bit kernel into compatibility mode, + // we set the user_cs to the kernel_cs because it adds 16 to jump to 64-bit + // mode. See AMD Manual 3.4 instruction SYSRET for more info. + uint64_t kernel_cs = 0x8; + uint64_t user_cs = kernel_cs; + star_val |= (kernel_cs << 32) | (user_cs << 48); + SetMSR(STAR, star_val); + SetMSR(LSTAR, reinterpret_cast(syscall_enter)); +} + +extern "C" void SyscallHandler(uint64_t call_id, char* message) { + dbgln(message); +} diff --git a/zion/syscall/syscall.h b/zion/syscall/syscall.h new file mode 100644 index 0000000..dc8e57c --- /dev/null +++ b/zion/syscall/syscall.h @@ -0,0 +1,3 @@ +#pragma once + +void InitSyscall(); diff --git a/zion/syscall/syscall_enter.s b/zion/syscall/syscall_enter.s new file mode 100644 index 0000000..8286762 --- /dev/null +++ b/zion/syscall/syscall_enter.s @@ -0,0 +1,60 @@ +.global syscall_enter +syscall_enter: + # Technically don't need to save all of these as + # the SYS V ABI will preserve some of them by + # default but I doubt that this costs us much. + push %rbx + push %rcx # Special! This is the return address + push %rdx + push %rsi + push %rdi + push %r8 + push %r9 + push %r10 + push %r11 + push %r12 + push %r13 + push %r14 + push %r15 + + call GetKernelRsp + + # RAX holds the kernel RSP now. + mov %rsp, %rbx + mov %rax, %rsp + push %rbx + + # Now that we are on the kernel stack we can re-enable interrupts. + sti + + # Restore caller registers using the userspace rsp in rbx + mov 0x40(%rbx), %rdi + mov 0x48(%rbx), %rsi + mov 0x50(%rbx), %rdx + mov 0x58(%rbx), %rcx + # Don't push the rbp and rsp as the callee will do so. + call SyscallHandler + + # Clear interrupts since we are moving back to the user stack here. + # The sysret call will re-enable them for us. + cli + + # Pop the userspace rsp off the stack + pop %rsp + + pop %r15 + pop %r14 + pop %r13 + pop %r12 + pop %r11 # Contains rflags. + pop %r10 + pop %r9 + pop %r8 + pop %rdi + pop %rsi + pop %rdx + pop %rcx # Contains return address. + pop %rbx + # Because we haven't touched rax since calling syscall_handler it should still have the return value. + sysretq + diff --git a/zion/zion.cpp b/zion/zion.cpp index d72256f..31e89e7 100644 --- a/zion/zion.cpp +++ b/zion/zion.cpp @@ -7,6 +7,7 @@ #include "memory/paging_util.h" #include "memory/physical_memory.h" #include "scheduler/scheduler.h" +#include "syscall/syscall.h" extern "C" void zion() { InitGdt(); @@ -17,6 +18,8 @@ extern "C" void zion() { KernelHeap heap(0xFFFFFFFF'40000000, 0xFFFFFFFF'80000000); phys_mem::InitPhysicalMemoryManager(); + InitSyscall(); + sched::InitScheduler(); Process p1; p1.CreateThread();