From 9273c16434316a8ab60dc78ee65a03e68031203a Mon Sep 17 00:00:00 2001 From: Kimplul Date: Fri, 29 Apr 2022 17:38:41 +0300 Subject: continue building base for irq handling --- .gitignore | 1 + Makefile | 8 ++- TODO.txt | 2 + arch/riscv64/conf/init | Bin 1096 -> 1096 bytes arch/riscv64/conf/initrd | Bin 1536 -> 1536 bytes arch/riscv64/conf/s.S | 10 +-- arch/riscv64/gen/asm-offsets.c | 44 +++++++++++++ arch/riscv64/gen/source.mk | 8 +++ arch/riscv64/include/regs.h | 9 +++ arch/riscv64/include/vmem.h | 22 ++++--- arch/riscv64/kernel/head.S | 136 ++++++++++++++++++++++++++++++----------- arch/riscv64/kernel/irq.c | 2 + arch/riscv64/kernel/proc.c | 17 ++++++ arch/riscv64/source.mk | 14 +++-- common/proc.c | 1 + common/tcb.c | 21 +++++-- common/uapi/dispatch.c | 7 +++ include/apos/syscalls.h | 4 ++ include/apos/uapi.h | 3 + include/apos/utils.h | 72 +++++++++++----------- 20 files changed, 282 insertions(+), 99 deletions(-) create mode 100644 arch/riscv64/gen/asm-offsets.c create mode 100644 arch/riscv64/gen/source.mk create mode 100644 arch/riscv64/include/regs.h diff --git a/.gitignore b/.gitignore index 2b3e86b..faa4724 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,6 @@ *.bin *.elf *.ld +include/apos/gen/* deps.mk *.img diff --git a/Makefile b/Makefile index 95a42ad..45c8b40 100644 --- a/Makefile +++ b/Makefile @@ -2,8 +2,8 @@ DO != echo -n > deps.mk # this could be done better DEBUGFLAGS != [ $(RELEASE) ] \ - && echo "-flto -O2 -DNDEBUG" \ - || echo "-O0 -ggdb3 -DDEBUG" + && echo "-flto -O2 -g -DNDEBUG" \ + || echo "-O0 -g -DDEBUG" CFLAGS = -ffreestanding -nostdlib -std=c17 -Wall -Wextra -Wvla DEPFLAGS = -MT $@ -MMD -MP -MF $@.d @@ -11,6 +11,10 @@ LINTFLAGS = -fsyntax-only PREPROCESS = -E LDFLAGS = -static-libgcc -lgcc +BUILD = build +ARCH_BUILD = $(BUILD)/arch/$(ARCH) +ARCH_SOURCE = arch/$(ARCH) + all: apos.bin # default values, overwrite if/when needed diff --git a/TODO.txt b/TODO.txt index e2d0b70..695d48f 100644 --- a/TODO.txt +++ b/TODO.txt @@ -64,3 +64,5 @@ SMP, IPC, timers a memory manager, everything else in userspace. + Choose between allowing the process manager to interrupt other cpus and each cpu starts its own process manager with a timer of some sort? + More const correctness, possibly better assembly but don't count on it. ++ Create some gdb scripts to ease debugging, sort of annoying to have to +constantly switch between physical and virtual ram diff --git a/arch/riscv64/conf/init b/arch/riscv64/conf/init index c415457..0a67c41 100755 Binary files a/arch/riscv64/conf/init and b/arch/riscv64/conf/init differ diff --git a/arch/riscv64/conf/initrd b/arch/riscv64/conf/initrd index 6d2ac10..6021c8f 100644 Binary files a/arch/riscv64/conf/initrd and b/arch/riscv64/conf/initrd differ diff --git a/arch/riscv64/conf/s.S b/arch/riscv64/conf/s.S index 1344f69..97c66ff 100644 --- a/arch/riscv64/conf/s.S +++ b/arch/riscv64/conf/s.S @@ -1,9 +1,9 @@ .text .global _start _start: -li a0, 1 -li a1, 2 -li a2, 3 -li a3, 4 -li a4, 5 +li a0, 0 /* 0 should be NOOP */ +li a1, 1 +li a2, 2 +li a3, 3 +li a4, 4 ecall diff --git a/arch/riscv64/gen/asm-offsets.c b/arch/riscv64/gen/asm-offsets.c new file mode 100644 index 0000000..d78e0ec --- /dev/null +++ b/arch/riscv64/gen/asm-offsets.c @@ -0,0 +1,44 @@ +#include +#include + +/* largely based on linux */ +#define DEFINE(sym, val) \ + __asm__ volatile("\n-> " #sym " %0 " #val "\n" ::"i"(val)) +#define OFFSETOF(m, s) DEFINE(offsetof_##m, offsetof(s, m)) +#define SIZEOF(n, s) DEFINE(sizeof_##n, sizeof(s)) + +void asm_offsets() +{ + OFFSETOF(ra, struct riscv_regs); + OFFSETOF(sp, struct riscv_regs); + OFFSETOF(tp, struct riscv_regs); + OFFSETOF(gp, struct riscv_regs); + OFFSETOF(t0, struct riscv_regs); + OFFSETOF(t1, struct riscv_regs); + OFFSETOF(t2, struct riscv_regs); + OFFSETOF(s0, struct riscv_regs); + OFFSETOF(s1, struct riscv_regs); + OFFSETOF(a0, struct riscv_regs); + OFFSETOF(a1, struct riscv_regs); + OFFSETOF(a2, struct riscv_regs); + OFFSETOF(a3, struct riscv_regs); + OFFSETOF(a4, struct riscv_regs); + OFFSETOF(a5, struct riscv_regs); + OFFSETOF(a6, struct riscv_regs); + OFFSETOF(a7, struct riscv_regs); + OFFSETOF(s2, struct riscv_regs); + OFFSETOF(s3, struct riscv_regs); + OFFSETOF(s4, struct riscv_regs); + OFFSETOF(s5, struct riscv_regs); + OFFSETOF(s6, struct riscv_regs); + OFFSETOF(s7, struct riscv_regs); + OFFSETOF(s8, struct riscv_regs); + OFFSETOF(s9, struct riscv_regs); + OFFSETOF(s10, struct riscv_regs); + OFFSETOF(s11, struct riscv_regs); + OFFSETOF(t3, struct riscv_regs); + OFFSETOF(t4, struct riscv_regs); + OFFSETOF(t5, struct riscv_regs); + OFFSETOF(t6, struct riscv_regs); + SIZEOF(registers, struct riscv_regs); +} diff --git a/arch/riscv64/gen/source.mk b/arch/riscv64/gen/source.mk new file mode 100644 index 0000000..d291983 --- /dev/null +++ b/arch/riscv64/gen/source.mk @@ -0,0 +1,8 @@ +$(ARCH_SOURCE)/include/gen/asm-offsets.h: $(ARCH_SOURCE)/gen/asm-offsets.c + echo "#ifndef APOS_ASM_OFFSETS_H" > $@ + echo "#define APOS_ASM_OFFSETS_H" >> $@ + $(COMPILER) $(INCLUDE_FLAGS) -S $< -o - |\ + awk '($$1 == "->") { print "#define " $$2 " " $$3 }' >> $@ + echo "#endif /* APOS_ASM_OFFSETS_H */" >> $@ + +CLEANUP += $(ARCH_SOURCE)/include/gen/* diff --git a/arch/riscv64/include/regs.h b/arch/riscv64/include/regs.h new file mode 100644 index 0000000..699af8b --- /dev/null +++ b/arch/riscv64/include/regs.h @@ -0,0 +1,9 @@ +#ifndef APOS_RISCV_REGS_H +#define APOS_RISCV_REGS_H + +struct riscv_regs { + long ra, sp, gp, tp, t0, t1, t2, s0, s1, a0, a1, a2, a3, a4, a5, a6, a7, + s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, t3, t4, t5, t6; +}; + +#endif /* APOS_RISCV_REGS_H */ diff --git a/arch/riscv64/include/vmem.h b/arch/riscv64/include/vmem.h index 9816110..222d54b 100644 --- a/arch/riscv64/include/vmem.h +++ b/arch/riscv64/include/vmem.h @@ -4,16 +4,20 @@ #include #include -#define RISCV_NUM_LEAVES (4096 / sizeof(void *)) +#if __riscv_xlen == 64 +#define RISCV_NUM_LEAVES 512 +#else +#define RISCV_NUM_LEAVES 1024 +#endif -#define VM_V (1 << 0) -#define VM_R (1 << 1) -#define VM_W (1 << 2) -#define VM_X (1 << 3) -#define VM_U (1 << 4) -#define VM_G (1 << 5) -#define VM_A (1 << 6) -#define VM_D (1 << 7) +#define VM_V (1 << 0) +#define VM_R (1 << 1) +#define VM_W (1 << 2) +#define VM_X (1 << 3) +#define VM_U (1 << 4) +#define VM_G (1 << 5) +#define VM_A (1 << 6) +#define VM_D (1 << 7) enum mm_mode { Sv48, diff --git a/arch/riscv64/kernel/head.S b/arch/riscv64/kernel/head.S index d2a1927..cef74ba 100644 --- a/arch/riscv64/kernel/head.S +++ b/arch/riscv64/kernel/head.S @@ -1,3 +1,4 @@ +#include #include #if __riscv_xlen == 64 @@ -8,28 +9,57 @@ #define lr lw #endif +/* very much based on linux, but why change it if works, eh? */ .section .text +.align 4 .global handle_irq handle_irq: - addi sp,sp,-144 - sr ra,136(sp) - sr t0,128(sp) - sr t1,120(sp) - sr t2,112(sp) - sr s0,104(sp) - sr a0,96(sp) - sr a1,88(sp) - sr a2,80(sp) - sr a3,72(sp) - sr a4,64(sp) - sr a5,56(sp) - sr a6,48(sp) - sr a7,40(sp) - sr t3,32(sp) - sr t4,24(sp) - sr t5,16(sp) - sr t6,8(sp) - addi s0,sp,144 + csrrw tp, CSR_SSCRATCH, tp + bnez tp, _save_context + /* the exception came from the kernel, should probably handle but for + * now just spin in place */ + 1: j 1b +_save_context: + /* store user stack pointer and replace it with kernel stack */ + addi tp, tp,-sizeof_registers + sr sp, offsetof_sp(tp) + mv sp, tp + + /* set scratch to 0 and move thread pointer back */ + csrrw tp, CSR_SSCRATCH, x0 + + /* save registers */ + sr ra, offsetof_ra(sp) + sr gp, offsetof_gp(sp) + sr tp, offsetof_tp(sp) + sr t0, offsetof_t0(sp) + sr t1, offsetof_t1(sp) + sr t2, offsetof_t2(sp) + sr s0, offsetof_s0(sp) + sr a0, offsetof_a0(sp) + sr a1, offsetof_a1(sp) + sr a2, offsetof_a2(sp) + sr a3, offsetof_a3(sp) + sr a4, offsetof_a4(sp) + sr a5, offsetof_a5(sp) + sr a6, offsetof_a6(sp) + sr a7, offsetof_a7(sp) + sr s1, offsetof_s1(sp) + sr s2, offsetof_s2(sp) + sr s3, offsetof_s3(sp) + sr s4, offsetof_s4(sp) + sr s5, offsetof_s5(sp) + sr s6, offsetof_s6(sp) + sr s7, offsetof_s7(sp) + sr s8, offsetof_s8(sp) + sr s9, offsetof_s9(sp) + sr s10,offsetof_s10(sp) + sr s11,offsetof_s11(sp) + sr t3, offsetof_t3(sp) + sr t4, offsetof_t4(sp) + sr t5, offsetof_t5(sp) + sr t6, offsetof_t6(sp) + /* load supervisor cause */ csrr s4, CSR_SCAUSE /* interrupts fall through, exceptions jump */ @@ -44,29 +74,61 @@ handle_exception: j restore_all handle_syscall: + /* TODO: CSR_EPC should be moved forward one instruction when dealing + * with syscalls */ jal syscall_dispatch + /* if we had a thread switch, load kernel stack of current thread and + * restore its context */ + /* TODO: is a whole function call necessary? */ + mv s0, a0 + /* get current tcb */ + call cur_tcb + mv tp, a0 + /* get associated kernel stack */ + mv sp, tp + addi sp, sp, -sizeof_registers j restore_noreturn restore_all: - lr a0,96(sp) + lr s0, offsetof_s0(sp) restore_noreturn: - lr ra,136(sp) - lr t0,128(sp) - lr t1,120(sp) - lr t2,112(sp) - lr s0,104(sp) - lr a1,88(sp) - lr a2,80(sp) - lr a3,72(sp) - lr a4,64(sp) - lr a5,56(sp) - lr a6,48(sp) - lr a7,40(sp) - lr t3,32(sp) - lr t4,24(sp) - lr t5,16(sp) - lr t6,8(sp) - addi sp,sp,144 + /* restore registers besides possible return value, assume instruction + * to return to has already been set (CSR_EPC) */ + csrw CSR_SSCRATCH, tp + + lr ra, offsetof_ra(sp) + lr gp, offsetof_gp(sp) + lr tp, offsetof_tp(sp) + lr t0, offsetof_t0(sp) + lr t1, offsetof_t1(sp) + lr t2, offsetof_t2(sp) + lr a0, offsetof_a0(sp) + lr a1, offsetof_a1(sp) + lr a2, offsetof_a2(sp) + lr a3, offsetof_a3(sp) + lr a4, offsetof_a4(sp) + lr a5, offsetof_a5(sp) + lr a6, offsetof_a6(sp) + lr s1, offsetof_s1(sp) + lr s2, offsetof_s2(sp) + lr s3, offsetof_s3(sp) + lr s4, offsetof_s4(sp) + lr s5, offsetof_s4(sp) + lr s6, offsetof_s6(sp) + lr s7, offsetof_s7(sp) + lr s8, offsetof_s8(sp) + lr s9, offsetof_s9(sp) + lr s10,offsetof_s10(sp) + lr s11,offsetof_s11(sp) + lr a7, offsetof_a7(sp) + lr t3, offsetof_t3(sp) + lr t4, offsetof_t4(sp) + lr t5, offsetof_t5(sp) + lr t6, offsetof_t6(sp) + + /* restore stack pointer */ + lr sp, offsetof_sp(sp) + /* assume supervisor for now */ sret diff --git a/arch/riscv64/kernel/irq.c b/arch/riscv64/kernel/irq.c index d350426..fdcd21e 100644 --- a/arch/riscv64/kernel/irq.c +++ b/arch/riscv64/kernel/irq.c @@ -3,6 +3,8 @@ #include #include +extern void handle_irq(void); + void init_irq(void *fdt) { UNUSED(fdt); diff --git a/arch/riscv64/kernel/proc.c b/arch/riscv64/kernel/proc.c index 66325ec..8653254 100644 --- a/arch/riscv64/kernel/proc.c +++ b/arch/riscv64/kernel/proc.c @@ -1,10 +1,12 @@ #include #include +#include #include stat_t jump_to_userspace(struct tcb *t, int argc, char **argv) { csr_write(CSR_SEPC, t->entry); + csr_write(CSR_SSCRATCH, t); __asm__ volatile("mv sp, %0\n" ::"r"(t->proc_stack_top) : "memory"); __asm__ volatile("sret\n" ::: "memory"); /* we should never reach this */ @@ -16,3 +18,18 @@ stat_t return_to_userspace(struct tcb *t) /* lol */ return ERR_ADDR; } + +stat_t prepare_thread(struct tcb *t) +{ + /* 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; + r--; + + r->sp = (long)t->proc_stack_top; + r->tp = (long)t; + + /* set entry point */ + csr_write(CSR_SEPC, t->entry); + return OK; +} diff --git a/arch/riscv64/source.mk b/arch/riscv64/source.mk index fec5f80..8c1dfcb 100644 --- a/arch/riscv64/source.mk +++ b/arch/riscv64/source.mk @@ -1,8 +1,9 @@ -KERNEL_LOCAL != echo arch/riscv64/kernel/*.[cS] +KERNEL_LOCAL != echo $(ARCH_SOURCE)/kernel/*.[cS] KERNEL_SOURCES += $(KERNEL_LOCAL) -INIT_SOURCES += arch/riscv64/init/*.[cS] +INIT_SOURCES += $(ARCH_SOURCE)/init/*.[cS] -CLEANUP_CMD := ./arch/riscv64/conf/rmimage.sh +# this doesn't work for rv32, but fine for now */ +CLEANUP_CMD := $(ARCH_SOURCE)/conf/rmimage.sh ARCH_CFLAGS := -mcmodel=medany ARCH_LDFLAGS := @@ -15,4 +16,9 @@ ARCH_LDFLAGS := #LINKFLAGS := -fuse-ld=bfd run: - ./arch/riscv64/conf/mkimage.sh + $(ARCH_SOURCE)/conf/mkimage.sh + +include $(ARCH_SOURCE)/gen/source.mk + +# dependecy generation +$(ARCH_BUILD)/kernel/head.o: $(ARCH_SOURCE)/include/gen/asm-offsets.h diff --git a/common/proc.c b/common/proc.c index ea3ab5d..8793d00 100644 --- a/common/proc.c +++ b/common/proc.c @@ -37,6 +37,7 @@ stat_t init_proc(void *fdt, struct vm_branch *b) init_uvmem(t, UVMEM_START, UVMEM_END); + /* TODO: this stuff should be placed in __sys_exec */ /* the binary gets to choose first what memory regions it requires */ t->entry = load_elf(t, get_init_base(fdt)); if (!t->entry) diff --git a/common/tcb.c b/common/tcb.c index 5d6b8a2..326d9ac 100644 --- a/common/tcb.c +++ b/common/tcb.c @@ -19,8 +19,6 @@ static struct tcb *cpu_tcb[MAX_CPUS] = { 0 }; void init_tcbs() { - /* assumption: init_tcb called after memory subsystem is initialized */ - init_nodes(&root, sizeof(struct tcb)); /* MM_O1 is 2MiB on riscv64, so 262144 different possible thread ids. * Should be enough, if we're really strapped for memory I might try * something smaller but this is fine for now. */ @@ -55,8 +53,17 @@ struct tcb *new_thread() if (unlikely(!tcbs)) return 0; - struct tcb *t = (struct tcb *)get_node(&root); - t->tid = __alloc_tid(t); + vm_t bottom = alloc_page(MM_O0, 0); + /* move tcb to top of kernel stack, keeping alignment in check + * (hopefully) */ + /* TODO: check alignment */ + struct tcb *t = (struct tcb *)align_down(bottom + __o_size(MM_O0) - sizeof(struct tcb), sizeof(long)); + memset(t, 0, sizeof(struct tcb)); + + id_t tid = __alloc_tid(t); + tcbs[tid] = t; + t->tid = tid; + return t; } @@ -67,8 +74,10 @@ void destroy_thread(struct tcb *t) /* remove thread id from list */ tcbs[t->tid] = 0; - /* free node associated with tcb */ - free_node(&root, t); + + /* free associated kernel stack */ + vm_t bottom = align_down((vm_t)t, __o_size(MM_O0)); + free_page(MM_O0, (pm_t)bottom); } struct tcb *cur_tcb() diff --git a/common/uapi/dispatch.c b/common/uapi/dispatch.c index e6c334a..5522c9f 100644 --- a/common/uapi/dispatch.c +++ b/common/uapi/dispatch.c @@ -1,6 +1,8 @@ #include static const sys_t syscall_table[] = { + /* noop */ + [SYS_NOOP] = sys_noop, /* mem */ [SYS_REQ_MEM] = sys_req_mem, [SYS_REQ_PMEM] = sys_req_pmem, @@ -29,6 +31,11 @@ static const sys_t syscall_table[] = { [SYS_POWEROFF] = sys_poweroff, }; +SYSCALL_DEFINE0(noop)() +{ + return 0; +} + vm_t syscall_dispatch(vm_t syscall, vm_t a, vm_t b, vm_t c, vm_t d) { sys_t call = syscall_table[syscall]; diff --git a/include/apos/syscalls.h b/include/apos/syscalls.h index ed18872..6f9e312 100644 --- a/include/apos/syscalls.h +++ b/include/apos/syscalls.h @@ -4,6 +4,10 @@ /* enum for now, possibly macros in the future once I get an approximate idea of * which syscalls are necessary etc. */ enum { + /* noop, for testing out how many syscalls per second can done I suppose + * */ + SYS_NOOP, + /* memory management */ SYS_REQ_MEM, /* request memory from anywhere */ SYS_REQ_PMEM, /* request physical address */ diff --git a/include/apos/uapi.h b/include/apos/uapi.h index fa66b31..a6fc2b7 100644 --- a/include/apos/uapi.h +++ b/include/apos/uapi.h @@ -60,6 +60,9 @@ typedef vm_t (*sys_t)(vm_t, vm_t, vm_t, vm_t); } \ static vm_t __##name +/* noop */ +SYSCALL_DECLARE(noop); + /* memory */ SYSCALL_DECLARE(req_mem); SYSCALL_DECLARE(req_pmem); diff --git a/include/apos/utils.h b/include/apos/utils.h index d7dd8b9..e09a35f 100644 --- a/include/apos/utils.h +++ b/include/apos/utils.h @@ -51,21 +51,21 @@ /* clang-format doesn't like _Generic, but I guess that's fine. */ #define align_up(x, y) \ - _Generic((x), signed char \ - : align_up_c, signed short \ - : align_up_s, signed int \ - : align_up_i, signed long \ - : align_up_l, signed long long \ - : align_up_ll,\ + _Generic((x), signed char \ + : align_up_c, signed short \ + : align_up_s, signed int \ + : align_up_i, signed long \ + : align_up_l, signed long long \ + : align_up_ll, \ \ - unsigned char \ - : align_up_uc, unsigned short \ - : align_up_us, unsigned int \ - : align_up_ui, unsigned long \ - : align_up_ul, unsigned long long\ + unsigned char \ + : align_up_uc, unsigned short \ + : align_up_us, unsigned int \ + : align_up_ui, unsigned long \ + : align_up_ul, unsigned long long \ : align_up_ull)((x), (y)) -#define DEFINE_ALIGN_UP(name, type) \ +#define DEFINE_ALIGN_UP(name, type) \ static inline type align_up_##name(type val, type a) \ { \ if (!a) \ @@ -92,21 +92,21 @@ DEFINE_ALIGN_UP(ul, unsigned long); DEFINE_ALIGN_UP(ull, unsigned long long); #define align_down(x, y) \ - _Generic((x), signed char \ + _Generic((x), signed char \ : align_down_c, signed short \ - : align_down_s, signed int \ - : align_down_i, signed long \ - : align_down_l, signed long long \ - : align_down_ll,\ + : align_down_s, signed int \ + : align_down_i, signed long \ + : align_down_l, signed long long \ + : align_down_ll, \ \ - unsigned char \ - : align_down_uc, unsigned short \ - : align_down_us, unsigned int \ - : align_down_ui, unsigned long \ - : align_down_ul, unsigned long long\ - : align_down_ull)((x), (y)) - -#define DEFINE_ALIGN_DOWN(name, type) \ + unsigned char \ + : align_down_uc, unsigned short \ + : align_down_us, unsigned int \ + : align_down_ui, unsigned long \ + : align_down_ul, unsigned long long \ + : align_down_ull)((x), (y)) + +#define DEFINE_ALIGN_DOWN(name, type) \ static inline type align_down_##name(type val, type a) \ { \ if (!a) \ @@ -128,21 +128,21 @@ DEFINE_ALIGN_DOWN(ul, unsigned long); DEFINE_ALIGN_DOWN(ull, unsigned long long); #define is_aligned(x, y) \ - _Generic((x), signed char \ + _Generic((x), signed char \ : is_aligned_c, signed short \ - : is_aligned_s, signed int \ - : is_aligned_i, signed long \ - : is_aligned_l, signed long long \ - : is_aligned_ll,\ + : is_aligned_s, signed int \ + : is_aligned_i, signed long \ + : is_aligned_l, signed long long \ + : is_aligned_ll, \ \ - unsigned char \ - : is_aligned_uc, unsigned short \ - : is_aligned_us, unsigned int \ - : is_aligned_ui, unsigned long \ - : is_aligned_ul, unsigned long long \ + unsigned char \ + : is_aligned_uc, unsigned short \ + : is_aligned_us, unsigned int \ + : is_aligned_ui, unsigned long \ + : is_aligned_ul, unsigned long long \ : is_aligned_ll)((x), (y)) -#define DEFINE_ALIGNED(name, type) \ +#define DEFINE_ALIGNED(name, type) \ static inline bool is_aligned_##name(type val, type a) \ { \ if (!a) \ -- cgit v1.3