Merge pull request 'add new syscalls; cross platform abstraction for io and rng seeding' (#1) from Monadic-Cat/libwasm:add-io-rand into master

Reviewed-on: https://git.ablecorp.us:443/able/libwasm/pulls/1
master
able 2022-04-25 20:40:11 +00:00
commit e50b7beb8a
5 changed files with 203 additions and 7 deletions

View File

@ -6,3 +6,19 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
once_cell = { version = "1.10.0", default-features = false, features = ["std"], optional = true }
rand_core = { version = "0.6", default-features = false, optional = true }
rand_pcg = { version = "0.3.1", default-features = false, optional = true }
[target.'cfg(not(ableOS))'.dependencies]
rand_core = { version = "0.6", default-features = false, optional = true, features = ["getrandom"] }
[features]
default = ["rng", "pcg_rng", "io"]
# Controls availability of the `rand` module, which provides seeding for `rand` (the crate) RNGs.
rng = ["dep:rand_core"]
# Controls the availability of `obtain_rng`, which internally uses `rand_pcg`.
pcg_rng = ["rng", "dep:rand_pcg"]
# Controls the availability of the cross platform `io` module.
io = ["dep:once_cell"]

144
src/io.rs Normal file
View File

@ -0,0 +1,144 @@
//! This module provides cross platform I/O facilities. It takes inspiration from the Rust `std::io` module,
//! and uses that for platform support outside of ableOS.
//! One day, we might hope for this module to be superseded by real `std` support for ableOS.
use ::std::io::{self, Read, BufRead, BufReader, IoSliceMut};
/// Print a string to the console.
fn print(x: &str) {
#[cfg(ableOS)] {
extern "C" {
fn print_char(x: u32);
}
fn print_byte(x: u8) {
unsafe { print_char(x as u32) }
}
for b in x.bytes() {
print_byte(b);
}
}
#[cfg(not(ableOS))] {
print!("{}", x)
}
}
/// Like [`print`], but starts a new line after.
pub fn println(x: &str) {
print(x);
print("\n");
}
/// Like [`println`], but meant to print to a standard error output.
pub fn eprintln(x: &str) {
#[cfg(ableOS)] {
println(x);
}
#[cfg(not(ableOS))] {
eprintln!("{}", x)
}
}
#[cfg(ableOS)]
static STDIN: ::once_cell::sync::Lazy<::std::sync::Mutex<BufReader<AbleStdinRaw>>> = ::once_cell::sync::Lazy::new(|| {
::std::sync::Mutex::new(BufReader::new(AbleStdinRaw))
});
#[cfg(ableOS)]
struct AbleStdinRaw;
#[cfg(ableOS)]
impl Read for AbleStdinRaw {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
// TODO: detect EOF
loop {
let chr = unsafe { crate::syscalls::get_input() };
if chr != 0 {
break Ok(char::from_u32(chr as u32).unwrap().encode_utf8(buf).len())
}
}
}
}
/// A handle to the standard input stream of the process.
/// See [`Stdin`](::std::io::Stdin) for roughly how this should be treated.
pub struct Stdin {
#[cfg(ableOS)]
inner: &'static ::std::sync::Mutex<BufReader<AbleStdinRaw>>,
#[cfg(not(ableOS))]
inner: io::Stdin,
}
impl Stdin {
pub fn lock(&self) -> StdinLock<'_> {
#[cfg(ableOS)] {
StdinLock {
inner: self.inner.lock().unwrap()
}
}
#[cfg(not(ableOS))] {
StdinLock {
inner: self.inner.lock(),
}
}
}
}
/// A locked reference to the [`Stdin`] handle.
/// See [`StdinLock`](::std::io::StdinLock) for roughly how this should be treated.
pub struct StdinLock<'a> {
#[cfg(ableOS)]
inner: ::std::sync::MutexGuard<'a, BufReader<AbleStdinRaw>>,
#[cfg(not(ableOS))]
inner: io::StdinLock<'a>,
}
impl<'a> BufRead for StdinLock<'_> {
fn fill_buf(&mut self) -> io::Result<&[u8]> {
self.inner.fill_buf()
}
fn consume(&mut self, n: usize) {
self.inner.consume(n)
}
fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> io::Result<usize> {
self.inner.read_until(byte, buf)
}
fn read_line(&mut self, buf: &mut String) -> io::Result<usize> {
self.inner.read_line(buf)
}
}
impl Read for StdinLock<'_> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.inner.read(buf)
}
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.inner.read_vectored(bufs)
}
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
self.inner.read_to_end(buf)
}
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
self.inner.read_to_string(buf)
}
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
self.inner.read_exact(buf)
}
}
/// Constructs a new handle to the standard input of the current process.
/// Each handle returned is a reference to a shared global buffer whose access is synchronized via a mutex.
/// If you need more explicit control over locking, see the [`Stdin::lock`] method.
pub fn stdin() -> Stdin {
#[cfg(ableOS)] {
Stdin {
inner: &STDIN
}
}
#[cfg(not(ableOS))] {
Stdin {
inner: ::std::io::stdin(),
}
}
}

View File

@ -1,13 +1,22 @@
#![no_std]
//! The base crate for writing guest programs to run on ableOS.
//!
//! ableOS is not a target known to rustc, and so we rely on a general `--cfg` switch
//! to control the use of ableOS target specific code when underlying the portable
//! abstractions defined in this crate.
//!
//! To target ableOS, set `cfg(ableOS)` and `--target=wasm32-unknown-unknown`.
//! ```text
//! RUSTFLAGS='--cfg=ableOS' cargo build --target=wasm32-unknown-unknown
//! ```
#[macro_use]
pub mod logger;
pub mod driver;
pub mod process;
pub mod syscalls;
#[cfg(feature = "io")]
pub mod io;
#[cfg(feature = "rng")]
pub mod rand;
pub use core::*;
extern "C" {
pub fn get_random() -> u32;
}

21
src/rand.rs Normal file
View File

@ -0,0 +1,21 @@
use ::rand_core::SeedableRng;
/// Seed an RNG with system provided randomness.
pub fn seed_rng<R: SeedableRng>() -> R {
#[cfg(ableOS)] {
let seed = unsafe {
let seed_a = crate::syscalls::get_random();
let seed_b = crate::syscalls::get_random();
(seed_a as u64) << 32 | seed_b as u64
};
R::seed_from_u64(seed)
}
#[cfg(not(ableOS))] {
R::from_entropy()
}
}
#[cfg(feature = "pcg_rng")]
pub fn obtain_rng() -> impl ::rand_core::RngCore {
seed_rng::<::rand_pcg::Pcg32>()
}

View File

@ -1,12 +1,11 @@
#![deny(missing_docs)]
//! The module of syscalls.
//! This module provides declarations for all host provided functions.
use crate::process::{signals::Signals, PID};
pub mod file_calls;
pub mod time_calls;
#[no_mangle]
/// All system calls are defined here.
extern "C" {
@ -21,4 +20,11 @@ extern "C" {
/// A temporary function to test the syscall interface
pub fn add(a: u32, b: u32) -> u32;
// TODO: ascertain whether the ableOS host provides randomness good enough for CSPRNGs
/// The ableOS host provides entropy usable for seeding PRNGs.
pub fn get_random() -> u32;
// This currently has very x86 specific semantics.
/// Pops a byte from the key buffer.
pub fn get_input() -> i32;
}