use core::time::Duration; use alloc::{boxed::Box, format, string::String, vec::Vec}; use bffs::{io::Read, path::Path}; use shared::syscall::exit; use crate::{ fs::FILE_SYSTEM, scheduler::{scheduler_without_ret, ACTIVE_PID, PROCESS_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 u64, pub sp: *const u64, pub gp: u64, pub tp: u64, pub a: [u64; 8], pub t: [u64; 7], pub s: [u64; 12], pub mepc: *const u64, pub mstatus: u64, } pub struct Process { pub pid: i64, pub name: String, pub state: ProcessState, pub entry: Option<&'static dyn Fn()>, pub wake_time: Duration, pub ctx: ExecutionContext, pub stack: [u64; 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("stack", &format!("[_; {}]", STACK_SIZE)) .finish() } } pub fn create_process_from_file<'a, T: Into>>(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 = 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, 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 } { next_pid += 1; } if next_pid >= PROCESS_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 = Some(code); PROCESS_TABLE[next_pid].ctx.a[0] = PROCESS_TABLE[next_pid].entry.as_ref().unwrap_unchecked() as *const &dyn Fn() as u64; PROCESS_TABLE[next_pid].ctx.mepc = process_launcher as *const _; PROCESS_TABLE[next_pid].ctx.mstatus = 1 << 1 | 1 << 5; PROCESS_TABLE[next_pid].ctx.sp = &raw const PROCESS_TABLE[next_pid].stack[STACK_SIZE - 1]; } next_pid as i64 } extern "C" fn process_launcher(code: *const &dyn Fn()) { unsafe { (*code)() }; // User code didn't exit before the end of its execution, so we call the exit syscall ourselves exit(); } pub fn exit_process(interrupt_context: &mut *mut ExecutionContext) { unsafe { PROCESS_TABLE[ACTIVE_PID].state = ProcessState::Dead; } scheduler_without_ret(interrupt_context) } 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; } scheduler_without_ret(interrupt_context) }