121 lines
3.4 KiB
Rust
121 lines
3.4 KiB
Rust
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<'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 }
|
|
{
|
|
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)
|
|
}
|