use crate::{FrameAllocator, PageTableEntry, PageTableRef, TableMeta, VirtAddr, frame::Frame};
use heapless::Vec;
const MAX_WALK_DEPTH: usize = 8;
#[derive(Debug, Clone, Copy)]
pub struct PteInfo<P: PageTableEntry> {
pub level: usize,
pub vaddr: VirtAddr,
pub is_final_mapping: bool,
pub pte: P,
}
#[derive(Debug, Clone, Copy)]
pub struct WalkConfig {
pub start_vaddr: VirtAddr,
pub end_vaddr: VirtAddr,
}
pub struct PageTableWalker<'a, T: TableMeta, A: FrameAllocator> {
_phantom: core::marker::PhantomData<&'a ()>,
config: WalkConfig,
stack: Vec<WalkState<T, A>, MAX_WALK_DEPTH>,
finished: bool,
}
#[derive(Clone, Copy)]
struct WalkState<T: TableMeta, A: FrameAllocator> {
frame: Frame<T, A>,
level: usize,
index: usize,
base_vaddr: VirtAddr,
}
impl<'a, T: TableMeta, A: FrameAllocator> PageTableWalker<'a, T, A> {
pub fn new(page_table: &'a PageTableRef<T, A>, config: WalkConfig) -> Self {
let mut walker = Self {
_phantom: core::marker::PhantomData,
config,
stack: Vec::new(),
finished: false,
};
if walker.config.start_vaddr < walker.config.end_vaddr {
let root_state = WalkState {
frame: Frame::from_paddr(page_table.root.paddr, page_table.root.allocator.clone()),
level: Frame::<T, A>::PT_LEVEL,
index: 0,
base_vaddr: VirtAddr::new(0),
};
walker.stack.push(root_state).ok(); } else {
walker.finished = true;
}
walker
}
fn find_next_entry(&mut self) -> Option<PteInfo<T::P>> {
loop {
if self.finished {
return None;
}
if self.stack.is_empty() {
self.finished = true;
return None;
}
let state = self.stack.last_mut().unwrap();
if state.index >= Frame::<T, A>::LEN {
self.stack.pop();
continue;
}
let entries = state.frame.as_slice();
let pte = entries[state.index];
state.index += 1;
let current_vaddr =
Frame::<T, A>::reconstruct_vaddr(state.index - 1, state.level, state.base_vaddr);
if current_vaddr < self.config.start_vaddr {
continue;
}
if current_vaddr >= self.config.end_vaddr {
self.finished = true;
return None;
}
let pte_config = pte.to_config(state.level > 1);
let is_final_mapping = pte_config.valid && (pte_config.huge || state.level == 1);
if pte_config.valid && !pte_config.huge && state.level > 1 {
let child_frame =
Frame::from_paddr(pte_config.paddr, state.frame.allocator.clone());
let child_base_vaddr = current_vaddr;
let child_state = WalkState {
frame: child_frame,
level: state.level - 1,
index: 0,
base_vaddr: child_base_vaddr,
};
let level = state.level;
let vaddr = current_vaddr;
self.stack.push(child_state).ok();
return Some(PteInfo {
level,
vaddr,
pte,
is_final_mapping,
});
}
return Some(PteInfo {
level: state.level,
vaddr: current_vaddr,
pte,
is_final_mapping,
});
}
}
}
impl<'a, T: TableMeta, A: FrameAllocator> Iterator for PageTableWalker<'a, T, A> {
type Item = PteInfo<T::P>;
fn next(&mut self) -> Option<Self::Item> {
self.find_next_entry()
}
}