aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/riscv64/kernel/entry.S19
-rw-r--r--arch/riscv64/kernel/except.c21
-rw-r--r--arch/riscv64/kernel/proc.c14
-rw-r--r--arch/riscv64/kernel/smp.c1
-rw-r--r--arch/riscv64/kernel/vmem.c17
-rw-r--r--include/arch/tcb.h2
-rw-r--r--include/kmi/panic.h4
-rw-r--r--include/kmi/vmem.h2
-rw-r--r--src/orphanage.c2
-rw-r--r--src/panic.c4
-rw-r--r--src/pmem.c4
-rw-r--r--src/proc.c1
-rw-r--r--src/tcb.c28
-rw-r--r--src/uapi/ipc.c1
-rw-r--r--src/uapi/proc.c26
-rw-r--r--src/vmem.c23
-rw-r--r--tests/create/init.c2
-rw-r--r--tests/fork-exhaustion/init.c4
18 files changed, 122 insertions, 53 deletions
diff --git a/arch/riscv64/kernel/entry.S b/arch/riscv64/kernel/entry.S
index 0bc999e..f894e0d 100644
--- a/arch/riscv64/kernel/entry.S
+++ b/arch/riscv64/kernel/entry.S
@@ -62,6 +62,7 @@ handle_trap:
bnez tp, continue_trap
/* the trap came from the kernel, should never happen so just panic
* and abort or whatever */
+ csrw CSR_SSCRATCH, x0
csrr a0, CSR_SEPC
csrr a1, CSR_STVAL
csrr a2, CSR_SCAUSE
@@ -92,8 +93,16 @@ continue_trap:
save_caller
mv a0, s10
+ /* get actual kernel stack into sp */
+ mv sp, tp
call riscv_handle_interrupt
+ lr sp, offsetof_regs(tp)
+ addi sp, sp, -sizeof_registers
+ /* set execution continuation */
+ lr s10, offsetof_exec(tp)
+ csrw CSR_SEPC, s10
+
j _load_context
handle_exception:
@@ -108,7 +117,14 @@ handle_exception:
csrr a0, CSR_SEPC
csrr a1, CSR_STVAL
csrr a2, CSR_SCAUSE
- call unhandled_panic
+ mv sp, tp
+ call riscv_handle_exception
+
+ lr sp, offsetof_regs(tp)
+ addi sp, sp, -sizeof_registers
+ /* set execution continuation */
+ lr s10, offsetof_exec(tp)
+ csrw CSR_SEPC, s10
j _load_context
@@ -129,6 +145,7 @@ fast_dispatch:
csrr s10, CSR_SEPC
sr s10, offsetof_exec(tp)
/* jump to C */
+ mv sp, tp
call dispatch
/* if we had a thread switch, load kernel stack of current thread and
* restore its context */
diff --git a/arch/riscv64/kernel/except.c b/arch/riscv64/kernel/except.c
new file mode 100644
index 0000000..e46b584
--- /dev/null
+++ b/arch/riscv64/kernel/except.c
@@ -0,0 +1,21 @@
+#include <kmi/types.h>
+#include <kmi/panic.h>
+#include <kmi/vmem.h>
+#include <kmi/bkl.h>
+
+void riscv_handle_exception(void *pc, void *addr, unsigned long id)
+{
+ switch (id) {
+ case 12:
+ case 13:
+ case 15: {
+ bkl_lock();
+ handle_pagefault((vm_t)addr);
+ bkl_unlock();
+ break;
+ }
+
+ default:
+ unhandled_panic(pc, addr, id);
+ }
+}
diff --git a/arch/riscv64/kernel/proc.c b/arch/riscv64/kernel/proc.c
index f012b20..3ae2e15 100644
--- a/arch/riscv64/kernel/proc.c
+++ b/arch/riscv64/kernel/proc.c
@@ -81,7 +81,7 @@ void set_thread(struct tcb *t)
struct riscv_regs *r = (struct riscv_regs *)(t->regs) - 1;
/* insert important values into register slots */
- r->sp = (long)t->thread_stack + t->thread_stack_size;
+ r->sp = (long)t->rpc_stack - BASE_PAGE_SIZE;
}
void set_stack(struct tcb *t, vm_t s)
@@ -99,9 +99,15 @@ vm_t get_stack(struct tcb *t)
void copy_regs(struct tcb *d, struct tcb *s)
{
- struct riscv_regs *rd = (struct riscv_regs *)(d->regs) - 1;
- struct riscv_regs *rs = (struct riscv_regs *)(s->regs) - 1;
- *rd = *rs;
+ struct riscv_regs rs = *((struct riscv_regs *)(s->regs) - 1);
+ /* again, not a fan of this, I guess we could query the underlying
+ * physical page? Would that be any faster? */
+ use_vmem(d->rpc.vmem);
+
+ struct riscv_regs *rd = ((struct riscv_regs *)(d->regs) - 1);
+ *rd = rs;
+
+ use_vmem(s->rpc.vmem);
}
void adjust_ipi(struct tcb *t)
diff --git a/arch/riscv64/kernel/smp.c b/arch/riscv64/kernel/smp.c
index acb5f04..5e30fee 100644
--- a/arch/riscv64/kernel/smp.c
+++ b/arch/riscv64/kernel/smp.c
@@ -139,7 +139,6 @@ __noreturn void core_bringup(long hartid)
struct tcb *t = create_thread(init);
assert(t);
- alloc_stack(t);
/* init is special in that all threads jump to the entrypoint of the
* program */
t->exec = init->exec;
diff --git a/arch/riscv64/kernel/vmem.c b/arch/riscv64/kernel/vmem.c
index cf1391e..5a44f53 100644
--- a/arch/riscv64/kernel/vmem.c
+++ b/arch/riscv64/kernel/vmem.c
@@ -563,6 +563,23 @@ size_t max_rpc_size()
return SZ_512K;
}
+void copy_rpc_stack(struct tcb *t, struct tcb *c)
+{
+ /* this is of terrible, copying 2MiB for every fork, not
+ * great. TODO: use the direct pointer to the rpc stack element? */
+ for (size_t i = 0; i < rpc_pages; ++i) {
+ pm_t p1, p2;
+ stat_t ok1 = stat_vpage(t->rpc.vmem, RPC_STACK_BASE + BASE_PAGE_SIZE * i,
+ &p1, NULL, NULL);
+
+ stat_t ok2 = stat_vpage(c->rpc.vmem, RPC_STACK_BASE + BASE_PAGE_SIZE * i,
+ &p2, NULL, NULL);
+
+ assert(ok1 == OK && ok2 == OK);
+ memcpy((void *)p2, (void *)p1, BASE_PAGE_SIZE);
+ }
+}
+
stat_t setup_rpc_stack(struct tcb *t)
{
/* by default rpc stack is marked inaccessible to generate segfaults on
diff --git a/include/arch/tcb.h b/include/arch/tcb.h
index 4f75419..8eddb07 100644
--- a/include/arch/tcb.h
+++ b/include/arch/tcb.h
@@ -42,6 +42,8 @@ stat_t setup_rpc_stack(struct tcb *t);
*/
void destroy_rpc_stack(struct tcb *t);
+void copy_rpc_stack(struct tcb *t, struct tcb *c);
+
/**
* Reset RPC stack to top.
*
diff --git a/include/kmi/panic.h b/include/kmi/panic.h
index 7a498fa..121323c 100644
--- a/include/kmi/panic.h
+++ b/include/kmi/panic.h
@@ -4,6 +4,8 @@
#ifndef KMI_PANIC_H
#define KMI_PANIC_H
+#include <kmi/attrs.h>
+
/**
* @file panic.h
* Kernel panic handler.
@@ -20,4 +22,6 @@
*/
__noreturn void kernel_panic(void *pc, void *addr, long cause);
+__noreturn void unhandled_panic(void *pc, void *addr, long cause);
+
#endif /* KMI_PANIC_H */
diff --git a/include/kmi/vmem.h b/include/kmi/vmem.h
index c53f009..9424e33 100644
--- a/include/kmi/vmem.h
+++ b/include/kmi/vmem.h
@@ -174,4 +174,6 @@ stat_t copy_uvmem(struct tcb *d, struct tcb *s);
*/
vmflags_t sanitize_uvflags(vmflags_t flags);
+void handle_pagefault(vm_t addr);
+
#endif /* KMI_VMEM_H */
diff --git a/src/orphanage.c b/src/orphanage.c
index 6de520f..6cde48a 100644
--- a/src/orphanage.c
+++ b/src/orphanage.c
@@ -31,7 +31,6 @@ void unorphanize(struct tcb *t)
struct tcb *init = get_tcb(1);
reference_thread(init);
- free_stack(t);
reset_rpc_stack(t);
id_t old_rid = t->rid;
@@ -41,7 +40,6 @@ void unorphanize(struct tcb *t)
clone_uvmem(init->proc.vmem, t->rpc.vmem);
use_vmem(t->rpc.vmem);
- alloc_stack(t);
assert(init->callback);
set_ret4(t, 0, t->tid, SYS_USER_ORPHANED, old_rid);
diff --git a/src/panic.c b/src/panic.c
index 34bfa69..afb598e 100644
--- a/src/panic.c
+++ b/src/panic.c
@@ -14,8 +14,8 @@
void kernel_panic(void *pc, void *addr, long cause)
{
/* could be useful to print out register values as well? */
- error("thread %d kernel paniced at pc: %p with address %p and cause %lx\n",
- cur_tcb()->cpu_id, pc, addr, cause);
+ error("kernel paniced at pc: %p with address %p and cause %lx\n",
+ pc, addr, cause);
info("attempting to reboot\n");
diff --git a/src/pmem.c b/src/pmem.c
index a0cd4ec..9c839f1 100644
--- a/src/pmem.c
+++ b/src/pmem.c
@@ -277,9 +277,7 @@ static pm_t __alloc_page(enum mm_order order)
__get_bit(bucket, a, &set, &bit);
bmap = __get_set(bucket, set);
- /* hmm, I might be running into a compiler bug here. Adding this
- * assert makes my error go away, weird. */
- assert(bmap->size <= order_width(order + 1));
+ bmap->size = order_width(order + 1);
bmap->next = NULL;
bmap->prev = NULL;
diff --git a/src/proc.c b/src/proc.c
index 1c90fdc..62b3e42 100644
--- a/src/proc.c
+++ b/src/proc.c
@@ -25,7 +25,6 @@ stat_t prepare_proc(struct tcb *t, vm_t bin, vm_t interp)
return ERR_INVAL;
t->callback = entry;
- alloc_stack(t);
set_thread(t);
set_return(t, entry);
return OK;
diff --git a/src/tcb.c b/src/tcb.c
index eebaaa4..f5b1652 100644
--- a/src/tcb.c
+++ b/src/tcb.c
@@ -90,24 +90,6 @@ static id_t __alloc_tid(struct tcb *t)
return ERR_NF;
}
-stat_t alloc_stack(struct tcb *t)
-{
- /* get parent process */
- struct tcb *p = get_tcb(t->eid);
- assert(p);
-
- if (setup_rpc_stack(t))
- return ERR_OOMEM;
-
- t->regs = t->rpc_stack - sizeof(struct call_ctx);
- return OK;
-}
-
-void free_stack(struct tcb *t)
-{
- destroy_rpc_stack(t);
-}
-
static stat_t __init_free_thread(struct tcb *t)
{
if (!(t->proc.vmem = create_vmem()))
@@ -130,6 +112,7 @@ static stat_t __init_free_thread(struct tcb *t)
return ERR_OOMEM;
}
+ t->regs = t->rpc_stack - sizeof(struct call_ctx);
t->pid = t->tid;
t->eid = t->tid;
t->rid = t->tid;
@@ -156,6 +139,7 @@ static stat_t __init_owned_thread(struct tcb *p, struct tcb *t)
return ERR_OOMEM;
}
+ t->regs = t->rpc_stack - sizeof(struct call_ctx);
reference_thread(p);
return OK;
}
@@ -208,16 +192,12 @@ struct tcb *create_thread(struct tcb *p)
static stat_t __copy_proc(struct tcb *p, struct tcb *n)
{
/** @todo setup rpc stack stuff */
- /** @todo I think keeping track of userspace stack stuff is unnecessary,
- * unless we want unlimited stack size but that sounds dumb. Anycase, we
- * need to duplicate stack info, whatever we do. */
n->exec = p->exec;
n->callback = p->callback;
- n->thread_stack = p->thread_stack;
- n->thread_stack_size = p->thread_stack_size;
copy_regs(n, p);
copy_caps(n->caps, p->caps);
+ copy_rpc_stack(p, n);
return copy_uvmem(n, p);
}
@@ -270,8 +250,6 @@ stat_t destroy_thread(struct tcb *t)
/* if we're our own root process, we unreference ourselves later */
unreference_thread(r);
- free_stack(t);
-
/* free memory backing rpc stack */
destroy_rpc_stack(t);
diff --git a/src/uapi/ipc.c b/src/uapi/ipc.c
index f1db76f..6e33165 100644
--- a/src/uapi/ipc.c
+++ b/src/uapi/ipc.c
@@ -102,7 +102,6 @@ static inline vm_t enter_rpc(struct tcb *t, struct sys_ret a,
* limit just give it more.
* */
t->rpc_stack = new_stack;
- set_stack(t, new_stack);
return new_stack;
}
diff --git a/src/uapi/proc.c b/src/uapi/proc.c
index b010220..3b65a04 100644
--- a/src/uapi/proc.c
+++ b/src/uapi/proc.c
@@ -38,19 +38,18 @@ SYSCALL_DEFINE5(create)(struct tcb *t, sys_arg_t func,
if (!c)
return_args1(t, ERR_OOMEM);
- /** @todo there's quite a bit of overlap between this and what
- * core_bringup() is doing, might separate this out into its own
- * function? */
- if (alloc_stack(c)) {
- destroy_thread(c);
- return_args1(t, ERR_OOMEM);
- }
+ /* temporarily jump into new thread memory to set arguments */
+ use_vmem(c->rpc.vmem);
+ /* this visit is likely not the cheapest thing in the universe, are
+ * there ways to speed up thread creation? */
set_thread(c);
-
set_ret5(c, c->tid, d0, d1, d2, d3);
set_return(c, func);
+ /* return back */
+ use_vmem(t->rpc.vmem);
+
c->notify_id = t->notify_id;
return_args1(t, c->tid);
}
@@ -81,9 +80,13 @@ SYSCALL_DEFINE0(fork)(struct tcb *t)
if (!n)
return_args1(t, ERR_OOMEM);
+ /* again, probably not fantastic that we're jumping between address
+ * spaces like this */
+ use_vmem(n->rpc.vmem);
/* prepare args for when we eventually swap to the new proc, giving
* parent ID as third return value */
set_args2(n, 0, get_eproc(t)->pid);
+ use_vmem(t->rpc.vmem);
n->notify_id = c->notify_id;
return_args1(t, n->pid);
@@ -189,6 +192,10 @@ SYSCALL_DEFINE2(spawn)(struct tcb *t, sys_arg_t bin, sys_arg_t interp)
*/
static void swap(struct tcb *t, struct tcb *s)
{
+ /* set return value for current thread, important to do first since
+ * use_tcb() switches the register slots, really easy to miss, not great */
+ set_args1(t, OK);
+
/* switch over to new thread */
use_tcb(s);
@@ -201,9 +208,6 @@ static void swap(struct tcb *t, struct tcb *s)
return;
}
- /* set return value for current thread */
- set_args1(t, OK);
-
/* handle possible queued notification */
if (s->notify_flags)
notify(s, 0);
diff --git a/src/vmem.c b/src/vmem.c
index f4f1bf7..bd0cd7c 100644
--- a/src/vmem.c
+++ b/src/vmem.c
@@ -9,6 +9,7 @@
#include <kmi/regions.h>
#include <kmi/assert.h>
#include <kmi/string.h>
+#include <kmi/panic.h>
#include <kmi/debug.h>
#include <kmi/bits.h>
#include <kmi/vmem.h>
@@ -408,3 +409,25 @@ vmflags_t sanitize_uvflags(vmflags_t flags)
{
return (flags & (VM_R | VM_W | VM_X)) | VM_V | VM_U;
}
+
+void handle_pagefault(vm_t addr)
+{
+ struct tcb *t = cur_tcb();
+ struct tcb *p = get_cproc(t);
+
+ size_t ref = __page(addr);
+
+ struct mem_region *m = find_closest_used_region(&p->uvmem.region, addr);
+ if (!m || (ref < m->start || ref > m->end) || !is_region_used(m)) {
+ error("cannot handle actual page fault just yet :(\n");
+ kernel_panic(NULL, NULL, 0);
+ return;
+ }
+
+ /* this is a valid address so presumably the proc virtual memory has
+ * some changes that haven't been reflected over in our rpc virtual
+ * memory so make them visible */
+ clone_uvmem(p->proc.vmem, t->rpc.vmem);
+ flush_tlb_all();
+ return;
+}
diff --git a/tests/create/init.c b/tests/create/init.c
index ca38c22..b596fe1 100644
--- a/tests/create/init.c
+++ b/tests/create/init.c
@@ -4,8 +4,8 @@ static int data = 0;
void callback(id_t tid, int d0, int d1, int d2, int d3)
{
- UNUSED(tid);
printf("hello from new thread\n");
+ check(tid == 2, "wrond tid\n");
check(d0 == 1, "wrong d0\n");
check(d1 == 2, "wrond d1\n");
check(d2 == 3, "wrong d2\n");
diff --git a/tests/fork-exhaustion/init.c b/tests/fork-exhaustion/init.c
index 0e330f4..7758613 100644
--- a/tests/fork-exhaustion/init.c
+++ b/tests/fork-exhaustion/init.c
@@ -11,8 +11,10 @@ START(pid, tid, d0, d1, d2, d3)
check(pid == 0, "illegal pid for init\n");
id_t our_id = 0;
+
+ int counter = 0;
while (1) {
- printf("forking\n");
+ printf("forking %d\n", counter++);
id_t new_id = sys_fork(&our_id);
if (new_id < 0)
break;