diff --git a/driver/block/core/src/lib.rs b/driver/block/core/src/lib.rs index 0ddaf8d6..5bed61ad 100644 --- a/driver/block/core/src/lib.rs +++ b/driver/block/core/src/lib.rs @@ -4,7 +4,7 @@ extern crate alloc; use core::task::{Context, Poll}; -use kernel_util::mem::{address::PhysicalAddress, PageProvider}; +use kernel_util::mem::PageProvider; use yggdrasil_abi::{error::Error, io::DeviceRequest}; pub mod device; diff --git a/lib/device-api/src/input.rs b/lib/device-api/src/input.rs deleted file mode 100644 index 907357ac..00000000 --- a/lib/device-api/src/input.rs +++ /dev/null @@ -1,3 +0,0 @@ -use yggdrasil_abi::error::Error; - -use crate::Device; diff --git a/lib/device-api/src/lib.rs b/lib/device-api/src/lib.rs index 30357982..7225486b 100644 --- a/lib/device-api/src/lib.rs +++ b/lib/device-api/src/lib.rs @@ -5,7 +5,6 @@ extern crate alloc; pub mod bus; pub mod device; -pub mod input; pub mod interrupt; pub mod manager; pub mod serial; diff --git a/lib/kernel-util/src/api.rs b/lib/kernel-util/src/api.rs index 2b5acb2b..fae48fe4 100644 --- a/lib/kernel-util/src/api.rs +++ b/lib/kernel-util/src/api.rs @@ -2,7 +2,10 @@ use core::time::Duration; use alloc::{string::String, sync::Arc}; use device_api::interrupt::MessageInterruptController; -use yggdrasil_abi::{error::Error, process::ExitCode}; +use yggdrasil_abi::{ + error::Error, + process::{ExitCode, Signal}, +}; use crate::{ mem::{address::PhysicalAddress, device::RawDeviceMemoryMapping}, @@ -46,4 +49,6 @@ extern "Rust" { pub fn __monotonic_timestamp() -> Result; pub fn __message_interrupt_controller() -> &'static dyn MessageInterruptController; + + pub fn __signal_process_group(group_id: u32, signal: Signal); } diff --git a/lib/kernel-util/src/lib.rs b/lib/kernel-util/src/lib.rs index 73f29421..c3627a67 100644 --- a/lib/kernel-util/src/lib.rs +++ b/lib/kernel-util/src/lib.rs @@ -8,10 +8,13 @@ strict_provenance, never_type, let_chains, - allocator_api + allocator_api, + maybe_uninit_uninit_array, + const_maybe_uninit_uninit_array )] use device_api::interrupt::MessageInterruptController; +use yggdrasil_abi::process::Signal; extern crate alloc; @@ -38,6 +41,11 @@ pub fn cpu_count() -> usize { unsafe { api::__cpu_count() } } +#[inline] +pub fn signal_process_group(group_id: u32, signal: Signal) { + unsafe { api::__signal_process_group(group_id, signal) } +} + #[repr(C)] pub struct AlignedTo { pub align: [Align; 0], diff --git a/lib/kernel-util/src/runtime/executor.rs b/lib/kernel-util/src/runtime/executor.rs index 864f2147..f4ff9ac2 100644 --- a/lib/kernel-util/src/runtime/executor.rs +++ b/lib/kernel-util/src/runtime/executor.rs @@ -1,17 +1,10 @@ use core::task::{Context, Poll}; use alloc::{boxed::Box, format, sync::Arc}; -use futures_util::{ - task::{waker_ref, ArcWake, WakerRef}, - Future, -}; +use futures_util::{task::waker_ref, Future}; use yggdrasil_abi::error::Error; -use crate::{ - api::__cpu_index, - cpu_index, - thread::{CurrentThread, Thread}, -}; +use crate::thread::Thread; use super::{ task::{Task, Termination}, diff --git a/lib/kernel-util/src/sync/spin_rwlock.rs b/lib/kernel-util/src/sync/spin_rwlock.rs index 64e6f368..5ed6feda 100644 --- a/lib/kernel-util/src/sync/spin_rwlock.rs +++ b/lib/kernel-util/src/sync/spin_rwlock.rs @@ -4,9 +4,7 @@ use core::{ sync::atomic::{AtomicUsize, Ordering}, }; -use yggdrasil_abi::error::Error; - -use super::{IrqGuard, LockMethod}; +use super::IrqGuard; struct RwLockInner { value: AtomicUsize, @@ -19,12 +17,12 @@ pub struct IrqSafeRwLock { pub struct IrqSafeRwLockReadGuard<'a, T> { lock: &'a IrqSafeRwLock, - guard: IrqGuard, + _guard: IrqGuard, } pub struct IrqSafeRwLockWriteGuard<'a, T> { lock: &'a IrqSafeRwLock, - guard: IrqGuard, + _guard: IrqGuard, } impl RwLockInner { @@ -106,13 +104,19 @@ impl IrqSafeRwLock { pub fn read(&self) -> IrqSafeRwLockReadGuard { let guard = IrqGuard::acquire(); self.inner.acquire_read(); - IrqSafeRwLockReadGuard { lock: self, guard } + IrqSafeRwLockReadGuard { + lock: self, + _guard: guard, + } } pub fn write(&self) -> IrqSafeRwLockWriteGuard { let guard = IrqGuard::acquire(); self.inner.acquire_write(); - IrqSafeRwLockWriteGuard { lock: self, guard } + IrqSafeRwLockWriteGuard { + lock: self, + _guard: guard, + } } unsafe fn release_read(&self) { diff --git a/lib/kernel-util/src/util.rs b/lib/kernel-util/src/util.rs index c5abb833..252ac535 100644 --- a/lib/kernel-util/src/util.rs +++ b/lib/kernel-util/src/util.rs @@ -7,6 +7,8 @@ use core::{ sync::atomic::{AtomicUsize, Ordering}, }; +pub mod ring; + pub enum ConstAssert {} pub trait IsTrue {} diff --git a/src/util/ring.rs b/lib/kernel-util/src/util/ring.rs similarity index 89% rename from src/util/ring.rs rename to lib/kernel-util/src/util/ring.rs index b8d9e1dc..dd371e53 100644 --- a/src/util/ring.rs +++ b/lib/kernel-util/src/util/ring.rs @@ -63,7 +63,11 @@ impl RingBuffer { false } - /// Reads a single value from the ring without checking if it's empty + /// Reads a single value from the ring without checking if it's empty. + /// + /// # Safety + /// + /// The caller must perform the necessary checks to avoid reading beyond the write head. #[inline] pub unsafe fn read_single_unchecked(&mut self) -> T where @@ -74,7 +78,11 @@ impl RingBuffer { res } - /// Reads all entries available from `pos` to the write head + /// Reads all entries available from `pos` to the write head. + /// + /// # Safety + /// + /// The caller must perform the necessary checks to avoid reading beyond the write head. pub unsafe fn read_all_static(&mut self, pos: usize, buffer: &mut [T]) -> usize where T: Copy, diff --git a/lib/vfs/src/channel.rs b/lib/vfs/src/channel.rs index e86604fc..51f4ff95 100644 --- a/lib/vfs/src/channel.rs +++ b/lib/vfs/src/channel.rs @@ -15,10 +15,7 @@ use kernel_util::{ block, sync::{mutex::Mutex, IrqSafeSpinlock, LockMethod}, }; -use yggdrasil_abi::{ - error::Error, - io::{MessageDestination, ReceivedMessageMetadata}, -}; +use yggdrasil_abi::{error::Error, io::MessageDestination}; use crate::{FileReadiness, FileRef}; diff --git a/lib/vfs/src/file/mod.rs b/lib/vfs/src/file/mod.rs index 44b0efc0..d79aced8 100644 --- a/lib/vfs/src/file/mod.rs +++ b/lib/vfs/src/file/mod.rs @@ -6,7 +6,6 @@ use core::{ }; use alloc::{ - boxed::Box, collections::{btree_map::Entry, BTreeMap}, sync::Arc, }; @@ -16,7 +15,9 @@ use kernel_util::{ }; use yggdrasil_abi::{ error::Error, - io::{DirectoryEntry, OpenOptions, RawFd, SeekFrom}, + io::{ + DeviceRequest, DirectoryEntry, OpenOptions, RawFd, SeekFrom, TerminalOptions, TerminalSize, + }, }; use crate::{ @@ -24,7 +25,7 @@ use crate::{ device::{BlockDeviceWrapper, CharDeviceWrapper}, node::NodeRef, traits::{Read, Seek, Write}, - FdPoll, FileReadiness, SharedMemory, + FdPoll, FileReadiness, PseudoTerminal, PseudoTerminalMaster, PseudoTerminalSlave, SharedMemory, }; use self::{ @@ -61,10 +62,13 @@ pub enum File { Regular(RegularFile), Block(BlockFile), Char(CharFile), + AnonymousPipe(PipeEnd), Poll(FdPoll), Channel(ChannelDescriptor), SharedMemory(Arc), + PtySlave(PseudoTerminalSlave), + PtyMaster(PseudoTerminalMaster), } /// Contains a per-process fd -> FileRef map @@ -99,6 +103,18 @@ impl File { Ok(Arc::new(Self::SharedMemory(Arc::new(shm)))) } + /// Creates a pair of PTY master/slave + pub fn new_pseudo_terminal( + config: TerminalOptions, + size: TerminalSize, + ) -> (Arc, Arc) { + let (master, slave) = PseudoTerminal::new(config, size); + ( + Arc::new(Self::PtyMaster(master)), + Arc::new(Self::PtySlave(slave)), + ) + } + pub(crate) fn directory(node: NodeRef, position: DirectoryOpenPosition) -> Arc { let position = IrqSafeSpinlock::new(position.into()); Arc::new(Self::Directory(DirectoryFile { node, position })) @@ -202,11 +218,25 @@ impl File { match self { Self::Char(f) => f.device.0.poll_read(cx), Self::Channel(ch) => ch.poll_read(cx), + Self::Poll(ch) => ch.poll_read(cx), + Self::PtyMaster(f) => f.poll_read(cx), + Self::PtySlave(f) => f.poll_read(cx), // Polling not implemented, return ready immediately (XXX ?) _ => Poll::Ready(Ok(())), } } + /// Performs a device-specific request + pub fn device_request(&self, req: &mut DeviceRequest) -> Result<(), Error> { + match self { + Self::Char(f) => f.device.0.device_request(req), + Self::Block(f) => f.device.0.device_request(req), + Self::PtySlave(f) => f.device_request(req), + Self::PtyMaster(f) => f.device_request(req), + _ => Err(Error::InvalidOperation), + } + } + /// Interprets the file as a poll channel pub fn as_poll_channel(&self) -> Result<&FdPoll, Error> { if let Self::Poll(poll) = self { @@ -251,6 +281,8 @@ impl Read for File { Self::Block(file) => file.read(buf), Self::Char(file) => file.read(buf), Self::AnonymousPipe(pipe) => pipe.read(buf), + Self::PtySlave(pt) => pt.read(buf), + Self::PtyMaster(pt) => pt.read(buf), // TODO maybe allow reading FDs from poll channels as if they were regular streams? Self::Poll(_) => Err(Error::InvalidOperation), // TODO maybe allow reading messages from Channels? @@ -268,6 +300,8 @@ impl Write for File { Self::Block(file) => file.write(buf), Self::Char(file) => file.write(buf), Self::AnonymousPipe(pipe) => pipe.write(buf), + Self::PtySlave(pt) => pt.write(buf), + Self::PtyMaster(pt) => pt.write(buf), // TODO maybe allow adding FDs to poll channels this way Self::Poll(_) => Err(Error::InvalidOperation), // TODO maybe allow writing messages to Channels? @@ -323,6 +357,8 @@ impl fmt::Debug for File { Self::Poll(_) => f.debug_struct("Poll").finish_non_exhaustive(), Self::Channel(_) => f.debug_struct("Channel").finish_non_exhaustive(), Self::SharedMemory(_) => f.debug_struct("SharedMemory").finish_non_exhaustive(), + Self::PtySlave(_) => f.debug_struct("PtySlave").finish_non_exhaustive(), + Self::PtyMaster(_) => f.debug_struct("PtyMaster").finish_non_exhaustive(), } } } diff --git a/lib/vfs/src/lib.rs b/lib/vfs/src/lib.rs index 3e3ca8e1..186f7c70 100644 --- a/lib/vfs/src/lib.rs +++ b/lib/vfs/src/lib.rs @@ -17,6 +17,7 @@ pub(crate) mod ioctx; pub(crate) mod node; pub(crate) mod path; pub(crate) mod poll; +pub(crate) mod pty; pub(crate) mod shared_memory; pub(crate) mod traits; @@ -29,5 +30,6 @@ pub use node::{ RegularImpl, SymlinkImpl, }; pub use poll::FdPoll; +pub use pty::{PseudoTerminal, PseudoTerminalMaster, PseudoTerminalSlave}; pub use shared_memory::SharedMemory; pub use traits::{FileReadiness, Read, Seek, Write}; diff --git a/lib/vfs/src/node/impls.rs b/lib/vfs/src/node/impls.rs index ae53bab8..0f752b61 100644 --- a/lib/vfs/src/node/impls.rs +++ b/lib/vfs/src/node/impls.rs @@ -2,7 +2,6 @@ use core::{marker::PhantomData, str::FromStr}; use alloc::{ - boxed::Box, string::{String, ToString}, sync::Arc, vec::Vec, diff --git a/lib/vfs/src/poll.rs b/lib/vfs/src/poll.rs index f96d9a83..8668509f 100644 --- a/lib/vfs/src/poll.rs +++ b/lib/vfs/src/poll.rs @@ -13,7 +13,7 @@ use kernel_util::{ }; use yggdrasil_abi::{error::Error, io::RawFd}; -use crate::FileRef; +use crate::{FileReadiness, FileRef}; /// Poll channel implementation. Allows blocking until a file descriptor signals an event or a /// timeout is reached. @@ -75,3 +75,15 @@ impl<'a> Future for FdPollFuture<'a> { Poll::Pending } } + +impl FileReadiness for FdPoll { + fn poll_read(&self, cx: &mut Context<'_>) -> Poll> { + for (_, file) in self.fds.lock().unwrap().iter() { + if file.poll_read(cx).is_ready() { + return Poll::Ready(Ok(())); + } + } + + Poll::Pending + } +} diff --git a/lib/vfs/src/pty.rs b/lib/vfs/src/pty.rs new file mode 100644 index 00000000..c5a9c1a7 --- /dev/null +++ b/lib/vfs/src/pty.rs @@ -0,0 +1,417 @@ +//! Pseudo-terminal devices + +// TODO handle erase key +// TODO handle werase key +use core::{ + pin::Pin, + sync::atomic::{AtomicBool, Ordering}, + task::{Context, Poll}, +}; + +use alloc::sync::Arc; +use futures_util::Future; +use kernel_util::{ + block, + runtime::QueueWaker, + signal_process_group, + sync::{spin_rwlock::IrqSafeRwLock, IrqSafeSpinlock, IrqSafeSpinlockGuard}, + util::ring::RingBuffer, +}; +use yggdrasil_abi::{ + error::Error, + io::{ + DeviceRequest, TerminalInputOptions, TerminalLineOptions, TerminalOptions, + TerminalOutputOptions, TerminalSize, + }, + process::Signal, +}; + +const CAPACITY: usize = 1024; + +struct PtyHalf { + ring: IrqSafeSpinlock>, + notify: QueueWaker, +} + +/// Pseudo-terminal shared device +pub struct PseudoTerminal { + config: IrqSafeRwLock, + signal_pgroup: IrqSafeRwLock>, + slave_to_master: PtyHalf, + master_to_slave: PtyHalf, + size: IrqSafeRwLock, + eof: AtomicBool, +} +/// Slave part of a PTY device +pub struct PseudoTerminalSlave { + pty: Arc, +} +/// Master part of a PTY device +pub struct PseudoTerminalMaster { + pty: Arc, +} + +fn input_discipline( + mut lock: IrqSafeSpinlockGuard>, + config: &TerminalOptions, + eof: &AtomicBool, + buffer: &mut [u8], +) -> Result { + let mut pos = 0; + + if !config.is_canonical() { + while !eof.load(Ordering::Acquire) && pos < buffer.len() && lock.is_readable() { + buffer[pos] = unsafe { lock.read_single_unchecked() }; + pos += 1; + } + Ok(pos) + } else { + while !eof.load(Ordering::Acquire) && pos < buffer.len() && lock.is_readable() { + let ch = unsafe { lock.read_single_unchecked() }; + + if ch == config.chars.erase { + pos = pos.saturating_sub(1); + continue; + } + + buffer[pos] = ch; + pos += 1; + + if ch == b'\n' { + break; + } + } + Ok(pos) + } +} + +impl PtyHalf { + pub fn new() -> Self { + Self { + ring: IrqSafeSpinlock::new(RingBuffer::new()), + notify: QueueWaker::new(), + } + } + + fn try_begin_read<'a>( + &'a self, + config: &TerminalOptions, + ) -> Option>> { + let lock = self.ring.lock(); + + let ready = if config.is_canonical() { + lock.is_readable() && lock.contains(&b'\n') + } else { + lock.is_readable() + }; + + if ready { + Some(lock) + } else { + None + } + } + + fn read( + &self, + config: &TerminalOptions, + eof: &AtomicBool, + buffer: &mut [u8], + ) -> Result { + if buffer.is_empty() || eof.load(Ordering::Acquire) { + return Ok(0); + } + + if let Some(lock) = self.try_begin_read(config) { + input_discipline(lock, config, eof, buffer) + } else { + block!(self.read_async(config, eof, buffer).await)? + } + } + + fn read_async<'a>( + &'a self, + config: &'a TerminalOptions, + eof: &'a AtomicBool, + buffer: &'a mut [u8], + ) -> impl Future> + 'a { + struct F<'f> { + config: &'f TerminalOptions, + eof: &'f AtomicBool, + half: &'f PtyHalf, + buffer: &'f mut [u8], + } + + impl<'f> Future for F<'f> { + type Output = Result; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.half.notify.register(cx.waker()); + + if self.eof.load(Ordering::Acquire) { + self.half.notify.remove(cx.waker()); + Poll::Ready(Ok(0)) + } else if let Some(lock) = self.half.try_begin_read(self.config) { + self.half.notify.remove(cx.waker()); + Poll::Ready(input_discipline(lock, self.config, self.eof, self.buffer)) + } else { + Poll::Pending + } + } + } + + F { + half: self, + eof, + buffer, + config, + } + } + + fn poll_read( + &self, + config: &TerminalOptions, + eof: &AtomicBool, + cx: &mut Context<'_>, + ) -> Poll> { + self.notify.register(cx.waker()); + + if eof.load(Ordering::Acquire) || self.try_begin_read(config).is_some() { + self.notify.remove(cx.waker()); + Poll::Ready(Ok(())) + } else { + Poll::Pending + } + } + + fn read_raw(&self, eof: &AtomicBool, buffer: &mut [u8]) -> Result { + if buffer.is_empty() || eof.load(Ordering::Acquire) { + return Ok(0); + } + + let mut lock = self.ring.lock(); + if lock.is_readable() { + let mut pos = 0; + while !eof.load(Ordering::Acquire) && lock.is_readable() { + buffer[pos] = unsafe { lock.read_single_unchecked() }; + pos += 1; + } + + Ok(pos) + } else { + todo!() + } + } + + fn poll_raw(&self, eof: &AtomicBool, cx: &mut Context<'_>) -> Poll> { + self.notify.register(cx.waker()); + + if eof.load(Ordering::Acquire) || self.ring.lock().is_readable() { + self.notify.remove(cx.waker()); + Poll::Ready(Ok(())) + } else { + Poll::Pending + } + } + + fn putc(&self, byte: u8, signal: bool) { + self.ring.lock().write(byte); + if signal { + self.notify.wake_all(); + } + } +} + +impl PseudoTerminal { + /// Creates a pair of PTY slave/master devices + pub fn new( + config: TerminalOptions, + size: TerminalSize, + ) -> (PseudoTerminalMaster, PseudoTerminalSlave) { + let pty = Arc::new(Self { + config: IrqSafeRwLock::new(config), + signal_pgroup: IrqSafeRwLock::new(None), + master_to_slave: PtyHalf::new(), + slave_to_master: PtyHalf::new(), + size: IrqSafeRwLock::new(size), + eof: AtomicBool::new(false), + }); + + let master = PseudoTerminalMaster { pty: pty.clone() }; + let slave = PseudoTerminalSlave { pty }; + + (master, slave) + } + + fn putc_from_slave(&self, byte: u8) { + let config = self.config.read(); + + if byte == b'\n' && config.output.contains(TerminalOutputOptions::NL_TO_CRNL) { + self.slave_to_master.putc(b'\r', true); + } + + self.slave_to_master.putc(byte, true) + } + + fn putc_from_master(&self, mut byte: u8) { + let config = self.config.read(); + + if byte == b'\r' && config.input.contains(TerminalInputOptions::CR_TO_NL) { + byte = b'\n'; + } + + if byte == b'\n' { + if config.is_echo_newline() { + if config.output.contains(TerminalOutputOptions::NL_TO_CRNL) { + self.slave_to_master.putc(b'\r', true); + } + self.slave_to_master.putc(byte, true); + } + } else if config.line.contains(TerminalLineOptions::ECHO) { + if byte.is_ascii_control() { + if byte != config.chars.erase && byte != config.chars.werase { + self.slave_to_master.putc(b'^', false); + self.slave_to_master.putc(byte + 0x40, true); + } + } else { + self.slave_to_master.putc(byte, true); + } + } + + if byte == config.chars.interrupt && config.line.contains(TerminalLineOptions::SIGNAL) { + self.slave_to_master.notify.wake_all(); + + if let Some(group_id) = *self.signal_pgroup.read() { + signal_process_group(group_id, Signal::Interrupted); + return; + } + } + + if byte == config.chars.eof { + self.slave_to_master.notify.wake_all(); + self.master_to_slave.notify.wake_all(); + self.eof.store(true, Ordering::Release); + + return; + } + + self.master_to_slave.putc( + byte, + !config.is_canonical() || byte == b'\n' || byte == b'\r', + ); + } + + fn write_from_slave(&self, buf: &[u8]) -> Result { + for &ch in buf { + self.putc_from_slave(ch); + } + Ok(buf.len()) + } + + fn write_from_master(&self, buf: &[u8]) -> Result { + for &ch in buf { + self.putc_from_master(ch); + } + Ok(buf.len()) + } + + fn read_from_slave(&self, buf: &mut [u8]) -> Result { + self.slave_to_master.read_raw(&self.eof, buf) + } + + fn read_from_master(&self, buf: &mut [u8]) -> Result { + let config = self.config.read(); + self.master_to_slave.read(&config, &self.eof, buf) + } + + fn poll_from_slave(&self, cx: &mut Context<'_>) -> Poll> { + self.slave_to_master.poll_raw(&self.eof, cx) + } + + fn poll_from_master(&self, cx: &mut Context<'_>) -> Poll> { + let config = self.config.read(); + self.master_to_slave.poll_read(&config, &self.eof, cx) + } + + fn device_request(&self, req: &mut DeviceRequest) -> Result<(), Error> { + match req { + DeviceRequest::SetTerminalGroup(group_id) => { + self.signal_pgroup.write().replace(*group_id); + Ok(()) + } + DeviceRequest::GetTerminalOptions(options) => { + options.write(*self.config.read()); + Ok(()) + } + DeviceRequest::SetTerminalOptions(options) => { + *self.config.write() = *options; + Ok(()) + } + DeviceRequest::GetTerminalSize(size) => { + size.write(*self.size.read()); + Ok(()) + } + _ => Err(Error::InvalidOperation), + } + } +} + +impl PseudoTerminalSlave { + /// Reads from the master-to-slave half of the PTY + pub fn read(&self, buf: &mut [u8]) -> Result { + self.pty.read_from_master(buf) + } + + /// Writes to the slave-to-master half of the PTY + pub fn write(&self, buf: &[u8]) -> Result { + self.pty.write_from_slave(buf) + } + + /// Polls PTY read readiness + pub fn poll_read(&self, cx: &mut Context<'_>) -> Poll> { + self.pty.poll_from_master(cx) + } + + /// Performs a device-specific request to the PTY + pub fn device_request(&self, req: &mut DeviceRequest) -> Result<(), Error> { + self.pty.device_request(req) + } +} + +impl PseudoTerminalMaster { + /// Reads from the slave-to-master half of the PTY + pub fn read(&self, buf: &mut [u8]) -> Result { + self.pty.read_from_slave(buf) + } + + /// Writes to the master-to-slave half of the PTY + pub fn write(&self, buf: &[u8]) -> Result { + self.pty.write_from_master(buf) + } + + /// Polls PTY read readiness + pub fn poll_read(&self, cx: &mut Context<'_>) -> Poll> { + self.pty.poll_from_slave(cx) + } + + /// Performs a device-specific request to the PTY + pub fn device_request(&self, req: &mut DeviceRequest) -> Result<(), Error> { + self.pty.device_request(req) + } +} + +impl Drop for PseudoTerminalMaster { + fn drop(&mut self) { + self.pty.eof.store(true, Ordering::Release); + self.pty.master_to_slave.notify.wake_all(); + self.pty.slave_to_master.notify.wake_all(); + } +} + +impl Drop for PseudoTerminalSlave { + fn drop(&mut self) { + self.pty.eof.store(true, Ordering::Release); + self.pty.master_to_slave.notify.wake_all(); + self.pty.slave_to_master.notify.wake_all(); + } +} diff --git a/src/arch/mod.rs b/src/arch/mod.rs index 9641c84c..58b7c2c3 100644 --- a/src/arch/mod.rs +++ b/src/arch/mod.rs @@ -275,7 +275,11 @@ pub trait CpuAccess: Sized { /// Returns current thread ID or none if idle fn current_thread_id(&self) -> Option; - /// Update the current thread ID + /// Update the current thread ID. + /// + /// # Safety + /// + /// Only meant to be called from within the scheduler. unsafe fn set_current_thread_id(&mut self, id: Option); } diff --git a/src/arch/x86_64/mod.rs b/src/arch/x86_64/mod.rs index f4666f7d..689a0597 100644 --- a/src/arch/x86_64/mod.rs +++ b/src/arch/x86_64/mod.rs @@ -10,7 +10,7 @@ use device_api::{ Device, }; use git_version::git_version; -use kernel_fs::devfs::{self, CharDeviceType}; +use kernel_fs::devfs; use kernel_util::{ mem::{ address::{FromRaw, IntoRaw, PhysicalAddress}, diff --git a/src/arch/x86_64/peripherals/ps2/mod.rs b/src/arch/x86_64/peripherals/ps2/mod.rs index 193fbd87..bb650446 100644 --- a/src/arch/x86_64/peripherals/ps2/mod.rs +++ b/src/arch/x86_64/peripherals/ps2/mod.rs @@ -55,17 +55,17 @@ impl Inner { self.command.write(cmd); } - fn recv_timeout(&mut self, timeout: u64) -> Option { - let mut counter = 0; - while self.command.read() & Self::STATUS_OUTPUT_FULL == 0 { - counter += 1; - if counter > timeout { - return None; - } - core::hint::spin_loop(); - } - Some(self.data.read()) - } + // fn recv_timeout(&mut self, timeout: u64) -> Option { + // let mut counter = 0; + // while self.command.read() & Self::STATUS_OUTPUT_FULL == 0 { + // counter += 1; + // if counter > timeout { + // return None; + // } + // core::hint::spin_loop(); + // } + // Some(self.data.read()) + // } fn try_recv(&mut self) -> Option { if self.command.read() & Inner::STATUS_OUTPUT_FULL != 0 { @@ -163,7 +163,3 @@ impl PS2Controller { } } } - -async fn input_worker() { - loop {} -} diff --git a/src/debug.rs b/src/debug.rs index 57b3b14d..c717a66a 100644 --- a/src/debug.rs +++ b/src/debug.rs @@ -2,9 +2,12 @@ use core::fmt::{self, Arguments}; use abi::error::Error; -use kernel_util::{sync::IrqSafeSpinlock, util::StaticVector}; +use kernel_util::{ + sync::IrqSafeSpinlock, + util::{ring::RingBuffer, StaticVector}, +}; -use crate::{task::process::Process, util::ring::RingBuffer}; +use crate::task::process::Process; const MAX_DEBUG_SINKS: usize = 4; const RING_LOGGER_CAPACITY: usize = 65536; diff --git a/src/device/display/linear_fb.rs b/src/device/display/linear_fb.rs index 8f13b060..0dfe5f41 100644 --- a/src/device/display/linear_fb.rs +++ b/src/device/display/linear_fb.rs @@ -157,7 +157,7 @@ impl PageProvider for LinearFramebuffer { } } - fn release_page(&self, offset: u64, phys: PhysicalAddress) -> Result<(), Error> { + fn release_page(&self, _offset: u64, _phys: PhysicalAddress) -> Result<(), Error> { Ok(()) } } diff --git a/src/device/input.rs b/src/device/input.rs index d1b772d6..dc6cc515 100644 --- a/src/device/input.rs +++ b/src/device/input.rs @@ -15,11 +15,10 @@ use futures_util::{task::AtomicWaker, Future}; use kernel_util::{ block, sync::{IrqSafeSpinlock, IrqSafeSpinlockGuard}, + util::ring::RingBuffer, }; use vfs::{CharDevice, FileReadiness}; -use crate::util::ring::RingBuffer; - // #[derive(Default)] // pub struct InputState { // lshift: bool, @@ -133,7 +132,7 @@ impl CharDevice for KeyboardDevice { false } - fn device_request(&self, req: &mut DeviceRequest) -> Result<(), Error> { + fn device_request(&self, _req: &mut DeviceRequest) -> Result<(), Error> { todo!() } diff --git a/src/device/tty.rs b/src/device/tty.rs index eb154f16..cf55e8d8 100644 --- a/src/device/tty.rs +++ b/src/device/tty.rs @@ -12,12 +12,9 @@ use abi::{ }; use device_api::serial::SerialDevice; use futures_util::Future; -use kernel_util::{runtime::QueueWaker, sync::IrqSafeSpinlock}; +use kernel_util::{runtime::QueueWaker, sync::IrqSafeSpinlock, util::ring::RingBuffer}; -use crate::{ - task::process::{Process, ProcessId}, - util::ring::RingBuffer, -}; +use crate::task::process::{Process, ProcessId}; struct TerminalRing { buffer: IrqSafeSpinlock>, @@ -47,12 +44,6 @@ impl TerminalRing { self.notify.wake_all(); } - // TODO EOF not yet implemented - // fn signal_eof(&self) { - // self.eof.store(true, Ordering::Release); - // self.notify.wake_all(); - // } - fn read_blocking(&self) -> impl Future> + '_ { struct F<'f> { ring: &'f TerminalRing, @@ -87,15 +78,6 @@ impl TerminalRing { F { ring: self } } - - fn can_read_now(&self, canonical: bool) -> bool { - let lock = self.buffer.lock(); - if canonical { - lock.contains(&b'\n') - } else { - lock.is_readable() - } - } } struct TtyContextInner { diff --git a/src/mem/process.rs b/src/mem/process.rs index d7b02a2b..bab29089 100644 --- a/src/mem/process.rs +++ b/src/mem/process.rs @@ -1,7 +1,5 @@ //! Process address space structures and management functions -use core::ops::Range; - use abi::error::Error; use cfg_if::cfg_if; use kernel_util::{ @@ -116,7 +114,7 @@ impl PageProvider for VirtualRangeBacking { impl PartialEq for VirtualRangeBacking { fn eq(&self, other: &Self) -> bool { - return matches!(self, Self::Anonymous) && matches!(other, Self::Anonymous); + matches!(self, Self::Anonymous) && matches!(other, Self::Anonymous) } } diff --git a/src/proc/elf.rs b/src/proc/elf.rs index 227b7e7d..8d5c0fb1 100644 --- a/src/proc/elf.rs +++ b/src/proc/elf.rs @@ -13,7 +13,6 @@ use yggdrasil_abi::{error::Error, io::SeekFrom}; use crate::{ mem::{ - phys, process::{ProcessAddressSpace, VirtualRangeBacking}, table::MapAttributes, }, diff --git a/src/proc/exec.rs b/src/proc/exec.rs index 2d803408..44e91ec3 100644 --- a/src/proc/exec.rs +++ b/src/proc/exec.rs @@ -14,7 +14,6 @@ use vfs::{FileRef, IoContext, Read, Seek}; use crate::{ mem::{ - phys, process::{ProcessAddressSpace, VirtualRangeBacking}, table::MapAttributes, ForeignPointer, diff --git a/src/syscall/mod.rs b/src/syscall/mod.rs index 688355ad..e35981b4 100644 --- a/src/syscall/mod.rs +++ b/src/syscall/mod.rs @@ -5,7 +5,8 @@ use abi::{ error::Error, io::{ DeviceRequest, DirectoryEntry, FileAttr, FileMode, MessageDestination, OpenOptions, - PollControl, RawFd, ReceivedMessageMetadata, SeekFrom, SentMessage, + PollControl, RawFd, ReceivedMessageMetadata, SeekFrom, SentMessage, TerminalOptions, + TerminalSize, }, mem::MappingSource, process::{ExitCode, MutexOperation, Signal, SpawnOption, SpawnOptions, ThreadSpawnOptions}, @@ -20,7 +21,7 @@ use crate::{ arch::L3, debug::LogLevel, fs, - mem::{phys, process::VirtualRangeBacking, table::MapAttributes}, + mem::{process::VirtualRangeBacking, table::MapAttributes}, proc::{self, io::ProcessIo, random}, task::{ process::{Process, ProcessId}, @@ -236,8 +237,8 @@ fn syscall_handler(func: SyscallFunction, args: &[u64]) -> Result run_with_io(process, |io| { let file = io.files.file(fd)?; - let node = file.node().ok_or(Error::InvalidFile)?; - node.device_request(req)?; + // let node = file.node().ok_or(Error::InvalidFile)?; + file.device_request(req)?; Ok(0) }) } @@ -408,7 +409,7 @@ fn syscall_handler(func: SyscallFunction, args: &[u64]) -> Result } metadata.write(ReceivedMessageMetadata::Data(len)); - buf[..len].copy_from_slice(&data); + buf[..len].copy_from_slice(data); Ok(len) } @@ -432,6 +433,21 @@ fn syscall_handler(func: SyscallFunction, args: &[u64]) -> Result Ok(fd.0 as usize) }) } + SyscallFunction::CreatePty => { + let options = arg_user_ref::(args[0] as usize)?; + let size = arg_user_ref::(args[1] as usize)?; + let output = arg_user_mut::>(args[2] as usize)?; + + 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.write((master_fd, slave_fd)); + + Ok(0) + }) + } // Process management SyscallFunction::SpawnProcess => { let options = arg_user_ref::(args[0] as usize)?; @@ -478,10 +494,11 @@ fn syscall_handler(func: SyscallFunction, args: &[u64]) -> Result }) { debugln!("{} requested terminal {:?}", pid, fd); let file = child_io.files.file(fd)?; - let node = file.node().ok_or(Error::InvalidFile)?; + // let node = file.node().ok_or(Error::InvalidFile)?; let mut req = DeviceRequest::SetTerminalGroup(child_process.group_id().into()); + file.device_request(&mut req)?; - node.device_request(&mut req)?; + // node.device_request(&mut req)?; } drop(child_io); @@ -520,8 +537,7 @@ fn syscall_handler(func: SyscallFunction, args: &[u64]) -> Result // after that let code = ExitCode::from(args[0] as i32); - thread.exit_process(code); - unreachable!() + thread.exit_process(code) } SyscallFunction::SendSignal => { let pid = ProcessId::from(args[0] as u32); diff --git a/src/task/context.rs b/src/task/context.rs index e3a7e23f..79268e1f 100644 --- a/src/task/context.rs +++ b/src/task/context.rs @@ -90,8 +90,7 @@ pub trait TaskContextImpl: Sized { ) -> ! { let closure = unsafe { Box::from_raw(closure_addr as *mut F) }; let result = closure(); - Thread::current().exit(result.into_exit_code()); - unreachable!(); + Thread::current().exit(result.into_exit_code()) } let closure = Box::new(f); diff --git a/src/task/process.rs b/src/task/process.rs index 39ae143c..b55d81b9 100644 --- a/src/task/process.rs +++ b/src/task/process.rs @@ -435,3 +435,8 @@ impl ProcessId { Self(id) } } + +#[no_mangle] +fn __signal_process_group(group_id: u32, signal: Signal) { + Process::signal_group(ProcessId(group_id as _), signal) +} diff --git a/src/task/sched.rs b/src/task/sched.rs index b9eba858..1398f96a 100644 --- a/src/task/sched.rs +++ b/src/task/sched.rs @@ -1,14 +1,9 @@ //! Per-CPU queue implementation -use core::sync::atomic::Ordering; - -use alloc::{collections::VecDeque, sync::Arc, vec::Vec}; +use alloc::{sync::Arc, vec::Vec}; use cfg_if::cfg_if; use crossbeam_queue::SegQueue; -use kernel_util::{ - sync::{IrqGuard, IrqSafeSpinlock}, - util::OneTimeInit, -}; +use kernel_util::{sync::IrqGuard, util::OneTimeInit}; use crate::{ arch::{Architecture, ArchitectureImpl, CpuAccess}, @@ -21,18 +16,6 @@ use super::{ Cpu, TaskContext, }; -// /// Per-CPU statistics -// #[derive(Default)] -// pub struct CpuQueueStats { -// /// Ticks spent idling -// pub idle_time: u64, -// /// Ticks spent running CPU tasks -// pub cpu_time: u64, -// -// /// Time since last measurement -// measure_time: u64, -// } - /// Per-CPU queue pub struct CpuQueue { queue: SegQueue, @@ -59,7 +42,11 @@ impl CpuQueue { self.index } - /// "Enters" the scheduler by selecting a first task to execute + /// "Enters" the scheduler by selecting a first task to execute. + /// + /// # Safety + /// + /// Only meant to be called once per each CPU when everything's ready. pub unsafe fn enter(&self) -> ! { let _guard = IrqGuard::acquire(); @@ -106,7 +93,11 @@ impl CpuQueue { (None, None) } - /// Selects a new thread from the queue and performs a context switch if necessary + /// Selects a new thread from the queue and performs a context switch if necessary. + /// + /// # Safety + /// + /// Only meant to be called from within the timer handler or the thread impl. pub unsafe fn yield_cpu(&self) -> bool { assert!(ArchitectureImpl::interrupt_mask()); @@ -124,7 +115,7 @@ impl CpuQueue { match sched.state { ThreadState::Ready => { - panic!("Thread {:?} was running, but is marked Ready", current.id); + self.queue.push(current.id); } ThreadState::Running => { sched.state = ThreadState::Ready; diff --git a/src/task/thread.rs b/src/task/thread.rs index fe12c438..4e0f5e81 100644 --- a/src/task/thread.rs +++ b/src/task/thread.rs @@ -240,6 +240,7 @@ impl Thread { /// Pushes a signal to the thread's signal queue pub fn raise_signal(&self, signal: Signal) { self.signal_queue.push(signal); + self.enqueue(); } // Scheduling @@ -248,7 +249,9 @@ impl Thread { pub fn enqueue_to(&self, queue: &'static CpuQueue) { let mut sched = self.sched.lock(); - assert_ne!(sched.state, ThreadState::Terminated); + if sched.state == ThreadState::Terminated { + return; + } match sched.state { ThreadState::Running | ThreadState::Ready => { diff --git a/src/util/mod.rs b/src/util/mod.rs index 44184486..26f1d001 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,7 +1,6 @@ //! Various kernel utility functions pub mod queue; -pub mod ring; /// Extension trait for [Iterator]s of [Result]s pub trait ResultIterator {