869 lines
26 KiB
Rust

//! System function call handlers
use abi::{error::Error, io::RawFd, SyscallFunction};
use libk_util::sync::IrqSafeSpinlockGuard;
use vfs::NodeRef;
use crate::{proc::io::ProcessIoImpl, task::process::ProcessImpl};
mod arg;
fn run_with_io<T, F: FnOnce(IrqSafeSpinlockGuard<ProcessIoImpl>) -> T>(
proc: &ProcessImpl,
f: F,
) -> T {
let io = proc.io.lock();
f(io)
}
fn run_with_io_at<
T,
F: FnOnce(NodeRef, IrqSafeSpinlockGuard<ProcessIoImpl>) -> Result<T, Error>,
>(
proc: &ProcessImpl,
at: Option<RawFd>,
f: F,
) -> Result<T, Error> {
let mut io = proc.io.lock();
let at = at
.map(|fd| {
io.files
.file(fd)
.and_then(|f| f.node().ok_or(Error::InvalidFile).cloned())
})
.transpose()?
// at = None
.unwrap_or_else(|| io.ioctx_mut().cwd().clone());
f(at, io)
}
mod impls {
pub(crate) use abi::{
error::Error,
io::{
DeviceRequest, DirectoryEntry, FileAttr, FileMetadataUpdate, FileMode,
MessageDestination, MountOptions, OpenOptions, PollControl, RawFd,
ReceivedMessageMetadata, SeekFrom, SentMessage, TerminalOptions, TerminalSize,
UnmountOptions,
},
mem::MappingSource,
net::{SocketOption, SocketType},
process::{ExitCode, MutexOperation, Signal, SignalEntryData, SpawnOptions},
system::SystemInfo,
};
use abi::{
io::ChannelPublisherId,
process::{ExecveOptions, ProcessId, SpawnOption},
};
use alloc::{boxed::Box, sync::Arc};
use libk::{block, runtime};
use vfs::{File, IoContext, MessagePayload, Read, Seek, Write};
use ygg_driver_net_core::socket::{RawSocket, TcpListener, TcpSocket, UdpSocket};
use core::{
mem::MaybeUninit, net::SocketAddr, num::NonZeroUsize, sync::atomic::AtomicU32,
time::Duration,
};
use libk_mm::{
phys,
process::VirtualRangeBacking,
table::{EntryLevelExt, MapAttributes},
};
use libk_thread::{
process::{Process, ProcessManager},
thread::Thread,
};
use crate::{
arch::L3,
debug::LogLevel,
fs,
proc::{self, random},
task::process::ProcessManagerImpl,
};
use super::{run_with_io, run_with_io_at};
// Misc
pub(crate) fn debug_trace(message: &str) {
let thread = Thread::current();
let process = thread.process::<ProcessManagerImpl>();
log_print_raw!(
LogLevel::Debug,
"[{}:{}] TRACE: {}\n",
process.id(),
thread.id,
message
);
}
pub(crate) fn get_random(buffer: &mut [u8]) {
random::read(buffer);
}
pub(crate) fn mount(options: &MountOptions<'_>) -> Result<(), Error> {
let thread = Thread::current();
let process = thread.process::<ProcessManagerImpl>();
run_with_io(&process, |mut io| {
let fs_root = fs::create_filesystem(options)?;
io.ioctx_mut().mount(options.target, fs_root)?;
Ok(())
})
}
pub(crate) fn unmount(_options: &UnmountOptions) -> Result<(), Error> {
todo!()
}
// 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::<ProcessManagerImpl>();
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 {
todo!();
}
unsafe {
space.unmap(address, len)?;
}
Ok(())
}
pub(crate) fn get_system_info(element: &mut SystemInfo) -> Result<(), Error> {
match element {
SystemInfo::MemoryStats(stats) => {
*stats = phys::stats();
Ok(())
}
}
}
// Process/thread management
pub(crate) fn exit_process(code: ExitCode) -> ! {
let thread = Thread::current();
thread.exit_process::<ProcessManagerImpl>(code)
}
pub(crate) fn spawn_process(options: &SpawnOptions<'_>) -> Result<ProcessId, Error> {
let thread = Thread::current();
let process = thread.process::<ProcessManagerImpl>();
run_with_io(&process, |mut io| {
// Setup a new process from the file
let (child_process, child_main) = proc::load_binary(
io.ioctx_mut(),
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 {
child_process.id()
} 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);
child_main.enqueue();
Ok(pid as _)
})
}
pub(crate) fn wait_process(pid: ProcessId, status: &mut ExitCode) -> Result<(), Error> {
let target = ProcessManagerImpl::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::<ProcessManagerImpl>();
process.id()
}
pub(crate) fn nanosleep(duration: &Duration) {
block! {
runtime::sleep(*duration).await
}
.unwrap();
}
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 = ProcessManagerImpl::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::<ProcessManagerImpl>();
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::<ProcessManagerImpl>();
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)
});
});
}
process.set_session_id(process.id());
process.set_group_id(process.id());
Ok(())
}
// I/O
pub(crate) fn open(
at: Option<RawFd>,
path: &str,
opts: OpenOptions,
mode: FileMode,
) -> Result<RawFd, Error> {
let thread = Thread::current();
let process = thread.process::<ProcessManagerImpl>();
run_with_io_at(&process, at, |at, mut io| {
let file = io.ioctx_mut().open(Some(at), path, opts, mode)?;
// TODO NO_CTTY?
if process.session_terminal().is_none()
&& let Some(node) = file.node()
&& node.is_terminal()
{
debugln!("Session terminal set for #{}: {}", process.id(), path);
process.set_session_terminal(node.clone());
}
let fd = io.files.place_file(file, true)?;
Ok(fd)
})
}
pub(crate) fn close(fd: RawFd) -> Result<(), Error> {
let thread = Thread::current();
let process = thread.process::<ProcessManagerImpl>();
run_with_io(&process, |mut io| {
let res = io.files.close_file(fd);
if res == Err(Error::InvalidFile) {
warnln!("Double close of fd {:?} in process {}", fd, process.id());
}
res
})
}
pub(crate) fn write(fd: RawFd, buffer: &[u8]) -> Result<usize, Error> {
let thread = Thread::current();
let process = thread.process::<ProcessManagerImpl>();
run_with_io(&process, |io| io.files.file(fd)?.write(buffer))
}
pub(crate) fn read(fd: RawFd, buffer: &mut [u8]) -> Result<usize, Error> {
let thread = Thread::current();
let process = thread.process::<ProcessManagerImpl>();
run_with_io(&process, |io| io.files.file(fd)?.read(buffer))
}
pub(crate) fn seek(fd: RawFd, pos: SeekFrom) -> Result<u64, Error> {
let thread = Thread::current();
let process = thread.process::<ProcessManagerImpl>();
run_with_io(&process, |io| io.files.file(fd)?.seek(pos))
}
pub(crate) fn open_directory(at: Option<RawFd>, path: &str) -> Result<RawFd, Error> {
let thread = Thread::current();
let process = thread.process::<ProcessManagerImpl>();
run_with_io_at(&process, at, |at, mut io| {
let node = io.ioctx_mut().find(Some(at), path, true, true)?;
let access = io.ioctx_mut().check_access(vfs::Action::Read, &node)?;
let file = node.open_directory(access)?;
let fd = io.files.place_file(file, true)?;
Ok(fd)
})
}
pub(crate) fn read_directory_entries(
fd: RawFd,
entries: &mut [MaybeUninit<DirectoryEntry>],
) -> Result<usize, Error> {
let thread = Thread::current();
let process = thread.process::<ProcessManagerImpl>();
run_with_io(&process, |io| io.files.file(fd)?.read_dir(entries))
}
pub(crate) fn create_directory(
at: Option<RawFd>,
path: &str,
mode: FileMode,
) -> Result<(), Error> {
let thread = Thread::current();
let process = thread.process::<ProcessManagerImpl>();
run_with_io_at(&process, at, |at, mut io| {
io.ioctx_mut().create_directory(Some(at), path, mode)?;
Ok(())
})
}
pub(crate) fn remove_directory(_at: Option<RawFd>, _path: &str) -> Result<(), Error> {
todo!()
}
pub(crate) fn remove(at: Option<RawFd>, path: &str) -> Result<(), Error> {
let thread = Thread::current();
let process = thread.process::<ProcessManagerImpl>();
run_with_io_at(&process, at, |at, mut io| {
io.ioctx_mut().remove_file(Some(at), path)?;
Ok(())
})
}
pub(crate) fn clone_fd(source_fd: RawFd, target_fd: Option<RawFd>) -> Result<RawFd, Error> {
let thread = Thread::current();
let process = thread.process::<ProcessManagerImpl>();
run_with_io(&process, |mut io| {
let file = io.files.file(source_fd)?.clone();
let fd = match target_fd {
Some(target_fd) => {
io.files.set_file(target_fd, file)?;
target_fd
}
None => io.files.place_file(file, true)?,
};
Ok(fd)
})
}
pub(crate) fn update_metadata(
at: Option<RawFd>,
_path: &str,
_update: &FileMetadataUpdate,
) -> Result<(), Error> {
let thread = Thread::current();
let process = thread.process::<ProcessManagerImpl>();
run_with_io_at(&process, at, |_at, _io| {
todo!();
})
}
pub(crate) fn get_metadata(
at: Option<RawFd>,
path: &str,
buffer: &mut MaybeUninit<FileAttr>,
follow: bool,
) -> Result<(), Error> {
let thread = Thread::current();
let process = thread.process::<ProcessManagerImpl>();
run_with_io_at(&process, at, |at, mut io| {
let node = if path.is_empty() {
at
// at.ok_or(Error::InvalidArgument)?
} else {
io.ioctx_mut().find(Some(at), path, follow, true)?
};
let metadata = node.metadata()?;
let size = node.size()?;
buffer.write(FileAttr {
size,
ty: node.ty(),
mode: metadata.mode,
uid: metadata.uid,
gid: metadata.gid,
});
Ok(())
})
}
pub(crate) fn device_request(fd: RawFd, req: &mut DeviceRequest) -> Result<(), Error> {
let thread = Thread::current();
let process = thread.process::<ProcessManagerImpl>();
run_with_io(&process, |io| io.files.file(fd)?.device_request(req))
}
// Misc I/O
pub(crate) fn open_channel(name: &str, subscribe: bool) -> Result<RawFd, Error> {
let thread = Thread::current();
let process = thread.process::<ProcessManagerImpl>();
run_with_io(&process, |mut io| {
let file = File::new_message_channel(name, subscribe);
let fd = io.files.place_file(file, true)?;
Ok(fd)
})
}
pub(crate) fn create_timer(repeat: bool) -> Result<RawFd, Error> {
let thread = Thread::current();
let process = thread.process::<ProcessManagerImpl>();
run_with_io(&process, |mut io| {
let file = File::new_timer(repeat);
let fd = io.files.place_file(file, true)?;
Ok(fd)
})
}
pub(crate) fn create_pty(
options: &TerminalOptions,
size: &TerminalSize,
output: &mut [MaybeUninit<RawFd>; 2],
) -> Result<(), Error> {
let thread = Thread::current();
let process = thread.process::<ProcessManagerImpl>();
run_with_io(&process, |mut io| {
let (master, slave) = File::new_pseudo_terminal(*options, *size)?;
let master_fd = io.files.place_file(master, true)?;
let slave_fd = io.files.place_file(slave, true)?;
output[0].write(master_fd);
output[1].write(slave_fd);
Ok(())
})
}
pub(crate) fn create_shared_memory(size: usize) -> Result<RawFd, Error> {
let thread = Thread::current();
let process = thread.process::<ProcessManagerImpl>();
run_with_io(&process, |mut io| {
let file = File::new_shared_memory(size)?;
let fd = io.files.place_file(file, true)?;
Ok(fd)
})
}
pub(crate) fn create_poll_channel() -> Result<RawFd, Error> {
let thread = Thread::current();
let process = thread.process::<ProcessManagerImpl>();
run_with_io(&process, |mut io| {
let poll = File::new_poll_channel();
let fd = io.files.place_file(poll, true)?;
Ok(fd)
})
}
pub(crate) fn create_pipe(ends: &mut [MaybeUninit<RawFd>; 2]) -> Result<(), Error> {
let thread = Thread::current();
let process = thread.process::<ProcessManagerImpl>();
run_with_io(&process, |mut io| {
let (read, write) = File::new_pipe_pair(256);
let read_fd = io.files.place_file(read, true)?;
let write_fd = io.files.place_file(write, true)?;
ends[0].write(read_fd);
ends[1].write(write_fd);
Ok(())
})
}
pub(crate) fn poll_channel_wait(
poll_fd: RawFd,
timeout: &Option<Duration>,
output: &mut Option<(RawFd, Result<(), Error>)>,
) -> Result<(), Error> {
let thread = Thread::current();
let process = thread.process::<ProcessManagerImpl>();
run_with_io(&process, |io| {
let poll_file = io.files.file(poll_fd)?;
let poll = poll_file.as_poll_channel()?;
*output = block! {
poll.wait(*timeout).await
}?;
Ok(())
})
}
pub(crate) fn poll_channel_control(
poll_fd: RawFd,
control: PollControl,
fd: RawFd,
) -> Result<(), Error> {
let thread = Thread::current();
let process = thread.process::<ProcessManagerImpl>();
run_with_io(&process, |io| {
let poll_file = io.files.file(poll_fd)?;
let poll = poll_file.as_poll_channel()?;
match control {
PollControl::AddFd => {
let polled_file = io.files.file(fd)?.clone();
poll.add(fd, polled_file);
}
}
Ok(())
})
}
pub(crate) fn send_message(
fd: RawFd,
message: &SentMessage<'_>,
destination: MessageDestination,
) -> Result<(), Error> {
let thread = Thread::current();
let process = thread.process::<ProcessManagerImpl>();
run_with_io(&process, |io| {
let file = io.files.file(fd)?;
let channel = file.as_message_channel()?;
match message {
&SentMessage::File(fd) => {
let sent_file = io.files.file(fd)?;
channel.send_message(MessagePayload::File(sent_file.clone()), destination)?;
}
&SentMessage::Data(data) => {
channel.send_message(MessagePayload::Data(Box::from(data)), destination)?;
}
}
Ok(())
})
}
pub(crate) fn receive_message(
fd: RawFd,
metadata: &mut MaybeUninit<ReceivedMessageMetadata>,
buf: &mut [u8],
from: &mut MaybeUninit<ChannelPublisherId>,
) -> Result<(), Error> {
let thread = Thread::current();
let process = thread.process::<ProcessManagerImpl>();
run_with_io(&process, |mut io| {
let file = io.files.file(fd)?;
let channel = file.as_message_channel()?;
let message = channel.receive_message()?;
from.write(message.source);
match &message.payload {
MessagePayload::Data(data) => {
// TODO allow truncated messages?
let len = data.len();
if buf.len() < len {
return Err(Error::MissingData);
}
metadata.write(ReceivedMessageMetadata::Data(len));
buf[..len].copy_from_slice(data);
Ok(())
}
MessagePayload::File(file) => {
let fd = io.files.place_file(file.clone(), true)?;
metadata.write(ReceivedMessageMetadata::File(fd));
Ok(())
}
}
})
}
// Network
pub(crate) fn connect_socket(
socket_fd: Option<RawFd>,
remote: &SocketAddr,
ty: SocketType,
local_result: &mut MaybeUninit<SocketAddr>,
) -> Result<RawFd, Error> {
assert!(socket_fd.is_none());
let thread = Thread::current();
let process = thread.process::<ProcessManagerImpl>();
run_with_io(&process, |mut io| {
let (local, file) = match ty {
SocketType::TcpStream => {
let (local, socket) = TcpSocket::connect((*remote).into())?;
(local, File::from_stream_socket(socket))
}
_ => return Err(Error::InvalidArgument),
};
let fd = io.files.place_file(file, true)?;
local_result.write(local.into());
Ok(fd)
})
}
pub(crate) fn bind_socket(listen: &SocketAddr, ty: SocketType) -> Result<RawFd, Error> {
let thread = Thread::current();
let process = thread.process::<ProcessManagerImpl>();
run_with_io(&process, |mut io| {
let file = match ty {
SocketType::UdpPacket => {
File::from_packet_socket(UdpSocket::bind((*listen).into())?)
}
SocketType::RawPacket => File::from_packet_socket(RawSocket::bind()?),
SocketType::TcpStream => {
File::from_listener_socket(TcpListener::bind((*listen).into())?)
}
};
let fd = io.files.place_file(file, true)?;
Ok(fd)
})
}
pub(crate) fn accept(
socket_fd: RawFd,
remote_result: &mut MaybeUninit<SocketAddr>,
) -> Result<RawFd, Error> {
let thread = Thread::current();
let process = thread.process::<ProcessManagerImpl>();
run_with_io(&process, |mut io| {
let file = io.files.file(socket_fd)?;
let mut remote = MaybeUninit::uninit();
let accepted_file = file.accept(&mut remote)?;
let accepted_fd = io.files.place_file(accepted_file, true)?;
unsafe {
remote_result.write(remote.assume_init().into());
}
Ok(accepted_fd)
})
}
pub(crate) fn send_to(
socket_fd: RawFd,
buffer: &[u8],
recepient: &Option<SocketAddr>,
) -> Result<usize, Error> {
let thread = Thread::current();
let process = thread.process::<ProcessManagerImpl>();
run_with_io(&process, |io| {
let file = io.files.file(socket_fd)?;
file.send_to(buffer, recepient.map(Into::into))
})
}
pub(crate) fn receive_from(
socket_fd: RawFd,
buffer: &mut [u8],
remote_result: &mut MaybeUninit<SocketAddr>,
) -> Result<usize, Error> {
let thread = Thread::current();
let process = thread.process::<ProcessManagerImpl>();
run_with_io(&process, |io| {
let file = io.files.file(socket_fd)?;
let mut remote = MaybeUninit::uninit();
let len = file.receive_from(buffer, &mut remote)?;
remote_result.write(unsafe { remote.assume_init() }.into());
Ok(len)
})
}
pub(crate) fn set_socket_option(socket_fd: RawFd, option: &SocketOption) -> Result<(), Error> {
let thread = Thread::current();
let process = thread.process::<ProcessManagerImpl>();
run_with_io(&process, |io| {
let file = io.files.file(socket_fd)?;
let socket = file.as_socket()?;
socket.set_option(option)
})
}
pub(crate) fn get_socket_option(
socket_fd: RawFd,
option: &mut SocketOption,
) -> Result<(), Error> {
let thread = Thread::current();
let process = thread.process::<ProcessManagerImpl>();
run_with_io(&process, |io| {
let file = io.files.file(socket_fd)?;
let socket = file.as_socket()?;
socket.get_option(option)
})
}
// Handled outside
pub(crate) fn exit_signal(_frame: &SignalEntryData) -> ! {
unreachable!()
}
pub(crate) fn fork() -> Result<ProcessId, Error> {
unreachable!()
}
pub(crate) fn execve(_options: &ExecveOptions<'_>) -> Result<(), Error> {
unreachable!()
}
}
mod generated {
#![allow(unreachable_code)]
use abi::{
io::ChannelPublisherId,
process::{ExecveOptions, ProcessId},
SyscallFunction,
};
use abi_lib::SyscallRegister;
use super::{
arg,
impls::{self, *},
};
include!(concat!(env!("OUT_DIR"), "/generated_dispatcher.rs"));
}
/// Entrypoint for system calls that takes raw argument values
pub fn raw_syscall_handler(func: u64, args: &[u64]) -> u64 {
let Ok(func) = SyscallFunction::try_from(func as usize) else {
todo!("Undefined syscall: {}", func);
};
let args = unsafe { core::mem::transmute(args) };
let result = generated::handle_syscall(func, args);
let value = match result {
Ok(value) => value,
Err(e) => (-(e as u32 as isize)) as usize,
};
value as _
}