mirror of
https://github.com/justinian/jsix.git
synced 2025-12-10 08:24:32 -08:00
Implement initial fork syscall
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct cpu_state
|
||||
{
|
||||
uint64_t ds;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user