Implement initial fork syscall

This commit is contained in:
Justin C. Miller
2019-03-09 12:18:21 -08:00
parent 241e1dacb0
commit 97ac3c09fa
12 changed files with 143 additions and 25 deletions

View File

@@ -1,5 +1,7 @@
#pragma once
#include <stdint.h>
struct cpu_state
{
uint64_t ds;

View File

@@ -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 &regs)
cons->puts("\n");
print_reg("rip", regs.rip);
cons->puts("\n");
print_reg("cr3", page_manager::get()->get_pml4());
}
void

View File

@@ -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;

View File

@@ -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();

View File

@@ -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;
}

View File

@@ -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<cpu_state *>(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<void*>(orig), size);
else
kutil::memset(stack0, 0, size);
kernel_stack_size = size;
kernel_stack = reinterpret_cast<uintptr_t>(stack0);
return stack0;
}
bool
process::wait_on_signal(uint64_t sigmask)
{

View File

@@ -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<process>;

View File

@@ -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<uint64_t>(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<uintptr_t>(loader_state);
proc->pml4 = pml4;
proc->quanta = process_quanta;

View File

@@ -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

View File

@@ -68,8 +68,8 @@ syscall_dispatch(uintptr_t return_rsp, cpu_state &regs)
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 &regs)
}
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);

View File

@@ -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
};