more musings
This commit is contained in:
parent
5404fb90d7
commit
b0007c83f4
|
@ -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",
|
||||
]
|
||||
|
||||
|
|
|
@ -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"] }
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>,
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue