BMC is now interruptable
This commit is contained in:
parent
9d27fb218d
commit
66ef81d8a0
|
@ -9,7 +9,7 @@ use {
|
||||||
};
|
};
|
||||||
|
|
||||||
fuzz_target!(|data: &[u8]| {
|
fuzz_target!(|data: &[u8]| {
|
||||||
if let Ok(mut vm) = Vm::<_, 0>::new_validated(data, TestTrapHandler, Default::default()) {
|
if let Ok(mut vm) = Vm::<_, 100>::new_validated(data, TestTrapHandler, Default::default()) {
|
||||||
let _ = vm.run();
|
let _ = vm.run();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -19,13 +19,12 @@ pub mod mem;
|
||||||
pub mod value;
|
pub mod value;
|
||||||
|
|
||||||
use {
|
use {
|
||||||
self::{mem::HandlePageFault, value::ValueVariant},
|
core::{cmp::Ordering, mem::size_of, ops},
|
||||||
core::{cmp::Ordering, ops},
|
|
||||||
hbbytecode::{
|
hbbytecode::{
|
||||||
valider, OpParam, ParamBB, ParamBBB, ParamBBBB, ParamBBD, ParamBBDH, ParamBBW, ParamBD,
|
valider, OpParam, ParamBB, ParamBBB, ParamBBBB, ParamBBD, ParamBBDH, ParamBBW, ParamBD,
|
||||||
},
|
},
|
||||||
mem::Memory,
|
mem::{bmc::BlockCopier, HandlePageFault, Memory},
|
||||||
value::Value,
|
value::{Value, ValueVariant},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// HoleyBytes Virtual Machine
|
/// HoleyBytes Virtual Machine
|
||||||
|
@ -53,6 +52,9 @@ pub struct Vm<'a, PfHandler, const TIMER_QUOTIENT: usize> {
|
||||||
|
|
||||||
/// Program timer
|
/// Program timer
|
||||||
timer: usize,
|
timer: usize,
|
||||||
|
|
||||||
|
/// Saved block copier
|
||||||
|
copier: Option<BlockCopier>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, PfHandler: HandlePageFault, const TIMER_QUOTIENT: usize>
|
impl<'a, PfHandler: HandlePageFault, const TIMER_QUOTIENT: usize>
|
||||||
|
@ -71,6 +73,7 @@ impl<'a, PfHandler: HandlePageFault, const TIMER_QUOTIENT: usize>
|
||||||
program_len: program.len() - 12,
|
program_len: program.len() - 12,
|
||||||
program,
|
program,
|
||||||
timer: 0,
|
timer: 0,
|
||||||
|
copier: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,13 +258,41 @@ impl<'a, PfHandler: HandlePageFault, const TIMER_QUOTIENT: usize>
|
||||||
}
|
}
|
||||||
BMC => {
|
BMC => {
|
||||||
// Block memory copy
|
// Block memory copy
|
||||||
let ParamBBD(src, dst, count) = self.decode();
|
match if let Some(copier) = &mut self.copier {
|
||||||
self.memory.block_copy(
|
// There is some copier, poll.
|
||||||
self.read_reg(src).cast::<u64>(),
|
copier.poll(&mut self.memory, &mut self.pfhandler)
|
||||||
self.read_reg(dst).cast::<u64>(),
|
} else {
|
||||||
count as _,
|
// There is none, make one!
|
||||||
&mut self.pfhandler,
|
let ParamBBD(src, dst, count) = self.decode();
|
||||||
)?;
|
|
||||||
|
// So we are still on BMC on next cycle
|
||||||
|
self.pc -= size_of::<ParamBBD>() + 1;
|
||||||
|
|
||||||
|
self.copier = Some(BlockCopier::new(
|
||||||
|
self.read_reg(src).cast(),
|
||||||
|
self.read_reg(dst).cast(),
|
||||||
|
count as _,
|
||||||
|
));
|
||||||
|
|
||||||
|
self.copier
|
||||||
|
.as_mut()
|
||||||
|
.unwrap_unchecked() // SAFETY: We just assigned there
|
||||||
|
.poll(&mut self.memory, &mut self.pfhandler)
|
||||||
|
} {
|
||||||
|
// We are done, shift program counter
|
||||||
|
core::task::Poll::Ready(Ok(())) => {
|
||||||
|
self.copier = None;
|
||||||
|
self.pc += size_of::<ParamBBD>() + 1;
|
||||||
|
}
|
||||||
|
// Error, shift program counter (for consistency)
|
||||||
|
// and yield error
|
||||||
|
core::task::Poll::Ready(Err(e)) => {
|
||||||
|
self.pc += size_of::<ParamBBD>() + 1;
|
||||||
|
return Err(e.into());
|
||||||
|
}
|
||||||
|
// Not done yet, proceed to next cycle
|
||||||
|
core::task::Poll::Pending => (),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
BRC => {
|
BRC => {
|
||||||
// Block register copy
|
// Block register copy
|
||||||
|
@ -353,7 +384,7 @@ impl<'a, PfHandler: HandlePageFault, const TIMER_QUOTIENT: usize>
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn decode<T: OpParam>(&mut self) -> T {
|
unsafe fn decode<T: OpParam>(&mut self) -> T {
|
||||||
let data = self.program.as_ptr().add(self.pc + 1).cast::<T>().read();
|
let data = self.program.as_ptr().add(self.pc + 1).cast::<T>().read();
|
||||||
self.pc += 1 + core::mem::size_of::<T>();
|
self.pc += 1 + size_of::<T>();
|
||||||
data
|
data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
152
hbvm/src/mem/bmc.rs
Normal file
152
hbvm/src/mem/bmc.rs
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
use {
|
||||||
|
super::MemoryAccessReason,
|
||||||
|
crate::{
|
||||||
|
mem::{perm_check, HandlePageFault, Memory},
|
||||||
|
VmRunError,
|
||||||
|
},
|
||||||
|
core::{mem::MaybeUninit, task::Poll},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Buffer size (defaults to 4 KiB, a smallest page size on most platforms)
|
||||||
|
const BUF_SIZE: usize = 4096;
|
||||||
|
|
||||||
|
// This should be equal to `BUF_SIZE`
|
||||||
|
#[repr(align(4096))]
|
||||||
|
struct AlignedBuf([MaybeUninit<u8>; BUF_SIZE]);
|
||||||
|
|
||||||
|
pub struct BlockCopier {
|
||||||
|
/// Source address
|
||||||
|
src: u64,
|
||||||
|
/// Destination address
|
||||||
|
dst: u64,
|
||||||
|
/// How many buffer sizes to copy?
|
||||||
|
n_buffers: usize,
|
||||||
|
/// …and what remainds after?
|
||||||
|
rem: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlockCopier {
|
||||||
|
pub fn new(src: u64, dst: u64, count: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
src,
|
||||||
|
dst,
|
||||||
|
n_buffers: count / BUF_SIZE,
|
||||||
|
rem: count % BUF_SIZE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Copy one block
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// - Same as for [`Memory::load`] and [`Memory::store`]
|
||||||
|
pub unsafe fn poll(
|
||||||
|
&mut self,
|
||||||
|
memory: &mut Memory,
|
||||||
|
traph: &mut impl HandlePageFault,
|
||||||
|
) -> Poll<Result<(), BlkCopyError>> {
|
||||||
|
// Safety: Assuming uninit of array of MaybeUninit is sound
|
||||||
|
let mut buf = AlignedBuf(MaybeUninit::uninit().assume_init());
|
||||||
|
|
||||||
|
if self.n_buffers != 0 {
|
||||||
|
if let Err(e) = act(
|
||||||
|
memory,
|
||||||
|
self.src,
|
||||||
|
self.dst,
|
||||||
|
buf.0.as_mut_ptr().cast(),
|
||||||
|
BUF_SIZE,
|
||||||
|
traph,
|
||||||
|
) {
|
||||||
|
return Poll::Ready(Err(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.src += BUF_SIZE as u64;
|
||||||
|
self.dst += BUF_SIZE as u64;
|
||||||
|
self.n_buffers -= 1;
|
||||||
|
|
||||||
|
return if self.n_buffers + self.rem == 0 {
|
||||||
|
// If there is nothing left, we are done
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
} else {
|
||||||
|
// Otherwise let's advice to run it again
|
||||||
|
Poll::Pending
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.rem != 0 {
|
||||||
|
if let Err(e) = act(
|
||||||
|
memory,
|
||||||
|
self.src,
|
||||||
|
self.dst,
|
||||||
|
buf.0.as_mut_ptr().cast(),
|
||||||
|
self.rem,
|
||||||
|
traph,
|
||||||
|
) {
|
||||||
|
return Poll::Ready(Err(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn act(
|
||||||
|
memory: &mut Memory,
|
||||||
|
src: u64,
|
||||||
|
dst: u64,
|
||||||
|
buf: *mut u8,
|
||||||
|
count: usize,
|
||||||
|
traph: &mut impl HandlePageFault,
|
||||||
|
) -> Result<(), BlkCopyError> {
|
||||||
|
// Load to buffer
|
||||||
|
memory
|
||||||
|
.memory_access(
|
||||||
|
MemoryAccessReason::Load,
|
||||||
|
src,
|
||||||
|
buf,
|
||||||
|
count,
|
||||||
|
perm_check::readable,
|
||||||
|
|src, dst, count| core::ptr::copy(src, dst, count),
|
||||||
|
traph,
|
||||||
|
)
|
||||||
|
.map_err(|addr| BlkCopyError {
|
||||||
|
access_reason: MemoryAccessReason::Load,
|
||||||
|
addr,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// Store from buffer
|
||||||
|
memory
|
||||||
|
.memory_access(
|
||||||
|
MemoryAccessReason::Store,
|
||||||
|
dst,
|
||||||
|
buf,
|
||||||
|
count,
|
||||||
|
perm_check::writable,
|
||||||
|
|dst, src, count| core::ptr::copy(src, dst, count),
|
||||||
|
traph,
|
||||||
|
)
|
||||||
|
.map_err(|addr| BlkCopyError {
|
||||||
|
access_reason: MemoryAccessReason::Store,
|
||||||
|
addr,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Error occured when copying a block of memory
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub struct BlkCopyError {
|
||||||
|
/// Kind of access
|
||||||
|
access_reason: MemoryAccessReason,
|
||||||
|
/// VM Address
|
||||||
|
addr: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<BlkCopyError> for VmRunError {
|
||||||
|
fn from(value: BlkCopyError) -> Self {
|
||||||
|
match value.access_reason {
|
||||||
|
MemoryAccessReason::Load => Self::LoadAccessEx(value.addr),
|
||||||
|
MemoryAccessReason::Store => Self::StoreAccessEx(value.addr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
//! Program memory implementation
|
//! Program memory implementation
|
||||||
|
|
||||||
pub mod paging;
|
pub mod paging;
|
||||||
|
pub mod bmc;
|
||||||
|
|
||||||
mod pfhandler;
|
mod pfhandler;
|
||||||
|
|
||||||
|
@ -8,7 +9,6 @@ pub use pfhandler::HandlePageFault;
|
||||||
|
|
||||||
use {
|
use {
|
||||||
super::VmRunError,
|
super::VmRunError,
|
||||||
core::mem::MaybeUninit,
|
|
||||||
derive_more::Display,
|
derive_more::Display,
|
||||||
paging::{PageTable, Permission},
|
paging::{PageTable, Permission},
|
||||||
};
|
};
|
||||||
|
@ -215,93 +215,6 @@ impl Memory {
|
||||||
.map_err(StoreError)
|
.map_err(StoreError)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Copy a block of memory
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
/// - Same as for [`Self::load`] and [`Self::store`]
|
|
||||||
/// - This function has been rewritten and is now pretty much boring
|
|
||||||
pub unsafe fn block_copy(
|
|
||||||
&mut self,
|
|
||||||
mut src: u64,
|
|
||||||
mut dst: u64,
|
|
||||||
count: usize,
|
|
||||||
traph: &mut impl HandlePageFault,
|
|
||||||
) -> Result<(), BlkCopyError> {
|
|
||||||
// Yea, i know it is possible to do this more efficiently, but I am too lazy.
|
|
||||||
|
|
||||||
impl Memory {
|
|
||||||
#[inline]
|
|
||||||
unsafe fn act(
|
|
||||||
&mut self,
|
|
||||||
src: u64,
|
|
||||||
dst: u64,
|
|
||||||
buf: *mut u8,
|
|
||||||
count: usize,
|
|
||||||
traph: &mut impl HandlePageFault,
|
|
||||||
) -> Result<(), BlkCopyError> {
|
|
||||||
// Load to buffer
|
|
||||||
self.memory_access(
|
|
||||||
MemoryAccessReason::Load,
|
|
||||||
src,
|
|
||||||
buf,
|
|
||||||
count,
|
|
||||||
perm_check::readable,
|
|
||||||
|src, dst, count| core::ptr::copy(src, dst, count),
|
|
||||||
traph,
|
|
||||||
)
|
|
||||||
.map_err(|addr| BlkCopyError {
|
|
||||||
access_reason: MemoryAccessReason::Load,
|
|
||||||
addr,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// Store from buffer
|
|
||||||
self.memory_access(
|
|
||||||
MemoryAccessReason::Store,
|
|
||||||
dst,
|
|
||||||
buf,
|
|
||||||
count,
|
|
||||||
perm_check::writable,
|
|
||||||
|dst, src, count| core::ptr::copy(src, dst, count),
|
|
||||||
traph,
|
|
||||||
)
|
|
||||||
.map_err(|addr| BlkCopyError {
|
|
||||||
access_reason: MemoryAccessReason::Store,
|
|
||||||
addr,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Buffer size (defaults to 4 KiB, a smallest page size on most platforms)
|
|
||||||
const BUF_SIZE: usize = 4096;
|
|
||||||
|
|
||||||
// This should be equal to `BUF_SIZE`
|
|
||||||
#[repr(align(4096))]
|
|
||||||
struct AlignedBuf([MaybeUninit<u8>; BUF_SIZE]);
|
|
||||||
|
|
||||||
// Safety: Assuming uninit of array of MaybeUninit is sound
|
|
||||||
let mut buf = AlignedBuf(MaybeUninit::uninit().assume_init());
|
|
||||||
|
|
||||||
// Calculate how many times we need to copy buffer-sized blocks if any and the rest.
|
|
||||||
let n_buffers = count / BUF_SIZE;
|
|
||||||
let rem = count % BUF_SIZE;
|
|
||||||
|
|
||||||
// Copy buffer-sized blocks
|
|
||||||
for _ in 0..n_buffers {
|
|
||||||
self.act(src, dst, buf.0.as_mut_ptr().cast(), BUF_SIZE, traph)?;
|
|
||||||
src += BUF_SIZE as u64;
|
|
||||||
dst += BUF_SIZE as u64;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy the rest (if any)
|
|
||||||
if rem != 0 {
|
|
||||||
self.act(src, dst, buf.0.as_mut_ptr().cast(), rem, traph)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -534,24 +447,6 @@ pub enum MemoryAccessReason {
|
||||||
Store,
|
Store,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Error occured when copying a block of memory
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
||||||
pub struct BlkCopyError {
|
|
||||||
/// Kind of access
|
|
||||||
access_reason: MemoryAccessReason,
|
|
||||||
/// VM Address
|
|
||||||
addr: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<BlkCopyError> for VmRunError {
|
|
||||||
fn from(value: BlkCopyError) -> Self {
|
|
||||||
match value.access_reason {
|
|
||||||
MemoryAccessReason::Load => Self::LoadAccessEx(value.addr),
|
|
||||||
MemoryAccessReason::Store => Self::StoreAccessEx(value.addr),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<LoadError> for VmRunError {
|
impl From<LoadError> for VmRunError {
|
||||||
fn from(value: LoadError) -> Self {
|
fn from(value: LoadError) -> Self {
|
||||||
Self::LoadAccessEx(value.0)
|
Self::LoadAccessEx(value.0)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
hex_literal_case = "Upper"
|
hex_literal_case = "Upper"
|
||||||
imports_granularity = "One"
|
imports_granularity = "One"
|
||||||
struct_field_align_threshold = 5
|
struct_field_align_threshold = 8
|
||||||
enum_discrim_align_threshold = 5
|
enum_discrim_align_threshold = 8
|
Loading…
Reference in a new issue