1use core::{
2 marker::PhantomData,
3 ops::{Deref, DerefMut, Range},
4 sync::atomic::{Ordering, fence},
5};
6
7use ax_memory_addr::MemoryAddr;
8
9use crate::{
10 FrameAllocator, PageTableEntry, PagingError, PagingResult, PhysAddr, PteConfigOf, TableMeta,
11 VirtAddr,
12 frame::{DetachedPageTableFrame, Frame, HugeSplitFill},
13 map::{MapConfig, MapRecursiveConfig, UnmapConfig, UnmapRecursiveConfig},
14 walk::{PageTableWalker, WalkConfig},
15};
16
17const TARGETED_FLUSH_LIMIT: usize = 32;
18const MAX_DEFERRED_PAGE_TABLE_LEVELS: usize = 8;
19
20#[must_use = "detached page-table frames must be reclaimed only after TLB confirmation"]
28pub struct DeferredPageTableFrames<A: FrameAllocator> {
29 allocator: A,
30 frames: heapless::Vec<PhysAddr, MAX_DEFERRED_PAGE_TABLE_LEVELS>,
31}
32
33impl<A: FrameAllocator> core::fmt::Debug for DeferredPageTableFrames<A> {
34 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
35 f.debug_struct("DeferredPageTableFrames")
36 .field("frames", &self.frames)
37 .finish_non_exhaustive()
38 }
39}
40
41impl<A: FrameAllocator> DeferredPageTableFrames<A> {
42 fn new(allocator: A) -> Self {
43 Self {
44 allocator,
45 frames: heapless::Vec::new(),
46 }
47 }
48
49 pub(crate) fn push(&mut self, frame: PhysAddr) {
50 self.frames
51 .push(frame)
52 .expect("one leaf cannot detach more page tables than the hierarchy depth");
53 }
54
55 pub fn is_empty(&self) -> bool {
57 self.frames.is_empty()
58 }
59
60 pub fn len(&self) -> usize {
62 self.frames.len()
63 }
64
65 pub unsafe fn reclaim(mut self) {
73 while let Some(frame) = self.frames.pop() {
74 self.allocator.dealloc_frame(frame);
75 }
76 }
77}
78
79impl<A: FrameAllocator> Drop for DeferredPageTableFrames<A> {
80 fn drop(&mut self) {
81 if !self.frames.is_empty() {
82 log::error!(
83 "leaking {} unconfirmed detached page-table frame(s)",
84 self.frames.len()
85 );
86 }
87 }
88}
89
90#[derive(Clone, Copy)]
91enum RegionPageSelection {
92 BasePages,
93 Linear { allow_huge: bool },
94}
95
96struct ReservedTable<T: TableMeta, A: FrameAllocator> {
102 frame: Option<Frame<T, A>>,
103}
104
105struct ReservedMapPath<T: TableMeta, A: FrameAllocator> {
111 root: Option<Frame<T, A>>,
112 root_level: usize,
113}
114
115pub struct PageTableMapPlan<T: TableMeta, A: FrameAllocator> {
121 root_paddr: PhysAddr,
122 parent_paddr: PhysAddr,
123 vaddr: VirtAddr,
124 page_size: usize,
125 attach_level: usize,
126 target_level: usize,
127 allocator: A,
128 _marker: PhantomData<T>,
129}
130
131pub struct PageTableMapDeposit<T: TableMeta, A: FrameAllocator> {
139 plan: PageTableMapPlan<T, A>,
140 paddr: PhysAddr,
141 config: PteConfigOf<T>,
142 path: Option<ReservedMapPath<T, A>>,
143}
144
145pub struct PageTableMapApplyError<T: TableMeta, A: FrameAllocator> {
150 error: PagingError,
151 deposit: PageTableMapDeposit<T, A>,
152}
153
154pub struct PageTablePathDeposit<T: TableMeta, A: FrameAllocator> {
161 plan: PageTableMapPlan<T, A>,
162 path: ReservedMapPath<T, A>,
163}
164
165pub struct PageTablePathApplyError<T: TableMeta, A: FrameAllocator> {
167 error: PagingError,
168 deposit: PageTablePathDeposit<T, A>,
169}
170
171#[derive(Clone, Copy)]
177pub struct PageTableLeafPlan<T: TableMeta> {
178 root_paddr: PhysAddr,
179 parent_paddr: PhysAddr,
180 vaddr: VirtAddr,
181 paddr: PhysAddr,
182 config: PteConfigOf<T>,
183 page_size: usize,
184 level: usize,
185 _marker: PhantomData<T>,
186}
187
188enum PageTableMoveDestination<T: TableMeta, A: FrameAllocator> {
189 Vacant(PageTableMapPlan<T, A>),
190 Occupied(PageTableLeafPlan<T>),
191}
192
193pub struct PageTableMovePlan<T: TableMeta, A: FrameAllocator> {
199 source: PageTableLeafPlan<T>,
200 destination_vaddr: VirtAddr,
201 destination: PageTableMoveDestination<T, A>,
202}
203
204pub struct HugeSplitDeposit<T: TableMeta, A: FrameAllocator> {
212 table: ReservedTable<T, A>,
213 root_paddr: PhysAddr,
214 block_vaddr: VirtAddr,
215 block_paddr: PhysAddr,
216 block_config: PteConfigOf<T>,
217 block_size: usize,
218}
219
220pub struct HugeSplitApplyError<T: TableMeta, A: FrameAllocator> {
224 error: PagingError,
225 deposit: HugeSplitDeposit<T, A>,
226}
227
228impl<T: TableMeta, A: FrameAllocator> HugeSplitApplyError<T, A> {
229 pub const fn error(&self) -> &PagingError {
230 &self.error
231 }
232
233 pub fn into_parts(self) -> (PagingError, HugeSplitDeposit<T, A>) {
234 (self.error, self.deposit)
235 }
236}
237
238impl<T: TableMeta, A: FrameAllocator> core::fmt::Debug for HugeSplitApplyError<T, A> {
239 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
240 f.debug_struct("HugeSplitApplyError")
241 .field("error", &self.error)
242 .field("deposit", &self.deposit)
243 .finish()
244 }
245}
246
247impl<T: TableMeta, A: FrameAllocator> HugeSplitDeposit<T, A> {
248 pub const fn block_vaddr(&self) -> VirtAddr {
249 self.block_vaddr
250 }
251
252 pub const fn block_paddr(&self) -> PhysAddr {
253 self.block_paddr
254 }
255
256 pub const fn block_size(&self) -> usize {
257 self.block_size
258 }
259
260 pub const fn block_config(&self) -> PteConfigOf<T> {
261 self.block_config
262 }
263}
264
265impl<T: TableMeta, A: FrameAllocator> core::fmt::Debug for HugeSplitDeposit<T, A> {
266 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
267 f.debug_struct("HugeSplitDeposit")
268 .field("root_paddr", &self.root_paddr)
269 .field("block_vaddr", &self.block_vaddr)
270 .field("block_paddr", &self.block_paddr)
271 .field("block_size", &self.block_size)
272 .finish_non_exhaustive()
273 }
274}
275
276pub struct InstalledHugeSplit<T: TableMeta> {
283 root_paddr: PhysAddr,
284 block_vaddr: VirtAddr,
285 block_paddr: PhysAddr,
286 block_config: PteConfigOf<T>,
287 block_size: usize,
288 child_table_paddr: PhysAddr,
289}
290
291impl<T: TableMeta> InstalledHugeSplit<T> {
292 pub const fn root_paddr(&self) -> PhysAddr {
293 self.root_paddr
294 }
295
296 pub const fn block_vaddr(&self) -> VirtAddr {
297 self.block_vaddr
298 }
299
300 pub const fn block_paddr(&self) -> PhysAddr {
301 self.block_paddr
302 }
303
304 pub const fn block_config(&self) -> PteConfigOf<T> {
305 self.block_config
306 }
307
308 pub const fn block_size(&self) -> usize {
309 self.block_size
310 }
311
312 pub const fn child_table_paddr(&self) -> PhysAddr {
313 self.child_table_paddr
314 }
315}
316
317impl<T: TableMeta> core::fmt::Debug for InstalledHugeSplit<T> {
318 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
319 f.debug_struct("InstalledHugeSplit")
320 .field("root_paddr", &self.root_paddr)
321 .field("block_vaddr", &self.block_vaddr)
322 .field("block_paddr", &self.block_paddr)
323 .field("block_size", &self.block_size)
324 .field("child_table_paddr", &self.child_table_paddr)
325 .finish_non_exhaustive()
326 }
327}
328
329impl<T: TableMeta, A: FrameAllocator> ReservedTable<T, A> {
330 fn frame(&self) -> Frame<T, A> {
331 match self.frame.as_ref() {
332 Some(frame) => frame.clone(),
333 None => unreachable!("a reserved table is consumed at most once"),
334 }
335 }
336
337 fn disarm(&mut self) {
338 self.frame = None;
339 }
340}
341
342impl<T: TableMeta, A: FrameAllocator> Drop for ReservedTable<T, A> {
343 fn drop(&mut self) {
344 if let Some(frame) = self.frame.take() {
345 frame.allocator.dealloc_frame(frame.paddr);
346 }
347 }
348}
349
350impl<T: TableMeta, A: FrameAllocator> ReservedMapPath<T, A> {
351 fn root(&self) -> Frame<T, A> {
352 self.root
353 .as_ref()
354 .cloned()
355 .expect("a reserved map path is consumed at most once")
356 }
357
358 fn disarm(&mut self) {
359 self.root = None;
360 }
361}
362
363impl<T: TableMeta, A: FrameAllocator> Drop for ReservedMapPath<T, A> {
364 fn drop(&mut self) {
365 if let Some(mut root) = self.root.take() {
366 root.deallocate_recursive(self.root_level);
367 }
368 }
369}
370
371impl<T: TableMeta, A: FrameAllocator> PageTableMapPlan<T, A> {
372 pub const fn vaddr(&self) -> VirtAddr {
373 self.vaddr
374 }
375
376 pub const fn page_size(&self) -> usize {
377 self.page_size
378 }
379
380 pub fn prepare_path(self) -> PagingResult<Option<PageTablePathDeposit<T, A>>> {
386 if self.attach_level == self.target_level {
387 return Ok(None);
388 }
389
390 let root_level = self.attach_level - 1;
391 let root = Frame::<T, A>::new(self.allocator.clone())?;
392 let path = ReservedMapPath {
393 root: Some(root),
394 root_level,
395 };
396 let mut current = path.root();
397 let mut current_level = root_level;
398 while current_level > self.target_level {
399 let child = Frame::<T, A>::new(self.allocator.clone())?;
400 let index = Frame::<T, A>::virt_to_index(self.vaddr, current_level);
401 current.as_slice_mut()[index] = T::P::new_table(child.paddr);
402 current = child;
403 current_level -= 1;
404 }
405 Ok(Some(PageTablePathDeposit { plan: self, path }))
406 }
407
408 pub fn prepare(
414 self,
415 paddr: PhysAddr,
416 config: PteConfigOf<T>,
417 ) -> PagingResult<PageTableMapDeposit<T, A>> {
418 if !paddr.as_usize().is_multiple_of(self.page_size) {
419 return Err(PagingError::alignment_error(
420 "Physical address not aligned to map-deposit leaf size",
421 ));
422 }
423
424 let path = if self.attach_level == self.target_level {
425 None
426 } else {
427 let root_level = self.attach_level - 1;
428 let root = Frame::<T, A>::new(self.allocator.clone())?;
429 let path = ReservedMapPath {
430 root: Some(root),
431 root_level,
432 };
433 let mut current = path.root();
434 let mut current_level = root_level;
435 while current_level > self.target_level {
436 let child = Frame::<T, A>::new(self.allocator.clone())?;
437 let index = Frame::<T, A>::virt_to_index(self.vaddr, current_level);
438 current.as_slice_mut()[index] = T::P::new_table(child.paddr);
439 current = child;
440 current_level -= 1;
441 }
442 let index = Frame::<T, A>::virt_to_index(self.vaddr, self.target_level);
443 current.as_slice_mut()[index] = T::P::new_page(paddr, config, self.target_level > 1);
444 Some(path)
445 };
446
447 Ok(PageTableMapDeposit {
448 plan: self,
449 paddr,
450 config,
451 path,
452 })
453 }
454}
455
456impl<T: TableMeta, A: FrameAllocator> PageTableMapDeposit<T, A> {
457 pub const fn vaddr(&self) -> VirtAddr {
458 self.plan.vaddr
459 }
460
461 pub const fn paddr(&self) -> PhysAddr {
462 self.paddr
463 }
464
465 pub const fn page_size(&self) -> usize {
466 self.plan.page_size
467 }
468}
469
470impl<T: TableMeta, A: FrameAllocator> PageTableMapApplyError<T, A> {
471 pub const fn error(&self) -> &PagingError {
472 &self.error
473 }
474
475 pub fn into_parts(self) -> (PagingError, PageTableMapDeposit<T, A>) {
476 (self.error, self.deposit)
477 }
478}
479
480impl<T: TableMeta, A: FrameAllocator> PageTablePathApplyError<T, A> {
481 pub const fn error(&self) -> &PagingError {
482 &self.error
483 }
484
485 pub fn into_parts(self) -> (PagingError, PageTablePathDeposit<T, A>) {
486 (self.error, self.deposit)
487 }
488}
489
490impl<T: TableMeta> PageTableLeafPlan<T> {
491 pub const fn vaddr(&self) -> VirtAddr {
492 self.vaddr
493 }
494
495 pub const fn paddr(&self) -> PhysAddr {
496 self.paddr
497 }
498
499 pub const fn config(&self) -> PteConfigOf<T> {
500 self.config
501 }
502
503 pub const fn page_size(&self) -> usize {
504 self.page_size
505 }
506}
507
508impl<T: TableMeta, A: FrameAllocator> PageTableMovePlan<T, A> {
509 pub const fn source_vaddr(&self) -> VirtAddr {
510 self.source.vaddr
511 }
512
513 pub const fn destination_vaddr(&self) -> VirtAddr {
514 self.destination_vaddr
515 }
516
517 pub const fn paddr(&self) -> PhysAddr {
518 self.source.paddr
519 }
520
521 pub const fn config(&self) -> PteConfigOf<T> {
522 self.source.config
523 }
524
525 pub const fn page_size(&self) -> usize {
526 self.source.page_size
527 }
528
529 pub const fn destination_is_occupied(&self) -> bool {
530 matches!(&self.destination, PageTableMoveDestination::Occupied(_))
531 }
532
533 pub const fn destination_page_size(&self) -> usize {
534 match &self.destination {
535 PageTableMoveDestination::Vacant(_) => self.source.page_size,
536 PageTableMoveDestination::Occupied(target) => target.page_size,
537 }
538 }
539}
540
541impl<T: TableMeta, A: FrameAllocator> core::fmt::Debug for PageTableMapPlan<T, A> {
542 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
543 f.debug_struct("PageTableMapPlan")
544 .field("root_paddr", &self.root_paddr)
545 .field("parent_paddr", &self.parent_paddr)
546 .field("vaddr", &self.vaddr)
547 .field("page_size", &self.page_size)
548 .field("attach_level", &self.attach_level)
549 .field("target_level", &self.target_level)
550 .finish_non_exhaustive()
551 }
552}
553
554impl<T: TableMeta, A: FrameAllocator> core::fmt::Debug for PageTableMapDeposit<T, A> {
555 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
556 f.debug_struct("PageTableMapDeposit")
557 .field("plan", &self.plan)
558 .field("paddr", &self.paddr)
559 .field("has_reserved_path", &self.path.is_some())
560 .finish_non_exhaustive()
561 }
562}
563
564impl<T: TableMeta, A: FrameAllocator> core::fmt::Debug for PageTableMapApplyError<T, A> {
565 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
566 f.debug_struct("PageTableMapApplyError")
567 .field("error", &self.error)
568 .field("deposit", &self.deposit)
569 .finish()
570 }
571}
572
573impl<T: TableMeta, A: FrameAllocator> core::fmt::Debug for PageTablePathDeposit<T, A> {
574 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
575 f.debug_struct("PageTablePathDeposit")
576 .field("plan", &self.plan)
577 .finish_non_exhaustive()
578 }
579}
580
581impl<T: TableMeta, A: FrameAllocator> core::fmt::Debug for PageTablePathApplyError<T, A> {
582 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
583 f.debug_struct("PageTablePathApplyError")
584 .field("error", &self.error)
585 .field("deposit", &self.deposit)
586 .finish()
587 }
588}
589
590impl<T: TableMeta> core::fmt::Debug for PageTableLeafPlan<T> {
591 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
592 f.debug_struct("PageTableLeafPlan")
593 .field("root_paddr", &self.root_paddr)
594 .field("parent_paddr", &self.parent_paddr)
595 .field("vaddr", &self.vaddr)
596 .field("paddr", &self.paddr)
597 .field("page_size", &self.page_size)
598 .finish_non_exhaustive()
599 }
600}
601
602impl<T: TableMeta, A: FrameAllocator> core::fmt::Debug for PageTableMovePlan<T, A> {
603 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
604 f.debug_struct("PageTableMovePlan")
605 .field("source", &self.source)
606 .field("destination_vaddr", &self.destination_vaddr)
607 .field("destination_occupied", &self.destination_is_occupied())
608 .finish()
609 }
610}
611
612pub struct PageTable<T: TableMeta, A: FrameAllocator> {
613 inner: PageTableRef<T, A>,
614 detached: bool,
617 #[cfg(feature = "copy-from")]
618 borrowed_root_entries: Option<Range<usize>>,
619}
620
621impl<T: TableMeta, A: FrameAllocator> PageTable<T, A> {
622 pub const VALID_BITS: usize = Frame::<T, A>::PT_VALID_BITS;
623
624 pub fn new(allocator: A) -> PagingResult<Self> {
626 let inner = unsafe { PageTableRef::new(allocator) }?;
627 Ok(Self {
628 inner,
629 detached: false,
630 #[cfg(feature = "copy-from")]
631 borrowed_root_entries: None,
632 })
633 }
634
635 pub const fn root_paddr(&self) -> PhysAddr {
636 self.inner.root.paddr
637 }
638
639 pub fn preallocate_shared_root_entries(
651 &mut self,
652 start_vaddr: VirtAddr,
653 size: usize,
654 ) -> PagingResult
655 where
656 PteConfigOf<T>: PartialEq,
657 {
658 let Some(entries) = Self::root_entry_range(start_vaddr, size)? else {
659 return Ok(());
660 };
661 let span = RootEntrySpan {
662 start: entries.start,
663 end: entries.end,
664 };
665 if self
666 .inner
667 .retained_root_entries
668 .is_some_and(|retained| retained != span)
669 {
670 return Err(PagingError::hierarchy_error(
671 "Page table already retains a different root-entry range",
672 ));
673 }
674 self.inner.retained_root_entries = Some(span);
675
676 let root_entry_size = Frame::<T, A>::level_size(Frame::<T, A>::PT_LEVEL);
677 let first_entry_vaddr = start_vaddr.align_down(root_entry_size);
678 for (entry_offset, index) in entries.enumerate() {
679 let current = self.inner.root.as_slice()[index];
680 if current.unused() {
681 let child = Frame::<T, A>::new(self.inner.root.allocator.clone())?;
682 self.inner.root.as_slice_mut()[index] = T::P::new_table(child.paddr);
683 continue;
684 }
685 if !current.present() {
686 return Err(PagingError::hierarchy_error(
687 "Shared root entry is not a child page table",
688 ));
689 }
690 if current.huge(true) {
691 let offset = entry_offset.checked_mul(root_entry_size).ok_or_else(|| {
692 PagingError::address_overflow("shared root entry virtual address")
693 })?;
694 let entry_vaddr = first_entry_vaddr
695 .as_usize()
696 .checked_add(offset)
697 .map(VirtAddr::from_usize)
698 .ok_or_else(|| {
699 PagingError::address_overflow("shared root entry virtual address")
700 })?;
701 self.split_huge_page(entry_vaddr)?;
702 }
703 }
704 Ok(())
705 }
706
707 pub unsafe fn detach(&mut self, mut release: impl FnMut(DetachedPageTableFrame<A>)) {
717 if self.detached {
718 return;
719 }
720 #[cfg(feature = "copy-from")]
721 self.detach_borrowed_root_entries();
722 self.detached = true;
726 self.inner
727 .root
728 .detach_recursive(Frame::<T, A>::PT_LEVEL, &mut release);
729 }
730
731 unsafe fn deallocate_inner(&mut self) {
735 if self.detached {
736 return;
737 }
738 self.inner
741 .root
742 .deallocate_recursive(Frame::<T, A>::PT_LEVEL);
743 self.detached = true;
744 }
745
746 pub unsafe fn deallocate(&mut self) {
754 unsafe {
756 self.deallocate_inner();
757 }
758 }
759
760 pub unsafe fn destroy(mut self) {
768 unsafe {
770 self.deallocate_inner();
771 }
772 }
773
774 pub fn leak(&mut self) {
784 #[cfg(feature = "copy-from")]
785 self.detach_borrowed_root_entries();
786 self.detached = true;
787 }
788
789 pub fn map_linear_pages(
793 &mut self,
794 start_vaddr: VirtAddr,
795 start_paddr: PhysAddr,
796 size: usize,
797 config: PteConfigOf<T>,
798 allow_huge: bool,
799 ) -> PagingResult {
800 if size == 0 || !size.is_multiple_of(T::PAGE_SIZE) {
801 return Err(PagingError::invalid_size(
802 "Linear mapping size must be base-page aligned",
803 ));
804 }
805 start_vaddr.as_usize().checked_add(size).ok_or_else(|| {
806 PagingError::address_overflow("Virtual address overflow in map_linear_pages")
807 })?;
808 start_paddr.as_usize().checked_add(size).ok_or_else(|| {
809 PagingError::address_overflow("Physical address overflow in map_linear_pages")
810 })?;
811 self.map_region_with_selection(
812 start_vaddr,
813 |vaddr| {
814 let offset = vaddr
815 .as_usize()
816 .checked_sub(start_vaddr.as_usize())
817 .ok_or_else(|| {
818 PagingError::address_overflow(
819 "Virtual address precedes linear mapping start",
820 )
821 })?;
822 let paddr = start_paddr.as_usize().checked_add(offset).ok_or_else(|| {
823 PagingError::address_overflow("Physical address overflow in linear mapping")
824 })?;
825 Ok(PhysAddr::from_usize(paddr))
826 },
827 size,
828 config,
829 RegionPageSelection::Linear { allow_huge },
830 )
831 }
832
833 pub fn clone_missing_root_entries_from(
847 &mut self,
848 other: &PageTableRef<T, A>,
849 start_vaddr: VirtAddr,
850 size: usize,
851 ) -> PagingResult {
852 let Some(entries) = Self::root_entry_range(start_vaddr, size)? else {
853 return Ok(());
854 };
855
856 let root_level = Frame::<T, A>::PT_LEVEL;
857 let mut changed = false;
858 for index in entries {
859 changed |= self
860 .inner
861 .root
862 .clone_entry_from(&other.root, index, root_level)?;
863 }
864 if changed {
865 T::flush(None);
866 }
867 Ok(())
868 }
869
870 #[cfg(feature = "copy-from")]
881 pub unsafe fn share_root_entries_from(
882 &mut self,
883 other: &Self,
884 start_vaddr: VirtAddr,
885 size: usize,
886 ) -> PagingResult {
887 if size == 0 {
888 return Ok(());
889 }
890 if self.borrowed_root_entries.is_some() {
891 return Err(PagingError::hierarchy_error(
892 "Page table already contains shared root entries",
893 ));
894 }
895
896 let Some(entries) = Self::root_entry_range(start_vaddr, size)? else {
897 return Ok(());
898 };
899 let root_level = Frame::<T, A>::PT_LEVEL;
900
901 for index in entries.clone() {
902 self.inner.root.dealloc_entry_recursive(index, root_level);
903 self.inner.root.as_slice_mut()[index] = other.inner.root.as_slice()[index];
904 }
905 self.borrowed_root_entries = Some(entries);
906 T::flush(None);
907 Ok(())
908 }
909
910 fn root_entry_range(start_vaddr: VirtAddr, size: usize) -> PagingResult<Option<Range<usize>>> {
911 if size == 0 {
912 return Ok(None);
913 }
914 let end_vaddr = start_vaddr
915 .as_usize()
916 .checked_add(size)
917 .ok_or_else(|| PagingError::address_overflow("root_entry_range"))?;
918 let root_level = Frame::<T, A>::PT_LEVEL;
919 let start_index = Frame::<T, A>::virt_to_index(start_vaddr, root_level);
920 let end_index =
921 Frame::<T, A>::virt_to_index(VirtAddr::from_usize(end_vaddr - 1), root_level) + 1;
922 if start_index >= end_index {
923 return Err(PagingError::invalid_range(
924 "Range must be contiguous in the root page table",
925 ));
926 }
927 Ok(Some(start_index..end_index))
928 }
929
930 #[cfg(feature = "copy-from")]
931 fn detach_borrowed_root_entries(&mut self) {
932 let Some(entries) = self.borrowed_root_entries.take() else {
933 return;
934 };
935 for index in entries {
936 self.inner.root.as_slice_mut()[index].clear();
937 }
938 }
939}
940
941impl<T: TableMeta, A: FrameAllocator> Drop for PageTable<T, A> {
942 fn drop(&mut self) {
943 if self.detached {
944 return;
945 }
946 #[cfg(feature = "copy-from")]
947 self.detach_borrowed_root_entries();
948 unsafe {
949 self.deallocate_inner();
951 }
952 }
953}
954
955impl<T: TableMeta, A: FrameAllocator> Deref for PageTable<T, A> {
956 type Target = PageTableRef<T, A>;
957
958 fn deref(&self) -> &Self::Target {
959 &self.inner
960 }
961}
962
963impl<T: TableMeta, A: FrameAllocator> DerefMut for PageTable<T, A> {
964 fn deref_mut(&mut self) -> &mut Self::Target {
965 &mut self.inner
966 }
967}
968
969pub struct PageTableRef<T: TableMeta, A: FrameAllocator> {
970 pub(crate) root: Frame<T, A>,
971 retained_root_entries: Option<RootEntrySpan>,
976}
977
978#[derive(Clone, Copy, Debug, Eq, PartialEq)]
979struct RootEntrySpan {
980 start: usize,
981 end: usize,
982}
983
984impl<T: TableMeta, A: FrameAllocator> core::fmt::Debug for PageTableRef<T, A>
985where
986 T::P: core::fmt::Debug,
987{
988 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
989 f.debug_struct("PageTable")
990 .field(
991 "root_paddr",
992 &format_args!("{:#x}", self.root.paddr.as_usize()),
993 )
994 .field("table_levels", &T::LEVEL_BITS.len())
995 .field("max_block_level", &T::MAX_BLOCK_LEVEL)
996 .field("page_size", &format_args!("{:#x}", T::PAGE_SIZE))
997 .finish()
998 }
999}
1000
1001impl<T: TableMeta, A: FrameAllocator> PageTableRef<T, A> {
1002 pub(crate) unsafe fn new(allocator: A) -> PagingResult<Self> {
1008 let root = Frame::new_root(allocator)?;
1009 Ok(Self {
1010 root,
1011 retained_root_entries: None,
1012 })
1013 }
1014
1015 pub unsafe fn from_paddr(paddr: PhysAddr, allocator: A) -> Self {
1030 let root = Frame::from_root_paddr(paddr, allocator);
1031 Self {
1032 root,
1033 retained_root_entries: None,
1034 }
1035 }
1036
1037 pub fn map_page(
1039 &mut self,
1040 vaddr: VirtAddr,
1041 paddr: PhysAddr,
1042 page_size: usize,
1043 config: PteConfigOf<T>,
1044 ) -> PagingResult {
1045 let Some(level) = Frame::<T, A>::level_for_page_size(page_size) else {
1046 return Err(PagingError::invalid_size(
1047 "Page size is not represented by the page-table levels",
1048 ));
1049 };
1050 if level > 1 && level > T::MAX_BLOCK_LEVEL {
1051 return Err(PagingError::invalid_size(
1052 "Page size exceeds the architecture's block-mapping level",
1053 ));
1054 }
1055 self.map(&MapConfig {
1056 vaddr: vaddr.align_down(page_size),
1057 paddr: paddr.align_down(page_size),
1058 size: page_size,
1059 pte: config,
1060 allow_huge: level > 1,
1061 flush: true,
1062 })
1063 }
1064
1065 pub fn map_region(
1076 &mut self,
1077 start_vaddr: VirtAddr,
1078 get_paddr: impl Fn(VirtAddr) -> PhysAddr,
1079 size: usize,
1080 config: PteConfigOf<T>,
1081 ) -> PagingResult {
1082 self.map_region_checked(start_vaddr, |vaddr| Ok(get_paddr(vaddr)), size, config)
1083 }
1084
1085 pub fn map_region_checked(
1094 &mut self,
1095 start_vaddr: VirtAddr,
1096 get_paddr: impl FnMut(VirtAddr) -> PagingResult<PhysAddr>,
1097 size: usize,
1098 config: PteConfigOf<T>,
1099 ) -> PagingResult {
1100 self.map_region_with_selection(
1101 start_vaddr,
1102 get_paddr,
1103 size,
1104 config,
1105 RegionPageSelection::BasePages,
1106 )
1107 }
1108
1109 fn map_region_with_selection(
1110 &mut self,
1111 start_vaddr: VirtAddr,
1112 mut get_paddr: impl FnMut(VirtAddr) -> PagingResult<PhysAddr>,
1113 size: usize,
1114 config: PteConfigOf<T>,
1115 page_selection: RegionPageSelection,
1116 ) -> PagingResult {
1117 if size == 0 {
1118 return Err(PagingError::invalid_size("Region size cannot be zero"));
1119 }
1120 if !start_vaddr.as_usize().is_multiple_of(T::PAGE_SIZE)
1121 || !size.is_multiple_of(T::PAGE_SIZE)
1122 {
1123 return Err(PagingError::alignment_error(
1124 "Region start and size must be base-page aligned",
1125 ));
1126 }
1127 start_vaddr.as_usize().checked_add(size).ok_or_else(|| {
1128 PagingError::address_overflow("Virtual address overflow in map_region")
1129 })?;
1130 self.validate_address_width(start_vaddr, size, "map_region")?;
1131
1132 let mut offset = 0;
1133 let mut flush_addrs = heapless::Vec::<VirtAddr, TARGETED_FLUSH_LIMIT>::new();
1134 let mut full_flush = false;
1135 let result = loop {
1136 if offset >= size {
1137 break Ok(());
1138 }
1139 let vaddr =
1140 VirtAddr::from_usize(start_vaddr.as_usize().checked_add(offset).ok_or_else(
1141 || PagingError::address_overflow("Virtual address overflow in map_region"),
1142 )?);
1143 let paddr = match get_paddr(vaddr) {
1144 Ok(paddr) => paddr,
1145 Err(error) => {
1146 let rollback_result = if offset == 0 {
1147 Ok(())
1148 } else {
1149 self.unmap_with_config(&UnmapConfig {
1150 start_vaddr,
1151 size: offset,
1152 flush: false,
1153 })
1154 };
1155 break match rollback_result {
1156 Ok(()) => Err(error),
1157 Err(rollback_err) => Err(rollback_err),
1158 };
1159 }
1160 };
1161 if !paddr.as_usize().is_multiple_of(T::PAGE_SIZE) {
1162 let rollback_result = if offset == 0 {
1163 Ok(())
1164 } else {
1165 self.unmap_with_config(&UnmapConfig {
1166 start_vaddr,
1167 size: offset,
1168 flush: false,
1169 })
1170 };
1171 break match rollback_result {
1172 Ok(()) => Err(PagingError::alignment_error(
1173 "Physical resolver returned an unaligned base page",
1174 )),
1175 Err(rollback_err) => Err(rollback_err),
1176 };
1177 }
1178 let page_size = match page_selection {
1179 RegionPageSelection::BasePages => T::PAGE_SIZE,
1180 RegionPageSelection::Linear { allow_huge } => {
1181 largest_page_size::<T, A>(vaddr, paddr, size - offset, allow_huge)
1182 }
1183 };
1184 if let Err(err) = self.map(&MapConfig {
1185 vaddr: vaddr.align_down(page_size),
1186 paddr: paddr.align_down(page_size),
1187 size: page_size,
1188 pte: config,
1189 allow_huge: page_size > T::PAGE_SIZE,
1190 flush: false,
1191 }) {
1192 let rollback_result = if offset == 0 {
1193 Ok(())
1194 } else {
1195 self.unmap_with_config(&UnmapConfig {
1196 start_vaddr,
1197 size: offset,
1198 flush: false,
1199 })
1200 };
1201 break match rollback_result {
1202 Ok(()) => Err(err),
1203 Err(rollback_err) => Err(rollback_err),
1204 };
1205 }
1206 if !full_flush && flush_addrs.push(vaddr).is_err() {
1207 full_flush = true;
1208 flush_addrs.clear();
1209 }
1210 offset = match offset.checked_add(page_size) {
1211 Some(offset) => offset,
1212 None => {
1213 let _ = self.unmap_with_config(&UnmapConfig {
1214 start_vaddr,
1215 size: offset,
1216 flush: false,
1217 });
1218 break Err(PagingError::address_overflow(
1219 "Mapping offset overflow in map_region",
1220 ));
1221 }
1222 };
1223 };
1224
1225 if full_flush {
1226 T::flush(None);
1227 } else {
1228 for vaddr in flush_addrs {
1229 T::flush(Some(vaddr));
1230 }
1231 }
1232 result
1233 }
1234
1235 pub fn unmap_page(
1237 &mut self,
1238 vaddr: VirtAddr,
1239 ) -> PagingResult<(PhysAddr, PteConfigOf<T>, usize)> {
1240 let (pte, level) = self
1241 .root
1242 .find_occupied_leaf(vaddr, Frame::<T, A>::PT_LEVEL)?;
1243 let page_size = Frame::<T, A>::level_size(level);
1244 let is_dir = level > 1;
1245 let paddr = pte.paddr(is_dir);
1246 let config = pte.config(is_dir);
1247 self.unmap_with_config(&UnmapConfig {
1248 start_vaddr: vaddr.align_down(page_size),
1249 size: page_size,
1250 flush: true,
1251 })?;
1252 Ok((paddr, config, page_size))
1253 }
1254
1255 pub fn unmap_page_deferred(
1262 &mut self,
1263 vaddr: VirtAddr,
1264 ) -> PagingResult<(PhysAddr, PteConfigOf<T>, usize, DeferredPageTableFrames<A>)> {
1265 if Frame::<T, A>::PT_LEVEL > MAX_DEFERRED_PAGE_TABLE_LEVELS {
1266 return Err(PagingError::hierarchy_error(
1267 "Page-table depth exceeds deferred reclaim capacity",
1268 ));
1269 }
1270 let mut deferred = DeferredPageTableFrames::new(self.root.allocator.clone());
1271 let (pte, level) =
1272 self.root
1273 .take_occupied_leaf_deferred(vaddr, Frame::<T, A>::PT_LEVEL, &mut deferred)?;
1274 let page_size = Frame::<T, A>::level_size(level);
1275 let is_dir = level > 1;
1276 let paddr = pte.paddr(is_dir);
1277 let config = pte.config(is_dir);
1278 Ok((paddr, config, page_size, deferred))
1279 }
1280
1281 pub fn peek_huge_block(&self, vaddr: VirtAddr) -> Option<(PhysAddr, PteConfigOf<T>, usize)> {
1285 let (pte, level) = self
1286 .root
1287 .find_occupied_leaf(vaddr, Frame::<T, A>::PT_LEVEL)
1288 .ok()?;
1289 let is_dir = level > 1;
1290 pte.huge(is_dir).then(|| {
1291 (
1292 pte.paddr(is_dir),
1293 pte.config(is_dir),
1294 Frame::<T, A>::level_size(level),
1295 )
1296 })
1297 }
1298
1299 pub fn plan_map_page(
1306 &self,
1307 vaddr: VirtAddr,
1308 page_size: usize,
1309 ) -> PagingResult<PageTableMapPlan<T, A>> {
1310 let Some(target_level) = Frame::<T, A>::level_for_page_size(page_size) else {
1311 return Err(PagingError::invalid_size(
1312 "Page size is not represented by the page-table levels",
1313 ));
1314 };
1315 if target_level > 1 && target_level > T::MAX_BLOCK_LEVEL {
1316 return Err(PagingError::invalid_size(
1317 "Page size exceeds the architecture's block-mapping level",
1318 ));
1319 }
1320 if !vaddr.as_usize().is_multiple_of(page_size) {
1321 return Err(PagingError::alignment_error(
1322 "Virtual address not aligned to map-deposit leaf size",
1323 ));
1324 }
1325 self.validate_address_width(vaddr, page_size, "plan_map_page")?;
1326
1327 let mut parent = self.root.clone();
1328 let mut level = Frame::<T, A>::PT_LEVEL;
1329 loop {
1330 let index = Frame::<T, A>::virt_to_index(vaddr, level);
1331 let entry = parent.as_slice()[index];
1332 if level == target_level || entry.unused() {
1333 if level == target_level && !entry.unused() {
1334 return Err(PagingError::mapping_conflict(vaddr, entry.paddr(level > 1)));
1335 }
1336 return Ok(PageTableMapPlan {
1337 root_paddr: self.root_paddr(),
1338 parent_paddr: parent.paddr,
1339 vaddr,
1340 page_size,
1341 attach_level: level,
1342 target_level,
1343 allocator: self.root.allocator.clone(),
1344 _marker: PhantomData,
1345 });
1346 }
1347 if entry.huge(true) {
1348 return Err(PagingError::mapping_conflict(vaddr, entry.paddr(true)));
1349 }
1350 if !entry.present() {
1351 return Err(PagingError::hierarchy_error(
1352 "Non-present intermediate entry is not a leaf",
1353 ));
1354 }
1355 parent = Frame::from_paddr(entry.paddr(true), self.root.allocator.clone());
1356 level -= 1;
1357 }
1358 }
1359
1360 pub fn try_map_page_with(
1368 &mut self,
1369 mut deposit: PageTableMapDeposit<T, A>,
1370 ) -> Result<(), PageTableMapApplyError<T, A>> {
1371 if self.root_paddr() != deposit.plan.root_paddr {
1372 return Err(PageTableMapApplyError {
1373 error: PagingError::stale_map_deposit(deposit.plan.vaddr),
1374 deposit,
1375 });
1376 }
1377
1378 let mut parent = self.root.clone();
1379 let mut level = Frame::<T, A>::PT_LEVEL;
1380 while level > deposit.plan.attach_level {
1381 let index = Frame::<T, A>::virt_to_index(deposit.plan.vaddr, level);
1382 let entry = parent.as_slice()[index];
1383 if entry.unused() || entry.huge(true) || !entry.present() {
1384 return Err(PageTableMapApplyError {
1385 error: PagingError::stale_map_deposit(deposit.plan.vaddr),
1386 deposit,
1387 });
1388 }
1389 parent = Frame::from_paddr(entry.paddr(true), self.root.allocator.clone());
1390 level -= 1;
1391 }
1392 if parent.paddr != deposit.plan.parent_paddr {
1393 return Err(PageTableMapApplyError {
1394 error: PagingError::stale_map_deposit(deposit.plan.vaddr),
1395 deposit,
1396 });
1397 }
1398 let index = Frame::<T, A>::virt_to_index(deposit.plan.vaddr, level);
1399 if !parent.as_slice()[index].unused() {
1400 return Err(PageTableMapApplyError {
1401 error: PagingError::stale_map_deposit(deposit.plan.vaddr),
1402 deposit,
1403 });
1404 }
1405
1406 if let Some(path) = deposit.path.as_mut() {
1407 let path_root = path.root();
1408 fence(Ordering::Release);
1412 parent.as_slice_mut()[index] = T::P::new_table(path_root.paddr);
1413 path.disarm();
1414 } else {
1415 debug_assert_eq!(deposit.plan.attach_level, deposit.plan.target_level);
1416 parent.as_slice_mut()[index] =
1417 T::P::new_page(deposit.paddr, deposit.config, deposit.plan.target_level > 1);
1418 }
1419 Ok(())
1420 }
1421
1422 pub fn try_install_map_path(
1428 &mut self,
1429 mut deposit: PageTablePathDeposit<T, A>,
1430 ) -> Result<(), PageTablePathApplyError<T, A>> {
1431 let stale = || PagingError::stale_map_deposit(deposit.plan.vaddr);
1432 if self.root_paddr() != deposit.plan.root_paddr
1433 || deposit.plan.attach_level <= deposit.plan.target_level
1434 {
1435 return Err(PageTablePathApplyError {
1436 error: stale(),
1437 deposit,
1438 });
1439 }
1440
1441 let mut parent = self.root.clone();
1442 let mut level = Frame::<T, A>::PT_LEVEL;
1443 while level > deposit.plan.attach_level {
1444 let index = Frame::<T, A>::virt_to_index(deposit.plan.vaddr, level);
1445 let entry = parent.as_slice()[index];
1446 if entry.unused() || entry.huge(true) || !entry.present() {
1447 return Err(PageTablePathApplyError {
1448 error: stale(),
1449 deposit,
1450 });
1451 }
1452 parent = Frame::from_paddr(entry.paddr(true), self.root.allocator.clone());
1453 level -= 1;
1454 }
1455 if parent.paddr != deposit.plan.parent_paddr {
1456 return Err(PageTablePathApplyError {
1457 error: stale(),
1458 deposit,
1459 });
1460 }
1461 let index = Frame::<T, A>::virt_to_index(deposit.plan.vaddr, level);
1462 if !parent.as_slice()[index].unused() {
1463 return Err(PageTablePathApplyError {
1464 error: stale(),
1465 deposit,
1466 });
1467 }
1468
1469 let path_root = deposit.path.root();
1470 fence(Ordering::Release);
1471 parent.as_slice_mut()[index] = T::P::new_table(path_root.paddr);
1472 deposit.path.disarm();
1473 Ok(())
1474 }
1475
1476 pub fn plan_unmap_page(&self, vaddr: VirtAddr) -> PagingResult<PageTableLeafPlan<T>> {
1478 if T::STRICT_ADDRESS_WIDTH && !Self::is_addr_in_width(vaddr.as_usize()) {
1479 return Err(PagingError::address_overflow("plan_unmap_page"));
1480 }
1481 let (pte, level) = self
1482 .root
1483 .find_occupied_leaf(vaddr, Frame::<T, A>::PT_LEVEL)?;
1484 let page_size = Frame::<T, A>::level_size(level);
1485 let leaf_vaddr = vaddr.align_down(page_size);
1486 let mut parent = self.root.clone();
1487 let mut current_level = Frame::<T, A>::PT_LEVEL;
1488 while current_level > level {
1489 let index = Frame::<T, A>::virt_to_index(leaf_vaddr, current_level);
1490 let entry = parent.as_slice()[index];
1491 if entry.unused() || entry.huge(true) || !entry.present() {
1492 return Err(PagingError::hierarchy_error(
1493 "Occupied leaf path changed while it was captured",
1494 ));
1495 }
1496 parent = Frame::from_paddr(entry.paddr(true), self.root.allocator.clone());
1497 current_level -= 1;
1498 }
1499 Ok(PageTableLeafPlan {
1500 root_paddr: self.root_paddr(),
1501 parent_paddr: parent.paddr,
1502 vaddr: leaf_vaddr,
1503 paddr: pte.paddr(level > 1),
1504 config: pte.config(level > 1),
1505 page_size,
1506 level,
1507 _marker: PhantomData,
1508 })
1509 }
1510
1511 pub fn plan_move_page(
1514 &self,
1515 source_vaddr: VirtAddr,
1516 destination_vaddr: VirtAddr,
1517 ) -> PagingResult<PageTableMovePlan<T, A>> {
1518 let source = self.plan_unmap_page(source_vaddr)?;
1519 if source.vaddr != source_vaddr
1520 || !destination_vaddr
1521 .as_usize()
1522 .is_multiple_of(source.page_size)
1523 {
1524 return Err(PagingError::alignment_error(
1525 "Page move endpoints must align to the source leaf",
1526 ));
1527 }
1528 self.validate_address_width(destination_vaddr, source.page_size, "plan_move_page")?;
1529
1530 let destination = match self.plan_map_page(destination_vaddr, source.page_size) {
1531 Ok(plan) if plan.attach_level == plan.target_level => {
1532 PageTableMoveDestination::Vacant(plan)
1533 }
1534 Ok(_) => {
1535 return Err(PagingError::hierarchy_error(
1536 "Destination page-table path must be prepared before a move",
1537 ));
1538 }
1539 Err(PagingError::MappingConflict { .. }) => {
1540 PageTableMoveDestination::Occupied(self.plan_unmap_page(destination_vaddr)?)
1541 }
1542 Err(error) => return Err(error),
1543 };
1544 Ok(PageTableMovePlan {
1545 source,
1546 destination_vaddr,
1547 destination,
1548 })
1549 }
1550
1551 fn validate_leaf_plan(&self, plan: &PageTableLeafPlan<T>) -> PagingResult<()>
1552 where
1553 PteConfigOf<T>: PartialEq,
1554 {
1555 let stale = || PagingError::stale_map_deposit(plan.vaddr);
1556 if self.root_paddr() != plan.root_paddr {
1557 return Err(stale());
1558 }
1559 let mut parent = self.root.clone();
1560 let mut level = Frame::<T, A>::PT_LEVEL;
1561 while level > plan.level {
1562 let index = Frame::<T, A>::virt_to_index(plan.vaddr, level);
1563 let entry = parent.as_slice()[index];
1564 if entry.unused() || entry.huge(true) || !entry.present() {
1565 return Err(stale());
1566 }
1567 parent = Frame::from_paddr(entry.paddr(true), self.root.allocator.clone());
1568 level -= 1;
1569 }
1570 if parent.paddr != plan.parent_paddr {
1571 return Err(stale());
1572 }
1573 let entry = parent.as_slice()[Frame::<T, A>::virt_to_index(plan.vaddr, level)];
1574 if entry.unused()
1575 || (level > 1 && !entry.huge(true))
1576 || entry.paddr(level > 1) != plan.paddr
1577 || entry.config(level > 1) != plan.config
1578 || Frame::<T, A>::level_size(level) != plan.page_size
1579 {
1580 return Err(stale());
1581 }
1582 Ok(())
1583 }
1584
1585 fn validate_vacant_move_target(&self, plan: &PageTableMapPlan<T, A>) -> PagingResult<()> {
1586 let stale = || PagingError::stale_map_deposit(plan.vaddr);
1587 if self.root_paddr() != plan.root_paddr || plan.attach_level != plan.target_level {
1588 return Err(stale());
1589 }
1590 let mut parent = self.root.clone();
1591 let mut level = Frame::<T, A>::PT_LEVEL;
1592 while level > plan.target_level {
1593 let index = Frame::<T, A>::virt_to_index(plan.vaddr, level);
1594 let entry = parent.as_slice()[index];
1595 if entry.unused() || entry.huge(true) || !entry.present() {
1596 return Err(stale());
1597 }
1598 parent = Frame::from_paddr(entry.paddr(true), self.root.allocator.clone());
1599 level -= 1;
1600 }
1601 if parent.paddr != plan.parent_paddr
1602 || !parent.as_slice()[Frame::<T, A>::virt_to_index(plan.vaddr, level)].unused()
1603 {
1604 return Err(stale());
1605 }
1606 Ok(())
1607 }
1608
1609 pub fn try_unmap_page_with(
1616 &mut self,
1617 plan: PageTableLeafPlan<T>,
1618 ) -> PagingResult<(PhysAddr, PteConfigOf<T>, usize)>
1619 where
1620 PteConfigOf<T>: PartialEq,
1621 {
1622 self.validate_leaf_plan(&plan)?;
1623 let mut parent: Frame<T, A> =
1624 Frame::from_paddr(plan.parent_paddr, self.root.allocator.clone());
1625 parent.as_slice_mut()[Frame::<T, A>::virt_to_index(plan.vaddr, plan.level)].clear();
1626 Ok((plan.paddr, plan.config, plan.page_size))
1627 }
1628
1629 pub fn try_move_pages_with(&mut self, plans: &[PageTableMovePlan<T, A>]) -> PagingResult<usize>
1637 where
1638 PteConfigOf<T>: PartialEq,
1639 {
1640 let mut source_bounds = None::<(VirtAddr, VirtAddr)>;
1641 let mut destination_bounds = None::<(VirtAddr, VirtAddr)>;
1642 let mut previous_source_end = None::<VirtAddr>;
1643 let mut previous_destination_end = None::<VirtAddr>;
1644
1645 for plan in plans {
1646 let source_end = plan
1647 .source
1648 .vaddr
1649 .checked_add(plan.source.page_size)
1650 .ok_or_else(|| PagingError::address_overflow("page move source range"))?;
1651 let destination_end = plan
1652 .destination_vaddr
1653 .checked_add(plan.source.page_size)
1654 .ok_or_else(|| PagingError::address_overflow("page move destination range"))?;
1655 if previous_source_end.is_some_and(|end| end > plan.source.vaddr)
1656 || previous_destination_end.is_some_and(|end| end > plan.destination_vaddr)
1657 {
1658 return Err(PagingError::invalid_range(
1659 "Page move plans must be sorted and non-overlapping",
1660 ));
1661 }
1662 previous_source_end = Some(source_end);
1663 previous_destination_end = Some(destination_end);
1664 source_bounds = Some(
1665 source_bounds.map_or((plan.source.vaddr, source_end), |(start, _)| {
1666 (start, source_end)
1667 }),
1668 );
1669 destination_bounds = Some(
1670 destination_bounds
1671 .map_or((plan.destination_vaddr, destination_end), |(start, _)| {
1672 (start, destination_end)
1673 }),
1674 );
1675
1676 self.validate_leaf_plan(&plan.source)?;
1677 match &plan.destination {
1678 PageTableMoveDestination::Vacant(target) => {
1679 self.validate_vacant_move_target(target)?;
1680 }
1681 PageTableMoveDestination::Occupied(target) => {
1682 self.validate_leaf_plan(target)?;
1683 }
1684 }
1685 }
1686 if let (Some((source_start, source_end)), Some((destination_start, destination_end))) =
1687 (source_bounds, destination_bounds)
1688 && source_start < destination_end
1689 && destination_start < source_end
1690 {
1691 return Err(PagingError::invalid_range(
1692 "Page move source and destination ranges overlap",
1693 ));
1694 }
1695
1696 let allocator = self.root.allocator.clone();
1697 for plan in plans {
1698 let mut source_parent: Frame<T, A> =
1699 Frame::from_paddr(plan.source.parent_paddr, allocator.clone());
1700 source_parent.as_slice_mut()
1701 [Frame::<T, A>::virt_to_index(plan.source.vaddr, plan.source.level)]
1702 .clear();
1703 if let PageTableMoveDestination::Vacant(target) = &plan.destination {
1704 let mut destination_parent: Frame<T, A> =
1705 Frame::from_paddr(target.parent_paddr, allocator.clone());
1706 fence(Ordering::Release);
1707 destination_parent.as_slice_mut()
1708 [Frame::<T, A>::virt_to_index(target.vaddr, target.target_level)] =
1709 T::P::new_page(
1710 plan.source.paddr,
1711 plan.source.config,
1712 target.target_level > 1,
1713 );
1714 }
1715 }
1716 Ok(plans.len())
1717 }
1718
1719 pub fn prepare_huge_split(&self, vaddr: VirtAddr) -> PagingResult<HugeSplitDeposit<T, A>> {
1727 let (block_paddr, block_config, block_size) = self
1728 .peek_huge_block(vaddr)
1729 .ok_or_else(PagingError::not_mapped)?;
1730 let block_vaddr = vaddr.align_down(block_size);
1731 let table = ReservedTable {
1732 frame: Some(Frame::<T, A>::new(self.root.allocator.clone())?),
1733 };
1734 Ok(HugeSplitDeposit {
1735 table,
1736 root_paddr: self.root_paddr(),
1737 block_vaddr,
1738 block_paddr,
1739 block_config,
1740 block_size,
1741 })
1742 }
1743
1744 fn validate_huge_split_deposit(&self, deposit: &HugeSplitDeposit<T, A>) -> PagingResult
1745 where
1746 PteConfigOf<T>: PartialEq,
1747 {
1748 let current = self.peek_huge_block(deposit.block_vaddr);
1749 if self.root_paddr() != deposit.root_paddr
1750 || !current.is_some_and(|(paddr, config, size)| {
1751 paddr == deposit.block_paddr
1752 && config == deposit.block_config
1753 && size == deposit.block_size
1754 })
1755 {
1756 return Err(PagingError::stale_huge_split(deposit.block_vaddr));
1757 }
1758 Ok(())
1759 }
1760
1761 fn try_apply_huge_split_deposit(
1762 &mut self,
1763 mut deposit: HugeSplitDeposit<T, A>,
1764 fill: HugeSplitFill,
1765 ) -> Result<InstalledHugeSplit<T>, HugeSplitApplyError<T, A>>
1766 where
1767 PteConfigOf<T>: PartialEq,
1768 {
1769 if let Err(error) = self.validate_huge_split_deposit(&deposit) {
1770 return Err(HugeSplitApplyError { error, deposit });
1771 }
1772 let child_table_paddr = deposit.table.frame().paddr;
1773 let frame = deposit.table.frame();
1774 let (block_paddr, block_config, block_size) = match self.root.split_huge_page_recursive(
1775 deposit.block_vaddr,
1776 Frame::<T, A>::PT_LEVEL,
1777 frame,
1778 fill,
1779 ) {
1780 Ok(installed) => installed,
1781 Err(error) => return Err(HugeSplitApplyError { error, deposit }),
1782 };
1783 deposit.table.disarm();
1787 Ok(InstalledHugeSplit {
1788 root_paddr: deposit.root_paddr,
1789 block_vaddr: deposit.block_vaddr,
1790 block_paddr,
1791 block_config,
1792 block_size,
1793 child_table_paddr,
1794 })
1795 }
1796
1797 pub fn split_huge_page_with(
1800 &mut self,
1801 deposit: HugeSplitDeposit<T, A>,
1802 ) -> PagingResult<InstalledHugeSplit<T>>
1803 where
1804 PteConfigOf<T>: PartialEq,
1805 {
1806 self.try_split_huge_page_with(deposit)
1807 .map_err(|failure| failure.into_parts().0)
1808 }
1809
1810 pub fn try_split_huge_page_with(
1814 &mut self,
1815 deposit: HugeSplitDeposit<T, A>,
1816 ) -> Result<InstalledHugeSplit<T>, HugeSplitApplyError<T, A>>
1817 where
1818 PteConfigOf<T>: PartialEq,
1819 {
1820 self.try_apply_huge_split_deposit(deposit, HugeSplitFill::Inherit)
1821 }
1822
1823 pub fn split_huge_block_to_empty_table(
1827 &mut self,
1828 deposit: HugeSplitDeposit<T, A>,
1829 ) -> PagingResult<InstalledHugeSplit<T>>
1830 where
1831 PteConfigOf<T>: PartialEq,
1832 {
1833 self.try_apply_huge_split_deposit(deposit, HugeSplitFill::Empty)
1834 .map_err(|failure| failure.into_parts().0)
1835 }
1836
1837 pub fn restore_huge_split(
1845 &mut self,
1846 installed: InstalledHugeSplit<T>,
1847 ) -> PagingResult<HugeSplitDeposit<T, A>> {
1848 if self.root_paddr() != installed.root_paddr {
1849 return Err(PagingError::stale_huge_split(installed.block_vaddr));
1850 }
1851 let frame = self.root.restore_huge_page_recursive(
1852 installed.block_vaddr,
1853 installed.block_paddr,
1854 installed.block_config,
1855 installed.block_size,
1856 installed.child_table_paddr,
1857 Frame::<T, A>::PT_LEVEL,
1858 )?;
1859 Ok(HugeSplitDeposit {
1860 table: ReservedTable { frame: Some(frame) },
1861 root_paddr: installed.root_paddr,
1862 block_vaddr: installed.block_vaddr,
1863 block_paddr: installed.block_paddr,
1864 block_config: installed.block_config,
1865 block_size: installed.block_size,
1866 })
1867 }
1868
1869 pub fn split_huge_page(&mut self, vaddr: VirtAddr) -> PagingResult<usize>
1873 where
1874 PteConfigOf<T>: PartialEq,
1875 {
1876 let deposit = self.prepare_huge_split(vaddr)?;
1877 self.split_huge_page_with(deposit)
1878 .map(|installed| installed.block_size())
1879 }
1880
1881 pub fn protect_page(&mut self, vaddr: VirtAddr, config: PteConfigOf<T>) -> PagingResult<usize> {
1883 let page_size = self
1884 .root
1885 .protect_recursive(vaddr, config, Frame::<T, A>::PT_LEVEL)?;
1886 T::flush(Some(vaddr));
1887 Ok(page_size)
1888 }
1889
1890 pub fn protect_region(
1892 &mut self,
1893 start_vaddr: VirtAddr,
1894 size: usize,
1895 config: PteConfigOf<T>,
1896 ) -> PagingResult {
1897 let end = start_vaddr
1898 .as_usize()
1899 .checked_add(size)
1900 .ok_or_else(|| PagingError::address_overflow("protect_region"))?;
1901 if size == 0 {
1902 return Ok(());
1903 }
1904
1905 self.root
1908 .split_leaf_for_boundary(start_vaddr, start_vaddr, Frame::<T, A>::PT_LEVEL)?;
1909 self.root.split_leaf_for_boundary(
1910 VirtAddr::from_usize(end - 1),
1911 VirtAddr::from_usize(end),
1912 Frame::<T, A>::PT_LEVEL,
1913 )?;
1914
1915 let mut vaddr = start_vaddr;
1916 while vaddr.as_usize() < end {
1917 match self.protect_page(vaddr, config) {
1918 Ok(page_size) => {
1919 vaddr = vaddr
1920 .as_usize()
1921 .checked_add(page_size)
1922 .map(VirtAddr::from_usize)
1923 .ok_or_else(|| {
1924 PagingError::address_overflow("protect_region address advance")
1925 })?;
1926 }
1927 Err(PagingError::NotMapped) => {
1928 vaddr = vaddr
1929 .as_usize()
1930 .checked_add(T::PAGE_SIZE)
1931 .map(VirtAddr::from_usize)
1932 .ok_or_else(|| {
1933 PagingError::address_overflow("protect_region address advance")
1934 })?;
1935 }
1936 Err(err) => return Err(err),
1937 }
1938 }
1939 Ok(())
1940 }
1941
1942 pub fn remap_page(
1944 &mut self,
1945 vaddr: VirtAddr,
1946 paddr: PhysAddr,
1947 config: PteConfigOf<T>,
1948 ) -> PagingResult<usize> {
1949 let page_size = self
1950 .root
1951 .remap_recursive(vaddr, paddr, config, Frame::<T, A>::PT_LEVEL)?;
1952 T::flush(Some(vaddr));
1953 Ok(page_size)
1954 }
1955
1956 pub fn query(&self, vaddr: VirtAddr) -> PagingResult<(PhysAddr, PteConfigOf<T>, usize)> {
1958 let (paddr, pte, level) = self.translate_with_level(vaddr)?;
1959 Ok((
1960 paddr,
1961 pte.config(level > 1),
1962 Frame::<T, A>::level_size(level),
1963 ))
1964 }
1965
1966 pub fn query_occupied(&self, vaddr: VirtAddr) -> PagingResult<(T::P, usize)> {
1974 if T::STRICT_ADDRESS_WIDTH && !Self::is_addr_in_width(vaddr.as_usize()) {
1975 return Err(PagingError::address_overflow("query_occupied"));
1976 }
1977 self.root.find_occupied_leaf(vaddr, Frame::<T, A>::PT_LEVEL)
1978 }
1979
1980 pub fn map(&mut self, config: &MapConfig<PteConfigOf<T>>) -> PagingResult {
1982 self.validate_map_config(config)?;
1984
1985 if config.vaddr.as_usize().checked_add(config.size).is_none()
1987 || config.paddr.as_usize().checked_add(config.size).is_none()
1988 {
1989 return Err(PagingError::address_overflow(
1990 "Virtual or physical address overflow",
1991 ));
1992 }
1993 self.validate_address_width(config.vaddr, config.size, "map")?;
1994
1995 let end_vaddr = config
1996 .vaddr
1997 .as_usize()
1998 .checked_add(config.size)
1999 .map(VirtAddr::from_usize)
2000 .ok_or_else(|| PagingError::address_overflow("Virtual address overflow in map"))?;
2001 self.root.map_range_recursive(MapRecursiveConfig {
2002 start_vaddr: config.vaddr,
2003 start_paddr: config.paddr,
2004 end_vaddr,
2005 level: Frame::<T, A>::PT_LEVEL,
2006 allow_huge: config.allow_huge,
2007 flush: config.flush,
2008 pte_template: config.pte,
2009 })?;
2010
2011 Ok(())
2012 }
2013
2014 pub fn unmap(&mut self, start_vaddr: VirtAddr, size: usize) -> PagingResult<()> {
2030 self.validate_unmap_params(start_vaddr, size)?;
2032
2033 let end_vaddr: VirtAddr = match start_vaddr.as_usize().checked_add(size) {
2035 Some(end) => VirtAddr::from_usize(end),
2036 None => {
2037 return Err(PagingError::address_overflow(
2038 "Virtual address overflow in unmap",
2039 ));
2040 }
2041 };
2042 self.validate_address_width(start_vaddr, size, "unmap")?;
2043
2044 self.root.unmap_range_recursive(UnmapRecursiveConfig {
2045 start_vaddr,
2046 end_vaddr,
2047 level: Frame::<T, A>::PT_LEVEL,
2048 flush: true, retained_root_entries: self
2050 .retained_root_entries
2051 .map(|entries| (entries.start, entries.end)),
2052 })?;
2053
2054 Ok(())
2055 }
2056
2057 pub fn unmap_with_config(&mut self, config: &UnmapConfig) -> PagingResult<()> {
2059 self.validate_unmap_params(config.start_vaddr, config.size)?;
2060
2061 let end_vaddr = match config.start_vaddr.as_usize().checked_add(config.size) {
2062 Some(end) => VirtAddr::from_usize(end),
2063 None => {
2064 return Err(PagingError::address_overflow(
2065 "Virtual address overflow in unmap_with_config",
2066 ));
2067 }
2068 };
2069 self.validate_address_width(config.start_vaddr, config.size, "unmap_with_config")?;
2070
2071 self.root.unmap_range_recursive(UnmapRecursiveConfig {
2072 start_vaddr: config.start_vaddr,
2073 end_vaddr,
2074 level: Frame::<T, A>::PT_LEVEL,
2075 flush: config.flush,
2076 retained_root_entries: self
2077 .retained_root_entries
2078 .map(|entries| (entries.start, entries.end)),
2079 })?;
2080
2081 Ok(())
2082 }
2083
2084 fn validate_unmap_params(&self, start_vaddr: VirtAddr, size: usize) -> PagingResult<()> {
2086 if size == 0 {
2087 return Err(PagingError::invalid_size("Size cannot be zero in unmap"));
2088 }
2089
2090 if !start_vaddr.as_usize().is_multiple_of(T::PAGE_SIZE) {
2092 return Err(PagingError::alignment_error(
2093 "Start virtual address not page aligned in unmap",
2094 ));
2095 }
2096
2097 if !size.is_multiple_of(T::PAGE_SIZE) {
2099 return Err(PagingError::alignment_error(
2100 "Size not page aligned in unmap",
2101 ));
2102 }
2103
2104 Ok(())
2105 }
2106
2107 pub fn walk_all(&self, config: WalkConfig) -> PageTableWalker<'_, T, A> {
2109 PageTableWalker::new(self, config)
2110 }
2111
2112 pub fn walk(
2113 &self,
2114 start_vaddr: VirtAddr,
2115 end_vaddr: VirtAddr,
2116 ) -> impl Iterator<Item = crate::walk::PteInfo<T::P>> + '_ {
2117 let config = WalkConfig {
2118 start_vaddr,
2119 end_vaddr,
2120 };
2121 PageTableWalker::new(self, config).filter(|p| p.pte.present())
2122 }
2123
2124 pub fn walk_valid(&self) -> impl Iterator<Item = crate::walk::PteInfo<T::P>> + '_ {
2126 self.walk(0.into(), usize::MAX.into())
2127 .filter(|p| p.pte.present() && p.is_final_mapping)
2128 }
2129
2130 pub fn walk_occupied(&self) -> impl Iterator<Item = crate::walk::PteInfo<T::P>> + '_ {
2137 self.walk_occupied_range(0.into(), usize::MAX.into())
2138 }
2139
2140 pub fn walk_occupied_range(
2147 &self,
2148 start_vaddr: VirtAddr,
2149 end_vaddr: VirtAddr,
2150 ) -> impl Iterator<Item = crate::walk::PteInfo<T::P>> + '_ {
2151 let config = WalkConfig {
2152 start_vaddr,
2153 end_vaddr,
2154 };
2155 PageTableWalker::new(self, config)
2156 .filter(|p| !p.pte.unused() && (p.level == 1 || p.pte.huge(p.level > 1)))
2157 }
2158
2159 pub fn mapping_size_for_level(&self, level: usize) -> Option<usize> {
2161 (level != 0 && level <= T::LEVEL_BITS.len()).then(|| Frame::<T, A>::level_size(level))
2162 }
2163
2164 fn validate_map_config(&self, config: &MapConfig<PteConfigOf<T>>) -> PagingResult {
2166 if config.size == 0 {
2167 return Err(PagingError::invalid_size("Size cannot be zero"));
2168 }
2169
2170 if !config.vaddr.as_usize().is_multiple_of(T::PAGE_SIZE) {
2172 return Err(PagingError::alignment_error(
2173 "Virtual address not page aligned",
2174 ));
2175 }
2176
2177 if !config.paddr.as_usize().is_multiple_of(T::PAGE_SIZE) {
2178 return Err(PagingError::alignment_error(
2179 "Physical address not page aligned",
2180 ));
2181 }
2182
2183 Ok(())
2184 }
2185
2186 fn validate_address_width(
2187 &self,
2188 start_vaddr: VirtAddr,
2189 size: usize,
2190 operation: &'static str,
2191 ) -> PagingResult<()> {
2192 if !T::STRICT_ADDRESS_WIDTH {
2193 return Ok(());
2194 }
2195 let Some(end) = start_vaddr.as_usize().checked_add(size) else {
2196 return Err(PagingError::address_overflow(
2197 "Virtual address range overflow",
2198 ));
2199 };
2200 let last = end - 1;
2201 if !Self::is_addr_in_width(start_vaddr.as_usize()) || !Self::is_addr_in_width(last) {
2202 return Err(PagingError::address_overflow(operation));
2203 }
2204 Ok(())
2205 }
2206
2207 pub const fn page_size() -> usize {
2208 T::PAGE_SIZE
2209 }
2210
2211 pub const fn table_levels() -> usize {
2212 T::LEVEL_BITS.len()
2213 }
2214
2215 pub const fn valid_bits() -> usize {
2216 Frame::<T, A>::PT_VALID_BITS
2217 }
2218
2219 fn is_addr_in_width(addr: usize) -> bool {
2220 let valid_bits = Self::valid_bits();
2221 if valid_bits >= usize::BITS as usize {
2222 return true;
2223 }
2224 addr < (1usize << valid_bits)
2225 }
2226
2227 pub fn translate(&self, vaddr: VirtAddr) -> PagingResult<(PhysAddr, T::P)> {
2239 self.translate_with_level(vaddr)
2240 .map(|(phys_addr, pte, _)| (phys_addr, pte))
2241 }
2242
2243 pub fn translate_with_level(&self, vaddr: VirtAddr) -> PagingResult<(PhysAddr, T::P, usize)> {
2245 if T::STRICT_ADDRESS_WIDTH && !Self::is_addr_in_width(vaddr.as_usize()) {
2246 return Err(PagingError::address_overflow("translate"));
2247 }
2248
2249 let (pte, level) = self
2250 .root
2251 .translate_recursive_with_level(vaddr, Frame::<T, A>::PT_LEVEL)?;
2252
2253 let is_huge = pte.huge(level > 1);
2254 let pte_paddr = pte.paddr(level > 1);
2255
2256 let (phys_addr, _) = if is_huge {
2258 let level_size = Frame::<T, A>::level_size(level);
2260 let offset_in_page = vaddr.as_usize() % level_size;
2261 (
2262 PhysAddr::from_usize(pte_paddr.as_usize() + offset_in_page),
2263 level_size,
2264 )
2265 } else {
2266 let offset_in_page = vaddr.as_usize() % T::PAGE_SIZE;
2268 (
2269 PhysAddr::from_usize(pte_paddr.as_usize() + offset_in_page),
2270 T::PAGE_SIZE,
2271 )
2272 };
2273
2274 Ok((phys_addr, pte, level))
2275 }
2276
2277 pub fn translate_phys(&self, vaddr: VirtAddr) -> PagingResult<PhysAddr> {
2286 let (p, _) = self.translate(vaddr)?;
2287 Ok(p)
2288 }
2289
2290 pub fn is_mapped(&self, vaddr: VirtAddr) -> bool {
2301 self.translate(vaddr).is_ok()
2302 }
2303
2304 pub fn root_paddr(&self) -> crate::PhysAddr {
2306 self.root.paddr
2307 }
2308}
2309
2310fn largest_page_size<T: TableMeta, A: FrameAllocator>(
2311 vaddr: VirtAddr,
2312 paddr: PhysAddr,
2313 remaining: usize,
2314 allow_huge: bool,
2315) -> usize {
2316 if allow_huge {
2317 let max_level = Frame::<T, A>::PT_LEVEL.min(T::MAX_BLOCK_LEVEL);
2318 for level in (2..=max_level).rev() {
2319 let page_size = Frame::<T, A>::level_size(level);
2320 if vaddr.is_aligned(page_size) && paddr.is_aligned(page_size) && remaining >= page_size
2321 {
2322 return page_size;
2323 }
2324 }
2325 }
2326 T::PAGE_SIZE
2327}