page_table_generic/
map.rs1use crate::{
2 FrameAllocator, PageTableEntry, PagingError, PagingResult, PhysAddr, PteConfigOf, TableMeta,
3 VirtAddr, frame::Frame,
4};
5
6#[repr(C)]
8#[derive(Clone, Copy)]
9pub struct MapConfig<C> {
10 pub vaddr: VirtAddr,
11 pub paddr: PhysAddr,
12 pub size: usize,
13 pub pte: C,
17 pub allow_huge: bool,
18 pub flush: bool,
19}
20
21#[derive(Clone, Copy)]
23pub struct MapRecursiveConfig<C> {
24 pub start_vaddr: VirtAddr,
25 pub start_paddr: PhysAddr,
26 pub end_vaddr: VirtAddr,
27 pub level: usize,
28 pub allow_huge: bool,
29 pub flush: bool,
30 pub pte_template: C,
31}
32
33#[derive(Clone, Copy)]
35pub struct UnmapConfig {
36 pub start_vaddr: VirtAddr,
37 pub size: usize,
38 pub flush: bool,
39}
40
41#[derive(Clone, Copy)]
43pub struct UnmapRecursiveConfig {
44 pub start_vaddr: VirtAddr,
45 pub end_vaddr: VirtAddr,
46 pub level: usize,
47 pub flush: bool,
48}
49
50impl<C> core::fmt::Debug for MapConfig<C> {
51 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
52 f.debug_struct("MapConfig")
53 .field("vaddr", &format_args!("{:#x}", self.vaddr.as_usize()))
54 .field("paddr", &format_args!("{:#x}", self.paddr.as_usize()))
55 .field("size", &format_args!("{:#x}", self.size))
56 .field("allow_huge", &self.allow_huge)
57 .field("flush", &self.flush)
58 .finish()
59 }
60}
61
62impl<T, A> Frame<T, A>
63where
64 T: TableMeta,
65 A: FrameAllocator,
66{
67 pub fn map_range_recursive(
69 &mut self,
70 config: MapRecursiveConfig<PteConfigOf<T>>,
71 ) -> PagingResult<()> {
72 let mut vaddr = config.start_vaddr;
73 let mut paddr = config.start_paddr;
74
75 while vaddr < config.end_vaddr {
76 let index = Self::virt_to_index(vaddr, config.level);
77 let level_size = Self::level_size(config.level);
78 let remaining_size = config.end_vaddr - vaddr;
79
80 if config.allow_huge
82 && config.level > 1
83 && config.level <= T::MAX_BLOCK_LEVEL
84 && level_size <= remaining_size
85 && vaddr.as_usize().is_multiple_of(level_size)
86 && paddr.as_usize().is_multiple_of(level_size)
87 {
88 let entries = self.as_slice_mut();
90 let pte_ref = &mut entries[index];
91 if !pte_ref.unused() {
92 return Err(PagingError::mapping_conflict(vaddr, paddr));
93 }
94 *pte_ref = T::P::new_page(paddr, config.pte_template, true);
95
96 if config.flush {
98 T::flush(Some(vaddr));
99 }
100
101 vaddr += level_size;
102 paddr += level_size;
103 continue;
104 }
105
106 if config.level == 1 {
108 let entries = self.as_slice_mut();
110 let pte_ref = &mut entries[index];
111 if !pte_ref.unused() {
112 return Err(PagingError::mapping_conflict(vaddr, paddr));
113 }
114
115 *pte_ref = T::P::new_page(paddr, config.pte_template, false);
116
117 if config.flush {
119 T::flush(Some(vaddr));
120 }
121
122 vaddr += T::PAGE_SIZE;
123 paddr += T::PAGE_SIZE;
124 continue;
125 }
126
127 let allocator = self.allocator.clone();
129 let current_pte = self.as_slice()[index];
130
131 let child_frame = if !current_pte.unused() {
132 if current_pte.huge(true) {
133 return Err(PagingError::mapping_conflict(
134 vaddr,
135 current_pte.paddr(true),
136 ));
137 }
138 if !current_pte.present() {
139 return Err(PagingError::hierarchy_error(
140 "Non-present intermediate entry is not a leaf",
141 ));
142 }
143
144 Frame::from_paddr(current_pte.paddr(true), allocator)
146 } else {
147 let new_frame = Frame::<T, A>::new(allocator)?;
149 let new_frame_paddr = new_frame.paddr;
150
151 let entries = self.as_slice_mut();
153 let pte_ref = &mut entries[index];
154 *pte_ref = T::P::new_table(new_frame_paddr);
155
156 new_frame
157 };
158
159 let current_entry_end = (vaddr.as_usize() / level_size)
162 .saturating_add(1)
163 .saturating_mul(level_size);
164 let next_level_vaddr =
165 VirtAddr::from_usize(current_entry_end.min(config.end_vaddr.as_usize()));
166 let mut child_frame = child_frame;
167 let child_config = MapRecursiveConfig {
168 start_vaddr: vaddr,
169 start_paddr: paddr,
170 end_vaddr: next_level_vaddr,
171 level: config.level - 1,
172 allow_huge: config.allow_huge,
173 flush: config.flush,
174 pte_template: config.pte_template,
175 };
176 child_frame.map_range_recursive(child_config)?;
177
178 let mapped_size = next_level_vaddr - vaddr;
180 vaddr = next_level_vaddr;
181 paddr += mapped_size;
182 }
183
184 Ok(())
185 }
186
187 pub fn unmap_range_recursive(&mut self, config: UnmapRecursiveConfig) -> PagingResult<bool> {
191 let mut vaddr = config.start_vaddr;
192 let mut can_reclaim = true;
193 let allocator = self.allocator.clone();
194
195 while vaddr < config.end_vaddr {
196 let index = Self::virt_to_index(vaddr, config.level);
197 let level_size = Self::level_size(config.level);
198 let remaining_size = config.end_vaddr - vaddr;
199
200 let entries = self.as_slice_mut();
201 let pte_ref = &mut entries[index];
202
203 if pte_ref.unused() {
206 vaddr += level_size.min(remaining_size);
207 continue;
208 }
209
210 if !pte_ref.present() {
211 pte_ref.clear();
212 if config.flush {
213 T::flush(Some(vaddr));
214 }
215 vaddr += level_size.min(remaining_size);
216 continue;
217 }
218
219 let is_huge = pte_ref.huge(config.level > 1);
221 if config.level == 1 || is_huge {
222 pte_ref.clear();
224
225 if config.flush {
227 T::flush(Some(vaddr));
228 }
229
230 vaddr += if is_huge { level_size } else { T::PAGE_SIZE };
231 continue;
232 }
233
234 let child_paddr = pte_ref.paddr(true);
237
238 let current_entry_end = ((vaddr.as_usize() / level_size) + 1) * level_size;
240 let next_level_vaddr =
241 VirtAddr::from_usize(current_entry_end.min(config.end_vaddr.as_usize()));
242
243 {
244 let mut child_frame: Frame<T, A> =
245 Frame::from_paddr(child_paddr, allocator.clone());
246 let child_config = UnmapRecursiveConfig {
247 start_vaddr: vaddr,
248 end_vaddr: next_level_vaddr,
249 level: config.level - 1,
250 flush: config.flush,
251 };
252
253 let child_can_reclaim = child_frame.unmap_range_recursive(child_config)?;
255
256 if child_can_reclaim {
257 pte_ref.clear();
260 allocator.dealloc_frame(child_paddr);
261 } else {
262 can_reclaim = false;
264 }
265 }
266
267 vaddr = next_level_vaddr;
268 }
269
270 if can_reclaim {
272 can_reclaim = self.is_frame_empty();
273 }
274
275 Ok(can_reclaim)
276 }
277
278 fn is_frame_empty(&self) -> bool {
280 let entries = self.as_slice();
281 for pte in entries {
282 if !pte.unused() {
283 return false;
284 }
285 }
286 true
287 }
288}