Skip to main content

page_table_generic/
table.rs

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/// Intermediate page-table frames detached by one leaf removal.
21///
22/// The frames remain allocated until the stage-1 owner confirms that every
23/// CPU which could walk the old hierarchy has completed a TLB invalidation.
24/// Dropping this token without confirmation intentionally leaks the frames;
25/// reclaiming them early would turn a recoverable shootdown failure into a
26/// use-after-free in a remote hardware page-table walk.
27#[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    /// Returns whether this removal detached no intermediate table frames.
56    pub fn is_empty(&self) -> bool {
57        self.frames.is_empty()
58    }
59
60    /// Returns the number of detached intermediate table frames.
61    pub fn len(&self) -> usize {
62        self.frames.len()
63    }
64
65    /// Reclaims the detached frames after all relevant CPUs confirm TLB
66    /// invalidation.
67    ///
68    /// # Safety
69    ///
70    /// The caller must prove that no CPU can retain a translation or hardware
71    /// page-walk reference through the detached page-table hierarchy.
72    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
96/// A move-only, pre-zeroed child-table reservation.
97///
98/// This raw allocation token never crosses the public API.  Callers receive a
99/// [`HugeSplitDeposit`] that also binds the frame to the huge leaf observed
100/// during prepare.
101struct ReservedTable<T: TableMeta, A: FrameAllocator> {
102    frame: Option<Frame<T, A>>,
103}
104
105/// A detached, fully initialized page-table suffix.
106///
107/// The root frame owns every child reachable through the suffix.  Publishing
108/// the suffix therefore requires one parent-entry store, while dropping an
109/// unpublished suffix recursively returns every reserved table frame.
110struct ReservedMapPath<T: TableMeta, A: FrameAllocator> {
111    root: Option<Frame<T, A>>,
112    root_level: usize,
113}
114
115/// Allocation-free description of where one absent leaf may be installed.
116///
117/// This is captured while the caller has a stable page-table view.  It owns no
118/// frames and may cross a lock boundary so allocation can happen before the
119/// page-table mutation critical section, like Linux's `vmf->prealloc_pte`.
120pub 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
131/// Move-only ownership of a detached page-table suffix for one exact leaf.
132///
133/// Dropping an unconsumed deposit releases only unpublished table frames.  A
134/// successful apply transfers those frames into the live page-table tree and
135/// disarms the deposit.  The target address, physical address, leaf size and
136/// PTE configuration are bound during prepare and cannot be redirected by the
137/// apply caller.
138pub 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
145/// Failed structural apply that returns the still-unpublished map deposit.
146///
147/// Returning ownership is essential for non-sleeping page-table critical
148/// sections: a stale deposit is released only after the caller drops its lock.
149pub struct PageTableMapApplyError<T: TableMeta, A: FrameAllocator> {
150    error: PagingError,
151    deposit: PageTableMapDeposit<T, A>,
152}
153
154/// Move-only ownership of page-table directories prepared for one vacant leaf.
155///
156/// Unlike [`PageTableMapDeposit`], the final leaf remains empty.  This mirrors
157/// Linux's `pmd_install()` preparation for `move_ptes()`: destination page-table
158/// structure can be allocated and published before the PTE lock is acquired,
159/// while the later leaf move is allocation-free.
160pub struct PageTablePathDeposit<T: TableMeta, A: FrameAllocator> {
161    plan: PageTableMapPlan<T, A>,
162    path: ReservedMapPath<T, A>,
163}
164
165/// Failed path publication that returns every still-detached table frame.
166pub struct PageTablePathApplyError<T: TableMeta, A: FrameAllocator> {
167    error: PagingError,
168    deposit: PageTablePathDeposit<T, A>,
169}
170
171/// Immutable identity of one occupied leaf.
172///
173/// It is deliberately allocation-free and owns neither the mapped data frame
174/// nor any page-table frame.  An apply API must revalidate this identity before
175/// clearing the descriptor.
176#[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
193/// Allocation-free preimage for one page-table leaf relocation.
194///
195/// A vacant destination must already have all intermediate directories.  Use
196/// [`PageTableMapPlan::prepare_path`] before constructing the final move plan
197/// when the destination hierarchy is absent.
198pub struct PageTableMovePlan<T: TableMeta, A: FrameAllocator> {
199    source: PageTableLeafPlan<T>,
200    destination_vaddr: VirtAddr,
201    destination: PageTableMoveDestination<T, A>,
202}
203
204/// A child-table deposit bound to one observed huge leaf.
205///
206/// This is the page-table-generic equivalent of Linux's deposited PTE page:
207/// allocation happens before the mutation critical section, dropping an
208/// unpublished deposit releases the frame, and apply consumes it only if the
209/// root and huge-leaf identity still match.  The target address is deliberately
210/// not accepted by apply, so a deposit cannot be redirected to another leaf.
211pub 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
220/// Failed structural apply that returns the still-unpublished deposit to its
221/// caller.  Transactional users must not lose the only child-table owner merely
222/// because the observed huge leaf became stale before apply.
223pub 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
276/// Receipt proving that one deposited child table is now reachable from the
277/// page-table tree.
278///
279/// The receipt does not own mapped data frames.  Its metadata is retained by a
280/// higher-level mutation receipt when rollback, reverse mappings, or delayed
281/// page-table-frame reclamation must be coordinated with a TLB obligation.
282pub 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    /// Prepares only the missing intermediate directories for this leaf.
381    ///
382    /// `Ok(None)` means the complete directory path already exists.  A
383    /// returned deposit owns an unreachable, zeroed suffix; publishing it is
384    /// one bounded parent-entry store and never installs the leaf itself.
385    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    /// Allocates and initializes every missing table below the captured parent.
409    ///
410    /// No live page-table entry is changed.  Failure drops the unpublished
411    /// partial suffix, so callers either receive a complete move-only deposit
412    /// or retain the exact pre-prepare page table.
413    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    /// Set once ownership of all page-table frames has been transferred to
615    /// detached tokens.  `Drop` must not release them a second time.
616    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    /// 创建一个新的页表
625    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    /// Preallocates and retains the root directories covering `range`.
640    ///
641    /// This is the page-table analogue of Linux preallocating the vmalloc
642    /// directory levels before process roots copy the kernel half. A process
643    /// root may subsequently borrow these entries once; all later mappings
644    /// are published below the stable shared directories.
645    ///
646    /// Callers must finish this operation before sharing the affected root
647    /// entries or otherwise publishing this page table. Allocation failure may
648    /// leave a prefix installed, but that private prefix remains owned by this
649    /// page table and is reclaimed by its normal destructor.
650    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    /// Detaches every page-table frame owned by this table and transfers the
708    /// release capability to `release`.  Mapped data frames are never touched.
709    ///
710    /// # Safety
711    ///
712    /// The caller must have stopped all page-table users and completed the
713    /// required local/remote TLB invalidations before reclaiming the returned
714    /// tokens.  The table is unusable after this call; its `Drop` implementation
715    /// intentionally skips frame release to prevent a double free.
716    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        // Publish the inert state before invoking caller code. If a callback
723        // unwinds after consuming a prefix of tokens, Drop must leak the
724        // undispatched suffix rather than recursively double-free that prefix.
725        self.detached = true;
726        self.inner
727            .root
728            .detach_recursive(Frame::<T, A>::PT_LEVEL, &mut release);
729    }
730
731    /// Releases the owning table exactly once.  `PageTableRef` remains a
732    /// copyable view for the legacy walker API, so the detached guard belongs
733    /// to this owning wrapper rather than to the view itself.
734    unsafe fn deallocate_inner(&mut self) {
735        if self.detached {
736            return;
737        }
738        // SAFETY: callers of this helper are the owning `Drop` path; the
739        // caller has exclusive access to the page-table tree.
740        self.inner
741            .root
742            .deallocate_recursive(Frame::<T, A>::PT_LEVEL);
743        self.detached = true;
744    }
745
746    /// Releases all page-table frames and permanently invalidates this
747    /// owning table.  Calling it more than once is harmless; the detached bit
748    /// makes the operation idempotent for teardown/recovery code.
749    ///
750    /// # Safety
751    ///
752    /// No CPU or walker may still use the table when this method is called.
753    pub unsafe fn deallocate(&mut self) {
754        // SAFETY: the precondition is carried by this public unsafe API.
755        unsafe {
756            self.deallocate_inner();
757        }
758    }
759
760    /// Consumes the owning table after releasing its page-table frames.
761    /// Mapped data frames are intentionally left to the mapping owner.
762    ///
763    /// # Safety
764    ///
765    /// The caller must establish the same quiescence requirements as
766    /// [`Self::deallocate`].
767    pub unsafe fn destroy(mut self) {
768        // SAFETY: forwarded from the method's quiescence contract.
769        unsafe {
770            self.deallocate_inner();
771        }
772    }
773
774    /// Abandons the allocator capability for this table without attempting a
775    /// fallible teardown.
776    ///
777    /// This is intentionally an explicit leak used only when an owning
778    /// address-space destructor discovers that mappings or a TLB quarantine
779    /// are still live.  `Drop` must not reclaim page-table frames in that
780    /// state: doing so could let a stale CPU walk a frame that has already
781    /// been reused.  The caller must retain an out-of-band repair record if
782    /// those frames are to be reclaimed after the missing quiescence is fixed.
783    pub fn leak(&mut self) {
784        #[cfg(feature = "copy-from")]
785        self.detach_borrowed_root_entries();
786        self.detached = true;
787    }
788
789    /// Convenience wrapper for a VA→PA mapping.  The endpoint arithmetic is
790    /// checked before any PTE is written.  Contiguous ranges may use block
791    /// descriptors; sparse/device ranges are represented by base-page leaves.
792    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    /// Deep-copies source root entries that are absent from this page table.
834    ///
835    /// Leaf mappings keep referring to the same physical memory, while every
836    /// copied intermediate page-table frame is independently owned by this
837    /// page table. Existing destination root entries are left unchanged.
838    ///
839    /// If allocation fails, entries copied before the failure remain installed
840    /// and are reclaimed normally when this page table is dropped.
841    ///
842    /// # Errors
843    ///
844    /// Returns an error if the range overflows or wraps around the root table,
845    /// or if an intermediate page-table frame cannot be allocated.
846    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    /// Shares root page-table entries from another page table.
871    ///
872    /// Mappings below the shared root entries remain owned by the source and
873    /// changes made there are visible through both page tables.
874    ///
875    /// # Safety
876    ///
877    /// The source page table must outlive this page table. The caller must
878    /// also prevent this page table from modifying or unmapping the shared
879    /// virtual-address range.
880    #[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            // 释放所有页表帧,但不释放映射的物理页
950            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    /// Root directories that must remain installed after their last leaf is
972    /// removed. Kernel page tables use this for ranges shared into process
973    /// roots: later mappings remain visible through the already-shared child
974    /// directory instead of requiring root-entry propagation.
975    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    /// 创建一个新的页表
1003    ///
1004    /// # Safety
1005    ///
1006    /// 调用者必须确保提供的FrameAllocator是有效的,并且在页表生命周期内保持有效
1007    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    /// Creates a non-owning view of an existing page-table root.
1016    ///
1017    /// The returned view may inspect and mutate page-table entries, but it
1018    /// never owns or releases any page-table frame. Only [`PageTable`] carries
1019    /// the corresponding frame-reclamation capability.
1020    ///
1021    /// # Safety
1022    ///
1023    /// The caller must ensure that `paddr` names an aligned, initialized root
1024    /// table for `T`, that every table frame reachable from it remains mapped
1025    /// by `allocator` for the entire use of this value, and that all mutable
1026    /// access is serialized with hardware walkers and other page-table users.
1027    /// The caller must also ensure that the owning page table outlives this
1028    /// view.
1029    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    /// Maps one page with the requested page size.
1038    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    /// Maps a virtual region from a per-base-page physical resolver.
1066    ///
1067    /// The resolver may return a non-contiguous physical page sequence, so this
1068    /// API deliberately installs only base-page leaves. Use
1069    /// [`Self::map_linear_pages`] when the physical range is known to be
1070    /// contiguous and block mappings are allowed.
1071    ///
1072    /// Mappings installed by this call are rolled back if a later page fails.
1073    /// TLB invalidation is deferred and batched until the region has been
1074    /// updated.
1075    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    /// Maps a virtual region using a fallible physical-address resolver.
1086    ///
1087    /// The resolver is evaluated before each PTE write.  If it rejects a
1088    /// later page, mappings already installed by this invocation are rolled
1089    /// back and the resolver error is returned. This is the capability used
1090    /// by allocation-backed or sparse device mappings: address resolution
1091    /// must remain checked all the way through the page-table walker. Because
1092    /// the resolver does not prove contiguity, this API uses base-page leaves.
1093    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    /// Unmaps one page and returns its physical address, flags, and page size.
1236    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    /// Unmaps one occupied leaf without reclaiming detached intermediate
1256    /// page-table frames.
1257    ///
1258    /// The returned ownership token must be retained by the stage-1 TLB gather
1259    /// until every CPU that could use this page table confirms invalidation.
1260    /// This method performs no TLB invalidation itself.
1261    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    /// Returns the huge block covering `vaddr` without changing the table.
1282    /// Retained non-present blocks are reported when the PTE format preserves
1283    /// their descriptor state.
1284    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    /// Captures the existing page-table prefix for one currently absent leaf.
1300    ///
1301    /// This operation performs no allocation.  The returned plan owns only an
1302    /// allocator capability and copyable identity data, so a caller can drop
1303    /// its page-table lock before [`PageTableMapPlan::prepare`] allocates the
1304    /// missing suffix.  Apply rewalks the prefix and rejects a stale plan.
1305    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    /// Installs one fully prepared leaf without allocating or releasing memory.
1361    ///
1362    /// On failure the move-only deposit is returned to the caller, which must
1363    /// drop it after leaving any non-sleeping page-table critical section.  A
1364    /// successful apply publishes an already initialized suffix with one
1365    /// release-ordered parent store, then transfers every reserved frame to the
1366    /// live page-table tree.
1367    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            // Every child descriptor and the leaf were initialized before the
1409            // suffix becomes reachable.  The release fence is the generic
1410            // counterpart of Linux pmd_install()'s smp_wmb().
1411            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    /// Publishes a prepared, empty page-table suffix.
1423    ///
1424    /// The operation only revalidates the captured prefix and performs one
1425    /// release-ordered parent-entry store.  It never allocates, frees, or
1426    /// flushes; a failed apply returns the detached suffix to its caller.
1427    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    /// Captures the exact occupied leaf covering `vaddr` without allocating.
1477    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    /// Captures one source leaf and the destination state for a later batch
1512    /// move.  The destination hierarchy must already exist when it is vacant.
1513    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    /// Clears one previously planned leaf without pruning directories or
1610    /// performing a TLB flush.
1611    ///
1612    /// The caller retains ownership of the mapped data frame and must attach a
1613    /// later TLB obligation before releasing it.  Empty page-table directories
1614    /// remain owned by this page table and can be reused by a subsequent fault.
1615    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    /// Applies a sorted batch of page moves after validating every preimage.
1630    ///
1631    /// No descriptor is changed until the whole slice passes validation.  The
1632    /// apply phase then consists only of PTE clears/stores: it cannot allocate,
1633    /// free, flush, or fail.  Source directories are intentionally retained,
1634    /// matching Linux's PTE move before `free_pgtables()` handles the detached
1635    /// source VMA.
1636    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    /// Allocates a pre-zeroed child table and binds it to the currently
1720    /// observed huge leaf.
1721    ///
1722    /// The returned deposit may be stored by a mapping owner until a future
1723    /// partial operation needs to split the leaf.  Apply revalidates the root,
1724    /// virtual range, physical frame, configuration, and size before touching
1725    /// any descriptor.
1726    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        // The child frame is now reachable from the tree.  Disarm immediately
1784        // after the structural apply so no later receipt construction can
1785        // accidentally free a live page-table frame.
1786        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    /// Consumes a bound deposit and splits its huge block into inherited finer
1798    /// leaves.  No allocation occurs during apply.
1799    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    /// Transactional variant of [`Self::split_huge_page_with`].  On failure the
1811    /// caller receives the still-owned deposit and can put it back into its
1812    /// mapping slot without allocating during recovery.
1813    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    /// Splits a huge block and installs an empty child table for a caller that
1824    /// will materialize non-contiguous finer leaves under the same mutation
1825    /// domain. The old block metadata is returned for rollback/accounting.
1826    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    /// Rolls an installed split back to the exact huge descriptor captured by
1838    /// its receipt and returns ownership of the withdrawn child table.
1839    ///
1840    /// No allocation occurs.  The returned deposit is bound to the restored
1841    /// block and can either be retained for a retry or dropped to release the
1842    /// now-unpublished page-table frame.  This is the inverse of
1843    /// [`Self::split_huge_page_with`] used by unpublished transaction aborts.
1844    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    /// Prepares and performs an inherited huge split. Transactional callers
1870    /// should retain [`HugeSplitDeposit`] from [`Self::prepare_huge_split`]
1871    /// before entering their mutation critical section.
1872    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    /// Changes one existing mapping's flags and returns its page size.
1882    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    /// Changes flags for a region. Unmapped base pages are skipped.
1891    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        // Linux splits only block mappings crossed by a protection boundary.
1906        // Interior blocks remain large mappings and can be updated as a unit.
1907        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    /// Remaps one existing mapping and returns its page size.
1943    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    /// Queries one mapping and returns the translated physical address, flags, and page size.
1957    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    /// Queries one occupied leaf, including a non-present software mapping.
1967    ///
1968    /// Unlike [`Self::query`], this method distinguishes an unused entry from
1969    /// a leaf whose descriptor is retained while address translation is
1970    /// disabled. It is intended for ownership, rollback, and destructive
1971    /// page-table operations. Callers must not use a successful result as
1972    /// proof that the virtual address is currently accessible.
1973    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    /// 映射虚拟地址范围到物理地址范围
1981    pub fn map(&mut self, config: &MapConfig<PteConfigOf<T>>) -> PagingResult {
1982        // 验证输入参数
1983        self.validate_map_config(config)?;
1984
1985        // 检查大小溢出
1986        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    /// 取消映射虚拟地址范围
2015    ///
2016    /// # 参数
2017    /// - `start_vaddr`: 要取消映射的起始虚拟地址
2018    /// - `size`: 要取消映射的大小(字节)
2019    ///
2020    /// # 返回值
2021    /// - `Ok(())`: 取消映射成功
2022    /// - `Err(PagingError)`: 取消映射失败
2023    ///
2024    /// # 行为
2025    /// - 清除指定虚拟地址范围内的所有页表项
2026    /// - 自动回收空的子页表帧
2027    /// - 支持大页和普通页面的取消映射
2028    /// - 根据配置刷新TLB
2029    pub fn unmap(&mut self, start_vaddr: VirtAddr, size: usize) -> PagingResult<()> {
2030        // 验证输入参数
2031        self.validate_unmap_params(start_vaddr, size)?;
2032
2033        // 检查大小溢出
2034        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, // 默认刷新TLB确保一致性
2049            retained_root_entries: self
2050                .retained_root_entries
2051                .map(|entries| (entries.start, entries.end)),
2052        })?;
2053
2054        Ok(())
2055    }
2056
2057    /// 使用配置对象取消映射
2058    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    /// 验证取消映射参数的有效性
2085    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        // 检查虚拟地址是否页对齐
2091        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        // 检查大小是否页对齐
2098        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    /// 创建页表遍历迭代器
2108    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    /// 遍历所有有效的最终映射页表项(过滤掉无效项和中间级别的页表指针)
2125    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    /// Walks every occupied final leaf, including retained non-present leaves.
2131    ///
2132    /// Rollback and quarantine code must distinguish an empty page-table slot
2133    /// from a descriptor that still owns a physical mapping but has had its
2134    /// access permissions removed. The walk scales with allocated page-table
2135    /// frames rather than with the represented virtual address span.
2136    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    /// Walks occupied final leaves whose represented range overlaps
2141    /// `[start_vaddr, end_vaddr)`.
2142    ///
2143    /// Unlike probing every base page, this follows only allocated page-table
2144    /// paths that intersect the requested range. Retained non-present leaves
2145    /// remain visible so rollback can keep PTE and software ownership in sync.
2146    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    /// Returns the mapping size represented by one page-table level.
2160    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    /// 验证映射配置的有效性
2165    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        // 检查虚拟地址和物理地址是否页对齐
2171        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    /// 通过虚拟地址查询页表项
2228    ///
2229    /// # 参数
2230    /// - `vaddr`: 要查询的虚拟地址
2231    ///
2232    /// # 返回值
2233    /// - `Ok(T::P)`: 找到的页表项,包含物理地址信息
2234    /// - `Err(PagingError)`: 查询失败,原因可能包括:
2235    ///   - 地址未映射
2236    ///   - 页表项无效
2237    ///   - 页表层次结构错误
2238    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    /// Translates a virtual address and returns the matched PTE level.
2244    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        // 根据页表项类型计算正确的偏移
2257        let (phys_addr, _) = if is_huge {
2258            // 大页映射:需要使用实际级别的大小来计算偏移
2259            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            // 普通页面映射:使用页面大小
2267            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    /// 通过虚拟地址查询物理地址(便利方法)
2278    ///
2279    /// # 参数
2280    /// - `vaddr`: 要查询的虚拟地址
2281    ///
2282    /// # 返回值
2283    /// - `Ok(PhysAddr)`: 找到的物理地址
2284    /// - `Err(PagingError)`: 查询失败
2285    pub fn translate_phys(&self, vaddr: VirtAddr) -> PagingResult<PhysAddr> {
2286        let (p, _) = self.translate(vaddr)?;
2287        Ok(p)
2288    }
2289
2290    /// 检查虚拟地址是否已映射
2291    ///
2292    /// 这是一个便利方法,用于快速检查地址是否已映射而不需要获取页表项
2293    ///
2294    /// # 参数
2295    /// - `vaddr`: 要检查的虚拟地址
2296    ///
2297    /// # 返回值
2298    /// - `true`: 地址已映射
2299    /// - `false`: 地址未映射
2300    pub fn is_mapped(&self, vaddr: VirtAddr) -> bool {
2301        self.translate(vaddr).is_ok()
2302    }
2303
2304    /// 获取页表的根帧物理地址
2305    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}