Files
riscv64-kernel/src/process.rs

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)
}