osdev5/kernel/src/proc/process.rs

370 lines
11 KiB
Rust
Raw Normal View History

2021-11-04 13:29:27 +02:00
//! Process data and control
use crate::arch::aarch64::exception::ExceptionFrame;
use crate::mem::{
self,
phys::{self, PageUsage},
virt::{MapAttributes, Space},
};
2021-11-17 13:05:51 +02:00
use crate::proc::{
wait::Wait, Context, ProcessIo, Thread, ThreadRef, ThreadState, PROCESSES, SCHED, THREADS,
};
use crate::sync::{IrqSafeSpinLock, IrqSafeSpinLockGuard};
use alloc::{rc::Rc, vec::Vec};
use core::cell::UnsafeCell;
use core::sync::atomic::{AtomicU32, Ordering};
2021-11-17 13:05:51 +02:00
use libsys::{
error::Errno,
proc::{ExitCode, Pid},
signal::Signal,
};
/// Wrapper type for a process struct reference
pub type ProcessRef = Rc<Process>;
/// List of possible process states
#[derive(Clone, Copy, Debug, PartialEq)]
2021-11-17 13:05:51 +02:00
pub enum ProcessState {
/// Process is alive
Active,
/// Process has finished execution and is waiting to be reaped
Finished,
}
struct ProcessInner {
space: Option<&'static mut Space>,
2021-11-17 13:05:51 +02:00
state: ProcessState,
id: Pid,
2021-10-26 13:38:29 +03:00
exit: Option<ExitCode>,
2021-11-17 13:05:51 +02:00
threads: Vec<u32>,
}
/// Structure describing an operating system process
#[allow(dead_code)]
pub struct Process {
2021-10-21 19:10:41 +03:00
inner: IrqSafeSpinLock<ProcessInner>,
2021-11-05 15:24:10 +02:00
exit_wait: Wait,
2021-11-11 13:46:36 +02:00
signal_state: AtomicU32,
2021-11-04 13:29:27 +02:00
/// Process I/O context
2021-11-02 15:36:34 +02:00
pub io: IrqSafeSpinLock<ProcessIo>,
}
impl Process {
const USTACK_VIRT_TOP: usize = 0x100000000;
const USTACK_PAGES: usize = 4;
2021-11-17 13:05:51 +02:00
#[inline]
pub fn id(&self) -> Pid {
self.inner.lock().id
}
#[inline]
2021-10-21 12:16:24 +03:00
pub fn current() -> ProcessRef {
2021-11-17 13:05:51 +02:00
Thread::current().owner().unwrap()
}
#[inline]
pub fn manipulate_space<F>(&self, f: F) -> Result<(), Errno>
where
F: FnOnce(&mut Space) -> Result<(), Errno>,
{
f(self.inner.lock().space.as_mut().unwrap())
}
pub fn new_kernel(entry: extern "C" fn(usize) -> !, arg: usize) -> Result<ProcessRef, Errno> {
let id = new_kernel_pid();
let thread = Thread::new_kernel(Some(id), entry, arg)?;
let mut inner = ProcessInner {
threads: Vec::new(),
id,
exit: None,
space: None,
state: ProcessState::Active,
};
inner.threads.push(thread.id());
let res = Rc::new(Self {
exit_wait: Wait::new(),
io: IrqSafeSpinLock::new(ProcessIo::new()),
signal_state: AtomicU32::new(0),
inner: IrqSafeSpinLock::new(inner),
});
debugln!("New kernel process: {:?}", id);
let prev = PROCESSES.lock().insert(id, res.clone());
assert!(prev.is_none());
Ok(res)
}
pub fn enqueue(&self) {
let inner = self.inner.lock();
for &tid in inner.threads.iter() {
SCHED.enqueue(tid);
}
2021-10-21 12:16:24 +03:00
}
2021-11-11 22:46:01 +02:00
/// Returns process (if any) to which `pid` refers
2021-11-05 15:24:10 +02:00
pub fn get(pid: Pid) -> Option<ProcessRef> {
PROCESSES.lock().get(&pid).cloned()
}
2021-11-12 10:26:58 +02:00
/// Sets a pending signal for a process
2021-11-11 13:46:36 +02:00
pub fn set_signal(&self, signal: Signal) {
2021-11-17 13:05:51 +02:00
let mut lock = self.inner.lock();
let main_thread = Thread::get(lock.threads[0]).unwrap();
// TODO check that `signal` is not a fault signal
// it is illegal to call this function with
// fault signals
2021-11-11 13:46:36 +02:00
2021-11-17 13:05:51 +02:00
match main_thread.state() {
ThreadState::Running => {
Process::enter_signal_on(lock, main_thread, signal);
2021-11-11 13:46:36 +02:00
}
2021-11-17 13:05:51 +02:00
ThreadState::Waiting => {
2021-11-11 13:46:36 +02:00
// TODO abort whatever the process is waiting for
todo!()
}
2021-11-17 13:05:51 +02:00
ThreadState::Ready => {
2021-11-11 13:46:36 +02:00
todo!()
}
2021-11-17 13:05:51 +02:00
ThreadState::Finished => {
2021-11-11 13:46:36 +02:00
// TODO report error back
todo!()
}
}
}
2021-11-17 13:05:51 +02:00
fn enter_signal_on(mut inner: IrqSafeSpinLockGuard<ProcessInner>, thread: ThreadRef, signal: Signal) {
let ttbr0 =
inner.space.as_mut().unwrap().address_phys() | ((inner.id.asid() as usize) << 48);
drop(inner);
thread.enter_signal(signal, ttbr0);
2021-11-11 13:46:36 +02:00
}
2021-11-17 13:05:51 +02:00
pub fn enter_fault_signal(&self, thread: ThreadRef, signal: Signal) {
let lock = self.inner.lock();
Process::enter_signal_on(lock, thread, signal);
2021-11-11 13:46:36 +02:00
}
2021-11-17 13:05:51 +02:00
pub fn new_user_thread(&self, entry: usize, stack: usize, arg: usize) -> Result<u32, Errno> {
2021-11-11 13:46:36 +02:00
let mut lock = self.inner.lock();
2021-10-31 22:37:29 +02:00
2021-11-17 13:05:51 +02:00
let space_phys = lock.space.as_mut().unwrap().address_phys();
let ttbr0 = space_phys | ((lock.id.asid() as usize) << 48);
2021-10-31 22:37:29 +02:00
2021-11-17 13:05:51 +02:00
let thread = Thread::new_user(lock.id, entry, stack, arg, ttbr0)?;
let tid = thread.id();
lock.threads.push(tid);
SCHED.enqueue(tid);
2021-10-31 22:37:29 +02:00
2021-11-17 13:05:51 +02:00
Ok(tid)
}
2021-11-04 13:29:27 +02:00
/// Creates a "fork" of the process, cloning its address space and
/// resources
2021-11-04 11:26:15 +02:00
pub fn fork(&self, frame: &mut ExceptionFrame) -> Result<Pid, Errno> {
let src_io = self.io.lock();
let mut src_inner = self.inner.lock();
let dst_id = new_user_pid();
2021-11-04 11:26:15 +02:00
let dst_space = src_inner.space.as_mut().unwrap().fork()?;
let dst_space_phys = (dst_space as *mut _ as usize) - mem::KERNEL_OFFSET;
let dst_ttbr0 = dst_space_phys | ((dst_id.asid() as usize) << 48);
2021-11-17 13:05:51 +02:00
let mut threads = Vec::new();
let tid = Thread::fork(Some(dst_id), frame, dst_ttbr0)?.id();
threads.push(tid);
2021-11-04 11:26:15 +02:00
let dst = Rc::new(Self {
2021-11-05 15:24:10 +02:00
exit_wait: Wait::new(),
2021-11-17 13:05:51 +02:00
io: IrqSafeSpinLock::new(src_io.fork()?),
2021-11-11 13:46:36 +02:00
signal_state: AtomicU32::new(0),
2021-11-04 11:26:15 +02:00
inner: IrqSafeSpinLock::new(ProcessInner {
2021-11-17 13:05:51 +02:00
threads,
2021-11-04 11:26:15 +02:00
exit: None,
space: Some(dst_space),
2021-11-17 13:05:51 +02:00
state: ProcessState::Active,
id: dst_id,
2021-11-04 13:29:27 +02:00
}),
2021-11-04 11:26:15 +02:00
});
2021-11-17 13:05:51 +02:00
debugln!("Process {:?} forked into {:?}", src_inner.id, dst_id);
2021-11-04 12:04:37 +02:00
assert!(PROCESSES.lock().insert(dst_id, dst).is_none());
2021-11-17 13:05:51 +02:00
SCHED.enqueue(tid);
2021-11-04 11:26:15 +02:00
Ok(dst_id)
}
2021-11-17 13:05:51 +02:00
// TODO a way to terminate a single thread?
/// Terminates a process.
2021-11-17 13:05:51 +02:00
pub fn exit<I: Into<ExitCode>>(status: I) {
unsafe {
asm!("msr daifclr, #0xF");
}
let status = status.into();
2021-11-17 13:05:51 +02:00
let thread = Thread::current();
let process = thread.owner().unwrap();
let mut lock = process.inner.lock();
infoln!("Process {:?} is exiting: {:?}", lock.id, status);
assert!(lock.exit.is_none());
lock.exit = Some(status);
lock.state = ProcessState::Finished;
for &tid in lock.threads.iter() {
debugln!("Dequeue {:?}", tid);
Thread::get(tid).unwrap().terminate();
SCHED.dequeue(tid);
}
SCHED.debug();
if let Some(space) = lock.space.take() {
unsafe {
Space::release(space);
asm!("tlbi aside1, {}", in(reg) ((lock.id.asid() as usize) << 48));
2021-11-09 18:24:08 +02:00
}
2021-11-17 13:05:51 +02:00
}
2021-11-09 18:24:08 +02:00
2021-11-17 13:05:51 +02:00
process.io.lock().handle_exit();
2021-11-05 15:24:10 +02:00
2021-11-17 13:05:51 +02:00
drop(lock);
process.exit_wait.wakeup_all();
SCHED.switch(true);
panic!("This code should never run");
}
fn collect(&self) -> Option<ExitCode> {
let lock = self.inner.lock();
2021-11-17 13:05:51 +02:00
if lock.state == ProcessState::Finished {
lock.exit
} else {
None
}
}
/// Waits for a process to finish and reaps it
pub fn waitpid(pid: Pid) -> Result<ExitCode, Errno> {
loop {
2021-10-26 13:38:29 +03:00
let proc = PROCESSES
.lock()
.get(&pid)
.cloned()
.ok_or(Errno::DoesNotExist)?;
if let Some(r) = proc.collect() {
2021-11-05 15:24:10 +02:00
// TODO drop the process struct itself
PROCESSES.lock().remove(&proc.id());
debugln!("pid {:?} has {} refs", proc.id(), Rc::strong_count(&proc));
return Ok(r);
}
2021-11-05 15:24:10 +02:00
proc.exit_wait.wait(None)?;
}
}
/// Loads a new program into current process address space
pub fn execve<F: FnOnce(&mut Space) -> Result<usize, Errno>>(
loader: F,
arg: usize,
) -> Result<(), Errno> {
unsafe {
// Run with interrupts disabled
asm!("msr daifset, #2");
}
2021-11-17 13:05:51 +02:00
let proc = Process::current();
let mut process_lock = proc.inner.lock();
if process_lock.threads.len() != 1 {
todo!();
}
let thread = Thread::get(process_lock.threads[0]).unwrap();
if process_lock.id.is_kernel() {
let mut processes = PROCESSES.lock();
let old_pid = process_lock.id;
let new_pid = new_user_pid();
debugln!("Downgrading process {:?} -> {:?}", old_pid, new_pid);
let r = processes.remove(&old_pid);
assert!(r.is_some());
process_lock.id = new_pid;
let r = processes.insert(new_pid, proc.clone());
assert!(r.is_none());
2021-11-05 00:42:14 +02:00
} else {
// Invalidate user ASID
2021-11-17 13:05:51 +02:00
let input = (process_lock.id.asid() as usize) << 48;
2021-11-05 00:42:14 +02:00
unsafe {
asm!("tlbi aside1, {}", in(reg) input);
}
}
2021-11-17 13:05:51 +02:00
thread.set_owner(process_lock.id);
2021-11-05 13:45:22 +02:00
proc.io.lock().handle_cloexec();
let new_space = Space::alloc_empty()?;
let new_space_phys = (new_space as *mut _ as usize) - mem::KERNEL_OFFSET;
let ustack_virt_bottom = Self::USTACK_VIRT_TOP - Self::USTACK_PAGES * mem::PAGE_SIZE;
for i in 0..Self::USTACK_PAGES {
let page = phys::alloc_page(PageUsage::UserPrivate).unwrap();
let flags = MapAttributes::SH_OUTER
| MapAttributes::NOT_GLOBAL
| MapAttributes::UXN
| MapAttributes::PXN
| MapAttributes::AP_BOTH_READWRITE;
new_space
.map(ustack_virt_bottom + i * mem::PAGE_SIZE, page, flags)
.unwrap();
}
let entry = loader(new_space)?;
debugln!("Will now enter at {:#x}", entry);
// TODO drop old address space
2021-11-17 13:05:51 +02:00
process_lock.space = Some(new_space);
unsafe {
// TODO drop old context
2021-11-17 13:05:51 +02:00
let ctx = thread.ctx.get();
ctx.write(Context::user(
entry,
arg,
2021-11-17 13:05:51 +02:00
new_space_phys | ((process_lock.id.asid() as usize) << 48),
Self::USTACK_VIRT_TOP,
));
2021-11-17 13:05:51 +02:00
drop(process_lock);
(*ctx).enter();
}
}
}
impl Drop for Process {
fn drop(&mut self) {
debugln!("Dropping process {:?}", self.id());
}
}
/// Allocates a new kernel-space PID
pub fn new_kernel_pid() -> Pid {
static LAST: AtomicU32 = AtomicU32::new(0);
let id = LAST.fetch_add(1, Ordering::Relaxed);
Pid::kernel(id)
}
/// Allocates a new user-space PID.
///
/// First user PID is #1.
pub fn new_user_pid() -> Pid {
static LAST: AtomicU32 = AtomicU32::new(1);
let id = LAST.fetch_add(1, Ordering::Relaxed);
assert!(id < 256, "Out of user PIDs");
Pid::user(id)
}