diff options
| author | Kimplul <kimi.h.kuparinen@gmail.com> | 2022-11-12 16:41:36 +0200 |
|---|---|---|
| committer | Kimplul <kimi.h.kuparinen@gmail.com> | 2022-11-12 16:41:36 +0200 |
| commit | 41ead8de77963bae907375c1bb115dad3315f46b (patch) | |
| tree | 4b364db137bd3bea69853f15329292af7662897d | |
| parent | 1d37792fb7a47135f0e7b7fc245e83bb1d8fc496 (diff) | |
| download | kmi-41ead8de77963bae907375c1bb115dad3315f46b.tar.gz kmi-41ead8de77963bae907375c1bb115dad3315f46b.zip | |
initial rpc implementations
| -rwxr-xr-x | arch/riscv64/conf/init | bin | 2664 -> 3448 bytes | |||
| -rw-r--r-- | arch/riscv64/conf/init.c | 94 | ||||
| -rw-r--r-- | arch/riscv64/conf/initrd | bin | 3072 -> 4096 bytes | |||
| -rw-r--r-- | arch/riscv64/config.h | 4 | ||||
| -rw-r--r-- | arch/riscv64/kernel/proc.c | 6 | ||||
| -rw-r--r-- | arch/riscv64/kernel/vmem.c | 2 | ||||
| -rw-r--r-- | common/proc.c | 4 | ||||
| -rw-r--r-- | common/tcb.c | 83 | ||||
| -rw-r--r-- | common/uapi/conf.c | 4 | ||||
| -rw-r--r-- | common/uapi/ipc.c | 16 | ||||
| -rw-r--r-- | common/uapi/proc.c | 10 | ||||
| -rw-r--r-- | include/apos/caps.h | 15 | ||||
| -rw-r--r-- | include/apos/sizes.h | 30 | ||||
| -rw-r--r-- | include/apos/tcb.h | 29 | ||||
| -rw-r--r-- | include/arch/proc.h | 8 |
15 files changed, 254 insertions, 51 deletions
diff --git a/arch/riscv64/conf/init b/arch/riscv64/conf/init Binary files differindex 11d838c..7ab8388 100755 --- a/arch/riscv64/conf/init +++ b/arch/riscv64/conf/init diff --git a/arch/riscv64/conf/init.c b/arch/riscv64/conf/init.c index 45274d9..01e219e 100644 --- a/arch/riscv64/conf/init.c +++ b/arch/riscv64/conf/init.c @@ -9,8 +9,13 @@ #include <stdint.h> #include "../../../include/apos/syscalls.h" -#define ecall() do { asm ("ecall" : : : "a0", "a1", "a2", "a3", "a4", "a5"); \ +#define ecall() do { asm volatile ("ecall" \ + : \ + : \ + : "a0", "a1", "a2", "a3", "a4", "a5", \ + "memory"); \ } while (0) + static void sys_noop() { long register a0 asm ("a0") = SYS_NOOP; @@ -108,6 +113,60 @@ static uint64_t sys_swap(long tid) return a0; } +static void sys_ipc_server(void *f) +{ + long register a0 asm ("a0") = SYS_IPC_SERVER; + long register a1 asm ("a1") = (long)f; + ecall(); + if (a0 != 0) + print_value("ipc_server() failed with error ", a0); +} + +static uint64_t sys_ipc_req(long tid, long *d0, long *d1, long *d2, long *d3) +{ + long register a0 asm ("a0") = SYS_IPC_REQ; + long register a1 asm ("a1") = tid; + long register a2 asm ("a2") = *d0; + long register a3 asm ("a3") = *d1; + long register a4 asm ("a4") = *d2; + long register a5 asm ("a5") = *d3; + ecall(); + if (a0) + print_value("ipc_req() failed with error ", a0); + + *d0 = a2; + *d1 = a3; + *d2 = a4; + *d3 = a5; + return a0; +} + +static void sys_ipc_resp(long d0, long d1, long d2, long d3) +{ + long register a0 asm ("a0") = SYS_IPC_RESP; + long register a1 asm ("a1") = d0; + long register a2 asm ("a2") = d1; + long register a3 asm ("a3") = d2; + long register a4 asm ("a4") = d3; + ecall(); +} + +static void sys_poweroff(long type) +{ + long register a0 asm ("a0") = SYS_POWEROFF; + long register a1 asm ("a1") = type; + ecall(); +} + +void callback(long status, long tid, long d0, long d1, long d2, long d3) +{ + sys_ipc_resp(d0, d1, d2, d3); +} + +#define CSR_TIME "0xc01" +#define csr_read(csr, \ + res) __asm__ volatile ("csrr %0, " csr : "=r" (res) :: "memory") + void _start() { sys_noop(); @@ -128,20 +187,41 @@ void _start() print_value("End ticks", i); print_value("Syscalls per second", n); + puts("Setting callback..."); + sys_ipc_server(callback); + puts("Starting fork():\n"); long pid = sys_fork(); if (pid != 0) { - print_value("Child pid: ", pid); + print_value("Child pid", pid); puts("Swapping to child...\n"); while(1) sys_swap(pid); } puts("Hello from child!\n"); - puts("Doing 1M swaps...\n"); - start = sys_ticks(); - for (long i = 0; i < 1000000; ++i) + puts("Doing swaps...\n"); + csr_read(CSR_TIME, i); + start = i; n = 0; + while (i < start + second) { sys_swap(1); - uint64_t ticks = sys_ticks() - start; - print_value("1M swaps took ", ticks / second); + csr_read(CSR_TIME, i); + n++; + } + + print_value("Swaps (both ways) per second", n); + + puts("Doing ipc requests...\n"); + long d0, d1, d2, d3; + csr_read(CSR_TIME, i); + start = i; n = 0; + while (i < start + second) { + sys_ipc_req(1, &d0, &d1, &d2, &d3); + csr_read(CSR_TIME, i); + n++; + } + + print_value("IPC requests per second", n); + + sys_poweroff(0); } diff --git a/arch/riscv64/conf/initrd b/arch/riscv64/conf/initrd Binary files differindex 5c0c9bb..492b80c 100644 --- a/arch/riscv64/conf/initrd +++ b/arch/riscv64/conf/initrd diff --git a/arch/riscv64/config.h b/arch/riscv64/config.h index 2c3dce6..857f885 100644 --- a/arch/riscv64/config.h +++ b/arch/riscv64/config.h @@ -77,10 +77,10 @@ #define UVMEM_END (SZ_256G - SZ_1G) /** RPC stack top. */ -#define RPC_STACK_TOP (UVMEM_END) +#define RPC_STACK_TOP (UVMEM_END + SZ_1G) /** RPC stack base. */ -#define RPC_STACK_BASE (RPC_STACK_TOP - SZ_1G) +#define RPC_STACK_BASE (UVMEM_END) /** * Size of the default top page. diff --git a/arch/riscv64/kernel/proc.c b/arch/riscv64/kernel/proc.c index 2ccc5bc..3c2ea59 100644 --- a/arch/riscv64/kernel/proc.c +++ b/arch/riscv64/kernel/proc.c @@ -56,6 +56,12 @@ void set_thread(struct tcb *t) r->tp = (long)t->thread_storage; } +vm_t get_stack(struct tcb *t) +{ + struct riscv_regs *r = (struct riscv_regs *)(t) - 1; + return r->sp; +} + void save_regs(struct tcb *t, void *p) { struct riscv_regs *r = (struct riscv_regs *)(t) - 1; diff --git a/arch/riscv64/kernel/vmem.c b/arch/riscv64/kernel/vmem.c index 8954ee3..d12015c 100644 --- a/arch/riscv64/kernel/vmem.c +++ b/arch/riscv64/kernel/vmem.c @@ -316,7 +316,7 @@ vm_t setup_kernel_io(struct vmem *b, vm_t paddr) stat_t clone_uvmem(struct vmem *r, struct vmem *b) { /** \todo error checking? */ - for (size_t i = 0; i <= CSTACK_PAGE; ++i) + for (size_t i = 0; i < CSTACK_PAGE; ++i) b->leaf[i] = r->leaf[i]; return OK; diff --git a/common/proc.c b/common/proc.c index 137a2fe..1acd5b8 100644 --- a/common/proc.c +++ b/common/proc.c @@ -21,7 +21,7 @@ stat_t prepare_proc(struct tcb *t, vm_t bin, vm_t interp) if (!entry) return ERR_INVAL; - alloc_stacks(t); + alloc_stack(t); set_thread(t); set_return(t, entry); return OK; @@ -40,7 +40,7 @@ stat_t init_proc(void *fdt) use_tcb(t); /* init process has all capabilities */ - set_caps(t->caps, 0, CAP_CAPS | CAP_PROC | CAP_CALL); + set_caps(t->caps, 0, CAP_CAPS | CAP_PROC | CAP_CALL | CAP_POWER); /* allocate stacks after ELF file to make sure nothing of importance * clashes */ diff --git a/common/tcb.c b/common/tcb.c index 0a676af..aa66d3f 100644 --- a/common/tcb.c +++ b/common/tcb.c @@ -96,11 +96,11 @@ static vm_t __setup_rpc_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); - map_vpage(t->proc.vmem, offset, + map_vpage(t->rpc.vmem, offset, RPC_STACK_TOP - BASE_PAGE_SIZE * i, flags, BASE_PAGE); } - + t->rpc_stack = RPC_STACK_TOP; return RPC_STACK_TOP - BASE_PAGE_SIZE * pages; } @@ -116,7 +116,7 @@ static vm_t __setup_thread_stack(struct tcb *t, size_t bytes) return alloc_uvmem(t, bytes, VM_V | VM_R | VM_W | VM_U); } -stat_t alloc_stacks(struct tcb *t) +stat_t alloc_stack(struct tcb *t) { /* get parent process */ struct tcb *p = get_tcb(t->eid); @@ -125,11 +125,6 @@ stat_t alloc_stacks(struct tcb *t) if (!t->thread_stack) return ERR_OOMEM; - /* rpc stack always starts at the same place in vmem. - * \todo: is this a security issue? */ - if (!__setup_rpc_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; @@ -166,6 +161,7 @@ struct tcb *create_thread(struct tcb *p) t->eid = t->pid; t->rid = p->rid; t->rpc.vmem = create_vmem(); + __setup_rpc_stack(t, __call_stack_size); set_canary(t); return t; @@ -186,6 +182,10 @@ static stat_t __copy_proc(struct tcb *p, struct tcb *n) * need to duplicate stack info, whatever we do. */ /** @todo should there be in-kernel child tracking? */ n->exec = p->exec; + n->callback = p->callback; + n->thread_stack = p->thread_stack; + n->thread_stack_top = p->thread_stack_top; + clone_regs(n, p); copy_caps(n->caps, p->caps); return clone_mem_regions(n, p); @@ -374,3 +374,70 @@ bool running(struct tcb *t) { return cpu_tcb(t->cpu_id) == t; } + +/** + * Mark rpc stack between \p start and \p end inaccessible. + * + * @param t Thread whose rpc stack to modify. + * @param start Start address of rpc stack to mark inaccessible. + * @param end End address of rpc stack to mark inaccessible. + */ +static void mark_rpc_inaccessible(struct tcb *t, vm_t start, vm_t end) +{ + size_t page_size = BASE_PAGE_SIZE; + size_t size = end - start; + size_t pages = size / page_size; + while (pages--) { + /** @todo something like mod_vpage_flags could be faster */ + vmflags_t flags; pm_t paddr; + stat_vpage(t->rpc.vmem, start, &paddr, NULL, &flags); + mod_vpage(t->rpc.vmem, start, paddr, clear_bits(flags, VM_U)); + } +} + +/** + * Mark rpc stack between \p start and \p end accessible. + * + * @param t Thread whose rpc stack to modify. + * @param start Start address of rpc stack to mark accessible. + * @param end End address of rpc stack to mark accessible. + */ +static void mark_rpc_accessible(struct tcb *t, vm_t start, vm_t end) +{ + size_t page_size = BASE_PAGE_SIZE; + size_t size = end - start; + size_t pages = size / page_size; + while (pages--) { + /** @todo something like mod_vpage_flags could be faster */ + vmflags_t flags; pm_t paddr; + stat_vpage(t->rpc.vmem, start, &paddr, NULL, &flags); + mod_vpage(t->rpc.vmem, start, paddr, set_bits(flags, VM_U)); + } +} + +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? */ + rpc_stack = align_down(get_stack(t), BASE_PAGE_SIZE); + + struct tcb *copy = (struct tcb *)(rpc_stack - sizeof(struct tcb)); + memcpy(copy, t, sizeof(struct tcb)); + clone_regs(copy, t); + + rpc_stack -= BASE_PAGE_SIZE; + mark_rpc_inaccessible(t, rpc_stack, t->rpc_stack); + t->rpc_stack = rpc_stack; +} + +void load_context(struct tcb *t) +{ + vm_t rpc_stack = t->rpc_stack; + struct tcb *copy = + (struct tcb *)(rpc_stack + BASE_PAGE_SIZE - sizeof(struct tcb)); + memcpy(t, copy, sizeof(struct tcb)); + clone_regs(t, copy); + + mark_rpc_accessible(t, rpc_stack, t->rpc_stack); +} diff --git a/common/uapi/conf.c b/common/uapi/conf.c index 7e88358..2094604 100644 --- a/common/uapi/conf.c +++ b/common/uapi/conf.c @@ -57,6 +57,10 @@ SYSCALL_DEFINE2(conf_set)(sys_arg_t param, sys_arg_t val) */ SYSCALL_DEFINE1(poweroff)(sys_arg_t type) { + struct tcb *t = cur_tcb(); + if (!(has_cap(t->caps, CAP_POWER))) + return SYS_RET1(ERR_PERM); + switch (type) { case SHUTDOWN: case COLD_REBOOT: diff --git a/common/uapi/ipc.c b/common/uapi/ipc.c index cb53ee3..e45c7dd 100644 --- a/common/uapi/ipc.c +++ b/common/uapi/ipc.c @@ -18,7 +18,7 @@ */ SYSCALL_DEFINE1(ipc_server)(sys_arg_t callback) { - cur_tcb()->callback = callback; + cur_proc()->callback = callback; return SYS_RET1(OK); } @@ -50,8 +50,12 @@ static struct sys_ret do_ipc(sys_arg_t pid, if (!r->callback) return SYS_RET1(ERR_NOINIT); - /** \todo place data on rpc stack and clone into virtual memory */ + clone_uvmem(r->proc.vmem, t->rpc.vmem); + use_vmem(t->rpc.vmem); + save_context(t); set_return(t, r->callback); + /** @todo associate thread with new proc, should be done in tcb.c I + * think */ if (!fwd) t->eid = t->pid; @@ -105,7 +109,13 @@ 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(); - /* something like return_from_callback(t, r) */ + load_context(t); + + if (is_rpc(t)) + use_vmem(t->rpc.vmem); + else + use_vmem(t->proc.vmem); + return SYS_RET6(OK, t->tid, d0, d1, d2, d3); } diff --git a/common/uapi/proc.c b/common/uapi/proc.c index 969dcd6..7a078da 100644 --- a/common/uapi/proc.c +++ b/common/uapi/proc.c @@ -32,6 +32,8 @@ SYSCALL_DEFINE5(create)(sys_arg_t func, if (!t) return SYS_RET1(ERR_OOMEM); + alloc_stack(t); + set_args(t, SYS_RET5(t->tid, d0, d1, d2, d3)); set_return(t, func); @@ -55,7 +57,7 @@ SYSCALL_DEFINE5(create)(sys_arg_t func, */ SYSCALL_DEFINE0(fork)(){ struct tcb *c = cur_proc(); - if (!(get_caps(c->caps, 0) & CAP_PROC)) + if (!(has_cap(c->caps, CAP_PROC))) return SYS_RET1(ERR_PERM); struct tcb *t = create_proc(eff_proc()); @@ -118,7 +120,7 @@ SYSCALL_DEFINE2(exec)(sys_arg_t bin, sys_arg_t interp){ SYSCALL_DEFINE2(spawn)(sys_arg_t bin, sys_arg_t interp) { struct tcb *c = cur_proc(); - if (!(get_caps(c->caps, 0) & CAP_PROC)) + if (!(has_cap(c->caps, CAP_PROC))) return SYS_RET1(ERR_PERM); struct tcb *t = create_proc(NULL); @@ -139,7 +141,7 @@ SYSCALL_DEFINE2(spawn)(sys_arg_t bin, sys_arg_t interp) SYSCALL_DEFINE1(kill)(sys_arg_t tid) { struct tcb *c = cur_proc(); - if (!(get_caps(c->caps, 0) & CAP_PROC)) + if (!(has_cap(c->caps, CAP_PROC))) return SYS_RET1(ERR_PERM); return SYS_RET1(OK); @@ -157,7 +159,7 @@ SYSCALL_DEFINE1(kill)(sys_arg_t tid) */ SYSCALL_DEFINE1(swap)(sys_arg_t tid){ struct tcb *c = cur_proc(); - if (!(get_caps(c->caps, 0) & CAP_PROC)) + if (!(has_cap(c->caps, CAP_PROC))) return SYS_RET1(ERR_PERM); struct tcb *t = get_tcb(tid); diff --git a/include/apos/caps.h b/include/apos/caps.h index e8ef76b..be64663 100644 --- a/include/apos/caps.h +++ b/include/apos/caps.h @@ -6,6 +6,9 @@ * Capabilities of threads. * \todo The list of capabilities should maybe be placed in some other file so * as to easier extract it into userspace programs. + * + * @todo is it realistic to assume we will never need more than 32 capabilities? + * If so, we can remove the offset nonsense. */ #include <apos/bits.h> @@ -22,6 +25,9 @@ enum { /** Thread is allowed to force interrupt to callback in other thread. */ CAP_CALL = (1 << 2), + + /** Thread is allowed to shut down system. */ + CAP_POWER = (1 << 3), }; /** @@ -67,4 +73,13 @@ enum { */ #define get_caps(x, o) (x) +/** + * Check if something has capability. + * + * @param x Capabilities to check in. + * @param c Capability to check for. + * @return \ref true if \p x has \p c, \ref false otherwise. + */ +#define has_cap(x, c) (x & c) + #endif /* APOS_CAPS_H */ diff --git a/include/apos/sizes.h b/include/apos/sizes.h index 2eafa2e..40c3590 100644 --- a/include/apos/sizes.h +++ b/include/apos/sizes.h @@ -122,46 +122,40 @@ #define SZ_32G 0x000800000000UL /** 64GiB. */ -#define SZ_64G 0x000400000000UL +#define SZ_64G 0x001000000000UL /** 128GiB. */ -#define SZ_128G 0x000800000000UL +#define SZ_128G 0x002000000000UL /** 256GiB. */ -#define SZ_256G 0x001000000000UL +#define SZ_256G 0x004000000000UL /** 512GiB. */ -#define SZ_512G 0x002000000000UL +#define SZ_512G 0x008000000000UL /** 1TiB. */ -#define SZ_1T 0x004000000000UL +#define SZ_1T 0x010000000000UL /** 2TiB. */ -#define SZ_2T 0x008000000000UL +#define SZ_2T 0x020000000000UL /** 4TiB. */ -#define SZ_4T 0x010000000000UL +#define SZ_4T 0x040000000000UL /** 8TiB. */ -#define SZ_8T 0x020000000000UL +#define SZ_8T 0x080000000000UL /** 16TiB. */ -#define SZ_16T 0x040000000000UL +#define SZ_16T 0x100000000000UL /** 32TiB. */ -#define SZ_32T 0x080000000000UL +#define SZ_32T 0x200000000000UL /** 64TiB. */ -#define SZ_64T 0x100000000000UL +#define SZ_64T 0x400000000000UL /** 128TiB. */ -#define SZ_128T 0x200000000000UL - -/** 256TiB. */ -#define SZ_256T 0x400000000000UL - -/** 512TiB. */ -#define SZ_512T 0x800000000000UL +#define SZ_128T 0x8000000000000UL #else diff --git a/include/apos/tcb.h b/include/apos/tcb.h index 0368957..9ed916f 100644 --- a/include/apos/tcb.h +++ b/include/apos/tcb.h @@ -28,7 +28,7 @@ * @param t Thread to check. * @return \c true if thread is in RPC, \c false otherwise. */ -#define is_rpc(t) (t->rid == t->pid) +#define is_rpc(t) (t->rid != t->pid) /** * Get the process thread of current thread. @@ -63,14 +63,14 @@ struct tcb_ctx { /** Enum for notification states. */ enum tcb_notify { + /** Thread is free to be notified. */ + NOTIFY_WAITING = 0, + /** Thread has notifcations queued. */ NOTIFY_QUEUED, /** Thread is running notification handler. */ NOTIFY_RUNNING, - - /** Thread is free to be notified. */ - NOTIFY_WAITING }; /** Thread control block. Main way to handle threads. */ @@ -133,6 +133,9 @@ struct tcb { /** Address of this thread's stack top. */ vm_t thread_stack_top; + /** Current address of usable rpc stack. */ + vm_t rpc_stack; + /** \todo Check if each thread should be allowed more than just one * region of thread local storage. */ /** Possible thread local storage. */ @@ -171,7 +174,7 @@ void destroy_tcbs(); * and the \ref tcb structure itself with a unique thread ID. If in a new * process context, a new process address space is created as well. * - * Userspace stack is allocated with \ref alloc_stacks(). + * Userspace stack is allocated with \ref alloc_stack(). * * \todo Thread local storage? * @@ -338,7 +341,7 @@ stat_t clone_rpc_maps(struct tcb *r); * @param t Thread whose stacks to allocate. * @return \ref OK on success, \ref ERR_OOMEM if out of memory. */ -stat_t alloc_stacks(struct tcb *t); +stat_t alloc_stack(struct tcb *t); /** * Set address to jump to when returning to userspace. @@ -356,4 +359,18 @@ void set_return(struct tcb *t, vm_t r); */ bool running(struct tcb *t); +/** + * Save thread context for rpc call. + * + * @param t Thread whose context to save. + */ +void save_context(struct tcb *t); + +/** + * Load thread context from rpc call. + * + * @param t Thread whose context to restore. + */ +void load_context(struct tcb *t); + #endif /* APOS_TCB_H */ diff --git a/include/arch/proc.h b/include/arch/proc.h index e2b99ea..6a2c036 100644 --- a/include/arch/proc.h +++ b/include/arch/proc.h @@ -49,6 +49,14 @@ struct sys_ret get_args(struct tcb *t); void set_thread(struct tcb *t); /** + * Get current userspace stack. + * + * @param t Thread whose stack to query. + * @return Current stack address. + */ +vm_t get_stack(struct tcb *t); + +/** * Copy registers from tcb save area to address \p p. * Intended to be used for copying thread state to rpc stack. * |
