more musings

This commit is contained in:
nattthebear 2020-06-21 14:08:27 -04:00
parent 5404fb90d7
commit b0007c83f4
7 changed files with 427 additions and 19 deletions

View File

@ -6,6 +6,33 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "cloudabi"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
dependencies = [
"bitflags",
]
[[package]]
name = "getset"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24b328c01a4d71d2d8173daa93562a73ab0fe85616876f02500f53d82948c504"
dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -18,6 +45,15 @@ version = "0.2.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49"
[[package]]
name = "lock_api"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75"
dependencies = [
"scopeguard",
]
[[package]]
name = "page_size"
version = "0.4.2"
@ -28,14 +64,136 @@ dependencies = [
"winapi",
]
[[package]]
name = "parking_lot"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3"
dependencies = [
"cfg-if",
"cloudabi",
"libc",
"redox_syscall",
"smallvec",
"winapi",
]
[[package]]
name = "proc-macro-error"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98e9e4b82e0ef281812565ea4751049f1bdcdfccda7d3f459f2e138a40c08678"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f5444ead4e9935abd7f27dc51f7e852a0569ac888096d5ec2499470794e2e53"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn-mid",
"version_check",
]
[[package]]
name = "proc-macro2"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.1.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "smallvec"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4"
[[package]]
name = "syn"
version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a994520748611c17d163e81b6c4a4b13d11b7f63884362ab2efac3aa9cf16d00"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "syn-mid"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-xid"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
[[package]]
name = "version_check"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
[[package]]
name = "waterboxhost"
version = "0.1.0"
dependencies = [
"bitflags",
"getset",
"lazy_static",
"libc",
"page_size",
"parking_lot",
"winapi",
]

View File

@ -11,6 +11,8 @@ publish = false
bitflags = "1.2.1"
page_size = "0.4.2"
lazy_static = "1.4.0"
getset = "0.1.1"
parking_lot = "0.10.2"
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3.8", features = ["memoryapi", "handleapi", "errhandlingapi", "winnt"] }

View File

