mod page_cache;
use crate::architecture::ArchitectureObj;
use crate::error::{Error, ErrorKind, ErrorOrigin, Result};
use crate::iter::PageChunks;
use crate::mem::{
MemOps, PhysicalMemory, PhysicalMemoryMapping, PhysicalMemoryMetadata, PhysicalReadMemOps,
PhysicalWriteMemOps,
};
use cglue::tuple::*;
use page_cache::{PageCache, PageValidity};
use crate::types::cache::{CacheValidator, DefaultCacheValidator};
use crate::types::{size, PageType};
use bumpalo::Bump;
pub struct CachedPhysicalMemory<'a, T, Q> {
mem: T,
cache: PageCache<'a, Q>,
arena: Bump,
}
impl<'a, T, Q> Clone for CachedPhysicalMemory<'a, T, Q>
where
T: Clone,
Q: CacheValidator + Clone,
{
fn clone(&self) -> Self {
Self {
mem: self.mem.clone(),
cache: self.cache.clone(),
arena: Bump::new(),
}
}
}
impl<'a, T: PhysicalMemory, Q: CacheValidator> CachedPhysicalMemory<'a, T, Q> {
pub fn new(mem: T, cache: PageCache<'a, Q>) -> Self {
Self {
mem,
cache,
arena: Bump::new(),
}
}
pub fn into_inner(self) -> T {
self.mem
}
}
impl<'a, T: PhysicalMemory> CachedPhysicalMemory<'a, T, DefaultCacheValidator> {
pub fn builder(mem: T) -> CachedPhysicalMemoryBuilder<T, DefaultCacheValidator> {
CachedPhysicalMemoryBuilder::new(mem)
}
}
impl<'a, T: PhysicalMemory, Q: CacheValidator> PhysicalMemory for CachedPhysicalMemory<'a, T, Q> {
fn phys_read_raw_iter(&mut self, data: PhysicalReadMemOps) -> Result<()> {
self.cache.validator.update_validity();
self.arena.reset();
self.cache.cached_read(&mut self.mem, data, &self.arena)
}
fn phys_write_raw_iter(
&mut self,
MemOps { inp, out, out_fail }: PhysicalWriteMemOps,
) -> Result<()> {
self.cache.validator.update_validity();
let mem = &mut self.mem;
let cache = &mut self.cache;
let inp = inp.map(move |CTup3(addr, meta_addr, data)| {
if cache.is_cached_page_type(addr.page_type()) {
for (paddr, data_chunk) in data.page_chunks(addr.address(), cache.page_size()) {
let mut cached_page = cache.cached_page_mut(paddr, false);
if let PageValidity::Valid(buf) = &mut cached_page.validity {
let start = (paddr - cached_page.address) as usize;
buf[start..(start + data_chunk.len())].copy_from_slice(data_chunk.into());
}
cache.put_entry(cached_page);
}
}
CTup3(addr, meta_addr, data)
});
MemOps::with_raw(inp, out, out_fail, move |data| {
mem.phys_write_raw_iter(data)
})
}
#[inline]
fn metadata(&self) -> PhysicalMemoryMetadata {
self.mem.metadata()
}
#[inline]
fn set_mem_map(&mut self, mem_map: &[PhysicalMemoryMapping]) {
self.mem.set_mem_map(mem_map)
}
}
pub struct CachedPhysicalMemoryBuilder<T, Q> {
mem: T,
validator: Q,
page_size: Option<usize>,
cache_size: usize,
page_type_mask: PageType,
}
impl<T: PhysicalMemory> CachedPhysicalMemoryBuilder<T, DefaultCacheValidator> {
pub fn new(mem: T) -> Self {
Self {
mem,
validator: DefaultCacheValidator::default(),
page_size: None,
cache_size: size::mb(2),
page_type_mask: PageType::PAGE_TABLE | PageType::READ_ONLY,
}
}
}
impl<T: PhysicalMemory, Q: CacheValidator> CachedPhysicalMemoryBuilder<T, Q> {
pub fn build<'a>(self) -> Result<CachedPhysicalMemory<'a, T, Q>> {
Ok(CachedPhysicalMemory::new(
self.mem,
PageCache::with_page_size(
self.page_size.ok_or_else(|| {
Error(ErrorOrigin::Cache, ErrorKind::Uninitialized)
.log_error("page_size must be initialized")
})?,
self.cache_size,
self.page_type_mask,
self.validator,
),
))
}
pub fn validator<QN: CacheValidator>(
self,
validator: QN,
) -> CachedPhysicalMemoryBuilder<T, QN> {
CachedPhysicalMemoryBuilder {
mem: self.mem,
validator,
page_size: self.page_size,
cache_size: self.cache_size,
page_type_mask: self.page_type_mask,
}
}
pub fn page_size(mut self, page_size: usize) -> Self {
self.page_size = Some(page_size);
self
}
pub fn arch(mut self, arch: impl Into<ArchitectureObj>) -> Self {
self.page_size = Some(arch.into().page_size());
self
}
pub fn cache_size(mut self, cache_size: usize) -> Self {
self.cache_size = cache_size;
self
}
pub fn page_type_mask(mut self, page_type_mask: PageType) -> Self {
self.page_type_mask = page_type_mask;
self
}
}
#[cfg(feature = "plugins")]
cglue::cglue_impl_group!(
CachedPhysicalMemory<'cglue_a, T: PhysicalMemory, Q: CacheValidator>,
crate::plugins::ConnectorInstance,
{}
);