diff options
Diffstat (limited to 'src/kernel')
-rw-r--r-- | src/kernel/arch/generic.h | 2 | ||||
-rw-r--r-- | src/kernel/arch/i386/pagedir.c | 33 | ||||
-rw-r--r-- | src/kernel/proc.c | 9 | ||||
-rw-r--r-- | src/kernel/proc.h | 1 | ||||
-rw-r--r-- | src/kernel/syscalls.c | 8 | ||||
-rw-r--r-- | src/kernel/syscalls.h | 1 |
6 files changed, 54 insertions, 0 deletions
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 }; |