diff --git a/src/drivers/nulldrv/main.s b/src/drivers/nulldrv/main.s index 5197242..0aadb8c 100644 --- a/src/drivers/nulldrv/main.s +++ b/src/drivers/nulldrv/main.s @@ -6,6 +6,10 @@ _start: int 0xee mov r12, rax ; save pid to r12 + mov rax, 8 ; FORK syscall + int 0xee + + mov r10, rax mov rax, 1 ; DEBUG syscall int 0xee diff --git a/src/kernel/cpu.h b/src/kernel/cpu.h index e0b3fe6..fe51896 100644 --- a/src/kernel/cpu.h +++ b/src/kernel/cpu.h @@ -1,5 +1,7 @@ #pragma once +#include + struct cpu_state { uint64_t ds; diff --git a/src/kernel/debug.cpp b/src/kernel/debug.cpp index e5c4d6b..c6bd72b 100644 --- a/src/kernel/debug.cpp +++ b/src/kernel/debug.cpp @@ -2,6 +2,7 @@ #include "cpu.h" #include "debug.h" #include "gdt.h" +#include "page_manager.h" #define print_reg(name, value) cons->printf(" %s: %016lx\n", name, (value)); @@ -39,6 +40,9 @@ print_regs(const cpu_state ®s) cons->puts("\n"); print_reg("rip", regs.rip); + + cons->puts("\n"); + print_reg("cr3", page_manager::get()->get_pml4()); } void diff --git a/src/kernel/interrupts.cpp b/src/kernel/interrupts.cpp index dc54281..0358599 100644 --- a/src/kernel/interrupts.cpp +++ b/src/kernel/interrupts.cpp @@ -171,7 +171,7 @@ isr_handler(uintptr_t return_rsp, cpu_state *regs) print_reg("rip", regs->rip); cons->puts("\n"); - print_stacktrace(2); + //print_stacktrace(2); } _halt(); break; diff --git a/src/kernel/main.cpp b/src/kernel/main.cpp index b522ffd..541977e 100644 --- a/src/kernel/main.cpp +++ b/src/kernel/main.cpp @@ -41,7 +41,7 @@ init_console() //log::enable(logs::apic, log::level::debug); //log::enable(logs::device, log::level::info); log::enable(logs::driver, log::level::debug); - log::enable(logs::memory, log::level::info); + log::enable(logs::memory, log::level::debug); log::enable(logs::fs, log::level::debug); log::enable(logs::task, log::level::debug); log::enable(logs::boot, log::level::debug); @@ -148,7 +148,7 @@ kernel_main(popcorn_data *header) for (auto &f : ird.files()) { if (f.executable()) - sched->create_process(f.name(), f.data(), f.size()); + sched->load_process(f.name(), f.data(), f.size()); } sched->start(); diff --git a/src/kernel/page_manager.cpp b/src/kernel/page_manager.cpp index a1f3e14..d15b4ff 100644 --- a/src/kernel/page_manager.cpp +++ b/src/kernel/page_manager.cpp @@ -92,12 +92,19 @@ page_table * page_manager::copy_table(page_table *from, page_table::level lvl) { page_table *to = get_table_page(); + log::debug(logs::memory, "Page manager copying level %d table at %016lx to %016lx.", lvl, from, to); + + if (lvl == page_table::level::pml4) { + to->entries[510] = m_kernel_pml4->entries[510]; + to->entries[511] = m_kernel_pml4->entries[511]; + } const int max = lvl == page_table::level::pml4 ? 510 : 512; + unsigned pages_copied = 0; for (int i = 0; i < max; ++i) { if (!from->is_present(i)) { to->entries[i] = 0; @@ -112,6 +119,7 @@ page_manager::copy_table(page_table *from, page_table::level lvl) uint16_t flags = from->entries[i] & 0xfffull; uintptr_t orig = from->entries[i] & ~0xfffull; to->entries[i] = copy_page(orig) | flags; + pages_copied++; } else { uint16_t flags = 0; page_table *next_from = from->get(i, &flags); @@ -120,6 +128,9 @@ page_manager::copy_table(page_table *from, page_table::level lvl) } } + if (pages_copied) + log::debug(logs::memory, " copied %3u pages", pages_copied); + return to; } diff --git a/src/kernel/process.cpp b/src/kernel/process.cpp index 3b292f5..5e38927 100644 --- a/src/kernel/process.cpp +++ b/src/kernel/process.cpp @@ -1,7 +1,59 @@ +#include "cpu.h" +#include "log.h" #include "process.h" #include "scheduler.h" +pid_t +process::fork(uintptr_t in_rsp) +{ + auto &sched = scheduler::get(); + auto *child = sched.create_process(); + kassert(child, "process::fork() got null child"); + + child->ppid = pid; + child->flags = + process_flags::running | + process_flags::ready; + + 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); + + // Add in the faked fork return value + cpu_state *regs = reinterpret_cast(child->rsp); + regs->rax = 0; + + return child->pid; +} + +void * +process::setup_kernel_stack(size_t size, uintptr_t orig) +{ + void *stack0 = kutil::malloc(size); + + if (orig) + kutil::memcpy(stack0, reinterpret_cast(orig), size); + else + kutil::memset(stack0, 0, size); + + kernel_stack_size = size; + kernel_stack = reinterpret_cast(stack0); + + return stack0; +} + bool process::wait_on_signal(uint64_t sigmask) { diff --git a/src/kernel/process.h b/src/kernel/process.h index 832cc82..50dfee5 100644 --- a/src/kernel/process.h +++ b/src/kernel/process.h @@ -7,6 +7,8 @@ #include "kutil/linked_list.h" #include "page_manager.h" +typedef uint32_t pid_t; + enum class process_flags : uint32_t { @@ -33,8 +35,8 @@ enum class process_wait : uint8_t /// A process struct process { - uint32_t pid; - uint32_t ppid; + pid_t pid; + pid_t ppid; process_flags flags; @@ -52,6 +54,15 @@ struct process uintptr_t rsp; page_table *pml4; + uintptr_t kernel_stack; + size_t kernel_stack_size; + + /// Copy this process. + /// \arg in_rsp The RSP of the calling process + /// \returns Returns the child's pid to the parent, and + /// 0 to the child. + pid_t fork(uint64_t in_rsp); + /// Unready this process until it gets a signal /// \arg sigmask A bitfield of signals to wake on /// \returns Whether the process should be rescheduled @@ -104,6 +115,16 @@ struct process /// \returns True if this wake was handled bool wake_on_receive(process *source); +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); }; using process_list = kutil::linked_list; diff --git a/src/kernel/scheduler.cpp b/src/kernel/scheduler.cpp index 7236513..37ab1af 100644 --- a/src/kernel/scheduler.cpp +++ b/src/kernel/scheduler.cpp @@ -108,9 +108,20 @@ load_process(const void *image_start, size_t bytes, process *proc, cpu_state sta log::debug(logs::task, " Loaded! New process rip: %016lx", state.rip); } -void -scheduler::create_process(const char *name, const void *data, size_t size) +process_node * +scheduler::create_process() { + auto *proc = m_process_allocator.pop(); + proc->pid = m_next_pid++; + proc->priority = default_priority; + return proc; +} + +void +scheduler::load_process(const char *name, const void *data, size_t size) +{ + auto *proc = create_process(); + 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 @@ -121,8 +132,7 @@ scheduler::create_process(const char *name, const void *data, size_t size) page_table *pml4 = page_manager::get()->create_process_map(); // Create a one-page kernel stack space - void *stack0 = kutil::malloc(stack_size); - kutil::memset(stack0, 0, stack_size); + void *stack0 = proc->setup_kernel_stack(stack_size, 0); // Stack grows down, point to the end void *sp0 = kutil::offset_pointer(stack0, stack_size); @@ -150,12 +160,6 @@ scheduler::create_process(const char *name, const void *data, size_t size) loader_state->rax = reinterpret_cast(data); loader_state->rbx = size; - uint16_t pid = m_next_pid++; - auto *proc = m_process_allocator.pop(); - - proc->pid = pid; - proc->ppid = 0; // TODO - proc->priority = default_priority; proc->rsp = reinterpret_cast(loader_state); proc->pml4 = pml4; proc->quanta = process_quanta; diff --git a/src/kernel/scheduler.h b/src/kernel/scheduler.h index 2253473..00e3493 100644 --- a/src/kernel/scheduler.h +++ b/src/kernel/scheduler.h @@ -34,7 +34,7 @@ public: /// \arg name Name of the program image /// \arg data Pointer to the image data /// \arg size Size of the program image, in bytes - void create_process(const char *name, const void *data, size_t size); + void load_process(const char *name, const void *data, size_t size); /// Start the scheduler working. This may involve starting /// timer interrupts or other preemption methods. @@ -61,6 +61,12 @@ public: private: friend uintptr_t syscall_dispatch(uintptr_t, cpu_state &); friend uintptr_t isr_handler(uintptr_t, cpu_state*); + friend class process; + + /// Create a new process object. This process will have its pid + /// set but nothing else. + /// \returns The new process object + process_node * create_process(); /// Handle a timer tick /// \arg rsp0 The stack pointer of the current interrupt handler diff --git a/src/kernel/syscall.cpp b/src/kernel/syscall.cpp index 378432f..c63c6e7 100644 --- a/src/kernel/syscall.cpp +++ b/src/kernel/syscall.cpp @@ -68,8 +68,8 @@ syscall_dispatch(uintptr_t return_rsp, cpu_state ®s) auto *p = s.current(); p->wait_on_signal(-1ull); cons->printf("\nProcess %u: Received PAUSE syscall\n", p->pid); - return_rsp = s.schedule(return_rsp); cons->set_color(); + return_rsp = s.schedule(return_rsp); } break; @@ -118,6 +118,19 @@ syscall_dispatch(uintptr_t return_rsp, cpu_state ®s) } break; + case syscall::fork: + { + cons->set_color(11); + cons->printf("\nProcess %u: Received FORK syscall\n", p->pid); + cons->set_color(); + + pid_t pid = p->fork(return_rsp); + if (pid == scheduler::get().current()->pid) + pid = 0; + regs.rax = pid; + } + break; + default: cons->set_color(9); cons->printf("\nReceived unknown syscall: %02x\n", call); diff --git a/src/kernel/syscall.h b/src/kernel/syscall.h index 18b65b1..7de9b22 100644 --- a/src/kernel/syscall.h +++ b/src/kernel/syscall.h @@ -6,14 +6,15 @@ struct cpu_state; enum class syscall : uint64_t { - noop, - debug, - message, - pause, - sleep, - getpid, - send, - receive, + noop = 0x0000, + debug = 0x0001, + message = 0x0002, + pause = 0x0003, + sleep = 0x0004, + getpid = 0x0005, + send = 0x0006, + receive = 0x0007, + fork = 0x0008, last_syscall };