summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/init/main.c2
-rw-r--r--src/kernel/arch/generic.h2
-rw-r--r--src/kernel/arch/i386/pagedir.c33
-rw-r--r--src/kernel/proc.c9
-rw-r--r--src/kernel/proc.h1
-rw-r--r--src/kernel/syscalls.c8
-rw-r--r--src/kernel/syscalls.h1
7 files changed, 56 insertions, 0 deletions
diff --git a/src/init/main.c b/src/init/main.c
index 119984e..41a6ac0 100644
--- a/src/init/main.c
+++ b/src/init/main.c
@@ -18,6 +18,8 @@ int main() {
debuglog("hello from init! ",
sizeof("hello from init! ") - 1);
+ _syscall(SC_FORK, 0, 0, 0);
+
exit( "bye from init! ",
sizeof("bye from init! ") - 1);
}
diff --git a/src/kernel/arch/generic.h b/src/kernel/arch/generic.h
index 3ab21b4..1427658 100644
--- a/src/kernel/arch/generic.h
+++ b/src/kernel/arch/generic.h
@@ -21,8 +21,10 @@ int syscall_handler(int, int, int, int);
// all of those can allocate memory
struct pagedir *pagedir_new();
+struct pagedir *pagedir_copy(const struct pagedir *orig);
void pagedir_map(struct pagedir *dir, void *virt, void *phys,
bool user, bool writeable);
+
void pagedir_switch(struct pagedir *);
// return 0 on failure
diff --git a/src/kernel/arch/i386/pagedir.c b/src/kernel/arch/i386/pagedir.c
index 02361e6..61ad170 100644
--- a/src/kernel/arch/i386/pagedir.c
+++ b/src/kernel/arch/i386/pagedir.c
@@ -1,5 +1,6 @@
#include <kernel/arch/generic.h>
#include <kernel/mem.h>
+#include <kernel/util.h>
#include <stdint.h>
/* <heat> nitpick: I highly recommend you dont use bitfields for paging
@@ -92,6 +93,38 @@ void pagedir_switch(struct pagedir *dir) {
asm volatile("mov %0, %%cr3;" : : "r" (dir) : "memory");
}
+// creates a new pagedir with exact copies of the user pages
+struct pagedir *pagedir_copy(const struct pagedir *orig) {
+ struct pagedir *clone = page_alloc(1);
+ struct pagetable_entry *orig_pt, *clone_pt;
+ void *orig_page, *clone_page;
+
+ for (int i = 0; i < 1024; i++) {
+ clone->e[i] = orig->e[i];
+ if (!orig->e[i].present) continue;
+ if (!orig->e[i].user) continue; // not really needed
+
+ orig_pt = (void*)(orig->e[i].address << 11);
+ clone_pt = page_alloc(1);
+ clone->e[i].address = (uintptr_t) clone_pt >> 11;
+
+ for (int j = 0; j < 1024; j++) {
+ clone_pt[j] = orig_pt[j];
+ if (!orig_pt[j].present) continue;
+ if (!orig_pt[j].user) continue;
+ // i could use .global?
+
+ orig_page = (void*)(orig_pt[j].address << 11);
+ clone_page = page_alloc(1);
+ clone_pt[j].address = (uintptr_t) clone_page >> 11;
+
+ memcpy(clone_page, orig_page, PAGE_SIZE);
+ }
+ }
+
+ return clone;
+}
+
void *pagedir_virt2phys(struct pagedir *dir, const void *virt,
bool user, bool writeable)
{
diff --git a/src/kernel/proc.c b/src/kernel/proc.c
index 9c7e390..b165878 100644
--- a/src/kernel/proc.c
+++ b/src/kernel/proc.c
@@ -1,6 +1,7 @@
#include <kernel/arch/generic.h>
#include <kernel/mem.h>
#include <kernel/proc.h>
+#include <kernel/util.h>
#include <stdint.h>
struct process *process_current;
@@ -23,6 +24,14 @@ struct process *process_new() {
return proc;
}
+struct process *process_clone(const struct process *orig) {
+ struct process *clone = page_alloc(1);
+ memcpy(clone, orig, sizeof(struct process));
+ clone->pages = pagedir_copy(orig->pages);
+
+ return clone;
+}
+
void process_switch(struct process *proc) {
process_current = proc;
pagedir_switch(proc->pages);
diff --git a/src/kernel/proc.h b/src/kernel/proc.h
index 7ab8da8..71880e6 100644
--- a/src/kernel/proc.h
+++ b/src/kernel/proc.h
@@ -10,4 +10,5 @@ struct process {
extern struct process *process_current;
struct process *process_new();
+struct process *process_clone(const struct process *orig);
_Noreturn void process_switch(struct process *proc);
diff --git a/src/kernel/syscalls.c b/src/kernel/syscalls.c
index a6a2b17..eff670e 100644
--- a/src/kernel/syscalls.c
+++ b/src/kernel/syscalls.c
@@ -9,6 +9,12 @@ _Noreturn void sc_exit(const char *msg, size_t len) {
panic();
}
+int sc_fork() {
+ struct process *orig = process_current;
+ process_current = process_clone(orig);
+ process_switch(process_current);
+}
+
int sc_debuglog(const char *msg, size_t len) {
struct pagedir *pages = process_current->pages;
void *phys = pagedir_virt2phys(pages, msg, true, false);
@@ -27,6 +33,8 @@ int syscall_handler(int num, int a, int b, int c) {
switch (num) {
case SC_EXIT:
sc_exit((void*)a, b);
+ case SC_FORK:
+ return sc_fork();
case SC_DEBUGLOG:
return sc_debuglog((void*)a, b);
default:
diff --git a/src/kernel/syscalls.h b/src/kernel/syscalls.h
index a249046..ceb90c1 100644
--- a/src/kernel/syscalls.h
+++ b/src/kernel/syscalls.h
@@ -3,6 +3,7 @@
// not caring about stable syscall numbers just yet
enum {
SC_EXIT,
+ SC_FORK,
SC_DEBUGLOG
};