From ca2362f858527c2d08b108cb629f9bc13ad35d8a Mon Sep 17 00:00:00 2001 From: "Justin C. Miller" Date: Sun, 31 Mar 2019 22:49:24 -0700 Subject: [PATCH] Simplify task switches No longer using the rsp from the entry to the kernel, but instead switching rsp at task-switching time in assembly. This currently breaks fork() --- modules.yaml | 1 + src/drivers/nulldrv/main.cpp | 2 + src/drivers/nulldrv/main.s | 12 ++++ src/kernel/cpu.h | 5 ++ src/kernel/debug.cpp | 7 +- src/kernel/debug.h | 1 + src/kernel/interrupts.cpp | 45 +++++-------- src/kernel/interrupts.s | 2 - src/kernel/loader.s | 11 ++-- src/kernel/log.cpp | 13 ++-- src/kernel/log_areas.inc | 8 +-- src/kernel/page_manager.cpp | 5 +- src/kernel/process.cpp | 50 +++++++++------ src/kernel/process.h | 31 +++++---- src/kernel/scheduler.cpp | 121 ++++++++++++++++------------------- src/kernel/scheduler.h | 14 ++-- src/kernel/syscall.cpp | 41 ++++++------ src/kernel/syscall.h | 2 +- src/kernel/syscall.s | 1 - src/kernel/task.s | 85 ++++++++++++++++++++++++ src/kernel/tasking.inc | 32 +++++++++ 21 files changed, 311 insertions(+), 178 deletions(-) create mode 100644 src/kernel/task.s create mode 100644 src/kernel/tasking.inc diff --git a/modules.yaml b/modules.yaml index cfbc445..cdae6ac 100644 --- a/modules.yaml +++ b/modules.yaml @@ -41,6 +41,7 @@ modules: - src/kernel/serial.cpp - src/kernel/syscall.cpp - src/kernel/syscall.s + - src/kernel/task.s - src/kernel/crtn.s boot: diff --git a/src/drivers/nulldrv/main.cpp b/src/drivers/nulldrv/main.cpp index 54100d6..4740524 100644 --- a/src/drivers/nulldrv/main.cpp +++ b/src/drivers/nulldrv/main.cpp @@ -3,6 +3,7 @@ extern "C" { int32_t getpid(); + int32_t fork(); void sleep(uint64_t til); void debug(); @@ -14,6 +15,7 @@ int main(int argc, const char **argv) { int32_t pid = getpid(); + //int32_t child = fork(); debug(); for (int i = 1; i < 5; ++i) sleep(i*10); diff --git a/src/drivers/nulldrv/main.s b/src/drivers/nulldrv/main.s index 37785c8..03e01ea 100644 --- a/src/drivers/nulldrv/main.s +++ b/src/drivers/nulldrv/main.s @@ -39,6 +39,18 @@ sleep: pop rbp ret +global fork +fork: + push rbp + mov rbp, rsp + + mov rax, 8 + syscall ; pid left in rax + + pop rbp + ret + + global _start _start: xor rbp, rbp ; Sentinel rbp diff --git a/src/kernel/cpu.h b/src/kernel/cpu.h index 9c1d4a0..dd48549 100644 --- a/src/kernel/cpu.h +++ b/src/kernel/cpu.h @@ -2,6 +2,8 @@ #include +struct process; + struct cpu_state { uint64_t r15, r14, r13, r12, r11, r10, r9, r8; @@ -10,10 +12,13 @@ struct cpu_state uint64_t rip, cs, rflags, user_rsp, ss; }; +/// Per-cpu state data. If you change this, remember to update the assembly +/// version in 'tasking.inc' struct cpu_data { uintptr_t rsp0; uintptr_t rsp3; + process *tcb; }; extern cpu_data bsp_cpu_data; diff --git a/src/kernel/debug.cpp b/src/kernel/debug.cpp index 29fd217..1c3ba25 100644 --- a/src/kernel/debug.cpp +++ b/src/kernel/debug.cpp @@ -12,6 +12,9 @@ print_regs(const cpu_state ®s) { console *cons = console::get(); + uint64_t cr2 = 0; + __asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2)); + print_regL("rax", regs.rax); print_regM("rbx", regs.rbx); print_regR("rcx", regs.rcx); @@ -36,7 +39,9 @@ print_regs(const cpu_state ®s) print_regL("rip", regs.rip); print_regM("cr3", page_manager::get()->get_pml4()); - cons->puts("\n\n"); + print_regR("cr2", cr2); + + cons->puts("\n"); } struct frame diff --git a/src/kernel/debug.h b/src/kernel/debug.h index 9484b82..de08ffe 100644 --- a/src/kernel/debug.h +++ b/src/kernel/debug.h @@ -9,6 +9,7 @@ extern "C" { uintptr_t get_rip(); uintptr_t get_frame(int frame); uintptr_t get_gsbase(); + void _halt(); } extern size_t __counter_syscall_enter; diff --git a/src/kernel/interrupts.cpp b/src/kernel/interrupts.cpp index 2ffafcc..5db7a03 100644 --- a/src/kernel/interrupts.cpp +++ b/src/kernel/interrupts.cpp @@ -18,9 +18,9 @@ static const uint16_t PIC2 = 0xa0; extern "C" { void _halt(); - uintptr_t isr_handler(uintptr_t, cpu_state*); - uintptr_t irq_handler(uintptr_t, cpu_state*); - uintptr_t syscall_handler(uintptr_t, cpu_state); + void isr_handler(cpu_state*); + void irq_handler(cpu_state*); + void syscall_handler(cpu_state*); #define ISR(i, name) extern void name (); #define EISR(i, name) extern void name (); @@ -104,8 +104,8 @@ interrupts_init() log::info(logs::boot, "Interrupts enabled."); } -uintptr_t -isr_handler(uintptr_t return_rsp, cpu_state *regs) +void +isr_handler(cpu_state *regs) { console *cons = console::get(); @@ -193,21 +193,14 @@ isr_handler(uintptr_t return_rsp, cpu_state *regs) if (regs->errorcode & 0x08) cons->puts(" reserved"); if (regs->errorcode & 0x10) cons->puts(" ip"); cons->puts("\n"); - - uint64_t cr2 = 0; - __asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2)); - print_regL("cr2", cr2); - print_regM("rsp", regs->user_rsp); - print_regR("rip", regs->rip); - //print_stacktrace(2); + print_regs(*regs); + print_stacktrace(2); } _halt(); break; - case isr::isrTimer: { - scheduler &s = scheduler::get(); - return_rsp = s.tick(return_rsp); - } + case isr::isrTimer: + scheduler::get().tick(); break; case isr::isrLINT0: @@ -226,14 +219,13 @@ isr_handler(uintptr_t return_rsp, cpu_state *regs) _halt(); break; - case isr::isrSyscall: { - return_rsp = syscall_dispatch(return_rsp, *regs); - } + case isr::isrSyscall: + syscall_dispatch(regs); break; case isr::isrSpurious: // No EOI for the spurious interrupt - return return_rsp; + return; case isr::isrIgnore0: case isr::isrIgnore1: @@ -274,12 +266,10 @@ isr_handler(uintptr_t return_rsp, cpu_state *regs) _halt(); } *reinterpret_cast(0xffffff80fee000b0) = 0; - - return return_rsp; } -uintptr_t -irq_handler(uintptr_t return_rsp, cpu_state *regs) +void +irq_handler(cpu_state *regs) { console *cons = console::get(); uint8_t irq = get_irq(regs->interrupt); @@ -293,11 +283,10 @@ irq_handler(uintptr_t return_rsp, cpu_state *regs) } *reinterpret_cast(0xffffff80fee000b0) = 0; - return return_rsp; } -uintptr_t -syscall_handler(uintptr_t return_rsp, cpu_state regs) +void +syscall_handler(cpu_state *regs) { - return syscall_dispatch(return_rsp, regs); + syscall_dispatch(regs); } diff --git a/src/kernel/interrupts.s b/src/kernel/interrupts.s index 8bbcba0..cbb9e7b 100644 --- a/src/kernel/interrupts.s +++ b/src/kernel/interrupts.s @@ -9,7 +9,6 @@ isr_handler_prelude: mov rdi, rsp mov rsi, rsp call isr_handler - mov rsp, rax jmp isr_handler_return extern irq_handler @@ -21,7 +20,6 @@ irq_handler_prelude: mov rdi, rsp mov rsi, rsp call irq_handler - mov rsp, rax ; fall through to isr_handler_return global isr_handler_return diff --git a/src/kernel/loader.s b/src/kernel/loader.s index e9310eb..5e9031f 100644 --- a/src/kernel/loader.s +++ b/src/kernel/loader.s @@ -7,12 +7,11 @@ ramdisk_process_loader: ; create_process already pushed a cpu_state onto the stack for us, this ; acts both as the cpu_state parameter to load_process, and the saved ; state for the following iretq - ; - ; Additional parameters: - ; rdi - the address of the program image - ; rsi - the size of the program image - ; rdx - the address of this process' process structure - ; rcx - the stack pointer, which points at the cpu_state + + pop rdi ; the address of the program image + pop rsi ; the size of the program image + pop rdx ; the address of this process' process structure + pop rcx ; the cpu_state call load_process swapgs diff --git a/src/kernel/log.cpp b/src/kernel/log.cpp index 666d30c..7f6543c 100644 --- a/src/kernel/log.cpp +++ b/src/kernel/log.cpp @@ -2,6 +2,7 @@ #include "kutil/memory.h" #include "console.h" #include "log.h" +#include "scheduler.h" namespace logs { @@ -21,7 +22,7 @@ output_log(log::area_t area, log::level severity, const char *message) { auto *cons = console::get(); cons->set_color(level_colors[static_cast(severity)]); - cons->printf("%9s %8s: %s\n", + cons->printf("%7s %5s: %s\n", g_logger.area_name(area), g_logger.level_name(severity), message); @@ -35,21 +36,23 @@ logger_task() auto *ent = reinterpret_cast(buffer); auto *cons = console::get(); - g_logger.set_immediate(nullptr); + //g_logger.set_immediate(nullptr); log::info(logs::task, "Starting kernel logger task"); + scheduler &s = scheduler::get(); + while (true) { - __asm__ ("cli"); if(g_logger.get_entry(buffer, sizeof(buffer))) { buffer[ent->bytes] = 0; cons->set_color(level_colors[static_cast(ent->severity)]); - cons->printf("%9s %8s: %s\n", + cons->printf("%7s %5s: %s\n", g_logger.area_name(ent->area), g_logger.level_name(ent->severity), ent->message); cons->set_color(); + } else { + s.schedule(); } - __asm__ ("sti"); } } diff --git a/src/kernel/log_areas.inc b/src/kernel/log_areas.inc index ec3b215..dd49215 100644 --- a/src/kernel/log_areas.inc +++ b/src/kernel/log_areas.inc @@ -1,8 +1,8 @@ LOG(apic, info); -LOG(device, debug); -LOG(paging, info); -LOG(driver, debug); +LOG(device, info); +LOG(paging, debug); +LOG(driver, info); LOG(memory, debug); -LOG(fs, debug); +LOG(fs, info); LOG(task, debug); LOG(boot, debug); diff --git a/src/kernel/page_manager.cpp b/src/kernel/page_manager.cpp index c8e8786..6701c81 100644 --- a/src/kernel/page_manager.cpp +++ b/src/kernel/page_manager.cpp @@ -150,7 +150,7 @@ page_manager::delete_process_map(page_table *pml4) void page_manager::map_offset_pointer(void **pointer, size_t length) { - log::info(logs::paging, "Mapping offset pointer region at %016lx size 0x%lx", *pointer, length); + log::debug(logs::paging, "Mapping offset pointer region at %016lx size 0x%lx", *pointer, length); *pointer = kutil::offset_pointer(*pointer, page_offset); } @@ -185,6 +185,7 @@ page_manager::get_table_page() free_page_header *page = m_page_cache; m_page_cache = page->next; + return reinterpret_cast(page); } @@ -294,8 +295,10 @@ page_manager::check_needs_page(page_table *table, unsigned index, bool user) void page_manager::page_in(page_table *pml4, uintptr_t phys_addr, uintptr_t virt_addr, size_t count, bool user, bool large) { + /* log::debug(logs::paging, "page_in for table %016lx p:%016lx v:%016lx c:%4d u:%d l:%d", pml4, phys_addr, virt_addr, count, user, large); + */ page_table_indices idx{virt_addr}; page_table *tables[4] = {pml4, nullptr, nullptr, nullptr}; diff --git a/src/kernel/process.cpp b/src/kernel/process.cpp index cc91a38..5db1981 100644 --- a/src/kernel/process.cpp +++ b/src/kernel/process.cpp @@ -1,4 +1,5 @@ #include "cpu.h" +#include "debug.h" #include "log.h" #include "process.h" #include "scheduler.h" @@ -12,7 +13,7 @@ process::exit(uint32_t code) } pid_t -process::fork(uintptr_t in_rsp) +process::fork(cpu_state *regs) { auto &sched = scheduler::get(); auto *child = sched.create_process(); @@ -25,40 +26,49 @@ process::fork(uintptr_t in_rsp) sched.m_runlists[child->priority].push_back(child); - child->rsp = in_rsp; - child->pml4 = page_manager::get()->copy_table(pml4); kassert(child->pml4, "process::fork() got null pml4"); - child->setup_kernel_stack(kernel_stack_size, kernel_stack); - child->rsp = child->kernel_stack + (in_rsp - kernel_stack); - log::debug(logs::task, "Copied process %d to %d, new PML4 %016lx.", pid, child->pid, child->pml4); - log::debug(logs::task, " copied stack %016lx to %016lx, rsp %016lx to %016lx.", - kernel_stack, child->kernel_stack, in_rsp, child->rsp); + log::debug(logs::task, " copied stack %016lx to %016lx, rsp %016lx.", + kernel_stack, child->kernel_stack, child->rsp); - // Add in the faked fork return value - cpu_state *regs = reinterpret_cast(child->rsp); - regs->rax = 0; + child->setup_kernel_stack(); + task_fork(child); // Both parent and child will return from this + + if (bsp_cpu_data.tcb->pid == child->pid) { + return 0; + } return child->pid; } void * -process::setup_kernel_stack(size_t size, uintptr_t orig) +process::setup_kernel_stack() { - void *stack0 = kutil::malloc(size); + constexpr unsigned null_frame_entries = 2; + constexpr size_t null_frame_size = null_frame_entries * sizeof(uint64_t); - if (orig) - kutil::memcpy(stack0, reinterpret_cast(orig), size); - else - kutil::memset(stack0, 0, size); + void *stack_bottom = kutil::malloc(initial_stack_size); + kutil::memset(stack_bottom, 0, initial_stack_size); - kernel_stack_size = size; - kernel_stack = reinterpret_cast(stack0); + log::debug(logs::memory, "Created kernel stack at %016lx size 0x%lx", + stack_bottom, initial_stack_size); - return stack0; + 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; + + kernel_stack_size = initial_stack_size; + kernel_stack = reinterpret_cast(stack_bottom); + rsp0 = reinterpret_cast(stack_top); + + return stack_top; } bool diff --git a/src/kernel/process.h b/src/kernel/process.h index 6911da2..7c5e1ef 100644 --- a/src/kernel/process.h +++ b/src/kernel/process.h @@ -8,6 +8,7 @@ #include "page_manager.h" typedef int32_t pid_t; +struct cpu_state; enum class process_flags : uint32_t @@ -32,9 +33,19 @@ enum class process_wait : uint8_t receive }; -/// A process +/// A process. +/// struct process { + static const size_t initial_stack_size = 0x1000; + + // Fields used by assembly routines go first. If you change any of these, + // be sure to change the assembly definitions in 'tasking.inc' + uintptr_t rsp; + uintptr_t rsp0; + page_table *pml4; + // End of assembly fields + pid_t pid; pid_t ppid; @@ -51,9 +62,6 @@ struct process uint32_t reserved1; - uintptr_t rsp; - page_table *pml4; - uintptr_t kernel_stack; size_t kernel_stack_size; @@ -62,10 +70,10 @@ struct process void exit(unsigned code); /// Copy this process. - /// \arg in_rsp The RSP of the calling process + /// \arg regs The saved state from the fork syscall /// \returns Returns the child's pid to the parent, and /// 0 to the child. - pid_t fork(uint64_t in_rsp); + pid_t fork(cpu_state *regs); /// Unready this process until it gets a signal /// \arg sigmask A bitfield of signals to wake on @@ -122,13 +130,10 @@ struct process private: friend class scheduler; - /// Set up a new kernel stack for this process, optionally copying the - /// given stack. Sets the kernel stack on the process object, but also - /// returns it. - /// \arg size Size of the stack to allocate - /// \arg orig Address of a stack to copy, or 0 for no copying. - /// \returns The address of the new stack as a pointer - void * setup_kernel_stack(size_t size, uintptr_t orig); + /// Set up a new empty kernel stack for this process. Sets rsp0 on this + /// process object, but also returns it. + /// \returns The new rsp0 as a pointer + void * setup_kernel_stack(); }; using process_list = kutil::linked_list; diff --git a/src/kernel/scheduler.cpp b/src/kernel/scheduler.cpp index a494984..3341b60 100644 --- a/src/kernel/scheduler.cpp +++ b/src/kernel/scheduler.cpp @@ -18,7 +18,6 @@ using memory::initial_stack; scheduler scheduler::s_instance(nullptr); -const int stack_size = 0x1000; const uint64_t rflags_noint = 0x002; const uint64_t rflags_int = 0x202; @@ -32,6 +31,7 @@ scheduler::scheduler(lapic *apic) : m_next_pid(1) { auto *idle = m_process_allocator.pop(); + idle->setup_kernel_stack(); uint8_t last_pri = num_priorities - 1; @@ -49,6 +49,9 @@ scheduler::scheduler(lapic *apic) : m_runlists[last_pri].push_back(idle); m_current = idle; + + bsp_cpu_data.rsp0 = idle->rsp0; + bsp_cpu_data.tcb = idle; } void @@ -120,6 +123,24 @@ scheduler::create_process(pid_t pid) return proc; } +static uintptr_t +add_fake_stack_return(uintptr_t rsp, uintptr_t rbp, uintptr_t rip) +{ + // Initialize a new empty stack with a fake return segment + // for returning out of task_switch + rsp -= sizeof(uintptr_t) * 7; + uintptr_t *stack = reinterpret_cast(rsp); + + stack[0] = rbp; // rbp + stack[1] = 0xbbbbbbbb; // rbx + stack[2] = 0x12121212; // r12 + stack[3] = 0x13131313; // r13 + stack[4] = 0x14141414; // r14 + stack[5] = 0x15151515; // r15 + stack[6] = rip; // return rip + return rsp; +} + void scheduler::load_process(const char *name, const void *data, size_t size) { @@ -132,15 +153,10 @@ scheduler::load_process(const char *name, const void *data, size_t size) uint16_t ss = (4 << 3) | 3; // User SS is GDT entry 4, ring 3 // Set up the page tables - this also allocates an initial user stack - page_table *pml4 = page_manager::get()->create_process_map(); - - // Create a one-page kernel stack space - void *stack0 = proc->setup_kernel_stack(stack_size, 0); - - // Stack grows down, point to the end, resere space for initial null frame - static const size_t null_frame = sizeof(uint64_t); - void *sp0 = kutil::offset_pointer(stack0, stack_size - null_frame); + proc->pml4 = page_manager::get()->create_process_map(); + // Create an initial kernel stack space + void *sp0 = proc->setup_kernel_stack(); cpu_state *state = reinterpret_cast(sp0) - 1; // Highest state in the stack is the process' kernel stack for the loader @@ -151,24 +167,18 @@ scheduler::load_process(const char *name, const void *data, size_t size) state->rip = 0; // to be filled by the loader state->user_rsp = initial_stack; - // Next state in the stack is the loader's kernel stack. The scheduler will - // iret to this which will kick off the loading: - cpu_state *loader_state = reinterpret_cast(sp0) - 2; + // Pass args to ramdisk_process_loader on the stack + uintptr_t *stack = reinterpret_cast(state) - 4; + stack[0] = reinterpret_cast(data); + stack[1] = reinterpret_cast(size); + stack[2] = reinterpret_cast(proc); + stack[3] = reinterpret_cast(state); - loader_state->ss = kss; - loader_state->cs = kcs; - loader_state->rflags = rflags_noint; - loader_state->rip = reinterpret_cast(ramdisk_process_loader); - loader_state->user_rsp = reinterpret_cast(state); + proc->rsp = add_fake_stack_return( + reinterpret_cast(stack), + proc->rsp0, + reinterpret_cast(ramdisk_process_loader)); - // Set up the registers to have the arguments to the load_process call - loader_state->rdi = reinterpret_cast(data); // arg 1 - loader_state->rsi = size; // arg 2 - loader_state->rdx = reinterpret_cast(proc); // arg 3 - loader_state->rcx = loader_state->user_rsp; // arg 4 - - proc->rsp = reinterpret_cast(loader_state); - proc->pml4 = pml4; proc->quanta = process_quanta; proc->flags = process_flags::running | @@ -177,10 +187,9 @@ scheduler::load_process(const char *name, const void *data, size_t size) m_runlists[default_priority].push_back(proc); - log::debug(logs::task, "Creating process %s: pid %d pri %d", name, proc->pid, proc->priority); log::debug(logs::task, " RSP0 %016lx", state); - log::debug(logs::task, " PML4 %016lx", pml4); + log::debug(logs::task, " PML4 %016lx", proc->pml4); } void @@ -191,24 +200,12 @@ scheduler::create_kernel_task(pid_t pid, void (*task)()) 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 a one-page kernel stack space - void *stack0 = proc->setup_kernel_stack(stack_size, 0); + // Create an initial kernel stack space + proc->setup_kernel_stack(); + proc->rsp = add_fake_stack_return( + proc->rsp0, proc->rsp0, + reinterpret_cast(task)); - // Stack grows down, point to the end, resere space for initial null frame - static const size_t null_frame = sizeof(uint64_t); - void *sp0 = kutil::offset_pointer(stack0, stack_size - null_frame); - - cpu_state *state = reinterpret_cast(sp0) - 1; - - // Highest state in the stack is the process' kernel stack for the loader - // to iret to: - state->ss = kss; - state->cs = kcs; - state->rflags = rflags_int; - state->rip = reinterpret_cast(task); - state->user_rsp = reinterpret_cast(state); - - proc->rsp = reinterpret_cast(state); proc->pml4 = page_manager::get()->get_kernel_pml4(); proc->quanta = process_quanta; proc->flags = @@ -218,7 +215,8 @@ scheduler::create_kernel_task(pid_t pid, void (*task)()) m_runlists[default_priority].push_back(proc); log::debug(logs::task, "Creating kernel task: pid %d pri %d", proc->pid, proc->priority); - log::debug(logs::task, " RSP0 %016lx", state); + log::debug(logs::task, " RSP0 %016lx", proc->rsp0); + log::debug(logs::task, " RSP %016lx", proc->rsp); } void @@ -278,17 +276,15 @@ void scheduler::prune(uint64_t now) } } -uintptr_t -scheduler::schedule(uintptr_t rsp0) +void +scheduler::schedule() { // TODO: lol a real clock static uint64_t now = 0; pid_t lastpid = m_current->pid; - m_current->rsp = rsp0; m_runlists[m_current->priority].remove(m_current); - if (m_current->flags && process_flags::ready) { m_runlists[m_current->priority].push_back(m_current); } else { @@ -304,34 +300,25 @@ scheduler::schedule(uintptr_t rsp0) } m_current = m_runlists[pri].pop_front(); - rsp0 = m_current->rsp; - - // Set rsp0 to after the end of the about-to-be-popped cpu state - tss_set_stack(0, rsp0 + sizeof(cpu_state)); - bsp_cpu_data.rsp0 = rsp0; - - // Swap page tables - page_table *pml4 = m_current->pml4; - page_manager::set_pml4(pml4); if (lastpid != m_current->pid) { - bool loading = m_current->flags && process_flags::loading; - log::debug(logs::task, "Scheduler switched to process %d, priority %d%s.", - m_current->pid, m_current->priority, loading ? " (loading)" : ""); - } - return rsp0; + bool loading = m_current->flags && process_flags::loading; + log::debug(logs::task, "Scheduler switching to process %d, priority %d%s.", + m_current->pid, m_current->priority, loading ? " (loading)" : ""); + + task_switch(m_current); + } } -uintptr_t -scheduler::tick(uintptr_t rsp0) +void +scheduler::tick() { if (--m_current->quanta == 0) { m_current->quanta = process_quanta; - rsp0 = schedule(rsp0); + schedule(); } m_apic->reset_timer(m_tick_count); - return rsp0; } process_node * diff --git a/src/kernel/scheduler.h b/src/kernel/scheduler.h index 55d2b09..9a137aa 100644 --- a/src/kernel/scheduler.h +++ b/src/kernel/scheduler.h @@ -10,7 +10,9 @@ class lapic; struct page_table; struct cpu_state; -extern "C" uintptr_t isr_handler(uintptr_t, cpu_state*); +extern "C" void isr_handler(cpu_state*); +extern "C" void task_switch(process *next); +extern "C" void task_fork(process *child); /// The task scheduler @@ -46,9 +48,7 @@ public: void start(); /// Run the scheduler, possibly switching to a new task - /// \arg rsp0 The stack pointer of the current interrupt handler - /// \returns The stack pointer to switch to - uintptr_t schedule(uintptr_t rsp0); + void schedule(); /// Get the current process. /// \returns A pointer to the current process' process struct @@ -65,7 +65,7 @@ public: private: friend uintptr_t syscall_dispatch(uintptr_t, cpu_state &); - friend uintptr_t isr_handler(uintptr_t, cpu_state*); + friend void isr_handler(cpu_state*); friend class process; /// Create a new process object. This process will have its pid @@ -75,9 +75,7 @@ private: process_node * create_process(pid_t pid = 0); /// Handle a timer tick - /// \arg rsp0 The stack pointer of the current interrupt handler - /// \returns The stack pointer to switch to - uintptr_t tick(uintptr_t rsp0); + void tick(); void prune(uint64_t now); diff --git a/src/kernel/syscall.cpp b/src/kernel/syscall.cpp index bcee5b3..676228b 100644 --- a/src/kernel/syscall.cpp +++ b/src/kernel/syscall.cpp @@ -34,11 +34,11 @@ syscall_enable() wrmsr(msr::ia32_fmask, 0x200); } -uintptr_t -syscall_dispatch(uintptr_t return_rsp, cpu_state ®s) +void +syscall_dispatch(cpu_state *regs) { console *cons = console::get(); - syscall call = static_cast(regs.rax); + syscall call = static_cast(regs->rax); auto &s = scheduler::get(); auto *p = s.current(); @@ -51,7 +51,7 @@ syscall_dispatch(uintptr_t return_rsp, cpu_state ®s) cons->set_color(11); cons->printf("\nProcess %d: Received DEBUG syscall\n", p->pid); cons->set_color(); - print_regs(regs); + print_regs(*regs); cons->printf("\n Syscall enters: %8d\n", __counter_syscall_enter); cons->printf(" Syscall sysret: %8d\n", __counter_syscall_sysret); break; @@ -71,7 +71,7 @@ syscall_dispatch(uintptr_t return_rsp, cpu_state ®s) p->wait_on_signal(-1ull); cons->printf("\nProcess %d: Received PAUSE syscall\n", p->pid); cons->set_color(); - return_rsp = s.schedule(return_rsp); + s.schedule(); } break; @@ -79,11 +79,11 @@ syscall_dispatch(uintptr_t return_rsp, cpu_state ®s) { cons->set_color(11); cons->printf("\nProcess %d: Received SLEEP syscall\n", p->pid); - cons->printf("Sleeping until %lu\n", regs.rdi); + cons->printf("Sleeping until %lu\n", regs->rdi); cons->set_color(); - p->wait_on_time(regs.rdi); - return_rsp = s.schedule(return_rsp); + p->wait_on_time(regs->rdi); + s.schedule(); } break; @@ -91,34 +91,34 @@ syscall_dispatch(uintptr_t return_rsp, cpu_state ®s) cons->set_color(11); cons->printf("\nProcess %d: Received GETPID syscall\n", p->pid); cons->set_color(); - regs.rax = p->pid; + regs->rax = p->pid; break; case syscall::send: { - pid_t target = regs.rdi; - uintptr_t data = regs.rsi; + pid_t target = regs->rdi; + uintptr_t data = regs->rsi; cons->set_color(11); cons->printf("\nProcess %d: Received SEND syscall, target %d, data %016lx\n", p->pid, target, data); cons->set_color(); if (p->wait_on_send(target)) - return_rsp = s.schedule(return_rsp); + s.schedule(); } break; case syscall::receive: { - pid_t source = regs.rdi; - uintptr_t data = regs.rsi; + pid_t source = regs->rdi; + uintptr_t data = regs->rsi; cons->set_color(11); cons->printf("\nProcess %d: Received RECEIVE syscall, source %d, dat %016lx\n", p->pid, source, data); cons->set_color(); if (p->wait_on_receive(source)) - return_rsp = s.schedule(return_rsp); + s.schedule(); } break; @@ -128,8 +128,9 @@ syscall_dispatch(uintptr_t return_rsp, cpu_state ®s) cons->printf("\nProcess %d: Received FORK syscall\n", p->pid); cons->set_color(); - pid_t pid = p->fork(return_rsp); - regs.rax = pid; + pid_t pid = p->fork(regs); + cons->printf("\n fork returning %d\n", pid); + regs->rax = pid; } break; @@ -137,8 +138,8 @@ syscall_dispatch(uintptr_t return_rsp, cpu_state ®s) cons->set_color(11); cons->printf("\nProcess %d: Received EXIT syscall\n", p->pid); cons->set_color(); - p->exit(regs.rdi); - return_rsp = s.schedule(return_rsp); + p->exit(regs->rdi); + s.schedule(); break; default: @@ -148,7 +149,5 @@ syscall_dispatch(uintptr_t return_rsp, cpu_state ®s) _halt(); break; } - - return return_rsp; } diff --git a/src/kernel/syscall.h b/src/kernel/syscall.h index fb2f08e..27bed28 100644 --- a/src/kernel/syscall.h +++ b/src/kernel/syscall.h @@ -21,5 +21,5 @@ enum class syscall : uint64_t }; void syscall_enable(); -uintptr_t syscall_dispatch(uintptr_t, cpu_state &); +void syscall_dispatch(cpu_state *); diff --git a/src/kernel/syscall.s b/src/kernel/syscall.s index 02b9b3f..dfe6973 100644 --- a/src/kernel/syscall.s +++ b/src/kernel/syscall.s @@ -28,7 +28,6 @@ syscall_handler_prelude: mov rdi, rsp call syscall_handler - mov rsp, rax mov rax, [rsp + 0x90] and rax, 0x3 diff --git a/src/kernel/task.s b/src/kernel/task.s new file mode 100644 index 0000000..3659578 --- /dev/null +++ b/src/kernel/task.s @@ -0,0 +1,85 @@ +%include "tasking.inc" + +extern g_tss + +global task_switch +task_switch: + push rbp + mov rbp, rsp + + ; Save the rest of the callee-saved regs + push rbx + push r12 + push r13 + push r14 + push r15 + + ; Update previous task's TCB + mov rax, [gs:CPU_DATA.tcb] ; rax: current task TCB + mov [rax + TCB.rsp], rsp + + ; 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 + 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 + + ; check if we need to update CR3 + mov rdx, cr3 ; rdx: old CR3 + cmp rax, rdx + je .no_cr3 + mov cr3, rax +.no_cr3: + + pop r15 + pop r14 + pop r13 + pop r12 + pop rbx + + pop rbp + ret + +global task_fork +task_fork: + push rbp + mov rbp, rsp + + ; Save the rest of the callee-saved regs + push rbx + push r12 + push r13 + push r14 + push r15 + + mov r14, rdi ; r14: child task TCB (function argument) + + mov rax, [gs:CPU_DATA.tcb] ; rax: current task TCB + mov rax, [rax + TCB.rsp0] ; rax: current task rsp0 + sub rax, rsp ; rax: size of kernel stack in bytes + + mov rcx, rax + shr rcx, 3 ; rcx: size of kernel stack in qwords + + mov rdi, [r14 + TCB.rsp0] ; rdi: child task rsp0 + sub rdi, rax ; rdi: child task rsp + mov rsi, rsp ; rsi: current rsp + + rep movsq + + pop r15 + pop r14 + pop r13 + pop r12 + pop rbx + + pop rbp + ret + diff --git a/src/kernel/tasking.inc b/src/kernel/tasking.inc new file mode 100644 index 0000000..ad1d0c0 --- /dev/null +++ b/src/kernel/tasking.inc @@ -0,0 +1,32 @@ +struc TCB +.rsp: resq 1 +.rsp0: resq 1 +.pml4: resq 1 +endstruc + +struc CPU_DATA +.rsp0: resq 1 +.rsp3: resq 1 +.tcb: resq 1 +endstruc + +struc TSS +.res0: resd 1 +.rsp0: resq 1 +.rsp1: resq 1 +.rsp2: resq 1 +.ist0: resq 1 +.ist1: resq 1 +.ist2: resq 1 +.ist3: resq 1 +.ist4: resq 1 +.ist5: resq 1 +.ist6: resq 1 +.ist7: resq 1 +.res1: resq 1 +.res2: resw 1 +.iomap: resw 1 +endstruc + + +; vim: ft=asm