aboutsummaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/main.c25
-rw-r--r--kernel/printk.c9
-rw-r--r--kernel/trap.c43
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.