yggdrasil/kernel/src/syscall/imp/sys_process.rs

265 lines
7.4 KiB
Rust

use core::{num::NonZeroUsize, sync::atomic::AtomicU32, time::Duration};
use abi::{
error::Error,
io::DeviceRequest,
mem::MappingSource,
process::{
ExitCode, MutexOperation, ProcessGroupId, ProcessId, Signal, SpawnOption, SpawnOptions,
ThreadSpawnOptions,
},
};
use alloc::sync::Arc;
use libk::{
block,
task::{debug::ThreadDebugger, process::Process, runtime, thread::Thread, ThreadId},
vfs::IoContext,
};
use libk_mm::{
process::VirtualRangeBacking,
table::{EntryLevelExt, MapAttributes},
};
use crate::{arch::L3, proc, syscall::run_with_io};
// Memory management
pub(crate) fn map_memory(
_hint: Option<NonZeroUsize>,
len: usize,
source: &MappingSource,
) -> Result<usize, Error> {
let thread = Thread::current();
let process = thread.process();
let space = thread.address_space();
let len = len.page_align_up::<L3>();
run_with_io(&process, |io| {
let backing = match source {
MappingSource::Anonymous => VirtualRangeBacking::anonymous(),
&MappingSource::File(fd, offset) => {
let file = io.files.file(fd)?;
VirtualRangeBacking::file(offset, file.clone())?
}
};
space.allocate(
None,
len,
backing,
MapAttributes::USER_WRITE | MapAttributes::USER_READ | MapAttributes::NON_GLOBAL,
)
})
}
pub(crate) fn unmap_memory(address: usize, len: usize) -> Result<(), Error> {
let thread = Thread::current();
let space = thread.address_space();
if len & 0xFFF != 0 {
return Err(Error::InvalidArgument);
}
unsafe {
space.unmap(address, len)?;
}
Ok(())
}
// Process/thread management
pub(crate) fn create_process_group() -> ProcessGroupId {
Process::create_group()
}
pub(crate) fn get_process_group_id() -> ProcessGroupId {
let thread = Thread::current();
let process = thread.process();
process.group_id()
}
pub(crate) fn exit_process(code: ExitCode) -> ! {
let thread = Thread::current();
thread.exit_process(code)
}
pub(crate) fn spawn_process(options: &SpawnOptions<'_>) -> Result<ProcessId, Error> {
let thread = Thread::current();
let process = thread.process();
run_with_io(&process, |mut io| {
let mut attach_debugger = None;
// TODO try_find_map()
for entry in options.optional {
if let &SpawnOption::AttachDebug(fd) = entry {
let channel = io.files.file(fd)?;
let channel = channel.as_message_channel()?.clone();
attach_debugger = Some(channel);
}
}
// Setup a new process from the file
let (child_process, child_main) = proc::load_binary(
io.ioctx_mut(),
process.group_id(),
Some(Arc::downgrade(&process)),
options.program,
options.arguments,
options.environment,
)?;
let pid = child_process.id;
// Inherit group and session from the creator
child_process.inherit(&process)?;
// Inherit root from the creator
// let child_ioctx = IoContext::new(io.ioctx().root().clone());
let child_ioctx = IoContext::inherit(io.ioctx_mut());
let mut child_io = child_process.io.lock();
child_io.set_ioctx(child_ioctx);
for opt in options.optional {
match opt {
&SpawnOption::InheritFile { source, child } => {
if let Ok(src_file) = io.files.file(source) {
child_io.files.set_file(child, src_file.clone())?;
}
}
&SpawnOption::SetProcessGroup(pgroup) => {
let pgroup = if pgroup.into_raw() == 0 {
todo!()
} else {
pgroup
};
child_process.set_group_id(pgroup);
}
_ => (),
}
}
if let Some(fd) = options.optional.iter().find_map(|item| {
if let &SpawnOption::GainTerminal(fd) = item {
Some(fd)
} else {
None
}
}) {
debugln!("{} requested terminal {:?}", pid, fd);
let file = child_io.files.file(fd)?;
// let node = file.node().ok_or(Error::InvalidFile)?;
let mut req = DeviceRequest::SetTerminalGroup(child_process.group_id());
file.device_request(&mut req)?;
if let Some(node) = file.node() {
child_process.set_session_terminal(node.clone());
}
// node.device_request(&mut req)?;
}
drop(child_io);
if let Some(debugger) = attach_debugger {
child_main.attach_debugger(ThreadDebugger::new(debugger));
}
child_main.enqueue();
Ok(pid as _)
})
}
pub(crate) fn wait_process(pid: ProcessId, status: &mut ExitCode) -> Result<(), Error> {
let target = Process::get(pid).ok_or(Error::DoesNotExist)?;
*status = block!(target.wait_for_exit().await)?;
Ok(())
}
pub(crate) fn get_pid() -> ProcessId {
let thread = Thread::current();
let process = thread.process();
process.id
}
pub(crate) fn nanosleep(duration: &Duration) -> Result<(), Error> {
block! {
runtime::sleep(*duration).await
}
}
pub(crate) fn set_signal_entry(ip: usize, sp: usize) {
let thread = Thread::current();
thread.set_signal_entry(ip, sp);
}
pub(crate) fn send_signal(pid: ProcessId, signal: Signal) -> Result<(), Error> {
let target = Process::get(pid).ok_or(Error::DoesNotExist)?;
target.raise_signal(signal);
Ok(())
}
pub(crate) fn mutex(mutex: &AtomicU32, op: &MutexOperation) -> Result<(), Error> {
let thread = Thread::current();
let process = thread.process();
let mutex = process.get_or_insert_mutex((mutex as *const AtomicU32).addr());
match op {
&MutexOperation::Wait(value, _timeout) => block! { mutex.wait(value).await }.unwrap(),
MutexOperation::Wake => mutex.wake(),
MutexOperation::WakeAll => mutex.wake_all(),
}
Ok(())
}
pub(crate) fn start_session() -> Result<(), Error> {
let thread = Thread::current();
let process = thread.process();
let session_terminal = process.clear_session_terminal();
if let Some(ctty) = session_terminal {
// Drop all FDs referring to the old session terminal
run_with_io(&process, |mut io| {
io.files.retain(|_, f| {
f.node()
.map(|node| !Arc::ptr_eq(node, &ctty))
.unwrap_or(true)
});
});
}
let group_id = Process::create_group();
process.set_session_id(process.id);
process.set_group_id(group_id);
Ok(())
}
pub(crate) fn spawn_thread(options: &ThreadSpawnOptions) -> Result<u32, Error> {
let thread = Thread::current();
let process = thread.process();
process
.spawn_thread(options)
.map(|tid| tid.as_user() as u32)
}
pub(crate) fn exit_thread() -> ! {
let thread = Thread::current();
// TODO exit codes are not supported in here
thread.exit(ExitCode::SUCCESS)
}
pub(crate) fn wait_thread(id: u32) -> Result<(), Error> {
let tid = ThreadId::User(id as u64);
let this_thread = Thread::current();
let process = this_thread.process();
block!(process.wait_for_thread(tid).await)??;
Ok(())
}