diff --git a/Cargo.toml b/Cargo.toml
index 9f61050..bd4d18f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,6 +8,7 @@ panic = "abort"
[profile.release]
panic = "abort"
+debug = true
[dependencies]
embedded-alloc = "0.7"
diff --git a/kernel.svg b/kernel.svg
new file mode 100644
index 0000000..4978169
--- /dev/null
+++ b/kernel.svg
@@ -0,0 +1,115 @@
+
+
+
+
diff --git a/src/boot/sbi.rs b/src/boot/sbi.rs
index ae81078..2b77a90 100644
--- a/src/boot/sbi.rs
+++ b/src/boot/sbi.rs
@@ -1,11 +1,11 @@
#[non_exhaustive]
#[repr(u64)]
-pub enum EID {
+pub enum EextensionID {
Time = 0x54494D45,
}
#[non_exhaustive]
#[repr(u64)]
-pub enum TimeFID {
+pub enum TimerFunctionID {
SetTimer = 0x0,
}
diff --git a/src/interrupt.rs b/src/interrupt.rs
index 7f1a716..8aae2c1 100644
--- a/src/interrupt.rs
+++ b/src/interrupt.rs
@@ -1,17 +1,19 @@
+use log::info;
+
use crate::{
- boot::sbi::{TimeFID, EID},
- clear_csr, generate_trap_handler, read_csr,
- riscv::disable_interrupt,
- set_csr,
- time::{setup_next_timer_interrupt, IRQ_M_TIMER},
- write_csr,
+ boot::sbi::{EextensionID, TimerFunctionID}, clear_csr, generate_trap_handler, process::ExecutionContext, read_csr, riscv::disable_interrupt, set_csr, time::{IRQ_M_TIMER, setup_next_timer_interrupt}, write_csr
};
use core::arch::naked_asm;
use crate::time::{setup_timer_interrupt, timer_interrupt};
#[unsafe(no_mangle)]
-unsafe extern "C" fn machine_trap_handler(mcause: u64, mie: u64, mip: u64) {
+unsafe extern "C" fn machine_trap_handler(
+ _interrupt_state: *const ExecutionContext,
+ mcause: u64,
+ mie: u64,
+ mip: u64,
+) {
let mepc = read_csr!(mepc);
let mtval = read_csr!(mtval);
if mcause & (1 << 63) == 0 {
@@ -40,8 +42,8 @@ unsafe extern "C" fn machine_trap_handler(mcause: u64, mie: u64, mip: u64) {
#[allow(clippy::single_match)]
match eid {
- c if c == EID::Time as u64 => match fid {
- c if c == TimeFID::SetTimer as u64 => {
+ c if c == EextensionID::Time as u64 => match fid {
+ c if c == TimerFunctionID::SetTimer as u64 => {
clear_csr!(mip, 1 << 5);
setup_next_timer_interrupt();
}
@@ -82,7 +84,12 @@ unsafe extern "C" fn machine_trap_handler(mcause: u64, mie: u64, mip: u64) {
}
#[unsafe(no_mangle)]
-unsafe extern "C" fn supervisor_trap_handler(scause: u64, _sie: u64, _sip: u64) {
+unsafe extern "C" fn supervisor_trap_handler(
+ interrupt_state: *const ExecutionContext,
+ scause: u64,
+ _sie: u64,
+ _sip: u64,
+) {
#[allow(clippy::single_match)]
match scause & !(1 << 63) {
5 => {
@@ -90,11 +97,11 @@ unsafe extern "C" fn supervisor_trap_handler(scause: u64, _sie: u64, _sip: u64)
core::arch::asm!(
"ecall",
in("a0") 0,
- in("a6") TimeFID::SetTimer as u64,
- in("a7") EID::Time as u64,
+ in("a6") TimerFunctionID::SetTimer as u64,
+ in("a7") EextensionID::Time as u64,
);
}
- timer_interrupt();
+ timer_interrupt(unsafe { *interrupt_state });
}
_ => {}
}
@@ -124,52 +131,128 @@ macro_rules! generate_trap_handler {
unsafe extern "C" fn $name() {
naked_asm!(
concat!("
- addi sp, sp, -128
+ addi sp, sp, -264
- sd ra, 120(sp)
+ # Store the current frame
+ sd ra, 0(sp)
+ sd sp, 8(sp)
+ sd gp, 16(sp)
+ sd tp, 24(sp)
+ sd a0, 32(sp)
+ sd a1, 40(sp)
+ sd a2, 48(sp)
+ sd a3, 56(sp)
+ sd a4, 64(sp)
+ sd a5, 72(sp)
+ sd a6, 80(sp)
+ sd a7, 88(sp)
+ sd t0, 96(sp)
+ sd t1, 104(sp)
+ sd t2, 112(sp)
+ sd t3, 120(sp)
+ sd t4, 128(sp)
+ sd t5, 136(sp)
+ sd t6, 144(sp)
+ sd s0, 152(sp)
+ sd s1, 160(sp)
+ sd s2, 168(sp)
+ sd s3, 176(sp)
+ sd s4, 184(sp)
+ sd s5, 192(sp)
+ sd s6, 200(sp)
+ sd s7, 208(sp)
+ sd s8, 216(sp)
+ sd s9, 224(sp)
+ sd s10, 232(sp)
+ sd s11, 240(sp)
+ csrr t0, sepc
+ sd t0, 248(sp)
+ csrr t0, sstatus
+ sd t0, 256(sp)
+
+ // sd ra, 120(sp)
- sd a0, 0(sp)
- sd a1, 8(sp)
- sd a2, 16(sp)
- sd a3, 24(sp)
- sd a4, 32(sp)
- sd a5, 40(sp)
- sd a6, 48(sp)
- sd a7, 56(sp)
+ // sd a0, 0(sp)
+ // sd a1, 8(sp)
+ // sd a2, 16(sp)
+ // sd a3, 24(sp)
+ // sd a4, 32(sp)
+ // sd a5, 40(sp)
+ // sd a6, 48(sp)
+ // sd a7, 56(sp)
- sd t0, 64(sp)
- sd t1, 72(sp)
- sd t2, 80(sp)
- sd t3, 88(sp)
- sd t4, 96(sp)
- sd t5, 104(sp)
- sd t6, 112(sp)
+ // sd t0, 64(sp)
+ // sd t1, 72(sp)
+ // sd t2, 80(sp)
+ // sd t3, 88(sp)
+ // sd t4, 96(sp)
+ // sd t5, 104(sp)
+ // sd t6, 112(sp)
- csrr a0, ", stringify!($mode),"cause
- csrr a1, ", stringify!($mode),"ie
- csrr a2, ", stringify!($mode),"ip
+ mv a0, sp
+ csrr a1, ", stringify!($mode),"cause
+ csrr a2, ", stringify!($mode),"ie
+ csrr a3, ", stringify!($mode),"ip
jal ", stringify!($jump_to), "
- ld a0, 0(sp)
- ld a1, 8(sp)
- ld a2, 16(sp)
- ld a3, 24(sp)
- ld a4, 32(sp)
- ld a5, 40(sp)
- ld a6, 48(sp)
- ld a7, 56(sp)
+ # Restore registers
+ ld t0, 248(sp)
+ csrw sepc, t0
+ ld t0, 256(sp)
+ csrw sstatus, t0
+ ld ra, 0(sp)
+ ld gp, 16(sp)
+ ld tp, 24(sp)
+ ld a0, 32(sp)
+ ld a1, 40(sp)
+ ld a2, 48(sp)
+ ld a3, 56(sp)
+ ld a4, 64(sp)
+ ld a5, 72(sp)
+ ld a6, 80(sp)
+ ld a7, 88(sp)
+ ld t0, 96(sp)
+ ld t1, 104(sp)
+ ld t2, 112(sp)
+ ld t3, 120(sp)
+ ld t4, 128(sp)
+ ld t5, 136(sp)
+ ld t6, 144(sp)
+ ld s0, 152(sp)
+ ld s1, 160(sp)
+ ld s2, 168(sp)
+ ld s3, 176(sp)
+ ld s4, 184(sp)
+ ld s5, 192(sp)
+ ld s6, 200(sp)
+ ld s7, 208(sp)
+ ld s8, 216(sp)
+ ld s9, 224(sp)
+ ld s10, 232(sp)
+ ld s11, 240(sp)
- ld t0, 64(sp)
- ld t1, 72(sp)
- ld t2, 80(sp)
- ld t3, 88(sp)
- ld t4, 96(sp)
- ld t5, 104(sp)
- ld t6, 112(sp)
+ ld sp, 8(sp)
- ld ra, 120(sp)
+ // ld a0, 0(sp)
+ // ld a1, 8(sp)
+ // ld a2, 16(sp)
+ // ld a3, 24(sp)
+ // ld a4, 32(sp)
+ // ld a5, 40(sp)
+ // ld a6, 48(sp)
+ // ld a7, 56(sp)
+
+ // ld t0, 64(sp)
+ // ld t1, 72(sp)
+ // ld t2, 80(sp)
+ // ld t3, 88(sp)
+ // ld t4, 96(sp)
+ // ld t5, 104(sp)
+ // ld t6, 112(sp)
+
+ // ld ra, 120(sp)
- addi sp, sp, 128
+ addi sp, sp, 264
", stringify!($mode),"ret")
)
diff --git a/src/main.rs b/src/main.rs
index 7d94f56..1a0d92b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,16 +1,18 @@
#![no_std]
#![no_main]
#![allow(static_mut_refs)]
-#![feature(riscv_ext_intrinsics)]
+#![feature(riscv_ext_intrinsics, const_trait_impl, iter_map_windows)]
-use core::arch::riscv64::wfi;
+use core::{arch::riscv64::wfi, time::Duration};
use embedded_alloc::LlffHeap as Heap;
use log::info;
use crate::{
io::init_log,
+ process::{create_processus, sleep},
riscv::enable_supervisor_interrupt,
+ scheduler::scheduler_init,
vga::{Color, Vga},
};
@@ -21,27 +23,50 @@ mod critical_section;
mod interrupt;
mod io;
mod panic_handler;
+mod process;
mod riscv;
+mod scheduler;
mod time;
mod uart;
mod vga;
-pub const HEAP_SIZE: usize = 40960;
+pub const HEAP_SIZE: usize = 4096;
#[global_allocator]
static HEAP: Heap = Heap::empty();
+extern "C" fn test() {
+ loop {
+ info!("test");
+ enable_supervisor_interrupt();
+ sleep(Duration::new(2, 0));
+ unsafe { wfi() };
+ }
+}
+
+extern "C" fn proc2() {
+ loop {
+ info!("proc2");
+ enable_supervisor_interrupt();
+ sleep(Duration::new(3, 0));
+ unsafe { wfi() };
+ }
+}
+
#[unsafe(no_mangle)]
pub extern "C" fn supervisor_mode_entry() {
unsafe {
embedded_alloc::init!(HEAP, HEAP_SIZE);
init_log().unwrap();
Vga::init();
+ scheduler_init();
enable_supervisor_interrupt();
}
info!("Hello World !");
unsafe { Vga::draw_string(10, 10, "Hello World !", Color::WHITE, Color::BLACK) };
+ create_processus(test, "proc1");
+ create_processus(proc2, "proc2");
loop {
unsafe { wfi() }
}
diff --git a/src/process.rs b/src/process.rs
new file mode 100644
index 0000000..9c26afc
--- /dev/null
+++ b/src/process.rs
@@ -0,0 +1,107 @@
+use core::{arch::riscv64::wfi, time::Duration};
+
+use alloc::{format, string::String};
+
+use crate::{
+ scheduler::{scheduler, ACTIVE_PID, PROCESSUS_COUNT, PROCESS_TABLE},
+ time::elapsed_time_since_startup,
+};
+
+const STACK_SIZE: usize = 4096;
+
+#[derive(Debug, PartialEq, Eq)]
+pub enum ProcessState {
+ Active,
+ Activable,
+ Dead,
+ Asleep,
+}
+
+#[repr(C)]
+#[derive(Debug, Clone, Copy)]
+pub struct ExecutionContext {
+ pub ra: *const usize,
+ pub sp: *const usize,
+ pub gp: usize,
+ pub tp: usize,
+ pub a: [usize; 8],
+ pub t: [usize; 7],
+ pub s: [usize; 11],
+ pub mepc: usize,
+ pub mstatus: usize,
+}
+
+pub struct Process {
+ pub pid: i64,
+ pub name: String,
+ pub state: ProcessState,
+ pub wake_time: Duration,
+ pub ctx: ExecutionContext,
+ pub entry_point: Option,
+ pub stack: [usize; STACK_SIZE],
+}
+
+impl core::fmt::Debug for Process {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ f.debug_struct("Process")
+ .field("pid", &self.pid)
+ .field("name", &self.name)
+ .field("state", &self.state)
+ .field("wake_time", &self.wake_time)
+ .field("ctx", &self.ctx)
+ .field("entry_point", &self.entry_point)
+ .field("stack", &format!("[_; {}]", STACK_SIZE))
+ .finish()
+ }
+}
+
+pub fn create_processus>(code: extern "C" fn(), name: T) -> i64 {
+ let mut next_pid = 0;
+ while next_pid < PROCESSUS_COUNT
+ && unsafe { PROCESS_TABLE[next_pid].state != ProcessState::Dead }
+ {
+ next_pid += 1;
+ }
+
+ if next_pid >= PROCESSUS_COUNT {
+ return -1;
+ }
+
+ unsafe {
+ PROCESS_TABLE[next_pid].pid = next_pid as i64;
+ PROCESS_TABLE[next_pid].name = name.into();
+ PROCESS_TABLE[next_pid].state = ProcessState::Activable;
+ PROCESS_TABLE[next_pid].entry_point = Some(code);
+ PROCESS_TABLE[next_pid].ctx.a[0] = code as usize;
+ PROCESS_TABLE[next_pid].ctx.ra = processus_launcher as *const _;
+ PROCESS_TABLE[next_pid].ctx.sp = &raw const PROCESS_TABLE[next_pid].stack[STACK_SIZE - 1];
+ }
+
+ next_pid as i64
+}
+
+extern "C" fn processus_launcher(code: extern "C" fn()) {
+ code();
+ terminate_processus();
+}
+
+fn terminate_processus() {
+ unsafe {
+ PROCESS_TABLE[ACTIVE_PID].state = ProcessState::Dead;
+ }
+ unsafe {
+ wfi();
+ }
+ // scheduler();
+}
+
+pub fn sleep(duration: Duration) {
+ unsafe {
+ PROCESS_TABLE[ACTIVE_PID].wake_time = elapsed_time_since_startup() + duration;
+ PROCESS_TABLE[ACTIVE_PID].state = ProcessState::Asleep;
+ }
+ unsafe {
+ wfi();
+ }
+ // scheduler();
+}
diff --git a/src/scheduler.rs b/src/scheduler.rs
new file mode 100644
index 0000000..2d34947
--- /dev/null
+++ b/src/scheduler.rs
@@ -0,0 +1,173 @@
+use core::{
+ arch::{naked_asm, riscv64::wfi},
+ array,
+ cell::LazyCell,
+ time::Duration,
+};
+
+use alloc::string::String;
+use log::info;
+
+use crate::{
+ process::{create_processus, ExecutionContext, Process, ProcessState},
+ riscv::enable_supervisor_interrupt,
+ time,
+};
+
+pub const PROCESSUS_COUNT: usize = 16;
+
+pub static mut ACTIVE_PID: usize = 0;
+pub static mut PROCESS_TABLE: LazyCell<[Process; PROCESSUS_COUNT]> = LazyCell::new(|| {
+ array::from_fn(|_| Process {
+ pid: -1,
+ name: String::new(),
+ state: ProcessState::Dead,
+ wake_time: Duration::new(0, 0),
+ ctx: ExecutionContext {
+ ra: core::ptr::null(),
+ sp: core::ptr::null(),
+ gp: 0,
+ tp: 0,
+ a: [0; _],
+ t: [0; _],
+ s: [0; _],
+ mepc: 0,
+ mstatus: 0,
+ },
+ entry_point: None,
+ stack: [0; _],
+ })
+});
+
+pub extern "C" fn idle() {
+ loop {
+ enable_supervisor_interrupt();
+ unsafe {
+ wfi();
+ }
+ }
+}
+
+pub fn scheduler_init() {
+ info!("scheduler init");
+ for pid in 0..PROCESSUS_COUNT {
+ unsafe {
+ PROCESS_TABLE[pid].state = ProcessState::Dead;
+ }
+ }
+
+ create_processus(idle, "idle");
+ unsafe {
+ PROCESS_TABLE[0].state = ProcessState::Active;
+ }
+}
+
+pub fn scheduler(interrupt_state: ExecutionContext) -> usize {
+ // info!("scheduler");
+ unsafe {
+ let prev_pid = ACTIVE_PID;
+
+ if PROCESS_TABLE[ACTIVE_PID].state == ProcessState::Active {
+ PROCESS_TABLE[ACTIVE_PID].state = ProcessState::Activable;
+ }
+
+ loop {
+ if PROCESS_TABLE[ACTIVE_PID].state == ProcessState::Asleep
+ && time::elapsed_time_since_startup() > PROCESS_TABLE[ACTIVE_PID].wake_time
+ {
+ PROCESS_TABLE[ACTIVE_PID].state = ProcessState::Activable;
+ }
+ ACTIVE_PID = (ACTIVE_PID + 1) % PROCESSUS_COUNT;
+ if PROCESS_TABLE[ACTIVE_PID].state == ProcessState::Activable {
+ break;
+ }
+ }
+ PROCESS_TABLE[ACTIVE_PID].state = ProcessState::Active;
+
+ PROCESS_TABLE[prev_pid].ctx = interrupt_state;
+ // PROCESS_TABLE[prev_pid].ctx.t = interrupt_state.t;
+ // PROCESS_TABLE[prev_pid].ctx.ra = interrupt_state.ra;
+ context_switch(
+ PROCESS_TABLE[ACTIVE_PID].entry_point.unwrap(),
+ &raw mut PROCESS_TABLE[prev_pid].ctx,
+ &raw mut PROCESS_TABLE[ACTIVE_PID].ctx,
+ );
+ prev_pid
+ }
+}
+
+#[unsafe(naked)]
+pub extern "C" fn context_switch(
+ code: extern "C" fn(),
+ current: *mut ExecutionContext,
+ next: *mut ExecutionContext,
+) {
+ naked_asm!(
+ "
+ // sd ra, 0(a1)
+ // sd sp, 8(a1)
+ // sd gp, 16(a1)
+ // sd tp, 24(a1)
+ // sd s0, 152(a1)
+ // sd s1, 160(a1)
+ // sd s2, 168(a1)
+ // sd s3, 176(a1)
+ // sd s4, 184(a1)
+ // sd s5, 192(a1)
+ // sd s6, 200(a1)
+ // sd s7, 208(a1)
+ // sd s8, 216(a1)
+ // sd s9, 224(a1)
+ // sd s10, 232(a1)
+ // sd s11, 240(a1)
+ // csrr t0, sepc
+ // sd t0, 248(a1)
+ // csrr t0, sstatus
+ // sd t0, 256(a1)
+
+ # Load next execution context
+ ld t0, 248(a2)
+ csrw sepc, t0
+ ld t0, 256(a2)
+ csrw sstatus, t0
+
+ ld ra, 0(a2)
+ ld sp, 8(a2)
+ ld gp, 16(a2)
+ ld tp, 24(a2)
+ ld a0, 32(a2)
+ ld a1, 40(a2)
+ // Skip a2 since it used as a pointer
+ ld a3, 56(a2)
+ ld a4, 64(a2)
+ ld a5, 72(a2)
+ ld a6, 80(a2)
+ ld a7, 88(a2)
+ ld t0, 96(a2)
+ ld t1, 104(a2)
+ ld t2, 112(a2)
+ ld t3, 120(a2)
+ ld t4, 128(a2)
+ ld t5, 136(a2)
+ ld t6, 144(a2)
+ ld s0, 152(a2)
+ ld s1, 160(a2)
+ ld s2, 168(a2)
+ ld s3, 176(a2)
+ ld s4, 184(a2)
+ ld s5, 192(a2)
+ ld s6, 200(a2)
+ ld s7, 208(a2)
+ ld s8, 216(a2)
+ ld s9, 224(a2)
+ ld s10, 232(a2)
+ ld s11, 240(a2)
+
+ // Restore a2 at the end
+ ld a2, 48(a2)
+
+ addi sp, sp, 264
+
+ sret"
+ );
+}
diff --git a/src/time.rs b/src/time.rs
index d9125d7..0d6062c 100644
--- a/src/time.rs
+++ b/src/time.rs
@@ -1,10 +1,10 @@
use core::time::Duration;
use alloc::format;
+use log::info;
use crate::{
- set_csr,
- vga::{Color, Vga, FONT_WIDTH, WIDTH},
+ process::ExecutionContext, scheduler::scheduler, set_csr, vga::{Color, FONT_WIDTH, Vga, WIDTH}
};
pub const IRQ_M_TIMER: u8 = 1 << 7;
@@ -30,7 +30,7 @@ pub fn setup_next_timer_interrupt() {
);
}
}
-pub fn timer_interrupt() {
+pub fn timer_interrupt(interrupt_state: ExecutionContext) -> usize {
let current_time = elapsed_time_since_startup();
let seconds = current_time.as_secs();
let minutes = seconds / 60 % 60;
@@ -46,6 +46,7 @@ pub fn timer_interrupt() {
Color::BLACK,
)
};
+ scheduler(interrupt_state)
}
pub fn elapsed_time_since_startup() -> Duration {
diff --git a/src/uart.rs b/src/uart.rs
index 40030f1..8312c7a 100644
--- a/src/uart.rs
+++ b/src/uart.rs
@@ -5,5 +5,11 @@ pub fn write_char_uart(c: char) {
}
pub fn write_uart>(print: T) {
- print.as_ref().chars().for_each(write_char_uart);
+ print.as_ref().chars().for_each(|a| {
+ // Add \r if needed
+ write_char_uart(a);
+ if a == '\n' {
+ write_char_uart('\r');
+ }
+ });
}