aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/common/include/proc.h7
-rw-r--r--src/kernel/core/thread.c55
-rw-r--r--src/kernel/core/worker.c1
-rw-r--r--src/kernel/include/process.h3
-rw-r--r--src/kernel/user/process.c158
-rw-r--r--src/kernel/user/syscall.c4
6 files changed, 199 insertions, 29 deletions
diff --git a/src/common/include/proc.h b/src/common/include/proc.h
index 01a6722..af50001 100644
--- a/src/common/include/proc.h
+++ b/src/common/include/proc.h
@@ -5,10 +5,15 @@
#define PS_LOADING 1
#define PS_RUNNING 2
-#define PS_DONE 3
+#define PS_FINISHED 3
#define PS_FAILURE 4 // exception or segfault or stuff
#define PS_KILLED 5
+#define FAIL_EXCEPTION 1 // unhandled processor exception
+#define FAIL_ZEROPTR 2 // segfault at < 4k
+#define FAIL_SEGFAULT 3
+#define FAIL_SC_SEGFAULT 4 // failed to validate parameter for system call
+
typedef struct {
int pid;
int state; // one of PS_*
diff --git a/src/kernel/core/thread.c b/src/kernel/core/thread.c
index 02e81e1..63523af 100644
--- a/src/kernel/core/thread.c
+++ b/src/kernel/core/thread.c
@@ -88,6 +88,21 @@ thread_t* dequeue_thread() {
return t;
}
+void remove_thread_from_queue(thread_t *t) {
+ if (queue_first_thread == t) {
+ queue_first_thread = t->next_in_queue;
+ if (queue_first_thread == 0) queue_last_thread = 0;
+ } else {
+ for (thread_t *it = queue_first_thread; it != 0; it = it->next_in_queue) {
+ if (it->next_in_queue == t) {
+ it->next_in_queue = t->next_in_queue;
+ if (it->next_in_queue == 0) queue_last_thread = t;
+ break;
+ }
+ }
+ }
+}
+
// ================ //
// THE TASKING CODE //
// ================ //
@@ -118,6 +133,9 @@ void run_scheduler() {
static void run_thread(void (*entry)(void*), void* data) {
ASSERT(current_thread->state == T_STATE_RUNNING);
+ dbg_printf("Begin thread 0x%p (in process %d)\n",
+ current_thread, (current_thread->proc ? current_thread->proc->pid : 0));
+
switch_pagedir(get_kernel_pagedir());
asm volatile("sti");
@@ -166,15 +184,24 @@ thread_t *new_thread(entry_t entry, void* data) {
t->last_ran = 0;
t->current_pd_d = get_kernel_pagedir();
+ t->critical_level = CL_USER;
// used by user processes
t->proc = 0;
+ t->next_in_proc = 0;
t->user_ex_handler = 0;
- t->critical_level = CL_USER;
return t;
}
+static void delete_thread(thread_t *t) {
+ if (t->proc != 0)
+ process_thread_deleted(t);
+
+ region_free_unmap_free(t->stack_region->addr);
+ free(t);
+}
+
// ========== //
// SETUP CODE //
// ========== //
@@ -227,8 +254,20 @@ void usleep(int usecs) {
}
void exit() {
+ void delete_thread_v(void* v) {
+ delete_thread((thread_t*)v);
+ }
+
+ int st = enter_critical(CL_NOSWITCH);
+ // the critical section here does not guarantee that worker_push will return immediately
+ // (it may switch before adding the delete_thread task), but once the task is added
+ // no other switch may happen, therefore this thread will not get re-enqueued
+
+ worker_push(delete_thread_v, current_thread);
current_thread->state = T_STATE_FINISHED;
- // TODO : add job for deleting the thread, or whatever
+
+ exit_critical(st);
+
yield(); // expected never to return!
ASSERT(false);
}
@@ -249,4 +288,16 @@ bool resume_thread(thread_t *thread) {
return ret;
}
+void kill_thread(thread_t *thread) {
+ ASSERT(thread != current_thread);
+
+ int st = enter_critical(CL_NOSWITCH);
+
+ thread->state = T_STATE_FINISHED;
+ remove_thread_from_queue(thread);
+ delete_thread(thread);
+
+ exit_critical(st);
+}
+
/* vim: set ts=4 sw=4 tw=0 noet :*/
diff --git a/src/kernel/core/worker.c b/src/kernel/core/worker.c
index 4f37b22..e50085a 100644
--- a/src/kernel/core/worker.c
+++ b/src/kernel/core/worker.c
@@ -36,6 +36,7 @@ void start_workers(int n) {
nworkers = n;
for (int i = 0; i < n; i++) {
workers[i] = new_thread(worker_thread, 0);
+ dbg_printf("New worker thread: 0x%p\n", workers[i]);
}
}
diff --git a/src/kernel/include/process.h b/src/kernel/include/process.h
index 82f1b4d..1b4012f 100644
--- a/src/kernel/include/process.h
+++ b/src/kernel/include/process.h
@@ -52,6 +52,7 @@ typedef struct process {
uint64_t last_ran;
int pid;
+ int status, exit_code;
struct process *parent;
struct process *next_child;
struct process *children;
@@ -74,7 +75,7 @@ bool start_process(process_t *p, proc_entry_t entry); // maps a region for user
void process_exit(process_t *p, int status, int exit_code); // exit current process
bool process_new_thread(process_t *p, proc_entry_t entry, void* sp);
-void process_thread_exited(thread_t *t); // called by threading code when a thread exits
+void process_thread_deleted(thread_t *t); // called by threading code when a thread exits
process_t *process_find_child(process_t *p, int pid);
void process_get_status(process_t *p, proc_status_t *st);
diff --git a/src/kernel/user/process.c b/src/kernel/user/process.c
index 649c801..af7d86f 100644
--- a/src/kernel/user/process.c
+++ b/src/kernel/user/process.c
@@ -5,7 +5,7 @@
#include <frame.h>
#include <process.h>
#include <freemem.h>
-
+#include <worker.h>
static int next_pid = 1;
@@ -22,6 +22,11 @@ typedef struct {
void *sp;
} setup_data_t;
+typedef struct {
+ process_t *proc;
+ int status, exit_code;
+} exit_data_t;
+
// ============================== //
// CREATING AND RUNNING PROCESSES //
// ============================== //
@@ -48,6 +53,7 @@ process_t *new_process(process_t *parent) {
proc->pid = (next_pid++);
proc->parent = parent;
proc->next_fd = 1;
+ proc->status = PS_LOADING;
return proc;
@@ -62,15 +68,15 @@ error:
static void run_user_code(void* param) {
setup_data_t *d = (setup_data_t*)param;
+ void* esp = d->sp;
+ proc_entry_t entry = d->entry;
+ free(d);
+
process_t *proc = current_thread->proc;
ASSERT(proc != 0);
switch_pagedir(proc->pd);
- void* esp = d->sp;
- proc_entry_t entry = d->entry;
- free(d);
-
asm volatile(" \
cli; \
\
@@ -91,18 +97,6 @@ static void run_user_code(void* param) {
iret \
"::"b"(esp),"c"(entry));
}
-bool start_process(process_t *p, void* entry) {
- bool stack_ok = mmap(p, (void*)USERSTACK_ADDR, USERSTACK_SIZE, MM_READ | MM_WRITE);
- if (!stack_ok) return false;
-
- bool ok = process_new_thread(p, entry, (void*)USERSTACK_ADDR + USERSTACK_SIZE);
- if (!ok) {
- munmap(p, (void*)USERSTACK_ADDR);
- return false;
- }
-
- return true;
-}
bool process_new_thread(process_t *p, proc_entry_t entry, void* sp) {
setup_data_t *d = (setup_data_t*)malloc(sizeof(setup_data_t));
@@ -117,7 +111,7 @@ bool process_new_thread(process_t *p, proc_entry_t entry, void* sp) {
th->proc = p;
th->user_ex_handler = proc_user_exception;
- { int st = enter_critical(CL_NOINT); // it's a bit complicated to use mutexes on process_t (TODO: think)
+ { int st = enter_critical(CL_NOSWITCH); // it's a bit complicated to use mutexes on process_t
th->next_in_proc = p->threads;
p->threads = th;
@@ -129,6 +123,124 @@ bool process_new_thread(process_t *p, proc_entry_t entry, void* sp) {
return true;
}
+bool start_process(process_t *p, void* entry) {
+ bool stack_ok = mmap(p, (void*)USERSTACK_ADDR, USERSTACK_SIZE, MM_READ | MM_WRITE);
+ if (!stack_ok) return false;
+
+ bool ok = process_new_thread(p, entry, (void*)USERSTACK_ADDR + USERSTACK_SIZE);
+ if (!ok) {
+ munmap(p, (void*)USERSTACK_ADDR);
+ return false;
+ }
+
+ p->status = PS_RUNNING;
+
+ return true;
+}
+
+void process_exit(process_t *p, int status, int exit_code) {
+ // --- Make sure we are not running in a thread we are about to kill
+
+ void process_exit_v(void* args) {
+ exit_data_t *d = (exit_data_t*)args;
+ process_exit(d->proc, d->status, d->exit_code);
+ free(d);
+ }
+
+ if (current_process() == p) {
+ exit_data_t *d = (exit_data_t*)malloc(sizeof(exit_data_t));
+
+ d->proc = p;
+ d->status = status;
+ d->exit_code = exit_code;
+
+ worker_push(process_exit_v, d);
+ pause();
+ }
+
+ // ---- Now we can do the actual cleanup
+
+ int st = enter_critical(CL_NOSWITCH);
+
+ p->status = status;
+ p->exit_code = exit_code;
+
+ while (p->threads != 0) {
+ thread_t *t = p->threads;
+ p->threads = p->threads->next_in_proc;
+
+ t->proc = 0;
+ kill_thread(t);
+ }
+
+ // release file descriptors
+ void release_fd(void* a, void* fd) {
+ unref_file((fs_handle_t*)fd);
+ }
+ hashtbl_iter(p->files, release_fd);
+ delete_hashtbl(p->files);
+ p->files = 0;
+
+ // unmap memory
+ while (p->regions != 0) {
+ munmap(p, p->regions->addr);
+ }
+ ASSERT(btree_count(p->regions_idx) == 0);
+ delete_btree(p->regions_idx);
+ p->regions_idx = 0;
+
+ // release filesystems
+ void release_fs(void* a, void* fs) {
+ unref_fs((fs_t*)fs);
+ }
+ hashtbl_iter(p->filesystems, release_fs);
+ delete_hashtbl(p->filesystems);
+ p->filesystems = 0;
+
+ // delete page directory
+ switch_pagedir(get_kernel_pagedir());
+ delete_pagedir(p->pd);
+ p->pd = 0;
+
+ // orphan children
+ while (p->children != 0) {
+ process_t *c = p->children;
+ p->children = c->next_child;
+
+ c->parent = 0;
+ c->next_child = 0;
+ }
+
+ if (p->parent == 0) {
+ free(p);
+ } else {
+ // TODO : notify parent
+ }
+
+ exit_critical(st);
+}
+
+void process_thread_deleted(thread_t *t) {
+ int st = enter_critical(CL_NOSWITCH);
+
+ process_t *p = t->proc;
+ if (p->threads == t) {
+ p->threads = t->next_in_proc;
+ } else {
+ for (thread_t *it = p->threads; it != 0; it = it->next_in_proc) {
+ if (it->next_in_proc == t) {
+ it->next_in_proc = t->next_in_proc;
+ break;
+ }
+ }
+ }
+
+ if (p->threads == 0 && p->status == PS_RUNNING)
+ process_exit(p, PS_FINISHED, 0);
+
+ exit_critical(st);
+}
+
// ================================== //
// MANAGING FILESYSTEMS FOR PROCESSES //
@@ -344,7 +456,7 @@ bool munmap(process_t *proc, void* addr) {
static void proc_user_exception(registers_t *regs) {
dbg_printf("Usermode exception in user process : exiting.\n");
dbg_dump_registers(regs);
- exit();
+ process_exit(current_process(), PS_FAILURE, FAIL_EXCEPTION);
}
static void proc_usermem_pf(void* p, registers_t *regs, void* addr) {
process_t *proc = (process_t*)p;
@@ -353,13 +465,13 @@ static void proc_usermem_pf(void* p, registers_t *regs, void* addr) {
if (r == 0) {
dbg_printf("Segmentation fault in process %d (0x%p : not mapped) : exiting.\n", proc->pid, addr);
dbg_dump_registers(regs);
- exit();
+ process_exit(current_process(), PS_FAILURE, (addr < (void*)PAGE_SIZE ? FAIL_ZEROPTR : FAIL_SEGFAULT));
}
bool wr = ((regs->err_code & PF_WRITE_BIT) != 0);
if (wr && !(r->mode & MM_WRITE)) {
dbg_printf("Segmentation fault in process %d (0x%p : not allowed to write) : exiting.\n", proc->pid, addr);
- exit();
+ process_exit(current_process(), PS_FAILURE, (addr < (void*)PAGE_SIZE ? FAIL_ZEROPTR : FAIL_SEGFAULT));
}
bool pr = ((regs->err_code & PF_PRESENT_BIT) != 0);
@@ -393,7 +505,7 @@ void probe_for_read(const void* addr, size_t len) {
if (r == 0 || addr + len > r->addr + r->size || !(r->mode & MM_READ)) {
dbg_printf("Access violation on read at 0x%p len 0x%p in process %d : exiting.\n",
addr, len, proc->pid);
- exit();
+ process_exit(current_process(), PS_FAILURE, FAIL_SC_SEGFAULT);
}
}
@@ -403,7 +515,7 @@ void probe_for_write(const void* addr, size_t len) {
if (r == 0 || addr + len > r->addr + r->size || !(r->mode & MM_WRITE)) {
dbg_printf("Access violation on write at 0x%p len 0x%p in process %d : exiting.\n",
addr, len, proc->pid);
- exit();
+ process_exit(current_process(), PS_FAILURE, FAIL_SC_SEGFAULT);
}
}
diff --git a/src/kernel/user/syscall.c b/src/kernel/user/syscall.c
index 009903c..d9131b2 100644
--- a/src/kernel/user/syscall.c
+++ b/src/kernel/user/syscall.c
@@ -33,9 +33,9 @@ static char* sc_copy_string(uint32_t s, uint32_t slen) {
static uint32_t exit_sc(sc_args_t args) {
dbg_printf("Proc %d exit with code %d\n", current_process()->pid, args.a);
- // TODO : use code... and process exiting is not supposed to be done this way
- exit();
+ process_exit(current_process(), PS_FINISHED, args.a);
+ ASSERT(false);
return 0;
}