@ -1,11 +1,18 @@
#![crate_type = "cdylib"]
use std::io::{Read, Write};
const PAGESIZE: usize = 0x1000;
const PAGEMASK: usize = 0xfff;
const PAGESHIFT: i32 = 12;
mod memory_block;
pub trait IStateable {
fn save_sate(&mut self, stream: Box<dyn Write>);
fn load_state(&mut self, stream: Box<dyn Read>);
}
#[cfg(test)]
mod tests {
#[test]

View File

@ -2,8 +2,26 @@ mod pageblock;
mod pal;
mod tripguard;
use std::ops::{DerefMut, Deref};
use parking_lot::ReentrantMutex;
use std::collections::HashMap;
use std::sync::Mutex;
use pageblock::PageBlock;
use bitflags::bitflags;
use crate::*;
use getset::Getters;
use lazy_static::lazy_static;
lazy_static! {
static ref LOCK_LIST: Mutex<HashMap<u32, ReentrantMutex<Option<MemoryBlockRef>>>> = Mutex::new(HashMap::new());
}
fn alignDown(p: usize) -> usize {
p & !PAGEMASK
}
fn alignUp(p: usize) -> usize {
((p - 1) | PAGEMASK) + 1
}
bitflags! {
struct PageFlags: u32 {
@ -18,7 +36,16 @@ bitflags! {
const STACK = 32;
}
}
pub enum Protection {
None,
R,
RW,
RX,
RWX,
RWStack
}
#[derive(Debug)]
enum Snapshot {
None,
ZeroFilled,
@ -26,15 +53,231 @@ enum Snapshot {
}
/// Information about a single page of memory
#[derive(Debug)]
struct Page {
pub flags: PageFlags,
pub snapshot: Snapshot,
}
#[derive(Getters)]
#[derive(Debug)]
struct MemoryBlock {
pub pages: Vec<Page>,
pub start: usize,
pub length: usize,
pub end: usize,
pub sealed: bool,
#[get]
pages: Vec<Page>,
#[get]
start: usize,
#[get]
length: usize,
#[get]
end: usize,
#[get]
sealed: bool,
lock_index: u32,
handle: pal::Handle,
lock_count: u32,
}
struct MemoryBlockGuard<'a> {
block: &'a mut MemoryBlock,
}
impl<'a> Drop for MemoryBlockGuard<'a> {
fn drop(&mut self) {
self.block.deactivate();
}
}
impl<'a> Deref for MemoryBlockGuard<'a> {
type Target = MemoryBlock;
fn deref(&self) -> &MemoryBlock {
self.block
}
}
impl<'a> DerefMut for MemoryBlockGuard<'a> {
fn deref_mut(&mut self) -> &mut MemoryBlock {
self.block
}
}
impl MemoryBlock {
pub fn new(start: usize, length: usize) -> MemoryBlock {
if start != alignDown(start) || length != alignDown(length) {
panic!("Addresses and sizes must be aligned!");
}
let end = start + length;
if start >> 32 != (end - 1) >> 32 {
panic!("MemoryBlock must fit into a single 4G region!");
}
let npage = length >> PAGESHIFT;
let mut pages = Vec::new();
pages.reserve_exact(npage);
for _ in 0..npage {
pages.push(Page {
flags: PageFlags::empty(),
snapshot: Snapshot::None,
});
}
let handle = pal::open(length).unwrap();
let lock_index = (start >> 32) as u32;
// add the lock_index stuff now, so we won't have to check for it later on activate / drop
{
let map = &mut LOCK_LIST.lock().unwrap();
map.entry(lock_index).or_insert(ReentrantMutex::new(None));
}
MemoryBlock {
pages,
start,
length,
end,
sealed: false,
lock_index,
handle,
lock_count: 0,
}
}
pub fn enter(&mut self) -> MemoryBlockGuard {
self.activate();
MemoryBlockGuard {
block: self,
}
}
/// lock self, and potentially swap this block into memory
pub fn activate(&mut self) {
unsafe {
let map = &mut LOCK_LIST.lock().unwrap();
let lock = map.get_mut(&self.lock_index).unwrap();
std::mem::forget(lock.lock());
let other_opt = lock.get_mut();
match *other_opt {
Some(MemoryBlockRef(other)) => {
if other != self {
assert!(!(*other).active());
(*other).swapout();
self.swapin();
*other_opt = Some(MemoryBlockRef(self));
}
},
None => {
self.swapin();
*other_opt = Some(MemoryBlockRef(self));
}
}
self.lock_count += 1;
}
}
/// unlock self, and potentially swap this block out of memory
pub fn deactivate(&mut self) {
unsafe {
assert!(self.active());
let map = &mut LOCK_LIST.lock().unwrap();
let lock = map.get_mut(&self.lock_index).unwrap();
self.lock_count -= 1;
#[cfg(debug_assertions)]
{
let other_opt = lock.get_mut();
let other = other_opt.as_ref().unwrap().0;
assert_eq!(&*other, self);
// in debug mode, forcibly evict to catch dangling pointers
if !self.active() {
self.swapout();
*other_opt = None;
}
}
lock.force_unlock();
}
}
unsafe fn swapin(&mut self) {
assert!(pal::map(&self.handle, self.start, self.length));
}
unsafe fn swapout(&mut self) {
assert!(pal::unmap(self.start, self.length));
}
pub fn active (&self) -> bool {
self.lock_count > 0
}
pub fn protect(&mut self, start: usize, length: usize, prot: Protection) {
}
pub fn seal(&mut self) {
}
}
impl IStateable for MemoryBlock {
fn save_sate(&mut self, stream: Box<dyn Write>) {
}
fn load_state(&mut self, stream: Box<dyn Read>) {
}
}
impl Drop for MemoryBlock {
fn drop(&mut self) {
assert!(!self.active());
let map = &mut LOCK_LIST.lock().unwrap();
let lock = map.get_mut(&self.lock_index).unwrap();
let other_opt = lock.get_mut();
match *other_opt {
Some(MemoryBlockRef(other)) => {
if other == self {
unsafe {
self.swapout();
}
*other_opt = None;
}
},
None => ()
}
let mut h = pal::bad();
std::mem::swap(&mut h, &mut self.handle);
unsafe {
pal::close(h);
}
}
}
impl PartialEq for MemoryBlock {
fn eq(&self, other: &MemoryBlock) -> bool {
self as *const MemoryBlock == other as *const MemoryBlock
}
}
impl Eq for MemoryBlock {}
struct MemoryBlockRef(*mut MemoryBlock);
unsafe impl Send for MemoryBlockRef {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_basic() {
drop(MemoryBlock::new(0x36300000000, 0x50000));
drop(MemoryBlock::new(0x36b00000000, 0x2000));
{
let mut b = MemoryBlock::new(0x36100000000, 0x65000);
b.activate();
b.deactivate();
b.enter();
}
{
let mut b = MemoryBlock::new(0x36e00000000, 0x5000);
b.activate();
b.activate();
let mut guard = b.enter();
guard.activate();
guard.deactivate();
drop(guard);
b.deactivate();
b.deactivate();
b.enter();
}
}
}

View File

@ -3,6 +3,7 @@ use core::ffi::c_void;
use crate::*;
/// wraps the allocation of a single PAGESIZE bytes of ram, and is safe-ish to call within a signal handler
#[derive(Debug)]
pub struct PageBlock {
ptr: NonNull<u8>,
}

View File

@ -1,16 +1,9 @@
// Platform abstraction layer over mmap/etc. Doesn't do much checking, not meant for general consumption
use super::Protection;
#[derive(Debug)]
pub struct Handle(usize);
pub enum Protection {
None,
R,
RW,
RX,
RWX,
RWStack
}
#[cfg(windows)]
pub use win::*;
#[cfg(windows)]
@ -52,6 +45,10 @@ mod win {
CloseHandle(handle.0 as *mut c_void) != 0
}
pub fn bad() -> Handle {
return Handle(INVALID_HANDLE_VALUE as usize);
}
pub fn map(handle: &Handle, start: usize, size: usize) -> bool {
unsafe {
let res = MapViewOfFileEx(
@ -124,6 +121,10 @@ mod nix {
libc::close(handle.0 as i32) == 0
}
pub fn bad() -> Handle {
return Handle(-1i32 as usize);
}
pub fn map(handle: &Handle, start: usize, size: usize) -> bool {
unsafe {
let res = mmap(start as *mut c_void,

View File

@ -17,10 +17,6 @@ struct GlobalData {
initialized: bool,
active_blocks: Vec<MemoryBlockRef>,
}
struct MemoryBlockRef(*mut MemoryBlock);
unsafe impl Send for MemoryBlockRef {
}
unsafe fn register(block: *mut MemoryBlock) {
let mut data = global_data.lock().unwrap();
@ -64,7 +60,7 @@ unsafe fn trip(addr: usize) -> TripResult {
page.snapshot = Snapshot::Data(snapshot);
}
page.flags.insert(PageFlags::DIRTY);
let new_prot = if page.flags.contains(PageFlags::X) { pal::Protection::RWX } else { pal::Protection::RW };
let new_prot = if page.flags.contains(PageFlags::X) { Protection::RWX } else { Protection::RW };
assert!(pal::protect(page_start_addr, PAGESIZE, new_prot));
TripResult::Handled
}