1use 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 pub(crate) retained_root_entries: Option<(usize, usize)>,
49}
50
51impl<C> core::fmt::Debug for MapConfig<C> {
52 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
53 f.debug_struct("MapConfig")
54 .field("vaddr", &format_args!("{:#x}", self.vaddr.as_usize()))
55 .field("paddr", &format_args!("{:#x}", self.paddr.as_usize()))
56 .field("size", &format_args!("{:#x}", self.size))
57 .field("allow_huge", &self.allow_huge)
58 .field("flush", &self.flush)
59 .finish()
60 }
61}
62
63impl<T, A> Frame<T, A>
64where
65 T: TableMeta,
66 A: FrameAllocator,
67{
68 pub fn map_range_recursive(
70 &mut self,
71 config: MapRecursiveConfig<PteConfigOf<T>>,
72 ) -> PagingResult<()> {
73 let mut vaddr = config.start_vaddr;
74 let mut paddr = config.start_paddr;
75
76 while vaddr < config.end_vaddr {
77 let index = Self::virt_to_index(vaddr, config.level);
78 let level_size = Self::level_size(config.level);
79 let remaining_size = config.end_vaddr - vaddr;
80
81 if config.allow_huge
83 && config.level > 1
84 && config.level <= T::MAX_BLOCK_LEVEL
85 && level_size <= remaining_size
86 && vaddr.as_usize().is_multiple_of(level_size)
87 && paddr.as_usize().is_multiple_of(level_size)
88 {
89 let entries = self.as_slice_mut();
91 let pte_ref = &mut entries[index];
92 if !pte_ref.unused() {
93 return Err(PagingError::mapping_conflict(vaddr, paddr));
94 }
95 *pte_ref = T::P::new_page(paddr, config.pte_template, true);
96
97 if config.flush {
99 T::flush(Some(vaddr));
100 }
101
102 vaddr = VirtAddr::from_usize(vaddr.as_usize().checked_add(level_size).ok_or_else(
103 || {
104 PagingError::address_overflow(
105 "Virtual address overflow in map_range_recursive",
106 )
107 },
108 )?);
109 paddr = PhysAddr::from_usize(paddr.as_usize().checked_add(level_size).ok_or_else(
110 || {
111 PagingError::address_overflow(
112 "Physical address overflow in map_range_recursive",
113 )
114 },
115 )?);
116 continue;
117 }
118
119 if config.level == 1 {
121 let entries = self.as_slice_mut();
123 let pte_ref = &mut entries[index];
124 if !pte_ref.unused() {
125 return Err(PagingError::mapping_conflict(vaddr, paddr));
126 }
127
128 *pte_ref = T::P::new_page(paddr, config.pte_template, false);
129
130 if config.flush {
132 T::flush(Some(vaddr));
133 }
134
135 vaddr = VirtAddr::from_usize(
136 vaddr.as_usize().checked_add(T::PAGE_SIZE).ok_or_else(|| {
137 PagingError::address_overflow(
138 "Virtual address overflow in map_range_recursive",
139 )
140 })?,
141 );
142 paddr = PhysAddr::from_usize(
143 paddr.as_usize().checked_add(T::PAGE_SIZE).ok_or_else(|| {
144 PagingError::address_overflow(
145 "Physical address overflow in map_range_recursive",
146 )
147 })?,
148 );
149 continue;
150 }
151
152 let allocator = self.allocator.clone();
154 let current_pte = self.as_slice()[index];
155
156 let child_frame = if !current_pte.unused() {
157 if current_pte.huge(true) {
158 return Err(PagingError::mapping_conflict(
159 vaddr,
160 current_pte.paddr(true),
161 ));
162 }
163 if !current_pte.present() {
164 return Err(PagingError::hierarchy_error(
165 "Non-present intermediate entry is not a leaf",
166 ));
167 }
168
169 Frame::from_paddr(current_pte.paddr(true), allocator)
171 } else {
172 let new_frame = Frame::<T, A>::new(allocator)?;
174 let new_frame_paddr = new_frame.paddr;
175
176 let entries = self.as_slice_mut();
178 let pte_ref = &mut entries[index];
179 *pte_ref = T::P::new_table(new_frame_paddr);
180
181 new_frame
182 };
183
184 let entry_base = (vaddr.as_usize() / level_size)
188 .checked_mul(level_size)
189 .ok_or_else(|| {
190 PagingError::address_overflow(
191 "Page-table entry base overflow in map_range_recursive",
192 )
193 })?;
194 let current_entry_end = match entry_base.checked_add(level_size) {
200 Some(end) => end,
201 None if config.end_vaddr.as_usize() > entry_base => config.end_vaddr.as_usize(),
202 None => {
203 return Err(PagingError::address_overflow(
204 "Page-table entry end overflow in map_range_recursive",
205 ));
206 }
207 };
208 let next_level_vaddr =
209 VirtAddr::from_usize(current_entry_end.min(config.end_vaddr.as_usize()));
210 let mut child_frame = child_frame;
211 let child_config = MapRecursiveConfig {
212 start_vaddr: vaddr,
213 start_paddr: paddr,
214 end_vaddr: next_level_vaddr,
215 level: config.level - 1,
216 allow_huge: config.allow_huge,
217 flush: config.flush,
218 pte_template: config.pte_template,
219 };
220 child_frame.map_range_recursive(child_config)?;
221
222 let mapped_size = next_level_vaddr - vaddr;
224 vaddr = next_level_vaddr;
225 paddr = PhysAddr::from_usize(paddr.as_usize().checked_add(mapped_size).ok_or_else(
226 || {
227 PagingError::address_overflow(
228 "Physical address overflow in map_range_recursive",
229 )
230 },
231 )?);
232 }
233
234 Ok(())
235 }
236
237 pub fn unmap_range_recursive(&mut self, config: UnmapRecursiveConfig) -> PagingResult<bool> {
244 let mut vaddr = config.start_vaddr;
245 let mut can_reclaim = true;
246 let allocator = self.allocator.clone();
247
248 while vaddr < config.end_vaddr {
249 let index = Self::virt_to_index(vaddr, config.level);
250 let level_size = Self::level_size(config.level);
251 let remaining_size = config.end_vaddr - vaddr;
252
253 let entries = self.as_slice_mut();
254 let pte_ref = &mut entries[index];
255
256 if pte_ref.unused() {
259 vaddr = checked_advance(vaddr, level_size.min(remaining_size))?;
260 continue;
261 }
262
263 if !pte_ref.present() {
264 pte_ref.clear();
265 if config.flush {
266 T::flush(Some(vaddr));
267 }
268 vaddr = checked_advance(vaddr, level_size.min(remaining_size))?;
269 continue;
270 }
271
272 let is_huge = pte_ref.huge(config.level > 1);
274 if config.level == 1 || is_huge {
275 pte_ref.clear();
277
278 if config.flush {
280 T::flush(Some(vaddr));
281 }
282
283 vaddr = checked_advance(vaddr, if is_huge { level_size } else { T::PAGE_SIZE })?;
284 continue;
285 }
286
287 let child_paddr = pte_ref.paddr(true);
290
291 let entry_base = (vaddr.as_usize() / level_size)
293 .checked_mul(level_size)
294 .ok_or_else(|| {
295 PagingError::address_overflow(
296 "Page-table entry base overflow in unmap_range_recursive",
297 )
298 })?;
299 let current_entry_end = match entry_base.checked_add(level_size) {
300 Some(end) => end,
301 None if config.end_vaddr.as_usize() > entry_base => config.end_vaddr.as_usize(),
302 None => {
303 return Err(PagingError::address_overflow(
304 "Page-table entry end overflow in unmap_range_recursive",
305 ));
306 }
307 };
308 let next_level_vaddr =
309 VirtAddr::from_usize(current_entry_end.min(config.end_vaddr.as_usize()));
310
311 {
312 let mut child_frame: Frame<T, A> =
313 Frame::from_paddr(child_paddr, allocator.clone());
314 let child_config = UnmapRecursiveConfig {
315 start_vaddr: vaddr,
316 end_vaddr: next_level_vaddr,
317 level: config.level - 1,
318 flush: config.flush,
319 retained_root_entries: None,
320 };
321
322 let child_can_reclaim = child_frame.unmap_range_recursive(child_config)?;
323
324 if child_can_reclaim
325 && config
326 .retained_root_entries
327 .is_some_and(|(start, end)| start <= index && index < end)
328 {
329 can_reclaim = false;
330 } else if child_can_reclaim {
331 pte_ref.clear();
334 allocator.dealloc_frame(child_paddr);
335 } else {
336 can_reclaim = false;
337 }
338 }
339
340 vaddr = next_level_vaddr;
341 }
342
343 if can_reclaim {
344 can_reclaim = self.as_slice().iter().all(PageTableEntry::unused);
345 }
346 Ok(can_reclaim)
347 }
348}
349
350fn checked_advance(address: VirtAddr, amount: usize) -> PagingResult<VirtAddr> {
351 address
352 .as_usize()
353 .checked_add(amount)
354 .map(VirtAddr::from_usize)
355 .ok_or_else(|| PagingError::address_overflow("page-table address advance overflow"))
356}