Better user programs with a special std. Sleep and exit are calling scheduler instead of wfi.

This commit is contained in:
2026-02-21 18:29:27 +01:00
parent 235f17e7cf
commit 8a8034bd11
25 changed files with 263 additions and 210 deletions

View File

@@ -10,7 +10,7 @@ pub mod sbi;
#[unsafe(naked)]
#[unsafe(no_mangle)]
pub extern "C" fn entry() {
pub extern "C" fn _start() {
naked_asm!(
"
la sp, _heap_end

View File

@@ -1,7 +1,33 @@
use bffs::io::{IoBase, Read, Seek};
use core::{cell::UnsafeCell, ops::Deref};
use bffs::{
io::{IoBase, Read, Seek},
Fat32FileSystem,
};
const DISK_ADDR: *const u8 = 0x9000_0000 as *const _;
pub struct FSTemp(UnsafeCell<Option<Fat32FileSystem<Disk>>>);
unsafe impl Sync for FSTemp {}
impl FSTemp {
pub unsafe fn init(&self) {
unsafe {
*self.0.get() = Some(Fat32FileSystem::new(Disk::new(1024 * 1024 * 16)).unwrap());
}
}
}
impl Deref for FSTemp {
type Target = Fat32FileSystem<Disk>;
fn deref(&self) -> &Self::Target {
unsafe { (&*self.0.get()).as_ref().unwrap_unchecked() }
}
}
pub static FILE_SYSTEM: FSTemp = FSTemp(UnsafeCell::new(None));
#[derive(Debug)]
pub struct Disk {
pos: u64,

View File

@@ -1,15 +1,15 @@
use alloc::str;
use log::info;
use shared::syscall::SysCall;
use crate::{
boot::sbi::{EextensionID, TimerFunctionID},
clear_csr, generate_trap_handler,
process::{sleep, ExecutionContext},
clear_csr,
process::{exit_process, sleep, ExecutionContext},
read_csr,
riscv::disable_interrupt,
scheduler::scheduler,
scheduler::scheduler_without_ret,
set_csr,
syscall::SysCall,
time::{setup_next_timer_interrupt, IRQ_M_TIMER},
write_csr,
};
@@ -87,7 +87,7 @@ unsafe extern "C" fn machine_trap_handler(
#[unsafe(no_mangle)]
unsafe extern "C" fn supervisor_trap_handler(
interrupt_state: *mut ExecutionContext,
mut interrupt_state: *mut ExecutionContext,
scause: u64,
_sie: u64,
_sip: u64,
@@ -96,13 +96,19 @@ unsafe extern "C" fn supervisor_trap_handler(
#[allow(clippy::single_match)]
match scause & !(1 << 63) {
8 => {
// Advance sepc to exit the ecall
unsafe {
(*interrupt_state).mepc = (*interrupt_state).mepc.byte_add(4);
}
// Environment call from S-mode
let syscall_u64: u64 = unsafe { (*interrupt_state).a[0] };
let a1: u64 = unsafe { (*interrupt_state).a[1] };
let a2: u64 = unsafe { (*interrupt_state).a[2] };
let syscall: SysCall = syscall_u64.into();
match syscall {
SysCall::NanoSleep => sleep(Duration::new(a1, a2 as u32)),
SysCall::Exit => exit_process(&mut interrupt_state),
SysCall::NanoSleep => sleep(Duration::new(a1, a2 as u32), &mut interrupt_state),
SysCall::WriteTemp => {
info!("Print from user space : {}", unsafe {
str::from_raw_parts(a1 as *const u8, a2 as usize)
@@ -115,11 +121,6 @@ unsafe extern "C" fn supervisor_trap_handler(
unimplemented!("Syscall {syscall_u64} is not implemented")
}
}
// Advance sepc to exit the ecall
unsafe {
(*interrupt_state).mepc = (*interrupt_state).mepc.byte_add(4);
}
}
_ => {}
}
@@ -137,7 +138,7 @@ unsafe extern "C" fn supervisor_trap_handler(
);
}
timer_interrupt();
return scheduler(unsafe { *interrupt_state });
scheduler_without_ret(&mut interrupt_state);
}
_ => {}
}
@@ -207,116 +208,107 @@ unsafe extern "C" fn _machine_mode_trap() {
mret"
)
}
generate_trap_handler! {
_supervisor_mode_trap, supervisor_trap_handler, s
#[unsafe(naked)]
#[unsafe(no_mangle)]
unsafe extern "C" fn _supervisor_mode_trap() {
naked_asm!(concat!(
"
// Store sp before it gets modified
sd sp, 8-264(sp)
addi sp, sp, -264
# Store the current frame
sd ra, 0(sp)
// 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)
mv a0, sp
csrr a1, scause
csrr a2, sie
csrr a3, sip
jal supervisor_trap_handler
# Restore registers and sret
jal restore_context"
))
}
#[macro_export]
macro_rules! generate_trap_handler {
($name:ident, $jump_to:ident, $mode:ident) => {
#[unsafe(naked)]
#[unsafe(no_mangle)]
unsafe extern "C" fn $name() {
naked_asm!(concat!(
"
mv t0, sp
addi sp, sp, -264
#[unsafe(naked)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn restore_context(context: *const ExecutionContext) -> ! {
naked_asm!(concat!(
"
ld t0, 248(a0)
csrw sepc, t0
ld t0, 256(a0)
csrw sstatus, t0
ld ra, 0(a0)
ld sp, 8(a0)
ld gp, 16(a0)
ld tp, 24(a0)
ld a1, 40(a0)
ld a2, 48(a0)
ld a3, 56(a0)
ld a4, 64(a0)
ld a5, 72(a0)
ld a6, 80(a0)
ld a7, 88(a0)
ld t0, 96(a0)
ld t1, 104(a0)
ld t2, 112(a0)
ld t3, 120(a0)
ld t4, 128(a0)
ld t5, 136(a0)
ld t6, 144(a0)
ld s0, 152(a0)
ld s1, 160(a0)
ld s2, 168(a0)
ld s3, 176(a0)
ld s4, 184(a0)
ld s5, 192(a0)
ld s6, 200(a0)
ld s7, 208(a0)
ld s8, 216(a0)
ld s9, 224(a0)
ld s10, 232(a0)
ld s11, 240(a0)
# Store the current frame
sd ra, 0(sp)
sd t0, 8(sp) // 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)
ld a0, 32(a0)
mv a0, sp
csrr a1, ",
stringify!($mode),
"cause
csrr a2, ",
stringify!($mode),
"ie
csrr a3, ",
stringify!($mode),
"ip
jal ",
stringify!($jump_to),
"
# Restore registers
ld t0, 248(a0)
csrw sepc, t0
ld t0, 256(a0)
csrw sstatus, t0
ld ra, 0(a0)
ld sp, 8(a0)
ld gp, 16(a0)
ld tp, 24(a0)
ld a1, 40(a0)
ld a2, 48(a0)
ld a3, 56(a0)
ld a4, 64(a0)
ld a5, 72(a0)
ld a6, 80(a0)
ld a7, 88(a0)
ld t0, 96(a0)
ld t1, 104(a0)
ld t2, 112(a0)
ld t3, 120(a0)
ld t4, 128(a0)
ld t5, 136(a0)
ld t6, 144(a0)
ld s0, 152(a0)
ld s1, 160(a0)
ld s2, 168(a0)
ld s3, 176(a0)
ld s4, 184(a0)
ld s5, 192(a0)
ld s6, 200(a0)
ld s7, 208(a0)
ld s8, 216(a0)
ld s9, 224(a0)
ld s10, 232(a0)
ld s11, 240(a0)
ld a0, 32(a0)
",
stringify!($mode),
"ret"
))
}
};
sret"
))
}
#[repr(C)]

View File

@@ -7,7 +7,7 @@ use log::{LevelFilter, SetLoggerError};
use crate::uart::write_uart;
fn print(content: String) {
pub(crate) fn print(content: String) {
write_uart(content);
}

View File

@@ -10,18 +10,15 @@
macro_metavar_expr_concat
)]
use alloc::{boxed::Box, vec::Vec};
use bffs::{io::Read, Fat32FileSystem};
use embedded_alloc::LlffHeap as Heap;
use log::info;
use crate::{
fs::Disk,
fs::FILE_SYSTEM,
io::init_log,
process::create_process,
process::{create_process, create_process_from_file},
riscv::enable_supervisor_interrupt,
scheduler::{idle, scheduler_init},
tests_fat::MemoryDisk,
user::{proc2, test},
vga::{Color, Vga},
};
@@ -36,14 +33,12 @@ mod panic_handler;
mod process;
mod riscv;
mod scheduler;
mod syscall;
mod tests_fat;
mod time;
mod uart;
mod user;
mod vga;
pub const HEAP_SIZE: usize = 40960;
pub const HEAP_SIZE: usize = 1024 * 1024; // 1Mo RAM
#[global_allocator]
static HEAP: Heap = Heap::empty();
@@ -56,6 +51,7 @@ pub extern "C" fn supervisor_mode_entry() {
embedded_alloc::init!(HEAP, HEAP_SIZE);
init_log().unwrap();
Vga::init();
FILE_SYSTEM.init();
scheduler_init();
}
@@ -65,16 +61,7 @@ pub extern "C" fn supervisor_mode_entry() {
create_process(&test, "proc1");
create_process(&proc2, "proc2");
let fs = Fat32FileSystem::new(Disk::new(1024 * 1024 * 16)).unwrap();
let mut bin = fs.open_file("/usr/bin/test").unwrap();
let mut content: Vec<u8> = Vec::new();
bin.read_to_end(&mut content).unwrap();
let test = unsafe { core::mem::transmute::<*const u8, extern "C" fn()>(content.as_ptr()) };
let test = Box::leak(Box::new(move || {
test();
}));
create_process(test, "dyn_proc");
create_process_from_file("/usr/bin/test_pic");
enable_supervisor_interrupt();
idle();

View File

@@ -1,9 +1,12 @@
use core::{arch::riscv64::wfi, time::Duration};
use core::time::Duration;
use alloc::{format, string::String};
use alloc::{boxed::Box, format, string::String, vec::Vec};
use bffs::{io::Read, path::Path};
use shared::syscall::exit;
use crate::{
scheduler::{ACTIVE_PID, PROCESS_COUNT, PROCESS_TABLE},
fs::FILE_SYSTEM,
scheduler::{scheduler_without_ret, ACTIVE_PID, PROCESS_COUNT, PROCESS_TABLE},
time::elapsed_time_since_startup,
};
@@ -54,6 +57,21 @@ impl core::fmt::Debug for Process {
}
}
pub fn create_process_from_file<'a, T: Into<Path<'a>>>(path: T) -> i64 {
let path = path.into();
let name = path.as_str();
let mut bin = FILE_SYSTEM.open_file(path).unwrap();
let mut content: Vec<u8> = Vec::new();
bin.read_to_end(&mut content).unwrap();
let test =
unsafe { core::mem::transmute::<*const u8, extern "C" fn()>(Vec::leak(content).as_ptr()) };
let test = Box::leak(Box::new(move || {
test();
}));
create_process(test, name)
}
pub fn create_process<T: Into<String>, F: Fn()>(code: &'static F, name: T) -> i64 {
let mut next_pid = 0;
while next_pid < PROCESS_COUNT && unsafe { PROCESS_TABLE[next_pid].state != ProcessState::Dead }
@@ -82,27 +100,21 @@ pub fn create_process<T: Into<String>, F: Fn()>(code: &'static F, name: T) -> i6
extern "C" fn process_launcher(code: *const &dyn Fn()) {
unsafe { (*code)() };
terminate_process();
// User code didn't exit before the end of its execution, so we call the exit syscall ourselves
exit();
}
fn terminate_process() {
pub fn exit_process(interrupt_context: &mut *mut ExecutionContext) {
unsafe {
PROCESS_TABLE[ACTIVE_PID].state = ProcessState::Dead;
}
loop {}
// unsafe {
// wfi();
// }
// scheduler();
scheduler_without_ret(interrupt_context)
}
pub fn sleep(duration: Duration) {
pub fn sleep(duration: Duration, interrupt_context: &mut *mut ExecutionContext) {
unsafe {
PROCESS_TABLE[ACTIVE_PID].wake_time = elapsed_time_since_startup() + duration;
PROCESS_TABLE[ACTIVE_PID].state = ProcessState::Asleep;
}
unsafe {
wfi();
}
// scheduler();
scheduler_without_ret(interrupt_context)
}

View File

@@ -57,11 +57,11 @@ pub fn scheduler_init() {
}
}
pub fn scheduler(interrupt_state: ExecutionContext) -> *const ExecutionContext {
pub fn scheduler_without_ret(interrupt_state: &mut *mut ExecutionContext) {
// info!("scheduler");
unsafe {
let prev_pid = ACTIVE_PID;
PROCESS_TABLE[prev_pid].ctx = interrupt_state;
PROCESS_TABLE[prev_pid].ctx = **interrupt_state;
if PROCESS_TABLE[prev_pid].state == ProcessState::Active {
PROCESS_TABLE[prev_pid].state = ProcessState::Activable;
@@ -80,6 +80,6 @@ pub fn scheduler(interrupt_state: ExecutionContext) -> *const ExecutionContext {
}
PROCESS_TABLE[ACTIVE_PID].state = ProcessState::Active;
&raw const PROCESS_TABLE[ACTIVE_PID].ctx
*interrupt_state = &raw mut PROCESS_TABLE[ACTIVE_PID].ctx
}
}

View File

@@ -1,98 +0,0 @@
use core::time::Duration;
#[repr(u64)]
pub enum SysCall {
NanoSleep = 101,
WriteIntTemp = 998,
WriteTemp = 999,
Unimplemented = 1 << 31,
}
impl From<u64> for SysCall {
fn from(value: u64) -> Self {
match value {
101 => SysCall::NanoSleep,
998 => SysCall::WriteIntTemp,
999 => SysCall::WriteTemp,
_ => SysCall::Unimplemented,
}
}
}
#[allow(clippy::too_many_arguments)]
unsafe fn _syscall(
syscall: SysCall,
mut a1: u64,
mut a2: u64,
mut a3: u64,
mut a4: u64,
mut a5: u64,
mut a6: u64,
mut a7: u64,
) -> (u64, u64, u64, u64, u64, u64, u64, u64) {
let mut a0 = syscall as u64;
unsafe {
core::arch::asm!(
"ecall",
inlateout("a0") a0,
inlateout("a1") a1,
inlateout("a2") a2,
inlateout("a3") a3,
inlateout("a4") a4,
inlateout("a5") a5,
inlateout("a6") a6,
inlateout("a7") a7,
clobber_abi("system")
);
}
(a0, a1, a2, a3, a4, a5, a6, a7)
}
macro_rules! syscall {
($syscall:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr, $a6:expr, $a7:expr) => {
_syscall($syscall, $a1, $a2, $a3, $a4, $a5, $a6, $a7)
};
($syscall:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr, $a6:expr) => {
syscall!($syscall, $a1, $a2, $a3, $a4, $a5, $a6, 0)
};
($syscall:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr) => {
syscall!($syscall, $a1, $a2, $a3, $a4, $a5, 0)
};
($syscall:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => {
syscall!($syscall, $a1, $a2, $a3, $a4, 0)
};
($syscall:expr, $a1:expr, $a2:expr, $a3:expr) => {
syscall!($syscall, $a1, $a2, $a3, 0)
};
($syscall:expr, $a1:expr, $a2:expr) => {
syscall!($syscall, $a1, $a2, 0)
};
($syscall:expr, $a1:expr) => {
syscall!($syscall, $a1, 0)
};
($syscall:expr) => {
syscall!($syscall, 0)
};
}
pub fn sleep(duration: Duration) {
unsafe {
let (duration_secs, duration_nanos) = (duration.as_secs(), duration.subsec_nanos() as u64);
syscall!(SysCall::NanoSleep, duration_secs, duration_nanos);
}
}
pub fn write_string_temp(content: &'static str) {
unsafe {
syscall!(
SysCall::WriteTemp,
content.as_ptr() as u64,
content.len() as u64
);
}
}
pub fn write_int_temp(content: u64) {
unsafe {
syscall!(SysCall::WriteIntTemp, content);
}
}

View File

@@ -1,13 +0,0 @@
use core::str;
use log::info;
const DISK_ADDR: usize = 0x9000_0000;
pub struct MemoryDisk {}
impl MemoryDisk {
pub fn test() {
let test = unsafe { str::from_raw_parts(DISK_ADDR as *const u8, 13) };
info!("{}", test);
}
}

View File

@@ -1,15 +1,9 @@
use core::time::Duration;
use alloc::vec::Vec;
use bffs::{io::Read, Fat32FileSystem};
use crate::{
fs::Disk,
syscall::{sleep, write_int_temp, write_string_temp},
};
use shared::syscall::{sleep, write_int_temp, write_string_temp};
#[repr(align(32))]
struct Alignement([u8; 157]);
struct Alignement([u8; include_bytes!("../user/test_pic/test_pic.mem").len()]);
static PROG: Alignement = Alignement(*include_bytes!("../user/test_pic/test_pic.mem"));