2021-11-04 13:29:27 +02:00
|
|
|
//! Process data and control
|
|
|
|
use crate::arch::aarch64::exception::ExceptionFrame;
|
2021-10-20 13:54:33 +03:00
|
|
|
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};
|
2021-10-20 13:54:33 +03:00
|
|
|
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,
|
|
|
|
};
|
2021-10-20 13:54:33 +03:00
|
|
|
|
|
|
|
/// Wrapper type for a process struct reference
|
2021-10-20 16:32:07 +03:00
|
|
|
pub type ProcessRef = Rc<Process>;
|
|
|
|
|
2021-10-20 13:54:33 +03:00
|
|
|
/// 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,
|
2021-10-20 13:54:33 +03:00
|
|
|
/// Process has finished execution and is waiting to be reaped
|
|
|
|
Finished,
|
|
|
|
}
|
|
|
|
|
2021-10-20 16:32:07 +03:00
|
|
|
struct ProcessInner {
|
|
|
|
space: Option<&'static mut Space>,
|
2021-11-17 13:05:51 +02:00
|
|
|
state: ProcessState,
|
2021-10-20 16:32:07 +03:00
|
|
|
id: Pid,
|
2021-10-26 13:38:29 +03:00
|
|
|
exit: Option<ExitCode>,
|
2021-11-17 13:05:51 +02:00
|
|
|
threads: Vec<u32>,
|
2021-10-20 16:32:07 +03:00
|
|
|
}
|
|
|
|
|
2021-10-20 13:54:33 +03:00
|
|
|
/// 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>,
|
2021-10-20 16:32:07 +03:00
|
|
|
}
|
|
|
|
|
2021-10-20 13:54:33 +03:00
|
|
|
impl Process {
|
2021-10-20 16:32:07 +03:00
|
|
|
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-10-20 13:54:33 +03:00
|
|
|
}
|
|
|
|
|
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();
|
|
|
|
|
2021-11-12 19:44:10 +02:00
|
|
|
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
|
|
|
|
2021-11-12 19:44:10 +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?
|
2021-10-20 13:54:33 +03:00
|
|
|
/// Terminates a process.
|
2021-11-17 13:05:51 +02:00
|
|
|
pub fn exit<I: Into<ExitCode>>(status: I) {
|
|
|
|
unsafe {
|
|
|
|
asm!("msr daifclr, #0xF");
|
|
|
|
}
|
2021-10-20 16:32:07 +03:00
|
|
|
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");
|
2021-10-20 16:32:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
fn collect(&self) -> Option<ExitCode> {
|
|
|
|
let lock = self.inner.lock();
|
2021-11-17 13:05:51 +02:00
|
|
|
if lock.state == ProcessState::Finished {
|
2021-10-20 16:32:07 +03:00
|
|
|
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)?;
|
2021-10-20 16:32:07 +03:00
|
|
|
|
|
|
|
if let Some(r) = proc.collect() {
|
2021-11-05 15:24:10 +02:00
|
|
|
// TODO drop the process struct itself
|
2021-10-20 16:32:07 +03:00
|
|
|
PROCESSES.lock().remove(&proc.id());
|
2021-11-12 19:44:10 +02:00
|
|
|
debugln!("pid {:?} has {} refs", proc.id(), Rc::strong_count(&proc));
|
2021-10-20 16:32:07 +03:00
|
|
|
return Ok(r);
|
|
|
|
}
|
2021-11-05 15:24:10 +02:00
|
|
|
|
|
|
|
proc.exit_wait.wait(None)?;
|
2021-10-20 16:32:07 +03:00
|
|
|
}
|
2021-10-20 13:54:33 +03:00
|
|
|
}
|
|
|
|
|
2021-10-20 16:32:07 +03:00
|
|
|
/// Loads a new program into current process address space
|
2021-10-20 13:54:33 +03:00
|
|
|
pub fn execve<F: FnOnce(&mut Space) -> Result<usize, Errno>>(
|
|
|
|
loader: F,
|
|
|
|
arg: usize,
|
|
|
|
) -> Result<(), Errno> {
|
2021-10-20 16:32:07 +03:00
|
|
|
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-10-20 16:32:07 +03:00
|
|
|
}
|
|
|
|
|
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();
|
|
|
|
|
2021-10-20 16:32:07 +03:00
|
|
|
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);
|
2021-10-20 16:32:07 +03:00
|
|
|
|
|
|
|
unsafe {
|
|
|
|
// TODO drop old context
|
2021-11-17 13:05:51 +02:00
|
|
|
let ctx = thread.ctx.get();
|
2021-10-20 16:32:07 +03:00
|
|
|
|
|
|
|
ctx.write(Context::user(
|
|
|
|
entry,
|
|
|
|
arg,
|
2021-11-17 13:05:51 +02:00
|
|
|
new_space_phys | ((process_lock.id.asid() as usize) << 48),
|
2021-10-20 16:32:07 +03:00
|
|
|
Self::USTACK_VIRT_TOP,
|
|
|
|
));
|
|
|
|
|
2021-11-17 13:05:51 +02:00
|
|
|
drop(process_lock);
|
2021-10-20 16:32:07 +03:00
|
|
|
|
|
|
|
(*ctx).enter();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for Process {
|
|
|
|
fn drop(&mut self) {
|
2021-11-12 19:44:10 +02:00
|
|
|
debugln!("Dropping process {:?}", self.id());
|
2021-10-20 13:54:33 +03:00
|
|
|
}
|
|
|
|
}
|
2021-11-12 19:44:10 +02:00
|
|
|
|
|
|
|
/// 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)
|
|
|
|
}
|