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
This commit is contained in:
Thomas Muller 2022-01-03 19:11:49 -05:00
commit e50fb2de2c
Signed by: thomas
GPG key ID: AF006EB730564952
6 changed files with 734 additions and 0 deletions

117
.gitignore vendored Normal file
View file

@ -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/

201
kernel/boot.S Normal file
View file

@ -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:

349
kernel/kernel.cpp Normal file
View file

@ -0,0 +1,349 @@
#include <stdarg.h>
#include <inttypes.h>
#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);
}

25
kernel/kernel.ld Normal file
View file

@ -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.*) }
}

26
meson.build Normal file
View file

@ -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
]
)

16
riscv.cross Normal file
View file

@ -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'