Implement initial fork syscall
This commit is contained in:
@@ -6,6 +6,10 @@ _start:
|
|||||||
int 0xee
|
int 0xee
|
||||||
mov r12, rax ; save pid to r12
|
mov r12, rax ; save pid to r12
|
||||||
|
|
||||||
|
mov rax, 8 ; FORK syscall
|
||||||
|
int 0xee
|
||||||
|
|
||||||
|
mov r10, rax
|
||||||
mov rax, 1 ; DEBUG syscall
|
mov rax, 1 ; DEBUG syscall
|
||||||
int 0xee
|
int 0xee
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
struct cpu_state
|
struct cpu_state
|
||||||
{
|
{
|
||||||
uint64_t ds;
|
uint64_t ds;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "gdt.h"
|
#include "gdt.h"
|
||||||
|
#include "page_manager.h"
|
||||||
|
|
||||||
#define print_reg(name, value) cons->printf(" %s: %016lx\n", name, (value));
|
#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");
|
cons->puts("\n");
|
||||||
print_reg("rip", regs.rip);
|
print_reg("rip", regs.rip);
|
||||||
|
|
||||||
|
cons->puts("\n");
|
||||||
|
print_reg("cr3", page_manager::get()->get_pml4());
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ isr_handler(uintptr_t return_rsp, cpu_state *regs)
|
|||||||
print_reg("rip", regs->rip);
|
print_reg("rip", regs->rip);
|
||||||
|
|
||||||
cons->puts("\n");
|
cons->puts("\n");
|
||||||
print_stacktrace(2);
|
//print_stacktrace(2);
|
||||||
}
|
}
|
||||||
_halt();
|
_halt();
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ init_console()
|
|||||||
//log::enable(logs::apic, log::level::debug);
|
//log::enable(logs::apic, log::level::debug);
|
||||||
//log::enable(logs::device, log::level::info);
|
//log::enable(logs::device, log::level::info);
|
||||||
log::enable(logs::driver, log::level::debug);
|
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::fs, log::level::debug);
|
||||||
log::enable(logs::task, log::level::debug);
|
log::enable(logs::task, log::level::debug);
|
||||||
log::enable(logs::boot, log::level::debug);
|
log::enable(logs::boot, log::level::debug);
|
||||||
@@ -148,7 +148,7 @@ kernel_main(popcorn_data *header)
|
|||||||
|
|
||||||
for (auto &f : ird.files()) {
|
for (auto &f : ird.files()) {
|
||||||
if (f.executable())
|
if (f.executable())
|
||||||
sched->create_process(f.name(), f.data(), f.size());
|
sched->load_process(f.name(), f.data(), f.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
sched->start();
|
sched->start();
|
||||||
|
|||||||
@@ -92,12 +92,19 @@ page_table *
|
|||||||
page_manager::copy_table(page_table *from, page_table::level lvl)
|
page_manager::copy_table(page_table *from, page_table::level lvl)
|
||||||
{
|
{
|
||||||
page_table *to = get_table_page();
|
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 =
|
const int max =
|
||||||
lvl == page_table::level::pml4 ?
|
lvl == page_table::level::pml4 ?
|
||||||
510 :
|
510 :
|
||||||
512;
|
512;
|
||||||
|
|
||||||
|
unsigned pages_copied = 0;
|
||||||
for (int i = 0; i < max; ++i) {
|
for (int i = 0; i < max; ++i) {
|
||||||
if (!from->is_present(i)) {
|
if (!from->is_present(i)) {
|
||||||
to->entries[i] = 0;
|
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;
|
uint16_t flags = from->entries[i] & 0xfffull;
|
||||||
uintptr_t orig = from->entries[i] & ~0xfffull;
|
uintptr_t orig = from->entries[i] & ~0xfffull;
|
||||||
to->entries[i] = copy_page(orig) | flags;
|
to->entries[i] = copy_page(orig) | flags;
|
||||||
|
pages_copied++;
|
||||||
} else {
|
} else {
|
||||||
uint16_t flags = 0;
|
uint16_t flags = 0;
|
||||||
page_table *next_from = from->get(i, &flags);
|
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;
|
return to;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,59 @@
|
|||||||
|
#include "cpu.h"
|
||||||
|
#include "log.h"
|
||||||
#include "process.h"
|
#include "process.h"
|
||||||
#include "scheduler.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
|
bool
|
||||||
process::wait_on_signal(uint64_t sigmask)
|
process::wait_on_signal(uint64_t sigmask)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
#include "kutil/linked_list.h"
|
#include "kutil/linked_list.h"
|
||||||
#include "page_manager.h"
|
#include "page_manager.h"
|
||||||
|
|
||||||
|
typedef uint32_t pid_t;
|
||||||
|
|
||||||
|
|
||||||
enum class process_flags : uint32_t
|
enum class process_flags : uint32_t
|
||||||
{
|
{
|
||||||
@@ -33,8 +35,8 @@ enum class process_wait : uint8_t
|
|||||||
/// A process
|
/// A process
|
||||||
struct process
|
struct process
|
||||||
{
|
{
|
||||||
uint32_t pid;
|
pid_t pid;
|
||||||
uint32_t ppid;
|
pid_t ppid;
|
||||||
|
|
||||||
process_flags flags;
|
process_flags flags;
|
||||||
|
|
||||||
@@ -52,6 +54,15 @@ struct process
|
|||||||
uintptr_t rsp;
|
uintptr_t rsp;
|
||||||
page_table *pml4;
|
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
|
/// Unready this process until it gets a signal
|
||||||
/// \arg sigmask A bitfield of signals to wake on
|
/// \arg sigmask A bitfield of signals to wake on
|
||||||
/// \returns Whether the process should be rescheduled
|
/// \returns Whether the process should be rescheduled
|
||||||
@@ -104,6 +115,16 @@ struct process
|
|||||||
/// \returns True if this wake was handled
|
/// \returns True if this wake was handled
|
||||||
bool wake_on_receive(process *source);
|
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>;
|
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);
|
log::debug(logs::task, " Loaded! New process rip: %016lx", state.rip);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
process_node *
|
||||||
scheduler::create_process(const char *name, const void *data, size_t size)
|
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 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 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();
|
page_table *pml4 = page_manager::get()->create_process_map();
|
||||||
|
|
||||||
// Create a one-page kernel stack space
|
// Create a one-page kernel stack space
|
||||||
void *stack0 = kutil::malloc(stack_size);
|
void *stack0 = proc->setup_kernel_stack(stack_size, 0);
|
||||||
kutil::memset(stack0, 0, stack_size);
|
|
||||||
|
|
||||||
// Stack grows down, point to the end
|
// Stack grows down, point to the end
|
||||||
void *sp0 = kutil::offset_pointer(stack0, stack_size);
|
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->rax = reinterpret_cast<uint64_t>(data);
|
||||||
loader_state->rbx = size;
|
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->rsp = reinterpret_cast<uintptr_t>(loader_state);
|
||||||
proc->pml4 = pml4;
|
proc->pml4 = pml4;
|
||||||
proc->quanta = process_quanta;
|
proc->quanta = process_quanta;
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ public:
|
|||||||
/// \arg name Name of the program image
|
/// \arg name Name of the program image
|
||||||
/// \arg data Pointer to the image data
|
/// \arg data Pointer to the image data
|
||||||
/// \arg size Size of the program image, in bytes
|
/// \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
|
/// Start the scheduler working. This may involve starting
|
||||||
/// timer interrupts or other preemption methods.
|
/// timer interrupts or other preemption methods.
|
||||||
@@ -61,6 +61,12 @@ public:
|
|||||||
private:
|
private:
|
||||||
friend uintptr_t syscall_dispatch(uintptr_t, cpu_state &);
|
friend uintptr_t syscall_dispatch(uintptr_t, cpu_state &);
|
||||||
friend uintptr_t isr_handler(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
|
/// Handle a timer tick
|
||||||
/// \arg rsp0 The stack pointer of the current interrupt handler
|
/// \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();
|
auto *p = s.current();
|
||||||
p->wait_on_signal(-1ull);
|
p->wait_on_signal(-1ull);
|
||||||
cons->printf("\nProcess %u: Received PAUSE syscall\n", p->pid);
|
cons->printf("\nProcess %u: Received PAUSE syscall\n", p->pid);
|
||||||
return_rsp = s.schedule(return_rsp);
|
|
||||||
cons->set_color();
|
cons->set_color();
|
||||||
|
return_rsp = s.schedule(return_rsp);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -118,6 +118,19 @@ syscall_dispatch(uintptr_t return_rsp, cpu_state ®s)
|
|||||||
}
|
}
|
||||||
break;
|
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:
|
default:
|
||||||
cons->set_color(9);
|
cons->set_color(9);
|
||||||
cons->printf("\nReceived unknown syscall: %02x\n", call);
|
cons->printf("\nReceived unknown syscall: %02x\n", call);
|
||||||
|
|||||||
@@ -6,14 +6,15 @@ struct cpu_state;
|
|||||||
|
|
||||||
enum class syscall : uint64_t
|
enum class syscall : uint64_t
|
||||||
{
|
{
|
||||||
noop,
|
noop = 0x0000,
|
||||||
debug,
|
debug = 0x0001,
|
||||||
message,
|
message = 0x0002,
|
||||||
pause,
|
pause = 0x0003,
|
||||||
sleep,
|
sleep = 0x0004,
|
||||||
getpid,
|
getpid = 0x0005,
|
||||||
send,
|
send = 0x0006,
|
||||||
receive,
|
receive = 0x0007,
|
||||||
|
fork = 0x0008,
|
||||||
|
|
||||||
last_syscall
|
last_syscall
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user