2020-03-16 04:18:46 +04:00
|
|
|
// process.rs
|
|
|
|
// Kernel and user processes
|
|
|
|
// Stephen Marz
|
|
|
|
// 27 Nov 2019
|
|
|
|
|
2020-04-25 02:37:48 +04:00
|
|
|
use crate::{cpu::{build_satp, get_mtime, satp_fence_asid, CpuMode, SatpMode, TrapFrame},
|
2020-04-25 22:16:03 +04:00
|
|
|
page::{alloc, dealloc, map, unmap, zalloc, EntryBits, Table, PAGE_SIZE},
|
|
|
|
syscall::syscall_exit};
|
2020-03-16 04:18:46 +04:00
|
|
|
use alloc::collections::vec_deque::VecDeque;
|
2020-04-25 02:37:48 +04:00
|
|
|
use core::ptr::null_mut;
|
2020-03-16 04:18:46 +04:00
|
|
|
|
|
|
|
// How many pages are we going to give a process for their
|
|
|
|
// stack?
|
2020-04-25 02:37:48 +04:00
|
|
|
const STACK_PAGES: usize = 5;
|
2020-03-16 04:18:46 +04:00
|
|
|
// We want to adjust the stack to be at the bottom of the memory allocation
|
|
|
|
// regardless of where it is on the kernel heap.
|
|
|
|
const STACK_ADDR: usize = 0x1_0000_0000;
|
|
|
|
// All processes will have a defined starting point in virtual memory.
|
|
|
|
// We will use this later when we load processes from disk.
|
|
|
|
// const PROCESS_STARTING_ADDR: usize = 0x2000_0000;
|
|
|
|
|
|
|
|
// Here, we store a process list. It uses the global allocator
|
|
|
|
// that we made before and its job is to store all processes.
|
|
|
|
// We will have this list OWN the process. So, anytime we want
|
|
|
|
// the process, we will consult the process list.
|
|
|
|
// Using an Option here is one method of creating a "lazy static".
|
|
|
|
// Rust requires that all statics be initialized, but all
|
|
|
|
// initializations must be at compile-time. We cannot allocate
|
|
|
|
// a VecDeque at compile time, so we are somewhat forced to
|
|
|
|
// do this.
|
|
|
|
pub static mut PROCESS_LIST: Option<VecDeque<Process>> = None;
|
|
|
|
// We can search through the process list to get a new PID, but
|
|
|
|
// it's probably easier and faster just to increase the pid:
|
|
|
|
static mut NEXT_PID: u16 = 1;
|
|
|
|
|
2020-04-26 03:34:20 +04:00
|
|
|
// The following set_* and get_by_pid functions are C-style functions
|
|
|
|
// They probably need to be re-written in a more Rusty style, but for
|
|
|
|
// now they are how we control processes by PID.
|
|
|
|
|
2020-04-24 22:39:33 +04:00
|
|
|
/// Set a process' state to running. This doesn't do any checks.
|
|
|
|
/// If this PID is not found, this returns false. Otherwise, it
|
|
|
|
/// returns true.
|
|
|
|
pub fn set_running(pid: u16) -> bool {
|
|
|
|
// Yes, this is O(n). A better idea here would be a static list
|
|
|
|
// of process pointers.
|
|
|
|
let mut retval = false;
|
|
|
|
unsafe {
|
|
|
|
if let Some(mut pl) = PROCESS_LIST.take() {
|
|
|
|
for proc in pl.iter_mut() {
|
|
|
|
if proc.pid == pid {
|
|
|
|
proc.set_state(ProcessState::Running);
|
|
|
|
retval = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Now, we no longer need the owned Deque, so we hand it
|
|
|
|
// back by replacing the PROCESS_LIST's None with the
|
|
|
|
// Some(pl).
|
|
|
|
PROCESS_LIST.replace(pl);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
retval
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Set a process' state to waiting. This doesn't do any checks.
|
|
|
|
/// If this PID is not found, this returns false. Otherwise, it
|
|
|
|
/// returns true.
|
|
|
|
pub fn set_waiting(pid: u16) -> bool {
|
|
|
|
// Yes, this is O(n). A better idea here would be a static list
|
|
|
|
// of process pointers.
|
|
|
|
let mut retval = false;
|
|
|
|
unsafe {
|
|
|
|
if let Some(mut pl) = PROCESS_LIST.take() {
|
|
|
|
for proc in pl.iter_mut() {
|
|
|
|
if proc.pid == pid {
|
|
|
|
proc.set_state(ProcessState::Waiting);
|
|
|
|
retval = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Now, we no longer need the owned Deque, so we hand it
|
|
|
|
// back by replacing the PROCESS_LIST's None with the
|
|
|
|
// Some(pl).
|
|
|
|
PROCESS_LIST.replace(pl);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
retval
|
|
|
|
}
|
|
|
|
|
2020-04-25 02:37:48 +04:00
|
|
|
/// Sleep a process
|
|
|
|
pub fn set_sleeping(pid: u16, duration: usize) -> bool {
|
|
|
|
// Yes, this is O(n). A better idea here would be a static list
|
|
|
|
// of process pointers.
|
|
|
|
let mut retval = false;
|
|
|
|
unsafe {
|
|
|
|
if let Some(mut pl) = PROCESS_LIST.take() {
|
|
|
|
for proc in pl.iter_mut() {
|
|
|
|
if proc.pid == pid {
|
|
|
|
proc.set_state(ProcessState::Sleeping);
|
|
|
|
proc.set_sleep_until(get_mtime() + duration);
|
|
|
|
retval = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Now, we no longer need the owned Deque, so we hand it
|
|
|
|
// back by replacing the PROCESS_LIST's None with the
|
|
|
|
// Some(pl).
|
|
|
|
PROCESS_LIST.replace(pl);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
retval
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Delete a process given by pid. If this process doesn't exist,
|
|
|
|
/// this function does nothing.
|
2020-04-24 23:22:57 +04:00
|
|
|
pub fn delete_process(pid: u16) {
|
|
|
|
unsafe {
|
|
|
|
if let Some(mut pl) = PROCESS_LIST.take() {
|
|
|
|
for i in 0..pl.len() {
|
|
|
|
let p = pl.get_mut(i).unwrap();
|
|
|
|
if p.get_pid() == pid {
|
|
|
|
// When the structure gets dropped, all of the
|
|
|
|
// allocations get deallocated.
|
|
|
|
pl.remove(i);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Now, we no longer need the owned Deque, so we hand it
|
|
|
|
// back by replacing the PROCESS_LIST's None with the
|
|
|
|
// Some(pl).
|
|
|
|
PROCESS_LIST.replace(pl);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-25 02:37:48 +04:00
|
|
|
/// Get a process by PID. Since we leak the process list, this is
|
|
|
|
/// unsafe since the process can be deleted and we'll still have a pointer.
|
|
|
|
pub unsafe fn get_by_pid(pid: u16) -> *mut Process {
|
|
|
|
let mut ret = null_mut();
|
|
|
|
if let Some(mut pl) = PROCESS_LIST.take() {
|
|
|
|
for i in pl.iter_mut() {
|
|
|
|
if i.get_pid() == pid {
|
|
|
|
ret = i as *mut Process;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
PROCESS_LIST.replace(pl);
|
|
|
|
}
|
|
|
|
|
|
|
|
ret
|
|
|
|
}
|
|
|
|
|
2020-03-16 04:18:46 +04:00
|
|
|
/// We will eventually move this function out of here, but its
|
|
|
|
/// job is just to take a slot in the process list.
|
|
|
|
fn init_process() {
|
|
|
|
// We can't do much here until we have system calls because
|
|
|
|
// we're running in User space.
|
|
|
|
loop {
|
2020-04-25 22:16:03 +04:00
|
|
|
// Alright, I forgot. We cannot put init to sleep since the
|
|
|
|
// scheduler will loop until it finds a process to run. Since
|
|
|
|
// the scheduler is called in an interrupt context, nothing else
|
|
|
|
// can happen until a process becomes available.
|
|
|
|
println!("Init is still here :), alright, back to sleep.");
|
2020-04-26 03:34:20 +04:00
|
|
|
// 500 wfi's should take 500 context switches before we print Init is still here.
|
|
|
|
// Depending on our context switch time, this might be around 3 seconds.
|
2020-04-25 22:16:03 +04:00
|
|
|
for _ in 0..500 {
|
2020-04-26 03:34:20 +04:00
|
|
|
// We can only write wfi here because init_process is being ran
|
|
|
|
// as a kernel process. If we ran this as a user process, it'd
|
|
|
|
// need a system call to execute a privileged instruction.
|
2020-04-25 22:16:03 +04:00
|
|
|
unsafe { asm!("wfi") };
|
2020-03-16 04:18:46 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Add a process given a function address and then
|
|
|
|
/// push it onto the LinkedList. Uses Process::new_default
|
|
|
|
/// to create a new stack, etc.
|
|
|
|
pub fn add_process_default(pr: fn()) {
|
|
|
|
unsafe {
|
|
|
|
// This is the Rust-ism that really trips up C++ programmers.
|
|
|
|
// PROCESS_LIST is wrapped in an Option<> enumeration, which
|
|
|
|
// means that the Option owns the Deque. We can only borrow from
|
|
|
|
// it or move ownership to us. In this case, we choose the
|
|
|
|
// latter, where we move ownership to us, add a process, and
|
|
|
|
// then move ownership back to the PROCESS_LIST.
|
|
|
|
// This allows mutual exclusion as anyone else trying to grab
|
|
|
|
// the process list will get None rather than the Deque.
|
|
|
|
if let Some(mut pl) = PROCESS_LIST.take() {
|
|
|
|
// .take() will replace PROCESS_LIST with None and give
|
|
|
|
// us the only copy of the Deque.
|
|
|
|
let p = Process::new_default(pr);
|
|
|
|
pl.push_back(p);
|
|
|
|
// Now, we no longer need the owned Deque, so we hand it
|
|
|
|
// back by replacing the PROCESS_LIST's None with the
|
|
|
|
// Some(pl).
|
|
|
|
PROCESS_LIST.replace(pl);
|
|
|
|
}
|
|
|
|
// TODO: When we get to multi-hart processing, we need to keep
|
|
|
|
// trying to grab the process list. We can do this with an
|
|
|
|
// atomic instruction. but right now, we're a single-processor
|
|
|
|
// computer.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-24 22:39:33 +04:00
|
|
|
/// Add a kernel process.
|
2020-04-25 02:37:48 +04:00
|
|
|
pub fn add_kernel_process(func: fn()) -> u16 {
|
2020-04-24 22:39:33 +04:00
|
|
|
// This is the Rust-ism that really trips up C++ programmers.
|
|
|
|
// PROCESS_LIST is wrapped in an Option<> enumeration, which
|
|
|
|
// means that the Option owns the Deque. We can only borrow from
|
|
|
|
// it or move ownership to us. In this case, we choose the
|
|
|
|
// latter, where we move ownership to us, add a process, and
|
|
|
|
// then move ownership back to the PROCESS_LIST.
|
|
|
|
// This allows mutual exclusion as anyone else trying to grab
|
|
|
|
// the process list will get None rather than the Deque.
|
|
|
|
if let Some(mut pl) = unsafe { PROCESS_LIST.take() } {
|
|
|
|
// .take() will replace PROCESS_LIST with None and give
|
|
|
|
// us the only copy of the Deque.
|
|
|
|
let func_addr = func as usize;
|
|
|
|
let func_vaddr = func_addr; //- 0x6000_0000;
|
|
|
|
// println!("func_addr = {:x} -> {:x}", func_addr, func_vaddr);
|
|
|
|
// We will convert NEXT_PID below into an atomic increment when
|
|
|
|
// we start getting into multi-hart processing. For now, we want
|
2020-04-25 22:16:03 +04:00
|
|
|
// a process. Get it to work, then improve it!
|
|
|
|
let my_pid = unsafe { NEXT_PID };
|
2020-04-24 22:39:33 +04:00
|
|
|
let mut ret_proc = Process { frame: zalloc(1) as *mut TrapFrame,
|
2020-04-24 23:22:57 +04:00
|
|
|
stack: zalloc(STACK_PAGES),
|
2020-04-25 02:37:48 +04:00
|
|
|
pid: my_pid,
|
|
|
|
root: zalloc(1) as *mut Table,
|
|
|
|
state: ProcessState::Running,
|
|
|
|
data: ProcessData::zero(),
|
|
|
|
sleep_until: 0, };
|
|
|
|
unsafe {
|
|
|
|
NEXT_PID += 1;
|
|
|
|
}
|
|
|
|
// Now we move the stack pointer to the bottom of the
|
|
|
|
// allocation. The spec shows that register x2 (2) is the stack
|
|
|
|
// pointer.
|
|
|
|
// We could use ret_proc.stack.add, but that's an unsafe
|
|
|
|
// function which would require an unsafe block. So, convert it
|
|
|
|
// to usize first and then add PAGE_SIZE is better.
|
|
|
|
// We also need to set the stack adjustment so that it is at the
|
|
|
|
// bottom of the memory and far away from heap allocations.
|
|
|
|
unsafe {
|
|
|
|
(*ret_proc.frame).pc = func_vaddr;
|
2020-04-25 03:16:45 +04:00
|
|
|
// 1 is the return address register. This makes it so we don't have to do
|
|
|
|
// syscall_exit() when a kernel process finishes.
|
|
|
|
(*ret_proc.frame).regs[1] = ra_delete_proc as usize;
|
2020-04-25 02:37:48 +04:00
|
|
|
(*ret_proc.frame).regs[2] = ret_proc.stack as usize + STACK_PAGES * 4096;
|
|
|
|
(*ret_proc.frame).mode = CpuMode::Machine as usize;
|
|
|
|
(*ret_proc.frame).pid = ret_proc.pid as usize;
|
|
|
|
}
|
|
|
|
pl.push_back(ret_proc);
|
|
|
|
// Now, we no longer need the owned Deque, so we hand it
|
|
|
|
// back by replacing the PROCESS_LIST's None with the
|
|
|
|
// Some(pl).
|
2020-04-25 22:16:03 +04:00
|
|
|
unsafe {
|
|
|
|
PROCESS_LIST.replace(pl);
|
|
|
|
}
|
2020-04-25 02:37:48 +04:00
|
|
|
my_pid
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// TODO: When we get to multi-hart processing, we need to keep
|
|
|
|
// trying to grab the process list. We can do this with an
|
|
|
|
// atomic instruction. but right now, we're a single-processor
|
|
|
|
// computer.
|
|
|
|
0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-25 22:16:03 +04:00
|
|
|
/// A kernel process is just a function inside of the kernel. Each
|
|
|
|
/// function will perform a "ret" or return through the return address
|
|
|
|
/// (ra) register. This function address is what it will return to, which
|
|
|
|
/// in turn calls exit. If we don't exit, the process will most likely
|
|
|
|
/// fault.
|
2020-04-25 03:16:45 +04:00
|
|
|
fn ra_delete_proc() {
|
|
|
|
syscall_exit();
|
|
|
|
}
|
|
|
|
|
2020-04-25 02:37:48 +04:00
|
|
|
/// This is the same as the add_kernel_process function, except you can pass
|
|
|
|
/// arguments. Typically, this will be a memory address on the heap where
|
|
|
|
/// arguments can be found.
|
|
|
|
pub fn add_kernel_process_args(func: fn(args_ptr: usize), args: usize) -> u16 {
|
|
|
|
// This is the Rust-ism that really trips up C++ programmers.
|
|
|
|
// PROCESS_LIST is wrapped in an Option<> enumeration, which
|
|
|
|
// means that the Option owns the Deque. We can only borrow from
|
|
|
|
// it or move ownership to us. In this case, we choose the
|
|
|
|
// latter, where we move ownership to us, add a process, and
|
|
|
|
// then move ownership back to the PROCESS_LIST.
|
|
|
|
// This allows mutual exclusion as anyone else trying to grab
|
|
|
|
// the process list will get None rather than the Deque.
|
|
|
|
if let Some(mut pl) = unsafe { PROCESS_LIST.take() } {
|
|
|
|
// .take() will replace PROCESS_LIST with None and give
|
|
|
|
// us the only copy of the Deque.
|
|
|
|
let func_addr = func as usize;
|
|
|
|
let func_vaddr = func_addr; //- 0x6000_0000;
|
|
|
|
// println!("func_addr = {:x} -> {:x}", func_addr, func_vaddr);
|
|
|
|
// We will convert NEXT_PID below into an atomic increment when
|
|
|
|
// we start getting into multi-hart processing. For now, we want
|
2020-04-25 22:16:03 +04:00
|
|
|
// a process. Get it to work, then improve it!
|
|
|
|
let my_pid = unsafe { NEXT_PID };
|
2020-04-25 02:37:48 +04:00
|
|
|
let mut ret_proc = Process { frame: zalloc(1) as *mut TrapFrame,
|
|
|
|
stack: zalloc(STACK_PAGES),
|
|
|
|
pid: my_pid,
|
2020-04-24 22:39:33 +04:00
|
|
|
root: zalloc(1) as *mut Table,
|
|
|
|
state: ProcessState::Running,
|
|
|
|
data: ProcessData::zero(),
|
|
|
|
sleep_until: 0, };
|
|
|
|
unsafe {
|
|
|
|
NEXT_PID += 1;
|
|
|
|
}
|
|
|
|
// Now we move the stack pointer to the bottom of the
|
|
|
|
// allocation. The spec shows that register x2 (2) is the stack
|
|
|
|
// pointer.
|
|
|
|
// We could use ret_proc.stack.add, but that's an unsafe
|
|
|
|
// function which would require an unsafe block. So, convert it
|
|
|
|
// to usize first and then add PAGE_SIZE is better.
|
|
|
|
// We also need to set the stack adjustment so that it is at the
|
|
|
|
// bottom of the memory and far away from heap allocations.
|
|
|
|
unsafe {
|
|
|
|
(*ret_proc.frame).pc = func_vaddr;
|
2020-04-25 02:37:48 +04:00
|
|
|
(*ret_proc.frame).regs[10] = args;
|
2020-04-25 03:16:45 +04:00
|
|
|
// 1 is the return address register. This makes it so we don't have to do
|
|
|
|
// syscall_exit() when a kernel process finishes.
|
|
|
|
(*ret_proc.frame).regs[1] = ra_delete_proc as usize;
|
2020-04-24 22:39:33 +04:00
|
|
|
(*ret_proc.frame).regs[2] = ret_proc.stack as usize + STACK_PAGES * 4096;
|
|
|
|
(*ret_proc.frame).mode = CpuMode::Machine as usize;
|
|
|
|
(*ret_proc.frame).pid = ret_proc.pid as usize;
|
|
|
|
}
|
|
|
|
pl.push_back(ret_proc);
|
|
|
|
// Now, we no longer need the owned Deque, so we hand it
|
|
|
|
// back by replacing the PROCESS_LIST's None with the
|
|
|
|
// Some(pl).
|
2020-04-25 22:16:03 +04:00
|
|
|
unsafe {
|
|
|
|
PROCESS_LIST.replace(pl);
|
|
|
|
}
|
2020-04-25 02:37:48 +04:00
|
|
|
my_pid
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// TODO: When we get to multi-hart processing, we need to keep
|
|
|
|
// trying to grab the process list. We can do this with an
|
|
|
|
// atomic instruction. but right now, we're a single-processor
|
|
|
|
// computer.
|
|
|
|
0
|
2020-04-24 22:39:33 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-16 04:18:46 +04:00
|
|
|
/// This should only be called once, and its job is to create
|
|
|
|
/// the init process. Right now, this process is in the kernel,
|
|
|
|
/// but later, it should call the shell.
|
|
|
|
pub fn init() -> usize {
|
|
|
|
unsafe {
|
|
|
|
PROCESS_LIST = Some(VecDeque::with_capacity(15));
|
2020-04-25 02:37:48 +04:00
|
|
|
// add_process_default(init_process);
|
|
|
|
add_kernel_process(init_process);
|
2020-03-16 04:18:46 +04:00
|
|
|
// Ugh....Rust is giving me fits over here!
|
|
|
|
// I just want a memory address to the trap frame, but
|
|
|
|
// due to the borrow rules of Rust, I'm fighting here. So,
|
|
|
|
// instead, let's move the value out of PROCESS_LIST, get
|
|
|
|
// the address, and then move it right back in.
|
|
|
|
let pl = PROCESS_LIST.take().unwrap();
|
|
|
|
let p = pl.front().unwrap().frame;
|
|
|
|
// let frame = p as *const TrapFrame as usize;
|
|
|
|
// println!("Init's frame is at 0x{:08x}", frame);
|
|
|
|
// Put the process list back in the global.
|
|
|
|
PROCESS_LIST.replace(pl);
|
|
|
|
// Return the first instruction's address to execute.
|
|
|
|
// Since we use the MMU, all start here.
|
|
|
|
(*p).pc
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Our process must be able to sleep, wait, or run.
|
|
|
|
// Running - means that when the scheduler finds this process, it can run it.
|
|
|
|
// Sleeping - means that the process is waiting on a certain amount of time.
|
|
|
|
// Waiting - means that the process is waiting on I/O
|
|
|
|
// Dead - We should never get here, but we can flag a process as Dead and clean
|
|
|
|
// it out of the list later.
|
|
|
|
pub enum ProcessState {
|
|
|
|
Running,
|
|
|
|
Sleeping,
|
|
|
|
Waiting,
|
|
|
|
Dead,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Process {
|
2020-04-24 22:39:33 +04:00
|
|
|
frame: *mut TrapFrame,
|
|
|
|
stack: *mut u8,
|
|
|
|
pid: u16,
|
|
|
|
root: *mut Table,
|
|
|
|
state: ProcessState,
|
|
|
|
data: ProcessData,
|
|
|
|
sleep_until: usize,
|
2020-03-16 04:18:46 +04:00
|
|
|
}
|
|
|
|
|
2020-04-26 03:34:20 +04:00
|
|
|
// Most of this operating system runs more of a C-style, where
|
|
|
|
// we have direct access to the structure members. By default, Rust
|
|
|
|
// will make them private unless we add the keyword pub in front of
|
|
|
|
// EVERY member. I wrote the process structure this way to show
|
|
|
|
// both ways Rust allows us to access members. Just like Python,
|
|
|
|
// the first parameter (the *this parameter in C++) is a reference
|
|
|
|
// to ourself. We can write static functions as a member of this
|
|
|
|
// structure by omitting a self.
|
2020-03-16 04:18:46 +04:00
|
|
|
impl Process {
|
|
|
|
pub fn get_frame_address(&self) -> usize {
|
|
|
|
self.frame as usize
|
|
|
|
}
|
2020-04-24 22:39:33 +04:00
|
|
|
|
2020-04-26 03:36:58 +04:00
|
|
|
pub fn get_frame_mut(&mut self) -> *mut TrapFrame {
|
|
|
|
self.frame
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_frame(&self) -> *const TrapFrame {
|
2020-04-25 02:37:48 +04:00
|
|
|
self.frame
|
|
|
|
}
|
|
|
|
|
2020-03-16 04:18:46 +04:00
|
|
|
pub fn get_program_counter(&self) -> usize {
|
|
|
|
unsafe { (*self.frame).pc }
|
|
|
|
}
|
2020-04-24 22:39:33 +04:00
|
|
|
|
2020-03-16 04:18:46 +04:00
|
|
|
pub fn get_table_address(&self) -> usize {
|
|
|
|
self.root as usize
|
|
|
|
}
|
2020-04-24 22:39:33 +04:00
|
|
|
|
2020-03-16 04:18:46 +04:00
|
|
|
pub fn get_state(&self) -> &ProcessState {
|
|
|
|
&self.state
|
|
|
|
}
|
2020-04-24 22:39:33 +04:00
|
|
|
|
|
|
|
pub fn set_state(&mut self, ps: ProcessState) {
|
|
|
|
self.state = ps;
|
|
|
|
}
|
|
|
|
|
2020-03-16 04:18:46 +04:00
|
|
|
pub fn get_pid(&self) -> u16 {
|
|
|
|
self.pid
|
|
|
|
}
|
2020-04-24 22:39:33 +04:00
|
|
|
|
2020-03-16 04:18:46 +04:00
|
|
|
pub fn get_sleep_until(&self) -> usize {
|
|
|
|
self.sleep_until
|
|
|
|
}
|
2020-04-24 22:39:33 +04:00
|
|
|
|
2020-04-25 02:37:48 +04:00
|
|
|
pub fn set_sleep_until(&mut self, until: usize) {
|
|
|
|
self.sleep_until = until;
|
|
|
|
}
|
|
|
|
|
2020-03-16 04:18:46 +04:00
|
|
|
pub fn new_default(func: fn()) -> Self {
|
|
|
|
let func_addr = func as usize;
|
2020-04-26 03:34:20 +04:00
|
|
|
let func_vaddr = func_addr;
|
2020-04-26 03:36:58 +04:00
|
|
|
// println!("func_addr = {:x} -> {:x}", func_addr, func_vaddr);
|
|
|
|
// We will convert NEXT_PID below into an atomic increment when
|
|
|
|
// we start getting into multi-hart processing. For now, we want
|
|
|
|
// a process. Get it to work, then improve it!
|
2020-04-24 22:39:33 +04:00
|
|
|
let mut ret_proc = Process { frame: zalloc(1) as *mut TrapFrame,
|
|
|
|
stack: alloc(STACK_PAGES),
|
|
|
|
pid: unsafe { NEXT_PID },
|
|
|
|
root: zalloc(1) as *mut Table,
|
|
|
|
state: ProcessState::Running,
|
|
|
|
data: ProcessData::zero(),
|
|
|
|
sleep_until: 0, };
|
2020-03-16 04:18:46 +04:00
|
|
|
unsafe {
|
|
|
|
satp_fence_asid(NEXT_PID as usize);
|
|
|
|
NEXT_PID += 1;
|
|
|
|
}
|
|
|
|
// Now we move the stack pointer to the bottom of the
|
|
|
|
// allocation. The spec shows that register x2 (2) is the stack
|
|
|
|
// pointer.
|
|
|
|
// We could use ret_proc.stack.add, but that's an unsafe
|
|
|
|
// function which would require an unsafe block. So, convert it
|
|
|
|
// to usize first and then add PAGE_SIZE is better.
|
|
|
|
// We also need to set the stack adjustment so that it is at the
|
|
|
|
// bottom of the memory and far away from heap allocations.
|
|
|
|
let saddr = ret_proc.stack as usize;
|
|
|
|
unsafe {
|
|
|
|
(*ret_proc.frame).pc = func_vaddr;
|
|
|
|
(*ret_proc.frame).regs[2] = STACK_ADDR + PAGE_SIZE * STACK_PAGES;
|
2020-04-24 22:39:33 +04:00
|
|
|
(*ret_proc.frame).mode = CpuMode::User as usize;
|
|
|
|
(*ret_proc.frame).pid = ret_proc.pid as usize;
|
2020-03-16 04:18:46 +04:00
|
|
|
}
|
|
|
|
// Map the stack on the MMU
|
|
|
|
let pt;
|
|
|
|
unsafe {
|
|
|
|
pt = &mut *ret_proc.root;
|
2020-04-26 03:36:58 +04:00
|
|
|
(*ret_proc.frame).satp = build_satp(SatpMode::Sv39, ret_proc.pid as usize, ret_proc.root as usize);
|
2020-03-16 04:18:46 +04:00
|
|
|
}
|
|
|
|
// We need to map the stack onto the user process' virtual
|
|
|
|
// memory This gets a little hairy because we need to also map
|
|
|
|
// the function code too.
|
|
|
|
for i in 0..STACK_PAGES {
|
|
|
|
let addr = i * PAGE_SIZE;
|
2020-04-24 22:39:33 +04:00
|
|
|
map(pt, STACK_ADDR + addr, saddr + addr, EntryBits::UserReadWrite.val(), 0);
|
2020-03-16 04:18:46 +04:00
|
|
|
// println!("Set stack from 0x{:016x} -> 0x{:016x}", STACK_ADDR + addr, saddr + addr);
|
|
|
|
}
|
|
|
|
// Map the program counter on the MMU and other bits
|
|
|
|
for i in 0..=100 {
|
|
|
|
let modifier = i * 0x1000;
|
2020-04-24 22:39:33 +04:00
|
|
|
map(pt, func_vaddr + modifier, func_addr + modifier, EntryBits::UserReadWriteExecute.val(), 0);
|
2020-03-16 04:18:46 +04:00
|
|
|
}
|
|
|
|
ret_proc
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for Process {
|
|
|
|
/// Since we're storing ownership of a Process in the linked list,
|
|
|
|
/// we can cause it to deallocate automatically when it is removed.
|
|
|
|
fn drop(&mut self) {
|
2020-04-24 23:22:57 +04:00
|
|
|
// println!("Dropping process {}", self.get_pid());
|
2020-03-16 04:18:46 +04:00
|
|
|
// We allocate the stack as a page.
|
|
|
|
dealloc(self.stack);
|
|
|
|
// This is unsafe, but it's at the drop stage, so we won't
|
|
|
|
// be using this again.
|
|
|
|
unsafe {
|
|
|
|
// Remember that unmap unmaps all levels of page tables
|
|
|
|
// except for the root. It also deallocates the memory
|
|
|
|
// associated with the tables.
|
|
|
|
unmap(&mut *self.root);
|
|
|
|
}
|
|
|
|
dealloc(self.root as *mut u8);
|
2020-04-24 23:22:57 +04:00
|
|
|
dealloc(self.frame as *mut u8);
|
2020-03-16 04:18:46 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// The private data in a process contains information
|
|
|
|
// that is relevant to where we are, including the path
|
|
|
|
// and open file descriptors.
|
|
|
|
// We will allow dead code for now until we have a need for the
|
|
|
|
// private process data. This is essentially our resource control block (RCB).
|
|
|
|
#[allow(dead_code)]
|
|
|
|
pub struct ProcessData {
|
|
|
|
cwd_path: [u8; 128],
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is private data that we can query with system calls.
|
|
|
|
// If we want to implement CFQ (completely fair queuing), which
|
|
|
|
// is a per-process block queuing algorithm, we can put that here.
|
|
|
|
impl ProcessData {
|
|
|
|
pub fn zero() -> Self {
|
|
|
|
ProcessData { cwd_path: [0; 128], }
|
|
|
|
}
|
|
|
|
}
|