From 6bb4f38edcb4365817f8401a9a75e7047ad58908 Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Tue, 16 Nov 2021 14:34:14 +0200 Subject: [PATCH] feature: Stdin/Stdout/Stderr structs --- Cargo.lock | 16 +++++ libusr/Cargo.toml | 1 + libusr/src/file.rs | 20 ++++++- libusr/src/io.rs | 65 -------------------- libusr/src/io/error.rs | 35 +++++++++++ libusr/src/io/mod.rs | 32 ++++++++++ libusr/src/io/stdio.rs | 130 ++++++++++++++++++++++++++++++++++++++++ libusr/src/io/writer.rs | 26 ++++++++ libusr/src/lib.rs | 33 +++++----- libusr/src/os.rs | 33 +--------- libusr/src/sync.rs | 54 +++++++++++++++++ libusr/src/sys/mod.rs | 39 ++++++++++++ user/src/shell/main.rs | 48 +++------------ 13 files changed, 376 insertions(+), 156 deletions(-) delete mode 100644 libusr/src/io.rs create mode 100644 libusr/src/io/error.rs create mode 100644 libusr/src/io/mod.rs create mode 100644 libusr/src/io/stdio.rs create mode 100644 libusr/src/io/writer.rs create mode 100644 libusr/src/sync.rs create mode 100644 libusr/src/sys/mod.rs diff --git a/Cargo.lock b/Cargo.lock index a042c61..0567f66 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -97,6 +97,15 @@ dependencies = [ "syn", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin", +] + [[package]] name = "libsys" version = "0.1.0" @@ -108,6 +117,7 @@ dependencies = [ name = "libusr" version = "0.1.0" dependencies = [ + "lazy_static", "libsys", ] @@ -195,6 +205,12 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "static_assertions" version = "1.1.0" diff --git a/libusr/Cargo.toml b/libusr/Cargo.toml index 0b91506..feb52ee 100644 --- a/libusr/Cargo.toml +++ b/libusr/Cargo.toml @@ -7,3 +7,4 @@ edition = "2021" [dependencies] libsys = { path = "../libsys", features = ["user"] } +lazy_static = { version = "^1.4.0", features = ["spin_no_std"] } diff --git a/libusr/src/file.rs b/libusr/src/file.rs index d90eff7..80e7727 100644 --- a/libusr/src/file.rs +++ b/libusr/src/file.rs @@ -1,12 +1,26 @@ +use crate::io::{AsRawFd, Error}; +use crate::os; +use crate::trace; use libsys::stat::FileDescriptor; -use crate::io; pub struct File { - fd: FileDescriptor + fd: FileDescriptor, } impl File { - pub fn open(path: &str) -> Result { + pub fn open(path: &str) -> Result { todo!() } } + +impl AsRawFd for File { + fn as_raw_fd(&self) -> FileDescriptor { + self.fd + } +} + +impl Drop for File { + fn drop(&mut self) { + todo!(); + } +} diff --git a/libusr/src/io.rs b/libusr/src/io.rs deleted file mode 100644 index 2cbb407..0000000 --- a/libusr/src/io.rs +++ /dev/null @@ -1,65 +0,0 @@ -use core::fmt; -use libsys::{ - calls::{sys_fstatat, sys_write}, - stat::{Stat, FileDescriptor}, -}; - -// TODO populate this type -pub struct Error; - -pub fn stat(pathname: &str) -> Result { - let mut buf = Stat::default(); - // TODO error handling - let res = unsafe { sys_fstatat(None, pathname, &mut buf, 0).unwrap() }; - Ok(buf) -} - -// print!/println! group - -#[macro_export] -macro_rules! print { - ($($args:tt)+) => ($crate::io::_print($crate::sys::FileDescriptor::STDOUT, format_args!($($args)+))) -} - -#[macro_export] -macro_rules! println { - ($($args:tt)+) => (print!("{}\n", format_args!($($args)+))) -} - -#[macro_export] -macro_rules! eprint { - ($($args:tt)+) => ($crate::io::_print($crate::sys::FileDescriptor::STDERR, format_args!($($args)+))) -} - -#[macro_export] -macro_rules! eprintln { - ($($args:tt)+) => (eprint!("{}\n", format_args!($($args)+))) -} - -struct BufferWriter<'a> { - buf: &'a mut [u8], - pos: usize, -} - -impl fmt::Write for BufferWriter<'_> { - fn write_str(&mut self, s: &str) -> fmt::Result { - for byte in s.bytes() { - self.buf[self.pos] = byte; - self.pos += 1; - } - Ok(()) - } -} - -pub fn _print(fd: FileDescriptor, args: fmt::Arguments) { - use core::fmt::Write; - static mut BUFFER: [u8; 4096] = [0; 4096]; - let mut writer = BufferWriter { - buf: unsafe { &mut BUFFER }, - pos: 0, - }; - writer.write_fmt(args).ok(); - unsafe { - sys_write(fd, &BUFFER[..writer.pos]); - } -} diff --git a/libusr/src/io/error.rs b/libusr/src/io/error.rs new file mode 100644 index 0000000..9818834 --- /dev/null +++ b/libusr/src/io/error.rs @@ -0,0 +1,35 @@ +use libsys::error::Errno; + +#[derive(Debug)] +pub struct Error { + repr: Repr, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] +pub enum ErrorKind { + NotFound, + PermissionDenied, + InvalidData, +} + +#[derive(Debug)] +enum Repr { + Os(Errno), + Simple(ErrorKind), +} + +impl Error { + pub const fn new(kind: ErrorKind) -> Self { + Self { + repr: Repr::Simple(kind), + } + } +} + +impl From for Error { + fn from(e: Errno) -> Self { + Self { + repr: Repr::Os(e) + } + } +} diff --git a/libusr/src/io/mod.rs b/libusr/src/io/mod.rs new file mode 100644 index 0000000..ac29239 --- /dev/null +++ b/libusr/src/io/mod.rs @@ -0,0 +1,32 @@ +use libsys::{ + calls::sys_fstatat, + stat::{FileDescriptor, Stat}, +}; +use core::fmt; + +mod error; +pub use error::{Error, ErrorKind}; +mod writer; +pub use writer::{_print}; +mod stdio; +pub use stdio::{stderr, stdin, stdout, Stderr, Stdin, Stdout}; + +pub trait Read { + fn read(&mut self, bytes: &mut [u8]) -> Result; +} + +pub trait Write { + fn write(&mut self, bytes: &[u8]) -> Result; + fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> Result<(), Error>; +} + +pub trait AsRawFd { + fn as_raw_fd(&self) -> FileDescriptor; +} + +pub fn stat(pathname: &str) -> Result { + let mut buf = Stat::default(); + // TODO error handling + let res = sys_fstatat(None, pathname, &mut buf, 0).unwrap(); + Ok(buf) +} diff --git a/libusr/src/io/stdio.rs b/libusr/src/io/stdio.rs new file mode 100644 index 0000000..de7fc99 --- /dev/null +++ b/libusr/src/io/stdio.rs @@ -0,0 +1,130 @@ +use libsys::{ + stat::FileDescriptor, + calls::{sys_read, sys_write} +}; +use crate::io::{Read, Write, Error}; +use crate::sync::{Mutex, MutexGuard}; +use core::fmt; + +struct InputInner { + fd: FileDescriptor +} +struct OutputInner { + fd: FileDescriptor +} + +pub struct StdinLock<'a> { + lock: MutexGuard<'a, InputInner> +} + +pub struct StdoutLock<'a> { + lock: MutexGuard<'a, OutputInner> +} + +pub struct StderrLock<'a> { + lock: MutexGuard<'a, OutputInner> +} + +pub struct Stdin { + inner: &'static Mutex, +} + +pub struct Stdout { + inner: &'static Mutex +} + +pub struct Stderr { + inner: &'static Mutex +} + +// STDIN + +impl Read for InputInner { + fn read(&mut self, bytes: &mut [u8]) -> Result { + sys_read(self.fd, bytes).map_err(Error::from) + } +} + +impl Read for Stdin { + fn read(&mut self, bytes: &mut [u8]) -> Result { + self.inner.lock().read(bytes) + } +} + +// STDOUT/STDERR + +impl fmt::Write for OutputInner { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.write(s.as_bytes()).map(|_| ()).map_err(|_| todo!()) + } +} + +impl Write for OutputInner { + fn write(&mut self, bytes: &[u8]) -> Result { + sys_write(self.fd, bytes).map_err(Error::from) + } + + fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> Result<(), Error> { + fmt::Write::write_fmt(self, args).map_err(|_| todo!()) + } +} + +impl Write for Stdout { + fn write(&mut self, bytes: &[u8]) -> Result { + self.inner.lock().write(bytes) + } + + fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> Result<(), Error> { + self.inner.lock().write_fmt(args) + } +} + +impl Write for Stderr { + fn write(&mut self, bytes: &[u8]) -> Result { + self.inner.lock().write(bytes) + } + + fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> Result<(), Error> { + self.inner.lock().write_fmt(args) + } +} + +impl Stdout { + pub fn lock(&self) -> StdoutLock { + StdoutLock { + lock: self.inner.lock() + } + } +} + +impl Stderr { + pub fn lock(&self) -> StderrLock { + StderrLock { + lock: self.inner.lock() + } + } +} + +lazy_static! { + static ref STDIN: Mutex = Mutex::new(InputInner { + fd: FileDescriptor::STDIN + }); + static ref STDOUT: Mutex = Mutex::new(OutputInner { + fd: FileDescriptor::STDOUT + }); + static ref STDERR: Mutex = Mutex::new(OutputInner { + fd: FileDescriptor::STDOUT + }); +} + +pub fn stdin() -> Stdin { + Stdin { inner: &STDIN } +} + +pub fn stdout() -> Stdout { + Stdout { inner: &STDOUT } +} + +pub fn stderr() -> Stderr { + Stderr { inner: &STDERR } +} diff --git a/libusr/src/io/writer.rs b/libusr/src/io/writer.rs new file mode 100644 index 0000000..30ed6d8 --- /dev/null +++ b/libusr/src/io/writer.rs @@ -0,0 +1,26 @@ +use core::fmt; +use crate::io::{self, Write}; + +#[macro_export] +macro_rules! print { + ($($args:tt)+) => ($crate::io::_print($crate::io::stdout, format_args!($($args)+))) +} + +#[macro_export] +macro_rules! println { + ($($args:tt)+) => (print!("{}\n", format_args!($($args)+))) +} + +#[macro_export] +macro_rules! eprint { + ($($args:tt)+) => ($crate::io::_print($crate::io::stderr, format_args!($($args)+))) +} + +#[macro_export] +macro_rules! eprintln { + ($($args:tt)+) => (eprint!("{}\n", format_args!($($args)+))) +} + +pub fn _print(out: fn() -> T, args: fmt::Arguments) { + out().write_fmt(args).expect("stdout/stderr write failed"); +} diff --git a/libusr/src/lib.rs b/libusr/src/lib.rs index 18c547b..f85f49a 100644 --- a/libusr/src/lib.rs +++ b/libusr/src/lib.rs @@ -4,23 +4,19 @@ use core::panic::PanicInfo; use libsys::proc::ExitCode; +#[macro_use] +extern crate lazy_static; + +pub mod file; pub mod io; pub mod os; -pub mod file; - -pub mod sys { - pub use libsys::signal::{Signal, SignalDestination}; - pub use libsys::termios; - pub use libsys::calls::*; - pub use libsys::stat::{self, FileDescriptor}; -} +pub mod sys; +pub mod sync; #[inline(never)] extern "C" fn _signal_handler(arg: sys::Signal) -> ! { trace!("Entered signal handler: arg={:?}", arg); - unsafe { - sys::sys_ex_sigreturn(); - } + sys::sys_ex_sigreturn(); } static mut SIGNAL_STACK: [u8; 4096] = [0; 4096]; @@ -31,17 +27,22 @@ extern "C" fn _start(_arg: usize) -> ! { extern "Rust" { fn main() -> i32; } - unsafe { - SIGNAL_STACK[0] = 1; - sys::sys_ex_signal(_signal_handler as usize, SIGNAL_STACK.as_ptr() as usize + 4096); - sys::sys_exit(ExitCode::from(main())); + unsafe { + sys::sys_ex_signal( + _signal_handler as usize, + SIGNAL_STACK.as_ptr() as usize + 4096, + ) + .unwrap(); } + + let res = unsafe { main() }; + sys::sys_exit(ExitCode::from(res)); } #[panic_handler] fn panic_handler(pi: &PanicInfo) -> ! { - // TODO formatted messages + // TODO print to stdout/stderr (if available) trace!("Panic ocurred: {}", pi); sys::sys_exit(ExitCode::from(-1)); } diff --git a/libusr/src/os.rs b/libusr/src/os.rs index 00c00ab..85a385a 100644 --- a/libusr/src/os.rs +++ b/libusr/src/os.rs @@ -3,35 +3,6 @@ use core::fmt; use core::mem::{size_of, MaybeUninit}; use libsys::{ioctl::IoctlCmd, stat::FileDescriptor, termios::Termios}; -pub fn get_tty_attrs(fd: FileDescriptor) -> Result { - let mut termios = MaybeUninit::::uninit(); - let res = sys::sys_ioctl( - fd, - IoctlCmd::TtyGetAttributes, - termios.as_mut_ptr() as usize, - size_of::(), - ) - .unwrap(); - if res != size_of::() { - return Err("Failed"); - } - Ok(unsafe { termios.assume_init() }) -} - -pub fn set_tty_attrs(fd: FileDescriptor, attrs: &Termios) -> Result<(), &'static str> { - let res = sys::sys_ioctl( - fd, - IoctlCmd::TtySetAttributes, - attrs as *const _ as usize, - size_of::(), - ) - .unwrap(); - if res != size_of::() { - return Err("Failed"); - } - Ok(()) -} - #[macro_export] macro_rules! trace { ($($args:tt)+) => ($crate::os::_trace(format_args!($($args)+))) @@ -60,7 +31,5 @@ pub fn _trace(args: fmt::Arguments) { pos: 0, }; writer.write_fmt(args).ok(); - unsafe { - sys::sys_ex_debug_trace(&BUFFER[..writer.pos]); - } + sys::sys_ex_debug_trace(unsafe { &BUFFER[..writer.pos] }).ok(); } diff --git a/libusr/src/sync.rs b/libusr/src/sync.rs new file mode 100644 index 0000000..e04b5c9 --- /dev/null +++ b/libusr/src/sync.rs @@ -0,0 +1,54 @@ +use core::cell::UnsafeCell; +use core::ops::{Deref, DerefMut}; +use crate::sys::RawMutex; + +pub struct Mutex { + inner: RawMutex, + data: UnsafeCell +} + +pub struct MutexGuard<'a, T> { + data: &'a mut T, + lock: &'a RawMutex, +} + +impl Mutex { + pub fn new(t: T) -> Self { + Self { + inner: RawMutex::new(), + data: UnsafeCell::new(t) + } + } + + pub fn lock(&self) -> MutexGuard<'_, T> { + unsafe { + self.inner.lock(); + MutexGuard { + data: (&mut *self.data.get()), + lock: &self.inner + } + } + } +} + +impl<'a, T> Drop for MutexGuard<'a, T> { + fn drop(&mut self) { + unsafe { self.lock.release(); } + } +} + +impl<'a, T> Deref for MutexGuard<'a, T> { + type Target = T; + + fn deref(&self) -> &T { + self.data + } +} + +impl<'a, T> DerefMut for MutexGuard<'a, T> { + fn deref_mut(&mut self) -> &mut T { + self.data + } +} + +unsafe impl Sync for Mutex {} diff --git a/libusr/src/sys/mod.rs b/libusr/src/sys/mod.rs new file mode 100644 index 0000000..af55f54 --- /dev/null +++ b/libusr/src/sys/mod.rs @@ -0,0 +1,39 @@ +pub use libsys::signal::{Signal, SignalDestination}; +pub use libsys::termios; +pub use libsys::calls::*; +pub use libsys::stat::{self, FileDescriptor}; + +use core::sync::atomic::{Ordering, AtomicBool}; + +// TODO replace with a proper mutex impl +pub(crate) struct RawMutex { + inner: AtomicBool +} + +impl RawMutex { + pub const fn new() -> Self { + Self { inner: AtomicBool::new(false) } + } + + #[inline] + unsafe fn try_lock(&self) -> bool { + self.inner.compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed).is_ok() + } + + #[inline] + unsafe fn is_locked(&self) -> bool { + self.inner.load(Ordering::Acquire) + } + + #[inline] + pub unsafe fn lock(&self) { + while !self.try_lock() { + asm!("nop"); + } + } + + #[inline] + pub unsafe fn release(&self) { + self.inner.store(false, Ordering::Release); + } +} diff --git a/user/src/shell/main.rs b/user/src/shell/main.rs index fbf27f2..d6044a1 100644 --- a/user/src/shell/main.rs +++ b/user/src/shell/main.rs @@ -4,54 +4,22 @@ #[macro_use] extern crate libusr; -use libusr::sys::stat::{FdSet, FileDescriptor}; -use libusr::sys::{Signal, SignalDestination}; - -fn readline(fd: FileDescriptor, buf: &mut [u8]) -> Result<&str, ()> { - // select() just for test - loop { - let mut rfds = FdSet::empty(); - rfds.set(fd); - let res = unsafe { - libusr::sys::sys_select(Some(&mut rfds), None, 1_000_000_000).unwrap() - }; - if res == 0 { - continue; - } - if !rfds.is_set(fd) { - panic!(); - } - - let count = unsafe { libusr::sys::sys_read(fd, buf).unwrap() }; - return core::str::from_utf8(&buf[..count as usize]).map_err(|_| ()); - } -} +use libusr::io::{self, Read}; #[no_mangle] fn main() -> i32 { let mut buf = [0; 512]; + let mut stdin = io::stdin(); + + eprintln!("stderr test"); loop { - print!("> "); - let line = readline(FileDescriptor::STDIN, &mut buf).unwrap(); - if line.is_empty() { - break; - } - let line = line.trim_end_matches('\n'); - - println!(":: {:?}", line); - - if line == "test" { - unsafe { - libusr::sys::sys_ex_kill(SignalDestination::This, Signal::Interrupt); - } - trace!("Returned from signal"); - continue; - } - - if line == "quit" || line == "exit" { + let count = stdin.read(&mut buf).unwrap(); + if count == 0 { break; } + let line = core::str::from_utf8(&buf[..count]).unwrap(); + println!("{:?}", line); } 0