diff options
32 files changed, 1201 insertions, 574 deletions
@@ -7,6 +7,9 @@ docs/output deps.mk *.img +initrd +init +build # keep gitkeep files wherever they are !.gitkeep @@ -2,6 +2,10 @@ all: setup $(MAKE) -f scripts/makefile +.PHONY: check +check: all + $(MAKE) -C tests check + # this kicks all unrecognised targets to the client script. # note that trying to compile individual files, e.g. # diff --git a/arch/riscv64/conf/init b/arch/riscv64/conf/init Binary files differdeleted file mode 100755 index b378582..0000000 --- a/arch/riscv64/conf/init +++ /dev/null diff --git a/arch/riscv64/conf/init.c b/arch/riscv64/conf/init.c deleted file mode 100644 index c4e8064..0000000 --- a/arch/riscv64/conf/init.c +++ /dev/null @@ -1,397 +0,0 @@ -/* SPDX-License-Identifier: copyleft-next-0.3.1 */ -/* Copyright 2023 Kim Kuparinen < kimi.h.kuparinen@gmail.com > */ - -/** - * 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 -Driscv64 -ffreestanding -nostdlib - * - * Create initrd with - * echo init | cpio -H newc -o > initrd - */ - -#include <stdint.h> -#include <stddef.h> -#include "../../../include/kmi/syscalls.h" - -char *strcpy(char * restrict dst, const char * restrict src) -{ - const char *s1 = src; - char *s2 = dst; - - while (*s1) - *(s2++) = *(s1++); - - return dst; -} - -static inline struct sys_ret ecall(size_t n, - sys_arg_t arg0, sys_arg_t arg1, - sys_arg_t arg2, sys_arg_t arg3, - sys_arg_t arg4, sys_arg_t arg5) -{ - /* here a static assert of n <= 6 && n >= 1 would be ideal */ - - register long a0 asm ("a0") = arg0; - register long a1 asm ("a1") = arg1; - register long a2 asm ("a2") = arg2; - register long a3 asm ("a3") = arg3; - register long a4 asm ("a4") = arg4; - register long a5 asm ("a5") = arg5; - -#define OUTPUTS "+r" (a0), "=r" (a1), "=r" (a2), "=r" (a3), "=r" (a4), "=r" (a5) - - if (n == 1) - asm volatile ("ecall" : OUTPUTS : "r" (a0)); - - else if (n == 2) - asm volatile ("ecall" : OUTPUTS : "r" (a0), "r" (a1)); - - else if (n == 3) - asm volatile ("ecall" : OUTPUTS : "r" (a0), "r" (a1), "r" (a2)); - - else if (n == 4) - asm volatile ("ecall" : OUTPUTS - : "r" (a0), "r" (a1), "r" (a2), "r" (a3)); - - else if (n == 5) - asm volatile ("ecall" : OUTPUTS - : "r" (a0), "r" (a1), "r" (a2), "r" (a3), - "r" (a4)); - - else if (n == 6) - asm volatile ("ecall" - : OUTPUTS - : "r" (a0), "r" (a1), "r" (a2), "r" (a3), - "r" (a4), "r" (a5)); - -#undef OUTPUTS - return (struct sys_ret){a0, a1, a2, a3, a4, a5}; -} - -#define ecall1(a) ecall(1, a, 0, 0, 0, 0, 0) -#define ecall2(a, b) ecall(2, a, b, 0, 0, 0, 0) -#define ecall3(a, b, c) ecall(3, a, b, c, 0, 0, 0) -#define ecall4(a, b, c, d) ecall(4, a, b, c, d, 0, 0) -#define ecall5(a, b, c, d, e) ecall(5, a, b, c, d, e, 0) -#define ecall6(a, b, c, d, e, f) ecall(6, a, b, c, d, e, f) - -static void sys_noop() -{ - ecall1(SYS_NOOP); -} - -static void sys_putch(char c) -{ - ecall2(SYS_PUTCH, c); -} - -static uint64_t sys_timebase() -{ - struct sys_ret r = ecall1(SYS_TIMEBASE); -#if defined(_LP64) - return r.ar0; -#else - uint64_t t = r.ar0; - t <<= 32; - return t + r.ar1; -#endif -} - -static uint64_t sys_ticks() -{ - struct sys_ret r = ecall1(SYS_TICKS); -#if defined(_LP64) - return r.ar0; -#else - uint64_t t = r.ar0; - t <<= 32; - return t + r.ar1; -#endif -} - -static void puts(const char *s) -{ - while (*s) - sys_putch(*s++); -} - -static void _print_number(uint64_t v) -{ - if (v == 0) - return; - - _print_number(v / 10); - sys_putch((v % 10) + '0'); -} - -static void print_number(uint64_t v) -{ - if (v == 0) { - sys_putch('0'); - return; - } - - _print_number(v); -} - -static void print_value(const char *s, uint64_t v) -{ - puts(s); - puts(": "); - - print_number(v); - sys_putch('\n'); -} - -static uint64_t sys_fork() -{ - struct sys_ret r = ecall1(SYS_FORK); - - if (r.s != 0) { - print_value("fork() failed with error ", r.s); - return r.s; - } - - return r.ar0; -} - -static uint64_t sys_swap(long tid) -{ - struct sys_ret r = ecall2(SYS_SWAP, tid); - - if (r.s != 0) { - print_value("swap() failed with error ", r.s); - return r.s; - } - - return 0; -} - -static inline struct sys_ret sys_ipc_req(sys_arg_t pid, - sys_arg_t d0, sys_arg_t d1, - sys_arg_t d2, - sys_arg_t d3) -{ - struct sys_ret r = ecall6(SYS_IPC_REQ, pid, d0, d1, d2, d3); - - if (r.s) - print_value("ipc_req() failed with error ", r.s); - - return r; -} - -static void sys_ipc_resp(sys_arg_t d0, sys_arg_t d1, sys_arg_t d2, sys_arg_t d3) -{ - ecall5(SYS_IPC_RESP, d0, d1, d2, d3); -} - -static void sys_poweroff(long type) -{ - ecall2(SYS_POWEROFF, type); -} - -static void *sys_req_mem(size_t count) -{ - struct sys_ret r = ecall3(SYS_REQ_MEM, count, VM_R | VM_W); - if (r.s) { - print_value("sys_req_mem() failed with error ", r.s); - return NULL; - } - - return (void *)r.ar0; -} - -static size_t get_ram_usage() -{ - struct sys_ret r = ecall2(SYS_CONF_GET, CONF_RAM_USAGE); - if (r.s) { - print_value("getting RAM usage failed with error ", r.s); - return 0; - } - - return r.ar0; -} - -static size_t get_ram_size() -{ - struct sys_ret r = ecall2(SYS_CONF_GET, CONF_RAM_SIZE); - if (r.s) { - print_value("getting RAM size failed with error ", r.s); - return 0; - } - - return r.ar0; -} - -static void sys_free_mem(void *p) -{ - struct sys_ret r = ecall2(SYS_FREE_MEM, (long)p); - - if (r.s) - print_value("sys_free_mem() failed with error ", r.s); -} - -static void *sys_req_sharedmem(long tid, unsigned long size, void **cbuf) -{ - struct sys_ret r = ecall3(SYS_REQ_SHAREDMEM, size, VM_R | VM_W); - if (r.s) { - print_value("sys_req_sharedmem() failed with error ", r.s); - return NULL; - } - - void *rw_buf = (void *)r.ar0; - - r = ecall4(SYS_REF_SHAREDMEM, tid, (sys_arg_t)rw_buf, VM_R | VM_W); - if (r.s) { - print_value("sys_ref_sharedmem() failed with error ", r.s); - return NULL; - } - - *cbuf = (void *)r.ar0; - return rw_buf; -} - -/* I'm guessing my elf parser doesn't handle data pages correctly yet... */ -static char *rw_buf = 0; -static size_t rw_buf_size = 4096; - -static void sys_sleep() -{ - ecall1(SYS_SLEEP); -} - -#define CSR_TIME "0xc01" -#define csr_read(csr, \ - res) __asm__ volatile ("csrr %0, " csr : "=r" (res) :: "memory") - -#define CSR_CYCLE "0xc00" - -static void handle_kernel(long pid, long tid, long d0, long d1, long d2, - long d3) -{ - if (d0 != SYS_USER_BOOTED) - return; - - if (tid != 1) { - puts("Woo, more cores!\n"); - while (1) - sys_sleep(); - } - - /* otherwise */ - sys_noop(); - puts("Hello, world!\n"); - - uint64_t second = sys_timebase(); - print_value("Timebase", second); - - uint64_t n = 0, i, start, cn, cstart, cend; - start = i = sys_ticks(); - csr_read(CSR_CYCLE, cstart); - while (i < start + second) { - i = sys_ticks(); - n++; - } - csr_read(CSR_CYCLE, cend); - - print_value("Start ticks", start); - print_value("End ticks", i); - print_value("Syscalls per second", n); - print_value("Executed cycles per second", cend - cstart); - - puts("Starting fork():\n"); - long cid = sys_fork(); - if (cid != 0) { - print_value("Child pid", cid); - puts("Swapping to child...\n"); - while(1) sys_swap(cid); - } - - puts("Hello from child!\n"); - - puts("Doing swaps...\n"); - csr_read(CSR_TIME, i); - start = i; n = 0; - while (i < start + second) { - sys_swap(1); - csr_read(CSR_TIME, i); - n++; - } - - print_value("Swaps (both ways) per second", n); - - puts("Doing ipc requests...\n"); - csr_read(CSR_TIME, i); - start = i; n = 0; - while (i < start + second) { - sys_ipc_req(1, 0, 0, 0, 0); - csr_read(CSR_TIME, i); - n++; - } - - print_value("IPC requests per second", n); - - puts("Doing memory allocations...\n"); - - size_t ram = get_ram_usage(); - size_t size = get_ram_size(); - print_value("Memory usage before allocations: ", ram); - print_value("Memory size in total: ", size); - for (i = 0; i < 1000000; ++i) { - char *p = sys_req_mem(10); - *p = 'c'; - sys_free_mem(p); - } - - ram = get_ram_usage(); - print_value("Memory usage after allocations: ", ram); - - puts("Checking shared memory\n"); - struct sys_ret r = sys_ipc_req(1, 1, 0, 0, 0); - rw_buf = (char *)r.ar1; - rw_buf_size = (size_t)r.ar2; - print_value("Response from", r.ar0); - print_value("Shared memory ptr", (uintptr_t)rw_buf); - print_value("Shared memory size", rw_buf_size); - - rw_buf[0] = 0; - puts("Sending string...\n"); - strcpy(rw_buf, "Hello from the other side!\n"); - sys_ipc_req(1, 2, 0, 0, 0); - - sys_poweroff(0); -} - -void _start(long pid, long tid, long d0, long d1, long d2, long d3) -{ - if (pid == 0) - handle_kernel(pid, tid, d0, d1, d2, d3); - - /* otherwise, implement test functionality */ - if (d0 == 1) { - void *cbuf = 0; - rw_buf = sys_req_sharedmem(pid, rw_buf_size, &cbuf); - sys_ipc_resp((long)cbuf, rw_buf_size, 0, 0); - __builtin_unreachable(); - - } else if (d0 == 2) { - puts("Received string: "); - puts(rw_buf); - sys_ipc_resp(0, 0, 0, 0); - __builtin_unreachable(); - } - - sys_ipc_resp(0, 0, 0, 0); - __builtin_unreachable(); -} diff --git a/arch/riscv64/conf/initrd b/arch/riscv64/conf/initrd Binary files differdeleted file mode 100644 index 4647812..0000000 --- a/arch/riscv64/conf/initrd +++ /dev/null diff --git a/arch/riscv64/kernel/main.c b/arch/riscv64/kernel/main.c index 01b8957..c6b5e4a 100644 --- a/arch/riscv64/kernel/main.c +++ b/arch/riscv64/kernel/main.c @@ -6,6 +6,7 @@ * riscv64 main */ +#include <kmi/syscalls.h> #include <kmi/utils.h> #include <arch/arch.h> #include "csr.h" diff --git a/arch/riscv64/kernel/pmem.c b/arch/riscv64/kernel/pmem.c index 56769c6..36f0284 100644 --- a/arch/riscv64/kernel/pmem.c +++ b/arch/riscv64/kernel/pmem.c @@ -6,6 +6,7 @@ * riscv64 implementation of arch-specific physical memory handling */ +#include <kmi/syscalls.h> #include <arch/pmem.h> stat_t stat_pmem_conf(void *fdt, size_t *max_order, size_t *base_bits, diff --git a/arch/riscv64/kernel/power.c b/arch/riscv64/kernel/power.c index 21e4957..2238eb1 100644 --- a/arch/riscv64/kernel/power.c +++ b/arch/riscv64/kernel/power.c @@ -34,15 +34,15 @@ stat_t poweroff(enum poweroff_type type) /** \todo this only shuts down the cpu itself, but may leave the SOC * active. Should read from fdt poweroff and syscon-poweroff etc */ switch (type) { - case SHUTDOWN: + case SYS_SHUTDOWN: sbi_system_reset(SBI_SHUTDOWN, SBI_NO_REASON); return ERR_MISC; - case COLD_REBOOT: + case SYS_COLD_REBOOT: sbi_system_reset(SBI_COLD_REBOOT, SBI_NO_REASON); return ERR_MISC; - case WARM_REBOOT: + case SYS_WARM_REBOOT: sbi_system_reset(SBI_WARM_REBOOT, SBI_NO_REASON); return ERR_MISC; }; diff --git a/arch/riscv64/kernel/proc.c b/arch/riscv64/kernel/proc.c index 19bd52f..aa33ec7 100644 --- a/arch/riscv64/kernel/proc.c +++ b/arch/riscv64/kernel/proc.c @@ -62,11 +62,11 @@ void set_args(struct tcb *t, size_t n, struct sys_ret a) { struct riscv_regs *r = (struct riscv_regs *)(t->regs) - 1; if (n >= 1) r->a0 = a.s; - if (n >= 2) r->a1 = a.ar0; - if (n >= 3) r->a2 = a.ar1; - if (n >= 4) r->a3 = a.ar2; - if (n >= 5) r->a4 = a.ar3; - if (n >= 6) r->a5 = a.ar4; + if (n >= 2) r->a1 = a.a0; + if (n >= 3) r->a2 = a.a1; + if (n >= 4) r->a3 = a.a2; + if (n >= 5) r->a4 = a.a3; + if (n >= 6) r->a5 = a.a4; } struct sys_ret get_args(struct tcb *t) diff --git a/include/kmi/caps.h b/include/kmi/caps.h index 02f6b3e..cc54dbe 100644 --- a/include/kmi/caps.h +++ b/include/kmi/caps.h @@ -16,51 +16,13 @@ #include <kmi/bits.h> -/** Helper typedef for capabilities. */ -typedef unsigned char capflags_t; - -enum { - /** Thread is allowed to set capabilities of other threads. */ - CAP_CAPS = (1 << 0), - - /** Thread is allowed to modify process statuses, create/exec/fork/etc. */ - CAP_PROC = (1 << 1), - - /** Thread is allowed to force notification in other thread. */ - CAP_NOTIFY = (1 << 2), - - /** Thread is allowed to shut down system. */ - CAP_POWER = (1 << 3), - - /** Thread is allowed to access configuration parameters. */ - CAP_CONF = (1 << 4), - - /** Thread is allowed to request to handle IRQs. */ - CAP_IRQ = (1 << 5), - - /** Thread is allowed to request notification handler. */ - CAP_SIGNAL = (1 << 6), - - /** Thread is allowed to request shared memory */ - CAP_SHARED = (1 << 7) -}; - -/** - * Check that offset is OK. - * - * @param o Offset to check. - * @return \ref true is OK, \ref false otherwise. - */ -#define cap_off_ok(o) (o == 0) - /** * Set capabilities. * * @param x Capabilities to modify. - * @param o Offset. * @param c Capabilities to set. */ -#define set_caps(x, o, c) set_bits(x, c) +#define set_caps(x, c) set_bits(x, c) /** * Clear capabilities. @@ -69,7 +31,7 @@ enum { * @param o Offset. * @param c Capabilities to set. */ -#define clear_caps(x, o, c) clear_bits(x, c) +#define clear_caps(x, c) clear_bits(x, c) /** * Copy capabilities. @@ -80,15 +42,6 @@ enum { #define copy_caps(x, y) (x = y) /** - * Get capabilities at offset \p o. - * - * @param x Capabilities to get from. - * @param o Offset to get capabilities from. - * @return Capabilities at offset \p o. - */ -#define get_caps(x, o) (x) - -/** * Check if something has capability. * * @param x Capabilities to check in. diff --git a/include/kmi/power.h b/include/kmi/power.h index db00999..d0ebeca 100644 --- a/include/kmi/power.h +++ b/include/kmi/power.h @@ -10,22 +10,10 @@ * host machines. */ +#include <kmi/syscalls.h> #include <kmi/types.h> #include <kmi/attrs.h> -/** Types of powering off. Still unclear what difference there is between warm - * and cold reboot. */ -enum poweroff_type { - /** Shut down. */ - SHUTDOWN, - - /** Cold or complete reboot. */ - COLD_REBOOT, - - /** Warm or partial reboot. */ - WARM_REBOOT -}; - /** * Power off the system. * diff --git a/include/kmi/syscalls.h b/include/kmi/syscalls.h index 1efd0d4..1ca2113 100644 --- a/include/kmi/syscalls.h +++ b/include/kmi/syscalls.h @@ -130,10 +130,10 @@ enum sys_code { /** @name Kernel management. */ /** @{ */ /** Configure system parameters (stack size etc.). */ - SYS_CONF_SET, + SYS_SET_CONF, /** Get system parameters. */ - SYS_CONF_GET, + SYS_GET_CONF, /** Set capability of thread. */ SYS_SET_CAP, @@ -153,8 +153,11 @@ enum sys_code { /** Request to handle IRQ. */ SYS_IRQ_REQ, + /** Retract IRQ. */ + SYS_FREE_IRQ, + /** Request a notification handler. */ - SYS_REQ_NOTIFICATION, + SYS_SET_HANDLER, /** Request a thread exits. */ SYS_EXIT, @@ -197,6 +200,19 @@ enum notify_flag { NOTIFY_ORPHANED = (1 << 3), }; +/** Types of powering off. Still unclear what difference there is between warm + * and cold reboot. */ +enum poweroff_type { + /** Shut down. */ + SYS_SHUTDOWN, + + /** Cold or complete reboot. */ + SYS_COLD_REBOOT, + + /** Warm or partial reboot. */ + SYS_WARM_REBOOT +}; + /* function declarations should be somewhere else, this file could be used in * userspace applications as well */ @@ -244,6 +260,63 @@ enum conf_param { CONF_PAGE_SIZE, }; +enum sys_cap { + /** Thread is allowed to set capabilities of other threads. */ + CAP_CAPS = (1 << 0), + + /** Thread is allowed to modify process statuses, create/exec/fork/etc. */ + CAP_PROC = (1 << 1), + + /** Thread is allowed to force notification in other thread. */ + CAP_NOTIFY = (1 << 2), + + /** Thread is allowed to shut down system. */ + CAP_POWER = (1 << 3), + + /** Thread is allowed to access configuration parameters. */ + CAP_CONF = (1 << 4), + + /** Thread is allowed to request to handle IRQs. */ + CAP_IRQ = (1 << 5), + + /** Thread is allowed to request notification handler. */ + CAP_SIGNAL = (1 << 6), + + /** Thread is allowed to request shared memory */ + CAP_SHARED = (1 << 7) +}; + +/** + * Status codes. + * Negative error codes are reserved for general usage, positive error codes are + * allowed to be function-specific, although that's sort of difficult to keep + * track of. + */ +enum sys_status { + /** Permission error. */ + ERR_PERM = -10, + /** Internal error, should probably halt */ + ERR_INT = -9, + /** Something went wrong :/ */ + ERR_MISC = -8, + /** Not initialized. */ + ERR_NOINIT = -7, + /** Invalid value. */ + ERR_INVAL = -6, + /** Already exists. */ + ERR_EXT = -5, + /** Out of memory. */ + ERR_OOMEM = -4, + /** Illegal address. */ + ERR_ADDR = -3, + /** Wrong alignment. */ + ERR_ALIGN = -2, + /** Not found. */ + ERR_NF = -1, + /** OK. */ + OK = 0, +}; + /** * Return structure of syscall. * \note Field names are generic, and can be used for whatever, @@ -255,19 +328,19 @@ struct sys_ret { sys_arg_t s; /** First argument. */ - sys_arg_t ar0; + sys_arg_t a0; /** Second argument. */ - sys_arg_t ar1; + sys_arg_t a1; /** Third argument. */ - sys_arg_t ar2; + sys_arg_t a2; /** Fourth argument. */ - sys_arg_t ar3; + sys_arg_t a3; /** Fifth argument. */ - sys_arg_t ar4; + sys_arg_t a4; }; #endif /* KMI_SYSCALLS_H */ diff --git a/include/kmi/tcb.h b/include/kmi/tcb.h index a06d801..a6522f8 100644 --- a/include/kmi/tcb.h +++ b/include/kmi/tcb.h @@ -184,7 +184,7 @@ struct tcb { enum notify_flag notify_flags; /** Capabilities of thread. */ - capflags_t caps; + enum sys_cap caps; /** Queue that connects together threads waiting for an ipi */ struct queue_head ipi_queue; diff --git a/include/kmi/types.h b/include/kmi/types.h index 1da5248..040d3c6 100644 --- a/include/kmi/types.h +++ b/include/kmi/types.h @@ -459,38 +459,6 @@ typedef int_fast32_t id_t; /** Memory region flags. */ typedef uint_fast16_t vmflags_t; -/** - * Status codes. - * Negative error codes are reserved for general usage, positive error codes are - * allowed to be function-specific, although that's sort of difficult to keep - * track of. - */ -/* should this enum be somewhere else? */ -enum status_codes { - /** Permission error. */ - ERR_PERM = -10, - /** Internal error, should probably halt */ - ERR_INT = -9, - /** Something went wrong :/ */ - ERR_MISC = -8, - /** Not initialized. */ - ERR_NOINIT = -7, - /** Invalid value. */ - ERR_INVAL = -6, - /** Already exists. */ - ERR_EXT = -5, - /** Out of memory. */ - ERR_OOMEM = -4, - /** Illegal address. */ - ERR_ADDR = -3, - /** Wrong alignment. */ - ERR_ALIGN = -2, - /** Not found. */ - ERR_NF = -1, - /** OK. */ - OK = 0, -}; - #include <arch/types.h> /* arch-specific type definitions (pm_t/vm_t etc) */ #endif /* KMI_TYPES_H */ diff --git a/include/kmi/uapi.h b/include/kmi/uapi.h index 350285f..3f03879 100644 --- a/include/kmi/uapi.h +++ b/include/kmi/uapi.h @@ -697,7 +697,7 @@ SYSCALL_DECLARE1(swap, tid); * * Returns \ref OK and 0. */ -SYSCALL_DECLARE2(conf_set, param, val); +SYSCALL_DECLARE2(set_conf, param, val); /** * Get configuration syscall. @@ -713,49 +713,49 @@ SYSCALL_DECLARE2(conf_set, param, val); * * Returns \ref OK. */ -SYSCALL_DECLARE2(conf_get, param, d0); +SYSCALL_DECLARE2(get_conf, param, d0); /** * Set capabilities. * * @param t Current tcb. * @param tid Thread ID whose capabilities to set. - * @param off Offset of capability, multiple of \c bits(cap). * @param caps Mask of capabilities to set. + * @param c Unused. * @param d Unused. * @param e Unused. * * Returns \ref OK on success, \ref ERR_INVAL on invalid input. */ -SYSCALL_DECLARE3(set_cap, tid, off, caps); +SYSCALL_DECLARE2(set_cap, tid, caps); /** * Get capabilities. * * @param t Current tcb. * @param tid Thread ID whose capabilities to get. - * @param off Offset of capability, multiple of \c bits(cap). + * @param b Unused. * @param c Unused. * @param d Unused. * @param e Unused. * * Returns \ref OK, capabilities. */ -SYSCALL_DECLARE2(get_cap, tid, off); +SYSCALL_DECLARE1(cap_get, tid); /** * Clear capabilities. * * @param t Current tcb. * @param tid Thread ID whose capabilities to clear. - * @param off Offset of capability, multiple of \c bits(cap). * @param cap Mask of capabilities to clear. + * @param c Unused. * @param d Unused. * @param e Unused. * * Returns \ref OK. */ -SYSCALL_DECLARE3(clear_cap, tid, off, cap); +SYSCALL_DECLARE2(clear_cap, tid, cap); /** * Set which process to move notifications to for thread. @@ -771,7 +771,7 @@ SYSCALL_DECLARE3(clear_cap, tid, off, cap); * * Returns \ref OK. */ -SYSCALL_DECLARE2(req_notification, tid, pid); +SYSCALL_DECLARE2(set_handler, tid, pid); /** * Power off syscall. diff --git a/src/panic.c b/src/panic.c index 03c0de8..5495ebe 100644 --- a/src/panic.c +++ b/src/panic.c @@ -6,6 +6,7 @@ * Kernel panic handler implementation. */ +#include <kmi/syscalls.h> #include <kmi/power.h> #include <kmi/debug.h> @@ -17,7 +18,7 @@ void kernel_panic(void *pc, void *addr, long cause) info("attempting to reboot\n"); - poweroff(COLD_REBOOT); + poweroff(SYS_COLD_REBOOT); /* spin if poweroff failed for some reason */ error("reboot failed, spinning in place\n"); @@ -49,7 +49,7 @@ stat_t init_proc(void *fdt, vm_t *proc_fdt, vm_t *proc_initrd) t->notify_id = t->tid; /* init process has all capabilities */ - set_caps(t->caps, 0, + set_caps(t->caps, CAP_CAPS | CAP_PROC | CAP_SIGNAL | CAP_POWER | CAP_NOTIFY | CAP_SHARED); diff --git a/src/regions.c b/src/regions.c index 1aa318d..4d82420 100644 --- a/src/regions.c +++ b/src/regions.c @@ -7,6 +7,7 @@ * subsystems. */ +#include <kmi/syscalls.h> #include <kmi/regions.h> #include <kmi/assert.h> #include <kmi/pmem.h> diff --git a/src/uapi/cap.c b/src/uapi/cap.c index 157ef47..050d181 100644 --- a/src/uapi/cap.c +++ b/src/uapi/cap.c @@ -11,25 +11,6 @@ */ /** - * Check that values are legal for thread ID and offset. - * - * @param tid Thread ID of thread whose caps should be fetched. - * @param off Offset of capabilities. - * @return Pointer to capabilities of \p tid, \c 0 otherwise. - */ -static capflags_t *__get_tcb_caps(id_t tid, size_t off) -{ - if (!cap_off_ok(off)) - return NULL; - - struct tcb *t = get_tcb(tid); - if (!t) - return NULL; - - return &t->caps; -} - -/** * Set capabilities. * * @param t Current tcb. @@ -38,17 +19,16 @@ static capflags_t *__get_tcb_caps(id_t tid, size_t off) * @param caps Mask of capabilities to set. * @return \ref OK on success, \ref ERR_INVAL on invalid input. */ -SYSCALL_DEFINE3(set_cap)(struct tcb *t, sys_arg_t tid, sys_arg_t off, - sys_arg_t caps) +SYSCALL_DEFINE2(set_cap)(struct tcb *t, sys_arg_t tid, sys_arg_t caps) { if (!is_set(t->caps, CAP_CAPS)) return_args1(t, ERR_PERM); - capflags_t *c; - if (!(c = __get_tcb_caps(tid, off))) - return_args1(t, ERR_INVAL); + struct tcb *c = get_tcb(tid); + if (!c) + return_args1(t, ERR_NF); - set_caps(*c, off, caps); + set_caps(c->caps, caps); return_args1(t, OK); } @@ -60,13 +40,13 @@ SYSCALL_DEFINE3(set_cap)(struct tcb *t, sys_arg_t tid, sys_arg_t off, * @param off Offset of capability, multiple of \c bits(cap). * @return \ref OK, capabilities. */ -SYSCALL_DEFINE2(get_cap)(struct tcb *t, sys_arg_t tid, sys_arg_t off) +SYSCALL_DEFINE1(get_cap)(struct tcb *t, sys_arg_t tid) { - capflags_t *c; - if (!(c = __get_tcb_caps(tid, off))) - return_args1(t, ERR_INVAL); + struct tcb *c = get_tcb(tid); + if (!c) + return_args1(t, ERR_NF); - return_args2(t, OK, get_caps(*c, off)); + return_args2(t, OK, c->caps); } /** @@ -74,21 +54,19 @@ SYSCALL_DEFINE2(get_cap)(struct tcb *t, sys_arg_t tid, sys_arg_t off) * * @param t Current tcb. * @param tid Thread ID whose capabilities to clear. - * @param off Offset of capability, multiple of \c bits(cap). * @param caps Mask of capabilities to clear. * @return ERR_LERM if invalid permissions, ERR_INVAL if \p tid doesn't exist, * otherwise OK. */ -SYSCALL_DEFINE3(clear_cap)(struct tcb *t, sys_arg_t tid, sys_arg_t off, - sys_arg_t caps) +SYSCALL_DEFINE2(clear_cap)(struct tcb *t, sys_arg_t tid, sys_arg_t caps) { if (!is_set(t->caps, CAP_CAPS)) return_args1(t, ERR_PERM); - capflags_t *c; - if (!(c = __get_tcb_caps(tid, off))) - return_args1(t, ERR_INVAL); + struct tcb *c = get_tcb(tid); + if (!c) + return_args1(t, ERR_NF); - clear_caps(*c, off, caps); + clear_caps(c->caps, caps); return_args1(t, OK); } diff --git a/src/uapi/conf.c b/src/uapi/conf.c index c8ec964..6a801f3 100644 --- a/src/uapi/conf.c +++ b/src/uapi/conf.c @@ -45,7 +45,7 @@ size_t rpc_stack_size() * @param d0 Optional data argument for parameter. * @return \ref OK and parameter value. */ -SYSCALL_DEFINE2(conf_get)(struct tcb *t, sys_arg_t param, sys_arg_t d0) +SYSCALL_DEFINE2(get_conf)(struct tcb *t, sys_arg_t param, sys_arg_t d0) { /* anyone can read any current parameter, I don't think they should be * hidden. */ @@ -93,7 +93,7 @@ SYSCALL_DEFINE2(conf_get)(struct tcb *t, sys_arg_t param, sys_arg_t d0) * @param val Value to set \c param to. * @return \ref OK and \c 0. */ -SYSCALL_DEFINE2(conf_set)(struct tcb *t, sys_arg_t param, sys_arg_t val) +SYSCALL_DEFINE2(set_conf)(struct tcb *t, sys_arg_t param, sys_arg_t val) { if (!has_cap(t->caps, CAP_CONF)) return_args1(t, ERR_PERM); @@ -133,9 +133,9 @@ SYSCALL_DEFINE1(poweroff)(struct tcb *t, sys_arg_t type) return_args1(t, ERR_PERM); switch (type) { - case SHUTDOWN: - case COLD_REBOOT: - case WARM_REBOOT: + case SYS_SHUTDOWN: + case SYS_COLD_REBOOT: + case SYS_WARM_REBOOT: return_args1(t, poweroff(type)); }; diff --git a/src/uapi/dispatch.c b/src/uapi/dispatch.c index 4135171..bbebd9c 100644 --- a/src/uapi/dispatch.c +++ b/src/uapi/dispatch.c @@ -57,15 +57,15 @@ void handle_syscall(sys_arg_t syscall, sys_arg_t a, sys_arg_t b, case SYS_FREE_MEM: sys_free_mem(t, a, b, c, d, e); break; case SYS_TIMEBASE: sys_timebase(t, a, b, c, d, e); break; case SYS_TICKS: sys_ticks(t, a, b, c, d, e); break; - case SYS_REQ_NOTIFICATION: sys_req_notification(t, a, b, c, d, e); - break; case SYS_REQ_REL_TIMER: sys_req_rel_timer(t, a, b, c, d, e); break; case SYS_REQ_ABS_TIMER: sys_req_abs_timer(t, a, b, c, d, e); break; + case SYS_FREE_TIMER: sys_free_timer(t, a, b, c, d, e); break; case SYS_IPC_REQ: sys_ipc_req(t, a, b, c, d, e); break; case SYS_IPC_FWD: sys_ipc_fwd(t, a, b, c, d, e); break; case SYS_IPC_KICK: sys_ipc_kick(t, a, b, c, d, e); break; case SYS_IPC_RESP: sys_ipc_resp(t, a, b, c, d, e); break; case SYS_IPC_GHOST: sys_ipc_ghost(t, a, b, c, d, e); break; + case SYS_SET_HANDLER: sys_set_handler(t, a, b, c, d, e); break; case SYS_NOTIFY: sys_notify(t, a, b, c, d, e); break; case SYS_CREATE: sys_create(t, a, b, c, d, e); break; case SYS_FORK: sys_fork(t, a, b, c, d, e); break; @@ -73,14 +73,15 @@ void handle_syscall(sys_arg_t syscall, sys_arg_t a, sys_arg_t b, case SYS_SPAWN: sys_spawn(t, a, b, c, d, e); break; case SYS_KILL: sys_kill(t, a, b, c, d, e); break; case SYS_SWAP: sys_swap(t, a, b, c, d, e); break; - case SYS_CONF_SET: sys_conf_set(t, a, b, c, d, e); break; - case SYS_CONF_GET: sys_conf_get(t, a, b, c, d, e); break; + case SYS_SET_CONF: sys_set_conf(t, a, b, c, d, e); break; + case SYS_GET_CONF: sys_get_conf(t, a, b, c, d, e); break; case SYS_SET_CAP: sys_set_cap(t, a, b, c, d, e); break; case SYS_GET_CAP: sys_get_cap(t, a, b, c, d, e); break; case SYS_CLEAR_CAP: sys_clear_cap(t, a, b, c, d, e); break; case SYS_POWEROFF: sys_poweroff(t, a, b, c, d, e); break; case SYS_SLEEP: sys_sleep(t, a, b, c, d, e); break; case SYS_IRQ_REQ: sys_irq_req(t, a, b, c, d, e); break; + case SYS_FREE_IRQ: sys_free_irq(t, a, b, c, d, e); break; case SYS_DETACH: sys_detach(t, a, b, c, d, e); break; case SYS_EXIT: sys_exit(t, a, b, c, d, e); break; default: diff --git a/src/uapi/irq.c b/src/uapi/irq.c index 9f3931c..69602af 100644 --- a/src/uapi/irq.c +++ b/src/uapi/irq.c @@ -28,16 +28,25 @@ SYSCALL_DEFINE1(irq_req)(struct tcb *t, sys_arg_t id) return_args1(t, register_irq(t, id)); } +SYSCALL_DEFINE1(free_irq)(struct tcb *t, sys_arg_t id) +{ + if (!has_cap(t->caps, CAP_IRQ)) + return_args1(t, ERR_PERM); + + return_args1(t, unregister_irq(t, id)); +} + /** * Actual notification handler setter. * * @param t Current tcb. - * @param tid Thread whose handler to set. + * @param tid Thread whose handler to set. Still not sure if this should just be + * the current thread, but I guess this might be a bit easier? * @param pid Process that is willing to handle notifications for the thread. * * @return OK on success, non-zero otherwise. */ -SYSCALL_DEFINE2(req_notification)(struct tcb *t, sys_arg_t tid, sys_arg_t pid) +SYSCALL_DEFINE2(set_handler)(struct tcb *t, sys_arg_t tid, sys_arg_t pid) { if (!has_cap(t->caps, CAP_SIGNAL)) return_args1(t, ERR_PERM); diff --git a/src/uapi/mem.c b/src/uapi/mem.c index b4b2df8..aeff7c5 100644 --- a/src/uapi/mem.c +++ b/src/uapi/mem.c @@ -140,7 +140,7 @@ SYSCALL_DEFINE2(req_page)(struct tcb *t, sys_arg_t size, sys_arg_t flags) * @param t Current tcb. * @param size Minimum size of allocation. * @param flags Flags of allocation. - * @return \ref OK, start and size + * @return \ref OK, start * in that order, \ref ERR_OOMEM otherwise. */ SYSCALL_DEFINE2(req_sharedmem)(struct tcb *t, sys_arg_t size, sys_arg_t flags) @@ -154,7 +154,7 @@ SYSCALL_DEFINE2(req_sharedmem)(struct tcb *t, sys_arg_t size, sys_arg_t flags) if (ERR_CODE(start)) return_args1(t, start); - return_args3(t, OK, start, size); + return_args2(t, OK, start); } /** @@ -185,5 +185,5 @@ SYSCALL_DEFINE3(ref_sharedmem)(struct tcb *t, sys_arg_t tid, sys_arg_t addr, if (ERR_CODE(start)) return_args1(t, start); - return_args3(t, OK, start, size); + return_args2(t, OK, start); } diff --git a/src/uapi/proc.c b/src/uapi/proc.c index eddbab8..74f211c 100644 --- a/src/uapi/proc.c +++ b/src/uapi/proc.c @@ -47,7 +47,7 @@ SYSCALL_DEFINE5(create)(struct tcb *t, sys_arg_t func, set_return(c, func); c->notify_id = t->notify_id; - return_args2(t, OK, c->tid); + return_args1(t, c->tid); } /** @@ -78,10 +78,10 @@ SYSCALL_DEFINE0(fork)(struct tcb *t) /* prepare args for when we eventually swap to the new proc, giving * parent ID as third return value */ - set_args3(n, OK, 0, get_eproc(t)->pid); + set_args2(n, 0, get_eproc(t)->pid); n->notify_id = c->notify_id; - return_args2(t, OK, n->pid); + return_args1(t, n->pid); } /** @@ -161,8 +161,17 @@ SYSCALL_DEFINE2(spawn)(struct tcb *t, sys_arg_t bin, sys_arg_t interp) if (!n) return_args1(t, ERR_OOMEM); + /** @todo copy over bin and interp into new process, this is not enough */ n->notify_id = c->notify_id; - return_args2(t, prepare_proc(n, bin, interp), n->pid); + stat_t ret = OK; + if ((ret = prepare_proc(n, bin, interp))) { + /* this kills the thread */ + orphanize(t); + unorphanize(t); + return_args1(t, ret); + } + + return_args1(t, n->pid); } /** diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 0000000..2012107 --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,34 @@ +TESTS := + +ARCH ?= riscv64 +CROSS_COMPILE ?= $(ARCH)-unknown-elf- + +LLVM ?= 0 +COMPILER != [ "$(LLVM)" != "0" ] \ + && echo clang --target="$(CROSS_COMPILE)" \ + || echo $(CROSS_COMPILE)gcc + +OBFLAGS := -ffreestanding -nostdlib -std=c17 -g +INCLUDEFLAGS := -I ../include -I. +WARNFLAGS := -Wall -Wextra +COMPILE := $(COMPILER) $(WARNFLAGS) $(INCLUDEFLAGS) $(OBFLAGS) + +GEN_INITRD := cpio -H newc -o > +QEMU := qemu-system-$(ARCH) -machine virt -kernel ../kmi.bin \ + -serial stdio \ + -monitor none \ + -nographic \ + -no-reboot \ + -initrd + +COMMON := build/printf.o ../kmi.bin ../include/kmi/syscalls.h + +include noop/source.mk +include hello-world/source.mk + +.PHONY: check +check: $(TESTS) + +build/printf.o: common/printf.c + mkdir -p build + $(COMPILE) -c common/printf.c -o build/printf.o diff --git a/tests/common/arch/riscv64/syscall.h b/tests/common/arch/riscv64/syscall.h new file mode 100644 index 0000000..aafda82 --- /dev/null +++ b/tests/common/arch/riscv64/syscall.h @@ -0,0 +1,50 @@ +#ifndef KMI_TESTS_ARCH_RISCV64_SYSCALL_H +#define KMI_TESTS_ARCH_RISCV64_SYSCALL_H + +#include <kmi/syscalls.h> +#include <kmi/attrs.h> + +static inline struct sys_ret syscall(size_t n, + long arg0, long arg1, long arg2, long arg3, + long arg4, + long arg5) +{ + register long a0 __asm__ ("a0") = arg0; + register long a1 __asm__ ("a1") = arg1; + register long a2 __asm__ ("a2") = arg2; + register long a3 __asm__ ("a3") = arg3; + register long a4 __asm__ ("a4") = arg4; + register long a5 __asm__ ("a5") = arg5; + +#define OUTPUTS "+r" (a0), "=r" (a1), "=r" (a2), "=r" (a3), "=r" (a4), "=r" (a5) + + if (n == 1) + __asm__ volatile ("ecall" : OUTPUTS : "r" (a0)); + + else if (n == 2) + __asm__ volatile ("ecall" : OUTPUTS : "r" (a0), "r" (a1)); + + else if (n == 3) + __asm__ volatile ("ecall" : OUTPUTS : "r" (a0), "r" (a1), + "r" (a2)); + + else if (n == 4) + __asm__ volatile ("ecall" : OUTPUTS + : "r" (a0), "r" (a1), "r" (a2), "r" (a3)); + + else if (n == 5) + __asm__ volatile ("ecall" : OUTPUTS + : "r" (a0), "r" (a1), "r" (a2), "r" (a3), + "r" (a4)); + + else if (n == 6) + __asm__ volatile ("ecall" : OUTPUTS + : "r" (a0), "r" (a1), "r" (a2), "r" (a3), + "r" (a4), "r" (a5)); + +#undef OUTPUTS + + return (struct sys_ret){a0, a1, a2, a3, a4, a5}; +} + +#endif /* KMI_TESTS_ARCH_RISCV64_SYSCALL_H */ diff --git a/tests/common/printf.c b/tests/common/printf.c new file mode 100644 index 0000000..0ad084b --- /dev/null +++ b/tests/common/printf.c @@ -0,0 +1,583 @@ +#include <stdarg.h> +#include "test.h" + +/* Directly copied from src/debug.c, which was copied from + * https://github.com/mpaland/printf/blob/master/printf.c + */ + +/** Ask kernel to print out one character for us. Only works if kernel was + * compiled with DEBUG=1. */ +static void __putchar(char c) +{ + sys_putch(c); +} + +/** Printf formatting left align flag. */ +#define LEFT_FLAG (1 << 0) + +/** Printf formatting explicit sign flag. */ +#define SIGN_FLAG (1 << 1) + +/** Printf formatting hash sign flag. */ +#define HASH_FLAG (1 << 2) + +/** Printf formatting zero padding flag. */ +#define ZERO_FLAG (1 << 3) + +/** Printf formatting ' flag. */ +#define FMT_FLAG (1 << 4) + +/** Printf formatting space flag. */ +#define SPACE_FLAG (1 << 5) + +/** Printf formatting long specifier flag. */ +#define LONG_FLAG (1 << 6) + +/** Printf formatting long long specifier flag. */ +#define LLONG_FLAG (1 << 7) + +/** Printf formatting short flag. */ +#define SHORT_FLAG (1 << 8) + +/** Printf formatting char flag. */ +#define CHAR_FLAG (1 << 9) + +/** Printf precision flag. */ +#define PRECS_FLAG (1 << 11) + +/** Printf unsigned flag. */ +#define UNSIGN_FLAG (1 << 12) + +/** Printf width flag. */ +#define WIDTH_FLAG (1 << 13) + +/** Printf padding flag. */ +#define PAD_FLAG (1 << 14) + +/** Printf continue flag. */ +#define CONT 1 + +/** Printf stop flag. */ +#define STOP 0 + +/** + * Check if character is ASCII decimal digit. + * + * @param c Character to check. + * @return \c true if character is ASCII decimal digit, \c false otherwise. + */ +static bool __is_digit(char c) +{ + return (c >= '0') && (c <= '9'); +} + +/** + * Convert string to corresponding number (assuming int). + * + * @param s Number string. + * @return Corresponding number. + */ +static int __atoi(const char *s) +{ + unsigned int i = 0; + while (__is_digit(*s)) { + i = i * 10 + (unsigned int)(*(s++) - '0'); + } + + return i; +} + +/** + * Calculate signed char from value using type interpretation. + * + * @param x Type to interpret value as. + * @param value Value to interpret. + * @param base Base to interpret value in. + */ +#define handle_type(x, value, base) \ + c = (x)value % (x)base; \ + value = (x)value / (x)base; +/** + * Convert number to string length. + * + * @param value Number to print. + * @param base Base to print in. + * @param flags Flags to output. + * @param print Print number as well. + * @return Length of corresponding string. + */ +static size_t __integral_val(ssize_t value, size_t base, size_t flags, + bool print) +{ + /* assume ascii numbers, which is why 'signed char' is probably fine */ + size_t ret = 0; + signed char c = 0; + + + if (flags & UNSIGN_FLAG) { + /* signed values, only with i format */ + if (flags & LLONG_FLAG) { + handle_type(signed long long, value, base); + } else if (flags & LONG_FLAG) { + handle_type(signed long, value, base); + } else if (flags & SHORT_FLAG) { + handle_type(signed short, value, base); + } else if (flags & CHAR_FLAG) { + handle_type(signed char, value, base); + } else { + handle_type(signed int, value, base); + } + + /* convert negative results into actual characters */ + c = c < 0 ? -c : c; + + } else { + /* unsigned values, everything else */ + if (flags & LLONG_FLAG) { + handle_type(unsigned long long, value, base); + } else if (flags & LONG_FLAG) { + handle_type(unsigned long, value, base); + } else if (flags & SHORT_FLAG) { + handle_type(unsigned short, value, base); + } else if (flags & CHAR_FLAG) { + handle_type(unsigned char, value, base); + } else { + handle_type(unsigned int, value, base); + } + } + + if (base == 16) + c += c > 9 ? 'a' - 10 : '0'; + else + c += '0'; + + if (value != 0) + ret = __integral_val(value, base, flags, print); + + if (print) + __putchar(c); + + return ret + 1; +} + +/** + * Print out a string. + * + * @param s String to print out. + * @return Bytes printed. + */ +static size_t __puts(const char *s) +{ + size_t i = 0; + while (s[i]) { + __putchar(s[i++]); + } + + return i; +} + +/** + * Print prefix corresponding to \c base. + * + * @param base Base to integer. + * @return Length of prefix as string. + */ +static size_t __print_prefix(size_t base) +{ + switch (base) { + case 16: return __puts("0x"); + case 8: return __puts("0"); + case 2: return __puts("0b"); + default: break; + } + + return 0; +} + +/** + * Print padding. + * + * @param pad Number of characters to print. + * @param pad_char Character to use as padding. + * @return Number of characters printed. + */ +static size_t __print_padding(size_t pad, char pad_char) +{ + size_t i = 0; + for (; i < pad; ++i) { + __putchar(pad_char); + } + + return i; +} + +/** + * Print signed value. + * + * @param value Value to print. + * @param flags Flags to printing. + * @return Number of characters written. + */ +static size_t __print_sign(ssize_t value, size_t flags) +{ + if (flags & LLONG_FLAG) + value = (signed long long)value; + else if (flags & LONG_FLAG) + value = (signed long)value; + else if (flags & SHORT_FLAG) + value = (signed short)value; + else if (flags & CHAR_FLAG) + value = (signed char)value; + else + value = (signed int)value; + + if (value < 0) { + __putchar('-'); + return 1; + } else if (flags & SIGN_FLAG) { + __putchar('+'); + return 1; + } + + return 0; +} + +/** + * Length of integral value as string. + * + * @param value Value to convert to string. + * @param base Base to interpret value as. + * @param flags Formatting flags. + * @return \see __integral_val(). + */ +#define __integral_len(value, base, flags) __integral_val((value), (base), \ + (flags), false) + +/** + * Print integral value as string. + * + * @param value Value to convert to string. + * @param base Base to interpret value as. + * @param flags Formatting flags. + * @return \see __integral_val(). + */ +#define __integral_print(value, base, flags) __integral_val((value), (base), \ + (flags), true) + +/** + * Print integral value. + * + * @param value Value to print. + * @param base Base to print value in. + * @param flags Flags to printing. + * @param width Minimum width of printing. + * @return Number of characters written. + */ +static size_t __print_integral(ssize_t value, size_t base, size_t flags, + size_t width) +{ + size_t ret = 0; + size_t raw_len = __integral_len(value, base, flags); + ssize_t pad = (flags & PAD_FLAG) ? width - raw_len : 0; + + /* depending on which flags are set, the prefix, sign and right justify has to + * be ordereder differently. */ + if (flags & ZERO_FLAG) { + if (!(flags & UNSIGN_FLAG)) + ret += __print_sign(value, flags); + + if (flags & HASH_FLAG) + ret += __print_prefix(base); + + if (pad > 0 && !(flags & LEFT_FLAG)) + ret += __print_padding(pad, '0'); + + } else if (flags & SPACE_FLAG) { + if (pad > 0 && !(flags & LEFT_FLAG)) + ret += __print_padding(pad, ' '); + + if (flags & UNSIGN_FLAG) + ret += __print_sign(value, flags); + + if (flags & HASH_FLAG) + ret += __print_prefix(base); + } else { + if (!(flags & UNSIGN_FLAG)) + ret += __print_sign(value, flags); + + if (flags & HASH_FLAG) + ret += __print_prefix(base); + } + + /* print value itself */ + ret += __integral_print(value, base, flags); + + /* left-justify */ + if (flags & ZERO_FLAG) { + if (pad > 0 && (flags & LEFT_FLAG)) + ret += __print_padding(pad, '0'); + } else if (flags & SPACE_FLAG) { + if (pad > 0 && (flags & LEFT_FLAG)) + ret += __print_padding(pad, ' '); + } + + return ret; +} + +int printf(const char *fmt, ...) +{ + /* largely inspired by + * https://github.com/mpaland/printf/blob/master/printf.c + */ + + /* Note that X is binary formatting, because who uses uppercase hex? */ + + va_list vl; + va_start(vl, fmt); + + size_t chars_written = 0; + + while (*fmt) { + if (*fmt != '%') { + __putchar(*fmt++); + chars_written++; + continue; + } + + fmt++; + if (*fmt == '%') { + /* literal percent sign */ + __putchar('%'); + chars_written++; + fmt++; + continue; + } + + /* check flags */ + size_t flags = 0; + int a = STOP; + do { + switch (*fmt) { + case ' ': + flags |= SPACE_FLAG; + fmt++; + a = CONT; + break; + + case '-': + flags |= LEFT_FLAG; + fmt++; + a = CONT; + break; + + case '+': + flags |= SIGN_FLAG; + fmt++; + a = CONT; + break; + + case '#': + flags |= HASH_FLAG; + fmt++; + a = CONT; + break; + + case '0': + flags |= ZERO_FLAG; + fmt++; + a = CONT; + break; + + case '\'': + flags |= FMT_FLAG; + fmt++; + a = CONT; + break; + + default: + a = STOP; + break; + } + } while (a != STOP); + + /* check width */ + size_t width = 0; + if (__is_digit(*fmt)) { + width = __atoi(fmt++); + flags |= WIDTH_FLAG | PAD_FLAG | SPACE_FLAG; + } else if (*fmt == '*') { + int w = va_arg(vl, int); + if (w < 0) { + width = -w; + flags |= LEFT_FLAG; + } else { + width = w; + } + flags |= WIDTH_FLAG | PAD_FLAG | SPACE_FLAG; + fmt++; + } + + /* check precision */ + size_t precision = 0; + if (*fmt == '.') { + fmt++; + flags |= PRECS_FLAG | PAD_FLAG | ZERO_FLAG; + if (__is_digit(*fmt)) { + precision = __atoi(fmt++); + } else if (*fmt == '*') { + precision = va_arg(vl, int); + fmt++; + } + } + + /* check length */ + switch (*fmt) { + case 'l': + fmt++; + if (*fmt == 'l') { + flags |= LLONG_FLAG; + fmt++; + } else { + flags |= LONG_FLAG; + } + break; + + case 'h': + fmt++; + if (*fmt == 'h') { + flags |= CHAR_FLAG; + fmt++; + } else { + flags |= SHORT_FLAG; + } + break; + + case 'j': + fmt++; + if (sizeof(intmax_t) == sizeof(long)) + flags |= LONG_FLAG; + else + flags |= LLONG_FLAG; + break; + + case 'z': + fmt++; + if (sizeof(size_t) == sizeof(long)) + flags |= LONG_FLAG; + else + flags |= LLONG_FLAG; + break; + + case 't': + fmt++; + if (sizeof(ptrdiff_t) == sizeof(long)) + flags |= LONG_FLAG; + else + flags |= LLONG_FLAG; + break; + } + + /* read actual specifier */ + size_t base = 10; + size_t value = 0; + int i = -1; + const char *s = 0; + void *p = 0; + int *n = 0; + char c = 0; + + switch (*fmt) { + case 'd': + case 'i': + case 'u': + case 'x': + case 'X': + case 'o': + case 'b': + /* integer handling */ + switch (*fmt) { + case 'x': + base = 16; + break; + case 'X': + base = 2; + break; + case 'o': + base = 8; + break; + default: + base = 10; + break; + } + + if (base == 10) + flags &= ~HASH_FLAG; + + /* precision takes precedence */ + if (flags & PRECS_FLAG) + width = precision; + + /* formatting doesn't apply to decimal integers + * */ + if (*fmt != 'i' && *fmt != 'd') { + flags &= ~SIGN_FLAG; + flags |= UNSIGN_FLAG; + } + + if (flags & LLONG_FLAG) + value = va_arg(vl, long long); + else if (flags & LONG_FLAG) + value = va_arg(vl, long); + else + value = va_arg(vl, int); + + chars_written += + __print_integral(value, base, flags, width); + fmt++; + break; + + case 'c': + c = va_arg(vl, int); + __putchar(c); + chars_written++; + fmt++; + break; + + case 's': + s = va_arg(vl, const char *); + + if (flags & PRECS_FLAG) + i = precision; + + for (; *s && i--;) { + __putchar(*s++); + chars_written++; + } + fmt++; + break; + + case 'p': + p = va_arg(vl, void *); + flags |= UNSIGN_FLAG | HASH_FLAG; + + if (sizeof(void *) == sizeof(long)) + flags |= LONG_FLAG; + else + flags |= LLONG_FLAG; + + chars_written += + __print_integral((ssize_t)p, 16, flags, width); + fmt++; + break; + + case 'n': + n = va_arg(vl, int *); + *n = chars_written; + fmt++; + break; + } + } + + va_end(vl); + return chars_written; +} diff --git a/tests/common/test.h b/tests/common/test.h new file mode 100644 index 0000000..3b591e0 --- /dev/null +++ b/tests/common/test.h @@ -0,0 +1,324 @@ +#ifndef KMI_TESTS_H +#define KMI_TESTS_H + +#include <kmi/attrs.h> +#include <kmi/types.h> + +#if defined(__riscv) +# if __riscv_xlen == 64 +#include "arch/riscv64/syscall.h" +# endif +#endif + +#define syscall0(op) syscall(1, op, 0, 0, 0, 0, 0) +#define syscall1(op, a0) syscall(2, op, a0, 0, 0, 0, 0) +#define syscall2(op, a0, a1) syscall(3, op, a0, a1, 0, 0, 0) +#define syscall3(op, a0, a1, a2) syscall(4, op, a0, a1, a2, 0, 0) +#define syscall4(op, a0, a1, a2, a3) syscall(5, op, a0, a1, a2, a3, 0) +#define syscall5(op, a0, a1, a2, a3, a4) syscall(6, op, a0, a1, a2, a3, a4) + +#define UNUSED(x) (void)x +#define START(pid, tid, d0, d1, d2, d3)\ +void _start(sys_arg_t pid, sys_arg_t tid,\ + sys_arg_t d0, sys_arg_t d1, sys_arg_t d2, sys_arg_t d3) + +static inline void sys_noop() +{ + syscall0(SYS_NOOP); +} + +static inline void sys_putch(char c) +{ + syscall1(SYS_PUTCH, c); +} + +static inline void *sys_req_mem(size_t size, vmflags_t flags) +{ + struct sys_ret r = syscall2(SYS_REQ_MEM, size, flags); + if (r.s) + return NULL; + + return (void *)r.a0; +} + +static inline void *sys_req_fixmem(uintptr_t fixed, size_t size, vmflags_t flags) +{ + struct sys_ret r = syscall3(SYS_REQ_FIXMEM, fixed, size, flags); + if (r.s) + return NULL; + + return (void *)r.a0; +} + +static inline void *sys_req_pmem(uintptr_t addr, size_t size, vmflags_t flags) +{ + struct sys_ret r = syscall3(SYS_REQ_PMEM, addr, size, flags); + if (r.s) + return NULL; + + return (void *)r.a0; +} + +static inline void *sys_req_page(size_t size, vmflags_t flags, uintptr_t *addr, size_t *asize) +{ + struct sys_ret r = syscall2(SYS_REQ_PAGE, size, flags); + if (r.s) + return NULL; + + if (addr) + *addr = r.a1; + + if (asize) + *asize = r.a2; + + return (void *)r.a0; +} + +static inline void *sys_req_sharedmem(size_t size, vmflags_t flags) +{ + struct sys_ret r = syscall2(SYS_REQ_SHAREDMEM, size, flags); + if (r.s) + return NULL; + + return (void *)r.a0; +} + +static inline void *sys_ref_sharedmem(id_t tid, uintptr_t addr, vmflags_t flags) +{ + struct sys_ret r = syscall3(SYS_REF_SHAREDMEM, tid, addr, flags); + if (r.s) + return NULL; + + return (void *)r.a0; +} + +static inline void sys_free_mem(uintptr_t start) +{ + syscall1(SYS_FREE_MEM, start); +} + +static inline uint64_t sys_timebase() +{ + struct sys_ret r = syscall0(SYS_TIMEBASE); + if (r.s) + return 0; + +#if defined(_LP64) + return r.a0; +#else + return ((uint64_t)r.a1 << 32) | r.a0 +#endif +} + +static inline uint64_t sys_ticks() +{ + struct sys_ret r = syscall0(SYS_TICKS); + if (r.s) + return 0; + +#if defined(_LP64) + return r.a0; +#else + return ((uint64_t)r.a1 << 32) | r.a0; +#endif +} + +static inline id_t sys_req_rel_timer(uint64_t ticks) +{ + struct sys_ret r; +#if defined(_LP64) + r = syscall2(SYS_REQ_REL_TIMER, ticks >> 32, ticks); +#else + r = syscall1(SYS_REQ_REL_TIMER, ticks); +#endif + + if (r.s) + return -1; + + return r.a0; +} + +static inline id_t sys_req_abs_timer(uint64_t ticks) +{ + struct sys_ret r; +#if defined(_LP64) + r = syscall2(SYS_REQ_ABS_TIMER, ticks >> 32, ticks); +#else + r = syscall1(SYS_REQ_ABS_TIMER, ticks); +#endif + + if (r.s) + return -1; + + return r.a0; +} + +#define sys_ipc_req0(pid) syscall1(SYS_IPC_REQ, pid) +#define sys_ipc_req1(pid, d0) syscall2(SYS_IPC_REQ, pid, d0) +#define sys_ipc_req2(pid, d0, d1) syscall3(SYS_IPC_REQ, pid, d0, d1) +#define sys_ipc_req3(pid, d0, d1, d2) syscall4(SYS_IPC_REQ, pid, d0, d1, d2) +#define sys_ipc_req4(pid, d0, d1, d2, d3) syscall5(SYS_IPC_REQ, pid, d0, d1, d2, d3) + +#define sys_ipc_fwd0(pid) syscall1(SYS_IPC_FWD, pid) +#define sys_ipc_fwd1(pid, d0) syscall2(SYS_IPC_FWD, pid, d0) +#define sys_ipc_fwd2(pid, d0, d1) syscall3(SYS_IPC_FWD, pid, d0, d1) +#define sys_ipc_fwd3(pid, d0, d1, d2) syscall4(SYS_IPC_FWD, pid, d0, d1, d2) +#define sys_ipc_fwd4(pid, d0, d1, d2, d3) syscall5(SYS_IPC_FWD, pid, d0, d1, d2, d3) + +#define sys_ipc_kick0(pid) syscall1(SYS_IPC_KICK, pid) +#define sys_ipc_kick1(pid, d0) syscall2(SYS_IPC_KICK, pid, d0) +#define sys_ipc_kick2(pid, d0, d1) syscall3(SYS_IPC_KICK, pid, d0, d1) +#define sys_ipc_kick3(pid, d0, d1, d2) syscall4(SYS_IPC_KICK, pid, d0, d1, d2) +#define sys_ipc_kick4(pid, d0, d1, d2, d3) syscall5(SYS_IPC_KICK, pid, d0, d1, d2, d3) + +#define sys_ipc_resp0(pid) syscall1(SYS_IPC_RESP, pid) +#define sys_ipc_resp1(pid, d0) syscall2(SYS_IPC_RESP, pid, d0) +#define sys_ipc_resp2(pid, d0, d1) syscall3(SYS_IPC_RESP, pid, d0, d1) +#define sys_ipc_resp3(pid, d0, d1, d2) syscall4(SYS_IPC_RESP, pid, d0, d1, d2) +#define sys_ipc_resp4(pid, d0, d1, d2, d3) syscall5(SYS_IPC_RESP, pid, d0, d1, d2, d3) + +#define sys_ipc_ghost0(pid) syscall1(SYS_IPC_GHOST, pid) +#define sys_ipc_ghost1(pid, d0) syscall2(SYS_IPC_GHOST, pid, d0) +#define sys_ipc_ghost2(pid, d0, d1) syscall3(SYS_IPC_GHOST, pid, d0, d1) +#define sys_ipc_ghost3(pid, d0, d1, d2) syscall4(SYS_IPC_GHOST, pid, d0, d1, d2) +#define sys_ipc_ghost4(pid, d0, d1, d2, d3) syscall5(SYS_IPC_GHOST, pid, d0, d1, d2, d3) + +static inline enum sys_status sys_set_handler(id_t tid, id_t pid) +{ + struct sys_ret r = syscall2(SYS_SET_HANDLER, tid, pid); + return r.s; +} + +static inline enum sys_status sys_notify(id_t tid) +{ + struct sys_ret r = syscall1(SYS_NOTIFY, tid); + return r.s; +} + +static inline id_t sys_create(uintptr_t func, long d0, long d1, long d2, long d3) +{ + struct sys_ret r = syscall5(SYS_CREATE, func, d0, d1, d2, d3); + return r.s; +} + +/* note that negative IDs are error values */ +static inline id_t sys_fork(id_t *new_id) +{ + struct sys_ret r = syscall0(SYS_FORK); + if (new_id) + *new_id = r.a0; + + return r.s; +} + +static inline enum sys_status sys_exec(uintptr_t bin, uintptr_t interp) +{ + struct sys_ret r = syscall2(SYS_EXEC, bin, interp); + /* if we reach this point, something's gone wrong */ + return r.s; +} + +/* negative IDs mean errors */ +static inline id_t sys_spawn(uintptr_t bin, uintptr_t interp) +{ + struct sys_ret r = syscall2(SYS_EXEC, bin, interp); + return r.s; +} + +static inline enum sys_status sys_kill(id_t pid) +{ + struct sys_ret r = syscall1(SYS_KILL, pid); + return r.s; +} + +static inline enum sys_status sys_swap(id_t tid) +{ + struct sys_ret r = syscall1(SYS_SWAP, tid); + return r.s; +} + +static inline enum sys_status sys_conf_set(enum conf_param param, long arg) +{ + struct sys_ret r = syscall2(SYS_SET_CONF, param, arg); + return r.s; +} + +static inline long sys_conf_get(enum conf_param param, long arg) +{ + struct sys_ret r = syscall2(SYS_GET_CONF, param, arg); + return r.a0; +} + +static inline enum sys_status sys_set_cap(id_t tid, enum sys_cap cap) +{ + struct sys_ret r = syscall2(SYS_SET_CAP, tid, cap); + return r.s; +} + +static inline enum sys_status sys_get_cap(id_t tid, enum sys_cap *cap) +{ + struct sys_ret r = syscall1(SYS_GET_CAP, tid); + *cap = r.a0; + return r.s; +} + +static inline enum sys_status sys_clear_cap(id_t tid, enum sys_cap cap) +{ + struct sys_ret r = syscall2(SYS_CLEAR_CAP, tid, cap); + return r.s; +} + +static inline enum sys_status sys_poweroff(enum poweroff_type type) +{ + struct sys_ret r = syscall1(SYS_POWEROFF, type); + return r.s; +} + +static inline enum sys_status sys_sleep() +{ + struct sys_ret r = syscall0(SYS_SLEEP); + return r.s; +} + +/** note that negative IDs mean errors */ +static inline id_t sys_irq_req(long irq) +{ + struct sys_ret r = syscall1(SYS_IRQ_REQ, irq); + return r.s; +} + +static inline enum sys_status sys_free_irq(long irq) +{ + struct sys_ret r = syscall1(SYS_FREE_IRQ, irq); + return r.s; +} + +static inline enum sys_status sys_detach(id_t tid) +{ + struct sys_ret r = syscall1(SYS_DETACH, tid); + return r.s; +} + +static inline enum sys_status sys_exit() +{ + struct sys_ret r = syscall0(SYS_EXIT); + return r.s; +} + +int printf(const char *fmt, ...) __printf; + +#define error(x, ...)\ + printf("ERROR: " x # __VA_ARGS__) + +#define check(x, y, ...)\ + if (!(x)) {\ + error(y #__VA_ARGS__);\ + sys_poweroff(SYS_SHUTDOWN);\ + } + +static inline void ok() { + printf("OK\n"); + sys_poweroff(SYS_SHUTDOWN); +} + +#endif /* KMI_TESTS_H */ diff --git a/tests/hello-world/init.c b/tests/hello-world/init.c new file mode 100644 index 0000000..fc882ec --- /dev/null +++ b/tests/hello-world/init.c @@ -0,0 +1,14 @@ +#include <common/test.h> + +START(pid, tid, d0, d1, d2, d3) +{ + UNUSED(pid); + UNUSED(tid); + UNUSED(d0); + UNUSED(d1); + UNUSED(d2); + UNUSED(d3); + + printf("Hello, world!\n"); + ok(); +} diff --git a/tests/hello-world/source.mk b/tests/hello-world/source.mk new file mode 100644 index 0000000..203a42e --- /dev/null +++ b/tests/hello-world/source.mk @@ -0,0 +1,8 @@ +TESTS := $(TESTS) hello-world + +.PHONY: hello-world +hello-world: hello-world/init.c $(COMMON) + mkdir -p build/hello-world + $(COMPILE) hello-world/init.c build/printf.o -o build/hello-world/init + echo build/hello-world/init | $(GEN_INITRD) hello-world/initrd + $(QEMU) hello-world/initrd | grep 'Hello, world!' diff --git a/tests/noop/init.c b/tests/noop/init.c new file mode 100644 index 0000000..a7e66f8 --- /dev/null +++ b/tests/noop/init.c @@ -0,0 +1,13 @@ +#include <common/test.h> + +START(pid, tid, d0, d1, d2, d3) +{ + UNUSED(pid); + UNUSED(tid); + UNUSED(d0); + UNUSED(d1); + UNUSED(d2); + UNUSED(d3); + sys_noop(); + ok(); +} diff --git a/tests/noop/source.mk b/tests/noop/source.mk new file mode 100644 index 0000000..087ce9b --- /dev/null +++ b/tests/noop/source.mk @@ -0,0 +1,8 @@ +TESTS := $(TESTS) noop + +.PHONY: noop +noop: noop/init.c $(COMMON) + mkdir -p build/noop + $(COMPILE) noop/init.c build/printf.o -o build/noop/init + echo build/noop/init | $(GEN_INITRD) noop/initrd + $(QEMU) noop/initrd | grep 'OK' |
