aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiquel Sabaté Solà <mikisabate@gmail.com>2024-11-22 23:00:46 +0100
committerMiquel Sabaté Solà <mikisabate@gmail.com>2024-11-22 23:00:46 +0100
commit7f6b234625a53d2ceb9ec80dc0172aac1cedb358 (patch)
tree4fe10fcbe2d5e90a0e673259dc895f48a76e06cd
parent224ed7b13ceadd05d27e61d3a0fdd34beb0c7745 (diff)
downloadfbos-7f6b234625a53d2ceb9ec80dc0172aac1cedb358.tar.gz
fbos-7f6b234625a53d2ceb9ec80dc0172aac1cedb358.zip
Enable exception handling from user mode
This allows us to actually catch system calls and start to run user space programs. That being said, the whole thing is still pretty brittle, and the scheduler is not quite there yet. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
-rw-r--r--include/fbos/compiler.h3
-rw-r--r--include/fbos/printk.h3
-rw-r--r--include/fbos/sched.h10
-rw-r--r--kernel/main.c25
-rw-r--r--kernel/printk.c9
-rw-r--r--kernel/trap.c43
-rw-r--r--usr/src/buzz.S1
-rw-r--r--usr/src/fizz.S1
-rw-r--r--usr/src/fizzbuzz.S1
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