vfs: implement PTY devices
This commit is contained in:
parent
69c73454c1
commit
2444e147c4
@ -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;
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
use yggdrasil_abi::error::Error;
|
|
||||||
|
|
||||||
use crate::Device;
|
|
@ -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;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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],
|
||||||
|
@ -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},
|
||||||
|
@ -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) {
|
||||||
|
@ -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 {}
|
||||||
|
|
||||||
|
@ -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,
|
@ -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};
|
||||||
|
|
||||||
|
@ -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(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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};
|
||||||
|
@ -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,
|
||||||
|
@ -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
417
lib/vfs/src/pty.rs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
@ -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>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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},
|
||||||
|
@ -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 {}
|
|
||||||
}
|
|
||||||
|
@ -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;
|
||||||
|
@ -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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
|
@ -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,
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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 => {
|
||||||
|
@ -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> {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user