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

@@ -6,4 +6,7 @@ build-std = ["core", "compiler_builtins", "alloc"]
build-std-features = ["compiler-builtins-mem"]
[target.riscv64]
rustflags = [
"-C", "link-arg=-Tilm.ld",
]
runner = "qemu-system-riscv64 -machine virt -device bochs-display -bios none -m 512M -device loader,file=/home/julien/ensimag/TPs/kernel/disk.img,addr=0x90000000 -kernel"

View File

@@ -1,6 +1,6 @@
[workspace]
resolver = "3"
members = ["user/*"]
members = ["crates/os-std", "crates/shared", "user/*"]
[package]
name = "kernel-rust"
@@ -13,3 +13,4 @@ kernel-macros = { path = "crates/kernel-macros" }
log = "0.4"
critical-section = { version = "1", features = ["restore-state-bool"] }
bffs = { path = "../../../code/bffs", features = ["alloc"] }
shared = { path = "crates/shared" }

View File

@@ -1,3 +0,0 @@
fn main() {
println!("cargo::rustc-link-arg=-Tilm.ld");
}

View File

@@ -0,0 +1,12 @@
[package]
name = "os-std-macros"
version = "0.1.0"
edition = "2024"
[lib]
proc-macro = true
[dependencies]
proc-macro2 = "1"
quote = "1"
syn = { version = "2", features = ["full"] }

View File

@@ -0,0 +1 @@

8
crates/os-std/Cargo.toml Normal file
View File

@@ -0,0 +1,8 @@
[package]
name = "os-std"
version = "0.1.0"
edition = "2024"
[dependencies]
os-std-macros = { path = "../os-std-macros" }
shared = { path = "../shared" }

21
crates/os-std/src/lib.rs Normal file
View File

@@ -0,0 +1,21 @@
#![no_std]
mod prelude;
pub use shared::syscall;
#[macro_export]
macro_rules! custom_std_setup {
() => {
#[panic_handler]
fn panic(_panic_info: &core::panic::PanicInfo) -> ! {
// TODO print
loop {}
}
#[unsafe(no_mangle)]
pub extern "C" fn _start() {
main()
}
};
}

View File

@@ -0,0 +1 @@

6
crates/shared/Cargo.toml Normal file
View File

@@ -0,0 +1,6 @@
[package]
name = "shared"
version = "0.1.0"
edition = "2024"
[dependencies]

3
crates/shared/src/lib.rs Normal file
View File

@@ -0,0 +1,3 @@
#![no_std]
pub mod syscall;

View File

