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.
32use vstd::{
33    assert_maps_equal, assert_sets_equal,
34    atomic::PermissionU64,
35    map::assert_maps_equal_internal,
36    prelude::*,
37    simple_pptr::{self, PPtr},
38    std_specs::cmp::PartialEqSpecImpl,
39};
40use vstd_extra::{cast_ptr::*, ownership::*, panic::may_panic};
41
42pub mod allocator;
43pub mod linked_list;
44pub mod meta;
45pub mod segment;
46pub mod unique;
47pub mod untyped;
48
49mod frame_ref;
50pub use frame_ref::FrameRef;
51
52#[cfg(ktest)]
53mod test;
54
55use crate::specs::{
56    arch::*,
57    mm::frame::{frame_specs::*, mapping::*, meta_owners::*, meta_region_owners::MetaRegionOwners},
58};
59
60use core::{
61    marker::PhantomData,
62    mem::ManuallyDrop,
63    sync::atomic::{AtomicUsize, Ordering},
64};
65
66//pub use allocator::GlobalFrameAllocator;
67use super::PagingLevel;
68use crate::mm::kspace::FRAME_METADATA_RANGE;
69use crate::mm::page_table::RCClone;
70use crate::mm::page_table::{PageTableConfig, PageTablePageMeta};
71use crate::mm::{
72    MAX_PADDR, Paddr, Vaddr,
73    frame::meta::{
74        META_SLOT_SIZE,
75        mapping::{frame_to_meta, meta_to_frame},
76    },
77    kspace::{LINEAR_MAPPING_BASE_VADDR, VMALLOC_BASE_VADDR},
78};
79pub use linked_list::{CursorMut, Link, LinkedList};
80pub use meta::{AnyFrameMeta, GetFrameError, MetaSlot};
81use meta::{REF_COUNT_MAX, REF_COUNT_UNIQUE, REF_COUNT_UNUSED, mapping};
82pub use segment::Segment;
83pub use unique::UniqueFrame;
84pub use untyped::{AnyUFrameMeta, UFrame};
85
86verus! {
87
88/*
89static MAX_PADDR: AtomicUsize = AtomicUsize::new(0);
90*/
91/// Returns the maximum physical address that is tracked by frame metadata.
92#[verifier::external_body]
93pub(in crate::mm) fn max_paddr() -> Paddr
94    returns
95        MAX_PADDR,
96{
97    // let max_paddr = MAX_PADDR.load(Ordering::Relaxed) as Paddr;
98    // debug_assert_ne!(max_paddr, 0);
99    // max_paddr
100    unimplemented!()
101}
102
103#[verifier::external_body]
104fn acquire_fence() {
105    core::sync::atomic::fence(Ordering::Acquire);
106}
107
108/// A smart pointer to a frame.
109///
110/// A frame is a contiguous range of bytes in physical memory. The [`Frame`]
111/// type is a smart pointer to a frame that is reference-counted.
112///
113/// Frames are associated with metadata. The type of the metadata `M` is
114/// determines the kind of the frame. If `M` implements [`AnyUFrameMeta`], the
115/// frame is a untyped frame. Otherwise, it is a typed frame.
116// #[repr(transparent)]
117pub struct Frame<M: ?Sized> {
118    pub ptr: PPtr<MetaSlot>,
119    pub _marker: PhantomData<M>,
120    /// The permission to access the `MetaSlot` fields.
121    #[cfg(verus_keep_ghost_body)]
122    pub tracked_slot_perm: Tracked<&'static simple_pptr::PointsTo<MetaSlot>>,
123    /// One fractional permission for the currently installed metadata.
124    #[cfg(verus_keep_ghost_body)]
125    pub tracked_metadata_perm: Tracked<Option<FracMetadataPerm>>,
126}
127
128#[verifier::external]
129unsafe impl<M: AnyFrameMeta + ?Sized> Send for Frame<M> {
130
131}
132
133#[verifier::external]
134unsafe impl<M: AnyFrameMeta + ?Sized> Sync for Frame<M> {
135
136}
137
138/*
139impl<M: AnyFrameMeta + ?Sized> core::fmt::Debug for Frame<M> {
140    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
141        write!(f, "Frame({:#x})", self.start_paddr())
142    }
143}
144*/
145
146verus!{
147
148impl<M: AnyFrameMeta + Repr<MetaSlotStorage> + ?Sized> PartialEqSpecImpl for Frame<M>{
149    open spec fn obeys_eq_spec() -> bool { true }
150
151    open spec fn eq_spec(&self, other: &Self) -> bool {
152        self.start_paddr_spec() == other.start_paddr_spec()
153    }
154}
155
156}
157
158#[verus_verify]
159impl<M: AnyFrameMeta + Repr<MetaSlotStorage> + ?Sized> PartialEq for Frame<M> {
160    fn eq(&self, other: &Self) -> bool {
161        proof!{
162            //FIXME: Add `ptr_inv` as type invariant when we fix visibility.
163            assume(self.ptr_inv());
164            assume(other.ptr_inv());
165        }
166        self.start_paddr() == other.start_paddr()
167    }
168}
169
170#[verus_verify]
171impl<M: AnyFrameMeta + Repr<MetaSlotStorage> + ?Sized> Eq for Frame<M> {
172
173}
174
175#[verus_verify]
176impl<M: AnyFrameMeta + Repr<MetaSlotStorage> + OwnerOf> Frame<M> {
177    /// Gets a [`Frame`] with a specific usage from a raw, unused page.
178    ///
179    /// The caller should provide the initial metadata of the page.
180    ///
181    /// If the provided frame is not truly unused at the moment, it will return
182    /// an error. If wanting to acquire a frame that is already in use, use
183    /// [`Frame::from_in_use`] instead.
184    /// # Verified Properties
185    /// ## Preconditions
186    /// - **Safety Invariant**: Metaslot region invariants must hold.
187    /// ## Postconditions
188    /// - **Safety Invariant**: Metaslot region invariants hold after the call.
189    /// - **Correctness**: If successful, the function returns a pointer to the metadata slot and a permission to the slot.
190    /// - **Correctness**: If successful, the slot is initialized with the given metadata.
191    /// - **Correctness**: If `paddr` does not have a corresponding metadata slot, the function returns an error.
192    /// - **Drop Bookkeeping**: If successful, the function returns a live frame, which is tracked correctly as needing to be dropped.
193    /// ## Safety
194    /// - This function returns an error if `paddr` does not correspond to a valid slot or the slot is in use.
195    #[verus_spec(r =>
196        with
197            Tracked(regions): Tracked<&mut MetaRegionOwners>,
198            Tracked(repr_perm): Tracked<&mut M::ReprPerm>
199        requires
200            old(regions).inv(),
201        ensures
202            final(regions).inv(),
203            r matches Ok(res) ==> {
204                &&& MetaSlot::get_from_unused_region_spec(
205                    paddr, false, res.metadata_perm().usage(),
206                    *old(regions), *final(regions))
207                &&& typed_meta_wf::<M>(res.slot_perm(), res.metadata_perm(), *final(repr_perm))
208                &&& M::from_repr_spec(res.metadata_perm().storage_perm.value(), *final(repr_perm)) == metadata
209                &&& res.inv()
210                &&& res.start_paddr_spec() == paddr
211                &&& res.wf_with_region(*final(regions))
212            },
213            r is Err ==> *final(regions) == *old(regions)
214    )]
215    pub fn from_unused(paddr: Paddr, metadata: M) -> Result<Self, GetFrameError> {
216        #[verus_spec(with
217            Tracked(regions),
218            Tracked(repr_perm) => Tracked(permissions)
219        )]
220        let from_unused = MetaSlot::get_from_unused(paddr, metadata, false);
221        if let Err(err) = from_unused {
222            Err(err)
223        } else {
224            proof_decl! {
225                lemma_paddr_to_meta_biinjective(paddr);
226                lemma_frame_to_meta_soundness(paddr);
227                let tracked slot_perm = regions.tracked_borrow_slot(paddr);
228                let ghost idx = frame_to_index(paddr);
229                assert(regions.slot_owners.contains_key(idx));
230                let tracked metadata_perm = permissions.tracked_unwrap().tracked_take_left();
231            }
232            let ptr = from_unused.unwrap();
233            Ok(
234                Self {
235                    ptr,
236                    _marker: PhantomData,
237                    #[cfg(verus_keep_ghost_body)]
238                    tracked_slot_perm: Tracked(slot_perm),
239                    #[cfg(verus_keep_ghost_body)]
240                    tracked_metadata_perm: Tracked(Some(metadata_perm)),
241                },
242            )
243        }
244    }
245
246    /// Gets the metadata of this page.
247    /// # Verified Properties
248    /// ## Preconditions
249    /// - The caller must have a valid permission for the frame.
250    /// ## Postconditions
251    /// - The function returns the borrowed metadata of the frame.
252    /// ## Safety
253    /// - By requiring the caller to provide a typed permission, we ensure that the metadata is of type `M`.
254    /// While a non-verified caller cannot be trusted to obey this interface, all functions that return a `Frame<M>` also
255    /// return an appropriate permission.
256    #[verus_spec(ret =>
257        with
258            Tracked(metadata_perm): Tracked<Option<&'a MetadataPerm>>,
259            Tracked(repr_perm): Tracked<&'a M::ReprPerm>,
260        requires
261            {
262                self.inv() && metadata_perm is None
263                && typed_meta_wf::<M>(self.slot_perm(), self.metadata_perm(), *repr_perm) ||
264                metadata_perm is Some
265                && self.external_meta_wf(*metadata_perm->0, *repr_perm)
266            },
267        ensures
268            {
269                let metadata_perm = if metadata_perm is Some {*metadata_perm -> 0} else
270                    { self.metadata_perm() };
271                ret == typed_meta_value::<M>(metadata_perm, *repr_perm)
272            },
273    )]
274    pub fn meta<'a>(&'a self) -> &'a M {
275        // SAFETY: The type is tracked by the typed storage permission.
276        //  unsafe { &*self.slot().as_meta_ptr::<M>() }
277        proof_decl! {
278            let tracked slot_perm = self.tracked_slot_perm.borrow();
279            let tracked metadata_perm = if metadata_perm is Some {
280                metadata_perm.tracked_borrow()
281            } else {
282                self.tracked_metadata_perm.tracked_borrow().tracked_borrow()
283            };
284        }
285        borrow_meta(
286            ReprPtr::<MetaSlotStorage, M>::from_pptr(PPtr::from_addr(self.ptr.addr())),
287            Tracked(slot_perm),
288            Tracked(metadata_perm),
289            Tracked(repr_perm),
290        )
291    }
292}
293
294#[verus_verify]
295impl<M: AnyFrameMeta + Repr<MetaSlotStorage>> Frame<M> {
296    /// Gets a dynamically typed [`Frame`] from a raw, in-use page.
297    ///
298    /// If the provided frame is not in use at the moment, it will return an error.
299    ///
300    /// The returned frame will have an extra reference count to the frame.
301    ///
302    /// # Verified Properties
303    /// ## Preconditions
304    /// - **Safety Invariant**: Metaslot region invariants must hold.
305    /// - *Termination*: The function may panic if `paddr` is a valid slot and its reference count is saturated.
306    /// ## Postconditions
307    /// - **Safety Invariant**: Metaslot region invariants hold after the call.
308    /// - **Correctness**: If successful, the function returns the frame at `paddr`.
309    /// - **Correctness**: If successful, the frame has an extra reference count.
310    /// - **Correctness**: If `paddr` does not have a valid metadata slot, the function returns an error.
311    /// - **Safety**: Frames other than the one at `paddr` are not affected by the call.
312    /// ## Safety
313    /// - If `paddr` is a valid frame address, it is safe to take a reference to the frame.
314    /// - If `paddr` is not a valid frame address, the function will return an error.
315    #[verus_spec(res =>
316        with Tracked(regions) : Tracked<&mut MetaRegionOwners>,
317        requires
318            old(regions).inv(),
319            valid_frame_paddr(paddr) ==> old(regions).ref_count(frame_to_index(paddr)) >= REF_COUNT_MAX ==> may_panic(),
320        ensures
321            final(regions).inv(),
322            res matches Ok(res) ==> {
323                &&& old(regions).inc_frame_reference_region_spec(paddr, *final(regions))
324                &&& res.inv()
325                &&& res.start_paddr_spec() == paddr
326                &&& res.wf_with_region(*final(regions))
327            },
328            res is Err ==> *old(regions) == *final(regions),
329    )]
330    pub fn from_in_use(paddr: Paddr) -> Result<Self, GetFrameError> {
331        proof_decl!{
332            let tracked frame_permission: Option<FracMetadataPerm>;
333        }
334        let res = #[verus_spec(with Tracked(regions) => Tracked(frame_permission))]
335        MetaSlot::get_from_in_use(paddr);
336        match res {
337            Ok(ptr) => {
338                proof {
339                    regions.lemma_contains_valid_frame_paddr(paddr);
340                }
341                let tracked slot_perm = regions.tracked_borrow_slot(paddr);
342                Ok(
343                    Self {
344                        ptr,
345                        _marker: PhantomData,
346                        #[cfg(verus_keep_ghost_body)]
347                        tracked_slot_perm: Tracked(slot_perm),
348                        #[cfg(verus_keep_ghost_body)]
349                        tracked_metadata_perm: Tracked(frame_permission),
350                    },
351                )
352            },
353            Err(e) => Err(e),
354        }
355    }
356}
357
358#[verus_verify]
359impl<M: AnyFrameMeta + Repr<MetaSlotStorage> + ?Sized> Frame<M> {
360    /// Gets the physical address of the start of the frame.
361    /// # Verified Properties
362    /// ## Preconditions
363    /// - **Bookkeeping**: takes the permission for the frame's metadata slot.
364    /// ## Postconditions
365    /// - **Correctness**: returns the physical address of the frame.
366    /// ## Safety
367    /// The caller cannot obtain a frame that doesn't have a valid permission,
368    /// and this function does not mutate any state, so it is always sound to call.
369    #[verus_spec(
370    requires
371        self.ptr_inv(),
372    returns
373        self.start_paddr_spec(),
374    )]
375    pub fn start_paddr(&self) -> Paddr {
376        let slot = self.slot();
377
378        #[verus_spec(with self.tracked_slot_perm)]
379        slot.frame_paddr()
380    }
381
382    /// Gets the map level of this page.
383    ///
384    /// This is the level of the page table entry that maps the frame,
385    /// which determines the size of the frame.
386    ///
387    /// Currently, the level is always 1, which means the frame is a regular
388    /// page frame.
389    pub const fn map_level(&self) -> PagingLevel
390        returns
391            1u8,
392    {
393        1
394    }
395
396    /// Gets the size of this page in bytes.
397    pub const fn size(&self) -> usize
398        returns
399            PAGE_SIZE,
400    {
401        PAGE_SIZE
402    }
403
404    /*    /// Gets the dynamically-typed metadata of this frame.
405    ///
406    /// If the type is known at compile time, use [`Frame::meta`] instead.
407    pub fn dyn_meta(&self) -> FrameMeta {
408        // SAFETY: The metadata is initialized and valid.
409        unsafe { &*self.slot().dyn_meta_ptr() }
410    }*/
411    /// Gets the reference count of the frame.
412    ///
413    /// It returns the number of all references to the frame, including all the
414    /// existing frame handles ([`Frame`], [`Frame<dyn AnyFrameMeta>`]), and all
415    /// the mappings in the page table that points to the frame.
416    ///
417    /// ## Safety
418    ///
419    /// The function is safe to call, but using it requires extra care. The
420    /// reference count can be changed by other threads at any time including
421    /// potentially between calling this method and acting on the result.
422    ///
423    /// # Verified Properties
424    /// ## Preconditions
425    /// - **Safety Invariant**: Metaslot region invariants must hold.
426    /// - **Bookkeeping**: The caller must have a valid and well-typed permission for the frame.
427    /// ## Postconditions
428    /// - **Correctness**: The function returns the reference count of the frame.
429    #[verus_spec(
430        with
431            Tracked(slot_own): Tracked<&MetaSlotOwner>,
432        requires
433            self.ptr_inv(),
434            self.tracked_slot_perm@.value().wf(*slot_own),
435        returns
436            slot_own.ref_count(),
437    )]
438    pub fn reference_count(&self) -> u64 {
439        let refcnt = self.slot().ref_count.load(Tracked(&slot_own.ref_count_perm));
440        refcnt
441    }
442
443    /// Borrows a reference from the given frame.
444    /// # Verified Properties
445    /// ## Preconditions
446    /// - **Safety Invariant**: Metaslot region invariants must hold.
447    /// ## Postconditions
448    /// - **Safety Invariant**: Metaslot region invariants hold after the call.
449    /// - **Correctness**: The function returns a reference to the frame.
450    /// - **Correctness**: The system context is unchanged.
451    #[verus_spec(res =>
452        requires
453            self.inv(),
454        ensures
455            res.inner@.slot_vaddr() == self.slot_vaddr(),
456            res.inner@.ptr_inv(),
457            res.inner@.slot_perm() == self.slot_perm(),
458            res.inner@.tracked_metadata_perm@ is None,
459            MetaSlot::perms_related(res.inner@.slot_perm(), self.metadata_perm()),
460            forall|repr_perm: M::ReprPerm|
461                #[trigger] typed_meta_wf::<M>(self.slot_perm(), self.metadata_perm(), repr_perm)
462                ==> res.inner@.external_meta_wf(self.metadata_perm(), repr_perm),
463    )]
464    pub fn borrow<'a>(&self) -> FrameRef<'a, M> {
465        let tracked slot_perm = *self.tracked_slot_perm.borrow();
466        let tracked metadata_perm = self.tracked_metadata_perm.borrow().tracked_borrow();
467        // SAFETY: Both the lifetime and the type matches `self`.
468        unsafe {
469            #[verus_spec(with Tracked(slot_perm), Tracked(metadata_perm))]
470            FrameRef::borrow_paddr(self.start_paddr())
471        }
472    }
473
474    /// Borrows a frame whose owning handle is represented by an external
475    /// counting permission, as is the case for page-table nodes stored raw in
476    /// PTEs.
477    #[verus_spec(res =>
478        with
479            Tracked(frame_permission): Tracked<&FracMetadataPerm>,
480            Tracked(regions): Tracked<&MetaRegionOwners>,
481        requires
482            self.ptr_inv(),
483            regions.inv(),
484            frame_permission.frac() == 1,
485            frame_permission.id() == regions.slot_owners[self.index()].metadata_perm.id(),
486            MetaSlot::perms_related(
487                *regions.slots[self.index()],
488                frame_permission.resource(),
489            ),
490        ensures
491            res.inner@.slot_vaddr() == self.slot_vaddr(),
492            res.inner@.ptr_inv(),
493            res.inner@.tracked_slot_perm@ == regions.slots[self.index()],
494            res.inner@.tracked_metadata_perm@ is None,
495            MetaSlot::perms_related(res.inner@.slot_perm(), frame_permission.resource()),
496            forall|repr_perm: M::ReprPerm|
497                #[trigger] typed_meta_wf::<M>(self.slot_perm(), frame_permission.resource(), repr_perm)
498                ==> res.inner@.external_meta_wf(frame_permission.resource(), repr_perm),
499    )]
500    pub(in crate::mm) fn borrow_with_permission<'a>(&self) -> FrameRef<'a, M> {
501        unsafe {
502            proof {
503                broadcast use group_page_meta;
504
505                regions.lemma_contains_valid_frame_paddr(self.start_paddr_spec());
506            }
507            let tracked slot_perm = regions.tracked_borrow_slot(self.start_paddr_spec());
508            #[verus_spec(with Tracked(slot_perm), Tracked(frame_permission))]
509            FrameRef::borrow_paddr(self.start_paddr())
510        }
511    }
512
513    /// Forgets the handle to the frame.
514    ///
515    /// This will result in the frame being leaked without calling the custom dropper.
516    ///
517    /// A physical address to the frame is returned in case the frame needs to be
518    /// restored using [`Frame::from_raw`] later. This is useful when some architectural
519    /// data structures need to hold the frame handle such as the page table.
520    ///
521    /// # Verified Properties
522    /// ## Preconditions
523    /// - **Safety**: The frame's invariant must be satisfied.
524    /// ## Postconditions
525    /// - **Correctness**: The function returns the physical address of the frame.
526    /// ## Safety
527    /// - 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.
528    /// - The owner's raw count is incremented so that we can enforce the safety requirement on `Frame::from_raw`.
529    #[verus_spec(r =>
530        with
531            -> raw_permission: Tracked<FrameRawPerms>,
532        requires
533            self.inv(),
534        ensures
535            r == self.start_paddr_spec(),
536            raw_permission@.inv(),
537    )]
538    pub(in crate::mm) fn into_raw(mut self) -> Paddr {
539        broadcast use group_page_meta;
540
541        proof_decl!{
542            let tracked perm = FrameRawPerms {
543                slot_perm: &*self.tracked_slot_perm,
544                metadata_perm: self.tracked_metadata_perm.tracked_take(),
545            };
546        }
547
548        let this = ManuallyDrop::new(self);
549        proof_with!(|= Tracked(perm));
550        this.start_paddr()
551    }
552}
553
554#[verus_verify]
555impl<M: ?Sized> Frame<M> {
556    /// Gets the metadata slot of the frame.
557    ///
558    /// # Verified Properties
559    /// ## Preconditions
560    /// - **Safety**: The caller must have a valid permission for the frame.
561    /// ## Postconditions
562    /// - **Correctness**: The function returns a reference to the metadata slot of the frame.
563    /// ## Safety
564    /// - There is no way to mutably borrow the metadata slot, so taking an immutable reference is safe.
565    /// (The fields of the slot can be mutably borrowed, but not the slot itself.)
566    #[verus_spec(
567        requires
568            self.ptr_inv(),
569        returns
570            self.tracked_slot_perm@.value(),
571    )]
572    pub fn slot<'a>(&'a self) -> &'a MetaSlot {
573        // SAFETY: `ptr` points to a valid `MetaSlot` that will never be
574        // mutably borrowed, so taking an immutable reference to it is safe.
575        proof_decl! {
576            let tracked slot_perm = *self.tracked_slot_perm;
577        }
578        self.ptr.borrow(Tracked(slot_perm))
579    }
580}
581
582#[verus_verify]
583impl<M: AnyFrameMeta + Repr<MetaSlotStorage> + ?Sized> Frame<M> {
584    /// Restores a forgotten [`Frame`] from a physical address.
585    ///
586    /// # Safety
587    ///
588    /// The caller should only restore a `Frame` that was previously forgotten using
589    /// [`Frame::into_raw`].
590    ///
591    /// And the restoring operation should only be done once for a forgotten
592    /// [`Frame`]. Otherwise double-free will happen.
593    ///
594    /// Also, the caller ensures that the usage of the frame is correct. There's
595    /// no checking of the usage in this function.
596    #[verus_spec(r =>
597        with
598            Tracked(perm): Tracked<FrameRawPerms>,
599        requires
600            valid_frame_paddr(paddr),
601            perm.slot_vaddr() == frame_to_meta(paddr),
602            perm.inv(),
603        ensures
604            r.tracked_slot_perm@ == perm.slot_perm,
605            r.tracked_metadata_perm@ == Some(perm.metadata_perm),
606            r.start_paddr_spec() == paddr,
607            r.inv(),
608    )]
609    pub(in crate::mm) unsafe fn from_raw(paddr: Paddr) -> Self
610        no_unwind
611    {
612        // debug_assert!(paddr < max_paddr());
613        let vaddr = frame_to_meta(paddr);
614        // let ptr = vaddr as *const MetaSlot;
615        let ptr = PPtr(vaddr, PhantomData);
616
617        proof_with!{ tracked_slot_perm: Tracked(perm.slot_perm), tracked_metadata_perm: Tracked(Some(perm.metadata_perm)) }
618        Self { ptr, _marker: PhantomData }
619    }
620}
621
622#[verus_verify]
623impl<M: AnyFrameMeta + Repr<MetaSlotStorage>> RCClone for Frame<M> {
624    open spec fn clone_requires(self, regions: MetaRegionOwners) -> bool {
625        let paddr = self.start_paddr_spec();
626        let ref_count = regions.slot_owner(paddr).ref_count();
627        &&& self.inv()
628        &&& regions.inv()
629        &&& self.wf_with_region(regions)
630        &&& ref_count > 0
631        &&& ref_count != REF_COUNT_UNUSED
632        &&& ref_count >= REF_COUNT_MAX ==> may_panic()
633    }
634
635    open spec fn clone_ensures(
636        self,
637        pre: MetaRegionOwners,
638        post: MetaRegionOwners,
639        res: Self,
640    ) -> bool {
641        let idx = self.index();
642        &&& pre.inc_frame_reference_region_spec(self.start_paddr_spec(), post)
643        &&& post.inv()
644        &&& res.tracked_metadata_perm@ is Some
645        &&& res.tracked_metadata_perm@->0.frac() == 1
646        &&& res.tracked_metadata_perm@->0.id() == post.slot_owners[idx].metadata_perm.id()
647        &&& res.tracked_slot_perm@ == post.slots[idx]
648        &&& res.ptr == self.ptr
649    }
650
651    fn clone(&self, Tracked(regions): Tracked<&mut MetaRegionOwners>) -> Self {
652        proof_decl! {
653            regions.lemma_contains_valid_frame_paddr(self.start_paddr_spec());
654            let ghost paddr = self.start_paddr_spec();
655            let tracked slot_perm = regions.tracked_borrow_slot(paddr);
656            let tracked slot_own = regions.tracked_borrow_mut_slot_owner(paddr);
657        }
658
659        // SAFETY: We have already held a reference to the frame.
660        unsafe {
661            #[verus_spec(with Tracked(&mut slot_own.ref_count_perm))]
662            self.slot().inc_ref_count();
663        }
664
665        proof_decl!{
666            let tracked metadata_perm = slot_own.metadata_perm.split_one();
667        };
668
669        Self {
670            ptr: PPtr::<MetaSlot>::from_addr(self.ptr.0),
671            _marker: PhantomData,
672            #[cfg(verus_keep_ghost_body)]
673            tracked_slot_perm: Tracked(slot_perm),
674            #[cfg(verus_keep_ghost_body)]
675            tracked_metadata_perm: Tracked(Some(metadata_perm)),
676        }
677    }
678}
679
680/*
681impl<M: AnyFrameMeta + ?Sized> Drop for Frame<M> {
682    fn drop(&mut self) {
683        let last_ref_cnt = self.slot().ref_count.fetch_sub(1, Ordering::Release);
684        debug_assert!(last_ref_cnt != 0 && last_ref_cnt != REF_COUNT_UNUSED);
685
686        if last_ref_cnt == 1 {
687            // A fence is needed here with the same reasons stated in the implementation of
688            // `Arc::drop`: <https://doc.rust-lang.org/std/sync/struct.Arc.html#method.drop>.
689            core::sync::atomic::fence(Ordering::Acquire);
690
691            // SAFETY: this is the last reference and is about to be dropped.
692            unsafe { self.slot().drop_last_in_place() };
693
694            allocator::get_global_frame_allocator().dealloc(self.start_paddr(), PAGE_SIZE);
695        }
696    }
697}*/
698
699impl<M: ?Sized> Frame<M> {
700    pub fn drop(mut self, Tracked(regions): Tracked<&mut MetaRegionOwners>)
701        requires
702            self.inv(),
703            old(regions).inv(),
704            self.wf_with_region(*old(regions)),
705            old(regions).slot_owners[self.index()].ref_count() == 1 ==> old(
706                regions,
707            ).slot_owners[self.index()].paths_in_pt.is_empty(),
708        ensures
709            final(regions).inv(),
710            ({
711                let paddr = self.start_paddr_spec();
712                let old_owner = old(regions).slot_owner(paddr);
713                let new_owner = final(regions).slot_owner(paddr);
714                &&& *final(regions) =~= old(regions).insert_slot_owner(paddr, new_owner)
715                &&& new_owner.slot_vaddr == old_owner.slot_vaddr
716                &&& new_owner.usage == old_owner.usage
717                &&& new_owner.paths_in_pt == old_owner.paths_in_pt
718                &&& new_owner.metadata_perm.id() == old_owner.metadata_perm.id()
719                &&& old_owner.ref_count() == 1 ==> new_owner.ref_count() == REF_COUNT_UNUSED
720                &&& old_owner.ref_count() > 1 ==> new_owner.ref_count() == (old_owner.ref_count()
721                    - 1) as u64
722            }),
723    {
724        proof_decl!{
725            let ghost idx = self.index();
726            let tracked slot_own = regions.tracked_borrow_mut_slot_owner(self.start_paddr_spec());
727            let tracked frame_permission = self.tracked_metadata_perm.tracked_take();
728            slot_own.metadata_perm.combine(frame_permission);
729        }
730
731        let last_ref_cnt = self.slot().ref_count.fetch_sub(
732            Tracked(&mut slot_own.ref_count_perm),
733            1,
734        );
735
736        if last_ref_cnt == 1 {
737            // A fence is needed here with the same reasons stated in the implementation of
738            // `Arc::drop`: <https://doc.rust-lang.org/std/sync/struct.Arc.html#method.drop>.
739            acquire_fence();
740            // SAFETY: this is the last reference and is about to be dropped.
741            unsafe {
742                #[verus_spec(with Tracked(slot_own))]
743                self.slot().drop_last_in_place()
744            };
745
746            // TODO: return page to allocator
747            // allocator::get_global_frame_allocator().dealloc(paddr, PAGE_SIZE);
748        }
749        proof {
750            assert_maps_equal!(
751                regions.slot_owners,
752                old(regions).slot_owners.insert(idx, regions.slot_owners[idx]),
753                i => {
754                    if i != idx {
755                        assert(regions.slot_owners[i] == old(regions).slot_owners[i]);
756                    }
757                }
758            );
759        }
760    }
761}
762
763/*
764
765impl<M: AnyFrameMeta> TryFrom<Frame<dyn AnyFrameMeta>> for Frame<M> {
766    type Error = Frame<dyn AnyFrameMeta>;
767
768    /// Tries converting a [`Frame<dyn AnyFrameMeta>`] into the statically-typed [`Frame`].
769    ///
770    /// If the usage of the frame is not the same as the expected usage, it will
771    /// return the dynamic frame itself as is.
772    fn try_from(dyn_frame: Frame<dyn AnyFrameMeta>) -> Result<Self, Self::Error> {
773        if (dyn_frame.dyn_meta() as &dyn core::any::Any).is::<M>() {
774            // SAFETY: The metadata is coerceable and the struct is transmutable.
775            Ok(unsafe { core::mem::transmute::<Frame<dyn AnyFrameMeta>, Frame<M>>(dyn_frame) })
776        } else {
777            Err(dyn_frame)
778        }
779    }
780}*/
781
782/*impl<M: AnyFrameMeta> From<UFrame> for Frame<M> {
783    fn from(frame: UFrame) -> Self {
784        // SAFETY: The metadata is coerceable and the struct is transmutable.
785        unsafe { core::mem::transmute(frame) }
786    }
787}*/
788
789/*impl TryFrom<Frame<FrameMeta>> for UFrame {
790    type Error = Frame<FrameMeta>;
791}*/
792
793#[verifier::external]
794impl<M: AnyUFrameMeta> From<Frame<M>> for UFrame {
795    fn from(frame: Frame<M>) -> Self {
796        // SAFETY: The metadata is coerceable and the struct is transmutable.
797        unsafe { core::mem::transmute(frame) }
798    }
799}
800
801/*
802impl From<UFrame> for Frame<dyn AnyFrameMeta> {
803    fn from(frame: UFrame) -> Self {
804        // SAFETY: The metadata is coerceable and the struct is transmutable.
805        unsafe { core::mem::transmute(frame) }
806    }
807}
808
809impl TryFrom<Frame<dyn AnyFrameMeta>> for UFrame {
810    type Error = Frame<dyn AnyFrameMeta>;
811
812    /// Tries converting a [`Frame<dyn AnyFrameMeta>`] into [`UFrame`].
813    ///
814    /// If the usage of the frame is not the same as the expected usage, it will
815    /// return the dynamic frame itself as is.
816    fn try_from(dyn_frame: Frame<dyn AnyFrameMeta>) -> Result<Self, Self::Error> {
817        if dyn_frame.dyn_meta().is_untyped() {
818            // SAFETY: The metadata is coerceable and the struct is transmutable.
819            Ok(unsafe { core::mem::transmute::<Frame<dyn AnyFrameMeta>, UFrame>(dyn_frame) })
820        } else {
821            Err(dyn_frame)
822        }
823    }
824}*/
825
826/// Increases the reference count of the frame by one.
827///
828/// # Safety
829///
830/// The caller should ensure the following conditions:
831///  1. The physical address must represent a valid frame;
832///  2. The caller must have already held a reference to the frame.
833///
834/// # Verified Properties
835/// ## Preconditions
836/// - **Safety Invariant**: Metaslot region invariants must hold.
837/// - **Safety**: The physical address must represent a valid frame.
838/// ## Postconditions
839/// - **Safety Invariant**: Metaslot region invariants hold after the call.
840/// - **Correctness**: The reference count of the frame is increased by one.
841/// - **Safety**: Frames other than this one are not affected by the call.
842#[verus_spec(permission =>
843    with
844        Tracked(regions): Tracked<&mut MetaRegionOwners>,
845    requires
846        old(regions).inv(),
847        old(regions).contains(frame_to_index(paddr)),
848        valid_frame_paddr(paddr),
849        old(regions).slot_owner(paddr).ref_count() > 0,
850        old(regions).slot_owner(paddr).ref_count()
851            != REF_COUNT_UNUSED,
852        old(regions).slot_owner(paddr).ref_count()
853            >= REF_COUNT_MAX ==> may_panic(),
854    ensures
855        final(regions).inv(),
856        permission@.frac() == 1,
857        permission@.id() == final(regions).slot_owner(paddr).metadata_perm.id(),
858        permission@.resource() == old(regions).slot_owner(paddr).metadata_perm@,
859        old(regions).inc_frame_reference_region_spec(paddr, *final(regions)),
860)]
861pub(in crate::mm) unsafe fn inc_frame_ref_count(paddr: Paddr) -> (permission: Tracked<
862    FracMetadataPerm,
863>) {
864    let tracked perm = regions.tracked_borrow_slot(paddr);
865    let tracked slot_own = regions.tracked_borrow_mut_slot_owner(paddr);
866
867    let vaddr: Vaddr = frame_to_meta(paddr);
868    // SAFETY: `vaddr` points to a valid `MetaSlot` that will never be mutably borrowed, so taking
869    // an immutable reference to it is always safe.
870    let slot = PPtr::<MetaSlot>(vaddr, PhantomData);
871
872    unsafe {
873        #[verus_spec(with Tracked(&mut slot_own.ref_count_perm))]
874        slot.borrow(Tracked(perm)).inc_ref_count()
875    };
876    let tracked frame_permission = slot_own.metadata_perm.split_one();
877
878    Tracked(frame_permission)
879}
880
881/// A dynamically-typed frame is represented by a frame of the underlying metadata type,
882/// which can be cast from any other type.
883pub type DynFrame = Frame<MetaSlotStorage>;
884
885impl<M: AnyFrameMeta + Repr<MetaSlotStorage> + 'static> Frame<M> {
886    /// Erases the static metadata type, yielding a `Frame<dyn AnyFrameMeta>`.
887    ///
888    /// Inherent method rather than `From`/`Into` to avoid trait-inference
889    /// ambiguity at call sites that previously relied on the blanket
890    /// `From<T> for T` (e.g. `frame.into()` for `Frame<UFrame>`).
891    ///
892    /// Axiomatized (`external_body`) because the body is `transmute`, which
893    /// Verus has no built-in spec for.
894    #[verifier::external_body]
895    pub fn into_dyn(self) -> Frame<dyn AnyFrameMeta> {
896        // SAFETY: `Frame<M>` is `#[repr(transparent)]` over `PPtr<MetaSlot>`
897        // plus a zero-size `PhantomData<M>`. `Frame<dyn AnyFrameMeta>` has
898        // the same runtime layout (thin pointer + ZST phantom).
899        unsafe { core::mem::transmute(self) }
900    }
901}
902
903} // verus!