265 lines
7.4 KiB
Rust
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(())
|
|
}
|