354 lines
11 KiB
Rust
354 lines
11 KiB
Rust
use core::{mem::MaybeUninit, num::NonZeroUsize, sync::atomic::AtomicU32, time::Duration};
|
|
|
|
use abi::{
|
|
error::Error,
|
|
mem::{MappingFlags, MappingSource},
|
|
process::{
|
|
options::ProcessOptionVariant, thread::ThreadOptionVariant, ExitCode, MutexOperation,
|
|
ProcessGroupId, ProcessId, ProcessWait, Signal, SpawnFlags, SpawnOption, SpawnOptions,
|
|
ThreadSpawnOptions, WaitFlags,
|
|
},
|
|
};
|
|
use alloc::sync::Arc;
|
|
use libk::{
|
|
block,
|
|
task::{binary::LoadOptions, process::Process, runtime, thread::Thread, ThreadId},
|
|
time::monotonic_time,
|
|
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,
|
|
flags: MappingFlags,
|
|
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())?
|
|
}
|
|
};
|
|
|
|
let mut attrs = MapAttributes::NON_GLOBAL | MapAttributes::USER_READ;
|
|
if flags.contains(MappingFlags::WRITE) {
|
|
attrs |= MapAttributes::USER_WRITE;
|
|
}
|
|
|
|
space.allocate(None, len, backing, attrs)
|
|
})
|
|
.inspect_err(|error| {
|
|
log::warn!("map_memory({len}) failed: {error:?}");
|
|
})
|
|
}
|
|
|
|
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();
|
|
|
|
let result = run_with_io(&process, |mut io| {
|
|
// let attach_debugger = options
|
|
// .optional
|
|
// .iter()
|
|
// .try_find_map::<_, Error, _>(|entry| {
|
|
// if let &SpawnOption::AttachDebug(fd) = entry {
|
|
// let channel = io.files.file(fd)?;
|
|
// let channel = channel.as_message_channel()?.clone();
|
|
|
|
// Ok(Some(channel))
|
|
// } else {
|
|
// Ok(None)
|
|
// }
|
|
// })?;
|
|
|
|
// Setup a new process from the file
|
|
let load_options = LoadOptions {
|
|
group_id: process.group_id(),
|
|
parent: Some(process.clone()),
|
|
path: options.program,
|
|
args: options.arguments,
|
|
envs: options.arguments,
|
|
single_step: false,
|
|
// single_step: attach_debugger.is_some(),
|
|
disable_aslr: options.flags.contains(SpawnFlags::DISABLE_ASLR),
|
|
};
|
|
let (child_process, child_main) = proc::load_binary(io.ioctx_mut(), &load_options)?;
|
|
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::MoveFile { source, child } => {
|
|
if let Ok(src_file) = io.files.take_file(source) {
|
|
child_io.files.set_file(child, src_file)?;
|
|
}
|
|
}
|
|
&SpawnOption::CopyFile { source, child } => {
|
|
if let Ok(src_file) = io.files.file(source) {
|
|
child_io.files.set_file(child, src_file.send()?)?;
|
|
}
|
|
}
|
|
&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
|
|
}
|
|
}) {
|
|
log::debug!("{} requested terminal {:?}", pid, fd);
|
|
let file = child_io.files.file(fd)?;
|
|
file.set_terminal_group(child_process.group_id())?;
|
|
|
|
if let Some(node) = file.node() {
|
|
child_process.set_session_terminal(node.clone());
|
|
}
|
|
}
|
|
|
|
drop(child_io);
|
|
|
|
child_main.enqueue();
|
|
// if let Some(debugger) = attach_debugger {
|
|
// child_main.attach_debugger(ThreadDebugger::new(debugger));
|
|
// } else {
|
|
// child_main.enqueue();
|
|
// }
|
|
|
|
Ok(pid as _)
|
|
});
|
|
|
|
result
|
|
}
|
|
|
|
pub(crate) fn wait_process(
|
|
wait: &ProcessWait,
|
|
status: &mut ExitCode,
|
|
flags: WaitFlags,
|
|
) -> Result<ProcessId, Error> {
|
|
let thread = Thread::current();
|
|
let process = thread.process();
|
|
match wait {
|
|
&ProcessWait::Process(id) => {
|
|
*status = process.wait_for_child(id, flags)?;
|
|
Ok(id)
|
|
}
|
|
ProcessWait::Group(_id) => todo!(),
|
|
ProcessWait::AnyChild => {
|
|
let (id, exit) = process.wait_for_any_child(flags)?;
|
|
*status = exit;
|
|
Ok(id)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate) fn get_pid() -> ProcessId {
|
|
let thread = Thread::current();
|
|
let process = thread.process();
|
|
process.id
|
|
}
|
|
|
|
pub(crate) fn get_tid() -> u32 {
|
|
let thread = Thread::current();
|
|
thread.id.as_user().try_into().unwrap()
|
|
}
|
|
|
|
pub(crate) fn nanosleep(
|
|
duration: &Duration,
|
|
remaining: &mut MaybeUninit<Duration>,
|
|
) -> Result<(), Error> {
|
|
let duration = *duration;
|
|
let now = monotonic_time();
|
|
let deadline = now + duration;
|
|
let result = block! { runtime::sleep_until(deadline).await };
|
|
match result {
|
|
Ok(()) => Ok(()),
|
|
Err(Error::Interrupted) => {
|
|
let now = monotonic_time();
|
|
let rem = deadline.checked_sub_time(&now).unwrap_or_default();
|
|
remaining.write(rem);
|
|
Err(Error::Interrupted)
|
|
}
|
|
Err(_) => unreachable!(),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn send_signal(pid: ProcessId, signal: Signal) -> Result<(), Error> {
|
|
// Debug is only issued by kernel for internal purposes of interrupting a thread when someone
|
|
// attaches to it
|
|
if signal == Signal::Debug {
|
|
return Err(Error::InvalidArgument);
|
|
}
|
|
let thread = Thread::current();
|
|
let target = Process::get(pid).ok_or(Error::DoesNotExist)?;
|
|
target.raise_signal(Some(thread.id), signal);
|
|
Ok(())
|
|
}
|
|
|
|
// TODO this would've been much simpler if physical pages behaved like Rc<...>'s and kernel could
|
|
// keep a copy of it, guaranteeing it cannot be freed and remapped into another process.
|
|
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::WaitWhileEqual(value, _timeout) => {
|
|
block! { mutex.wait_until(|v| v != value).await }?
|
|
}
|
|
&MutexOperation::WaitUntilEqual(value, _timeout) => {
|
|
block! { mutex.wait_until(|v| v == value).await }?
|
|
}
|
|
&MutexOperation::WaitWhileSet(mask, _timeout) => {
|
|
block! { mutex.wait_until(|v| v & mask != v).await }?
|
|
}
|
|
&MutexOperation::WaitUntilSet(mask, _timeout) => {
|
|
block! { mutex.wait_until(|v| v & mask == v).await }?
|
|
}
|
|
MutexOperation::Wake(limit) => {
|
|
mutex.wake(*limit as _);
|
|
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)?
|
|
}
|
|
|
|
pub(crate) fn get_thread_option(option: u32, buffer: &mut [u8]) -> Result<usize, Error> {
|
|
let option = ThreadOptionVariant::try_from(option)?;
|
|
let thread = Thread::current();
|
|
thread.get_option(option, buffer)
|
|
}
|
|
|
|
pub(crate) fn set_thread_option(option: u32, buffer: &[u8]) -> Result<(), Error> {
|
|
let option = ThreadOptionVariant::try_from(option)?;
|
|
let thread = Thread::current();
|
|
thread.set_option(option, buffer)
|
|
}
|
|
|
|
pub(crate) fn get_process_option(option: u32, buffer: &mut [u8]) -> Result<usize, Error> {
|
|
let option = ProcessOptionVariant::try_from(option)?;
|
|
let thread = Thread::current();
|
|
let process = thread.process();
|
|
process.get_option(option, buffer)
|
|
}
|
|
|
|
pub(crate) fn set_process_option(option: u32, buffer: &[u8]) -> Result<(), Error> {
|
|
let option = ProcessOptionVariant::try_from(option)?;
|
|
let thread = Thread::current();
|
|
let process = thread.process();
|
|
process.set_option(option, buffer)
|
|
}
|