@@ -2,6 +2,7 @@ use core::time::Duration;
#[repr(u64)]
pub enum SysCall {
Exit = 60,
NanoSleep = 101,
WriteIntTemp = 998,
WriteTemp = 999,
@@ -11,6 +12,7 @@ pub enum SysCall {
impl From<u64> for SysCall {
fn from(value: u64) -> Self {
match value {
60 => SysCall::Exit,
101 => SysCall::NanoSleep,
998 => SysCall::WriteIntTemp,
999 => SysCall::WriteTemp,
@@ -75,6 +77,12 @@ macro_rules! syscall {
};
}
pub fn exit() {
unsafe {
syscall!(SysCall::Exit);
}
}
pub fn sleep(duration: Duration) {
unsafe {
let (duration_secs, duration_nanos) = (duration.as_secs(), duration.subsec_nanos() as u64);

5
ilm.ld
View File

@@ -2,17 +2,16 @@
* ld directives the for barmetal RISCV
*/
OUTPUT_ARCH(riscv)
ENTRY(entry)
ENTRY(_start)
MEMORY {
RAM (wxa) : ORIGIN = 0x80000000, LENGTH = 128M
}
SECTIONS {
/* The kernel starts at 0x80000000 */
. = 0x80000000;
.text : {
KEEP(*(.text.entry))
KEEP(*(.text._start))
*(.text .text.*)
} > RAM

View File

@@ -3,16 +3,23 @@ cargo_flags := if release != "" { "--release" } else { "" }
default: run
build_user_prog prog:
cd {{ "user" / prog }} && \
RUSTFLAGS="-C relocation-model=pic" cargo b {{ cargo_flags }}
riscv64-elf-objcopy -O binary {{ "target/riscv64/debug" / prog }} {{ "user" / prog / prog + ".mem" }}
mount_filesystem:
# Add some permissions to be able to do next operations without sudo
@mountpoint -q mnt || sudo mount -o umask=0022,gid=$(id -g $USER),uid=$(id -u $USER) disk.img mnt
build: (map_dir "user" "build_user_prog")
sync_filesystem:
sync
build_user_prog prog:
RUSTFLAGS="-C relocation-model=pic -C link-arg=-Tilm.ld" cargo b {{ cargo_flags }} --package {{ prog }}
riscv64-elf-objcopy -O binary {{ "target/riscv64/debug" / prog }} {{ "mnt/usr/bin" / prog }}
build: mount_filesystem (map_dir "user" "build_user_prog")
cargo b {{ cargo_flags }}
just sync_filesystem
run: build
cargo r {{ cargo_flags }} --bin kernel-rust
cargo r {{ cargo_flags }}
map_dir dir recipe:
@for file in `ls {{ dir }}`; do \

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,24 +208,18 @@ unsafe extern "C" fn _machine_mode_trap() {
mret"
)
}
generate_trap_handler! {
_supervisor_mode_trap, supervisor_trap_handler, s
}
#[macro_export]
macro_rules! generate_trap_handler {
($name:ident, $jump_to:ident, $mode:ident) => {
#[unsafe(naked)]
#[unsafe(no_mangle)]
unsafe extern "C" fn $name() {
#[unsafe(naked)]
#[unsafe(no_mangle)]
unsafe extern "C" fn _supervisor_mode_trap() {
naked_asm!(concat!(
"
mv t0, sp
// Store sp before it gets modified
sd sp, 8-264(sp)
addi sp, sp, -264
# Store the current frame
sd ra, 0(sp)
sd t0, 8(sp) // sp
// sp
sd gp, 16(sp)
sd tp, 24(sp)
sd a0, 32(sp)
@@ -260,20 +255,21 @@ macro_rules! generate_trap_handler {
sd t0, 256(sp)
mv a0, sp
csrr a1, ",
stringify!($mode),
"cause
csrr a2, ",
stringify!($mode),
"ie
csrr a3, ",
stringify!($mode),
"ip
jal ",
stringify!($jump_to),
"
csrr a1, scause
csrr a2, sie
csrr a3, sip
jal supervisor_trap_handler
# Restore registers
# Restore registers and sret
jal restore_context"
))
}
#[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)
@@ -311,12 +307,8 @@ macro_rules! generate_trap_handler {
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,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"));

View File

@@ -4,3 +4,4 @@ version = "0.1.0"
edition = "2024"
[dependencies]
os-std = { path = "../../crates/os-std" }

View File

@@ -1,3 +0,0 @@
fn main() {
println!("cargo::rustc-link-arg=-Tilm.ld");
}

View File

@@ -1,21 +1,11 @@
#![no_std]
#![no_main]
os_std::custom_std_setup! {}
#[panic_handler]
fn panic(_panic_info: &core::panic::PanicInfo) -> ! {
loop {}
}
use os_std::syscall::write_string_temp;
#[unsafe(no_mangle)]
pub extern "C" fn entry() {
let test = "Hello from PIC program loaded dynamically";
unsafe {
core::arch::asm!(
"ecall",
in("a0") 999,
in("a1") test.as_ptr(),
in("a2") test.len(),
clobber_abi("system")
fn main() {
write_string_temp(
"Hello from PIC program loaded dynamically with custom std and a better justfile, and syscalls !",
);
}
}