diff --git a/modules.yaml b/modules.yaml index 5ecb5e2..6828a1e 100644 --- a/modules.yaml +++ b/modules.yaml @@ -50,6 +50,8 @@ modules: - src/kernel/syscall.s - src/kernel/syscalls/object.cpp - src/kernel/syscalls/process.cpp + - src/kernel/syscalls/system.cpp + - src/kernel/syscalls/thread.cpp - src/kernel/task.s - src/kernel/crtn.s diff --git a/src/drivers/nulldrv/main.cpp b/src/drivers/nulldrv/main.cpp index 2c552af..444cfdc 100644 --- a/src/drivers/nulldrv/main.cpp +++ b/src/drivers/nulldrv/main.cpp @@ -3,32 +3,53 @@ #include "j6/types.h" #include "j6/errors.h" +#include "j6/signals.h" extern "C" { - j6_status_t get_process_koid(j6_koid_t *koid); - j6_status_t sleep(uint64_t til); - j6_status_t debug(); - j6_status_t message(const char *msg); + j6_status_t system_log(const char *msg); + + j6_status_t object_wait(j6_handle_t obj, j6_signal_t sig, j6_signal_t *out); + + j6_status_t process_koid(j6_koid_t *koid); + + j6_status_t thread_koid(j6_koid_t *koid); + j6_status_t thread_create(void (*koid)(), j6_handle_t *handle); + j6_status_t thread_sleep(uint64_t til); + j6_status_t thread_exit(int64_t status); int main(int, const char **); } +void +thread_proc() +{ + system_log("sub thread starting"); + for (int i = 1; i < 5; ++i) + thread_sleep(i*10); + + system_log("sub thread exiting"); + thread_exit(0); +} int main(int argc, const char **argv) { - uint64_t pid = 0; - uint64_t child = 0; - j6_koid_t process = 0; + j6_handle_t child = 0; + j6_signal_t out = 0; - j6_status_t result = get_process_koid(&process); + system_log("main thread starting"); + + j6_status_t result = thread_create(&thread_proc, &child); if (result != j6_status_ok) return result; - message("hello from nulldrv!"); + system_log("main thread waiting on child"); - for (int i = 1; i < 5; ++i) - sleep(i*10); + result = object_wait(child, -1ull, &out); + if (result != j6_status_ok) + return result; - return pid; + system_log("main thread done, exiting"); + return 0; } + diff --git a/src/drivers/nulldrv/main.s b/src/drivers/nulldrv/main.s index dd760dc..fc696b0 100644 --- a/src/drivers/nulldrv/main.s +++ b/src/drivers/nulldrv/main.s @@ -6,52 +6,30 @@ extern main extern exit section .text -global get_process_koid -get_process_koid: - push rbp - mov rbp, rsp - ; address of out var should already be in rdi - mov rax, 0x10 ; getpid syscall - syscall ; result is now already in rax, so just return +%macro SYSCALL 2 + global %1 + %1: + push rbp + mov rbp, rsp - pop rbp - ret + ; address of args should already be in rdi, etc + mov rax, %2 + syscall + ; result is now already in rax, so just return -global debug -debug: - push rbp - mov rbp, rsp - - mov rax, 0x00 ; debug syscall - syscall - - pop rbp - ret - -global sleep -sleep: - push rbp - mov rbp, rsp - - mov rax, 0x14 ; sleep syscall - syscall - - pop rbp - ret - -global message -message: - push rbp - mov rbp, rsp - - ; message should already be in rdi - mov rax, 0x12 - syscall - - pop rbp - ret + pop rbp + ret +%endmacro +SYSCALL system_log, 0x00 +SYSCALL object_wait, 0x09 +SYSCALL process_koid, 0x10 +SYSCALL thread_koid, 0x18 +SYSCALL thread_create, 0x19 +SYSCALL thread_exit, 0x1a +SYSCALL thread_pause, 0x1b +SYSCALL thread_sleep, 0x1c global _start _start: diff --git a/src/include/j6/signals.h b/src/include/j6/signals.h index c1154d0..65e9d7f 100644 --- a/src/include/j6/signals.h +++ b/src/include/j6/signals.h @@ -6,8 +6,13 @@ #define j6_signal_no_handles (1ull << 0) // Signals 16-47 are defined per-object-type + +// Process signals #define j6_signal_process_exit (1ull << 16) +// Thread signals +#define j6_signal_thread_exit (1ull << 16) + // Signals 48-63 are user-defined signals #define j6_signal_user0 (1ull << 48) #define j6_signal_user1 (1ull << 49) diff --git a/src/include/j6/types.h b/src/include/j6/types.h index bae5e9a..9bff9b0 100644 --- a/src/include/j6/types.h +++ b/src/include/j6/types.h @@ -18,3 +18,5 @@ typedef uint64_t j6_signal_t; /// The rights of a handle/capability are a bitmap of 64 possible rights typedef uint64_t j6_rights_t; + +#define j6_handle_invalid 0xffffffff diff --git a/src/include/kernel_memory.h b/src/include/kernel_memory.h index 1edcf1e..4c0c868 100644 --- a/src/include/kernel_memory.h +++ b/src/include/kernel_memory.h @@ -16,9 +16,6 @@ namespace memory { /// Offset from physical where page tables are mapped. constexpr uintptr_t page_offset = 0xffffc00000000000; - /// Initial process thread's stack address - constexpr uintptr_t initial_stack = 0x0000800000000000; - /// Initial process thread's stack size, in pages constexpr unsigned initial_stack_pages = 1; diff --git a/src/kernel/cpu.h b/src/kernel/cpu.h index 66110a7..84108fd 100644 --- a/src/kernel/cpu.h +++ b/src/kernel/cpu.h @@ -3,6 +3,8 @@ #include struct TCB; +class thread; +class process; struct cpu_state { @@ -19,6 +21,8 @@ struct cpu_data uintptr_t rsp0; uintptr_t rsp3; TCB *tcb; + thread *t; + process *p; }; extern cpu_data bsp_cpu_data; diff --git a/src/kernel/debug.cpp b/src/kernel/debug.cpp index 1b3c7d3..5025738 100644 --- a/src/kernel/debug.cpp +++ b/src/kernel/debug.cpp @@ -2,6 +2,8 @@ #include "cpu.h" #include "debug.h" #include "gdt.h" +#include "objects/process.h" +#include "objects/thread.h" #include "page_manager.h" size_t __counter_syscall_enter = 0; @@ -15,6 +17,9 @@ print_regs(const cpu_state ®s) uint64_t cr2 = 0; __asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2)); + cons->printf(" process: %llx", bsp_cpu_data.p->koid()); + cons->printf(" thread: %llx\n", bsp_cpu_data.t->koid()); + print_regL("rax", regs.rax); print_regM("rbx", regs.rbx); print_regR("rcx", regs.rcx); diff --git a/src/kernel/objects/kobject.h b/src/kernel/objects/kobject.h index 875a824..d9be0bd 100644 --- a/src/kernel/objects/kobject.h +++ b/src/kernel/objects/kobject.h @@ -41,6 +41,9 @@ public: /// \returns The object type for the koid static type koid_type(j6_koid_t koid); + /// Get this object's type + inline type get_type() const { return koid_type(m_koid); } + /// Get this object's koid inline j6_koid_t koid() const { return m_koid; } diff --git a/src/kernel/objects/process.cpp b/src/kernel/objects/process.cpp index 86751a0..dcb40d7 100644 --- a/src/kernel/objects/process.cpp +++ b/src/kernel/objects/process.cpp @@ -8,7 +8,8 @@ kutil::vector process::s_processes; process::process(page_table *pml4) : kobject(kobject::type::process), - m_pml4(pml4) + m_pml4(pml4), + m_state(state::running) { s_processes.append(this); } @@ -21,6 +22,12 @@ process::~process() void process::exit(unsigned code) { + // TODO: make this thread-safe + if (m_state != state::running) + return; + else + m_state = state::exited; + for (auto *thread : m_threads) { thread->exit(code); } @@ -53,10 +60,23 @@ process::update() } thread * -process::create_thread(uint8_t priority) +process::create_thread(uint8_t priority, bool user) { thread *th = new thread(*this, priority); kassert(th, "Failed to create thread!"); + + if (user) { + uintptr_t stack_top = stacks_top - (m_threads.count() * stack_size); + auto *pm = page_manager::get(); + pm->map_pages( + stack_top - stack_size, + page_manager::page_count(stack_size), + true, // user stack + m_pml4); + + th->tcb()->rsp3 = stack_top; + } + m_threads.append(th); return th; } @@ -76,3 +96,36 @@ process::thread_exited(thread *th) return false; } + +j6_handle_t +process::add_handle(kobject *obj) +{ + if (!obj) + return j6_handle_invalid; + + obj->handle_retain(); + size_t len = m_handles.count(); + m_handles.append(obj); + return static_cast(len); +} + +bool +process::remove_handle(j6_handle_t handle) +{ + if (handle < m_handles.count()) { + kobject *obj = m_handles[handle]; + m_handles[handle] = nullptr; + if (obj) + obj->handle_release(); + return true; + } + return false; +} + +kobject * +process::lookup_handle(j6_handle_t handle) +{ + if (handle < m_handles.count()) + return m_handles[handle]; + return nullptr; +} diff --git a/src/kernel/objects/process.h b/src/kernel/objects/process.h index 38e8d22..f429930 100644 --- a/src/kernel/objects/process.h +++ b/src/kernel/objects/process.h @@ -9,6 +9,12 @@ class process : public kobject { public: + /// Top of memory area where thread stacks are allocated + constexpr static uintptr_t stacks_top = 0x0000800000000000; + + /// Size of userspace thread stacks + constexpr static size_t stack_size = 0x4000; + /// Constructor. /// \args pml4 Root of the process' page tables process(page_table *pml4); @@ -28,19 +34,44 @@ public: /// Create a new thread in this process /// \args priority The new thread's scheduling priority + /// \args user If true, create a userspace stack for this thread /// \returns The newly created thread object - thread * create_thread(uint8_t priorty); + thread * create_thread(uint8_t priorty, bool user = true); + + /// Start tracking an object with a handle. + /// \args obj The object this handle refers to + /// \returns The new handle for this object + j6_handle_t add_handle(kobject *obj); + + /// Stop tracking an object with a handle. + /// \args handle The handle that refers to the object + /// \returns True if the handle was removed + bool remove_handle(j6_handle_t handle); + + /// Lookup an object for a handle + /// \args handle The handle to the object + /// \returns Pointer to the object, or null if not found + kobject * lookup_handle(j6_handle_t handle); /// Inform the process of an exited thread /// \args th The thread which has exited /// \returns True if this thread ending has ended the process bool thread_exited(thread *th); + /// Create the special kernel process that owns kernel tasks + /// \arg pml4 The kernel-only pml4 + /// \arg idle_rsp The idle thread's rsp + static process * create_kernel_process(page_table *pml4, uintptr_t idle_rsp); + private: uint32_t m_return_code; page_table *m_pml4; kutil::vector m_threads; + kutil::vector m_handles; + + enum class state : uint8_t { running, exited }; + state m_state; static kutil::vector s_processes; }; diff --git a/src/kernel/objects/thread.cpp b/src/kernel/objects/thread.cpp index 6b9e48f..a31266a 100644 --- a/src/kernel/objects/thread.cpp +++ b/src/kernel/objects/thread.cpp @@ -1,11 +1,13 @@ #include "j6/signals.h" +#include "log.h" #include "objects/thread.h" #include "objects/process.h" #include "scheduler.h" +extern "C" void kernel_to_user_trampoline(); static constexpr j6_signal_t thread_default_signals = 0; -thread::thread(process &parent, uint8_t pri) : +thread::thread(process &parent, uint8_t pri, bool user) : kobject(kobject::type::thread, thread_default_signals), m_parent(parent), m_state(state::loading), @@ -16,6 +18,22 @@ thread::thread(process &parent, uint8_t pri) : TCB *tcbp = tcb(); tcbp->pml4 = parent.pml4(); tcbp->priority = pri; + setup_kernel_stack(); + set_state(state::ready); +} + +thread::thread(process &parent, uint8_t pri, uintptr_t rsp0) : + kobject(kobject::type::thread, thread_default_signals), + m_parent(parent), + m_state(state::loading), + m_wait_type(wait_type::none), + m_wait_data(0), + m_wait_obj(0) +{ + TCB *tcbp = tcb(); + tcbp->pml4 = parent.pml4(); + tcbp->priority = pri; + tcbp->rsp0 = rsp0; set_state(state::ready); } @@ -51,7 +69,8 @@ thread::wake_on_signals(kobject *obj, j6_signal_t signals) return false; m_wait_type = wait_type::none; - m_wait_data = j6_status_ok; + m_wait_result = j6_status_ok; + m_wait_data = signals; m_wait_obj = obj->koid(); set_state(state::ready); return true; @@ -65,7 +84,8 @@ thread::wake_on_time(uint64_t now) return false; m_wait_type = wait_type::none; - m_wait_data = j6_status_ok; + m_wait_result = j6_status_ok; + m_wait_data = now; m_wait_obj = 0; set_state(state::ready); return true; @@ -75,7 +95,8 @@ void thread::wake_on_result(kobject *obj, j6_status_t result) { m_wait_type = wait_type::none; - m_wait_data = result; + m_wait_result = result; + m_wait_data = 0; m_wait_obj = obj->koid(); set_state(state::ready); } @@ -86,5 +107,77 @@ thread::exit(uint32_t code) m_return_code = code; set_state(state::exited); clear_state(state::ready); + assert_signal(j6_signal_thread_exit); } +void +thread::add_thunk_kernel(uintptr_t rip) +{ + m_tcb.rsp -= sizeof(uintptr_t) * 7; + uintptr_t *stack = reinterpret_cast(m_tcb.rsp); + + stack[6] = rip; // return rip + stack[5] = m_tcb.rsp0; // rbp + stack[4] = 0xbbbbbbbb; // rbx + stack[3] = 0x12121212; // r12 + stack[2] = 0x13131313; // r13 + stack[1] = 0x14141414; // r14 + stack[0] = 0x15151515; // r15 +} + +void +thread::add_thunk_user(uintptr_t rip) +{ + m_tcb.rsp -= sizeof(uintptr_t) * 8; + uintptr_t *stack = reinterpret_cast(m_tcb.rsp); + + stack[7] = rip; // return rip in rcx + stack[6] = m_tcb.rsp3; // rbp + stack[5] = 0xbbbbbbbb; // rbx + stack[4] = 0x00000200; // r11 sets RFLAGS + stack[3] = 0x12121212; // r12 + stack[2] = 0x13131313; // r13 + stack[1] = 0x14141414; // r14 + stack[0] = 0x15151515; // r15 + + static const uintptr_t trampoline = + reinterpret_cast(kernel_to_user_trampoline); + add_thunk_kernel(trampoline); +} + +void +thread::setup_kernel_stack() +{ + constexpr size_t initial_stack_size = 0x1000; + constexpr unsigned null_frame_entries = 2; + constexpr size_t null_frame_size = null_frame_entries * sizeof(uint64_t); + + void *stack_bottom = kutil::kalloc(initial_stack_size); + kutil::memset(stack_bottom, 0, initial_stack_size); + + log::debug(logs::memory, "Created kernel stack at %016lx size 0x%lx", + stack_bottom, initial_stack_size); + + void *stack_top = + kutil::offset_pointer(stack_bottom, + initial_stack_size - null_frame_size); + + uint64_t *null_frame = reinterpret_cast(stack_top); + for (unsigned i = 0; i < null_frame_entries; ++i) + null_frame[i] = 0; + + m_tcb.kernel_stack_size = initial_stack_size; + m_tcb.kernel_stack = reinterpret_cast(stack_bottom); + m_tcb.rsp0 = reinterpret_cast(stack_top); + m_tcb.rsp = m_tcb.rsp0; +} + +thread * +thread::create_idle_thread(process &kernel, uint8_t pri, uintptr_t rsp0) +{ + thread *idle = new thread(kernel, pri, rsp0); + idle->set_state(thread::state::constant); + log::info(logs::task, "Created idle thread as koid %llx", idle->koid()); + + return idle; +} diff --git a/src/kernel/objects/thread.h b/src/kernel/objects/thread.h index 495897a..201527f 100644 --- a/src/kernel/objects/thread.h +++ b/src/kernel/objects/thread.h @@ -92,6 +92,12 @@ public: /// \arg result Result code to return to the thread void wake_on_result(kobject *obj, j6_status_t result); + /// Get the result status code from the last blocking operation + j6_status_t get_wait_result() const { return m_wait_result; } + + /// Get the current blocking opreation's wait data + uint64_t get_wait_data() const { return m_wait_data; } + inline bool has_state(state s) const { return static_cast(m_state) & static_cast(s); } @@ -111,6 +117,20 @@ public: /// \arg code The return code to exit with. void exit(unsigned code); + /// Add a stack header that returns to the given address in kernel space. + /// \arg rip The address to return to, must be kernel space + void add_thunk_kernel(uintptr_t rip); + + /// Add a stack header that returns to the given address in user space. + /// \arg rip The address to return to, must be user space + void add_thunk_user(uintptr_t rip); + + /// Create the kernel idle thread + /// \arg kernel The process object that owns kernel tasks + /// \arg pri The idle thread priority value + /// \arg rsp The existing stack for the idle thread + static thread * create_idle_thread(process &kernel, uint8_t pri, uintptr_t rsp); + private: thread() = delete; thread(const thread &other) = delete; @@ -118,14 +138,24 @@ private: friend class process; /// Constructor. - /// \arg p The process which owns this thread - /// \arg pri Initial priority level of this thread - thread(process &parent, uint8_t pri); + /// \arg parent The process which owns this thread + /// \arg pri Initial priority level of this thread + /// \arg user True if this is a userspace thread + thread(process &parent, uint8_t pri, bool user = true); - process &m_parent; + /// Constructor. Used when a kernel stack already exists. + /// \arg parent The process which owns this thread + /// \arg pri Initial priority level of this thread + /// \arg rsp0 The existing kernel stack rsp + thread(process &parent, uint8_t pri, uintptr_t rsp0); + + /// Set up a new empty kernel stack for this thread. + void setup_kernel_stack(); tcb_node m_tcb; + process &m_parent; + state m_state; wait_type m_wait_type; // There should be 1 byte of padding here @@ -133,5 +163,6 @@ private: uint32_t m_return_code; uint64_t m_wait_data; + j6_status_t m_wait_result; j6_koid_t m_wait_obj; }; diff --git a/src/kernel/page_manager.cpp b/src/kernel/page_manager.cpp index 68c4b98..2edbc8d 100644 --- a/src/kernel/page_manager.cpp +++ b/src/kernel/page_manager.cpp @@ -60,13 +60,6 @@ page_manager::create_process_map() for (unsigned i = pml4e_kernel; i < table_entries; ++i) table->entries[i] = m_kernel_pml4->entries[i]; - // Create the initial user stack - map_pages( - memory::initial_stack - (memory::initial_stack_pages * frame_size), - memory::initial_stack_pages, - true, // This is the ring3 stack, user = true - table); - return table; } diff --git a/src/kernel/scheduler.cpp b/src/kernel/scheduler.cpp index 87e2960..1c0fa56 100644 --- a/src/kernel/scheduler.cpp +++ b/src/kernel/scheduler.cpp @@ -16,8 +16,6 @@ #include "elf/elf.h" #include "kutil/assert.h" -using memory::initial_stack; - scheduler scheduler::s_instance(nullptr); const uint64_t rflags_noint = 0x002; @@ -30,57 +28,6 @@ extern "C" { extern uint64_t idle_stack_end; -/// Set up a new empty kernel stack for this thread. Sets rsp0 on the -/// TCB object, but also returns it. -/// \returns The new rsp0 as a pointer -static void * -setup_kernel_stack(TCB *tcb) -{ - constexpr size_t initial_stack_size = 0x1000; - constexpr unsigned null_frame_entries = 2; - constexpr size_t null_frame_size = null_frame_entries * sizeof(uint64_t); - - void *stack_bottom = kutil::kalloc(initial_stack_size); - kutil::memset(stack_bottom, 0, initial_stack_size); - - log::debug(logs::memory, "Created kernel stack at %016lx size 0x%lx", - stack_bottom, initial_stack_size); - - void *stack_top = - kutil::offset_pointer(stack_bottom, - initial_stack_size - null_frame_size); - - uint64_t *null_frame = reinterpret_cast(stack_top); - for (unsigned i = 0; i < null_frame_entries; ++i) - null_frame[i] = 0; - - tcb->kernel_stack_size = initial_stack_size; - tcb->kernel_stack = reinterpret_cast(stack_bottom); - tcb->rsp0 = reinterpret_cast(stack_top); - tcb->rsp = tcb->rsp0; - - return stack_top; -} - -/// Initialize this process' kenrel stack with a fake return segment for -/// returning out of task_switch. -/// \arg tcb TCB of the thread to modify -/// \arg rip The rip to return to -static void -add_fake_task_return(TCB *tcb, uintptr_t rip) -{ - tcb->rsp -= sizeof(uintptr_t) * 7; - uintptr_t *stack = reinterpret_cast(tcb->rsp); - - stack[6] = rip; // return rip - stack[5] = tcb->rsp0; // rbp - stack[4] = 0xbbbbbbbb; // rbx - stack[3] = 0x12121212; // r12 - stack[2] = 0x13131313; // r13 - stack[1] = 0x14141414; // r14 - stack[0] = 0x15151515; // r15 -} - scheduler::scheduler(lapic *apic) : m_apic(apic), m_next_pid(1), @@ -88,25 +35,24 @@ scheduler::scheduler(lapic *apic) : m_last_promotion(0) { page_table *pml4 = page_manager::get_pml4(); - m_kernel_process = new process(pml4); - thread *idle = m_kernel_process->create_thread(max_priority); + process *kp = new process(pml4); + m_kernel_process = kp; - auto *tcb = idle->tcb(); + log::debug(logs::task, "Kernel process koid %llx", kp->koid()); + + thread *idle = thread::create_idle_thread(*kp, max_priority, + reinterpret_cast(&idle_stack_end)); log::debug(logs::task, "Idle thread koid %llx", idle->koid()); - // The kernel idle task, also the thread we're in now - tcb->rsp = 0; // This will get set when we switch away - tcb->rsp3 = 0; // Never used for the idle task - tcb->rsp0 = reinterpret_cast(&idle_stack_end); - - idle->set_state(thread::state::constant); - + auto *tcb = idle->tcb(); m_runlists[max_priority].push_back(tcb); m_current = tcb; bsp_cpu_data.rsp0 = tcb->rsp0; bsp_cpu_data.tcb = tcb; + bsp_cpu_data.p = kp; + bsp_cpu_data.t = idle; } uintptr_t @@ -171,10 +117,10 @@ load_process_image(const void *image_start, size_t bytes, TCB *tcb) } thread * -scheduler::create_process(page_table *pml4) +scheduler::create_process(page_table *pml4, bool user) { process *p = new process(pml4); - thread *th = p->create_thread(default_priority); + thread *th = p->create_thread(default_priority, user); auto *tcb = th->tcb(); tcb->time_left = quantum(default_priority) + startup_bonus; @@ -198,12 +144,11 @@ scheduler::load_process(const char *name, const void *data, size_t size) // Set up the page tables - this also allocates an initial user stack page_table *pml4 = page_manager::get()->create_process_map(); - thread* th = create_process(pml4); + thread* th = create_process(pml4, true); auto *tcb = th->tcb(); // Create an initial kernel stack space - void *sp0 = setup_kernel_stack(tcb); - uintptr_t *stack = reinterpret_cast(sp0) - 7; + uintptr_t *stack = reinterpret_cast(tcb->rsp0) - 7; // Pass args to ramdisk_process_loader on the stack stack[0] = reinterpret_cast(data); @@ -211,16 +156,15 @@ scheduler::load_process(const char *name, const void *data, size_t size) stack[2] = reinterpret_cast(tcb); tcb->rsp = reinterpret_cast(stack); - add_fake_task_return(tcb, - reinterpret_cast(ramdisk_process_loader)); + th->add_thunk_kernel(reinterpret_cast(ramdisk_process_loader)); // Arguments for iret - rip will be pushed on before these stack[3] = cs; stack[4] = rflags_int; - stack[5] = initial_stack; + stack[5] = process::stacks_top; stack[6] = ss; - tcb->rsp3 = initial_stack; + tcb->rsp3 = process::stacks_top; m_runlists[default_priority].push_back(tcb); @@ -234,16 +178,13 @@ void scheduler::create_kernel_task(void (*task)(), uint8_t priority, bool constant) { page_table *pml4 = page_manager::get()->get_kernel_pml4(); - thread *th = create_process(pml4); + thread *th = create_process(pml4, false); auto *tcb = th->tcb(); uint16_t kcs = (1 << 3) | 0; // Kernel CS is GDT entry 1, ring 0 uint16_t kss = (2 << 3) | 0; // Kernel SS is GDT entry 2, ring 0 - // Create an initial kernel stack space - setup_kernel_stack(tcb); - add_fake_task_return(tcb, - reinterpret_cast(task)); + th->add_thunk_kernel(reinterpret_cast(task)); tcb->priority = priority; tcb->pml4 = page_manager::get()->get_kernel_pml4(); @@ -389,32 +330,13 @@ scheduler::schedule() if (next != m_current) { m_current = next; + bsp_cpu_data.t = thread::from_tcb(m_current); + bsp_cpu_data.p = &th->parent(); + thread *next_thread = thread::from_tcb(m_current); + + log::debug(logs::task, "Scheduler switching threads %llx->%llx, priority %d time left %d @ %lld.", + th->koid(), next_thread->koid(), m_current->priority, m_current->time_left, m_clock); + task_switch(m_current); - - log::debug(logs::task, "Scheduler switched to thread %llx, priority %d time left %d @ %lld.", - th->koid(), m_current->priority, m_current->time_left, m_clock); } } - -/* -process_node * -scheduler::get_process_by_id(uint32_t pid) -{ - // TODO: this needs to be a hash map - for (auto *proc : m_blocked) { - if (proc->pid == pid) return proc; - } - - for (int i = 0; i < num_priorities; ++i) { - for (auto *proc : m_runlists[i]) { - if (proc->pid == pid) return proc; - } - } - - for (auto *proc : m_exited) { - if (proc->pid == pid) return proc; - } - - return nullptr; -} -*/ diff --git a/src/kernel/scheduler.h b/src/kernel/scheduler.h index 6b1e960..efceeff 100644 --- a/src/kernel/scheduler.h +++ b/src/kernel/scheduler.h @@ -72,12 +72,7 @@ public: /// \returns A pointer to the current thread's TCB inline TCB * current() { return m_current; } - /* - /// Look up a process by its PID - /// \arg pid The requested PID - /// \returns The process matching that PID, or nullptr - tcb_node * get_process_by_id(uint32_t pid); - */ + inline void add_thread(TCB *t) { m_runlists[t->priority].push_back(static_cast(t)); } /// Get a reference to the system scheduler /// \returns A reference to the global system scheduler @@ -92,8 +87,9 @@ private: /// Create a new process object. This process will have its pid /// set but nothing else. /// \arg pml4 The root page table of the process + /// \arg user True if this thread will enter userspace /// \returns The new process' main thread - thread * create_process(page_table *pml4); + thread * create_process(page_table *pml4, bool user); void prune(uint64_t now); void check_promotions(uint64_t now); @@ -114,3 +110,4 @@ private: static scheduler s_instance; }; + diff --git a/src/kernel/syscalls.inc b/src/kernel/syscalls.inc index 892ed5b..6a5074e 100644 --- a/src/kernel/syscalls.inc +++ b/src/kernel/syscalls.inc @@ -1,8 +1,14 @@ -SYSCALL(0x00, object_noop, void) -SYSCALL(0x01, object_wait, j6_handle_t, j6_signal_t, j6_signal_t *) +SYSCALL(0x00, system_log, const char *) +SYSCALL(0x01, system_noop, void) + +SYSCALL(0x09, object_wait, j6_handle_t, j6_signal_t, j6_signal_t *) SYSCALL(0x10, process_koid, j6_koid_t *) SYSCALL(0x11, process_exit, int64_t) -SYSCALL(0x12, process_log, const char *) -SYSCALL(0x13, process_pause, void) -SYSCALL(0x14, process_sleep, uint64_t) + +SYSCALL(0x18, thread_koid, j6_koid_t *) +SYSCALL(0x19, thread_create, void *, j6_handle_t *) +SYSCALL(0x1a, thread_exit, int64_t) +SYSCALL(0x1b, thread_pause, void) +SYSCALL(0x1c, thread_sleep, uint64_t) + diff --git a/src/kernel/syscalls/object.cpp b/src/kernel/syscalls/object.cpp index 73cc12d..87ce733 100644 --- a/src/kernel/syscalls/object.cpp +++ b/src/kernel/syscalls/object.cpp @@ -2,24 +2,32 @@ #include "j6/types.h" #include "log.h" +#include "objects/process.h" +#include "objects/thread.h" #include "scheduler.h" namespace syscalls { j6_status_t -object_noop() +object_wait(j6_handle_t handle, j6_signal_t mask, j6_signal_t *sigs) { - auto &s = scheduler::get(); - TCB *tcb = s.current(); - thread *th = thread::from_tcb(tcb); - log::debug(logs::syscall, "Thread %llx called noop syscall.", th->koid()); - return j6_status_ok; -} + scheduler &s = scheduler::get(); + thread *th = thread::from_tcb(s.current()); + process &p = th->parent(); -j6_status_t -object_wait(j6_handle_t, j6_signal_t, j6_signal_t*) -{ - return j6_err_nyi; + kobject *obj = p.lookup_handle(handle); + if (!obj) + return j6_err_invalid_arg; + + obj->add_blocked_thread(th); + th->wait_on_signals(obj, mask); + s.schedule(); + + j6_status_t result = th->get_wait_result(); + if (result == j6_status_ok) { + *sigs = th->get_wait_data(); + } + return result; } } // namespace syscalls diff --git a/src/kernel/syscalls/process.cpp b/src/kernel/syscalls/process.cpp index d58a06d..5be2499 100644 --- a/src/kernel/syscalls/process.cpp +++ b/src/kernel/syscalls/process.cpp @@ -1,28 +1,13 @@ #include "j6/errors.h" #include "j6/types.h" -#include "objects/process.h" - #include "log.h" +#include "objects/process.h" +#include "objects/thread.h" #include "scheduler.h" namespace syscalls { -j6_status_t -process_exit(int64_t status) -{ - auto &s = scheduler::get(); - TCB *tcb = s.current(); - thread *th = thread::from_tcb(tcb); - log::debug(logs::syscall, "Thread %llx exiting with code %d", th->koid(), status); - - th->exit(status); - s.schedule(); - - log::error(logs::syscall, "returned to exit syscall"); - return j6_err_unexpected; -} - j6_status_t process_koid(j6_koid_t *koid) { @@ -38,41 +23,19 @@ process_koid(j6_koid_t *koid) } j6_status_t -process_log(const char *message) -{ - if (message == nullptr) { - return j6_err_invalid_arg; - } - - auto &s = scheduler::get(); - TCB *tcb = s.current(); - thread *th = thread::from_tcb(tcb); - log::info(logs::syscall, "Message[%llx]: %s", th->koid(), message); - return j6_status_ok; -} - -j6_status_t -process_pause() +process_exit(int64_t status) { auto &s = scheduler::get(); TCB *tcb = s.current(); - thread *th = thread::from_tcb(tcb); - th->wait_on_signals(th, -1ull); + process &p = thread::from_tcb(tcb)->parent(); + + log::debug(logs::syscall, "Process %llx exiting with code %d", p.koid(), status); + + p.exit(status); s.schedule(); - return j6_status_ok; -} -j6_status_t -process_sleep(uint64_t til) -{ - auto &s = scheduler::get(); - TCB *tcb = s.current(); - thread *th = thread::from_tcb(tcb); - log::debug(logs::syscall, "Thread %llx sleeping until %llu", th->koid(), til); - - th->wait_on_time(til); - s.schedule(); - return j6_status_ok; + log::error(logs::syscall, "returned to exit syscall"); + return j6_err_unexpected; } } // namespace syscalls diff --git a/src/kernel/syscalls/system.cpp b/src/kernel/syscalls/system.cpp new file mode 100644 index 0000000..4d01b46 --- /dev/null +++ b/src/kernel/syscalls/system.cpp @@ -0,0 +1,33 @@ +#include "j6/errors.h" +#include "j6/types.h" + +#include "log.h" +#include "scheduler.h" + +namespace syscalls { + +j6_status_t +system_log(const char *message) +{ + if (message == nullptr) { + return j6_err_invalid_arg; + } + + auto &s = scheduler::get(); + TCB *tcb = s.current(); + thread *th = thread::from_tcb(tcb); + log::info(logs::syscall, "Message[%llx]: %s", th->koid(), message); + return j6_status_ok; +} + +j6_status_t +system_noop() +{ + auto &s = scheduler::get(); + TCB *tcb = s.current(); + thread *th = thread::from_tcb(tcb); + log::debug(logs::syscall, "Thread %llx called noop syscall.", th->koid()); + return j6_status_ok; +} + +} // namespace syscalls diff --git a/src/kernel/syscalls/thread.cpp b/src/kernel/syscalls/thread.cpp new file mode 100644 index 0000000..9818be8 --- /dev/null +++ b/src/kernel/syscalls/thread.cpp @@ -0,0 +1,80 @@ +#include "j6/errors.h" +#include "j6/types.h" + +#include "log.h" +#include "objects/process.h" +#include "scheduler.h" + +namespace syscalls { + +j6_status_t +thread_koid(j6_koid_t *koid) +{ + if (koid == nullptr) { + return j6_err_invalid_arg; + } + + TCB *tcb = scheduler::get().current(); + *koid = thread::from_tcb(tcb)->koid(); + return j6_status_ok; +} + +j6_status_t +thread_create(void *rip, j6_handle_t *handle) +{ + scheduler &s = scheduler::get(); + TCB *tcb = s.current(); + thread *parent = thread::from_tcb(tcb); + process &p = parent->parent(); + + thread *child = p.create_thread(scheduler::default_priority); + child->add_thunk_user(reinterpret_cast(rip)); + *handle = p.add_handle(child); + s.add_thread(child->tcb()); + + log::debug(logs::syscall, "Thread %llx spawned new thread %llx, handle %d", + parent->koid(), child->koid(), *handle); + + return j6_status_ok; +} + +j6_status_t +thread_exit(int64_t status) +{ + auto &s = scheduler::get(); + TCB *tcb = s.current(); + thread *th = thread::from_tcb(tcb); + log::debug(logs::syscall, "Thread %llx exiting with code %d", th->koid(), status); + + th->exit(status); + s.schedule(); + + log::error(logs::syscall, "returned to exit syscall"); + return j6_err_unexpected; +} + +j6_status_t +thread_pause() +{ + auto &s = scheduler::get(); + TCB *tcb = s.current(); + thread *th = thread::from_tcb(tcb); + th->wait_on_signals(th, -1ull); + s.schedule(); + return j6_status_ok; +} + +j6_status_t +thread_sleep(uint64_t til) +{ + auto &s = scheduler::get(); + TCB *tcb = s.current(); + thread *th = thread::from_tcb(tcb); + log::debug(logs::syscall, "Thread %llx sleeping until %llu", th->koid(), til); + + th->wait_on_time(til); + s.schedule(); + return j6_status_ok; +} + +} // namespace syscalls diff --git a/src/kernel/task.s b/src/kernel/task.s index 84794df..f192212 100644 --- a/src/kernel/task.s +++ b/src/kernel/task.s @@ -24,20 +24,20 @@ task_switch: ; Install next task's TCB mov [gs:CPU_DATA.tcb], rdi ; rdi: next TCB (function param) mov rsp, [rdi + TCB.rsp] ; next task's stack pointer - mov rax, 0x0000007fffffffff + mov rax, 0x00003fffffffffff and rax, [rdi + TCB.pml4] ; rax: next task's pml4 (phys portion of address) ; Update syscall/interrupt rsp mov rcx, [rdi + TCB.rsp0] ; rcx: top of next task's kernel stack mov [gs:CPU_DATA.rsp0], rcx + lea rdx, [rel g_tss] ; rdx: address of TSS + mov [rdx + TSS.rsp0], rcx + ; Update saved user rsp mov rcx, [rdi + TCB.rsp3] ; rcx: new task's saved user rsp mov [gs:CPU_DATA.rsp3], rcx - lea rdx, [rel g_tss] ; rdx: address of TSS - mov [rdx + TSS.rsp0], rcx - ; check if we need to update CR3 mov rdx, cr3 ; rdx: old CR3 cmp rax, rdx @@ -56,8 +56,7 @@ task_switch: extern syscall_handler_prelude.return -global task_fork_return_thunk -task_fork_return_thunk: - mov rax, 0 +global kernel_to_user_trampoline +kernel_to_user_trampoline: jmp syscall_handler_prelude.return