Softpage improvements
This commit is contained in:
parent
af1de4b9ec
commit
bd9b4e0364
|
@ -1,2 +1,3 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["hbasm", "hbbytecode", "hbvm"]
|
resolver = "2"
|
||||||
|
members = ["hbasm", "hbbytecode", "hbvm"]
|
||||||
|
|
|
@ -35,12 +35,12 @@ macro_rules! constmod {
|
||||||
/// - I: Immediate
|
/// - I: Immediate
|
||||||
/// - L: Memory load / store size (u16)
|
/// - L: Memory load / store size (u16)
|
||||||
/// - Other types are identity-mapped
|
/// - Other types are identity-mapped
|
||||||
///
|
///
|
||||||
/// # BRC special-case
|
/// # BRC special-case
|
||||||
/// BRC's 3rd operand is plain byte, not a register. Encoding is the same, but for some cases it may matter.
|
/// BRC's 3rd operand is plain byte, not a register. Encoding is the same, but for some cases it may matter.
|
||||||
///
|
///
|
||||||
/// Please, if you distinguish in your API between byte and register, special case this one.
|
/// Please, if you distinguish in your API between byte and register, special case this one.
|
||||||
///
|
///
|
||||||
/// Sorry for that :(
|
/// Sorry for that :(
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! invoke_with_def {
|
macro_rules! invoke_with_def {
|
||||||
|
@ -134,34 +134,29 @@ constmod!(pub opcode(u8) {
|
||||||
|
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
pub struct ParamBBBB(pub u8, pub u8, pub u8, pub u8);
|
pub struct ParamBBBB(pub u8, pub u8, pub u8, pub u8);
|
||||||
|
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
pub struct ParamBBB(pub u8, pub u8, pub u8);
|
pub struct ParamBBB(pub u8, pub u8, pub u8);
|
||||||
|
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
pub struct ParamBBDH(pub u8, pub u8, pub u64, pub u16);
|
pub struct ParamBBDH(pub u8, pub u8, pub u64, pub u16);
|
||||||
|
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
pub struct ParamBBD(pub u8, pub u8, pub u64);
|
pub struct ParamBBD(pub u8, pub u8, pub u64);
|
||||||
|
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
pub struct ParamBBW(pub u8, pub u8, pub u32);
|
pub struct ParamBBW(pub u8, pub u8, pub u32);
|
||||||
|
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
pub struct ParamBB(pub u8, pub u8);
|
pub struct ParamBB(pub u8, pub u8);
|
||||||
|
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
pub struct ParamBD(pub u8, pub u64);
|
pub struct ParamBD(pub u8, pub u64);
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// Has to be valid to be decoded from bytecode.
|
/// Has to be valid to be decoded from bytecode.
|
||||||
pub unsafe trait OpParam {}
|
pub unsafe trait ProgramVal {}
|
||||||
unsafe impl OpParam for ParamBBBB {}
|
unsafe impl ProgramVal for ParamBBBB {}
|
||||||
unsafe impl OpParam for ParamBBB {}
|
unsafe impl ProgramVal for ParamBBB {}
|
||||||
unsafe impl OpParam for ParamBBDH {}
|
unsafe impl ProgramVal for ParamBBDH {}
|
||||||
unsafe impl OpParam for ParamBBD {}
|
unsafe impl ProgramVal for ParamBBD {}
|
||||||
unsafe impl OpParam for ParamBBW {}
|
unsafe impl ProgramVal for ParamBBW {}
|
||||||
unsafe impl OpParam for ParamBB {}
|
unsafe impl ProgramVal for ParamBB {}
|
||||||
unsafe impl OpParam for ParamBD {}
|
unsafe impl ProgramVal for ParamBD {}
|
||||||
unsafe impl OpParam for u64 {}
|
unsafe impl ProgramVal for u64 {}
|
||||||
unsafe impl OpParam for () {}
|
unsafe impl ProgramVal for u8 {} // Opcode
|
||||||
|
unsafe impl ProgramVal for () {}
|
||||||
|
|
|
@ -16,10 +16,11 @@ fuzz_target!(|data: &[u8]| {
|
||||||
if validate(data).is_ok() {
|
if validate(data).is_ok() {
|
||||||
let mut vm = unsafe {
|
let mut vm = unsafe {
|
||||||
Vm::<_, 16384>::new(
|
Vm::<_, 16384>::new(
|
||||||
SoftPagedMem {
|
SoftPagedMem::<_, true> {
|
||||||
pf_handler: TestTrapHandler,
|
pf_handler: TestTrapHandler,
|
||||||
program: data,
|
program: data,
|
||||||
root_pt: Box::into_raw(Default::default()),
|
root_pt: Box::into_raw(Default::default()),
|
||||||
|
icache: Default::default(),
|
||||||
},
|
},
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
|
@ -31,6 +32,8 @@ fuzz_target!(|data: &[u8]| {
|
||||||
alloc_and_map(&mut vm.memory, 4096),
|
alloc_and_map(&mut vm.memory, 4096),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
unsafe { vm.memory.write() };
|
||||||
|
|
||||||
// Run VM
|
// Run VM
|
||||||
let _ = vm.run();
|
let _ = vm.run();
|
||||||
|
|
||||||
|
@ -38,6 +41,8 @@ fuzz_target!(|data: &[u8]| {
|
||||||
for (i, page) in pages.into_iter().enumerate() {
|
for (i, page) in pages.into_iter().enumerate() {
|
||||||
unmap_and_dealloc(&mut vm.memory, page, i as u64 * 4096);
|
unmap_and_dealloc(&mut vm.memory, page, i as u64 * 4096);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let _ = unsafe { Box::from_raw(vm.memory.root_pt) };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,9 @@ use {
|
||||||
bmc::BlockCopier,
|
bmc::BlockCopier,
|
||||||
core::{cmp::Ordering, mem::size_of, ops},
|
core::{cmp::Ordering, mem::size_of, ops},
|
||||||
derive_more::Display,
|
derive_more::Display,
|
||||||
hbbytecode::{OpParam, ParamBB, ParamBBB, ParamBBBB, ParamBBD, ParamBBDH, ParamBBW, ParamBD},
|
hbbytecode::{
|
||||||
|
ParamBB, ParamBBB, ParamBBBB, ParamBBD, ParamBBDH, ParamBBW, ParamBD, ProgramVal,
|
||||||
|
},
|
||||||
value::{Value, ValueVariant},
|
value::{Value, ValueVariant},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -366,7 +368,7 @@ where
|
||||||
|
|
||||||
/// Decode instruction operands
|
/// Decode instruction operands
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
unsafe fn decode<T: OpParam>(&mut self) -> T {
|
unsafe fn decode<T: ProgramVal>(&mut self) -> T {
|
||||||
let pc1 = self.pc + 1;
|
let pc1 = self.pc + 1;
|
||||||
let data = self.memory.prog_read_unchecked::<T>(pc1 as _);
|
let data = self.memory.prog_read_unchecked::<T>(pc1 as _);
|
||||||
self.pc += 1 + size_of::<T>();
|
self.pc += 1 + size_of::<T>();
|
||||||
|
@ -515,13 +517,13 @@ pub trait Memory {
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// - Data read have to be valid
|
/// - Data read have to be valid
|
||||||
unsafe fn prog_read<T>(&mut self, addr: u64) -> Option<T>;
|
unsafe fn prog_read<T: ProgramVal>(&mut self, addr: u64) -> Option<T>;
|
||||||
|
|
||||||
/// Read from program memory to exectue
|
/// Read from program memory to exectue
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// - You have to be really sure that these bytes are there, understand?
|
/// - You have to be really sure that these bytes are there, understand?
|
||||||
unsafe fn prog_read_unchecked<T>(&mut self, addr: u64) -> T;
|
unsafe fn prog_read_unchecked<T: ProgramVal>(&mut self, addr: u64) -> T;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unhandled load access trap
|
/// Unhandled load access trap
|
||||||
|
|
|
@ -17,10 +17,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
} else {
|
} else {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut vm = Vm::<_, 0>::new(
|
let mut vm = Vm::<_, 0>::new(
|
||||||
SoftPagedMem {
|
SoftPagedMem::<_, true> {
|
||||||
pf_handler: TestTrapHandler,
|
pf_handler: TestTrapHandler,
|
||||||
program: &prog,
|
program: &prog,
|
||||||
root_pt: Box::into_raw(Default::default()),
|
root_pt: Box::into_raw(Default::default()),
|
||||||
|
icache: Default::default(),
|
||||||
},
|
},
|
||||||
4,
|
4,
|
||||||
);
|
);
|
||||||
|
@ -46,12 +47,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
println!("Program interrupt: {:?}", vm.run());
|
println!("Program interrupt: {:?}", vm.run());
|
||||||
println!("{:?}", vm.registers);
|
println!("{:?}", vm.registers);
|
||||||
|
|
||||||
println!("{:?}", core::slice::from_raw_parts(data, 4096));
|
|
||||||
std::alloc::dealloc(
|
std::alloc::dealloc(
|
||||||
data,
|
data,
|
||||||
std::alloc::Layout::from_size_align_unchecked(4096, 4096),
|
std::alloc::Layout::from_size_align_unchecked(4096, 4096),
|
||||||
);
|
);
|
||||||
vm.memory.unmap(8192).unwrap();
|
vm.memory.unmap(8192).unwrap();
|
||||||
|
let _ = Box::from_raw(vm.memory.root_pt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
103
hbvm/src/mem/softpaging/icache.rs
Normal file
103
hbvm/src/mem/softpaging/icache.rs
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
//! Program instruction cache
|
||||||
|
|
||||||
|
use {
|
||||||
|
super::{lookup::AddrPageLookuper, paging::PageTable, PageSize},
|
||||||
|
core::{
|
||||||
|
mem::{size_of, MaybeUninit},
|
||||||
|
ptr::{copy_nonoverlapping, NonNull},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Instruction cache
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct ICache {
|
||||||
|
/// Current page address base
|
||||||
|
base: u64,
|
||||||
|
/// Curent page pointer
|
||||||
|
data: Option<NonNull<u8>>,
|
||||||
|
/// Current page size
|
||||||
|
size: PageSize,
|
||||||
|
/// Address mask
|
||||||
|
mask: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ICache {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
base: Default::default(),
|
||||||
|
data: Default::default(),
|
||||||
|
size: PageSize::Size4K,
|
||||||
|
mask: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ICache {
|
||||||
|
/// Fetch instruction from cache
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `T` should be valid to read from instruction memory
|
||||||
|
pub(super) unsafe fn fetch<T>(&mut self, addr: u64, root_pt: *const PageTable) -> Option<T> {
|
||||||
|
let mut ret = MaybeUninit::<T>::uninit();
|
||||||
|
|
||||||
|
let pbase = self
|
||||||
|
.data
|
||||||
|
.or_else(|| self.fetch_page(self.base.checked_add(self.size as _)?, root_pt))?;
|
||||||
|
|
||||||
|
// Get address base
|
||||||
|
let base = addr & self.mask;
|
||||||
|
|
||||||
|
// Base not matching, fetch anew
|
||||||
|
if base != self.base {
|
||||||
|
self.fetch_page(base, root_pt)?;
|
||||||
|
};
|
||||||
|
|
||||||
|
let offset = addr & !self.mask;
|
||||||
|
let requ_size = size_of::<T>();
|
||||||
|
|
||||||
|
// Page overflow
|
||||||
|
let rem = (offset as usize)
|
||||||
|
.saturating_add(requ_size)
|
||||||
|
.saturating_sub(self.size as _);
|
||||||
|
let first_copy = requ_size.saturating_sub(rem);
|
||||||
|
|
||||||
|
// Copy non-overflowing part
|
||||||
|
copy_nonoverlapping(pbase.as_ptr(), ret.as_mut_ptr().cast::<u8>(), first_copy);
|
||||||
|
|
||||||
|
// Copy overflow
|
||||||
|
if rem != 0 {
|
||||||
|
let pbase = self.fetch_page(self.base.checked_add(self.size as _)?, root_pt)?;
|
||||||
|
|
||||||
|
// Unlikely, unsupported scenario
|
||||||
|
if rem > self.size as _ {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
copy_nonoverlapping(
|
||||||
|
pbase.as_ptr(),
|
||||||
|
ret.as_mut_ptr().cast::<u8>().add(first_copy),
|
||||||
|
rem,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(ret.assume_init())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch a page
|
||||||
|
unsafe fn fetch_page(&mut self, addr: u64, pt: *const PageTable) -> Option<NonNull<u8>> {
|
||||||
|
let res = AddrPageLookuper::new(addr, 0, pt).next()?.ok()?;
|
||||||
|
if !super::perm_check::executable(res.perm) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
(self.size, self.mask) = match res.size {
|
||||||
|
4096 => (PageSize::Size4K, !((1 << 8) - 1)),
|
||||||
|
2097152 => (PageSize::Size2M, !((1 << (8 * 2)) - 1)),
|
||||||
|
1073741824 => (PageSize::Size1G, !((1 << (8 * 3)) - 1)),
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
self.data = Some(NonNull::new(res.ptr)?);
|
||||||
|
self.base = addr & self.mask;
|
||||||
|
self.data
|
||||||
|
}
|
||||||
|
}
|
|
@ -111,7 +111,7 @@ impl Iterator for AddrPageLookuper {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get available byte count in the selected page with offset
|
// Get available byte count in the selected page with offset
|
||||||
let avail = (size as usize - offset).clamp(0, self.size);
|
let avail = (size as usize).saturating_sub(offset).clamp(0, self.size);
|
||||||
self.bump(size);
|
self.bump(size);
|
||||||
|
|
||||||
Some(Ok(AddrPageLookupOk {
|
Some(Ok(AddrPageLookupOk {
|
||||||
|
|
|
@ -10,7 +10,7 @@ use {
|
||||||
derive_more::Display,
|
derive_more::Display,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl<'p, A> SoftPagedMem<'p, A> {
|
impl<'p, A, const OUT_PROG_EXEC: bool> SoftPagedMem<'p, A, OUT_PROG_EXEC> {
|
||||||
/// Maps host's memory into VM's memory
|
/// Maps host's memory into VM's memory
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
use core::mem::size_of;
|
use core::mem::size_of;
|
||||||
|
|
||||||
|
use self::icache::ICache;
|
||||||
|
|
||||||
|
pub mod icache;
|
||||||
pub mod lookup;
|
pub mod lookup;
|
||||||
pub mod paging;
|
pub mod paging;
|
||||||
|
|
||||||
|
@ -15,17 +18,25 @@ use {
|
||||||
};
|
};
|
||||||
|
|
||||||
/// HoleyBytes software paged memory
|
/// HoleyBytes software paged memory
|
||||||
|
///
|
||||||
|
/// - `OUT_PROG_EXEC`: set to `false` to disable executing program
|
||||||
|
/// not contained in initially provided program, even the pages
|
||||||
|
/// are executable
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct SoftPagedMem<'p, PfH> {
|
pub struct SoftPagedMem<'p, PfH, const OUT_PROG_EXEC: bool = true> {
|
||||||
/// Root page table
|
/// Root page table
|
||||||
pub root_pt: *mut PageTable,
|
pub root_pt: *mut PageTable,
|
||||||
/// Page fault handler
|
/// Page fault handler
|
||||||
pub pf_handler: PfH,
|
pub pf_handler: PfH,
|
||||||
/// Program memory segment
|
/// Program memory segment
|
||||||
pub program: &'p [u8],
|
pub program: &'p [u8],
|
||||||
|
/// Program instruction cache
|
||||||
|
pub icache: ICache,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'p, PfH: HandlePageFault> Memory for SoftPagedMem<'p, PfH> {
|
impl<'p, PfH: HandlePageFault, const OUT_PROG_EXEC: bool> Memory
|
||||||
|
for SoftPagedMem<'p, PfH, OUT_PROG_EXEC>
|
||||||
|
{
|
||||||
/// Load value from an address
|
/// Load value from an address
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
|
@ -65,6 +76,10 @@ impl<'p, PfH: HandlePageFault> Memory for SoftPagedMem<'p, PfH> {
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
unsafe fn prog_read<T>(&mut self, addr: u64) -> Option<T> {
|
unsafe fn prog_read<T>(&mut self, addr: u64) -> Option<T> {
|
||||||
|
if OUT_PROG_EXEC && addr as usize > self.program.len() {
|
||||||
|
return self.icache.fetch::<T>(addr, self.root_pt);
|
||||||
|
}
|
||||||
|
|
||||||
let addr = addr as usize;
|
let addr = addr as usize;
|
||||||
self.program
|
self.program
|
||||||
.get(addr..addr + size_of::<T>())
|
.get(addr..addr + size_of::<T>())
|
||||||
|
@ -73,11 +88,18 @@ impl<'p, PfH: HandlePageFault> Memory for SoftPagedMem<'p, PfH> {
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
unsafe fn prog_read_unchecked<T>(&mut self, addr: u64) -> T {
|
unsafe fn prog_read_unchecked<T>(&mut self, addr: u64) -> T {
|
||||||
|
if OUT_PROG_EXEC && addr as usize > self.program.len() {
|
||||||
|
return self
|
||||||
|
.icache
|
||||||
|
.fetch::<T>(addr as _, self.root_pt)
|
||||||
|
.unwrap_or_else(|| core::mem::zeroed());
|
||||||
|
}
|
||||||
|
|
||||||
self.program.as_ptr().add(addr as _).cast::<T>().read()
|
self.program.as_ptr().add(addr as _).cast::<T>().read()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'p, PfH: HandlePageFault> SoftPagedMem<'p, PfH> {
|
impl<'p, PfH: HandlePageFault, const OUT_PROG_EXEC: bool> SoftPagedMem<'p, PfH, OUT_PROG_EXEC> {
|
||||||
// Everyone behold, the holy function, the god of HBVM memory accesses!
|
// Everyone behold, the holy function, the god of HBVM memory accesses!
|
||||||
|
|
||||||
/// Split address to pages, check their permissions and feed pointers with offset
|
/// Split address to pages, check their permissions and feed pointers with offset
|
||||||
|
|
Loading…
Reference in a new issue