commit e50fb2de2c7c39796a2cba56bbc3a1506bf68e6c Author: Quantum Date: Mon Jan 3 19:11:49 2022 -0500 Initial Commit Mostly stolen from https://github.com/avdgrinten/riscv-toy-os Changed load address to 0x45000000 so it would run on real hardware Played around with GPIO and the watchdog diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f045a63 --- /dev/null +++ b/.gitignore @@ -0,0 +1,117 @@ +### C ### +# Prerequisites +*.d + +# Object files +*.ko +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects +*.so +*.so.* +*.dylib + +# Executables +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +### C++ ### +# Prerequisites + +# Compiled Object files +*.slo + +# Precompiled Headers + +# Compiled Dynamic libraries + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai + +# Executables + +### Meson ### +# subproject directories +/subprojects/* +!/subprojects/*.wrap + +# Meson Directories +meson-logs +meson-private + +# Meson Files +meson_benchmark_setup.dat +meson_test_setup.dat +sanitycheckcpp.cc # C++ specific +sanitycheckcpp.exe # C++ specific + +# Ninja +build.ninja +.ninja_deps +.ninja_logs + +# Misc +compile_commands.json + +### Vim ### +# Swap +[._]*.s[a-v][a-z] +!*.svg # comment out if you don't need vector files +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim +Sessionx.vim + +# Temporary +.netrwhist +# Auto-generated tag files +tags +# Persistent undo +[._]*.un~ + +# Cache +.cache/ + +### Build dir ### +build/ diff --git a/kernel/boot.S b/kernel/boot.S new file mode 100644 index 0000000..ca07be3 --- /dev/null +++ b/kernel/boot.S @@ -0,0 +1,201 @@ +.section .text + +.global _start +_start: + # a0: HART ID + # a1: DTB + + la sp, stack_top + mv s0, zero + + jal kmain + unimp + +# void save_stack(void (*f)(void *, continuation), void *ctx) +.global save_stack +save_stack: + addi sp, sp, -0x78 + + sd gp, 0x00(sp) + sd tp, 0x08(sp) + sd s0, 0x10(sp) + sd s1, 0x18(sp) + sd s2, 0x20(sp) + sd s3, 0x28(sp) + sd s4, 0x30(sp) + sd s5, 0x38(sp) + sd s6, 0x40(sp) + sd s7, 0x48(sp) + sd s8, 0x50(sp) + sd s9, 0x58(sp) + sd s10, 0x60(sp) + sd s11, 0x68(sp) + sd ra, 0x70(sp) + + mv t0, a0 + mv a0, a1 + mv a1, sp + jr t0 + +.global restore_stack +restore_stack: + mv sp, a0 + ld gp, 0x00(sp) + ld tp, 0x08(sp) + ld s0, 0x10(sp) + ld s1, 0x18(sp) + ld s2, 0x20(sp) + ld s3, 0x28(sp) + ld s4, 0x30(sp) + ld s5, 0x38(sp) + ld s6, 0x40(sp) + ld s7, 0x48(sp) + ld s8, 0x50(sp) + ld s9, 0x58(sp) + ld s10, 0x60(sp) + ld s11, 0x68(sp) + ld ra, 0x70(sp) + + addi sp, sp, 0x78 + + ret + +# void enter_umode(void *entry); +.global enter_umode +enter_umode: + # Do not trash x10 (= a0) + mv x1, zero + #mv x2, zero # SP + mv x3, zero + mv x4, zero + mv x5, zero + mv x6, zero + mv x7, zero + mv x8, zero + mv x9, zero + mv x11, zero + mv x12, zero + mv x13, zero + mv x14, zero + mv x15, zero + mv x16, zero + mv x17, zero + mv x18, zero + mv x19, zero + mv x20, zero + mv x21, zero + mv x22, zero + mv x23, zero + mv x24, zero + mv x25, zero + mv x26, zero + mv x27, zero + mv x28, zero + mv x29, zero + mv x30, zero + mv x31, zero + + csrw sepc, a0 + + csrr a0, sstatus + # Set SPIE. + ori a0, a0, 0x20 + # Clear SPP. + andi a0, a0, -1 - 0x100 + csrw sstatus, a0 + + sret + +.global isr +.align 2 +isr: + # Exchange s1 and sscratch. + csrrw s1, sscratch, s1 + + # Store all temporary registers. + sd ra, 0x10(s1) + sd a0, 0x18(s1) + sd a1, 0x20(s1) + sd a2, 0x28(s1) + sd a3, 0x30(s1) + sd a4, 0x38(s1) + sd a5, 0x40(s1) + sd a6, 0x48(s1) + sd a7, 0x50(s1) + sd t0, 0x58(s1) + sd t1, 0x60(s1) + sd t2, 0x68(s1) + sd t3, 0x70(s1) + sd t4, 0x78(s1) + sd t5, 0x80(s1) + sd t6, 0x88(s1) + + # Save sstatus (contains interrupt state etc.) + csrr t0, sstatus + sd t0, 0x98(s1) + # Save sepc. + csrr t0, sepc + sd t0, 0xA0(s1) + + # Check if we interrupted user mode or supervisor mode. +# csrr t0, scause +# andi t0, t0, 0x100 +# bnez t0, 1f + + # TODO: If we interrupted user mode: also store callee-saved registers. + # (For example, to implement signals.) + # TODO: If we interrupted user mode: load a kernel stack pointer. + +1: + # Store the next isr_frame in sccratch and re-load the saved s1 (which is still in sccratch). + ld t0, 0x08(s1) + beqz t0, 2f + csrrw t1, sscratch, t0 + # Store the saved s1 in the isr_frame. + sd t1, 0x90(s1) + + mv a0, s1 + jal handle_isr + + # Load the saved s1 into sscratch. + ld t0, 0x90(s1) + csrw sscratch, t0 + + # Restore sstatus. + ld t0, 0x98(s1) + csrw sstatus, t0 + # Restore sepc. + ld t0, 0xA0(s1) + csrw sepc, t0 + + # Store all temporary registers. + ld ra, 0x10(s1) + ld a0, 0x18(s1) + ld a1, 0x20(s1) + ld a2, 0x28(s1) + ld a3, 0x30(s1) + ld a4, 0x38(s1) + ld a5, 0x40(s1) + ld a6, 0x48(s1) + ld a7, 0x50(s1) + ld t0, 0x58(s1) + ld t1, 0x60(s1) + ld t2, 0x68(s1) + ld t3, 0x70(s1) + ld t4, 0x78(s1) + ld t5, 0x80(s1) + ld t6, 0x88(s1) + + # Exchange s1 and sscratch. + csrrw s1, sscratch, s1 + + # Returns from the trap. + sret + +2: + jal isr_frame_overflow + unimp + +.section .bss + .space 0x10000 +stack_top: diff --git a/kernel/kernel.cpp b/kernel/kernel.cpp new file mode 100644 index 0000000..99d5a7e --- /dev/null +++ b/kernel/kernel.cpp @@ -0,0 +1,349 @@ +#include +#include + +#define read_csr(csr) \ + ({ \ + unsigned long __v; \ + asm volatile ("csrr %0, " csr : "=r"(__v) : : "memory");\ + __v;\ + }) + +#define write_csr(csr, v) \ + ({ \ + unsigned long __v = (v); \ + asm volatile ("csrw " csr ", %0" : : "r"(__v) : "memory");\ + }) + +#define set_csr_bits(csr, bits) \ + ({ \ + unsigned long __v = (bits); \ + asm volatile ("csrs " csr ", %0" : : "r"(__v) : "memory");\ + }) + +#define clear_csr_bits(csr, bits) \ + ({ \ + unsigned long __v = (bits); \ + asm volatile ("csrc " csr ", %0" : : "r"(__v) : "memory");\ + }) + +typedef unsigned long sbi_word; + +void sbi_call1(int ext, int func, sbi_word arg0) { + register sbi_word rExt asm("a7") = ext; + register sbi_word rFunc asm("a6") = func; + register sbi_word rArg0 asm("a0") = arg0; + register sbi_word rArg1 asm("a1"); + asm volatile("ecall" : "+r"(rArg0), "=r"(rArg1) : "r"(rExt), "r"(rFunc)); + if(rArg0) + __builtin_trap(); +} + +void sbi_call2(int ext, int func, sbi_word arg0, sbi_word arg1) { + register sbi_word rExt asm("a7") = ext; + register sbi_word rFunc asm("a6") = func; + register sbi_word rArg0 asm("a0") = arg0; + register sbi_word rArg1 asm("a1") = arg1; + asm volatile("ecall" : "+r"(rArg0), "+r"(rArg1) : "r"(rExt), "r"(rFunc)); + if(rArg0) + __builtin_trap(); +} + +void fmt(const char *f, ...) { + va_list va; + va_start(va, f); + while(*f) { + if(*f != '{') { + sbi_call1(1, 0, *(f++)); + continue; + } + f++; + + while(*f != '}') { + if(!(*f)) + return; + f++; + } + f++; + + unsigned long v = va_arg(va, unsigned long); + for(int i = 0; i < 16; ++i) { + const char *digits = "0123456789abcdef"; + int d = (v >> (60 - i * 4)) & 0xF; + sbi_call1(1, 0, digits[d]); + } + } + va_end(va); +} + +enum { + pte_valid = 1 << 0, + pte_r = 1 << 1, + pte_w = 1 << 2, + pte_x = 1 << 3, + pte_user = 1 << 4, + pte_access = 1 << 6, + pte_dirty = 1 << 7, +}; + +enum { + pte_ppn_shift = 10 +}; + +struct pt_t { + unsigned long ptes[512]; +} __attribute__((aligned(4096))); + +enum { + satp_sv48 = 9UL << 60 +}; + +const char *exception_strings[] = { + "instruction misaligned", + "instruction access fault", + "illegal instruction", + "breakpoint", + "load misaligned", + "load access fault", + "store misaligned", + "store access fault", + "u-mode ecall", + "s-mode ecall", + "reserved (10)", + "reserved (11)", + "instruction page fault", + "load page fault", + "reserved (14)", + "store page fault", +}; + +enum { + sie_s_software = (1 << 1), + sie_s_timer = (1 << 5) +}; + +enum { + sstatus_sie = (1 << 1) +}; + +void cli() { + clear_csr_bits("sstatus", sstatus_sie); +} + +void sti() { + set_csr_bits("sstatus", sstatus_sie); +} + +typedef struct { + void *sp; +} continuation_t; + +struct task_t; + +struct isr_frame_t { + task_t *task; + isr_frame_t *next; + unsigned long ra; // Offset 0x10. + unsigned long a[8]; // Offset 0x18. + unsigned long t[7]; // Offset 0x58. + unsigned long s1; // Offset 0x90. + unsigned long sstatus; // Offset 0x98. + unsigned long sepc; // Offset 0xA0. +}; + +enum { + kernel_stack_size = 0x10000 +}; + +struct task_t { + continuation_t cont; + isr_frame_t isr_frames[2]; + isr_frame_t *next_isr; + char kernel_stack[kernel_stack_size]; +}; + +task_t task1; +task_t task2; +task_t *current_task; + +continuation_t prepare_stack(void *s_top, void (*mainfn)(continuation_t)) { + void *sp = (void *)((uintptr_t)s_top - 0x78); + *((uint64_t *)((uintptr_t)sp + 0x70)) = (uint64_t)mainfn; + return (continuation_t){.sp = sp}; +} + +extern "C" void save_stack(void (*f)(void *, continuation_t), void *ctx); +extern "C" void restore_stack(continuation_t c); + +void create_task(task_t *task, void (*mainfn)(continuation_t)) { + task->isr_frames[0].task = task; + task->isr_frames[1].task = task; + task->isr_frames[0].next = &task->isr_frames[1]; + task->isr_frames[1].next = 0; + task->next_isr = &task->isr_frames[0]; + + task->cont = prepare_stack((void *)(task->kernel_stack + kernel_stack_size), mainfn); +} + +void switch_task(void *ctx, continuation_t c) { + task_t *task = (task_t *)ctx; + if(task) { + task->next_isr = (isr_frame_t *)read_csr("sscratch"); + task->cont = c; + } + + task_t *next; + if(task == &task1) { + next = &task2; + }else{ + next = &task1; + } + current_task = next; + + write_csr("sscratch", (unsigned long)next->next_isr); + + restore_stack(next->cont); +} + +extern "C" void isr(); + +extern "C" void handle_isr(isr_frame_t *frame) { + unsigned long cause = read_csr("scause"); + unsigned long code = cause & ~(1UL << 63); + if(cause & (1UL << 63)) { + if(code == 1) { + fmt("it's an IPI\n"); + clear_csr_bits("sip", sie_s_software); + }else if(code == 5) { // Timer interrupt. + //clear_csr_bits("sie", sie_s_timer); + unsigned long time = read_csr("time"); + sbi_call1(0x54494D45, 0, time + 100000); + save_stack(switch_task, current_task); + }else{ + fmt("it's an unhandled interrupt, code: {}\n", code); + } + }else{ + if(code == 8) { // Syscall. + sbi_call1(1, 0, frame->a[0]); + }else{ + unsigned long sepc = read_csr("sepc"); + fmt("it's an exception at {}\n", sepc); + const char *s = exception_strings[cause]; + while(*s) + sbi_call1(1, 0, *(s++)); + while(1) + ; + } + } +} + +extern "C" void isr_frame_overflow() { + fmt("isr frame overflow\n"); + while(1) + ; +} + +pt_t pml4; + +extern "C" void enter_umode(void *entry); + +void syscall(int n, unsigned long arg0) { + register sbi_word rArg0 asm("a0") = arg0; + register sbi_word rN asm("a7") = n; + asm volatile("ecall" : "+r"(rArg0) : "r"(rN) : "memory"); + if(rArg0) + __builtin_trap(); +} + +void task1_umode() { + while(1) + syscall(0, '!'); +} + +void task1_main(continuation_t c) { + fmt("hello from task1\n"); + sti(); + //syscall(0, '!'); + //enter_umode((void *)task1_umode); +} + +void task2_main(continuation_t c) { + fmt("hello from task2\n"); + sti(); + //while(1) + //fmt("."); +} + +isr_frame_t global_isr_frames[2]; + +extern "C" void kmain(unsigned long hart, void *dtb) { + (void)hart; + (void)dtb; + + //fmt("test\n"); + + volatile uint32_t *cfg = (uint32_t *)0x2000060; + volatile uint32_t *dat = (uint32_t *)0x2000070; + + *cfg = 0x10; + *dat = 0xFF; + + fmt("y"); + + cli(); + + global_isr_frames[0].next = &global_isr_frames[1]; + write_csr("sscratch", (unsigned long)&global_isr_frames[0]); + write_csr("stvec", ((unsigned long)&isr)); + + const char *s; + + set_csr_bits("sie", sie_s_software | sie_s_timer); + + // Self-IPI. + sbi_call2(0x735049, 0, 1 << hart, 0); + + sti(); + cli(); + + unsigned long time; + volatile uint32_t *meme = (uint32_t *)0x6011010; + //while(true) { + time = read_csr("time"); + fmt("hello world {}\n", time); + *meme = *meme | (0xA57 << 1) | (1 << 0); + //} + + // Identity map the first 512GiB. + pml4.ptes[0] = ((0UL >> 12) << pte_ppn_shift) | pte_valid + | pte_r | pte_w | pte_x | pte_access | pte_dirty | pte_user; + // Map 512GiB -> 0. + pml4.ptes[1] = ((0UL >> 12) << pte_ppn_shift) | pte_valid + | pte_r | pte_w | pte_x | pte_access | pte_dirty | pte_user; + + fmt("Writing paging memes\n"); + unsigned long pml4p = (unsigned long)&pml4; + fmt("pml4p @ {}\n", pml4p); + fmt("Setting SStatus\n"); + set_csr_bits("sstatus", 1 << 18); + fmt("Setting Satp\n"); + write_csr("satp", (pml4p >> 12) | satp_sv48); + + fmt("Paging might work?...\n"); + + s = "paging works!\n"; + s += 512UL * 1024 * 1024 * 1024; + while(*s) + sbi_call1(1, 0, *(s++)); + + create_task(&task1, task1_main); + create_task(&task2, task2_main); + + time = read_csr("time"); + //sbi_call1(0x54494D45, 0, time + 100000); + + //save_stack(switch_task, 0); + + sbi_call1(8, 0, 0); + + while(1); +} diff --git a/kernel/kernel.ld b/kernel/kernel.ld new file mode 100644 index 0000000..0d26b9b --- /dev/null +++ b/kernel/kernel.ld @@ -0,0 +1,25 @@ +OUTPUT_ARCH("riscv") +OUTPUT_FORMAT("elf64-littleriscv") +ENTRY(_start) +SECTIONS { + /* R-X segment. */ + . = 0x45000000; + + .text : { *(.text) *(.text.*) } + + /* R-- segment. */ + . = ALIGN(0x1000) + (. & (0xFFF)); + /* For some reason, ld does not emit a read-only segment without an additional gap. */ + . += 0x1000; + + .rodata : { *(.rodata) *(.rodata.*) } + .note.gnu.build-id : { *(.note.gnu.build-id) } + + /* RW- segment. */ + . = ALIGN(0x1000) + (. & (0xFFF)); + + .data : { *(.data) *(.data.*) } + .got : { *(.got) } + .got.plt : { *(.got.plt) } + .bss : { *(.bss) *(.bss.*) } +} diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..c996ea6 --- /dev/null +++ b/meson.build @@ -0,0 +1,26 @@ +project('riscv-qtech-os', ['c', 'cpp']) + +kernel = executable('kernel', + 'kernel/boot.S', + 'kernel/kernel.cpp', + link_args: [ + '-Wl,-T,' + (meson.current_source_dir() / 'kernel/kernel.ld'), + '-static' + ] +) + +run_target('qemu', + command: [ + find_program('qemu-system-riscv64'), + '-machine', 'virt', + '-cpu', 'rv64', + '-bios', 'opensbi-riscv64-generic-fw_dynamic.bin', + '-m', '256m', + '-serial', 'stdio', + '-s', + '-display', 'none', + # '-d', 'int', + '-kernel', kernel + ] +) + diff --git a/riscv.cross b/riscv.cross new file mode 100644 index 0000000..e0e430f --- /dev/null +++ b/riscv.cross @@ -0,0 +1,16 @@ +[binaries] +c = 'riscv64-linux-gnu-gcc' +cpp = 'riscv64-linux-gnu-g++' + +[host_machine] +system = 'riscv-qtech-os' +cpu_family = 'riscv' +cpu = 'rv64' +endian = 'little' + +[built-in options] + +c_args = '-march=rv64gc -mabi=lp64d -mcmodel=medany -ffreestanding' +c_link_args = '-march=rv64gc -mabi=lp64d -mcmodel=medany -nostdlib' +cpp_args = '-march=rv64gc -mabi=lp64d -mcmodel=medany -ffreestanding' +cpp_link_args = '-march=rv64gc -mabi=lp64d -mcmodel=medany -nostdlib'