diff options
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/main.c | 25 | ||||
| -rw-r--r-- | kernel/printk.c | 9 | ||||
| -rw-r--r-- | kernel/trap.c | 43 |
3 files changed, 66 insertions, 11 deletions
diff --git a/kernel/main.c b/kernel/main.c index 968c9d5..4ed99b5 100644 --- a/kernel/main.c +++ b/kernel/main.c @@ -13,6 +13,19 @@ struct task_struct tasks[4] = { [TASK_FIZZBUZZ] = { .stack = init_stack[3], .addr = nullptr, .entry_offset = 0, }, }; +int next_task; + +// TODO: this feels really brittle +__kernel void switch_to(int task_id) +{ + const char *ddr = (const char *)tasks[task_id].addr + tasks[task_id].entry_offset; + + asm volatile("csrc sstatus, %[mask]" : : [mask] "r"(1 << 8)); + asm volatile("mv ra, %0" : : "r"(ddr)); + asm volatile("csrw sepc, ra"); + asm volatile("sret"); +} + /* * This is the main entry point of the kernel after head.S is done. This * function can (and will) assume that everything has been reset and that we can @@ -26,15 +39,23 @@ __noreturn __kernel void start_kernel(void *dtb) struct initrd_addr addr = find_dt_initrd_addr(dtb); extract_initrd((unsigned char *)addr.start, addr.end - addr.start); + next_task = TASK_UNKNOWN; + // At this point everything has already been handled: setup the interrupt // vector and enable the timer to start ticking and scheduling the three // tasks at hand. seconds_elapsed = 0; setup_interrupts(); + idle(); +} + +__noreturn __kernel void idle(void) +{ for (;;) { - // Put the machine on low power consumption since we are not doing - // anything fancy here. + if (next_task != TASK_UNKNOWN && next_task != TASK_INIT) { + switch_to(next_task); + } asm volatile("wfi"); } } diff --git a/kernel/printk.c b/kernel/printk.c index 51c1a7a..b3c925a 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -12,14 +12,19 @@ void __noreturn __kernel die(const char *const message) ; } -void __kernel printk(const char *const message) +__kernel void printk(const char *const message) { size_t len = strlen(message); if (!len) { return; } - struct sbi_ret ret = sbi_ecall2(DBCN_EXT, DBCN_WRITE, len, (unsigned long)message); + write(message, len); +} + +__kernel void write(const char *const message, size_t n) +{ + struct sbi_ret ret = sbi_ecall2(DBCN_EXT, DBCN_WRITE, n, (unsigned long)message); if (ret.error != SBI_SUCCESS) { die(nullptr); } diff --git a/kernel/trap.c b/kernel/trap.c index 24b459b..84f8398 100644 --- a/kernel/trap.c +++ b/kernel/trap.c @@ -1,6 +1,7 @@ #include <fbos/init.h> #include <fbos/sbi.h> #include <fbos/printk.h> +#include <fbos/sched.h> // TODO: this is QEMU-specific. To obtain this: // - Parse the DTB and look for the 'cpus.timebase-frequency' property. @@ -15,6 +16,13 @@ // Mask for 'scause' to figure out if the interrupt was caused by the timer. #define TIMER_SCAUSE_MASK 0x05 +// Mask for 'scause' to figure out if the exception was cause by U privilege +// mode making an 'ecall'. +#define USER_ECALL_MASK 0x08 + +// Identifier for the 'write' system call. +#define NR_WRITE 0x01 + // Declared in include/fbos/init.h. uint64_t seconds_elapsed; @@ -38,6 +46,27 @@ __kernel void time_out_in_one_second(void) } } +__kernel __always_inline void exception_handler(uint64_t cause) +{ + register char *message asm("a0"); + register size_t n asm("a1"); + register uint64_t syscall_id asm("a7"); + + if ((cause & USER_ECALL_MASK) != USER_ECALL_MASK) { + die("Don't know how to handle this exception :D\n"); + } + if (syscall_id != NR_WRITE) { + die("Bad syscall\n"); + } + + // TODO: acknowledge 'sip' bit? + next_task = TASK_UNKNOWN; + write(message, n); + + // TODO: is it safe to jump to idle here? Any preparation to be done? + idle(); +} + /* * Direct interrupt handler. Handles interrupts such as the timer event and user * mode entries. @@ -57,13 +86,11 @@ __aligned(4) __s_interrupt __kernel void interrupt_handler(void) asm volatile("csrr %0, scause" : "=r"(cause)::); if (IS_EXCEPTION(cause)) { - die("Don't know how to handle exceptions :D\n"); + exception_handler(cause); } if ((cause & TIMER_SCAUSE_MASK) == TIMER_SCAUSE_MASK) { - // Clear timer interrupt pending bit from the 'sip' register. Also clear - // the timer interrupt enable so it's re-enabled after running the - // fizz/buzz logic. + // Clear timer interrupt pending bit from the 'sip' register. asm volatile("li t0, 32\n\t" "csrc sip, t0\n\t" "csrc sie, t0" @@ -74,11 +101,13 @@ __aligned(4) __s_interrupt __kernel void interrupt_handler(void) // BEHOLD! The fizz buzz logic! :D seconds_elapsed += 1; if ((seconds_elapsed % 15) == 0) { - printk("Should run fizzbuzz\n"); + next_task = TASK_FIZZBUZZ; } else if ((seconds_elapsed % 5) == 0) { - printk("Should run buzz\n"); + next_task = TASK_BUZZ; } else if ((seconds_elapsed % 3) == 0) { - printk("Should run fizz\n"); + next_task = TASK_FIZZ; + } else { + next_task = TASK_UNKNOWN; } // Re-enable timer interrupts. |
