diff --git a/.gitignore b/.gitignore index 53eaa21960..693699042b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target **/*.rs.bk +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml index 22ce267e3e..4089caf073 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ edition = "2018" [dependencies] generate = { path = "crates/generate" } +memory = { path = "crates/memory" } [workspace] members = ["crates/generate"] diff --git a/crates/memory/.gitignore b/crates/memory/.gitignore new file mode 100644 index 0000000000..a9d37c560c --- /dev/null +++ b/crates/memory/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/crates/memory/Cargo.toml b/crates/memory/Cargo.toml new file mode 100644 index 0000000000..f605782c99 --- /dev/null +++ b/crates/memory/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "memory" +version = "0.1.0" +authors = ["Pat Hickey "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +thiserror = "1" diff --git a/crates/memory/src/lib.rs b/crates/memory/src/lib.rs new file mode 100644 index 0000000000..aa768b498c --- /dev/null +++ b/crates/memory/src/lib.rs @@ -0,0 +1,188 @@ +#![allow(dead_code, unused)] // DURING DEVELOPMENT + +use std::cell::RefCell; +use std::marker::PhantomData; +use std::rc::Rc; +use thiserror::Error; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct Region { + start: u32, + len: u32, +} + +impl Region { + fn overlaps(&self, rhs: Region) -> bool { + let self_start = self.start as u64; + let self_end = self.start as u64 + self.len as u64; + + let rhs_start = rhs.start as u64; + let rhs_end = rhs.start as u64 + rhs.len as u64; + + // start of rhs inside self: + if (rhs_start >= self_start && rhs_start < self_end) { + return true; + } + + // end of rhs inside self: + if (rhs_end >= self_start && rhs_end < self_end) { + return true; + } + + // start of self inside rhs: + if (self_start >= rhs_start && self_start < rhs_end) { + return true; + } + + // end of self inside rhs: XXX is this redundant? i suspect it is but im too tired + if (self_end >= rhs_start && self_end < rhs_end) { + return true; + } + + false + } +} + +struct GuestBorrows { + immutable: Vec, + mutable: Vec, +} + +impl GuestBorrows { + pub fn new() -> Self { + GuestBorrows { + immutable: Vec::new(), + mutable: Vec::new(), + } + } + + fn is_borrowed_immut(&self, r: Region) -> bool { + !self.immutable.iter().all(|b| !b.overlaps(r)) + } + + fn is_borrowed_mut(&self, r: Region) -> bool { + !self.mutable.iter().all(|b| !b.overlaps(r)) + } + + pub fn borrow_immut(&mut self, r: Region) -> bool { + if self.is_borrowed_mut(r) { + return false; + } + self.immutable.push(r); + true + } + + pub fn unborrow_immut(&mut self, r: Region) { + let (ix, _) = self + .immutable + .iter() + .enumerate() + .find(|(_, reg)| r == **reg) + .expect("region exists in borrows"); + self.immutable.remove(ix); + } + + pub fn borrow_mut(&mut self, r: Region) -> bool { + if self.is_borrowed_immut(r) || self.is_borrowed_mut(r) { + return false; + } + self.mutable.push(r); + true + } + + pub fn unborrow_mut(&mut self, r: Region) { + let (ix, _) = self + .mutable + .iter() + .enumerate() + .find(|(_, reg)| r == **reg) + .expect("region exists in borrows"); + self.mutable.remove(ix); + } +} + +pub struct GuestMemory<'a> { + ptr: *mut u8, + len: u32, + lifetime: PhantomData<&'a ()>, + borrows: Rc>, +} + +impl<'a> GuestMemory<'a> { + pub fn new(ptr: *mut u8, len: u32) -> GuestMemory<'a> { + GuestMemory { + ptr, + len, + lifetime: PhantomData, + borrows: Rc::new(RefCell::new(GuestBorrows::new())), + } + } + + fn contains(&self, r: Region) -> bool { + r.start < self.len + && r.len < self.len // make sure next clause doesnt underflow + && r.start < (self.len - r.len) + } + + pub fn ptr(&'a self, r: Region) -> Result>, MemoryError> { + let mut borrows = self.borrows.borrow_mut(); + if !self.contains(r) { + Err(MemoryError::OutOfBounds)?; + } + if borrows.borrow_immut(r) { + Ok(Some(GuestPtr { + mem: &self, + region: r, + })) + } else { + Ok(None) + } + } + + pub fn ptr_mut(&'a self, r: Region) -> Result>, MemoryError> { + let mut borrows = self.borrows.borrow_mut(); + if !self.contains(r) { + Err(MemoryError::OutOfBounds)?; + } + if borrows.borrow_immut(r) { + Ok(Some(GuestPtrMut { + mem: &self, + region: r, + })) + } else { + Ok(None) + } + } + +} + +pub struct GuestPtr<'a> { + mem: &'a GuestMemory<'a>, + region: Region, +} + +impl<'a> Drop for GuestPtr<'a> { + fn drop(&mut self) { + let mut borrows = self.mem.borrows.borrow_mut(); + borrows.unborrow_immut(self.region); + } +} + + +pub struct GuestPtrMut<'a> { + mem: &'a GuestMemory<'a>, + region: Region, +} + +impl<'a> Drop for GuestPtrMut<'a> { + fn drop(&mut self) { + let mut borrows = self.mem.borrows.borrow_mut(); + borrows.unborrow_mut(self.region); + } +} + +#[derive(Debug, Error)] +pub enum MemoryError { + #[error("Out of bounds")] + OutOfBounds, +}