diff options
| -rw-r--r-- | include/fbos/compiler.h | 3 | ||||
| -rw-r--r-- | include/fbos/printk.h | 3 | ||||
| -rw-r--r-- | include/fbos/sched.h | 10 | ||||
| -rw-r--r-- | kernel/main.c | 25 | ||||
| -rw-r--r-- | kernel/printk.c | 9 | ||||
| -rw-r--r-- | kernel/trap.c | 43 | ||||
| -rw-r--r-- | usr/src/buzz.S | 1 | ||||
| -rw-r--r-- | usr/src/fizz.S | 1 | ||||
| -rw-r--r-- | usr/src/fizzbuzz.S | 1 |
9 files changed, 85 insertions, 11 deletions
diff --git a/include/fbos/compiler.h b/include/fbos/compiler.h index 95596dd..67f3194 100644 --- a/include/fbos/compiler.h +++ b/include/fbos/compiler.h @@ -6,6 +6,9 @@ */ #define __noreturn __attribute__((__noreturn__)) +#ifndef __always_inline +#define __always_inline __attribute__((__always_inline__)) inline +#endif #define __s_interrupt __attribute__((interrupt("supervisor"))) #define __aligned(x) __attribute__((aligned(x))) diff --git a/include/fbos/printk.h b/include/fbos/printk.h index 6b32555..5a12d7c 100644 --- a/include/fbos/printk.h +++ b/include/fbos/printk.h @@ -1,6 +1,8 @@ #ifndef __FBOS_PRINTK_H_ #define __FBOS_PRINTK_H_ +#include <fbos/compiler.h> + /* * This file might be pulled from user space tests. Hence, define alternatives * for this functions from glibc. @@ -9,6 +11,7 @@ #ifdef __KERNEL__ extern void die(const char *const message); extern void printk(const char *const message); +extern void write(const char *const message, size_t n); #else #include <stdio.h> #include <stdlib.h> diff --git a/include/fbos/sched.h b/include/fbos/sched.h index 6a59756..c2a4b19 100644 --- a/include/fbos/sched.h +++ b/include/fbos/sched.h @@ -3,6 +3,7 @@ #include <fbos/compiler.h> +// All the possible IDs for the tasks on this kernel. enum task_id { TASK_UNKNOWN = -1, TASK_INIT = 0, @@ -11,12 +12,21 @@ enum task_id { TASK_FIZZBUZZ = 3, }; +// TODO: if we only care about the absolute address, it can be further +// simplified. struct task_struct { void *stack; const void *addr; uint64_t entry_offset; }; +// Tasks available on this kernel. extern struct task_struct tasks[4]; +// Identifier for the next task to be run. +extern int next_task; + +// Bring the machine to idle mode. +__noreturn __kernel void idle(void); + #endif // __FBOS_SCHED_H_ 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. diff --git a/usr/src/buzz.S b/usr/src/buzz.S index d5f83c5..d4d9a4b 100644 --- a/usr/src/buzz.S +++ b/usr/src/buzz.S @@ -4,6 +4,7 @@ _start: li a7, 1 la a0, buzz + li a1, 5 ecall .Loop: j .Loop diff --git a/usr/src/fizz.S b/usr/src/fizz.S index a6e8de2..ffabe82 100644 --- a/usr/src/fizz.S +++ b/usr/src/fizz.S @@ -4,6 +4,7 @@ _start: li a7, 1 la a0, fizz + li a1, 5 ecall .Loop: j .Loop diff --git a/usr/src/fizzbuzz.S b/usr/src/fizzbuzz.S index 6974fbd..e78b424 100644 --- a/usr/src/fizzbuzz.S +++ b/usr/src/fizzbuzz.S @@ -4,6 +4,7 @@ _start: li a7, 1 la a0, fizzbuzz + li a1, 9 ecall .Loop: j .Loop |
