Skip to main content

ostd/mm/frame/
mod.rs

1// SPDX-License-Identifier: MPL-2.0
2//! Frame (physical memory page) management.
3//!
4//! A frame is an aligned, contiguous range of bytes in physical memory. The
5//! sizes of base frames and huge frames (that are mapped as "huge pages") are
6//! architecture-dependent. A frame can be mapped to virtual address spaces
7//! using the page table.
8//!
9//! Frames can be accessed through frame handles, namely, [`Frame`]. A frame
10//! handle is a reference-counted pointer to a frame. When all handles to a
11//! frame are dropped, the frame is released and can be reused.  Contiguous
12//! frames are managed with [`Segment`].
13//!
14//! There are various kinds of frames. The top-level grouping of frame kinds
15//! are "typed" frames and "untyped" frames. Typed frames host Rust objects
16//! that must follow the visibility, lifetime and borrow rules of Rust, thus
17//! not being able to be directly manipulated. Untyped frames are raw memory
18//! that can be manipulated directly. So only untyped frames can be
19//!  - safely shared to external entities such as device drivers or user-space
20//!    applications.
21//!  - or directly manipulated with readers and writers that neglect Rust's
22//!    "alias XOR mutability" rule.
23//!
24//! The kind of a frame is determined by the type of its metadata. Untyped
25//! frames have its metadata type that implements the [`AnyUFrameMeta`]
26//! trait, while typed frames don't.
27//!
28//! Frames can have dedicated metadata, which is implemented in the [`meta`]
29//! module. The reference count and usage of a frame are stored in the metadata
30//! as well, leaving the handle only a pointer to the metadata slot. Users
31//! can create custom metadata types by implementing the [`AnyFrameMeta`] trait.
32//pub mod allocator;
33pub mod allocator;
34pub mod linked_list;
35pub mod meta;
36pub mod segment;
37pub mod unique;
38pub mod untyped;
39
40mod frame_ref;
41pub mod obligation_demo;
42
43#[cfg(ktest)]
44mod test;
45
46use vstd::atomic::PermissionU64;
47use vstd::prelude::*;
48use vstd::simple_pptr::{self, PPtr};
49use vstd_extra::cast_ptr::*;
50use vstd_extra::drop_tracking::*;
51use vstd_extra::ownership::*;
52use vstd_extra::panic::may_panic;
53
54use core::{
55    marker::PhantomData,
56    sync::atomic::{AtomicUsize, Ordering},
57};
58
59//pub use allocator::GlobalFrameAllocator;
60use meta::{REF_COUNT_MAX, REF_COUNT_UNIQUE, REF_COUNT_UNUSED};
61pub use segment::Segment;
62
63// Re-export commonly used types
64use crate::specs::arch::kspace::FRAME_METADATA_RANGE;
65pub use frame_ref::FrameRef;
66pub use linked_list::{CursorMut, Link, LinkedList};
67pub use meta::mapping::{META_SLOT_SIZE, frame_to_index, frame_to_meta, meta_addr, meta_to_frame};
68pub use meta::{AnyFrameMeta, GetFrameError, MetaSlot, has_safe_slot};
69pub use unique::UniqueFrame;
70pub use untyped::{AnyUFrameMeta, UFrame};
71
72use crate::mm::page_table::{PageTableConfig, PageTablePageMeta};
73
74use crate::mm::page_table::RCClone;
75use crate::mm::{
76    MAX_PADDR, Paddr, PagingLevel, Vaddr,
77    kspace::{LINEAR_MAPPING_BASE_VADDR, VMALLOC_BASE_VADDR},
78};
79use crate::specs::arch::mm::{MAX_NR_PAGES, PAGE_SIZE};
80use crate::specs::mm::frame::frame_specs::*;
81use crate::specs::mm::frame::meta_owners::*;
82use crate::specs::mm::frame::meta_region_owners::MetaRegionOwners;
83
84verus! {
85
86/*
87static MAX_PADDR: AtomicUsize = AtomicUsize::new(0);
88
89/// Returns the maximum physical address that is tracked by frame metadata.
90pub(in crate::mm) fn max_paddr() -> Paddr {
91    let max_paddr = MAX_PADDR.load(Ordering::Relaxed) as Paddr;
92    debug_assert_ne!(max_paddr, 0);
93    max_paddr
94}*/
95#[verifier::external_body]
96fn acquire_fence() {
97    core::sync::atomic::fence(Ordering::Acquire);
98}
99
100/// A smart pointer to a frame.
101///
102/// A frame is a contiguous range of bytes in physical memory. The [`Frame`]
103/// type is a smart pointer to a frame that is reference-counted.
104///
105/// Frames are associated with metadata. The type of the metadata `M` is
106/// determines the kind of the frame. If `M` implements [`AnyUFrameMeta`], the
107/// frame is a untyped frame. Otherwise, it is a typed frame.
108/// # Verification Design
109#[repr(transparent)]
110pub struct Frame<M: ?Sized> {
111    pub ptr: PPtr<MetaSlot>,
112    pub _marker: PhantomData<M>,
113}
114
115/*
116impl<M: ?Sized> Inv for Frame<M> {
117    open spec fn inv(self) -> bool {
118        &&& self.ptr.addr() % META_SLOT_SIZE == 0
119        &&& FRAME_METADATA_RANGE.start <= self.ptr.addr() < FRAME_METADATA_RANGE.start
120            + MAX_NR_PAGES * META_SLOT_SIZE
121    }
122}
123
124// Unbounded so the PT-node `on_drop` body can use `Frame::<Self>::from_raw` /
125// `Drop for Frame<Self>` without forcing trait resolution back through the
126// in-flight `AnyFrameMeta for PageTablePageMeta<C>` impl. Body is pure
127// pointer arithmetic — no M-specific machinery.
128impl<M: ?Sized> Frame<M> {
129    pub open spec fn paddr(self) -> usize {
130        meta_to_frame(self.ptr.addr())
131    }
132}*/
133
134/*
135impl<M: AnyFrameMeta + ?Sized> PartialEq for Frame<M> {
136    fn eq(&self, other: &Self) -> bool {
137        self.start_paddr() == other.start_paddr()
138    }
139}
140
141impl<M: AnyFrameMeta + ?Sized> Eq for Frame<M> {}
142*/
143
144impl<M: ?Sized> Frame<M> {
145    /// Cross-object well-formedness predicate: this `Frame` handle and
146    /// the supplied [`MetaRegionOwners`] state are mutually consistent.
147    /// Packages the static "Frame ⟷ state" conjuncts (slot/pointer
148    /// identity, slot in-use range) so that consumer specs
149    /// ([`drop_requires`], [`clone_requires`]) read uniformly.
150    ///
151    /// **Name**: `wf_state` (not just `wf`) to avoid clashing with the
152    /// `OwnerOf::wf(self, Self::Owner)` impl that
153    /// [`PageTableNode<C> = Frame<PageTablePageMeta<C>>`] inherits — the
154    /// two predicates take different argument types and serve different
155    /// purposes (per-handle vs. per-owner well-formedness).
156    ///
157    /// The rc range (`> 0 ∧ ≠ UNUSED ∧ ≠ UNIQUE ∧ ≤ MAX`) captures the
158    /// fact that holding a `Frame<M>` is itself evidence that the slot
159    /// is in the SHARED state — no UNUSED, no UNIQUE (which is reserved
160    /// for [`UniqueFrame`]). Combined with
161    /// [`MetaSlotOwner::inv`]'s SHARED branch (post Item 1), `wf_state`
162    /// implies `storage.is_init`, `in_list == 0`, and `vtable_ptr.is_init`
163    /// at the slot, so consumers don't have to repeat those.
164    ///
165    /// **Not preserved by `drop` for `self`**: dropping `self` releases
166    /// the reference; for *other* handles to the same slot, `wf_state`
167    /// is preserved by `drop`'s `>1` branch (post rc ∈ [1, MAX-1]) and
168    /// vacuous in the `==1` branch (no other handles to break).
169    pub open spec fn wf_state(self, s: MetaRegionOwners) -> bool {
170        let idx = self.index();
171        let slot_own = s.slot_owners[idx];
172        &&& self.inv()
173        &&& s.inv()
174        &&& s.slots.contains_key(idx)
175        &&& s.slots[idx].pptr() == self.ptr
176        &&& s.slot_owners.contains_key(idx)
177        &&& slot_own.inner_perms.ref_count.value() != REF_COUNT_UNUSED
178        &&& slot_own.inner_perms.ref_count.value() != REF_COUNT_UNIQUE
179        &&& slot_own.inner_perms.ref_count.value() > 0
180        &&& slot_own.inner_perms.ref_count.value() <= REF_COUNT_MAX
181    }
182}
183
184#[verus_verify]
185impl<M> Frame<M> {
186    /// Restores a forgotten [`Frame`] from a physical address.
187    ///
188    /// # Safety
189    ///
190    /// The caller should only restore a `Frame` that was previously forgotten using
191    /// [`Frame::into_raw`].
192    ///
193    /// And the restoring operation should only be done once for a forgotten
194    /// [`Frame`]. Otherwise double-free will happen.
195    ///
196    /// Also, the caller ensures that the usage of the frame is correct. There's
197    /// no checking of the usage in this function.
198    #[verus_spec(r =>
199        with
200            Tracked(regions): Tracked<&mut MetaRegionOwners>,
201            -> obl: Tracked<vstd_extra::drop_tracking::DropObligation<usize>>,
202        requires
203            Self::from_raw_requires_safety(*old(regions), paddr),
204            old(regions).slots.contains_key(frame_to_index(paddr)),
205            // Borrow-protocol safety: the slot must be alive (not torn
206            // down). The `unsafe` keyword still gates whether the produced
207            // Frame corresponds to a real prior `into_raw`; this condition
208            // only ensures the slot isn't a dead/unused one.
209            old(regions).slot_owners[frame_to_index(paddr)].inner_perms.ref_count.value()
210                != REF_COUNT_UNUSED,
211        ensures
212            Self::from_raw_ensures(*old(regions), *final(regions), paddr, r),
213            final(regions).slots == old(regions).slots,
214            obl@.value() == frame_to_index(paddr),
215    )]
216    pub(in crate::mm) unsafe fn from_raw(paddr: Paddr) -> Self {
217        let vaddr = frame_to_meta(paddr);
218        let ptr = PPtr::from_addr(vaddr);
219
220        let ghost idx = frame_to_index(paddr);
221
222        proof_decl! {
223            let tracked obl_minted: vstd_extra::drop_tracking::DropObligation<usize>;
224        }
225        proof {
226            // Mint the obligation that will be consumed by either
227            // `ManuallyDrop::new` (FrameRef-style borrow) or
228            // `Frame::drop` (reclaim-and-drop). `raw_count` is no longer
229            // touched — the field is dormant pending its removal.
230            obl_minted = regions.tracked_mint_frame_obligation(idx);
231        }
232
233        proof_with!(|= Tracked(obl_minted));
234        Self { ptr, _marker: PhantomData }
235    }
236}
237
238#[verus_verify]
239impl<'a, M: AnyFrameMeta + Repr<MetaSlotStorage> + OwnerOf> Frame<M> {
240    /// Gets a [`Frame`] with a specific usage from a raw, unused page.
241    ///
242    /// The caller should provide the initial metadata of the page.
243    ///
244    /// If the provided frame is not truly unused at the moment, it will return
245    /// an error. If wanting to acquire a frame that is already in use, use
246    /// [`Frame::from_in_use`] instead.
247    /// # Verified Properties
248    /// ## Preconditions
249    /// - **Safety Invariant**: Metaslot region invariants must hold.
250    /// ## Postconditions
251    /// - **Safety Invariant**: Metaslot region invariants hold after the call.
252    /// - **Correctness**: If successful, the function returns a pointer to the metadata slot and a permission to the slot.
253    /// - **Correctness**: If successful, the slot is initialized with the given metadata.
254    /// - **Correctness**: If `paddr` does not have a corresponding metadata slot, the function returns an error.
255    /// - **Drop Bookkeeping**: If successful, the function returns a live frame, which is tracked correctly as needing to be dropped.
256    /// ## Safety
257    /// - This function returns an error if `paddr` does not correspond to a valid slot or the slot is in use.
258    #[verus_spec(r =>
259        with
260            Tracked(regions): Tracked<&mut MetaRegionOwners>
261        requires
262            old(regions).inv(),
263        ensures
264            final(regions).inv(),
265            r matches Ok(res) ==> res.ptr.addr() == frame_to_meta(paddr),
266            r is Ok ==> MetaSlot::get_from_unused_spec(paddr, false, *old(regions), *final(regions)),
267            r is Ok ==> MetaSlot::slot_perm_reparked_spec(paddr, *old(regions), *final(regions)),
268            !has_safe_slot(paddr) ==> r is Err,
269            r is Ok ==> MetaSlot::live_frame_obligations_ok_spec(paddr, *old(regions), *final(regions)),
270            r is Err ==> MetaSlot::live_frame_obligations_err_spec(*old(regions), *final(regions)),
271            // On failure nothing was claimed: the region state is untouched.
272            r is Err ==> *final(regions) =~= *old(regions),
273    )]
274    pub fn from_unused(paddr: Paddr, metadata: M) -> Result<Self, GetFrameError> {
275        let ghost pre = *regions;
276        #[verus_spec(with Tracked(regions))]
277        let from_unused = MetaSlot::get_from_unused(paddr, metadata, false);
278        if let Err(err) = from_unused {
279            Err(err)
280        } else {
281            let (ptr, Tracked(perm)) = from_unused.unwrap();
282            let ghost idx = frame_to_index(paddr);
283            proof {
284                assert(frame_to_index(paddr) < crate::mm::frame::meta::mapping::max_meta_slots());
285                assert(pre.slot_owners.contains_key(idx));
286                assert(pre.slots.contains_key(idx));
287                regions.sync_slot_perm(idx, &perm);
288                // Mint the pending-Drop obligation for the new live value.
289                let tracked _ = regions.tracked_mint_frame_obligation(idx);
290            }
291            Ok(Self { ptr, _marker: PhantomData })
292        }
293    }
294
295    /// Gets the metadata of this page.
296    /// # Verified Properties
297    /// ## Preconditions
298    /// - The caller must have a valid permission for the frame.
299    /// ## Postconditions
300    /// - The function returns the borrowed metadata of the frame.
301    /// ## Safety
302    /// - By requiring the caller to provide a typed permission, we ensure that the metadata is of type `M`.
303    /// While a non-verified caller cannot be trusted to obey this interface, all functions that return a `Frame<M>` also
304    /// return an appropriate permission.
305    #[verus_spec(
306        with
307            Tracked(perm) : Tracked<&'a PointsTo<MetaSlot, Metadata<M>>>,
308        requires
309            self.ptr.addr() == perm.addr(),
310            self.ptr == perm.points_to.pptr(),
311            perm.is_init(),
312            perm.wf(&perm.inner_perms),
313        returns
314            perm.value().metadata,
315    )]
316    pub fn meta(&self) -> &'a M {
317        // SAFETY: The type is tracked by the type system.
318        // unsafe { &*self.slot().as_meta_ptr::<M>() }
319        #[verus_spec(with Tracked(&perm.points_to))]
320        let slot = self.slot();
321
322        #[verus_spec(with Tracked(&perm.points_to))]
323        let ptr = slot.as_meta_ptr();
324
325        &ptr.borrow(Tracked(perm)).metadata
326    }
327}
328
329#[verus_verify]
330impl<M: AnyFrameMeta + Repr<MetaSlotStorage>> Frame<M> {
331    /// Gets a dynamically typed [`Frame`] from a raw, in-use page.
332    ///
333    /// If the provided frame is not in use at the moment, it will return an error.
334    ///
335    /// The returned frame will have an extra reference count to the frame.
336    ///
337    /// # Verified Properties
338    /// ## Preconditions
339    /// - **Safety Invariant**: Metaslot region invariants must hold.
340    /// - *Termination*: The function may panic if `paddr` is a valid slot and its reference count is saturated.
341    /// ## Postconditions
342    /// - **Safety Invariant**: Metaslot region invariants hold after the call.
343    /// - **Correctness**: If successful, the function returns the frame at `paddr`.
344    /// - **Correctness**: If successful, the frame has an extra reference count.
345    /// - **Correctness**: If `paddr` does not have a valid metadata slot, the function returns an error.
346    /// - **Safety**: Frames other than the one at `paddr` are not affected by the call.
347    /// ## Safety
348    /// - If `paddr` is a valid frame address, it is safe to take a reference to the frame.
349    /// - If `paddr` is not a valid frame address, the function will return an error.
350    #[verus_spec(res =>
351        with Tracked(regions) : Tracked<&mut MetaRegionOwners>,
352        requires
353            old(regions).inv(),
354            has_safe_slot(paddr) ==> old(regions).ref_count(frame_to_index(paddr)) >= REF_COUNT_MAX ==> may_panic(),
355        ensures
356            final(regions).inv(),
357            res is Ok ==>
358                final(regions).ref_count(frame_to_index(paddr)) ==
359                old(regions).ref_count(frame_to_index(paddr)) + 1,
360            res matches Ok(res) ==>
361                res.ptr == old(regions).slots[frame_to_index(paddr)].pptr(),
362            !has_safe_slot(paddr) ==> res is Err,
363            old(regions).slot_owners_agree_except(*final(regions), frame_to_index(paddr)),
364            res is Ok ==> MetaSlot::live_frame_obligations_ok_spec(paddr, *old(regions), *final(regions)),
365            res is Err ==> MetaSlot::live_frame_obligations_err_spec(*old(regions), *final(regions)),
366    )]
367    pub fn from_in_use(paddr: Paddr) -> Result<Self, GetFrameError> {
368        let res = #[verus_spec(with Tracked(regions))]
369        MetaSlot::get_from_in_use(paddr);
370        match res {
371            Ok(ptr) => {
372                proof {
373                    // Mint the pending-Drop obligation for the new live value.
374                    let tracked _ = regions.tracked_mint_frame_obligation(frame_to_index(paddr));
375                }
376                Ok(Self { ptr, _marker: PhantomData })
377            },
378            Err(e) => Err(e),
379        }
380    }
381}
382
383#[verus_verify]
384impl<'a, M: AnyFrameMeta + Repr<MetaSlotStorage>> Frame<M> {
385    /// Gets the physical address of the start of the frame.
386    /// # Verified Properties
387    /// ## Preconditions
388    /// - **Bookkeeping**: takes the permission for the frame's metadata slot.
389    /// ## Postconditions
390    /// - **Correctness**: returns the physical address of the frame.
391    /// ## Safety
392    /// The caller cannot obtain a frame that doesn't have a valid permission,
393    /// and this function does not mutate any state, so it is always sound to call.
394    #[verus_spec(
395        with Tracked(perm): Tracked<&vstd::simple_pptr::PointsTo<MetaSlot>>,
396    requires
397        perm.addr() == self.ptr.addr(),
398        perm.is_init(),
399        FRAME_METADATA_RANGE.start <= perm.addr() < FRAME_METADATA_RANGE.end,
400        perm.addr() % META_SLOT_SIZE == 0,
401    returns
402        meta_to_frame(self.ptr.addr()),
403    )]
404    pub fn start_paddr(&self) -> Paddr {
405        #[verus_spec(with Tracked(perm))]
406        let slot = self.slot();
407
408        #[verus_spec(with Tracked(perm))]
409        slot.frame_paddr()
410    }
411
412    /// Compares two frames by their start physical address.
413    ///
414    /// # Verified Properties
415    /// ## Preconditions
416    /// - **Safety Invariant**: the frames and metadata regions must satisfy the global invariants.
417    /// ## Postconditions
418    /// - **Correctness**: the function returns true if the frames have
419    /// the same physical addresses and false otherwise.
420    /// ## Safety
421    /// Everything is immutable, so the safety invariant is preserved implicitly.
422    /// ## Verification Design
423    /// This is an inherent impl equivalent to `PartialEq::eq` for `Frame<M>`: freed from the
424    /// trait signature so that this version can thread the tracked `MetaRegionOwners` via `verus_spec`.
425    #[verus_spec(res =>
426        with
427            Tracked(regions): Tracked<&MetaRegionOwners>,
428        requires
429            self.inv(),
430            other.inv(),
431            regions.inv(),
432        ensures
433            res == (meta_to_frame(self.ptr.addr()) == meta_to_frame(other.ptr.addr())),
434    )]
435    pub fn eq(&self, other: &Self) -> bool {
436        let ghost self_idx = frame_to_index(meta_to_frame(self.ptr.addr()));
437        let ghost other_idx = frame_to_index(meta_to_frame(other.ptr.addr()));
438        proof {
439            broadcast use crate::mm::frame::meta::mapping::group_page_meta;
440
441            regions.inv_implies_correct_addr(meta_to_frame(self.ptr.addr()));
442            regions.inv_implies_correct_addr(meta_to_frame(other.ptr.addr()));
443            assert(regions.slots.contains_key(self_idx));
444            assert(regions.slots.contains_key(other_idx));
445            assert(regions.slots[self_idx].addr() == self.ptr.addr());
446            assert(regions.slots[other_idx].addr() == other.ptr.addr());
447        }
448        let tracked self_perm = regions.slots.tracked_borrow(self_idx);
449        let tracked other_perm = regions.slots.tracked_borrow(other_idx);
450
451        (#[verus_spec(with Tracked(self_perm))]
452        self.start_paddr() == #[verus_spec(with Tracked(other_perm))]
453        other.start_paddr())
454    }
455
456    /// Gets the map level of this page.
457    ///
458    /// This is the level of the page table entry that maps the frame,
459    /// which determines the size of the frame.
460    ///
461    /// Currently, the level is always 1, which means the frame is a regular
462    /// page frame.
463    pub const fn map_level(&self) -> PagingLevel {
464        1
465    }
466
467    /// Gets the size of this page in bytes.
468    pub const fn size(&self) -> usize {
469        PAGE_SIZE
470    }
471
472    /*    /// Gets the dynamically-typed metadata of this frame.
473    ///
474    /// If the type is known at compile time, use [`Frame::meta`] instead.
475    pub fn dyn_meta(&self) -> FrameMeta {
476        // SAFETY: The metadata is initialized and valid.
477        unsafe { &*self.slot().dyn_meta_ptr() }
478    }*/
479    /// Gets the reference count of the frame.
480    ///
481    /// It returns the number of all references to the frame, including all the
482    /// existing frame handles ([`Frame`], [`Frame<dyn AnyFrameMeta>`]), and all
483    /// the mappings in the page table that points to the frame.
484    ///
485    /// # Verified Properties
486    /// ## Preconditions
487    /// - **Safety Invariant**: Metaslot region invariants must hold.
488    /// - **Bookkeeping**: The caller must have a valid and well-typed permission for the frame.
489    /// ## Postconditions
490    /// - **Correctness**: The function returns the reference count of the frame.
491    /// ## Safety
492    /// - The function is safe to call, but using it requires extra care. The
493    /// reference count can be changed by other threads at any time including
494    /// potentially between calling this method and acting on the result.
495    #[verus_spec(
496        with
497            Tracked(slot_own): Tracked<&mut MetaSlotOwner>,
498            Tracked(perm) : Tracked<&PointsTo<MetaSlot, Metadata<M>>>,
499        requires
500            perm.addr() == self.ptr.addr(),
501            perm.points_to.pptr() == self.ptr,
502            perm.is_init(),
503            perm.wf(&perm.inner_perms),
504            perm.inner_perms.ref_count.id() == perm.points_to.value().ref_count.id(),
505        returns
506            perm.value().ref_count,
507    )]
508    pub fn reference_count(&self) -> u64 {
509        let refcnt = (#[verus_spec(with Tracked(&perm.points_to))]
510        self.slot()).ref_count.load(Tracked(&perm.inner_perms.ref_count));
511        refcnt
512    }
513
514    /// Borrows a reference from the given frame.
515    /// # Verified Properties
516    /// ## Preconditions
517    /// - **Safety Invariant**: Metaslot region invariants must hold.
518    /// ## Postconditions
519    /// - **Safety Invariant**: Metaslot region invariants hold after the call.
520    /// - **Correctness**: The function returns a reference to the frame.
521    /// - **Correctness**: The system context is unchanged.
522    #[verus_spec(res =>
523        with
524            Tracked(regions): Tracked<&mut MetaRegionOwners>,
525        requires
526            self.inv_with_regions(*old(regions)),
527        ensures
528            final(regions).inv(),
529            res.inner@.ptr.addr() == self.ptr.addr(),
530            final(regions).slot_owners =~= old(regions).slot_owners,
531            final(regions).slots =~= old(regions).slots,
532            final(regions).frame_obligations =~= old(regions).frame_obligations,
533    )]
534    pub fn borrow(&self) -> FrameRef<'a, M> {
535        proof {
536            // The slot perm is canonical in `regions.slots`; `inv_with_regions` already
537            // pins its presence and that `slots[idx].pptr() == self.ptr`, so the
538            // caller no longer threads a separate `MetaPerm`.
539            broadcast use crate::mm::frame::meta::mapping::group_page_meta;
540
541            regions.inv_implies_correct_addr(self.paddr());
542            assert(regions.slots.contains_key(self.index()));
543            assert(regions.slots[self.index()].addr() == self.ptr.addr());
544        }
545        let tracked slot_perm = regions.slots.tracked_borrow(self.index());
546
547        // SAFETY: Both the lifetime and the type matches `self`.
548        unsafe {
549            #[verus_spec(with Tracked(regions))]
550            FrameRef::borrow_paddr(
551                #[verus_spec(with Tracked(slot_perm))]
552                self.start_paddr(),
553            )
554        }
555    }
556
557    /// Forgets the handle to the frame.
558    ///
559    /// This will result in the frame being leaked without calling the custom dropper.
560    ///
561    /// A physical address to the frame is returned in case the frame needs to be
562    /// restored using [`Frame::from_raw`] later. This is useful when some architectural
563    /// data structures need to hold the frame handle such as the page table.
564    ///
565    /// # Verified Properties
566    /// ## Preconditions
567    /// - **Safety Invariant**: Metaslot region invariants must hold.
568    /// - **Safety**: The frame must be in use (not unused).
569    /// ## Postconditions
570    /// - **Safety Invariant**: Metaslot region invariants hold after the call.
571    /// - **Correctness**: The function returns the physical address of the frame.
572    /// - **Correctness**: The frame's raw count is incremented.
573    /// - **Safety**: Frames other than this one are not affected by the call.
574    /// ## Safety
575    /// - We require the slot to be in use to ensure that a fresh frame handle will not be created until the raw frame is restored.
576    /// - The owner's raw count is incremented so that we can enforce the safety requirement on `Frame::from_raw`.
577    #[verus_spec(r =>
578        with
579            Tracked(regions): Tracked<&mut MetaRegionOwners>,
580        requires
581            old(regions).inv(),
582            old(regions).slots.contains_key(self.index()),
583            self.inv(),
584            old(regions).slot_owners[self.index()].inner_perms.ref_count.value() != REF_COUNT_UNUSED,
585            old(regions).slot_owners[self.index()].usage
586                != crate::specs::mm::frame::meta_owners::PageUsage::PageTable,
587            old(regions).frame_obligations.count(self.index()) > 0,
588        ensures
589            final(regions).inv(),
590            r == self.paddr(),
591            final(regions).slot_owners[self.index()].usage
592                == old(regions).slot_owners[self.index()].usage,
593            self.into_raw_post_noninterference(*old(regions), *final(regions)),
594            final(regions).slots == old(regions).slots,
595            final(regions).frame_obligations =~= old(regions).frame_obligations.remove(self.index()),
596    )]
597    pub(in crate::mm) fn into_raw(self) -> Paddr {
598        broadcast use crate::mm::frame::meta::mapping::group_page_meta;
599
600        let tracked perm = regions.slots.tracked_borrow(self.index());
601
602        assert(perm.addr() == self.ptr.addr()) by {
603            assert(frame_to_meta(meta_to_frame(self.ptr.addr())) == self.ptr.addr());
604        };
605
606        #[verus_spec(with Tracked(perm))]
607        let paddr = self.start_paddr();
608
609        let _ = ManuallyDrop::new(self, Tracked(regions));
610
611        paddr
612    }
613
614    /// Gets the metadata slot of the frame.
615    ///
616    /// # Verified Properties
617    /// ## Preconditions
618    /// - **Safety**: The caller must have a valid permission for the frame.
619    /// ## Postconditions
620    /// - **Correctness**: The function returns a reference to the metadata slot of the frame.
621    /// ## Safety
622    /// - There is no way to mutably borrow the metadata slot, so taking an immutable reference is safe.
623    /// (The fields of the slot can be mutably borrowed, but not the slot itself.)
624    #[verus_spec(slot =>
625        with
626            Tracked(slot_perm): Tracked<&'a vstd::simple_pptr::PointsTo<MetaSlot>>,
627        requires
628            slot_perm.pptr() == self.ptr,
629            slot_perm.is_init(),
630        returns
631            slot_perm.value(),
632    )]
633    pub fn slot(&self) -> &'a MetaSlot {
634        // SAFETY: `ptr` points to a valid `MetaSlot` that will never be
635        // mutably borrowed, so taking an immutable reference to it is safe.
636        self.ptr.borrow(Tracked(slot_perm))
637    }
638}
639
640#[verus_verify]
641impl<M: AnyFrameMeta + Repr<MetaSlotStorage>> RCClone for Frame<M> {
642    open spec fn clone_requires(self, perm: MetaRegionOwners) -> bool {
643        let idx = frame_to_index(meta_to_frame(self.ptr.addr()));
644        &&& self.inv()
645        &&& perm.inv()
646        &&& perm.slots.contains_key(idx)
647        &&& perm.slot_owners.contains_key(idx)
648        &&& perm.slot_owners[idx].inner_perms.ref_count.value() > 0
649        &&& perm.slot_owners[idx].inner_perms.ref_count.value()
650            != meta::REF_COUNT_UNUSED
651        // Saturation aborts (Arc-style) via `inc_ref_count`'s diverging panic.
652        &&& perm.slot_owners[idx].inner_perms.ref_count.value() >= meta::REF_COUNT_MAX
653            ==> may_panic()
654        &&& has_safe_slot(meta_to_frame(self.ptr.addr()))
655    }
656
657    open spec fn clone_ensures(
658        self,
659        old_perm: MetaRegionOwners,
660        new_perm: MetaRegionOwners,
661        res: Self,
662    ) -> bool {
663        let idx = frame_to_index(meta_to_frame(self.ptr.addr()));
664        &&& new_perm.inv()
665        // ref_count incremented
666        &&& new_perm.slot_owners[idx].inner_perms.ref_count.value()
667            == old_perm.slot_owners[idx].inner_perms.ref_count.value() + 1
668        &&& new_perm.slot_owners[idx].inner_perms.ref_count.id()
669            == old_perm.slot_owners[idx].inner_perms.ref_count.id()
670        // All other fields at idx unchanged
671        &&& new_perm.slot_owners[idx].inner_perms.storage
672            == old_perm.slot_owners[idx].inner_perms.storage
673        &&& new_perm.slot_owners[idx].inner_perms.vtable_ptr
674            == old_perm.slot_owners[idx].inner_perms.vtable_ptr
675        &&& new_perm.slot_owners[idx].inner_perms.in_list
676            == old_perm.slot_owners[idx].inner_perms.in_list
677        &&& new_perm.slot_owners[idx].paths_in_pt == old_perm.slot_owners[idx].paths_in_pt
678        &&& new_perm.slot_owners[idx].self_addr == old_perm.slot_owners[idx].self_addr
679        &&& new_perm.slot_owners[idx].usage
680            == old_perm.slot_owners[idx].usage
681        // Other slot_owners unchanged
682        &&& new_perm.slots =~= old_perm.slots
683        &&& forall|i: usize|
684            i != idx ==> (#[trigger] new_perm.slot_owners[i] == old_perm.slot_owners[i])
685        &&& new_perm.slot_owners.dom() =~= old_perm.slot_owners.dom()
686        &&& new_perm.frame_obligations =~= old_perm.frame_obligations.insert(idx)
687    }
688
689    fn clone(&self, Tracked(perm): Tracked<&mut MetaRegionOwners>) -> Self {
690        let paddr = meta_to_frame(self.ptr.addr());
691        let ghost idx = frame_to_index(meta_to_frame(self.ptr.addr()));
692
693        unsafe {
694            #[verus_spec(with Tracked(perm))]
695            inc_frame_ref_count(paddr)
696        };
697
698        proof {
699            // Mint the pending-Drop obligation for the freshly cloned live
700            // value; `inc_frame_ref_count` left `frame_obligations` intact.
701            let tracked _ = perm.tracked_mint_frame_obligation(idx);
702        }
703
704        Self { ptr: PPtr::<MetaSlot>::from_addr(self.ptr.0), _marker: PhantomData }
705    }
706}
707
708impl<M: ?Sized> Drop for Frame<M> {
709    fn drop(
710        self,
711        Tracked(regions): Tracked<&mut MetaRegionOwners>,
712        Tracked(obl): Tracked<DropObligation<usize>>,
713    ) {
714        // Single redeem path: route through `consume_obligation` before
715        // running the destructor body. For Frame's current
716        // ledger-less `Key = usize`, this is a no-op on state; for
717        // future ledger-enforcing variants, this is where the ledger
718        // entry is removed.
719        proof {
720            self.consume_obligation(regions, obl);
721        }
722        let ghost idx = frame_to_index(meta_to_frame(self.ptr.addr()));
723        let ghost old_regions = *regions;
724
725        let tracked mut slot_own = regions.slot_owners.tracked_remove(idx);
726        // Design B: a shared `Frame` is Arc-like; its `drop` only adjusts
727        // the refcount. The slot permission is *borrowed* from
728        // `regions.slots`, never moved out and back.
729        let tracked perm = regions.slots.tracked_borrow(idx);
730        let slot = self.ptr.borrow(Tracked(perm));
731
732        // Snapshot of the slot's pre-drop state for the strengthened
733        // `drop_ensures` (refcount transition + identity preservation).
734        let ghost so0 = slot_own;
735
736        proof {
737            assert(slot.ref_count.id() == slot_own.inner_perms.ref_count.id());
738        }
739        let last_ref_cnt = slot.ref_count.fetch_sub(
740            Tracked(&mut slot_own.inner_perms.ref_count),
741            1,
742        );
743        // `fetch_sub` returns the pre-decrement value and only mutates
744        // the `ref_count` permission — the other `MetaSlotOwner` fields
745        // are untouched here.
746        proof {
747            assert(last_ref_cnt == so0.inner_perms.ref_count.value());
748            assert(slot_own.self_addr == so0.self_addr);
749            assert(slot_own.usage == so0.usage);
750            assert(slot_own.paths_in_pt == so0.paths_in_pt);
751        }
752
753        if last_ref_cnt == 1 {
754            // A fence is needed here with the same reasons stated in the implementation of
755            // `Arc::drop`: <https://doc.rust-lang.org/std/sync/struct.Arc.html#method.drop>.
756            acquire_fence();
757
758            proof {
759                // Teardown reclaims the last reference and any dormant
760                // forgotten references: zero `raw_count` so the resulting
761                // `UNUSED` slot satisfies `MetaSlotOwner::inv`
762                assert(slot_own.inner_perms.ref_count.value() == 0u64);
763                assert(slot_own.inner_perms.storage.is_init());
764                assert(slot_own.inner_perms.in_list.value() == 0u64);
765                assert(slot_own.inv());
766                assert(MetaSlot::drop_last_in_place_safety_cond(slot_own));
767                assert(slot.ref_count.id() == slot_own.inner_perms.ref_count.id());
768            }
769            unsafe {
770                #[verus_spec(with Tracked(&mut slot_own))]
771                slot.drop_last_in_place()
772            };
773
774            proof {
775                // last-ref teardown: slot is UNUSED, identity preserved
776                // (drop_last_in_place ensures self_addr/usage/paths_in_pt).
777                assert(so0.inner_perms.ref_count.value() == 1);
778                assert(slot_own.inner_perms.ref_count.value() == REF_COUNT_UNUSED);
779                assert(slot_own.self_addr == so0.self_addr);
780                assert(slot_own.usage == so0.usage);
781                assert(slot_own.paths_in_pt == so0.paths_in_pt);
782            }
783
784            // TODO: return page to allocator
785            // allocator::get_global_frame_allocator().dealloc(paddr, PAGE_SIZE);
786        } else {
787            proof {
788                assert(last_ref_cnt > 1);
789                assert(slot_own.inner_perms.ref_count.value() == last_ref_cnt - 1);
790                assert(slot_own.inner_perms.vtable_ptr.is_init());
791                assert(slot_own.inv());
792                // Decrement branch: refcount = old - 1, identity preserved.
793                assert(so0.inner_perms.ref_count.value() > 1);
794                assert(slot_own.inner_perms.ref_count.value() == (so0.inner_perms.ref_count.value()
795                    - 1) as u64);
796                assert(slot_own.self_addr == so0.self_addr);
797                assert(slot_own.usage == so0.usage);
798                assert(slot_own.paths_in_pt == so0.paths_in_pt);
799            }
800        }
801
802        proof {
803            regions.slot_owners.tracked_insert(idx, slot_own);
804
805            assert forall|i: usize| i != idx implies #[trigger] regions.slot_owners[i]
806                == old_regions.slot_owners[i] by {}
807            assert(regions.slots =~= old_regions.slots);
808            assert(regions.slot_owners.dom() =~= old_regions.slot_owners.dom());
809
810            // Strengthened `drop_ensures`: `so0` is exactly the
811            // pre-drop owner at `idx`, and `regions.slot_owners[idx]`
812            // is now `slot_own` (the post-drop owner).
813            assert(so0 == old_regions.slot_owners[idx]);
814            assert(regions.slot_owners[idx] == slot_own);
815            assert(regions.slot_owners[idx].self_addr == old_regions.slot_owners[idx].self_addr);
816            assert(regions.slot_owners[idx].usage == old_regions.slot_owners[idx].usage);
817            assert(regions.slot_owners[idx].paths_in_pt
818                == old_regions.slot_owners[idx].paths_in_pt);
819            assert(old_regions.slot_owners[idx].inner_perms.ref_count.value() == 1
820                ==> regions.slot_owners[idx].inner_perms.ref_count.value() == REF_COUNT_UNUSED);
821            assert(old_regions.slot_owners[idx].inner_perms.ref_count.value() > 1
822                ==> regions.slot_owners[idx].inner_perms.ref_count.value() == (
823            old_regions.slot_owners[idx].inner_perms.ref_count.value() - 1) as u64);
824
825            // Re-establish `regions.inv()` for the post-state. The
826            // tracked_insert at `idx` only touches that one entry; for other
827            // indices, the invariant carries over from `old_regions.inv()`.
828            // For `idx`, `slot_own.inv()` and the perm/slot agreement at
829            // `idx` are already asserted above.
830            assert(regions.slots.dom().finite());
831
832            assert forall|i: usize|
833                i < crate::mm::frame::meta::mapping::max_meta_slots()
834                    <==> #[trigger] regions.slot_owners.contains_key(i) by {}
835
836            assert forall|i: usize| #[trigger] regions.slots.contains_key(i) implies i
837                < crate::mm::frame::meta::mapping::max_meta_slots() by {
838                if i == idx {
839                    assert(regions.slot_owners.contains_key(idx));
840                }
841            }
842
843            assert forall|i: usize| #[trigger] regions.slots.contains_key(i) implies ({
844                &&& regions.slot_owners.contains_key(i)
845                &&& regions.slot_owners[i].inv()
846                &&& regions.slots[i].is_init()
847                &&& regions.slots[i].addr() == meta_addr(i)
848                &&& regions.slots[i].value().wf(regions.slot_owners[i])
849                &&& regions.slot_owners[i].self_addr == regions.slots[i].addr()
850            }) by {
851                if i == idx {
852                    assert(regions.slots[i].is_init());
853                    assert(regions.slots[i].addr() == meta_addr(i));
854                    assert(regions.slots[i].value().wf(regions.slot_owners[i]));
855                    assert(regions.slot_owners[i].self_addr == regions.slots[i].addr());
856                }
857            }
858
859            assert forall|i: usize| #[trigger]
860                regions.slot_owners.contains_key(i) implies regions.slot_owners[i].inv() by {
861                if i == idx {
862                    assert(slot_own.inv());
863                }
864            }
865        }
866    }
867}
868
869/*
870
871impl<M: AnyFrameMeta> TryFrom<Frame<dyn AnyFrameMeta>> for Frame<M> {
872    type Error = Frame<dyn AnyFrameMeta>;
873
874    /// Tries converting a [`Frame<dyn AnyFrameMeta>`] into the statically-typed [`Frame`].
875    ///
876    /// If the usage of the frame is not the same as the expected usage, it will
877    /// return the dynamic frame itself as is.
878    fn try_from(dyn_frame: Frame<dyn AnyFrameMeta>) -> Result<Self, Self::Error> {
879        if (dyn_frame.dyn_meta() as &dyn core::any::Any).is::<M>() {
880            // SAFETY: The metadata is coerceable and the struct is transmutable.
881            Ok(unsafe { core::mem::transmute::<Frame<dyn AnyFrameMeta>, Frame<M>>(dyn_frame) })
882        } else {
883            Err(dyn_frame)
884        }
885    }
886}*/
887
888/*impl<M: AnyFrameMeta> From<UFrame> for Frame<M> {
889    fn from(frame: UFrame) -> Self {
890        // SAFETY: The metadata is coerceable and the struct is transmutable.
891        unsafe { core::mem::transmute(frame) }
892    }
893}*/
894
895/*impl TryFrom<Frame<FrameMeta>> for UFrame {
896    type Error = Frame<FrameMeta>;
897}*/
898
899#[verifier::external]
900impl<M: AnyUFrameMeta> From<Frame<M>> for UFrame {
901    fn from(frame: Frame<M>) -> Self {
902        // SAFETY: The metadata is coerceable and the struct is transmutable.
903        unsafe { core::mem::transmute(frame) }
904    }
905}
906
907/*
908impl From<UFrame> for Frame<dyn AnyFrameMeta> {
909    fn from(frame: UFrame) -> Self {
910        // SAFETY: The metadata is coerceable and the struct is transmutable.
911        unsafe { core::mem::transmute(frame) }
912    }
913}
914
915impl TryFrom<Frame<dyn AnyFrameMeta>> for UFrame {
916    type Error = Frame<dyn AnyFrameMeta>;
917
918    /// Tries converting a [`Frame<dyn AnyFrameMeta>`] into [`UFrame`].
919    ///
920    /// If the usage of the frame is not the same as the expected usage, it will
921    /// return the dynamic frame itself as is.
922    fn try_from(dyn_frame: Frame<dyn AnyFrameMeta>) -> Result<Self, Self::Error> {
923        if dyn_frame.dyn_meta().is_untyped() {
924            // SAFETY: The metadata is coerceable and the struct is transmutable.
925            Ok(unsafe { core::mem::transmute::<Frame<dyn AnyFrameMeta>, UFrame>(dyn_frame) })
926        } else {
927            Err(dyn_frame)
928        }
929    }
930}*/
931
932/// Increases the reference count of the frame by one.
933///
934/// # Verified Properties
935/// ## Preconditions
936/// - **Safety Invariant**: Metaslot region invariants must hold.
937/// - **Safety**: The physical address must represent a valid frame.
938/// ## Postconditions
939/// - **Safety Invariant**: Metaslot region invariants hold after the call.
940/// - **Correctness**: The reference count of the frame is increased by one.
941/// - **Safety**: Frames other than this one are not affected by the call.
942/// ## Safety
943/// We enforce the safety requirements that `paddr` represents a valid frame and the caller has already held a reference to the it.
944/// It is safe to require these as preconditions because the function is internal, so the caller must obey the preconditions.
945#[verus_spec(
946    with
947        Tracked(regions): Tracked<&mut MetaRegionOwners>,
948    requires
949        old(regions).inv(),
950        old(regions).slots.contains_key(frame_to_index(paddr)),
951        has_safe_slot(paddr),
952        // The caller holds a reference, so rc > 0, and the slot must be live
953        // (not the UNUSED sentinel). Saturation is caught at runtime by
954        // `inc_ref_count`'s Arc-style abort.
955        old(regions).slot_owners[frame_to_index(paddr)].inner_perms.ref_count.value() > 0,
956        old(regions).slot_owners[frame_to_index(paddr)].inner_perms.ref_count.value()
957            != REF_COUNT_UNUSED,
958        old(regions).slot_owners[frame_to_index(paddr)].inner_perms.ref_count.value()
959            >= REF_COUNT_MAX ==> may_panic(),
960    ensures
961        final(regions).inv(),
962        final(regions).slot_owners[frame_to_index(paddr)].inner_perms.ref_count.value() == old(
963            regions,
964        ).slot_owners[frame_to_index(paddr)].inner_perms.ref_count.value() + 1,
965        final(regions).slot_owners[frame_to_index(paddr)].inner_perms.ref_count.id() == old(
966            regions,
967        ).slot_owners[frame_to_index(paddr)].inner_perms.ref_count.id(),
968        final(regions).slot_owners[frame_to_index(paddr)].inner_perms.storage == old(
969            regions,
970        ).slot_owners[frame_to_index(paddr)].inner_perms.storage,
971        final(regions).slot_owners[frame_to_index(paddr)].inner_perms.vtable_ptr == old(
972            regions,
973        ).slot_owners[frame_to_index(paddr)].inner_perms.vtable_ptr,
974        final(regions).slot_owners[frame_to_index(paddr)].inner_perms.in_list == old(
975            regions,
976        ).slot_owners[frame_to_index(paddr)].inner_perms.in_list,
977        final(regions).slot_owners[frame_to_index(paddr)].paths_in_pt == old(
978            regions,
979        ).slot_owners[frame_to_index(paddr)].paths_in_pt,
980        final(regions).slot_owners[frame_to_index(paddr)].self_addr == old(
981            regions,
982        ).slot_owners[frame_to_index(paddr)].self_addr,
983        final(regions).slot_owners[frame_to_index(paddr)].usage == old(
984            regions,
985        ).slot_owners[frame_to_index(paddr)].usage,
986        final(regions).slots =~= old(regions).slots,
987        forall|i: usize|
988            i != frame_to_index(paddr) ==> (#[trigger] final(regions).slot_owners[i] == old(
989                regions,
990            ).slot_owners[i]),
991        final(regions).slot_owners.dom() =~= old(regions).slot_owners.dom(),
992        // Linear-drop pilot: refcount bump doesn't touch segment or frame
993        // obligation ledgers.
994        final(regions).frame_obligations =~= old(regions).frame_obligations,
995)]
996pub(in crate::mm) unsafe fn inc_frame_ref_count(paddr: Paddr) {
997    let tracked mut slot_own = regions.slot_owners.tracked_remove(frame_to_index(paddr));
998    let tracked perm = regions.slots.tracked_borrow(frame_to_index(paddr));
999    let tracked mut inner_perms = slot_own.take_inner_perms();
1000
1001    let vaddr: Vaddr = frame_to_meta(paddr);
1002    // SAFETY: `vaddr` points to a valid `MetaSlot` that will never be mutably borrowed, so taking
1003    // an immutable reference to it is always safe.
1004    let slot = PPtr::<MetaSlot>::from_addr(vaddr);
1005
1006    unsafe {
1007        #[verus_spec(with Tracked(&mut inner_perms.ref_count))]
1008        slot.borrow(Tracked(perm)).inc_ref_count()
1009    };
1010
1011    proof {
1012        let idx = frame_to_index(paddr);
1013
1014        // inc_ref_count preserves permission id
1015        assert(inner_perms.ref_count.id() == old(
1016            regions,
1017        ).slot_owners[idx].inner_perms.ref_count.id());
1018
1019        // sync_inner: slot_own.inner_perms = inner_perms, other fields unchanged
1020        slot_own.sync_inner(&inner_perms);
1021
1022        // slot_own.inv() holds: rc in (0, REF_COUNT_MAX), vtable_ptr init, self_addr ok
1023        assert(slot_own.inv());
1024
1025        // wf: the slot's cell ids still match the (updated) inner_perms ids
1026        assert(regions.slots[idx].value().wf(slot_own));
1027
1028        regions.slot_owners.tracked_insert(idx, slot_own);
1029    }
1030}
1031
1032/// A dynamically-typed frame is represented by a frame of the underlying metadata type,
1033/// which can be cast from any other type.
1034pub type DynFrame = Frame<MetaSlotStorage>;
1035
1036impl<M: AnyFrameMeta + Repr<MetaSlotStorage> + 'static> Frame<M> {
1037    /// Erases the static metadata type, yielding a `Frame<dyn AnyFrameMeta>`.
1038    ///
1039    /// Inherent method rather than `From`/`Into` to avoid trait-inference
1040    /// ambiguity at call sites that previously relied on the blanket
1041    /// `From<T> for T` (e.g. `frame.into()` for `Frame<UFrame>`).
1042    ///
1043    /// Axiomatized (`external_body`) because the body is `transmute`, which
1044    /// Verus has no built-in spec for.
1045    #[verifier::external_body]
1046    pub fn into_dyn(self) -> Frame<dyn AnyFrameMeta> {
1047        // SAFETY: `Frame<M>` is `#[repr(transparent)]` over `PPtr<MetaSlot>`
1048        // plus a zero-size `PhantomData<M>`. `Frame<dyn AnyFrameMeta>` has
1049        // the same runtime layout (thin pointer + ZST phantom).
1050        unsafe { core::mem::transmute(self) }
1051    }
1052}
1053
1054} // verus!