diff options
| author | Kimplul <kimi.h.kuparinen@gmail.com> | 2024-10-30 00:57:33 +0200 |
|---|---|---|
| committer | Kimplul <kimi.h.kuparinen@gmail.com> | 2024-10-30 00:57:33 +0200 |
| commit | 44a73ecab6e91519627786a5101cd4f1a2f2b0d7 (patch) | |
| tree | f5c811050a4d5ee832f5883c4d0c27efddd60817 | |
| parent | 5976ad390a15726c3ddfacf8c8b282c8350f9554 (diff) | |
| download | kmi-44a73ecab6e91519627786a5101cd4f1a2f2b0d7.tar.gz kmi-44a73ecab6e91519627786a5101cd4f1a2f2b0d7.zip | |
most of the way to passing tests
| -rw-r--r-- | arch/riscv64/kernel/entry.S | 19 | ||||
| -rw-r--r-- | arch/riscv64/kernel/except.c | 21 | ||||
| -rw-r--r-- | arch/riscv64/kernel/proc.c | 14 | ||||
| -rw-r--r-- | arch/riscv64/kernel/smp.c | 1 | ||||
| -rw-r--r-- | arch/riscv64/kernel/vmem.c | 17 | ||||
| -rw-r--r-- | include/arch/tcb.h | 2 | ||||
| -rw-r--r-- | include/kmi/panic.h | 4 | ||||
| -rw-r--r-- | include/kmi/vmem.h | 2 | ||||
| -rw-r--r-- | src/orphanage.c | 2 | ||||
| -rw-r--r-- | src/panic.c | 4 | ||||
| -rw-r--r-- | src/pmem.c | 4 | ||||
| -rw-r--r-- | src/proc.c | 1 | ||||
| -rw-r--r-- | src/tcb.c | 28 | ||||
| -rw-r--r-- | src/uapi/ipc.c | 1 | ||||
| -rw-r--r-- | src/uapi/proc.c | 26 | ||||
| -rw-r--r-- | src/vmem.c | 23 | ||||
| -rw-r--r-- | tests/create/init.c | 2 | ||||
| -rw-r--r-- | tests/fork-exhaustion/init.c | 4 |
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"); @@ -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; @@ -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; @@ -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); @@ -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; |
