//! 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>( proc: &ProcessImpl, f: F, ) -> T { let io = proc.io.lock(); f(io) } fn run_with_io_at< T, F: FnOnce(NodeRef, IrqSafeSpinlockGuard) -> Result, >( proc: &ProcessImpl, at: Option, f: F, ) -> Result { 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::(); 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::(); 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, len: usize, source: &MappingSource, ) -> Result { let thread = Thread::current(); let process = thread.process::(); let space = thread.address_space(); let len = len.page_align_up::(); 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::(code) } pub(crate) fn spawn_process(options: &SpawnOptions<'_>) -> Result { let thread = Thread::current(); let process = thread.process::(); 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::(); 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::(); 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) }); }); } process.set_session_id(process.id()); process.set_group_id(process.id()); Ok(()) } // I/O pub(crate) fn open( at: Option, path: &str, opts: OpenOptions, mode: FileMode, ) -> Result { let thread = Thread::current(); let process = thread.process::(); 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::(); 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 { let thread = Thread::current(); let process = thread.process::(); run_with_io(&process, |io| io.files.file(fd)?.write(buffer)) } pub(crate) fn read(fd: RawFd, buffer: &mut [u8]) -> Result { let thread = Thread::current(); let process = thread.process::(); run_with_io(&process, |io| io.files.file(fd)?.read(buffer)) } pub(crate) fn seek(fd: RawFd, pos: SeekFrom) -> Result { let thread = Thread::current(); let process = thread.process::(); run_with_io(&process, |io| io.files.file(fd)?.seek(pos)) } pub(crate) fn open_directory(at: Option, path: &str) -> Result { let thread = Thread::current(); let process = thread.process::(); 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], ) -> Result { let thread = Thread::current(); let process = thread.process::(); run_with_io(&process, |io| io.files.file(fd)?.read_dir(entries)) } pub(crate) fn create_directory( at: Option, path: &str, mode: FileMode, ) -> Result<(), Error> { let thread = Thread::current(); let process = thread.process::(); 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, _path: &str) -> Result<(), Error> { todo!() } pub(crate) fn remove(at: Option, path: &str) -> Result<(), Error> { let thread = Thread::current(); let process = thread.process::(); 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) -> Result { let thread = Thread::current(); let process = thread.process::(); 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, _path: &str, _update: &FileMetadataUpdate, ) -> Result<(), Error> { let thread = Thread::current(); let process = thread.process::(); run_with_io_at(&process, at, |_at, _io| { todo!(); }) } pub(crate) fn get_metadata( at: Option, path: &str, buffer: &mut MaybeUninit, follow: bool, ) -> Result<(), Error> { let thread = Thread::current(); let process = thread.process::(); 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::(); 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 { let thread = Thread::current(); let process = thread.process::(); 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 { let thread = Thread::current(); let process = thread.process::(); 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; 2], ) -> Result<(), Error> { let thread = Thread::current(); let process = thread.process::(); 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 { let thread = Thread::current(); let process = thread.process::(); 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 { let thread = Thread::current(); let process = thread.process::(); 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; 2]) -> Result<(), Error> { let thread = Thread::current(); let process = thread.process::(); 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, output: &mut Option<(RawFd, Result<(), Error>)>, ) -> Result<(), Error> { let thread = Thread::current(); let process = thread.process::(); 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::(); 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::(); 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, buf: &mut [u8], from: &mut MaybeUninit, ) -> Result<(), Error> { let thread = Thread::current(); let process = thread.process::(); 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, remote: &SocketAddr, ty: SocketType, local_result: &mut MaybeUninit, ) -> Result { assert!(socket_fd.is_none()); let thread = Thread::current(); let process = thread.process::(); 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 { let thread = Thread::current(); let process = thread.process::(); 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, ) -> Result { let thread = Thread::current(); let process = thread.process::(); 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, ) -> Result { let thread = Thread::current(); let process = thread.process::(); 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, ) -> Result { let thread = Thread::current(); let process = thread.process::(); 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::(); 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::(); 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 { 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 _ }