From 034b482817f287a9aa1e69025d3ffd0b73408458 Mon Sep 17 00:00:00 2001 From: Erin Date: Mon, 7 Aug 2023 01:41:26 +0200 Subject: [PATCH 1/9] Spec update --- spec.md | 1 + 1 file changed, 1 insertion(+) diff --git a/spec.md b/spec.md index 1fdbfd7e..8c353b18 100644 --- a/spec.md +++ b/spec.md @@ -260,6 +260,7 @@ # Memory - Addresses are 64 bit +- Program should be in the same address space as all other data - Memory implementation is arbitrary - In case of accessing invalid address: - Program shall trap (LoadAccessEx, StoreAccessEx) with parameter of accessed address From a2be0adefa4660ae18b0ab7c8e2dca7abdd3c34a Mon Sep 17 00:00:00 2001 From: Erin Date: Mon, 7 Aug 2023 01:43:29 +0200 Subject: [PATCH 2/9] Spec update --- spec.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec.md b/spec.md index 8c353b18..e98852ff 100644 --- a/spec.md +++ b/spec.md @@ -261,13 +261,13 @@ # Memory - Addresses are 64 bit - Program should be in the same address space as all other data +- Address `0x0` is invalid and acessing it traps - Memory implementation is arbitrary - In case of accessing invalid address: - Program shall trap (LoadAccessEx, StoreAccessEx) with parameter of accessed address - Value of register when trapped is undefined ## Recommendations -- Leave address `0x0` as invalid - If paging used: - Leave first page invalid - Pages should be at least 4 KiB From 33c049997759a9bf799374f0383807abd92d366d Mon Sep 17 00:00:00 2001 From: Erin Date: Mon, 7 Aug 2023 01:50:21 +0200 Subject: [PATCH 3/9] Shrunk --- hbvm/src/lib.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/hbvm/src/lib.rs b/hbvm/src/lib.rs index 5c1021ad..f71aeb95 100644 --- a/hbvm/src/lib.rs +++ b/hbvm/src/lib.rs @@ -11,9 +11,10 @@ // - Mapped pages should be at least 4 KiB #![no_std] - #![cfg_attr(feature = "nightly", feature(fn_align))] +use core::marker::PhantomData; + #[cfg(feature = "alloc")] extern crate alloc; @@ -47,11 +48,14 @@ pub struct Vm<'a, PfHandler, const TIMER_QUOTIENT: usize> { pub pc: usize, /// Program - program: &'a [u8], + program: *const u8, /// Cached program length (without unreachable end) program_len: usize, + /// Program lifetime + _program_lt: PhantomData<&'a [u8]>, + /// Program timer timer: usize, @@ -73,7 +77,8 @@ impl<'a, PfHandler: HandlePageFault, const TIMER_QUOTIENT: usize> pfhandler: traph, pc: 0, program_len: program.len() - 12, - program: &program[4..], + program: program[4..].as_ptr(), + _program_lt: Default::default(), timer: 0, copier: None, } @@ -121,7 +126,7 @@ impl<'a, PfHandler: HandlePageFault, const TIMER_QUOTIENT: usize> // - Yes, we assume you run 64 bit CPU. Else ?conradluget a better CPU // sorry 8 bit fans, HBVM won't run on your Speccy :( unsafe { - match *self.program.get_unchecked(self.pc) { + match *self.program.add(self.pc) { UN => { self.decode::<()>(); return Err(VmRunError::Unreachable); @@ -386,7 +391,7 @@ impl<'a, PfHandler: HandlePageFault, const TIMER_QUOTIENT: usize> /// Decode instruction operands #[inline] unsafe fn decode(&mut self) -> T { - let data = self.program.as_ptr().add(self.pc + 1).cast::().read(); + let data = self.program.add(self.pc + 1).cast::().read(); self.pc += 1 + size_of::(); data } From 2fb695b3a91d30145a2429f5da8894f93a1049f5 Mon Sep 17 00:00:00 2001 From: Erin Date: Tue, 8 Aug 2023 01:44:33 +0200 Subject: [PATCH 4/9] const perm check --- hbvm/src/mem/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hbvm/src/mem/mod.rs b/hbvm/src/mem/mod.rs index 8ef07b3d..f2d9ac71 100644 --- a/hbvm/src/mem/mod.rs +++ b/hbvm/src/mem/mod.rs @@ -479,7 +479,7 @@ pub mod perm_check { /// Page is readable #[inline(always)] - pub fn readable(perm: Permission) -> bool { + pub const fn readable(perm: Permission) -> bool { matches!( perm, Permission::Readonly | Permission::Write | Permission::Exec @@ -488,7 +488,7 @@ pub mod perm_check { /// Page is writable #[inline(always)] - pub fn writable(perm: Permission) -> bool { - perm == Permission::Write + pub const fn writable(perm: Permission) -> bool { + matches!(perm, Permission::Write) } } From 2aad3a1002e65bf6b0108cb302b056444d588bd7 Mon Sep 17 00:00:00 2001 From: Erin Date: Tue, 8 Aug 2023 02:06:15 +0200 Subject: [PATCH 5/9] Reimplemented BMC --- hbvm/src/mem/bmc.rs | 28 ++++++---------------------- hbvm/src/mem/mod.rs | 4 ++-- 2 files changed, 8 insertions(+), 24 deletions(-) diff --git a/hbvm/src/mem/bmc.rs b/hbvm/src/mem/bmc.rs index 35d282db..0d47566a 100644 --- a/hbvm/src/mem/bmc.rs +++ b/hbvm/src/mem/bmc.rs @@ -1,7 +1,7 @@ use { super::MemoryAccessReason, crate::{ - mem::{perm_check, HandlePageFault, Memory}, + mem::{HandlePageFault, Memory}, VmRunError, }, core::{mem::MaybeUninit, task::Poll}, @@ -68,7 +68,7 @@ impl BlockCopier { Some(n) => self.src = n, None => return Poll::Ready(Err(BlkCopyError::OutOfBounds)), }; - + match self.dst.checked_add(BUF_SIZE as u64) { Some(n) => self.dst = n, None => return Poll::Ready(Err(BlkCopyError::OutOfBounds)), @@ -113,32 +113,16 @@ unsafe fn act( ) -> 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 { + .load(src, buf, count, traph) + .map_err(|super::LoadError(addr)| BlkCopyError::Access { 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 { + .store(dst, buf, count, traph) + .map_err(|super::StoreError(addr)| BlkCopyError::Access { access_reason: MemoryAccessReason::Store, addr, })?; diff --git a/hbvm/src/mem/mod.rs b/hbvm/src/mem/mod.rs index f2d9ac71..38b99012 100644 --- a/hbvm/src/mem/mod.rs +++ b/hbvm/src/mem/mod.rs @@ -426,12 +426,12 @@ impl PageSize { /// Unhandled load access trap #[derive(Clone, Copy, Display, Debug, PartialEq, Eq)] #[display(fmt = "Load access error at address {_0:#x}")] -pub struct LoadError(u64); +pub struct LoadError(pub u64); /// Unhandled store access trap #[derive(Clone, Copy, Display, Debug, PartialEq, Eq)] #[display(fmt = "Store access error at address {_0:#x}")] -pub struct StoreError(u64); +pub struct StoreError(pub u64); /// There was no entry in page table to unmap /// From 1e92797775bb865fc9c53b5cf05fb00dd387f140 Mon Sep 17 00:00:00 2001 From: Erin Date: Tue, 8 Aug 2023 02:48:47 +0200 Subject: [PATCH 6/9] Abstraction of memory --- hbvm/src/lib.rs | 29 +- hbvm/src/main.rs | 15 +- hbvm/src/mem/bmc.rs | 20 +- hbvm/src/mem/mod.rs | 460 +----------------------- hbvm/src/mem/pfhandler.rs | 20 -- hbvm/src/mem/softpaged/mod.rs | 475 +++++++++++++++++++++++++ hbvm/src/mem/{ => softpaged}/paging.rs | 0 7 files changed, 508 insertions(+), 511 deletions(-) delete mode 100644 hbvm/src/mem/pfhandler.rs create mode 100644 hbvm/src/mem/softpaged/mod.rs rename hbvm/src/mem/{ => softpaged}/paging.rs (100%) diff --git a/hbvm/src/lib.rs b/hbvm/src/lib.rs index f71aeb95..ed352300 100644 --- a/hbvm/src/lib.rs +++ b/hbvm/src/lib.rs @@ -26,12 +26,12 @@ use { hbbytecode::{ valider, OpParam, ParamBB, ParamBBB, ParamBBBB, ParamBBD, ParamBBDH, ParamBBW, ParamBD, }, - mem::{bmc::BlockCopier, HandlePageFault, Memory}, + mem::bmc::BlockCopier, value::{Value, ValueVariant}, }; /// HoleyBytes Virtual Machine -pub struct Vm<'a, PfHandler, const TIMER_QUOTIENT: usize> { +pub struct Vm<'a, Memory, const TIMER_QUOTIENT: usize> { /// Holds 256 registers /// /// Writing to register 0 is considered undefined behaviour @@ -41,9 +41,6 @@ pub struct Vm<'a, PfHandler, const TIMER_QUOTIENT: usize> { /// Memory implementation pub memory: Memory, - /// Trap handler - pub pfhandler: PfHandler, - /// Program counter pub pc: usize, @@ -63,18 +60,18 @@ pub struct Vm<'a, PfHandler, const TIMER_QUOTIENT: usize> { copier: Option, } -impl<'a, PfHandler: HandlePageFault, const TIMER_QUOTIENT: usize> - Vm<'a, PfHandler, TIMER_QUOTIENT> +impl<'a, Memory, const TIMER_QUOTIENT: usize> Vm<'a, Memory, TIMER_QUOTIENT> +where + Memory: mem::Memory, { /// Create a new VM with program and trap handler /// /// # Safety /// Program code has to be validated - pub unsafe fn new_unchecked(program: &'a [u8], traph: PfHandler, memory: Memory) -> Self { + pub unsafe fn new_unchecked(program: &'a [u8], memory: Memory) -> Self { Self { registers: [Value::from(0_u64); 256], memory, - pfhandler: traph, pc: 0, program_len: program.len() - 12, program: program[4..].as_ptr(), @@ -85,13 +82,9 @@ impl<'a, PfHandler: HandlePageFault, const TIMER_QUOTIENT: usize> } /// Create a new VM with program and trap handler only if it passes validation - pub fn new_validated( - program: &'a [u8], - traph: PfHandler, - memory: Memory, - ) -> Result { + pub fn new_validated(program: &'a [u8], memory: Memory) -> Result { valider::validate(program)?; - Ok(unsafe { Self::new_unchecked(program, traph, memory) }) + Ok(unsafe { Self::new_unchecked(program, memory) }) } /// Execute program @@ -250,7 +243,6 @@ impl<'a, PfHandler: HandlePageFault, const TIMER_QUOTIENT: usize> .add(usize::from(dst) + usize::from(n)) .cast(), usize::from(count).saturating_sub(n.into()), - &mut self.pfhandler, )?; } ST => { @@ -260,14 +252,13 @@ impl<'a, PfHandler: HandlePageFault, const TIMER_QUOTIENT: usize> self.ldst_addr_uber(dst, base, off, count, 0)?, self.registers.as_ptr().add(usize::from(dst)).cast(), count.into(), - &mut self.pfhandler, )?; } BMC => { // Block memory copy match if let Some(copier) = &mut self.copier { // There is some copier, poll. - copier.poll(&mut self.memory, &mut self.pfhandler) + copier.poll(&mut self.memory) } else { // There is none, make one! let ParamBBD(src, dst, count) = self.decode(); @@ -284,7 +275,7 @@ impl<'a, PfHandler: HandlePageFault, const TIMER_QUOTIENT: usize> self.copier .as_mut() .unwrap_unchecked() // SAFETY: We just assigned there - .poll(&mut self.memory, &mut self.pfhandler) + .poll(&mut self.memory) } { // We are done, shift program counter core::task::Poll::Ready(Ok(())) => { diff --git a/hbvm/src/main.rs b/hbvm/src/main.rs index f3cc2d0c..fd46a3e5 100644 --- a/hbvm/src/main.rs +++ b/hbvm/src/main.rs @@ -1,9 +1,8 @@ +use hbvm::mem::softpaged::{HandlePageFault, PageSize, SoftPagedMem}; + use { hbbytecode::valider::validate, - hbvm::{ - mem::{HandlePageFault, Memory, MemoryAccessReason, PageSize}, - Vm, - }, + hbvm::{mem::MemoryAccessReason, Vm}, std::io::{stdin, Read}, }; @@ -16,7 +15,8 @@ fn main() -> Result<(), Box> { return Ok(()); } else { unsafe { - let mut vm = Vm::<_, 0>::new_unchecked(&prog, TestTrapHandler, Default::default()); + let mut vm = + Vm::<_, 0>::new_unchecked(&prog, SoftPagedMem::::default()); let data = { let ptr = std::alloc::alloc_zeroed(std::alloc::Layout::from_size_align_unchecked( 4096, 4096, @@ -31,7 +31,7 @@ fn main() -> Result<(), Box> { .map( data, 0, - hbvm::mem::paging::Permission::Write, + hbvm::mem::softpaged::paging::Permission::Write, PageSize::Size4K, ) .unwrap(); @@ -54,12 +54,13 @@ pub fn time() -> u32 { 9 } +#[derive(Default)] struct TestTrapHandler; impl HandlePageFault for TestTrapHandler { fn page_fault( &mut self, _: MemoryAccessReason, - _: &mut Memory, + //_: &mut Memory, _: u64, _: PageSize, _: *mut u8, diff --git a/hbvm/src/mem/bmc.rs b/hbvm/src/mem/bmc.rs index 0d47566a..4155ea15 100644 --- a/hbvm/src/mem/bmc.rs +++ b/hbvm/src/mem/bmc.rs @@ -1,9 +1,6 @@ use { super::MemoryAccessReason, - crate::{ - mem::{HandlePageFault, Memory}, - VmRunError, - }, + crate::{mem::Memory, VmRunError}, core::{mem::MaybeUninit, task::Poll}, }; @@ -40,11 +37,7 @@ impl BlockCopier { /// /// # Safety /// - Same as for [`Memory::load`] and [`Memory::store`] - pub unsafe fn poll( - &mut self, - memory: &mut Memory, - traph: &mut impl HandlePageFault, - ) -> Poll> { + pub unsafe fn poll(&mut self, memory: &mut impl Memory) -> Poll> { // Safety: Assuming uninit of array of MaybeUninit is sound let mut buf = AlignedBuf(MaybeUninit::uninit().assume_init()); @@ -56,7 +49,6 @@ impl BlockCopier { self.dst, buf.0.as_mut_ptr().cast(), BUF_SIZE, - traph, ) { return Poll::Ready(Err(e)); } @@ -92,7 +84,6 @@ impl BlockCopier { self.dst, buf.0.as_mut_ptr().cast(), self.rem, - traph, ) { return Poll::Ready(Err(e)); } @@ -104,16 +95,15 @@ impl BlockCopier { #[inline] unsafe fn act( - memory: &mut Memory, + memory: &mut impl Memory, src: u64, dst: u64, buf: *mut u8, count: usize, - traph: &mut impl HandlePageFault, ) -> Result<(), BlkCopyError> { // Load to buffer memory - .load(src, buf, count, traph) + .load(src, buf, count) .map_err(|super::LoadError(addr)| BlkCopyError::Access { access_reason: MemoryAccessReason::Load, addr, @@ -121,7 +111,7 @@ unsafe fn act( // Store from buffer memory - .store(dst, buf, count, traph) + .store(dst, buf, count) .map_err(|super::StoreError(addr)| BlkCopyError::Access { access_reason: MemoryAccessReason::Store, addr, diff --git a/hbvm/src/mem/mod.rs b/hbvm/src/mem/mod.rs index 38b99012..2391a3bf 100644 --- a/hbvm/src/mem/mod.rs +++ b/hbvm/src/mem/mod.rs @@ -1,426 +1,27 @@ //! Program memory implementation pub mod bmc; -pub mod paging; +pub mod softpaged; -mod pfhandler; +use {super::VmRunError, derive_more::Display}; -pub use pfhandler::HandlePageFault; - -use { - super::VmRunError, - derive_more::Display, - paging::{PageTable, Permission}, -}; - -#[cfg(feature = "alloc")] -use {alloc::boxed::Box, paging::PtEntry}; - -/// HoleyBytes virtual memory -#[derive(Clone, Debug)] -pub struct Memory { - /// Root page table - pub root_pt: *mut PageTable, -} - -#[cfg(feature = "alloc")] -impl Default for Memory { - fn default() -> Self { - Self { - root_pt: Box::into_raw(Default::default()), - } - } -} - -#[cfg(feature = "alloc")] -impl Drop for Memory { - fn drop(&mut self) { - let _ = unsafe { Box::from_raw(self.root_pt) }; - } -} - -impl Memory { - /// Maps host's memory into VM's memory +pub trait Memory { + /// Load data from memory on address /// /// # Safety - /// - Your faith in the gods of UB - /// - Addr-san claims it's fine but who knows is she isn't lying :ferrisSus: - /// - Alright, Miri-sama is also fine with this, who knows why - #[cfg(feature = "alloc")] - pub unsafe fn map( - &mut self, - host: *mut u8, - target: u64, - perm: Permission, - pagesize: PageSize, - ) -> Result<(), MapError> { - let mut current_pt = self.root_pt; + /// - Shall not overrun the buffer + unsafe fn load(&mut self, addr: u64, target: *mut u8, count: usize) -> Result<(), LoadError>; - // Decide on what level depth are we going - let lookup_depth = match pagesize { - PageSize::Size4K => 0, - PageSize::Size2M => 1, - PageSize::Size1G => 2, - }; - - // Walk pagetable levels - for lvl in (lookup_depth + 1..5).rev() { - let entry = (*current_pt) - .table - .get_unchecked_mut(addr_extract_index(target, lvl)); - - let ptr = entry.ptr(); - match entry.permission() { - // Still not on target and already seeing empty entry? - // No worries! Let's create one (allocates). - Permission::Empty => { - // Increase children count - (*current_pt).childen += 1; - - let table = Box::into_raw(Box::new(paging::PtPointedData { - pt: PageTable::default(), - })); - - core::ptr::write(entry, PtEntry::new(table, Permission::Node)); - current_pt = table as _; - } - // Continue walking - Permission::Node => current_pt = ptr as _, - - // There is some entry on place of node - _ => return Err(MapError::PageOnNode), - } - } - - let node = (*current_pt) - .table - .get_unchecked_mut(addr_extract_index(target, lookup_depth)); - - // Check if node is not mapped - if node.permission() != Permission::Empty { - return Err(MapError::AlreadyMapped); - } - - // Write entry - (*current_pt).childen += 1; - core::ptr::write(node, PtEntry::new(host.cast(), perm)); - - Ok(()) - } - - /// Unmaps pages from VM's memory - /// - /// If errors, it only means there is no entry to unmap and in most cases - /// just should be ignored. - #[cfg(feature = "alloc")] - pub fn unmap(&mut self, addr: u64) -> Result<(), NothingToUnmap> { - let mut current_pt = self.root_pt; - let mut page_tables = [core::ptr::null_mut(); 5]; - - // Walk page table in reverse - for lvl in (0..5).rev() { - let entry = unsafe { - (*current_pt) - .table - .get_unchecked_mut(addr_extract_index(addr, lvl)) - }; - - let ptr = entry.ptr(); - match entry.permission() { - // Nothing is there, throw an error, not critical! - Permission::Empty => return Err(NothingToUnmap), - // Node – Save to visited pagetables and continue walking - Permission::Node => { - page_tables[lvl as usize] = entry; - current_pt = ptr as _ - } - // Page entry – zero it out! - // Zero page entry is completely valid entry with - // empty permission - no UB here! - _ => unsafe { - core::ptr::write_bytes(entry, 0, 1); - break; - }, - } - } - - // Now walk in order visited page tables - for entry in page_tables.into_iter() { - // Level not visited, skip. - if entry.is_null() { - continue; - } - - unsafe { - let children = &mut (*(*entry).ptr()).pt.childen; - *children -= 1; // Decrease children count - - // If there are no children, deallocate. - if *children == 0 { - let _ = Box::from_raw((*entry).ptr() as *mut PageTable); - - // Zero visited entry - core::ptr::write_bytes(entry, 0, 1); - } else { - break; - } - } - } - - Ok(()) - } - - /// Load value from an address + /// Store data to memory on address /// /// # Safety - /// Applies same conditions as for [`core::ptr::copy_nonoverlapping`] - pub unsafe fn load( - &mut self, - addr: u64, - target: *mut u8, - count: usize, - traph: &mut impl HandlePageFault, - ) -> Result<(), LoadError> { - self.memory_access( - MemoryAccessReason::Load, - addr, - target, - count, - perm_check::readable, - |src, dst, count| core::ptr::copy_nonoverlapping(src, dst, count), - traph, - ) - .map_err(LoadError) - } - - /// Store value to an address - /// - /// # Safety - /// Applies same conditions as for [`core::ptr::copy_nonoverlapping`] - pub unsafe fn store( + /// - Shall not overrun the buffer + unsafe fn store( &mut self, addr: u64, source: *const u8, count: usize, - traph: &mut impl HandlePageFault, - ) -> Result<(), StoreError> { - self.memory_access( - MemoryAccessReason::Store, - addr, - source.cast_mut(), - count, - perm_check::writable, - |dst, src, count| core::ptr::copy_nonoverlapping(src, dst, count), - traph, - ) - .map_err(StoreError) - } - - // Everyone behold, the holy function, the god of HBVM memory accesses! - - /// Split address to pages, check their permissions and feed pointers with offset - /// to a specified function. - /// - /// If page is not found, execute page fault trap handler. - #[allow(clippy::too_many_arguments)] // Silence peasant - fn memory_access( - &mut self, - reason: MemoryAccessReason, - src: u64, - mut dst: *mut u8, - len: usize, - permission_check: fn(Permission) -> bool, - action: fn(*mut u8, *mut u8, usize), - traph: &mut impl HandlePageFault, - ) -> Result<(), u64> { - // Create new splitter - let mut pspl = AddrPageLookuper::new(src, len, self.root_pt); - loop { - match pspl.next() { - // Page is found - Some(Ok(AddrPageLookupOk { - vaddr, - ptr, - size, - perm, - })) => { - if !permission_check(perm) { - return Err(vaddr); - } - - // Perform specified memory action and bump destination pointer - action(ptr, dst, size); - dst = unsafe { dst.add(size) }; - } - // No page found - Some(Err(AddrPageLookupError { addr, size })) => { - // Attempt to execute page fault handler - if traph.page_fault(reason, self, addr, size, dst) { - // Shift the splitter address - pspl.bump(size); - - // Bump dst pointer - dst = unsafe { dst.add(size as _) }; - } else { - return Err(addr); // Unhandleable, VM will yield. - } - } - // No remaining pages, we are done! - None => return Ok(()), - } - } - } -} - -/// Good result from address split -struct AddrPageLookupOk { - /// Virtual address - vaddr: u64, - - /// Pointer to the start for perform operation - ptr: *mut u8, - - /// Size to the end of page / end of desired size - size: usize, - - /// Page permission - perm: Permission, -} - -/// Errornous address split result -struct AddrPageLookupError { - /// Address of failure - addr: u64, - - /// Requested page size - size: PageSize, -} - -/// Address splitter into pages -struct AddrPageLookuper { - /// Current address - addr: u64, - - /// Size left - size: usize, - - /// Page table - pagetable: *const PageTable, -} - -impl AddrPageLookuper { - /// Create a new page lookuper - #[inline] - pub const fn new(addr: u64, size: usize, pagetable: *const PageTable) -> Self { - Self { - addr, - size, - pagetable, - } - } - - /// Bump address by size X - fn bump(&mut self, page_size: PageSize) { - self.addr += page_size as u64; - self.size = self.size.saturating_sub(page_size as _); - } -} - -impl Iterator for AddrPageLookuper { - type Item = Result; - - fn next(&mut self) -> Option { - // The end, everything is fine - if self.size == 0 { - return None; - } - - let (base, perm, size, offset) = 'a: { - let mut current_pt = self.pagetable; - - // Walk the page table - for lvl in (0..5).rev() { - // Get an entry - unsafe { - let entry = (*current_pt) - .table - .get_unchecked(addr_extract_index(self.addr, lvl)); - - let ptr = entry.ptr(); - match entry.permission() { - // No page → page fault - Permission::Empty => { - return Some(Err(AddrPageLookupError { - addr: self.addr, - size: PageSize::from_lvl(lvl)?, - })) - } - - // Node → proceed waking - Permission::Node => current_pt = ptr as _, - - // Leaf → return relevant data - perm => { - break 'a ( - // Pointer in host memory - ptr as *mut u8, - perm, - PageSize::from_lvl(lvl)?, - // In-page offset - addr_extract_index(self.addr, lvl), - ); - } - } - } - } - return None; // Reached the end (should not happen) - }; - - // Get available byte count in the selected page with offset - let avail = (size as usize - offset).clamp(0, self.size); - self.bump(size); - - Some(Ok(AddrPageLookupOk { - vaddr: self.addr, - ptr: unsafe { base.add(offset) }, // Return pointer to the start of region - size: avail, - perm, - })) - } -} - -/// Extract index in page table on specified level -/// -/// The level shall not be larger than 4, otherwise -/// the output of the function is unspecified (yes, it can also panic :) -pub fn addr_extract_index(addr: u64, lvl: u8) -> usize { - debug_assert!(lvl <= 4); - usize::try_from((addr >> (lvl * 8 + 12)) & ((1 << 8) - 1)).expect("?conradluget a better CPU") -} - -/// Page size -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum PageSize { - /// 4 KiB page (on level 0) - Size4K = 4096, - - /// 2 MiB page (on level 1) - Size2M = 1024 * 1024 * 2, - - /// 1 GiB page (on level 2) - Size1G = 1024 * 1024 * 1024, -} - -impl PageSize { - /// Convert page table level to size of page - const fn from_lvl(lvl: u8) -> Option { - match lvl { - 0 => Some(PageSize::Size4K), - 1 => Some(PageSize::Size2M), - 2 => Some(PageSize::Size1G), - _ => None, - } - } + ) -> Result<(), StoreError>; } /// Unhandled load access trap @@ -433,15 +34,6 @@ pub struct LoadError(pub u64); #[display(fmt = "Store access error at address {_0:#x}")] pub struct StoreError(pub u64); -/// There was no entry in page table to unmap -/// -/// No worry, don't panic, nothing bad has happened, -/// but if you are 120% sure there should be something, -/// double-check your addresses. -#[derive(Clone, Copy, Display, Debug)] -#[display(fmt = "There was no entry to unmap")] -pub struct NothingToUnmap; - /// Reason to access memory #[derive(Clone, Copy, Display, Debug, PartialEq, Eq)] pub enum MemoryAccessReason { @@ -460,35 +52,3 @@ impl From for VmRunError { Self::StoreAccessEx(value.0) } } - -/// Error mapping -#[derive(Clone, Copy, Display, Debug, PartialEq, Eq)] -pub enum MapError { - /// Entry was already mapped - #[display(fmt = "There is already a page mapped on specified address")] - AlreadyMapped, - /// When walking a page entry was - /// encounterd. - #[display(fmt = "There was a page mapped on the way instead of node")] - PageOnNode, -} - -/// Permisison checks -pub mod perm_check { - use super::paging::Permission; - - /// Page is readable - #[inline(always)] - pub const fn readable(perm: Permission) -> bool { - matches!( - perm, - Permission::Readonly | Permission::Write | Permission::Exec - ) - } - - /// Page is writable - #[inline(always)] - pub const fn writable(perm: Permission) -> bool { - matches!(perm, Permission::Write) - } -} diff --git a/hbvm/src/mem/pfhandler.rs b/hbvm/src/mem/pfhandler.rs deleted file mode 100644 index fb0cb229..00000000 --- a/hbvm/src/mem/pfhandler.rs +++ /dev/null @@ -1,20 +0,0 @@ -//! Program trap handling interfaces - -use super::{Memory, MemoryAccessReason, PageSize}; - -/// Handle VM traps -pub trait HandlePageFault { - /// Handle page fault - /// - /// Return true if handling was sucessful, - /// otherwise the program will be interrupted and will - /// yield an error. - fn page_fault( - &mut self, - reason: MemoryAccessReason, - memory: &mut Memory, - vaddr: u64, - size: PageSize, - dataptr: *mut u8, - ) -> bool; -} diff --git a/hbvm/src/mem/softpaged/mod.rs b/hbvm/src/mem/softpaged/mod.rs new file mode 100644 index 00000000..2ec72e57 --- /dev/null +++ b/hbvm/src/mem/softpaged/mod.rs @@ -0,0 +1,475 @@ +//! Platform independent, software paged memory implementation + +pub mod paging; + +use { + super::{LoadError, Memory, MemoryAccessReason, StoreError}, + derive_more::Display, + paging::{PageTable, Permission}, +}; + +#[cfg(feature = "alloc")] +use {alloc::boxed::Box, paging::PtEntry}; + +/// HoleyBytes software paged memory +#[derive(Clone, Debug)] +pub struct SoftPagedMem { + /// Root page table + pub root_pt: *mut PageTable, + /// Page fault handler + pub pf_handler: PfHandler, +} + +impl Memory for SoftPagedMem { + /// Load value from an address + /// + /// # Safety + /// Applies same conditions as for [`core::ptr::copy_nonoverlapping`] + unsafe fn load(&mut self, addr: u64, target: *mut u8, count: usize) -> Result<(), LoadError> { + self.memory_access( + MemoryAccessReason::Load, + addr, + target, + count, + perm_check::readable, + |src, dst, count| core::ptr::copy_nonoverlapping(src, dst, count), + ) + .map_err(LoadError) + } + + /// Store value to an address + /// + /// # Safety + /// Applies same conditions as for [`core::ptr::copy_nonoverlapping`] + unsafe fn store( + &mut self, + addr: u64, + source: *const u8, + count: usize, + ) -> Result<(), StoreError> { + self.memory_access( + MemoryAccessReason::Store, + addr, + source.cast_mut(), + count, + perm_check::writable, + |dst, src, count| core::ptr::copy_nonoverlapping(src, dst, count), + ) + .map_err(StoreError) + } +} + +impl SoftPagedMem { + // Everyone behold, the holy function, the god of HBVM memory accesses! + + /// Split address to pages, check their permissions and feed pointers with offset + /// to a specified function. + /// + /// If page is not found, execute page fault trap handler. + #[allow(clippy::too_many_arguments)] // Silence peasant + fn memory_access( + &mut self, + reason: MemoryAccessReason, + src: u64, + mut dst: *mut u8, + len: usize, + permission_check: fn(Permission) -> bool, + action: fn(*mut u8, *mut u8, usize), + ) -> Result<(), u64> { + // Create new splitter + let mut pspl = AddrPageLookuper::new(src, len, self.root_pt); + loop { + match pspl.next() { + // Page is found + Some(Ok(AddrPageLookupOk { + vaddr, + ptr, + size, + perm, + })) => { + if !permission_check(perm) { + return Err(vaddr); + } + + // Perform specified memory action and bump destination pointer + action(ptr, dst, size); + dst = unsafe { dst.add(size) }; + } + // No page found + Some(Err(AddrPageLookupError { addr, size })) => { + // Attempt to execute page fault handler + if self.pf_handler.page_fault(reason, addr, size, dst) { + // Shift the splitter address + pspl.bump(size); + + // Bump dst pointer + dst = unsafe { dst.add(size as _) }; + } else { + return Err(addr); // Unhandleable, VM will yield. + } + } + // No remaining pages, we are done! + None => return Ok(()), + } + } + } +} + +/// Good result from address split +struct AddrPageLookupOk { + /// Virtual address + vaddr: u64, + + /// Pointer to the start for perform operation + ptr: *mut u8, + + /// Size to the end of page / end of desired size + size: usize, + + /// Page permission + perm: Permission, +} + +/// Errornous address split result +struct AddrPageLookupError { + /// Address of failure + addr: u64, + + /// Requested page size + size: PageSize, +} + +/// Address splitter into pages +struct AddrPageLookuper { + /// Current address + addr: u64, + + /// Size left + size: usize, + + /// Page table + pagetable: *const PageTable, +} + +impl AddrPageLookuper { + /// Create a new page lookuper + #[inline] + pub const fn new(addr: u64, size: usize, pagetable: *const PageTable) -> Self { + Self { + addr, + size, + pagetable, + } + } + + /// Bump address by size X + fn bump(&mut self, page_size: PageSize) { + self.addr += page_size as u64; + self.size = self.size.saturating_sub(page_size as _); + } +} + +impl Iterator for AddrPageLookuper { + type Item = Result; + + fn next(&mut self) -> Option { + // The end, everything is fine + if self.size == 0 { + return None; + } + + let (base, perm, size, offset) = 'a: { + let mut current_pt = self.pagetable; + + // Walk the page table + for lvl in (0..5).rev() { + // Get an entry + unsafe { + let entry = (*current_pt) + .table + .get_unchecked(addr_extract_index(self.addr, lvl)); + + let ptr = entry.ptr(); + match entry.permission() { + // No page → page fault + Permission::Empty => { + return Some(Err(AddrPageLookupError { + addr: self.addr, + size: PageSize::from_lvl(lvl)?, + })) + } + + // Node → proceed waking + Permission::Node => current_pt = ptr as _, + + // Leaf → return relevant data + perm => { + break 'a ( + // Pointer in host memory + ptr as *mut u8, + perm, + PageSize::from_lvl(lvl)?, + // In-page offset + addr_extract_index(self.addr, lvl), + ); + } + } + } + } + return None; // Reached the end (should not happen) + }; + + // Get available byte count in the selected page with offset + let avail = (size as usize - offset).clamp(0, self.size); + self.bump(size); + + Some(Ok(AddrPageLookupOk { + vaddr: self.addr, + ptr: unsafe { base.add(offset) }, // Return pointer to the start of region + size: avail, + perm, + })) + } +} + +#[cfg(feature = "alloc")] +impl Default for SoftPagedMem { + fn default() -> Self { + Self { + root_pt: Box::into_raw(Default::default()), + pf_handler: Default::default(), + } + } +} + +#[cfg(feature = "alloc")] +impl Drop for SoftPagedMem { + fn drop(&mut self) { + let _ = unsafe { Box::from_raw(self.root_pt) }; + } +} + +#[cfg(feature = "alloc")] +impl SoftPagedMem { + /// Maps host's memory into VM's memory + /// + /// # Safety + /// - Your faith in the gods of UB + /// - Addr-san claims it's fine but who knows is she isn't lying :ferrisSus: + /// - Alright, Miri-sama is also fine with this, who knows why + pub unsafe fn map( + &mut self, + host: *mut u8, + target: u64, + perm: Permission, + pagesize: PageSize, + ) -> Result<(), MapError> { + let mut current_pt = self.root_pt; + + // Decide on what level depth are we going + let lookup_depth = match pagesize { + PageSize::Size4K => 0, + PageSize::Size2M => 1, + PageSize::Size1G => 2, + }; + + // Walk pagetable levels + for lvl in (lookup_depth + 1..5).rev() { + let entry = (*current_pt) + .table + .get_unchecked_mut(addr_extract_index(target, lvl)); + + let ptr = entry.ptr(); + match entry.permission() { + // Still not on target and already seeing empty entry? + // No worries! Let's create one (allocates). + Permission::Empty => { + // Increase children count + (*current_pt).childen += 1; + + let table = Box::into_raw(Box::new(paging::PtPointedData { + pt: PageTable::default(), + })); + + core::ptr::write(entry, PtEntry::new(table, Permission::Node)); + current_pt = table as _; + } + // Continue walking + Permission::Node => current_pt = ptr as _, + + // There is some entry on place of node + _ => return Err(MapError::PageOnNode), + } + } + + let node = (*current_pt) + .table + .get_unchecked_mut(addr_extract_index(target, lookup_depth)); + + // Check if node is not mapped + if node.permission() != Permission::Empty { + return Err(MapError::AlreadyMapped); + } + + // Write entry + (*current_pt).childen += 1; + core::ptr::write(node, PtEntry::new(host.cast(), perm)); + + Ok(()) + } + + /// Unmaps pages from VM's memory + /// + /// If errors, it only means there is no entry to unmap and in most cases + /// just should be ignored. + pub fn unmap(&mut self, addr: u64) -> Result<(), NothingToUnmap> { + let mut current_pt = self.root_pt; + let mut page_tables = [core::ptr::null_mut(); 5]; + + // Walk page table in reverse + for lvl in (0..5).rev() { + let entry = unsafe { + (*current_pt) + .table + .get_unchecked_mut(addr_extract_index(addr, lvl)) + }; + + let ptr = entry.ptr(); + match entry.permission() { + // Nothing is there, throw an error, not critical! + Permission::Empty => return Err(NothingToUnmap), + // Node – Save to visited pagetables and continue walking + Permission::Node => { + page_tables[lvl as usize] = entry; + current_pt = ptr as _ + } + // Page entry – zero it out! + // Zero page entry is completely valid entry with + // empty permission - no UB here! + _ => unsafe { + core::ptr::write_bytes(entry, 0, 1); + break; + }, + } + } + + // Now walk in order visited page tables + for entry in page_tables.into_iter() { + // Level not visited, skip. + if entry.is_null() { + continue; + } + + unsafe { + let children = &mut (*(*entry).ptr()).pt.childen; + *children -= 1; // Decrease children count + + // If there are no children, deallocate. + if *children == 0 { + let _ = Box::from_raw((*entry).ptr() as *mut PageTable); + + // Zero visited entry + core::ptr::write_bytes(entry, 0, 1); + } else { + break; + } + } + } + + Ok(()) + } +} + +/// Extract index in page table on specified level +/// +/// The level shall not be larger than 4, otherwise +/// the output of the function is unspecified (yes, it can also panic :) +pub fn addr_extract_index(addr: u64, lvl: u8) -> usize { + debug_assert!(lvl <= 4); + usize::try_from((addr >> (lvl * 8 + 12)) & ((1 << 8) - 1)).expect("?conradluget a better CPU") +} + +/// Page size +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum PageSize { + /// 4 KiB page (on level 0) + Size4K = 4096, + + /// 2 MiB page (on level 1) + Size2M = 1024 * 1024 * 2, + + /// 1 GiB page (on level 2) + Size1G = 1024 * 1024 * 1024, +} + +impl PageSize { + /// Convert page table level to size of page + const fn from_lvl(lvl: u8) -> Option { + match lvl { + 0 => Some(PageSize::Size4K), + 1 => Some(PageSize::Size2M), + 2 => Some(PageSize::Size1G), + _ => None, + } + } +} + +/// Error mapping +#[derive(Clone, Copy, Display, Debug, PartialEq, Eq)] +pub enum MapError { + /// Entry was already mapped + #[display(fmt = "There is already a page mapped on specified address")] + AlreadyMapped, + /// When walking a page entry was + /// encounterd. + #[display(fmt = "There was a page mapped on the way instead of node")] + PageOnNode, +} + +/// There was no entry in page table to unmap +/// +/// No worry, don't panic, nothing bad has happened, +/// but if you are 120% sure there should be something, +/// double-check your addresses. +#[derive(Clone, Copy, Display, Debug)] +#[display(fmt = "There was no entry to unmap")] +pub struct NothingToUnmap; + +/// Permisison checks +pub mod perm_check { + use super::paging::Permission; + + /// Page is readable + #[inline(always)] + pub const fn readable(perm: Permission) -> bool { + matches!( + perm, + Permission::Readonly | Permission::Write | Permission::Exec + ) + } + + /// Page is writable + #[inline(always)] + pub const fn writable(perm: Permission) -> bool { + matches!(perm, Permission::Write) + } +} + +/// Handle VM traps +pub trait HandlePageFault { + /// Handle page fault + /// + /// Return true if handling was sucessful, + /// otherwise the program will be interrupted and will + /// yield an error. + fn page_fault( + &mut self, + reason: MemoryAccessReason, + // memory: &mut SoftPagedMem, TODO: Make work + vaddr: u64, + size: PageSize, + dataptr: *mut u8, + ) -> bool + where + Self: Sized; +} diff --git a/hbvm/src/mem/paging.rs b/hbvm/src/mem/softpaged/paging.rs similarity index 100% rename from hbvm/src/mem/paging.rs rename to hbvm/src/mem/softpaged/paging.rs From 62d241e78ca3c0c15d6231005390e12aaf7016e4 Mon Sep 17 00:00:00 2001 From: Erin Date: Tue, 8 Aug 2023 03:03:15 +0200 Subject: [PATCH 7/9] Changed stuff aroud --- hbbytecode/src/lib.rs | 3 +- hbvm/src/{mem => }/bmc.rs | 12 ++- hbvm/src/lib.rs | 75 ++++++++++++++++--- hbvm/src/main.rs | 9 ++- hbvm/src/mem/mod.rs | 54 ------------- hbvm/src/{mem/softpaged => softpaging}/mod.rs | 0 .../{mem/softpaged => softpaging}/paging.rs | 2 + hbvm/src/value.rs | 19 ++++- 8 files changed, 99 insertions(+), 75 deletions(-) rename hbvm/src/{mem => }/bmc.rs (92%) delete mode 100644 hbvm/src/mem/mod.rs rename hbvm/src/{mem/softpaged => softpaging}/mod.rs (100%) rename hbvm/src/{mem/softpaged => softpaging}/paging.rs (97%) diff --git a/hbbytecode/src/lib.rs b/hbbytecode/src/lib.rs index 36c3a370..a1c9862c 100644 --- a/hbbytecode/src/lib.rs +++ b/hbbytecode/src/lib.rs @@ -17,6 +17,7 @@ macro_rules! constmod { }; } +#[allow(rustdoc::invalid_rust_codeblocks)] /// Invoke macro with bytecode definition /// # Input syntax /// ```no_run @@ -31,7 +32,7 @@ macro_rules! constmod { /// - Per-instructions there will be generated opcode-specific functions calling the generic ones /// - Operand types /// - R: Register (u8) -/// - I: Immediate (implements [`crate::Imm`] trait) +/// - I: Immediate /// - L: Memory load / store size (u16) /// - Other types are identity-mapped /// diff --git a/hbvm/src/mem/bmc.rs b/hbvm/src/bmc.rs similarity index 92% rename from hbvm/src/mem/bmc.rs rename to hbvm/src/bmc.rs index 4155ea15..391579f6 100644 --- a/hbvm/src/mem/bmc.rs +++ b/hbvm/src/bmc.rs @@ -1,16 +1,18 @@ +//! Block memory copier state machine + use { - super::MemoryAccessReason, - crate::{mem::Memory, VmRunError}, + super::{Memory, MemoryAccessReason, VmRunError}, core::{mem::MaybeUninit, task::Poll}, }; -// Buffer size (defaults to 4 KiB, a smallest page size on most platforms) +/// 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` +/// Buffer of possibly uninitialised bytes, aligned to [`BUF_SIZE`] #[repr(align(4096))] struct AlignedBuf([MaybeUninit; BUF_SIZE]); +/// State for block memory copy pub struct BlockCopier { /// Source address src: u64, @@ -23,6 +25,7 @@ pub struct BlockCopier { } impl BlockCopier { + /// Construct a new one #[inline] pub fn new(src: u64, dst: u64, count: usize) -> Self { Self { @@ -93,6 +96,7 @@ impl BlockCopier { } } +/// Load to buffer and store from buffer #[inline] unsafe fn act( memory: &mut impl Memory, diff --git a/hbvm/src/lib.rs b/hbvm/src/lib.rs index ed352300..84447d98 100644 --- a/hbvm/src/lib.rs +++ b/hbvm/src/lib.rs @@ -2,8 +2,8 @@ //! //! # Alloc feature //! - Enabled by default -//! - Provides [`mem::Memory`] mapping / unmapping, as well as -//! [`Default`] and [`Drop`] implementation +//! - Provides mapping / unmapping, as well as [`Default`] and [`Drop`] +//! implementations for soft-paged memory implementation // # General safety notice: // - Validation has to assure there is 256 registers (r0 - r255) @@ -12,26 +12,30 @@ #![no_std] #![cfg_attr(feature = "nightly", feature(fn_align))] +#![warn(missing_docs, clippy::missing_docs_in_private_items)] use core::marker::PhantomData; #[cfg(feature = "alloc")] extern crate alloc; -pub mod mem; +pub mod softpaging; pub mod value; +mod bmc; + use { + bmc::BlockCopier, core::{cmp::Ordering, mem::size_of, ops}, + derive_more::Display, hbbytecode::{ valider, OpParam, ParamBB, ParamBBB, ParamBBBB, ParamBBD, ParamBBDH, ParamBBW, ParamBD, }, - mem::bmc::BlockCopier, value::{Value, ValueVariant}, }; /// HoleyBytes Virtual Machine -pub struct Vm<'a, Memory, const TIMER_QUOTIENT: usize> { +pub struct Vm<'a, Mem, const TIMER_QUOTIENT: usize> { /// Holds 256 registers /// /// Writing to register 0 is considered undefined behaviour @@ -39,7 +43,7 @@ pub struct Vm<'a, Memory, const TIMER_QUOTIENT: usize> { pub registers: [Value; 256], /// Memory implementation - pub memory: Memory, + pub memory: Mem, /// Program counter pub pc: usize, @@ -60,15 +64,15 @@ pub struct Vm<'a, Memory, const TIMER_QUOTIENT: usize> { copier: Option, } -impl<'a, Memory, const TIMER_QUOTIENT: usize> Vm<'a, Memory, TIMER_QUOTIENT> +impl<'a, Mem, const TIMER_QUOTIENT: usize> Vm<'a, Mem, TIMER_QUOTIENT> where - Memory: mem::Memory, + Mem: Memory, { /// Create a new VM with program and trap handler /// /// # Safety /// Program code has to be validated - pub unsafe fn new_unchecked(program: &'a [u8], memory: Memory) -> Self { + pub unsafe fn new_unchecked(program: &'a [u8], memory: Mem) -> Self { Self { registers: [Value::from(0_u64); 256], memory, @@ -82,7 +86,7 @@ where } /// Create a new VM with program and trap handler only if it passes validation - pub fn new_validated(program: &'a [u8], memory: Memory) -> Result { + pub fn new_validated(program: &'a [u8], memory: Mem) -> Result { valider::validate(program)?; Ok(unsafe { Self::new_unchecked(program, memory) }) } @@ -502,3 +506,54 @@ pub enum VmRunOk { /// Environment call Ecall, } + +/// Load-store memory access +pub trait Memory { + /// Load data from memory on address + /// + /// # Safety + /// - Shall not overrun the buffer + unsafe fn load(&mut self, addr: u64, target: *mut u8, count: usize) -> Result<(), LoadError>; + + /// Store data to memory on address + /// + /// # Safety + /// - Shall not overrun the buffer + unsafe fn store( + &mut self, + addr: u64, + source: *const u8, + count: usize, + ) -> Result<(), StoreError>; +} + +/// Unhandled load access trap +#[derive(Clone, Copy, Display, Debug, PartialEq, Eq)] +#[display(fmt = "Load access error at address {_0:#x}")] +pub struct LoadError(pub u64); + +/// Unhandled store access trap +#[derive(Clone, Copy, Display, Debug, PartialEq, Eq)] +#[display(fmt = "Store access error at address {_0:#x}")] +pub struct StoreError(pub u64); + +/// Reason to access memory +#[derive(Clone, Copy, Display, Debug, PartialEq, Eq)] +pub enum MemoryAccessReason { + /// Memory was accessed for load (read) + Load, + /// Memory was accessed for store (write) + Store, +} + +impl From for VmRunError { + fn from(value: LoadError) -> Self { + Self::LoadAccessEx(value.0) + } +} + +impl From for VmRunError { + fn from(value: StoreError) -> Self { + Self::StoreAccessEx(value.0) + } +} diff --git a/hbvm/src/main.rs b/hbvm/src/main.rs index fd46a3e5..05d23c7f 100644 --- a/hbvm/src/main.rs +++ b/hbvm/src/main.rs @@ -1,8 +1,9 @@ -use hbvm::mem::softpaged::{HandlePageFault, PageSize, SoftPagedMem}; - use { hbbytecode::valider::validate, - hbvm::{mem::MemoryAccessReason, Vm}, + hbvm::{ + softpaging::{HandlePageFault, PageSize, SoftPagedMem}, + MemoryAccessReason, Vm, + }, std::io::{stdin, Read}, }; @@ -31,7 +32,7 @@ fn main() -> Result<(), Box> { .map( data, 0, - hbvm::mem::softpaged::paging::Permission::Write, + hbvm::softpaging::paging::Permission::Write, PageSize::Size4K, ) .unwrap(); diff --git a/hbvm/src/mem/mod.rs b/hbvm/src/mem/mod.rs deleted file mode 100644 index 2391a3bf..00000000 --- a/hbvm/src/mem/mod.rs +++ /dev/null @@ -1,54 +0,0 @@ -//! Program memory implementation - -pub mod bmc; -pub mod softpaged; - -use {super::VmRunError, derive_more::Display}; - -pub trait Memory { - /// Load data from memory on address - /// - /// # Safety - /// - Shall not overrun the buffer - unsafe fn load(&mut self, addr: u64, target: *mut u8, count: usize) -> Result<(), LoadError>; - - /// Store data to memory on address - /// - /// # Safety - /// - Shall not overrun the buffer - unsafe fn store( - &mut self, - addr: u64, - source: *const u8, - count: usize, - ) -> Result<(), StoreError>; -} - -/// Unhandled load access trap -#[derive(Clone, Copy, Display, Debug, PartialEq, Eq)] -#[display(fmt = "Load access error at address {_0:#x}")] -pub struct LoadError(pub u64); - -/// Unhandled store access trap -#[derive(Clone, Copy, Display, Debug, PartialEq, Eq)] -#[display(fmt = "Store access error at address {_0:#x}")] -pub struct StoreError(pub u64); - -/// Reason to access memory -#[derive(Clone, Copy, Display, Debug, PartialEq, Eq)] -pub enum MemoryAccessReason { - Load, - Store, -} - -impl From for VmRunError { - fn from(value: LoadError) -> Self { - Self::LoadAccessEx(value.0) - } -} - -impl From for VmRunError { - fn from(value: StoreError) -> Self { - Self::StoreAccessEx(value.0) - } -} diff --git a/hbvm/src/mem/softpaged/mod.rs b/hbvm/src/softpaging/mod.rs similarity index 100% rename from hbvm/src/mem/softpaged/mod.rs rename to hbvm/src/softpaging/mod.rs diff --git a/hbvm/src/mem/softpaged/paging.rs b/hbvm/src/softpaging/paging.rs similarity index 97% rename from hbvm/src/mem/softpaged/paging.rs rename to hbvm/src/softpaging/paging.rs index 05511537..c1ab8426 100644 --- a/hbvm/src/mem/softpaged/paging.rs +++ b/hbvm/src/softpaging/paging.rs @@ -59,7 +59,9 @@ impl Debug for PtEntry { #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[repr(align(4096))] pub struct PageTable { + /// How much entries are in use pub childen: u8, + /// Entries pub table: [PtEntry; 256], } diff --git a/hbvm/src/value.rs b/hbvm/src/value.rs index fb8738e3..50d36d1e 100644 --- a/hbvm/src/value.rs +++ b/hbvm/src/value.rs @@ -13,7 +13,10 @@ macro_rules! value_def { #[derive(Copy, Clone)] #[repr(packed)] pub union Value { - $(pub $ty: $ty),* + $( + #[doc = concat!(stringify!($ty), " type")] + pub $ty: $ty + ),* } @@ -37,10 +40,22 @@ macro_rules! value_def { } impl Value { + /// Byte reinterpret value to target variant #[inline] - pub fn cast(self) -> Variant { + pub fn cast(self) -> V { + /// Evil. + /// + /// Transmute cannot be performed with generic type + /// as size is unknown, so union is used. + /// + /// # Safety + /// If [`ValueVariant`] implemented correctly, it's fine :) + /// + /// :ferrisClueless: union Transmute { + /// Self src: Value, + /// Target variant variant: Variant, } From 67a7d8ee256437255a0ee68407347258df3749a5 Mon Sep 17 00:00:00 2001 From: Erin Date: Tue, 8 Aug 2023 03:10:11 +0200 Subject: [PATCH 8/9] Added inner memory access --- hbvm/src/main.rs | 4 +++- hbvm/src/softpaging/mod.rs | 10 ++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/hbvm/src/main.rs b/hbvm/src/main.rs index 05d23c7f..f5cc6136 100644 --- a/hbvm/src/main.rs +++ b/hbvm/src/main.rs @@ -1,3 +1,5 @@ +use hbvm::softpaging::paging::PageTable; + use { hbbytecode::valider::validate, hbvm::{ @@ -61,7 +63,7 @@ impl HandlePageFault for TestTrapHandler { fn page_fault( &mut self, _: MemoryAccessReason, - //_: &mut Memory, + _: &mut PageTable, _: u64, _: PageSize, _: *mut u8, diff --git a/hbvm/src/softpaging/mod.rs b/hbvm/src/softpaging/mod.rs index 2ec72e57..96005a41 100644 --- a/hbvm/src/softpaging/mod.rs +++ b/hbvm/src/softpaging/mod.rs @@ -98,7 +98,13 @@ impl SoftPagedMem { // No page found Some(Err(AddrPageLookupError { addr, size })) => { // Attempt to execute page fault handler - if self.pf_handler.page_fault(reason, addr, size, dst) { + if self.pf_handler.page_fault( + reason, + unsafe { &mut *self.root_pt }, + addr, + size, + dst, + ) { // Shift the splitter address pspl.bump(size); @@ -465,7 +471,7 @@ pub trait HandlePageFault { fn page_fault( &mut self, reason: MemoryAccessReason, - // memory: &mut SoftPagedMem, TODO: Make work + pagetable: &mut PageTable, vaddr: u64, size: PageSize, dataptr: *mut u8, From 2b2d2f24346faa606047f4d45a1b906d9ddc8e3c Mon Sep 17 00:00:00 2001 From: Erin Date: Tue, 8 Aug 2023 03:10:23 +0200 Subject: [PATCH 9/9] fmt --- hbvm/src/main.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hbvm/src/main.rs b/hbvm/src/main.rs index f5cc6136..f2a23aba 100644 --- a/hbvm/src/main.rs +++ b/hbvm/src/main.rs @@ -1,9 +1,7 @@ -use hbvm::softpaging::paging::PageTable; - use { hbbytecode::valider::validate, hbvm::{ - softpaging::{HandlePageFault, PageSize, SoftPagedMem}, + softpaging::{paging::PageTable, HandlePageFault, PageSize, SoftPagedMem}, MemoryAccessReason, Vm, }, std::io::{stdin, Read},