From 96e5d8dc99010e2cec8bd2edf59545bd13f6f769 Mon Sep 17 00:00:00 2001 From: Kimplul Date: Sat, 28 May 2022 14:08:12 +0300 Subject: start implementing new proc/rpc handling + Essentially, separate root process and remote procedure call handling. I really should write down some documentation so I don't forget, but essentially: All threads share a common process virtual memory, and when a thread wants to make an rpc, it switches over to its own rpc vmem space that is linked to the remote process. --- Makefile | 2 +- arch/riscv64/kernel/proc.c | 4 +- common/dmem.c | 24 +++++++++--- common/elf.c | 5 ++- common/proc.c | 4 +- common/tcb.c | 91 +++++++++++++++++++++++++++++++++++----------- common/uapi/dispatch.c | 2 + common/vmem.c | 32 +++++++++------- include/apos/debug.h | 8 ++-- include/apos/tcb.h | 49 ++++++++++++++----------- include/apos/utils.h | 1 + include/arch/proc.h | 3 +- 12 files changed, 152 insertions(+), 73 deletions(-) diff --git a/Makefile b/Makefile index f676636..dad5309 100644 --- a/Makefile +++ b/Makefile @@ -97,7 +97,7 @@ apos.bin: init.bin kernel.bin format: find arch lib common include -iname '*.[ch]' |\ - uncrustify -c uncrustify.conf -q --no-backup -F - + uncrustify -c uncrustify.conf --no-backup -F - .PHONY: docs docs: diff --git a/arch/riscv64/kernel/proc.c b/arch/riscv64/kernel/proc.c index c95021d..0eb3ad2 100644 --- a/arch/riscv64/kernel/proc.c +++ b/arch/riscv64/kernel/proc.c @@ -25,14 +25,14 @@ stat_t set_return(vm_t v) return OK; } -stat_t prepare_thread(struct tcb *t) +stat_t set_thread(struct tcb *t, vm_t stack) { /* get location of registers in memory */ /* TODO: check alignment, should be fine but just to be sure */ struct riscv_regs *r = (struct riscv_regs *)(--t); /* insert important values into register slots */ - r->sp = (long)t->thread_stack_top; + r->sp = (long)stack; r->tp = (long)t; return OK; diff --git a/common/dmem.c b/common/dmem.c index 1d5608c..8bd9da3 100644 --- a/common/dmem.c +++ b/common/dmem.c @@ -5,6 +5,7 @@ * \todo Handle NUMA. */ +#include #include static struct mem_region_root pre_ram = { 0 }; @@ -61,6 +62,8 @@ stat_t dev_free_wrapper(struct vmem *b, pm_t *offset, vm_t vaddr, vm_t alloc_devmem(struct tcb *t, pm_t dev_start, size_t bytes, vmflags_t flags) { + hard_assert(t && is_proc(t), ERR_INVAL); + vm_t region = 0; if (dev_start < __pre_top) region = alloc_region(&pre_ram, bytes, 0, flags); @@ -71,14 +74,22 @@ vm_t alloc_devmem(struct tcb *t, pm_t dev_start, size_t bytes, vmflags_t flags) if (!region) return 0; - return map_fill_region(t->b_r, &dev_alloc_wrapper, dev_start, region, - bytes, flags, 0); + stat_t status = OK; + const vm_t w = map_fill_region(t->proc.vmem, &dev_alloc_wrapper, + dev_start, region, + bytes, flags, &status); + if (is_rpc(t) && status == INFO_SEFF) + clone_rpc_maps(t); + + return w; } stat_t free_devmem(struct tcb *t, vm_t dev_start) { + hard_assert(t && is_proc(t), ERR_INVAL); + pm_t dev_paddr = 0; - stat_vpage(t->b_r, dev_start, &dev_paddr, 0, 0); + stat_vpage(t->proc.vmem, dev_start, &dev_paddr, 0, 0); if (dev_paddr >= __pre_top && dev_paddr <= __post_base) return ERR_ADDR; @@ -94,8 +105,11 @@ stat_t free_devmem(struct tcb *t, vm_t dev_start) return ERR_NF; size_t region_size = __addr(m->end - m->start); - map_fill_region(t->b_r, &dev_free_wrapper, dev_paddr, dev_start, - region_size, 0, 0); + stat_t status = OK; + map_fill_region(t->proc.vmem, &dev_free_wrapper, dev_paddr, dev_start, + region_size, 0, &status); + if (is_rpc(t) && status == INFO_SEFF) + clone_rpc_maps(t); if (dev_paddr < __pre_top) free_region(&pre_ram, dev_paddr); diff --git a/common/elf.c b/common/elf.c index b88be89..88c5ddc 100644 --- a/common/elf.c +++ b/common/elf.c @@ -7,6 +7,7 @@ #include #include #include +#include static uint8_t __elf_to_uvflags(uint8_t elf_flags) { @@ -27,6 +28,8 @@ static uint8_t __elf_to_uvflags(uint8_t elf_flags) static void __map_exec(struct tcb *t, vm_t bin, uint8_t ei_c, vm_t phstart, size_t phnum, size_t phsize) { + hard_assert(t && is_proc(t), RETURN_VOID); + /* TODO: take alignment into consideration? */ /* TODO: take overlapping memory regions into account, probably mostly * by keeping track of previously allocated area and seeing if the @@ -51,7 +54,7 @@ static void __map_exec(struct tcb *t, vm_t bin, uint8_t ei_c, vm_t phstart, uint8_t elf_flags = program_header_prop(ei_c, runner, p_flags); uint8_t uvflags = __elf_to_uvflags(elf_flags); - map_allocd_region(t->b_r, start, vsz, default_flags, 0); + map_allocd_region(t->proc.vmem, start, vsz, default_flags, 0); vm_t vo = bin + program_header_prop(ei_c, runner, p_offset); vm_t vfz = program_header_prop(ei_c, runner, p_filesz); diff --git a/common/proc.c b/common/proc.c index 3f6da6d..bf8a4d1 100644 --- a/common/proc.c +++ b/common/proc.c @@ -18,7 +18,7 @@ stat_t prepare_proc(struct tcb *t, vm_t bin, vm_t interp) return ERR_INVAL; alloc_stacks(t); - prepare_thread(t); + set_thread(t, t->thread_stack_top); set_return(entry); return OK; } @@ -34,7 +34,7 @@ stat_t init_proc(void *fdt) /* set current tcb */ use_tcb(t); - use_vmem(t->b_r); + use_vmem(t->proc.vmem); /* allocate stacks after ELF file to make sure nothing of importance * clashes */ diff --git a/common/tcb.c b/common/tcb.c index 0809829..84218dc 100644 --- a/common/tcb.c +++ b/common/tcb.c @@ -63,7 +63,8 @@ static vm_t __setup_call_stack(struct tcb *t, size_t bytes) vmflags_t flags = VM_V | VM_R | VM_W | VM_U; for (size_t i = 1; i <= pages; ++i) { offset = alloc_page(BASE_PAGE, offset); - map_vpage(t->b_r, offset, PROC_STACK_TOP - BASE_PAGE_SIZE * i, + map_vpage(t->proc.vmem, offset, + PROC_STACK_TOP - BASE_PAGE_SIZE * i, flags, BASE_PAGE); } @@ -77,20 +78,21 @@ static vm_t __setup_thread_stack(struct tcb *t, size_t bytes) stat_t alloc_stacks(struct tcb *t) { - struct tcb *p = is_proc(t) ? t : t->proc; + /* get parent process */ + struct tcb *p = get_tcb(t->eid); t->thread_stack = __setup_thread_stack(p, __thread_stack_size); if (!t->thread_stack) return ERR_OOMEM; - t->call_stack = __setup_call_stack(p, __call_stack_size); - if (!t->call_stack) + /* call stack always starts at the same place in vmem. + * TODO: is this a security issue? */ + if (!__setup_call_stack(p, __call_stack_size)) return ERR_OOMEM; /* TODO: this only allows for a global stack size, what if a user wants * per thread stack sizes? */ t->thread_stack_top = t->thread_stack + __thread_stack_size; - t->call_stack_top = t->call_stack + __call_stack_size; return OK; } @@ -112,14 +114,17 @@ struct tcb *create_thread(struct tcb *p) if (likely(p)) { t->pid = p->pid; - t->proc = p; + t->proc.vmem = p->proc.vmem; } else { init_uvmem(t, UVMEM_START, UVMEM_END); + t->proc.vmem = create_vmem(); t->pid = t->tid; p = t; } - t->b_r = create_vmem(); + t->eid = t->pid; + t->rid = p->rid; + t->rpc.vmem = create_vmem(); return t; } @@ -149,13 +154,15 @@ struct tcb *create_proc(struct tcb *p) static stat_t __destroy_thread_data(struct tcb *t) { - /* free vmem */ - destroy_vmem(t->b_r); + /* free rpc vmem */ + destroy_vmem(t->rpc.vmem); /* free associated kernel stack and the structure itself */ vm_t bottom = align_down((vm_t)t, __o_size(MM_O0)); free_page(MM_O0, (pm_t)bottom); + /* TODO: free stacks */ + return OK; } @@ -168,11 +175,7 @@ stat_t destroy_thread(struct tcb *t) tcbs[t->tid] = 0; /* remove thread from process list */ - if (t->next) - t->next->prev = t->prev; - - if (t->prev) - t->prev->next = t->next; + detach_proc(get_rproc(t), t); return __destroy_thread_data(t); } @@ -182,13 +185,47 @@ stat_t destroy_proc(struct tcb *p) hard_assert(tcbs, ERR_NOINIT); hard_assert(is_proc(p), ERR_INVAL); - for (struct tcb *iter = p; (iter = iter->next);) + for (struct tcb *iter = p; (iter = iter->proc.next);) destroy_thread(iter); catastrophic_assert(destroy_uvmem(p)); return __destroy_thread_data(p); } +#define DEFINE_ATTACH(name, type) \ + stat_t name(struct tcb *r, struct tcb *t) \ + { \ + hard_assert(r != t, ERR_INVAL); \ + struct tcb *next = r->type.next; \ + t->type.next = next; \ +\ + if (next) { next->type.prev = t; } \ +\ + t->type.prev = r; \ + r->type.next = t; \ + return OK; \ + } + +DEFINE_ATTACH(attach_rpc, rpc); +DEFINE_ATTACH(attach_proc, proc); + +#define DEFINE_DETACH(name, type) \ + stat_t name(struct tcb *r, struct tcb *t) \ + { \ + MAYBE_UNUSED(r); \ + hard_assert(r != t, ERR_INVAL); \ + struct tcb *prev = t->type.prev; \ + struct tcb *next = t->type.next; \ +\ + if (prev) { prev->type.next = next; } \ + if (next) { next->type.prev = prev; } \ +\ + return OK; \ + } + +DEFINE_DETACH(detach_rpc, rpc); +DEFINE_DETACH(detach_proc, proc); + struct tcb *cur_tcb() { return cpu_tcb[cpu_id()]; @@ -197,10 +234,7 @@ struct tcb *cur_tcb() struct tcb *cur_proc() { struct tcb *t = cur_tcb(); - if (likely(is_proc(t))) - return t; - else - return t->proc; + return get_tcb(t->eid); } void use_tcb(struct tcb *t) @@ -215,12 +249,25 @@ struct tcb *get_tcb(id_t tid) return tcbs[tid]; } -stat_t clone_tcb_maps(struct tcb *r) +stat_t clone_rpc_maps(struct tcb *r) +{ + hard_assert(r && is_proc(r), ERR_INVAL); + struct tcb *t = r; + while ((t = t->rpc.next)) { + stat_t ret = clone_uvmem(r->proc.vmem, t->rpc.vmem); + if (ret) + return ret; + } + + return OK; +} + +stat_t clone_proc_maps(struct tcb *r) { hard_assert(r && is_proc(r), ERR_INVAL); struct tcb *t = r; - while ((t = t->next)) { - stat_t ret = clone_uvmem(r->b_r, t->b_r); + while ((t = t->proc.next)) { + stat_t ret = clone_uvmem(r->proc.vmem, t->proc.vmem); if (ret) return ret; } diff --git a/common/uapi/dispatch.c b/common/uapi/dispatch.c index 924a0ed..df211d5 100644 --- a/common/uapi/dispatch.c +++ b/common/uapi/dispatch.c @@ -3,6 +3,7 @@ * Syscall dispatch. */ +#include #include static const sys_t syscall_table[] = { @@ -39,6 +40,7 @@ static const sys_t syscall_table[] = { }; SYSCALL_DEFINE0(noop)(){ + info("sys_noop\n"); return (struct sys_ret){ OK, 0 }; } diff --git a/common/vmem.c b/common/vmem.c index 74701d8..230620f 100644 --- a/common/vmem.c +++ b/common/vmem.c @@ -19,7 +19,7 @@ static stat_t __free_mapped_region(struct tcb *t, struct mem_region *m) { stat_t status = OK; pm_t pa = __addr(m->end - m->start); - if (unmap_freed_region(t->b_r, m->start, pa, m->flags, &status)) + if (unmap_freed_region(t->proc.vmem, m->start, pa, m->flags, &status)) return ERR_MISC; return status; @@ -56,9 +56,13 @@ vm_t alloc_uvmem(struct tcb *t, size_t size, vmflags_t flags) stat_t status = OK; const vm_t v = alloc_region(&t->sp_r, size, &size, flags); - const vm_t w = map_allocd_region(t->b_r, v, size, flags, &status); - if (status == INFO_SEFF) - clone_tcb_maps(t); + const vm_t w = map_allocd_region(t->proc.vmem, v, size, flags, &status); + /* TODO: this could be changed so that each thread allocated the memory + * region for itself to start with, and only when someone tries to + * access it from some other thread, is it actually cloned. Would likely + * need some major reworkings, so this is good enough for now. */ + if (is_rpc(t) && status == INFO_SEFF) + clone_rpc_maps(t); return w; } @@ -69,10 +73,10 @@ vm_t alloc_fixed_uvmem(struct tcb *t, vm_t start, size_t size, vmflags_t flags) stat_t status = OK; const vm_t v = alloc_fixed_region(&t->sp_r, start, size, &size, flags); - const vm_t w = map_allocd_region(t->b_r, v, size, flags, &status); + const vm_t w = map_allocd_region(t->proc.vmem, v, size, flags, &status); - if (status == INFO_SEFF) - clone_tcb_maps(t); + if (is_rpc(t) && status == INFO_SEFF) + clone_rpc_maps(t); return w; } @@ -85,10 +89,10 @@ vm_t alloc_shared_uvmem(struct tcb *t, size_t size, vmflags_t flags) stat_t status = OK; const vm_t v = alloc_region(&t->sp_r, size, &size, flags | MR_SHARED | MR_OWNED); - const vm_t w = map_shared_region(t->b_r, v, size, flags, &status); + const vm_t w = map_shared_region(t->proc.vmem, v, size, flags, &status); - if (status == INFO_SEFF) - clone_tcb_maps(t); + if (is_rpc(t) && status == INFO_SEFF) + clone_rpc_maps(t); return w; } @@ -110,8 +114,8 @@ vm_t ref_shared_uvmem(struct tcb *t1, struct tcb *t2, vm_t va, vmflags_t flags) vm_t runner = v; for (; pages; --pages) { pm_t paddr; - stat_vpage(t1->b_r, v, &paddr, 0, 0); - map_vpage(t2->b_r, paddr, runner, flags, MM_O0); + stat_vpage(t1->proc.vmem, v, &paddr, 0, 0); + map_vpage(t2->proc.vmem, paddr, runner, flags, MM_O0); runner += __o_size(MM_O0); } @@ -128,8 +132,8 @@ stat_t free_uvmem(struct tcb *t, vm_t va) free_region(&t->sp_r, va); stat_t status = __free_mapped_region(t, m); - if (status == INFO_SEFF) - return clone_tcb_maps(t); + if (is_rpc(t) && status == INFO_SEFF) + return clone_rpc_maps(t); return status; } diff --git a/include/apos/debug.h b/include/apos/debug.h index 6d49f12..88668ff 100644 --- a/include/apos/debug.h +++ b/include/apos/debug.h @@ -148,11 +148,11 @@ struct dbg_info dbg_from_fdt(const void *fdt); #define COMMON_FORMAT "[%s] %s:%d\n\t" #define COMMON_ARGS(s) s, __FILE__, __LINE__ -#define bug(fmt, ...) dbg(COMMON_FORMAT fmt, COMMON_ARGS("BUG"), __VA_ARGS__) -#define warn(fmt, ...) dbg(COMMON_FORMAT fmt, COMMON_ARGS("WARN"), __VA_ARGS__) -#define info(fmt, ...) dbg(COMMON_FORMAT fmt, COMMON_ARGS("INFO"), __VA_ARGS__) +#define bug(fmt, ...) dbg(COMMON_FORMAT fmt, COMMON_ARGS("BUG"),##__VA_ARGS__) +#define warn(fmt, ...) dbg(COMMON_FORMAT fmt, COMMON_ARGS("WARN"),##__VA_ARGS__) +#define info(fmt, ...) dbg(COMMON_FORMAT fmt, COMMON_ARGS("INFO"),##__VA_ARGS__) #define error(fmt, ...) \ - dbg(COMMON_FORMAT fmt, COMMON_ARGS("ERROR"), __VA_ARGS__) + dbg(COMMON_FORMAT fmt, COMMON_ARGS("ERROR"),##__VA_ARGS__) #else diff --git a/include/apos/tcb.h b/include/apos/tcb.h index 2b602f6..7abd432 100644 --- a/include/apos/tcb.h +++ b/include/apos/tcb.h @@ -11,23 +11,31 @@ #include /* arch-specific data */ /* process(/main) threads don't have any previous threads */ -#define is_proc(t) (!t->prev) +#define is_proc(t) (t->rid == t->tid) +#define is_rpc(t) (t->rid == t->pid) + +#define get_proc(t) (get_tcb(t->eid)) +#define get_rproc(t) (get_tcb(t->rid)) + +/* forward declaration */ +struct tcb; + +struct tcb_ctx { + struct vmem *vmem; + struct tcb *next; + struct tcb *prev; +}; struct tcb { struct arch_tcbd tcbd; - /* mapping data - * TODO: should mem_region_root be renamed mem_root or something? feels - * kind of clunky */ - union { - /* if we're the main thread, we control the memory regions */ - struct mem_region_root sp_r; - /* if we're not the main thread, we have a pointer to the main - * thread */ - struct tcb *proc; - }; + /* mapping data */ + struct mem_region_root sp_r; + id_t eid; id_t pid; + + id_t rid; id_t tid; vm_t callback; @@ -35,15 +43,8 @@ struct tcb { vm_t thread_stack; vm_t thread_stack_top; - vm_t call_stack; - vm_t call_stack_top; - - /* vm root branch */ - struct vmem *b_r; - - /* linked list of threads in this process */ - struct tcb *next; - struct tcb *prev; + struct tcb_ctx proc; + struct tcb_ctx rpc; }; void init_tcbs(); @@ -54,12 +55,18 @@ struct tcb *create_proc(struct tcb *p); stat_t destroy_thread(struct tcb *t); stat_t destroy_proc(struct tcb *p); +stat_t attach_rpc(struct tcb *r, struct tcb *t); +stat_t detach_rpc(struct tcb *r, struct tcb *t); +stat_t attach_proc(struct tcb *r, struct tcb *t); +stat_t detach_proc(struct tcb *r, struct tcb *t); + struct tcb *cur_tcb(); void use_tcb(struct tcb *); struct tcb *get_tcb(id_t tid); -stat_t clone_tcb_maps(struct tcb *); +stat_t clone_proc_maps(struct tcb *); +stat_t clone_rpc_maps(struct tcb *); stat_t alloc_stacks(struct tcb *); #endif /* APOS_TCB_H */ diff --git a/include/apos/utils.h b/include/apos/utils.h index 03bb801..c4a83be 100644 --- a/include/apos/utils.h +++ b/include/apos/utils.h @@ -27,6 +27,7 @@ #define QUOTE(x) QUOTE2(x) #define UNUSED(x) ((void)(x)) +#define MAYBE_UNUSED(x) UNUSED(x) #include diff --git a/include/arch/proc.h b/include/arch/proc.h index 1dcb0af..9af44c9 100644 --- a/include/arch/proc.h +++ b/include/arch/proc.h @@ -13,8 +13,9 @@ #endif stat_t set_return(vm_t r); +stat_t set_ipc(struct tcb *t, id_t pid, id_t tid); /*TODO: should this be in arch/tcb.h or something? */ -stat_t prepare_thread(struct tcb *t); +stat_t set_thread(struct tcb *t, vm_t stack); stat_t run_init(struct tcb *t, void *fdt); #endif /* APOS_ARCH_PROC_H */ -- cgit v1.3