diff --git a/modules.yaml b/modules.yaml index 75bc913..c98abbc 100644 --- a/modules.yaml +++ b/modules.yaml @@ -29,7 +29,6 @@ modules: - src/kernel/interrupts.cpp - src/kernel/interrupts.s - src/kernel/io.cpp - - src/kernel/loader.s - src/kernel/log.cpp - src/kernel/main.cpp - src/kernel/memory_bootstrap.cpp @@ -314,6 +313,7 @@ modules: - src/tests/main.cpp - src/tests/map.cpp - src/tests/vector.cpp + overlays: - url: https://f000.backblazeb2.com/file/jsix-os/sysroot-llvm8-20190706.tar.bz2 path: sysroot diff --git a/src/kernel/loader.s b/src/kernel/loader.s deleted file mode 100644 index c96793c..0000000 --- a/src/kernel/loader.s +++ /dev/null @@ -1,20 +0,0 @@ -%include "push_all.inc" - -extern load_process_image - -global preloaded_process_init -preloaded_process_init: - - ; create_process already pushed the arguments for load_process_image and - ; the following iretq onto the stack for us - - pop rdi ; a pointer to the program descriptor - call load_process_image - - ; user rsp is now in rax, put it in the right place for iret - mov [rsp + 0x18], rax - - ; the entrypoint should already be on the stack - swapgs - iretq - diff --git a/src/kernel/main.cpp b/src/kernel/main.cpp index 7c049d3..2347afe 100644 --- a/src/kernel/main.cpp +++ b/src/kernel/main.cpp @@ -35,12 +35,13 @@ extern "C" { extern void __kernel_assert(const char *, unsigned, const char *); +using namespace kernel; + /// Bootstrap the memory managers. void setup_pat(); -void memory_initialize_pre_ctors(kernel::args::header &kargs); -void memory_initialize_post_ctors(kernel::args::header &kargs); - -using namespace kernel; +void memory_initialize_pre_ctors(args::header &kargs); +void memory_initialize_post_ctors(args::header &kargs); +process * load_simple_process(args::program &program); /// TODO: not this. this is awful. args::framebuffer *fb = nullptr; @@ -150,13 +151,8 @@ kernel_main(args::header *header) scheduler *sched = new scheduler(devices.get_lapic()); // Skip program 0, which is the kernel itself - for (size_t i = 1; i < header->num_programs; ++i) { - args::program *prog = memory::to_virtual(&header->programs[i]); - thread *th = sched->load_process(*prog); - if (i == 2) { - //th->set_state(thread::state::constant); - } - } + for (unsigned i = 1; i < header->num_programs; ++i) + load_simple_process(header->programs[i]); if (!has_video) sched->create_kernel_task(logger_task, scheduler::max_priority/2, true); diff --git a/src/kernel/memory_bootstrap.cpp b/src/kernel/memory_bootstrap.cpp index 5ff52e5..ed448c1 100644 --- a/src/kernel/memory_bootstrap.cpp +++ b/src/kernel/memory_bootstrap.cpp @@ -1,16 +1,20 @@ #include #include "kernel_args.h" +#include "j6/init.h" #include "kutil/assert.h" #include "kutil/heap_allocator.h" #include "kutil/no_construct.h" +#include "device_manager.h" #include "frame_allocator.h" #include "io.h" #include "log.h" #include "msr.h" #include "objects/process.h" +#include "objects/thread.h" +#include "objects/system.h" #include "objects/vm_area.h" #include "vm_space.h" @@ -19,6 +23,8 @@ using memory::kernel_max_heap; using namespace kernel; +extern "C" void initialize_main_thread(); +extern "C" uintptr_t initialize_main_user_stack(); // These objects are initialized _before_ global constructors are called, // so we don't want them to have global constructors at all, lest they @@ -178,3 +184,99 @@ setup_pat() } +process * +load_simple_process(args::program &program) +{ + using kernel::args::section_flags; + + process *p = new process; + vm_space &space = p->space(); + + for (const auto § : program.sections) { + vm_flags flags = + (bitfield_has(sect.type, section_flags::execute) ? vm_flags::exec : vm_flags::none) | + (bitfield_has(sect.type, section_flags::write) ? vm_flags::write : vm_flags::none); + + vm_area *vma = new vm_area_fixed(sect.size, flags); + space.add(sect.virt_addr, vma); + vma->commit(sect.phys_addr, 0, memory::page_count(sect.size)); + } + + uint64_t iopl = (3ull << 12); + uintptr_t trampoline = reinterpret_cast(initialize_main_thread); + + thread *main = p->create_thread(); + main->add_thunk_user(program.entrypoint, trampoline, iopl); + main->set_state(thread::state::ready); + + return p; +} + +template +inline T * push(uintptr_t &rsp, size_t size = sizeof(T)) { + rsp -= size; + T *p = reinterpret_cast(rsp); + rsp &= ~(sizeof(uint64_t)-1); // Align the stack + return p; +} + +uintptr_t +initialize_main_user_stack() +{ + process &proc = process::current(); + thread &th = thread::current(); + TCB *tcb = th.tcb(); + + const char message[] = "Hello from the kernel!"; + char *message_arg = push(tcb->rsp3, sizeof(message)); + kutil::memcpy(message_arg, message, sizeof(message)); + + extern args::framebuffer *fb; + j6_init_framebuffer *fb_desc = push(tcb->rsp3); + fb_desc->addr = fb ? reinterpret_cast(0x100000000) : nullptr; + fb_desc->size = fb ? fb->size : 0; + fb_desc->vertical = fb ? fb->vertical : 0; + fb_desc->horizontal = fb ? fb->horizontal : 0; + fb_desc->scanline = fb ? fb->scanline : 0; + fb_desc->flags = 0; + + if (fb && fb->type == kernel::args::fb_type::bgr8) + fb_desc->flags |= 1; + + unsigned n = 0; + + j6_init_value *initv = push(tcb->rsp3); + initv->type = j6_init_handle_other; + initv->handle.type = j6_object_type_system; + initv->handle.handle = proc.add_handle(&system::get()); + ++n; + + initv = push(tcb->rsp3); + initv->type = j6_init_handle_self; + initv->handle.type = j6_object_type_process; + initv->handle.handle = proc.self_handle(); + ++n; + + initv = push(tcb->rsp3); + initv->type = j6_init_handle_self; + initv->handle.type = j6_object_type_thread; + initv->handle.handle = th.self_handle(); + ++n; + + initv = push(tcb->rsp3); + initv->type = j6_init_desc_framebuffer; + initv->data = fb_desc; + ++n; + + uint64_t *initc = push(tcb->rsp3); + *initc = n; + + char **argv0 = push(tcb->rsp3); + *argv0 = message_arg; + + uint64_t *argc = push(tcb->rsp3); + *argc = 1; + + th.clear_state(thread::state::loading); + return tcb->rsp3; +} diff --git a/src/kernel/objects/process.cpp b/src/kernel/objects/process.cpp index 2ccd2aa..0702e01 100644 --- a/src/kernel/objects/process.cpp +++ b/src/kernel/objects/process.cpp @@ -106,7 +106,9 @@ process::create_thread(uint8_t priority, bool user) vm_flags::zero|vm_flags::write); m_space.add(stack_top - stack_size, vma); - th->tcb()->rsp3 = stack_top; + // Space for null frame - because the page gets zeroed on + // allocation, just pointing rsp here does the trick + th->tcb()->rsp3 = stack_top - 2 * sizeof(uint64_t); } m_threads.append(th); diff --git a/src/kernel/objects/process.h b/src/kernel/objects/process.h index c9d9c00..c02e2df 100644 --- a/src/kernel/objects/process.h +++ b/src/kernel/objects/process.h @@ -16,7 +16,7 @@ public: constexpr static uintptr_t stacks_top = 0x0000800000000000; /// Size of userspace thread stacks - constexpr static size_t stack_size = 0x4000; + constexpr static size_t stack_size = 0x4000000; // 64MiB /// Value that represents default priority constexpr static uint8_t default_priority = 0xff; diff --git a/src/kernel/objects/thread.cpp b/src/kernel/objects/thread.cpp index 524517f..163d7dd 100644 --- a/src/kernel/objects/thread.cpp +++ b/src/kernel/objects/thread.cpp @@ -149,6 +149,10 @@ thread::exit(int32_t code) void thread::add_thunk_kernel(uintptr_t rip) { + // This adds just enough values to the top of the + // kernel stack to come out of task_switch correctly + // and start executing at rip (still in kernel mode) + m_tcb.rsp -= sizeof(uintptr_t) * 7; uintptr_t *stack = reinterpret_cast(m_tcb.rsp); @@ -162,15 +166,24 @@ thread::add_thunk_kernel(uintptr_t rip) } void -thread::add_thunk_user(uintptr_t rip) +thread::add_thunk_user(uintptr_t rip3, uintptr_t rip0, uint64_t flags) { + // This sets up the stack to: + // a) come out of task_switch and return to rip0 (default is the + // kernel/user trampoline) (via add_thunk_kernel) - if this is + // changed, it needs to end up at the trampoline with the stack + // as it was + // b) come out of the kernel/user trampoline and start executing + // in user mode at rip + m_tcb.rsp -= sizeof(uintptr_t) * 8; uintptr_t *stack = reinterpret_cast(m_tcb.rsp); + flags |= 0x200; - stack[7] = rip; // return rip in rcx + stack[7] = rip3; // return rip in rcx stack[6] = m_tcb.rsp3; // rbp stack[5] = 0xbbbbbbbb; // rbx - stack[4] = 0x00000200; // r11 sets RFLAGS + stack[4] = flags; // r11 sets RFLAGS stack[3] = 0x12121212; // r12 stack[2] = 0x13131313; // r13 stack[1] = 0x14141414; // r14 @@ -178,7 +191,7 @@ thread::add_thunk_user(uintptr_t rip) static const uintptr_t trampoline = reinterpret_cast(kernel_to_user_trampoline); - add_thunk_kernel(trampoline); + add_thunk_kernel(rip0 ? rip0 : trampoline); } void diff --git a/src/kernel/objects/thread.h b/src/kernel/objects/thread.h index dbbaea4..4118f4d 100644 --- a/src/kernel/objects/thread.h +++ b/src/kernel/objects/thread.h @@ -137,9 +137,12 @@ public: /// \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); + /// Add a stack header that returns to the given address in user space + /// via a function in kernel space. + /// \arg rip3 The user space address to return to + /// \arg rip0 The kernel function to pass through, optional + /// \arg flags Extra RFLAGS values to set, optional + void add_thunk_user(uintptr_t rip3, uintptr_t rip0 = 0, uint64_t flags = 0); /// Get the handle representing this thread to its process j6_handle_t self_handle() const { return m_self_handle; } diff --git a/src/kernel/scheduler.cpp b/src/kernel/scheduler.cpp index 66317da..3b09d39 100644 --- a/src/kernel/scheduler.cpp +++ b/src/kernel/scheduler.cpp @@ -7,6 +7,7 @@ #include "console.h" #include "cpu.h" #include "debug.h" +#include "device_manager.h" #include "gdt.h" #include "interrupts.h" #include "io.h" @@ -32,11 +33,6 @@ extern kernel::args::framebuffer *fb; const uint64_t rflags_noint = 0x002; const uint64_t rflags_int = 0x202; -extern "C" { - void preloaded_process_init(); - uintptr_t load_process_image(const kernel::args::program*); -}; - extern uint64_t idle_stack_end; scheduler::scheduler(lapic *apic) : @@ -75,98 +71,13 @@ inline T * push(uintptr_t &rsp, size_t size = sizeof(T)) { return p; } -uintptr_t -load_process_image(const kernel::args::program *program) -{ - using memory::page_align_down; - using memory::page_align_up; - using kernel::args::section_flags; - - // We're now in the process space for this process, allocate memory for the - // process code and load it - process &proc = process::current(); - thread &th = thread::current(); - TCB *tcb = th.tcb(); - vm_space &space = proc.space(); - - for (const auto § : program->sections) { - vm_flags flags = - (bitfield_has(sect.type, section_flags::execute) ? vm_flags::exec : vm_flags::none) | - (bitfield_has(sect.type, section_flags::write) ? vm_flags::write : vm_flags::none); - - vm_area *vma = new vm_area_open(sect.size, space, flags); - space.add(sect.virt_addr, vma); - vma->commit(sect.phys_addr, 0, memory::page_count(sect.size)); - } - - // double zero stack sentinel - *push(tcb->rsp3) = 0; - *push(tcb->rsp3) = 0; - - const char message[] = "Hello from the kernel!"; - char *message_arg = push(tcb->rsp3, sizeof(message)); - kutil::memcpy(message_arg, message, sizeof(message)); - - j6_init_framebuffer *fb_desc = push(tcb->rsp3); - fb_desc->addr = fb ? reinterpret_cast(0x100000000) : nullptr; - fb_desc->size = fb ? fb->size : 0; - fb_desc->vertical = fb ? fb->vertical : 0; - fb_desc->horizontal = fb ? fb->horizontal : 0; - fb_desc->scanline = fb ? fb->scanline : 0; - fb_desc->flags = 0; - - if (fb && fb->type == kernel::args::fb_type::bgr8) - fb_desc->flags |= 1; - - j6_init_value *initv = push(tcb->rsp3); - initv->type = j6_init_handle_other; - initv->handle.type = j6_object_type_system; - initv->handle.handle = proc.add_handle(&system::get()); - - initv = push(tcb->rsp3); - initv->type = j6_init_handle_self; - initv->handle.type = j6_object_type_process; - initv->handle.handle = proc.self_handle(); - - initv = push(tcb->rsp3); - initv->type = j6_init_handle_self; - initv->handle.type = j6_object_type_thread; - initv->handle.handle = th.self_handle(); - - initv = push(tcb->rsp3); - initv->type = j6_init_desc_framebuffer; - initv->data = fb_desc; - - uint64_t *initc = push(tcb->rsp3); - *initc = 4; - - char **argv0 = push(tcb->rsp3); - *argv0 = message_arg; - - uint64_t *argc = push(tcb->rsp3); - *argc = 1; - - // Crazypants framebuffer part - if (fb) { - vm_area *vma = new vm_area_open(fb->size, space, - vm_flags::write|vm_flags::mmio|vm_flags::write_combine); - space.add(0x100000000, vma); - vma->commit(fb->phys_addr, 0, memory::page_count(fb->size)); - } - - th.clear_state(thread::state::loading); - return tcb->rsp3; -} - thread * scheduler::create_process(bool user) { process *p = new process; thread *th = p->create_thread(default_priority, user); - auto *tcb = th->tcb(); - tcb->time_left = quantum(default_priority); - + TCB *tcb = th->tcb(); log::debug(logs::task, "Creating thread %llx, priority %d, time slice %d", th->koid(), tcb->priority, tcb->time_left); @@ -174,45 +85,6 @@ scheduler::create_process(bool user) return th; } -thread * -scheduler::load_process(kernel::args::program &program) -{ - - uint16_t kcs = (1 << 3) | 0; // Kernel CS is GDT entry 1, ring 0 - uint16_t cs = (5 << 3) | 3; // User CS is GDT entry 5, ring 3 - - uint16_t kss = (2 << 3) | 0; // Kernel SS is GDT entry 2, ring 0 - uint16_t ss = (4 << 3) | 3; // User SS is GDT entry 4, ring 3 - - thread* th = create_process(true); - auto *tcb = th->tcb(); - - // Create an initial kernel stack space - uintptr_t *stack = reinterpret_cast(tcb->rsp0) - 6; - - // Pass args to preloaded_process_init on the stack - stack[0] = reinterpret_cast(&program); - - tcb->rsp = reinterpret_cast(stack); - th->add_thunk_kernel(reinterpret_cast(preloaded_process_init)); - - // Arguments for iret - rip will be pushed on before these - stack[1] = reinterpret_cast(program.entrypoint); - stack[2] = cs; - stack[3] = rflags_int | (3 << 12); - stack[4] = process::stacks_top; - stack[5] = ss; - - tcb->rsp3 = process::stacks_top; - - log::debug(logs::task, "Loading thread %llx pri %d", th->koid(), tcb->priority); - log::debug(logs::task, " RSP %016lx", tcb->rsp); - log::debug(logs::task, " RSP0 %016lx", tcb->rsp0); - log::debug(logs::task, " PML4 %016lx", tcb->pml4); - - return th; -} - void scheduler::create_kernel_task(void (*task)(), uint8_t priority, bool constant) { @@ -248,6 +120,14 @@ scheduler::start() m_apic->reset_timer(10); } +void +scheduler::add_thread(TCB *t) +{ + m_blocked.push_back(static_cast(t)); + t->time_left = quantum(t->priority); + +} + void scheduler::prune(uint64_t now) { // Find processes that are ready or have exited and diff --git a/src/kernel/scheduler.h b/src/kernel/scheduler.h index f762798..7c119ab 100644 --- a/src/kernel/scheduler.h +++ b/src/kernel/scheduler.h @@ -60,7 +60,7 @@ public: bool constant = false); /// Get the quantum for a given priority. - uint32_t quantum(int priority); + static uint32_t quantum(int priority); /// Start the scheduler working. This may involve starting /// timer interrupts or other preemption methods. @@ -73,7 +73,9 @@ public: /// \returns A pointer to the current thread's TCB inline TCB * current() { return m_current; } - inline void add_thread(TCB *t) { m_blocked.push_back(static_cast(t)); } + /// Start scheduling a new thread. + /// \arg t The new thread's TCB + void add_thread(TCB *t); /// Get a reference to the system scheduler /// \returns A reference to the global system scheduler diff --git a/src/kernel/syscall.s b/src/kernel/syscall.s index 659bbf4..375713d 100644 --- a/src/kernel/syscall.s +++ b/src/kernel/syscall.s @@ -12,7 +12,6 @@ extern syscall_registry extern syscall_invalid global syscall_handler_prelude -global syscall_handler_prelude.return syscall_handler_prelude: swapgs mov [gs:CPU_DATA.rsp3], rsp @@ -55,7 +54,8 @@ syscall_handler_prelude: inc qword [rel __counter_syscall_sysret] -.return: +global kernel_to_user_trampoline +kernel_to_user_trampoline: pop r15 pop r14 pop r13 diff --git a/src/kernel/task.s b/src/kernel/task.s index f192212..bc2fdfa 100644 --- a/src/kernel/task.s +++ b/src/kernel/task.s @@ -55,8 +55,14 @@ task_switch: ret -extern syscall_handler_prelude.return -global kernel_to_user_trampoline -kernel_to_user_trampoline: - jmp syscall_handler_prelude.return +extern initialize_main_user_stack +extern kernel_to_user_trampoline +global initialize_main_thread +initialize_main_thread: + call initialize_main_user_stack + ; user rsp is now in rax, put it in the right place for sysret + mov [rsp + 0x30], rax + + ; the entrypoint should already be on the stack + jmp kernel_to_user_trampoline