aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiquel Sabaté Solà <mikisabate@gmail.com>2024-11-22 15:46:59 +0100
committerMiquel Sabaté Solà <mikisabate@gmail.com>2024-11-22 15:46:59 +0100
commitdcda5731ecbbce70edeaa7662e9ea0fc190a1943 (patch)
tree8f0ec0843f573c49d069112dc62fb324678296c7
parent8231af9d8d759ae89c8214ca2410b0feab9dfb8c (diff)
downloadfbos-dcda5731ecbbce70edeaa7662e9ea0fc190a1943.tar.gz
fbos-dcda5731ecbbce70edeaa7662e9ea0fc190a1943.zip
Enable the timer from the SBI interface
A basic interrupt handler has been implemented which tracks the seconds that have been elapsing along the way. Depending on the value of these seconds, then a task has to be called. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
-rw-r--r--include/fbos/compiler.h2
-rw-r--r--include/fbos/init.h9
-rw-r--r--include/fbos/sbi.h7
-rw-r--r--kernel/head.S4
-rw-r--r--kernel/main.c14
-rw-r--r--kernel/trap.c125
6 files changed, 153 insertions, 8 deletions
diff --git a/include/fbos/compiler.h b/include/fbos/compiler.h
index c26c05c..95596dd 100644
--- a/include/fbos/compiler.h
+++ b/include/fbos/compiler.h
@@ -6,6 +6,8 @@
*/
#define __noreturn __attribute__((__noreturn__))
+#define __s_interrupt __attribute__((interrupt("supervisor")))
+#define __aligned(x) __attribute__((aligned(x)))
/*
* Compiler attributes specific to linker sections.
diff --git a/include/fbos/init.h b/include/fbos/init.h
index 978eebe..711d2cf 100644
--- a/include/fbos/init.h
+++ b/include/fbos/init.h
@@ -3,10 +3,19 @@
#include <fbos/compiler.h>
+// Tracks the amount of seconds that have elapsed since activating timer
+// interrupts.
+//
+// Instantiated in kernel/trap.c, initialized in main.c.
+extern uint64_t seconds_elapsed;
+
// Extract the executables from the initrd that is located at `base_addr` and
// has the given `size`.
void extract_initrd(const unsigned char *const base_addr, uint64_t size);
+// Setup the interrupt vectors and the SBI timer.
+void setup_interrupts(void);
+
// The entry point for the kernel.
void start_kernel(void *dtb);
diff --git a/include/fbos/sbi.h b/include/fbos/sbi.h
index 2575415..b1c99ea 100644
--- a/include/fbos/sbi.h
+++ b/include/fbos/sbi.h
@@ -18,6 +18,7 @@ enum sbi_ret_error {
// SBI extensions supported by this kernel.
enum sbi_ext {
DBCN_EXT = 0x4442434E,
+ TIME_EXT = 0x54494D45,
};
// Function IDs for the Debug Console Extension "DBCN".
@@ -25,6 +26,11 @@ enum dbcn_actions {
DBCN_WRITE = 0x00,
};
+// Function IDs for the Timer Extension "TIME".
+enum time_actions {
+ TIME_SET_TIMER = 0x00,
+};
+
// Return value for any SBI ecall.
struct sbi_ret {
long error;
@@ -37,6 +43,7 @@ extern struct sbi_ret __sbi_ecall(unsigned long arg0, unsigned long arg1, unsign
unsigned long arg3, unsigned long arg4, unsigned long arg5,
int fid, int ext);
+#define sbi_ecall1(ext, fid, arg0) __sbi_ecall(arg0, 0, 0, 0, 0, 0, fid, ext)
#define sbi_ecall2(ext, fid, arg0, arg1) __sbi_ecall(arg0, arg1, 0, 0, 0, 0, fid, ext)
#endif // __FBOS_SBI_H_
diff --git a/kernel/head.S b/kernel/head.S
index 9018a8e..c8f523d 100644
--- a/kernel/head.S
+++ b/kernel/head.S
@@ -53,3 +53,7 @@ _start:
// Start the kernel.
tail start_kernel
+
+ // We really shouldn't reach this point, but just in case, just loop
+ // infinitely here.
+ j .
diff --git a/kernel/main.c b/kernel/main.c
index 089614f..9cdb61e 100644
--- a/kernel/main.c
+++ b/kernel/main.c
@@ -20,19 +20,17 @@ struct task_struct tasks[4] = {
*/
__noreturn __kernel void start_kernel(void *dtb)
{
- // TODO: disable irqs, etc.
-
printk("Welcome to FizzBuzz OS!\n");
+ // Extract information from the DTB blob.
struct initrd_addr addr = find_dt_initrd_addr(dtb);
-
extract_initrd((unsigned char *)addr.start, addr.end - addr.start);
- // TODO: this is the jump address for the first task.
- const char *ddr = (const char *)tasks[1].addr + tasks[1].entry_offset;
- __unused(ddr);
-
- // TODO: reenable stuff
+ // 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();
for (;;)
;
diff --git a/kernel/trap.c b/kernel/trap.c
new file mode 100644
index 0000000..24b459b
--- /dev/null
+++ b/kernel/trap.c
@@ -0,0 +1,125 @@
+#include <fbos/init.h>
+#include <fbos/sbi.h>
+#include <fbos/printk.h>
+
+// TODO: this is QEMU-specific. To obtain this:
+// - Parse the DTB and look for the 'cpus.timebase-frequency' property.
+// - If the system is on ACPI (e.g. VisionFive2), then the frequency has to be
+// picked up from somewhere else (constant on known boards?).
+#define TICKS_PER_SECOND 10000000
+
+// Mask for 'scause' to check whether it came from an interrupt or an exception.
+#define INTERRUPT_MASK 0x8000000000000000
+#define IS_EXCEPTION(x) ((x & INTERRUPT_MASK) == 0)
+
+// Mask for 'scause' to figure out if the interrupt was caused by the timer.
+#define TIMER_SCAUSE_MASK 0x05
+
+// Declared in include/fbos/init.h.
+uint64_t seconds_elapsed;
+
+// Set up a timer through the SBI interface that sends an interrupt in one
+// second from the time this function is called.
+__kernel void time_out_in_one_second(void)
+{
+ struct sbi_ret ret;
+ register uint64_t one_second asm("a0");
+
+ asm volatile("rdtime t0\n\t"
+ "li t1, %1\n\t"
+ "add %0, t0, t1"
+ : "=r"(one_second)
+ : "i"(TICKS_PER_SECOND)
+ : "t0", "t1");
+
+ ret = sbi_ecall1(TIME_EXT, TIME_SET_TIMER, one_second);
+ if (ret.error != SBI_SUCCESS) {
+ die("Could not set timer\n");
+ }
+}
+
+/*
+ * Direct interrupt handler. Handles interrupts such as the timer event and user
+ * mode entries.
+ *
+ * NOTE: as per RISC-V specification, the handler's address as set on the
+ * 'stvec' register *must* be aligned on a 4-byte boundary. Hence, ensuring a
+ * proper alignment is mandatory.
+ *
+ * NOTE: the '__s_interrupt' attribute already handles the saving/restoring of
+ * all registers. It's probably a bit over the top since it also does that for
+ * registers we never care on this kernel (e.g. floating point registers), but
+ * it's convenient.
+ */
+__aligned(4) __s_interrupt __kernel void interrupt_handler(void)
+{
+ uint64_t cause;
+ asm volatile("csrr %0, scause" : "=r"(cause)::);
+
+ if (IS_EXCEPTION(cause)) {
+ die("Don't know how to handle exceptions :D\n");
+ }
+
+ 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.
+ asm volatile("li t0, 32\n\t"
+ "csrc sip, t0\n\t"
+ "csrc sie, t0"
+ :
+ :
+ : "t0");
+
+ // BEHOLD! The fizz buzz logic! :D
+ seconds_elapsed += 1;
+ if ((seconds_elapsed % 15) == 0) {
+ printk("Should run fizzbuzz\n");
+ } else if ((seconds_elapsed % 5) == 0) {
+ printk("Should run buzz\n");
+ } else if ((seconds_elapsed % 3) == 0) {
+ printk("Should run fizz\n");
+ }
+
+ // Re-enable timer interrupts.
+ asm volatile("li t0, 32\n\t"
+ "csrs sie, t0"
+ :
+ :
+ : "t0");
+
+ // Reset the timer one second from now.
+ time_out_in_one_second();
+ } else {
+ printk("WARN: unknown interrupt just came in...\n");
+ }
+}
+
+__kernel void setup_interrupts(void)
+{
+ /*
+ * - stvec: point to our interrupt handler. The two least-significant bits are
+ * going to be '00', meaning we are using direct mode.
+ * - sstatus: set the SIE (S Interrupt Enable) bit. Interrupts are now on!
+ */
+ asm volatile("csrw stvec, %0\n\t"
+ "csrsi sstatus, 2"
+ :
+ : "r"(&interrupt_handler)
+ :);
+
+ /*
+ * - sie: set bit 5 (STIE: S Timer Interrupt Enable).
+ *
+ * NOTE: head.S zeroes out both 'sip' and 'sie' registers. Hence, there are
+ * no pending interrupts.
+ */
+ asm volatile("li t0, 32\n\t"
+ "csrs sie, t0"
+ :
+ :
+ : "t0");
+
+ // And initialize the timer to send an interrupt in one second from now.
+ time_out_in_one_second();
+}