page_table_generic/
walk.rs1use crate::{FrameAllocator, PageTableEntry, PageTableRef, TableMeta, VirtAddr, frame::Frame};
2
3use heapless::Vec;
4
5const MAX_WALK_DEPTH: usize = 8;
7
8#[derive(Debug, Clone, Copy)]
10pub struct PteInfo<P: PageTableEntry> {
11 pub level: usize,
13 pub vaddr: VirtAddr,
15 pub is_final_mapping: bool,
19 pub pte: P,
21}
22
23#[derive(Debug, Clone, Copy)]
25pub struct WalkConfig {
26 pub start_vaddr: VirtAddr,
28 pub end_vaddr: VirtAddr,
30}
31
32pub struct PageTableWalker<'a, T: TableMeta, A: FrameAllocator> {
34 _phantom: core::marker::PhantomData<&'a ()>,
35 config: WalkConfig,
36 stack: Vec<WalkState<T, A>, MAX_WALK_DEPTH>,
38 finished: bool,
39}
40
41#[derive(Clone, Copy)]
43struct WalkState<T: TableMeta, A: FrameAllocator> {
44 frame: Frame<T, A>,
45 level: usize,
46 index: usize,
47 base_vaddr: VirtAddr,
48}
49
50impl<'a, T: TableMeta, A: FrameAllocator> PageTableWalker<'a, T, A> {
51 pub fn new(page_table: &'a PageTableRef<T, A>, config: WalkConfig) -> Self {
53 let mut walker = Self {
54 _phantom: core::marker::PhantomData,
55 config,
56 stack: Vec::new(),
57 finished: false,
58 };
59
60 if walker.config.start_vaddr < walker.config.end_vaddr {
62 let root_state = WalkState {
63 frame: Frame::from_paddr(page_table.root.paddr, page_table.root.allocator.clone()),
64 level: Frame::<T, A>::PT_LEVEL,
65 index: 0,
66 base_vaddr: VirtAddr::new(0),
67 };
68 walker.stack.push(root_state).ok(); } else {
70 walker.finished = true;
71 }
72
73 walker
74 }
75
76 fn find_next_entry(&mut self) -> Option<PteInfo<T::P>> {
78 loop {
79 if self.finished {
80 return None;
81 }
82
83 if self.stack.is_empty() {
84 self.finished = true;
85 return None;
86 }
87
88 let state = self.stack.last_mut().unwrap();
89
90 if state.index >= Frame::<T, A>::LEN {
92 self.stack.pop();
93 continue;
94 }
95
96 let entries = state.frame.as_slice();
98 let pte = entries[state.index];
99 state.index += 1;
100
101 let current_vaddr =
103 Frame::<T, A>::reconstruct_vaddr(state.index - 1, state.level, state.base_vaddr);
104
105 if current_vaddr < self.config.start_vaddr {
107 continue;
108 }
109
110 if current_vaddr >= self.config.end_vaddr {
111 self.finished = true;
112 return None;
113 }
114
115 let pte_config = pte.to_config(state.level > 1);
121 let is_final_mapping = pte_config.valid && (pte_config.huge || state.level == 1);
122
123 if pte_config.valid && !pte_config.huge && state.level > 1 {
125 let child_frame =
126 Frame::from_paddr(pte_config.paddr, state.frame.allocator.clone());
127
128 let child_base_vaddr = current_vaddr;
130
131 let child_state = WalkState {
133 frame: child_frame,
134 level: state.level - 1,
135 index: 0,
136 base_vaddr: child_base_vaddr,
137 };
138
139 let level = state.level;
141 let vaddr = current_vaddr;
142
143 self.stack.push(child_state).ok();
144
145 return Some(PteInfo {
146 level,
147 vaddr,
148 pte,
149 is_final_mapping,
150 });
151 }
152
153 return Some(PteInfo {
155 level: state.level,
156 vaddr: current_vaddr,
157 pte,
158 is_final_mapping,
159 });
160 }
161 }
162}
163
164impl<'a, T: TableMeta, A: FrameAllocator> Iterator for PageTableWalker<'a, T, A> {
165 type Item = PteInfo<T::P>;
166
167 fn next(&mut self) -> Option<Self::Item> {
168 self.find_next_entry()
169 }
170}