diff --git a/hbvm/src/vm/mem/mod.rs b/hbvm/src/vm/mem/mod.rs index b2439343..fdbe50ca 100644 --- a/hbvm/src/vm/mem/mod.rs +++ b/hbvm/src/vm/mem/mod.rs @@ -1,7 +1,9 @@ mod paging; +use core::mem::MaybeUninit; + use self::paging::{PageTable, Permission, PtEntry}; -use super::trap::HandleTrap; +use super::{trap::HandleTrap, VmRunError}; use alloc::boxed::Box; use derive_more::Display; @@ -105,37 +107,80 @@ impl Memory { } /// Copy a block of memory - pub unsafe fn block_copy(&mut self, src: u64, dst: u64, count: u64) -> Result<(), ()> { - /* let count = usize::try_from(count).expect("?conradluget a better CPU"); + /// + /// # Safety + /// - Same as for [`Self::load`] and [`Self::store`] + /// - Your faith in the gods of UB + /// - Addr-san claims it's fine but who knows is she isn't lying :ferrisSus: + pub unsafe fn block_copy( + &mut self, + src: u64, + dst: u64, + count: usize, + traph: &mut impl HandleTrap, + ) -> Result<(), BlkCopyError> { + // Yea, i know it is possible to do this more efficiently, but I am too lazy. - let mut srcs = PageSplitter::new(src, count, self.root_pt); - let mut dsts = PageSplitter::new(dst, count, self.root_pt); - let mut c_src = srcs.next().ok_or(())?; - let mut c_dst = dsts.next().ok_or(())?; + const STACK_BUFFER_SIZE: usize = 512; - loop { - let min_size = c_src.size.min(c_dst.size); + // Decide if to use stack-allocated buffer or to heap allocate + // Deallocation is again decided on size at the end of the function + let mut buf = MaybeUninit::<[u8; STACK_BUFFER_SIZE]>::uninit(); + let buf = if count <= STACK_BUFFER_SIZE { + buf.as_mut_ptr().cast() + } else { unsafe { - core::ptr::copy(c_src.ptr, c_dst.ptr, min_size); - } + let layout = core::alloc::Layout::from_size_align_unchecked(count, 1); + let ptr = alloc::alloc::alloc(layout); + if ptr.is_null() { + alloc::alloc::handle_alloc_error(layout); + } - match ( - match c_src.size.saturating_sub(min_size) { - 0 => srcs.next(), - size => Some(PageSplitResult { size, ..c_src }), - }, - match c_dst.size.saturating_sub(min_size) { - 0 => dsts.next(), - size => Some(PageSplitResult { size, ..c_dst }), - }, - ) { - (None, None) => return Ok(()), - (Some(src), Some(dst)) => (c_src, c_dst) = (src, dst), - _ => return Err(()), + ptr } - } */ - // TODO - Err(()) + }; + + // Perform memory block transfer + let status = (|| { + // Load to buffer + self.memory_access( + src, + buf, + count, + |perm| { + matches!( + perm, + Permission::Readonly | Permission::Write | Permission::Exec + ) + }, + |src, dst, count| core::ptr::copy(src, dst, count), + traph, + ) + .map_err(|_| BlkCopyError::Load)?; + + // Store from buffer + self.memory_access( + dst, + buf, + count, + |perm| perm == Permission::Write, + |dst, src, count| core::ptr::copy(src, dst, count), + traph, + ) + .map_err(|_| BlkCopyError::Store)?; + + Ok::<_, BlkCopyError>(()) + })(); + + // Deallocate if used heap-allocated array + if count > STACK_BUFFER_SIZE { + alloc::alloc::dealloc( + buf, + core::alloc::Layout::from_size_align_unchecked(count, 1), + ); + } + + status } /// Split address to pages, check their permissions and feed pointers with offset @@ -302,7 +347,7 @@ pub enum PageSize { /// 2 MiB page (on level 1) Size2M = 1024 * 1024 * 2, - + /// 1 GiB page (on level 2) Size1G = 1024 * 1024 * 1024, } @@ -326,3 +371,31 @@ pub struct LoadError; /// Unhandled store access trap #[derive(Clone, Copy, Display, Debug, PartialEq, Eq)] pub struct StoreError; + +/// Unhandled block transfer trap +#[derive(Clone, Copy, Display, Debug, PartialEq, Eq)] +pub enum BlkCopyError { + Load, + Store, +} + +impl From for VmRunError { + fn from(_: LoadError) -> Self { + Self::LoadAccessEx + } +} + +impl From for VmRunError { + fn from(_: StoreError) -> Self { + Self::StoreAccessEx + } +} + +impl From for VmRunError { + fn from(value: BlkCopyError) -> Self { + match value { + BlkCopyError::Load => Self::LoadAccessEx, + BlkCopyError::Store => Self::StoreAccessEx, + } + } +} diff --git a/hbvm/src/vm/mod.rs b/hbvm/src/vm/mod.rs index 2e83b962..834e90fd 100644 --- a/hbvm/src/vm/mod.rs +++ b/hbvm/src/vm/mod.rs @@ -26,7 +26,6 @@ use { value::Value, }; - /// Extract a parameter from program macro_rules! param { ($self:expr, $ty:ty) => {{ @@ -227,47 +226,30 @@ impl<'a, T: HandleTrap> Vm<'a, T> { _ => 0, }; - if self - .memory - .load( - self.read_reg(base).as_u64() + off + n as u64, - self.registers.as_mut_ptr().add(usize::from(dst) + n).cast(), - usize::from(count).saturating_sub(n), - &mut self.traph, - ) - .is_err() - { - return Err(VmRunError::LoadAccessEx); - } + self.memory.load( + self.read_reg(base).as_u64() + off + n as u64, + self.registers.as_mut_ptr().add(usize::from(dst) + n).cast(), + usize::from(count).saturating_sub(n), + &mut self.traph, + )?; } ST => { let ParamBBDH(dst, base, off, count) = param!(self, ParamBBDH); - if self - .memory - .store( - self.read_reg(base).as_u64() + off, - self.registers.as_ptr().add(usize::from(dst)).cast(), - count.into(), - &mut self.traph, - ) - .is_err() - { - return Err(VmRunError::LoadAccessEx); - } + self.memory.store( + self.read_reg(base).as_u64() + off, + self.registers.as_ptr().add(usize::from(dst)).cast(), + count.into(), + &mut self.traph, + )?; } BMC => { let ParamBBD(src, dst, count) = param!(self, ParamBBD); - if self - .memory - .block_copy( - self.read_reg(src).as_u64(), - self.read_reg(dst).as_u64(), - count, - ) - .is_err() - { - return Err(VmRunError::LoadAccessEx); - } + self.memory.block_copy( + self.read_reg(src).as_u64(), + self.read_reg(dst).as_u64(), + count as _, + &mut self.traph, + )?; } BRC => { let ParamBBB(src, dst, count) = param!(self, ParamBBB);