From 6fc08b5dbb8f4cc64751624d9064ffd45793060a Mon Sep 17 00:00:00 2001 From: Julien THILLARD Date: Wed, 11 Feb 2026 15:19:10 +0100 Subject: [PATCH] Too late but it kinda works --- Cargo.toml | 1 + kernel.svg | 115 +++++++++++++++++++++++++++++ src/boot/sbi.rs | 4 +- src/interrupt.rs | 183 ++++++++++++++++++++++++++++++++++------------- src/main.rs | 31 +++++++- src/process.rs | 107 +++++++++++++++++++++++++++ src/scheduler.rs | 173 ++++++++++++++++++++++++++++++++++++++++++++ src/time.rs | 7 +- src/uart.rs | 8 ++- 9 files changed, 570 insertions(+), 59 deletions(-) create mode 100644 kernel.svg create mode 100644 src/process.rs create mode 100644 src/scheduler.rs 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 @@ + + + + + + + + + + + + + + Machine + Supervisor + User + + 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'); + } + }); }