diff options
| author | Kimplul <kimi.h.kuparinen@gmail.com> | 2022-11-13 15:15:22 +0200 |
|---|---|---|
| committer | Kimplul <kimi.h.kuparinen@gmail.com> | 2022-11-13 15:15:22 +0200 |
| commit | e6dc962ef7758c438039d7f8ac7e2bf3ebcb5c10 (patch) | |
| tree | 9484a801bc205e0e5f921ebeaba0a15e4d561400 | |
| parent | b36e3b83b402ee054c319f0472b16a2bd64bba7e (diff) | |
| download | kmi-e6dc962ef7758c438039d7f8ac7e2bf3ebcb5c10.tar.gz kmi-e6dc962ef7758c438039d7f8ac7e2bf3ebcb5c10.zip | |
improve documentation on new features
| -rw-r--r-- | arch/riscv64/asm/asm-offsets.c | 3 | ||||
| -rw-r--r-- | arch/riscv64/conf/init.c | 46 | ||||
| -rw-r--r-- | arch/riscv64/include/tcb.h | 4 | ||||
| -rw-r--r-- | arch/riscv64/kernel/proc.c | 14 | ||||
| -rw-r--r-- | arch/riscv64/kernel/vmem.c | 85 | ||||
| -rw-r--r-- | common/tcb.c | 39 | ||||
| -rw-r--r-- | common/uapi/ipc.c | 5 | ||||
| -rw-r--r-- | common/vmem.c | 5 | ||||
| -rw-r--r-- | docs/doxygen.conf | 2 | ||||
| -rw-r--r-- | include/apos/tcb.h | 14 | ||||
| -rw-r--r-- | include/arch/vmem.h | 5 |
11 files changed, 158 insertions, 64 deletions
diff --git a/arch/riscv64/asm/asm-offsets.c b/arch/riscv64/asm/asm-offsets.c index 1239489..731dd4a 100644 --- a/arch/riscv64/asm/asm-offsets.c +++ b/arch/riscv64/asm/asm-offsets.c @@ -86,6 +86,9 @@ void asm_offsets() OFFSETOF(exec, struct tcb); OFFSETOF(regs, struct tcb); + /* At the moment tcbd is just a single register slot, so this works, but + * if it's expanded in the future I'll need to figure out a way to + * target specific substructure members. */ OFFSETOF(tcbd, struct tcb); SIZEOF(tcb, struct tcb); } diff --git a/arch/riscv64/conf/init.c b/arch/riscv64/conf/init.c index 438bff4..de9814e 100644 --- a/arch/riscv64/conf/init.c +++ b/arch/riscv64/conf/init.c @@ -1,16 +1,25 @@ -/* This file is allowed to be undocumented as it is only temporarily in this - * tree. At some point in the (hopefully) near future, I intend to move the +/** + * Header for silencing scripts/warn-undocumented + * + * @file init.c + * + * Test init program. + * + * This file is only temporarily in this tree. + * At some point in the (hopefully) near future, I intend to move the * kernel code and initrd generation stuff into separate repositories, to make * things easier for myself. For now though, this is good enough. + * + * Compile with + * riscv64-unknown-elf-gcc -ffreestanding -nostdlib + * + * Create initrd with + * echo init | cpio -H newc -o > initrd */ -/* compile with riscv64-unknown-elf-gcc -ffreestanding -nostdlib */ -/* create initrd with echo init | cpio -H newc -o > initrd */ #include <stdint.h> #include "../../../include/apos/syscalls.h" -#define CLOBBER_LIST "a0", "a1", "a2", "a3", "a4", "a5" - struct sys_ret { long a0, a1, a2, a3, a4, a5; }; @@ -25,8 +34,10 @@ struct sys_ret ecall(struct sys_ret s) register long a5 asm ("a5") = s.a5; asm volatile ("ecall" - : "=r"(a0), "=r"(a1), "=r"(a2), "=r"(a3), "=r"(a4), "=r"(a5) - : "r"(a0), "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5)); + : "=r" (a0), "=r" (a1), "=r" (a2), "=r" (a3), "=r" (a4), + "=r" (a5) + : "r" (a0), "r" (a1), "r" (a2), "r" (a3), "r" (a4), + "r" (a5)); return (struct sys_ret){a0, a1, a2, a3, a4, a5}; } @@ -134,6 +145,7 @@ static void sys_ipc_server(void *f) print_value("ipc_server() failed with error ", r.a0); } +/** Helper for ipc arguments/return values. */ struct ipc_args { long a0, a1, a2, a3; }; @@ -141,11 +153,11 @@ struct ipc_args { static struct ipc_args sys_ipc_req(long tid, long d0, long d1, long d2, long d3) { struct sys_ret r = {.a0 = SYS_IPC_REQ, - .a1 = tid, - .a2 = d0, - .a3 = d1, - .a4 = d2, - .a5 = d3}; + .a1 = tid, + .a2 = d0, + .a3 = d1, + .a4 = d2, + .a5 = d3}; r = ecall(r); @@ -158,10 +170,10 @@ static struct ipc_args sys_ipc_req(long tid, long d0, long d1, long d2, long d3) static void sys_ipc_resp(long d0, long d1, long d2, long d3) { struct sys_ret r = {.a0 = SYS_IPC_RESP, - .a1 = d0, - .a2 = d1, - .a3 = d2, - .a4 = d3}; + .a1 = d0, + .a2 = d1, + .a3 = d2, + .a4 = d3}; ecall(r); } diff --git a/arch/riscv64/include/tcb.h b/arch/riscv64/include/tcb.h index 829ce6a..f3e4cd0 100644 --- a/arch/riscv64/include/tcb.h +++ b/arch/riscv64/include/tcb.h @@ -11,11 +11,9 @@ /** * riscv-specific thread handling stuff. - * - * Empty for now, but should probably be filled with stuff like register - * saving of something */ struct arch_tcbd { + /** Extra scratch register. */ long scratch; }; diff --git a/arch/riscv64/kernel/proc.c b/arch/riscv64/kernel/proc.c index 4180bf8..1990523 100644 --- a/arch/riscv64/kernel/proc.c +++ b/arch/riscv64/kernel/proc.c @@ -62,20 +62,6 @@ vm_t get_stack(struct tcb *t) return r->sp; } -void save_regs(struct tcb *t, void *p) -{ - struct riscv_regs *r = (struct riscv_regs *)(t->regs) - 1; - struct riscv_regs *rp = (struct riscv_regs *)(p) - 1; - *rp = *r; -} - -void load_regs(void *p, struct tcb *t) -{ - struct riscv_regs *r = (struct riscv_regs *)(t->regs) - 1; - struct riscv_regs *rp = (struct riscv_regs *)(p) - 1; - *r = *rp; -} - void clone_regs(struct tcb *d, struct tcb *s) { struct riscv_regs *rd = (struct riscv_regs *)(d->regs) - 1; diff --git a/arch/riscv64/kernel/vmem.c b/arch/riscv64/kernel/vmem.c index b24be83..547cbfc 100644 --- a/arch/riscv64/kernel/vmem.c +++ b/arch/riscv64/kernel/vmem.c @@ -89,8 +89,48 @@ */ #define is_branch(pte) (is_active(pte) && !(pte_flags(pte) & ~VM_V)) -#define GRAVESTONE 2 +/** + * Gravestone marker. + * + * Riscv allows us to have arbitrary data in page entries, as long as they're + * not marked active (VM_V) the content is ignored. Here we use this to our + * advantage by differentiating between empty entries (NULL) and filler entries + * (GRAVESTONE). + * + * A gravestone tells us that somewhere above it (= higher index) there is an + * active entry. This is useful mainly in \ref clone_uvmem(), where we can stop + * copying data as soon as we hit an empty entry. I expect typical programs to + * generally have most active entries in relatively low addresses, and allowing + * us to skip copying 'obvious' entries is way quicker than copying the whole + * 2048 byte user virtual memory. + * + * Current optimisations also include setting the uvmem to stop on an 8-page + * boundary, allowing \ref clone_uvmem() to work in eight page increments for a + * bit of extra speed. Gravestones are only applied to userspace virtual memory, + * that is kernel and rpc memory regions are ignored. + * + * Example of how stuff should look like: + * + * Startin with page entries: + * 1 2 3 4 0 0 0 ... + * + * Mapping a page: + * 1 2 3 4 0 5 0 ... + * + * Adding gravestones: + * 1 2 3 4 G 5 0 ... + * + * More testing is probably necessary, as the init tests program doesn't really + * excercise the mapping utilities. + */ +#define GRAVESTONE VM_G +/** + * Check if pte is unused, i.e. either a gravestone or empty. + * + * @param b pte to check. + * @return \ref true if \p b is unused. + */ static bool __unused(pm_t b) { return b == GRAVESTONE || b == NULL; @@ -228,12 +268,21 @@ static void __destroy_branch(struct vmem *b) free_page(MM_KPAGE, (pm_t)__pa(b)); } -static void add_graves(struct vmem *branch, size_t idx) +/** + * Add graves if necessary. + * + * Checks that the index is within user virtual memory. If it is, change all + * NULL-entries to gravestones at lower addresses than \p idx. + * + * @param branch Top level branch to add graves to. + * @param idx Index of new entry just added. + */ +static void __add_graves(struct vmem *branch, size_t idx) { if (idx >= CSTACK_PAGE) return; - for (size_t i = idx; i < CSTACK_PAGE; ++i) { + for (ssize_t i = idx - 1; i >= 0; --i) { if (!__unused((pm_t)branch->leaf[i])) return; @@ -265,20 +314,30 @@ stat_t map_vpage(struct vmem *branch, pm_t paddr, vm_t vaddr, vmflags_t flags, branch->leaf[idx] = (struct vmem *)to_pte((pm_t)__pa(paddr), vp_flags(flags)); - add_graves(root, vm_to_index(vaddr, __mm_max_order)); + __add_graves(root, vm_to_index(vaddr, __mm_max_order)); return top == __mm_max_order ? INFO_SEFF : OK; } -static void remove_graves(struct vmem *branch, size_t idx) +/** + * Remove graves if possible. + * + * Checks if \p idx is in user virtual memory. If it is, check if the entry at + * \p idx was the top page and was turned into a gravestone. If it was, start + * removing gravestoned until we hit the next top. + * + * @param branch Top level branch to remove gravestones in. + * @param idx Index of just unmapped page at the top level. + */ +static void __remove_graves(struct vmem *branch, size_t idx) { - if (idx > CSTACK_PAGE) + if (idx >= CSTACK_PAGE) return; - if (__unused((pm_t)branch->leaf[idx + 1])) + if ((pm_t)branch->leaf[idx + 1] != NULL) return; - for (size_t i = idx; i < CSTACK_PAGE; ++i) { - if (!__unused((pm_t)branch->leaf[i])) + for (ssize_t i = idx; i >= 0; --i) { + if ((pm_t)branch->leaf[i] != GRAVESTONE) return; branch->leaf[i] = NULL; @@ -290,7 +349,7 @@ stat_t unmap_vpage(struct vmem *branch, vm_t vaddr) pm_t *pte = __find_vmem(branch, vaddr, 0); if (pte) { *pte = GRAVESTONE; - remove_graves(branch, vm_to_index(vaddr, __mm_max_order)); + __remove_graves(branch, vm_to_index(vaddr, __mm_max_order)); return OK; } @@ -385,12 +444,6 @@ vm_t setup_kernel_io(struct vmem *b, vm_t paddr) } #endif -/* something of an optimisation, letting the compiler know which parts to copy - * however it sees best */ -struct uvmem_sv39_map { - struct vmem *leaf[CSTACK_PAGE - 1]; -}; - void clone_uvmem(struct vmem *r, struct vmem *b) { size_t i = 0; diff --git a/common/tcb.c b/common/tcb.c index 201b15e..5121940 100644 --- a/common/tcb.c +++ b/common/tcb.c @@ -149,6 +149,9 @@ struct tcb *create_thread(struct tcb *p) if (likely(p)) { t->pid = p->pid; + /** @todo I'm assuming two threads can share the same vmem + * structure, this works on riscv but in the event that other + * systems don't we can easily turn this into a clone_uvmem. */ t->proc.vmem = p->proc.vmem; } else { init_uvmem(t, UVMEM_START, UVMEM_END); @@ -259,23 +262,24 @@ stat_t destroy_proc(struct tcb *p) * * @param name name of function to define. * @param type Field name of type \c tcb_ctx. + * @param root Root \ref tcb_ctx to attach to. */ -#define DEFINE_ATTACH(name, type) \ +#define DEFINE_ATTACH(name, type, root) \ stat_t name(struct tcb *r, struct tcb *t) \ { \ hard_assert(r != t, ERR_INVAL); \ - struct tcb *next = r->type.next; \ + struct tcb *next = r->root.next; \ t->type.next = next; \ \ if (next) { next->type.prev = t; } \ \ t->type.prev = r; \ - r->type.next = t; \ + r->root.next = t; \ return OK; \ } -DEFINE_ATTACH(attach_rpc, rpc); -DEFINE_ATTACH(attach_proc, proc); +DEFINE_ATTACH(attach_rpc, rpc, server); +DEFINE_ATTACH(attach_proc, proc, proc); /** * Convenience marco for defining function to detach a thread from either process @@ -283,8 +287,9 @@ DEFINE_ATTACH(attach_proc, proc); * * @param name name of function to define. * @param type Field name of type \c tcb_ctx. + * @param root Root \ref tcb_ctx to detach from. */ -#define DEFINE_DETACH(name, type) \ +#define DEFINE_DETACH(name, type, root) \ stat_t name(struct tcb *r, struct tcb *t) \ { \ MAYBE_UNUSED(r); \ @@ -298,8 +303,8 @@ DEFINE_ATTACH(attach_proc, proc); return OK; \ } -DEFINE_DETACH(detach_rpc, rpc); -DEFINE_DETACH(detach_proc, proc); +DEFINE_DETACH(detach_rpc, rpc, server); +DEFINE_DETACH(detach_proc, proc, proc); /* weak to allow optimisation on risc-v, but provide fallback for future */ __weak struct tcb *cur_tcb() @@ -402,18 +407,32 @@ static void mark_rpc_accessible(struct tcb *t, vm_t start, vm_t end) set_vpage_flags(t->rpc.vmem, start + pages * page_size, VM_U); } +/** Structure for maintaingin the required context data for an rpc call. */ struct call_ctx { + /** Execution continuation point. */ vm_t exec; + + /** Register save area. */ vm_t regs; + + /** Position in rpc stack. */ vm_t rpc_stack; - id_t eid, pid; + + /** Effective process ID. */ + id_t eid; + + /** Current process ID. */ + id_t pid; }; void save_context(struct tcb *t) { vm_t rpc_stack = t->rpc_stack; if (is_rpc(t)) - /** @todo what if user uses their own stack? */ + /** @todo what if user uses their own stack? Or is a dick and + * sets the stack pointer to RPC_STACK_TOP or something? It'll + * likely only cause a fuckup in the process who did the dumb + * thing, so maybe just consider it user error? */ rpc_stack = align_down(get_stack(t), BASE_PAGE_SIZE); diff --git a/common/uapi/ipc.c b/common/uapi/ipc.c index e9a6e6b..052e0ad 100644 --- a/common/uapi/ipc.c +++ b/common/uapi/ipc.c @@ -55,8 +55,7 @@ static struct sys_ret do_ipc(sys_arg_t pid, save_context(t); set_return(t, r->callback); - /** @todo associate thread with new proc, should be done in tcb.c I - * think */ + attach_rpc(r, t); if (!fwd) t->eid = t->pid; @@ -110,7 +109,9 @@ SYSCALL_DEFINE4(ipc_resp)(sys_arg_t d0, sys_arg_t d1, sys_arg_t d2, sys_arg_t d3) { struct tcb *t = cur_tcb(); + struct tcb *r = cur_proc(); load_context(t); + detach_rpc(r, t); if (is_rpc(t)) use_vmem(t->rpc.vmem); diff --git a/common/vmem.c b/common/vmem.c index 458624c..56bc01a 100644 --- a/common/vmem.c +++ b/common/vmem.c @@ -140,6 +140,8 @@ vm_t alloc_fixed_uvmem(struct tcb *t, vm_t start, size_t size, vmflags_t flags) const vm_t v = alloc_fixed_region(&t->sp_r, start, size, &size, flags); const vm_t w = map_allocd_region(t->proc.vmem, v, size, flags, &status); + /** @todo should probably update rpc maps even if the thread that does + * the allocation isn't in an ipc? */ if (is_rpc(t) && status == INFO_SEFF) clone_rpc_maps(t); @@ -273,6 +275,9 @@ stat_t free_uvmem_wrapper(struct vmem *b, pm_t *offset, vm_t vaddr, if (order != v_order) return INFO_TRGN; + /** @todo we might need to cause an ipi to flush the tlb for other + * cores */ + stat_t *status = (stat_t *)data, ret; ret = unmap_vpage(b, vaddr); if (status) diff --git a/docs/doxygen.conf b/docs/doxygen.conf index 7cdfa37..34b7b48 100644 --- a/docs/doxygen.conf +++ b/docs/doxygen.conf @@ -1000,7 +1000,7 @@ RECURSIVE = YES # Note that relative paths are relative to the directory from which doxygen is # run. -EXCLUDE = +EXCLUDE = arch/riscv64/conf/init.c # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded diff --git a/include/apos/tcb.h b/include/apos/tcb.h index 23e6451..6097153 100644 --- a/include/apos/tcb.h +++ b/include/apos/tcb.h @@ -78,6 +78,10 @@ struct tcb { /** Execution continuation point. Important that it is first. */ vm_t exec; + /** + * Address where to save registers. + * @note his address is the top of the register save structure. + */ vm_t regs; /** Arch-specific data. */ @@ -149,6 +153,16 @@ struct tcb { /** RPC context of thread. */ struct tcb_ctx rpc; + /** + * RPC server context of thread. When a thread attaches itself to this + * process, its \ref rpc member is added to the list maintained in this + * variable. This allows the original thread to do rpc calls without + * messing up other threads' rpc status. + * + * I think, more testing required. + */ + struct tcb_ctx server; + /** Notifcation state of thread. */ enum tcb_notify notify_state; diff --git a/include/arch/vmem.h b/include/arch/vmem.h index f6660ed..c829629 100644 --- a/include/arch/vmem.h +++ b/include/arch/vmem.h @@ -53,6 +53,8 @@ stat_t unmap_vpage(struct vmem *branch, vm_t vaddr); * @param branch Branch in which to work. * @param vaddr Virtual address of page. * @param flags Flags to set. + * @return \ref ERR_NF if no page could be found at \p vaddr, + * \ref INFO_SEFF if modification has side effects, otherwise \ref OK. */ stat_t set_vpage_flags(struct vmem *branch, vm_t vaddr, vmflags_t flags); @@ -62,6 +64,8 @@ stat_t set_vpage_flags(struct vmem *branch, vm_t vaddr, vmflags_t flags); * @param branch Branch in which to work. * @param vaddr Virtual address of page. * @param flags Flags to clear. + * @return \ref ERR_NF if no page could be found at \p vaddr, + * \ref INFO_SEFF if modification has side effects, otherwise \ref OK. */ stat_t clear_vpage_flags(struct vmem *branch, vm_t vaddr, vmflags_t flags); @@ -157,7 +161,6 @@ stat_t destroy_vmem(struct vmem *b); * * @param r Source virtual memory of clone. * @param b Destination virtual memory of clone. - * @return \ref OK. */ void clone_uvmem(struct vmem *r, struct vmem *b); |
