use crate::iter::SplitAtIndex;
use crate::types::{umem, Address};
use super::{ArchMmuDef, ArchMmuSpec, MmuTranslationBase};
use std::cmp::Ordering;
use super::MVec;
pub type TranslateVec<'a> = MVec<'a, TranslationChunk<Address>>;
pub type TranslateDataVec<'a, T> = MVec<'a, TranslateData<T>>;
unsafe fn shorten_datavec_lifetime<'a: 'b, 'b, O>(
r: &'b mut TranslateDataVec<'a, O>,
) -> &'b mut TranslateDataVec<'b, O> {
std::mem::transmute(r)
}
unsafe fn shorten_pair_lifetime<'a: 't, 'b: 't, 't, O>(
r: &'t mut (TranslateVec<'a>, TranslateDataVec<'b, O>),
) -> &'t mut (TranslateVec<'t>, TranslateDataVec<'t, O>) {
std::mem::transmute(r)
}
#[derive(Debug)]
pub struct TranslateData<T> {
pub addr: Address,
pub meta_addr: Address,
pub buf: T,
}
impl<T: SplitAtIndex> TranslateData<T> {
pub fn split_at_address(self, addr: Address) -> (Option<Self>, Option<Self>) {
let sub = self.addr.to_umem();
self.split_at(addr.to_umem().saturating_sub(sub))
}
pub fn split_at_address_rev(self, addr: Address) -> (Option<Self>, Option<Self>) {
let base = self.addr + self.length();
self.split_at_rev(base.to_umem().saturating_sub(addr.to_umem()))
}
}
impl<T: SplitAtIndex> Ord for TranslateData<T> {
fn cmp(&self, other: &Self) -> Ordering {
self.addr.cmp(&other.addr)
}
}
impl<T: SplitAtIndex> Eq for TranslateData<T> {}
impl<T> PartialOrd for TranslateData<T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.addr.partial_cmp(&other.addr)
}
}
impl<T> PartialEq for TranslateData<T> {
fn eq(&self, other: &Self) -> bool {
self.addr == other.addr
}
}
impl<T: SplitAtIndex> SplitAtIndex for TranslateData<T> {
fn split_at(self, idx: umem) -> (Option<Self>, Option<Self>)
where
Self: Sized,
{
let addr = self.addr;
let meta_addr = self.meta_addr;
let (bleft, bright) = self.buf.split_at(idx);
(
bleft.map(|buf| TranslateData {
addr,
meta_addr,
buf,
}),
bright.map(|buf| TranslateData {
buf,
addr: addr + idx,
meta_addr: meta_addr + idx,
}),
)
}
unsafe fn split_at_mut(&mut self, idx: umem) -> (Option<Self>, Option<Self>)
where
Self: Sized,
{
let addr = self.addr;
let meta_addr = self.meta_addr;
let (bleft, bright) = self.buf.split_at_mut(idx);
(
bleft.map(|buf| TranslateData {
addr,
meta_addr,
buf,
}),
bright.map(|buf| TranslateData {
buf,
addr: addr + idx,
meta_addr: meta_addr + idx,
}),
)
}
fn length(&self) -> umem {
self.buf.length()
}
fn size_hint(&self) -> usize {
self.buf.size_hint()
}
}
bitflags! {
#[repr(transparent)]
#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
#[cfg_attr(feature = "abi_stable", derive(::abi_stable::StableAbi))]
pub struct FlagsType: u8 {
const NONE = 0b00;
const WRITEABLE = 0b01;
const NX = 0b10;
}
}
#[derive(Debug)]
pub struct TranslationChunk<T> {
pub pt_addr: T,
pub addr_count: usize,
pub min_addr: Address,
max_addr: Address,
pub step: usize,
pub prev_flags: FlagsType,
}
impl FlagsType {
pub fn nx(mut self, flag: bool) -> Self {
self &= !(FlagsType::NX);
if flag {
self | FlagsType::NX
} else {
self
}
}
pub fn writeable(mut self, flag: bool) -> Self {
self &= !(FlagsType::WRITEABLE);
if flag {
self | FlagsType::WRITEABLE
} else {
self
}
}
}
impl TranslationChunk<Address> {
pub fn update_flags(&mut self, mmu_def: &ArchMmuDef) {
self.prev_flags = FlagsType::NONE
.writeable((mmu_def.writeable_bit)(
self.pt_addr,
self.prev_flags.contains(FlagsType::WRITEABLE),
))
.nx((mmu_def.nx_bit)(
self.pt_addr,
self.prev_flags.contains(FlagsType::NX),
));
}
}
impl<T> TranslationChunk<T> {
pub fn new(pt_addr: T, prev_flags: FlagsType) -> Self {
let (min, max) = (!0u64, 0u64);
Self::with_minmax(pt_addr, prev_flags, min.into(), max.into())
}
pub fn with_minmax(
pt_addr: T,
prev_flags: FlagsType,
min_addr: Address,
max_addr: Address,
) -> Self {
Self {
pt_addr,
addr_count: 0,
step: 0,
min_addr,
max_addr,
prev_flags,
}
}
}
impl<T: MmuTranslationBase> TranslationChunk<T> {
pub fn push_data<U: SplitAtIndex>(
&mut self,
data: TranslateData<U>,
stack: &mut TranslateDataVec<U>,
) {
self.min_addr = std::cmp::min(self.min_addr, data.addr);
self.max_addr = std::cmp::max(self.max_addr, data.addr + data.length());
self.addr_count += 1;
stack.push(data);
}
pub fn pop_data<U: SplitAtIndex>(
&mut self,
stack: &mut TranslateDataVec<U>,
) -> Option<TranslateData<U>> {
if self.addr_count > 0 {
self.addr_count -= 1;
stack.pop()
} else {
None
}
}
pub fn next_max_addr_count(&self, spec: &ArchMmuSpec) -> umem {
let step_size = spec.page_size_step_unchecked(self.step + 1);
let addr_diff = self.max_addr.wrapping_sub(self.min_addr).to_umem();
let add = (addr_diff % step_size != 0) as umem;
self.addr_count as umem * (addr_diff / step_size + add)
}
pub fn split_chunk<U: SplitAtIndex>(
mut self,
spec: &ArchMmuSpec,
(addr_stack, tmp_addr_stack): (&mut TranslateDataVec<U>, &mut TranslateDataVec<U>),
out_target: &mut (TranslateVec, TranslateDataVec<U>),
wait_target: &mut (TranslateVec, TranslateDataVec<U>),
) {
let mut addr_stack = unsafe { shorten_datavec_lifetime(addr_stack) };
let mut tmp_addr_stack = unsafe { shorten_datavec_lifetime(tmp_addr_stack) };
let mut out_target = unsafe { shorten_pair_lifetime(out_target) };
let mut wait_target = unsafe { shorten_pair_lifetime(wait_target) };
let align_as = spec.page_size_step_unchecked(self.step);
let step_size = spec.page_size_step_unchecked(self.step + 1);
let upper = (self.max_addr - 1usize).as_mem_aligned(step_size).to_umem();
let lower = self.min_addr.as_mem_aligned(step_size).to_umem();
let mut cur_max_addr: umem = !0;
for (cnt, addr) in (0..=((upper - lower) / step_size))
.map(|i| upper - i * step_size)
.enumerate()
{
if addr > cur_max_addr {
continue;
}
cur_max_addr = 0;
let remaining = (addr - lower) / step_size + 1;
let (chunks_out, addrs_out) = if out_target.0.capacity() as umem
>= out_target.0.len() as umem + remaining
&& out_target.1.capacity() as umem
>= out_target.1.len() as umem + self.addr_count as umem * remaining
{
&mut out_target
} else {
&mut wait_target
};
let addr = Address::from(addr);
let addr_aligned = addr.as_mem_aligned(align_as);
let index = (addr - addr_aligned) as umem / step_size;
let (pt_addr, _) = self.pt_addr.get_pt_by_index(index as usize);
let pt_addr = spec.vtop_step(pt_addr, addr, self.step);
let mut new_chunk = TranslationChunk::new(pt_addr, self.prev_flags);
for _ in 0..self.addr_count {
let data = self.pop_data(addr_stack).unwrap();
debug_assert!(
data.addr >= self.min_addr,
"__ {} {:x}+{:x} | {:#?}",
cnt,
data.addr,
data.length(),
&self
);
debug_assert!(
data.addr + data.length() <= self.max_addr,
"{} {:x}+{:x} | {:#?}",
cnt,
data.addr,
data.length(),
&self
);
let (left, right) = data.split_at_address(addr);
if let Some(data) = right {
new_chunk.push_data(data, addrs_out);
}
if let Some(data) = left {
cur_max_addr =
std::cmp::max((data.addr + data.length()).to_umem(), cur_max_addr);
self.push_data(data, tmp_addr_stack);
}
}
if new_chunk.addr_count > 0 {
new_chunk.step = self.step;
chunks_out.push(new_chunk);
}
std::mem::swap(&mut addr_stack, &mut tmp_addr_stack);
}
debug_assert!(self.addr_count == 0);
}
}