use std::prelude::v1::*;
use super::{MemoryRange, MemoryRangeCallback, VtopRange};
use std::cmp::*;
use cglue::prelude::v1::*;
use itertools::Itertools;
pub mod direct_translate;
use crate::iter::SplitAtIndex;
pub use direct_translate::DirectTranslate;
use crate::architecture::ArchitectureObj;
use crate::types::gap_remover::GapRemover;
#[macro_use]
pub mod mmu;
pub mod cache;
pub use cache::*;
#[cfg(test)]
mod tests;
use crate::error::{Result, *};
use crate::mem::PhysicalMemory;
use crate::types::{imem, umem, Address, Page, PhysicalAddress};
#[cfg_attr(feature = "plugins", cglue_trait)]
#[int_result]
pub trait VirtualTranslate: Send {
fn virt_to_phys_list(
&mut self,
addrs: &[VtopRange],
out: VirtualTranslationCallback,
out_fail: VirtualTranslationFailCallback,
);
fn virt_to_phys_range(
&mut self,
start: Address,
end: Address,
out: VirtualTranslationCallback,
) {
assert!(end >= start);
self.virt_to_phys_list(
&[CTup2(start, (end - start) as umem)],
out,
(&mut |_| true).into(),
)
}
fn virt_translation_map_range(
&mut self,
start: Address,
end: Address,
out: VirtualTranslationCallback,
) {
let mut set = std::collections::BTreeSet::new();
self.virt_to_phys_range(
start,
end,
(&mut |v| {
set.insert(v);
true
})
.into(),
);
set.into_iter()
.coalesce(|a, b| {
if b.in_virtual == (a.in_virtual + a.size)
&& b.out_physical.address() == (a.out_physical.address() + a.size)
&& a.out_physical.page_type() == b.out_physical.page_type()
{
Ok(VirtualTranslation {
in_virtual: a.in_virtual,
size: a.size + b.size,
out_physical: a.out_physical,
})
} else {
Err((a, b))
}
})
.feed_into(out);
}
fn virt_page_map_range(
&mut self,
gap_size: imem,
start: Address,
end: Address,
out: MemoryRangeCallback,
) {
let mut gap_remover = GapRemover::new(out, gap_size, start, end);
self.virt_to_phys_range(
start,
end,
(&mut |VirtualTranslation {
in_virtual,
size,
out_physical,
}| {
gap_remover.push_range(CTup3(in_virtual, size, out_physical.page_type));
true
})
.into(),
);
}
fn virt_to_phys(&mut self, address: Address) -> Result<PhysicalAddress> {
let mut out = Err(Error(ErrorOrigin::VirtualTranslate, ErrorKind::OutOfBounds));
self.virt_to_phys_list(
&[CTup2(address, 1)],
(&mut |VirtualTranslation {
in_virtual: _,
size: _,
out_physical,
}| {
out = Ok(out_physical);
false
})
.into(),
(&mut |_| true).into(),
);
out
}
fn virt_page_info(&mut self, addr: Address) -> Result<Page> {
let paddr = self.virt_to_phys(addr)?;
Ok(paddr.containing_page())
}
#[skip_func]
fn virt_page_map_range_vec(
&mut self,
gap_size: imem,
start: Address,
end: Address,
) -> Vec<MemoryRange> {
let mut out = vec![];
self.virt_page_map_range(gap_size, start, end, (&mut out).into());
out
}
fn virt_translation_map(&mut self, out: VirtualTranslationCallback) {
self.virt_translation_map_range(Address::null(), Address::invalid(), out)
}
#[skip_func]
fn virt_translation_map_vec(&mut self) -> Vec<VirtualTranslation> {
let mut out = vec![];
self.virt_translation_map((&mut out).into());
out
}
fn phys_to_virt(&mut self, phys: Address) -> Option<Address> {
let mut virt = None;
let callback = &mut |VirtualTranslation {
in_virtual,
size: _,
out_physical,
}| {
if out_physical.address() == phys {
virt = Some(in_virtual);
false
} else {
true
}
};
self.virt_translation_map(callback.into());
virt
}
#[skip_func]
fn phys_to_virt_vec(&mut self, phys: Address) -> Vec<Address> {
let mut virt = vec![];
let callback = &mut |VirtualTranslation {
in_virtual,
size: _,
out_physical,
}| {
if out_physical.address() == phys {
virt.push(in_virtual);
true
} else {
true
}
};
self.virt_translation_map(callback.into());
virt
}
fn virt_page_map(&mut self, gap_size: imem, out: MemoryRangeCallback) {
self.virt_page_map_range(gap_size, Address::null(), Address::invalid(), out)
}
#[skip_func]
fn virt_page_map_vec(&mut self, gap_size: imem) -> Vec<MemoryRange> {
let mut out = vec![];
self.virt_page_map(gap_size, (&mut out).into());
out
}
}
pub type VirtualTranslationCallback<'a> = OpaqueCallback<'a, VirtualTranslation>;
pub type VirtualTranslationFailCallback<'a> = OpaqueCallback<'a, VirtualTranslationFail>;
#[repr(C)]
#[derive(Clone, Debug, Eq, Copy)]
#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
#[cfg_attr(feature = "abi_stable", derive(::abi_stable::StableAbi))]
pub struct VirtualTranslation {
pub in_virtual: Address,
pub size: umem,
pub out_physical: PhysicalAddress,
}
impl Ord for VirtualTranslation {
fn cmp(&self, other: &Self) -> Ordering {
self.in_virtual.cmp(&other.in_virtual)
}
}
impl PartialOrd for VirtualTranslation {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq for VirtualTranslation {
fn eq(&self, other: &Self) -> bool {
self.in_virtual == other.in_virtual
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
#[cfg_attr(feature = "abi_stable", derive(::abi_stable::StableAbi))]
pub struct VirtualTranslationFail {
pub from: Address,
pub size: umem,
}
pub trait VirtualTranslate2
where
Self: Send,
{
fn virt_to_phys_iter<T, B, D, VI>(
&mut self,
phys_mem: &mut T,
translator: &D,
addrs: VI,
out: &mut VtopOutputCallback<B>,
out_fail: &mut VtopFailureCallback<B>,
) where
T: PhysicalMemory + ?Sized,
B: SplitAtIndex,
D: VirtualTranslate3,
VI: Iterator<Item = CTup3<Address, Address, B>>;
fn virt_to_phys<T: PhysicalMemory + ?Sized, D: VirtualTranslate3>(
&mut self,
phys_mem: &mut T,
translator: &D,
vaddr: Address,
) -> Result<PhysicalAddress> {
let mut output = None;
let success = &mut |elem: CTup3<PhysicalAddress, Address, _>| {
if output.is_none() {
output = Some(elem.0);
}
false
};
let mut output_err = None;
let fail = &mut |elem: (Error, _)| {
output_err = Some(elem.0);
true
};
self.virt_to_phys_iter(
phys_mem,
translator,
Some(CTup3::<_, _, umem>(vaddr, vaddr, 1)).into_iter(),
&mut success.into(),
&mut fail.into(),
);
output.map(Ok).unwrap_or_else(|| Err(output_err.unwrap()))
}
}
impl<T, P> VirtualTranslate2 for P
where
T: VirtualTranslate2 + ?Sized,
P: std::ops::DerefMut<Target = T> + Send,
{
#[inline]
fn virt_to_phys_iter<U, B, D, VI>(
&mut self,
phys_mem: &mut U,
translator: &D,
addrs: VI,
out: &mut VtopOutputCallback<B>,
out_fail: &mut VtopFailureCallback<B>,
) where
U: PhysicalMemory + ?Sized,
B: SplitAtIndex,
D: VirtualTranslate3,
VI: Iterator<Item = CTup3<Address, Address, B>>,
{
(**self).virt_to_phys_iter(phys_mem, translator, addrs, out, out_fail)
}
}
pub trait VirtualTranslate3: Clone + Copy + Send {
fn virt_to_phys<T: PhysicalMemory>(
&self,
mem: &mut T,
addr: Address,
) -> Result<PhysicalAddress> {
let mut buf: [std::mem::MaybeUninit<u8>; 512] =
unsafe { std::mem::MaybeUninit::uninit().assume_init() };
let mut output = None;
let success = &mut |elem: CTup3<PhysicalAddress, Address, _>| {
if output.is_none() {
output = Some(elem.0);
}
false
};
let mut output_err = None;
let fail = &mut |elem: (Error, _)| {
output_err = Some(elem.0);
true
};
self.virt_to_phys_iter(
mem,
Some(CTup3::<_, _, umem>(addr, addr, 1)).into_iter(),
&mut success.into(),
&mut fail.into(),
&mut buf,
);
output.map(Ok).unwrap_or_else(|| Err(output_err.unwrap()))
}
fn virt_to_phys_iter<
T: PhysicalMemory + ?Sized,
B: SplitAtIndex,
VI: Iterator<Item = CTup3<Address, Address, B>>,
>(
&self,
mem: &mut T,
addrs: VI,
out: &mut VtopOutputCallback<B>,
out_fail: &mut VtopFailureCallback<B>,
tmp_buf: &mut [std::mem::MaybeUninit<u8>],
);
fn translation_table_id(&self, address: Address) -> umem;
fn arch(&self) -> ArchitectureObj;
}
pub type VtopOutputCallback<'a, B> = OpaqueCallback<'a, CTup3<PhysicalAddress, Address, B>>;
pub type VtopFailureCallback<'a, B> = OpaqueCallback<'a, (Error, CTup3<Address, Address, B>)>;