diff --git a/Cargo.toml b/Cargo.toml index 7fc1ade..1c77a26 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,3 @@ [workspace] -members = ["hbasm", "hbbytecode", "hbvm"] +resolver = "2" +members = ["hbasm", "hbbytecode", "hbvm"] diff --git a/hbvm/src/main.rs b/hbvm/src/main.rs index d61d329..80814c6 100644 --- a/hbvm/src/main.rs +++ b/hbvm/src/main.rs @@ -21,6 +21,7 @@ fn main() -> Result<(), Box> { pf_handler: TestTrapHandler, program: &prog, root_pt: Box::into_raw(Default::default()), + icache: Default::default(), }, 4, ); diff --git a/hbvm/src/mem/softpaging/icache.rs b/hbvm/src/mem/softpaging/icache.rs new file mode 100644 index 0000000..e3cfce3 --- /dev/null +++ b/hbvm/src/mem/softpaging/icache.rs @@ -0,0 +1,107 @@ +//! Program instruction cache + +use { + super::{lookup::AddrPageLookuper, paging::PageTable, HandlePageFault, 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>, + /// 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( + &mut self, + addr: u64, + root_pt: *const PageTable, + ) -> Option { + let mut ret = MaybeUninit::::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::(); + + // 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::(), 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::().add(first_copy), + rem, + ); + } + + Some(ret.assume_init()) + } + + /// Fetch a page + unsafe fn fetch_page(&mut self, addr: u64, pt: *const PageTable) -> Option> { + 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 + } +} diff --git a/hbvm/src/mem/softpaging/lookup.rs b/hbvm/src/mem/softpaging/lookup.rs index 9111390..1e9c3e3 100644 --- a/hbvm/src/mem/softpaging/lookup.rs +++ b/hbvm/src/mem/softpaging/lookup.rs @@ -111,7 +111,7 @@ impl Iterator for AddrPageLookuper { }; // 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); Some(Ok(AddrPageLookupOk { diff --git a/hbvm/src/mem/softpaging/mod.rs b/hbvm/src/mem/softpaging/mod.rs index 6ed66dc..1acf984 100644 --- a/hbvm/src/mem/softpaging/mod.rs +++ b/hbvm/src/mem/softpaging/mod.rs @@ -2,6 +2,9 @@ use core::mem::size_of; +use self::icache::ICache; + +pub mod icache; pub mod lookup; pub mod paging; @@ -23,6 +26,8 @@ pub struct SoftPagedMem<'p, PfH> { pub pf_handler: PfH, /// Program memory segment pub program: &'p [u8], + /// Program instruction cache + pub icache: ICache, } impl<'p, PfH: HandlePageFault> Memory for SoftPagedMem<'p, PfH> { @@ -65,6 +70,10 @@ impl<'p, PfH: HandlePageFault> Memory for SoftPagedMem<'p, PfH> { #[inline(always)] unsafe fn prog_read(&mut self, addr: u64) -> Option { + if addr as usize > self.program.len() { + return self.icache.fetch::(addr, self.root_pt); + } + let addr = addr as usize; self.program .get(addr..addr + size_of::()) @@ -73,6 +82,13 @@ impl<'p, PfH: HandlePageFault> Memory for SoftPagedMem<'p, PfH> { #[inline(always)] unsafe fn prog_read_unchecked(&mut self, addr: u64) -> T { + if addr as usize > self.program.len() { + return self + .icache + .fetch::(addr as _, self.root_pt) + .unwrap_or_else(|| core::mem::zeroed()); + } + self.program.as_ptr().add(addr as _).cast::().read() } }