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,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,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 !",
);
}