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
This commit is contained in:
commit
860f978670
16
Cargo.toml
16
Cargo.toml
|
@ -6,3 +6,19 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[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
144
src/io.rs
Normal 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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
19
src/lib.rs
19
src/lib.rs
|
@ -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]
|
#[macro_use]
|
||||||
pub mod logger;
|
pub mod logger;
|
||||||
pub mod driver;
|
pub mod driver;
|
||||||
pub mod process;
|
pub mod process;
|
||||||
pub mod syscalls;
|
pub mod syscalls;
|
||||||
|
#[cfg(feature = "io")]
|
||||||
|
pub mod io;
|
||||||
|
#[cfg(feature = "rng")]
|
||||||
|
pub mod rand;
|
||||||
|
|
||||||
pub use core::*;
|
pub use core::*;
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
pub fn get_random() -> u32;
|
|
||||||
}
|
|
||||||
|
|
21
src/rand.rs
Normal file
21
src/rand.rs
Normal 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>()
|
||||||
|
}
|
|
@ -1,12 +1,11 @@
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
//! The module of syscalls.
|
//! This module provides declarations for all host provided functions.
|
||||||
|
|
||||||
use crate::process::{signals::Signals, PID};
|
use crate::process::{signals::Signals, PID};
|
||||||
|
|
||||||
pub mod file_calls;
|
pub mod file_calls;
|
||||||
pub mod time_calls;
|
pub mod time_calls;
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
/// All system calls are defined here.
|
/// All system calls are defined here.
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
|
@ -21,4 +20,11 @@ extern "C" {
|
||||||
/// A temporary function to test the syscall interface
|
/// A temporary function to test the syscall interface
|
||||||
pub fn add(a: u32, b: u32) -> u32;
|
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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue