vfs: implement PTY devices

This commit is contained in:
Mark Poliakov 2024-01-04 21:22:18 +02:00
parent 69c73454c1
commit 2444e147c4
31 changed files with 589 additions and 117 deletions

View File

@ -4,7 +4,7 @@ extern crate alloc;
use core::task::{Context, Poll}; use core::task::{Context, Poll};
use kernel_util::mem::{address::PhysicalAddress, PageProvider}; use kernel_util::mem::PageProvider;
use yggdrasil_abi::{error::Error, io::DeviceRequest}; use yggdrasil_abi::{error::Error, io::DeviceRequest};
pub mod device; pub mod device;

View File

@ -1,3 +0,0 @@
use yggdrasil_abi::error::Error;
use crate::Device;

View File

@ -5,7 +5,6 @@ extern crate alloc;
pub mod bus; pub mod bus;
pub mod device; pub mod device;
pub mod input;
pub mod interrupt; pub mod interrupt;
pub mod manager; pub mod manager;
pub mod serial; pub mod serial;

View File

@ -2,7 +2,10 @@ use core::time::Duration;
use alloc::{string::String, sync::Arc}; use alloc::{string::String, sync::Arc};
use device_api::interrupt::MessageInterruptController; use device_api::interrupt::MessageInterruptController;
use yggdrasil_abi::{error::Error, process::ExitCode}; use yggdrasil_abi::{
error::Error,
process::{ExitCode, Signal},
};
use crate::{ use crate::{
mem::{address::PhysicalAddress, device::RawDeviceMemoryMapping}, mem::{address::PhysicalAddress, device::RawDeviceMemoryMapping},
@ -46,4 +49,6 @@ extern "Rust" {
pub fn __monotonic_timestamp() -> Result<Duration, Error>; pub fn __monotonic_timestamp() -> Result<Duration, Error>;
pub fn __message_interrupt_controller() -> &'static dyn MessageInterruptController; pub fn __message_interrupt_controller() -> &'static dyn MessageInterruptController;
pub fn __signal_process_group(group_id: u32, signal: Signal);
} }

View File

@ -8,10 +8,13 @@
strict_provenance, strict_provenance,
never_type, never_type,
let_chains, let_chains,
allocator_api allocator_api,
maybe_uninit_uninit_array,
const_maybe_uninit_uninit_array
)] )]
use device_api::interrupt::MessageInterruptController; use device_api::interrupt::MessageInterruptController;
use yggdrasil_abi::process::Signal;
extern crate alloc; extern crate alloc;
@ -38,6 +41,11 @@ pub fn cpu_count() -> usize {
unsafe { api::__cpu_count() } 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)] #[repr(C)]
pub struct AlignedTo<Align, Bytes: ?Sized> { pub struct AlignedTo<Align, Bytes: ?Sized> {
pub align: [Align; 0], pub align: [Align; 0],

View File

@ -1,17 +1,10 @@
use core::task::{Context, Poll}; use core::task::{Context, Poll};
use alloc::{boxed::Box, format, sync::Arc}; use alloc::{boxed::Box, format, sync::Arc};
use futures_util::{ use futures_util::{task::waker_ref, Future};
task::{waker_ref, ArcWake, WakerRef},
Future,
};
use yggdrasil_abi::error::Error; use yggdrasil_abi::error::Error;
use crate::{ use crate::thread::Thread;
api::__cpu_index,
cpu_index,
thread::{CurrentThread, Thread},
};
use super::{ use super::{
task::{Task, Termination}, task::{Task, Termination},

View File

@ -4,9 +4,7 @@ use core::{
sync::atomic::{AtomicUsize, Ordering}, sync::atomic::{AtomicUsize, Ordering},
}; };
use yggdrasil_abi::error::Error; use super::IrqGuard;
use super::{IrqGuard, LockMethod};
struct RwLockInner { struct RwLockInner {
value: AtomicUsize, value: AtomicUsize,
@ -19,12 +17,12 @@ pub struct IrqSafeRwLock<T> {
pub struct IrqSafeRwLockReadGuard<'a, T> { pub struct IrqSafeRwLockReadGuard<'a, T> {
lock: &'a IrqSafeRwLock<T>, lock: &'a IrqSafeRwLock<T>,
guard: IrqGuard, _guard: IrqGuard,
} }
pub struct IrqSafeRwLockWriteGuard<'a, T> { pub struct IrqSafeRwLockWriteGuard<'a, T> {
lock: &'a IrqSafeRwLock<T>, lock: &'a IrqSafeRwLock<T>,
guard: IrqGuard, _guard: IrqGuard,
} }
impl RwLockInner { impl RwLockInner {
@ -106,13 +104,19 @@ impl<T> IrqSafeRwLock<T> {
pub fn read(&self) -> IrqSafeRwLockReadGuard<T> { pub fn read(&self) -> IrqSafeRwLockReadGuard<T> {
let guard = IrqGuard::acquire(); let guard = IrqGuard::acquire();
self.inner.acquire_read(); self.inner.acquire_read();
IrqSafeRwLockReadGuard { lock: self, guard } IrqSafeRwLockReadGuard {
lock: self,
_guard: guard,
}
} }
pub fn write(&self) -> IrqSafeRwLockWriteGuard<T> { pub fn write(&self) -> IrqSafeRwLockWriteGuard<T> {
let guard = IrqGuard::acquire(); let guard = IrqGuard::acquire();
self.inner.acquire_write(); self.inner.acquire_write();
IrqSafeRwLockWriteGuard { lock: self, guard } IrqSafeRwLockWriteGuard {
lock: self,
_guard: guard,
}
} }
unsafe fn release_read(&self) { unsafe fn release_read(&self) {

View File

@ -7,6 +7,8 @@ use core::{
sync::atomic::{AtomicUsize, Ordering}, sync::atomic::{AtomicUsize, Ordering},
}; };
pub mod ring;
pub enum ConstAssert<const CHECK: bool> {} pub enum ConstAssert<const CHECK: bool> {}
pub trait IsTrue {} pub trait IsTrue {}

View File

@ -63,7 +63,11 @@ impl<T, const N: usize> RingBuffer<T, N> {
false 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] #[inline]
pub unsafe fn read_single_unchecked(&mut self) -> T pub unsafe fn read_single_unchecked(&mut self) -> T
where where
@ -74,7 +78,11 @@ impl<T, const N: usize> RingBuffer<T, N> {
res 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 pub unsafe fn read_all_static(&mut self, pos: usize, buffer: &mut [T]) -> usize
where where
T: Copy, T: Copy,

View File

@ -15,10 +15,7 @@ use kernel_util::{
block, block,
sync::{mutex::Mutex, IrqSafeSpinlock, LockMethod}, sync::{mutex::Mutex, IrqSafeSpinlock, LockMethod},
}; };
use yggdrasil_abi::{ use yggdrasil_abi::{error::Error, io::MessageDestination};
error::Error,
io::{MessageDestination, ReceivedMessageMetadata},
};
use crate::{FileReadiness, FileRef}; use crate::{FileReadiness, FileRef};

View File

@ -6,7 +6,6 @@ use core::{
}; };
use alloc::{ use alloc::{
boxed::Box,
collections::{btree_map::Entry, BTreeMap}, collections::{btree_map::Entry, BTreeMap},
sync::Arc, sync::Arc,
}; };
@ -16,7 +15,9 @@ use kernel_util::{
}; };
use yggdrasil_abi::{ use yggdrasil_abi::{
error::Error, error::Error,
io::{DirectoryEntry, OpenOptions, RawFd, SeekFrom}, io::{
DeviceRequest, DirectoryEntry, OpenOptions, RawFd, SeekFrom, TerminalOptions, TerminalSize,
},
}; };
use crate::{ use crate::{
@ -24,7 +25,7 @@ use crate::{
device::{BlockDeviceWrapper, CharDeviceWrapper}, device::{BlockDeviceWrapper, CharDeviceWrapper},
node::NodeRef, node::NodeRef,
traits::{Read, Seek, Write}, traits::{Read, Seek, Write},
FdPoll, FileReadiness, SharedMemory, FdPoll, FileReadiness, PseudoTerminal, PseudoTerminalMaster, PseudoTerminalSlave, SharedMemory,
}; };
use self::{ use self::{
@ -61,10 +62,13 @@ pub enum File {
Regular(RegularFile), Regular(RegularFile),
Block(BlockFile), Block(BlockFile),
Char(CharFile), Char(CharFile),
AnonymousPipe(PipeEnd), AnonymousPipe(PipeEnd),
Poll(FdPoll), Poll(FdPoll),
Channel(ChannelDescriptor), Channel(ChannelDescriptor),
SharedMemory(Arc<SharedMemory>), SharedMemory(Arc<SharedMemory>),
PtySlave(PseudoTerminalSlave),
PtyMaster(PseudoTerminalMaster),
} }
/// Contains a per-process fd -> FileRef map /// Contains a per-process fd -> FileRef map
@ -99,6 +103,18 @@ impl File {
Ok(Arc::new(Self::SharedMemory(Arc::new(shm)))) 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<Self>, Arc<Self>) {
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<Self> { pub(crate) fn directory(node: NodeRef, position: DirectoryOpenPosition) -> Arc<Self> {
let position = IrqSafeSpinlock::new(position.into()); let position = IrqSafeSpinlock::new(position.into());
Arc::new(Self::Directory(DirectoryFile { node, position })) Arc::new(Self::Directory(DirectoryFile { node, position }))
@ -202,11 +218,25 @@ impl File {
match self { match self {
Self::Char(f) => f.device.0.poll_read(cx), Self::Char(f) => f.device.0.poll_read(cx),
Self::Channel(ch) => ch.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 ?) // Polling not implemented, return ready immediately (XXX ?)
_ => Poll::Ready(Ok(())), _ => 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 /// Interprets the file as a poll channel
pub fn as_poll_channel(&self) -> Result<&FdPoll, Error> { pub fn as_poll_channel(&self) -> Result<&FdPoll, Error> {
if let Self::Poll(poll) = self { if let Self::Poll(poll) = self {
@ -251,6 +281,8 @@ impl Read for File {
Self::Block(file) => file.read(buf), Self::Block(file) => file.read(buf),
Self::Char(file) => file.read(buf), Self::Char(file) => file.read(buf),
Self::AnonymousPipe(pipe) => pipe.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? // TODO maybe allow reading FDs from poll channels as if they were regular streams?
Self::Poll(_) => Err(Error::InvalidOperation), Self::Poll(_) => Err(Error::InvalidOperation),
// TODO maybe allow reading messages from Channels? // TODO maybe allow reading messages from Channels?
@ -268,6 +300,8 @@ impl Write for File {
Self::Block(file) => file.write(buf), Self::Block(file) => file.write(buf),
Self::Char(file) => file.write(buf), Self::Char(file) => file.write(buf),
Self::AnonymousPipe(pipe) => pipe.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 // TODO maybe allow adding FDs to poll channels this way
Self::Poll(_) => Err(Error::InvalidOperation), Self::Poll(_) => Err(Error::InvalidOperation),
// TODO maybe allow writing messages to Channels? // 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::Poll(_) => f.debug_struct("Poll").finish_non_exhaustive(),
Self::Channel(_) => f.debug_struct("Channel").finish_non_exhaustive(), Self::Channel(_) => f.debug_struct("Channel").finish_non_exhaustive(),
Self::SharedMemory(_) => f.debug_struct("SharedMemory").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(),
} }
} }
} }

View File

@ -17,6 +17,7 @@ pub(crate) mod ioctx;
pub(crate) mod node; pub(crate) mod node;
pub(crate) mod path; pub(crate) mod path;
pub(crate) mod poll; pub(crate) mod poll;
pub(crate) mod pty;
pub(crate) mod shared_memory; pub(crate) mod shared_memory;
pub(crate) mod traits; pub(crate) mod traits;
@ -29,5 +30,6 @@ pub use node::{
RegularImpl, SymlinkImpl, RegularImpl, SymlinkImpl,
}; };
pub use poll::FdPoll; pub use poll::FdPoll;
pub use pty::{PseudoTerminal, PseudoTerminalMaster, PseudoTerminalSlave};
pub use shared_memory::SharedMemory; pub use shared_memory::SharedMemory;
pub use traits::{FileReadiness, Read, Seek, Write}; pub use traits::{FileReadiness, Read, Seek, Write};

View File

@ -2,7 +2,6 @@
use core::{marker::PhantomData, str::FromStr}; use core::{marker::PhantomData, str::FromStr};
use alloc::{ use alloc::{
boxed::Box,
string::{String, ToString}, string::{String, ToString},
sync::Arc, sync::Arc,
vec::Vec, vec::Vec,

View File

@ -13,7 +13,7 @@ use kernel_util::{
}; };
use yggdrasil_abi::{error::Error, io::RawFd}; 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 /// Poll channel implementation. Allows blocking until a file descriptor signals an event or a
/// timeout is reached. /// timeout is reached.
@ -75,3 +75,15 @@ impl<'a> Future for FdPollFuture<'a> {
Poll::Pending Poll::Pending
} }
} }
impl FileReadiness for FdPoll {
fn poll_read(&self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
for (_, file) in self.fds.lock().unwrap().iter() {
if file.poll_read(cx).is_ready() {
return Poll::Ready(Ok(()));
}
}
Poll::Pending
}
}

417
lib/vfs/src/pty.rs Normal file
View File

@ -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<RingBuffer<u8, CAPACITY>>,
notify: QueueWaker,
}
/// Pseudo-terminal shared device
pub struct PseudoTerminal {
config: IrqSafeRwLock<TerminalOptions>,
signal_pgroup: IrqSafeRwLock<Option<u32>>,
slave_to_master: PtyHalf,
master_to_slave: PtyHalf,
size: IrqSafeRwLock<TerminalSize>,
eof: AtomicBool,
}
/// Slave part of a PTY device
pub struct PseudoTerminalSlave {
pty: Arc<PseudoTerminal>,
}
/// Master part of a PTY device
pub struct PseudoTerminalMaster {
pty: Arc<PseudoTerminal>,
}
fn input_discipline(
mut lock: IrqSafeSpinlockGuard<RingBuffer<u8, CAPACITY>>,
config: &TerminalOptions,
eof: &AtomicBool,
buffer: &mut [u8],
) -> Result<usize, Error> {
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<IrqSafeSpinlockGuard<'a, RingBuffer<u8, CAPACITY>>> {
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<usize, Error> {
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<Output = Result<usize, Error>> + '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<usize, Error>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
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<Result<(), Error>> {
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<usize, Error> {
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<Result<(), Error>> {
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<usize, Error> {
for &ch in buf {
self.putc_from_slave(ch);
}
Ok(buf.len())
}
fn write_from_master(&self, buf: &[u8]) -> Result<usize, Error> {
for &ch in buf {
self.putc_from_master(ch);
}
Ok(buf.len())
}
fn read_from_slave(&self, buf: &mut [u8]) -> Result<usize, Error> {
self.slave_to_master.read_raw(&self.eof, buf)
}
fn read_from_master(&self, buf: &mut [u8]) -> Result<usize, Error> {
let config = self.config.read();
self.master_to_slave.read(&config, &self.eof, buf)
}
fn poll_from_slave(&self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
self.slave_to_master.poll_raw(&self.eof, cx)
}
fn poll_from_master(&self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
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<usize, Error> {
self.pty.read_from_master(buf)
}
/// Writes to the slave-to-master half of the PTY
pub fn write(&self, buf: &[u8]) -> Result<usize, Error> {
self.pty.write_from_slave(buf)
}
/// Polls PTY read readiness
pub fn poll_read(&self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
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<usize, Error> {
self.pty.read_from_slave(buf)
}
/// Writes to the master-to-slave half of the PTY
pub fn write(&self, buf: &[u8]) -> Result<usize, Error> {
self.pty.write_from_master(buf)
}
/// Polls PTY read readiness
pub fn poll_read(&self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
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();
}
}

View File

@ -275,7 +275,11 @@ pub trait CpuAccess: Sized {
/// Returns current thread ID or none if idle /// Returns current thread ID or none if idle
fn current_thread_id(&self) -> Option<ThreadId>; fn current_thread_id(&self) -> Option<ThreadId>;
/// 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<ThreadId>); unsafe fn set_current_thread_id(&mut self, id: Option<ThreadId>);
} }

View File

@ -10,7 +10,7 @@ use device_api::{
Device, Device,
}; };
use git_version::git_version; use git_version::git_version;
use kernel_fs::devfs::{self, CharDeviceType}; use kernel_fs::devfs;
use kernel_util::{ use kernel_util::{
mem::{ mem::{
address::{FromRaw, IntoRaw, PhysicalAddress}, address::{FromRaw, IntoRaw, PhysicalAddress},

View File

@ -55,17 +55,17 @@ impl Inner {
self.command.write(cmd); self.command.write(cmd);
} }
fn recv_timeout(&mut self, timeout: u64) -> Option<u8> { // fn recv_timeout(&mut self, timeout: u64) -> Option<u8> {
let mut counter = 0; // let mut counter = 0;
while self.command.read() & Self::STATUS_OUTPUT_FULL == 0 { // while self.command.read() & Self::STATUS_OUTPUT_FULL == 0 {
counter += 1; // counter += 1;
if counter > timeout { // if counter > timeout {
return None; // return None;
} // }
core::hint::spin_loop(); // core::hint::spin_loop();
} // }
Some(self.data.read()) // Some(self.data.read())
} // }
fn try_recv(&mut self) -> Option<u8> { fn try_recv(&mut self) -> Option<u8> {
if self.command.read() & Inner::STATUS_OUTPUT_FULL != 0 { if self.command.read() & Inner::STATUS_OUTPUT_FULL != 0 {
@ -163,7 +163,3 @@ impl PS2Controller {
} }
} }
} }
async fn input_worker() {
loop {}
}

View File

@ -2,9 +2,12 @@
use core::fmt::{self, Arguments}; use core::fmt::{self, Arguments};
use abi::error::Error; 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 MAX_DEBUG_SINKS: usize = 4;
const RING_LOGGER_CAPACITY: usize = 65536; const RING_LOGGER_CAPACITY: usize = 65536;

View File

@ -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(()) Ok(())
} }
} }

View File

@ -15,11 +15,10 @@ use futures_util::{task::AtomicWaker, Future};
use kernel_util::{ use kernel_util::{
block, block,
sync::{IrqSafeSpinlock, IrqSafeSpinlockGuard}, sync::{IrqSafeSpinlock, IrqSafeSpinlockGuard},
util::ring::RingBuffer,
}; };
use vfs::{CharDevice, FileReadiness}; use vfs::{CharDevice, FileReadiness};
use crate::util::ring::RingBuffer;
// #[derive(Default)] // #[derive(Default)]
// pub struct InputState { // pub struct InputState {
// lshift: bool, // lshift: bool,
@ -133,7 +132,7 @@ impl CharDevice for KeyboardDevice {
false false
} }
fn device_request(&self, req: &mut DeviceRequest) -> Result<(), Error> { fn device_request(&self, _req: &mut DeviceRequest) -> Result<(), Error> {
todo!() todo!()
} }

View File

@ -12,12 +12,9 @@ use abi::{
}; };
use device_api::serial::SerialDevice; use device_api::serial::SerialDevice;
use futures_util::Future; use futures_util::Future;
use kernel_util::{runtime::QueueWaker, sync::IrqSafeSpinlock}; use kernel_util::{runtime::QueueWaker, sync::IrqSafeSpinlock, util::ring::RingBuffer};
use crate::{ use crate::task::process::{Process, ProcessId};
task::process::{Process, ProcessId},
util::ring::RingBuffer,
};
struct TerminalRing { struct TerminalRing {
buffer: IrqSafeSpinlock<RingBuffer<u8, { TerminalRing::CAPACITY }>>, buffer: IrqSafeSpinlock<RingBuffer<u8, { TerminalRing::CAPACITY }>>,
@ -47,12 +44,6 @@ impl TerminalRing {
self.notify.wake_all(); 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<Output = Option<u8>> + '_ { fn read_blocking(&self) -> impl Future<Output = Option<u8>> + '_ {
struct F<'f> { struct F<'f> {
ring: &'f TerminalRing, ring: &'f TerminalRing,
@ -87,15 +78,6 @@ impl TerminalRing {
F { ring: self } 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 { struct TtyContextInner {

View File

@ -1,7 +1,5 @@
//! Process address space structures and management functions //! Process address space structures and management functions
use core::ops::Range;
use abi::error::Error; use abi::error::Error;
use cfg_if::cfg_if; use cfg_if::cfg_if;
use kernel_util::{ use kernel_util::{
@ -116,7 +114,7 @@ impl PageProvider for VirtualRangeBacking {
impl PartialEq for VirtualRangeBacking { impl PartialEq for VirtualRangeBacking {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
return matches!(self, Self::Anonymous) && matches!(other, Self::Anonymous); matches!(self, Self::Anonymous) && matches!(other, Self::Anonymous)
} }
} }

View File

@ -13,7 +13,6 @@ use yggdrasil_abi::{error::Error, io::SeekFrom};
use crate::{ use crate::{
mem::{ mem::{
phys,
process::{ProcessAddressSpace, VirtualRangeBacking}, process::{ProcessAddressSpace, VirtualRangeBacking},
table::MapAttributes, table::MapAttributes,
}, },

View File

@ -14,7 +14,6 @@ use vfs::{FileRef, IoContext, Read, Seek};
use crate::{ use crate::{
mem::{ mem::{
phys,
process::{ProcessAddressSpace, VirtualRangeBacking}, process::{ProcessAddressSpace, VirtualRangeBacking},
table::MapAttributes, table::MapAttributes,
ForeignPointer, ForeignPointer,

View File

@ -5,7 +5,8 @@ use abi::{
error::Error, error::Error,
io::{ io::{
DeviceRequest, DirectoryEntry, FileAttr, FileMode, MessageDestination, OpenOptions, DeviceRequest, DirectoryEntry, FileAttr, FileMode, MessageDestination, OpenOptions,
PollControl, RawFd, ReceivedMessageMetadata, SeekFrom, SentMessage, PollControl, RawFd, ReceivedMessageMetadata, SeekFrom, SentMessage, TerminalOptions,
TerminalSize,
}, },
mem::MappingSource, mem::MappingSource,
process::{ExitCode, MutexOperation, Signal, SpawnOption, SpawnOptions, ThreadSpawnOptions}, process::{ExitCode, MutexOperation, Signal, SpawnOption, SpawnOptions, ThreadSpawnOptions},
@ -20,7 +21,7 @@ use crate::{
arch::L3, arch::L3,
debug::LogLevel, debug::LogLevel,
fs, fs,
mem::{phys, process::VirtualRangeBacking, table::MapAttributes}, mem::{process::VirtualRangeBacking, table::MapAttributes},
proc::{self, io::ProcessIo, random}, proc::{self, io::ProcessIo, random},
task::{ task::{
process::{Process, ProcessId}, process::{Process, ProcessId},
@ -236,8 +237,8 @@ fn syscall_handler(func: SyscallFunction, args: &[u64]) -> Result<usize, Error>
run_with_io(process, |io| { run_with_io(process, |io| {
let file = io.files.file(fd)?; let file = io.files.file(fd)?;
let node = file.node().ok_or(Error::InvalidFile)?; // let node = file.node().ok_or(Error::InvalidFile)?;
node.device_request(req)?; file.device_request(req)?;
Ok(0) Ok(0)
}) })
} }
@ -408,7 +409,7 @@ fn syscall_handler(func: SyscallFunction, args: &[u64]) -> Result<usize, Error>
} }
metadata.write(ReceivedMessageMetadata::Data(len)); metadata.write(ReceivedMessageMetadata::Data(len));
buf[..len].copy_from_slice(&data); buf[..len].copy_from_slice(data);
Ok(len) Ok(len)
} }
@ -432,6 +433,21 @@ fn syscall_handler(func: SyscallFunction, args: &[u64]) -> Result<usize, Error>
Ok(fd.0 as usize) Ok(fd.0 as usize)
}) })
} }
SyscallFunction::CreatePty => {
let options = arg_user_ref::<TerminalOptions>(args[0] as usize)?;
let size = arg_user_ref::<TerminalSize>(args[1] as usize)?;
let output = arg_user_mut::<MaybeUninit<(RawFd, RawFd)>>(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 // Process management
SyscallFunction::SpawnProcess => { SyscallFunction::SpawnProcess => {
let options = arg_user_ref::<SpawnOptions>(args[0] as usize)?; let options = arg_user_ref::<SpawnOptions>(args[0] as usize)?;
@ -478,10 +494,11 @@ fn syscall_handler(func: SyscallFunction, args: &[u64]) -> Result<usize, Error>
}) { }) {
debugln!("{} requested terminal {:?}", pid, fd); debugln!("{} requested terminal {:?}", pid, fd);
let file = child_io.files.file(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()); 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); drop(child_io);
@ -520,8 +537,7 @@ fn syscall_handler(func: SyscallFunction, args: &[u64]) -> Result<usize, Error>
// after that // after that
let code = ExitCode::from(args[0] as i32); let code = ExitCode::from(args[0] as i32);
thread.exit_process(code); thread.exit_process(code)
unreachable!()
} }
SyscallFunction::SendSignal => { SyscallFunction::SendSignal => {
let pid = ProcessId::from(args[0] as u32); let pid = ProcessId::from(args[0] as u32);

View File

@ -90,8 +90,7 @@ pub trait TaskContextImpl: Sized {
) -> ! { ) -> ! {
let closure = unsafe { Box::from_raw(closure_addr as *mut F) }; let closure = unsafe { Box::from_raw(closure_addr as *mut F) };
let result = closure(); let result = closure();
Thread::current().exit(result.into_exit_code()); Thread::current().exit(result.into_exit_code())
unreachable!();
} }
let closure = Box::new(f); let closure = Box::new(f);

View File

@ -435,3 +435,8 @@ impl ProcessId {
Self(id) Self(id)
} }
} }
#[no_mangle]
fn __signal_process_group(group_id: u32, signal: Signal) {
Process::signal_group(ProcessId(group_id as _), signal)
}

View File

@ -1,14 +1,9 @@
//! Per-CPU queue implementation //! Per-CPU queue implementation
use core::sync::atomic::Ordering; use alloc::{sync::Arc, vec::Vec};
use alloc::{collections::VecDeque, sync::Arc, vec::Vec};
use cfg_if::cfg_if; use cfg_if::cfg_if;
use crossbeam_queue::SegQueue; use crossbeam_queue::SegQueue;
use kernel_util::{ use kernel_util::{sync::IrqGuard, util::OneTimeInit};
sync::{IrqGuard, IrqSafeSpinlock},
util::OneTimeInit,
};
use crate::{ use crate::{
arch::{Architecture, ArchitectureImpl, CpuAccess}, arch::{Architecture, ArchitectureImpl, CpuAccess},
@ -21,18 +16,6 @@ use super::{
Cpu, TaskContext, 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 /// Per-CPU queue
pub struct CpuQueue { pub struct CpuQueue {
queue: SegQueue<ThreadId>, queue: SegQueue<ThreadId>,
@ -59,7 +42,11 @@ impl CpuQueue {
self.index 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) -> ! { pub unsafe fn enter(&self) -> ! {
let _guard = IrqGuard::acquire(); let _guard = IrqGuard::acquire();
@ -106,7 +93,11 @@ impl CpuQueue {
(None, None) (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 { pub unsafe fn yield_cpu(&self) -> bool {
assert!(ArchitectureImpl::interrupt_mask()); assert!(ArchitectureImpl::interrupt_mask());
@ -124,7 +115,7 @@ impl CpuQueue {
match sched.state { match sched.state {
ThreadState::Ready => { ThreadState::Ready => {
panic!("Thread {:?} was running, but is marked Ready", current.id); self.queue.push(current.id);
} }
ThreadState::Running => { ThreadState::Running => {
sched.state = ThreadState::Ready; sched.state = ThreadState::Ready;

View File

@ -240,6 +240,7 @@ impl Thread {
/// Pushes a signal to the thread's signal queue /// Pushes a signal to the thread's signal queue
pub fn raise_signal(&self, signal: Signal) { pub fn raise_signal(&self, signal: Signal) {
self.signal_queue.push(signal); self.signal_queue.push(signal);
self.enqueue();
} }
// Scheduling // Scheduling
@ -248,7 +249,9 @@ impl Thread {
pub fn enqueue_to(&self, queue: &'static CpuQueue) { pub fn enqueue_to(&self, queue: &'static CpuQueue) {
let mut sched = self.sched.lock(); let mut sched = self.sched.lock();
assert_ne!(sched.state, ThreadState::Terminated); if sched.state == ThreadState::Terminated {
return;
}
match sched.state { match sched.state {
ThreadState::Running | ThreadState::Ready => { ThreadState::Running | ThreadState::Ready => {

View File

@ -1,7 +1,6 @@
//! Various kernel utility functions //! Various kernel utility functions
pub mod queue; pub mod queue;
pub mod ring;
/// Extension trait for [Iterator]s of [Result]s /// Extension trait for [Iterator]s of [Result]s
pub trait ResultIterator<T, E> { pub trait ResultIterator<T, E> {