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::{
58        frame_specs::*,
59        mapping::{frame_to_index, group_page_meta, index_to_meta, max_meta_slots},
60        meta_owners::*,
61        meta_region_owners::MetaRegionOwners,
62    },
63};
64
65use core::{
66    marker::PhantomData,
67    mem::ManuallyDrop,
68    sync::atomic::{AtomicUsize, Ordering},
69};
70
71//pub use allocator::GlobalFrameAllocator;
72use super::PagingLevel;
73use crate::mm::kspace::FRAME_METADATA_RANGE;
74use crate::mm::page_table::RCClone;
75use crate::mm::page_table::{PageTableConfig, PageTablePageMeta};
76use crate::mm::{
77    MAX_PADDR, Paddr, Vaddr,
78    frame::meta::{
79        META_SLOT_SIZE,
80        mapping::{frame_to_meta, meta_to_frame},
81    },
82    kspace::{LINEAR_MAPPING_BASE_VADDR, VMALLOC_BASE_VADDR},
83};
84pub use linked_list::{CursorMut, Link, LinkedList};
85pub use meta::{AnyFrameMeta, GetFrameError, MetaSlot};
86use meta::{REF_COUNT_MAX, REF_COUNT_UNIQUE, REF_COUNT_UNUSED, mapping};
87pub use segment::Segment;
88pub use unique::UniqueFrame;
89pub use untyped::{AnyUFrameMeta, UFrame};
90
91verus! {
92
93/*
94static MAX_PADDR: AtomicUsize = AtomicUsize::new(0);
95*/
96/// Returns the maximum physical address that is tracked by frame metadata.
97#[verifier::external_body]
98pub(in crate::mm) fn max_paddr() -> Paddr
99    returns
100        MAX_PADDR,
101{
102    // let max_paddr = MAX_PADDR.load(Ordering::Relaxed) as Paddr;
103    // debug_assert_ne!(max_paddr, 0);
104    // max_paddr
105    unimplemented!()
106}
107
108#[verifier::external_body]
109fn acquire_fence() {
110    core::sync::atomic::fence(Ordering::Acquire);
111}
112
113/// A smart pointer to a frame.
114///
115/// A frame is a contiguous range of bytes in physical memory. The [`Frame`]
116/// type is a smart pointer to a frame that is reference-counted.
117///
118/// Frames are associated with metadata. The type of the metadata `M` is
119/// determines the kind of the frame. If `M` implements [`AnyUFrameMeta`], the
120/// frame is a untyped frame. Otherwise, it is a typed frame.
121// #[repr(transparent)]
122pub struct Frame<M: ?Sized> {
123    pub ptr: PPtr<MetaSlot>,
124    pub _marker: PhantomData<M>,
125    /// The permission to access the `MetaSlot` fields.
126    #[cfg(verus_keep_ghost_body)]
127    pub tracked_slot_perm: Tracked<&'static simple_pptr::PointsTo<MetaSlot>>,
128    /// One fractional permission for the currently installed metadata.
129    #[cfg(verus_keep_ghost_body)]
130    pub tracked_metadata_perm: Tracked<Option<FracMetadataPerm>>,
131}
132
133#[verifier::external]
134unsafe impl<M: AnyFrameMeta + ?Sized> Send for Frame<M> {
135
136}
137
138#[verifier::external]
139unsafe impl<M: AnyFrameMeta + ?Sized> Sync for Frame<M> {
140
141}
142
143/*
144impl<M: AnyFrameMeta + ?Sized> core::fmt::Debug for Frame<M> {
145    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
146        write!(f, "Frame({:#x})", self.start_paddr())
147    }
148}
149*/
150
151verus!{
152
153impl<M: AnyFrameMeta + Repr<MetaSlotStorage> + ?Sized> PartialEqSpecImpl for Frame<M>{
154    open spec fn obeys_eq_spec() -> bool { true }
155
156    open spec fn eq_spec(&self, other: &Self) -> bool {
157        self.start_paddr_spec() == other.start_paddr_spec()
158    }
159}
160
161}
162
163#[verus_verify]
164impl<M: AnyFrameMeta + Repr<MetaSlotStorage> + ?Sized> PartialEq for Frame<M> {
165    fn eq(&self, other: &Self) -> bool {
166        proof!{
167            //FIXME: Add `ptr_inv` as type invariant when we fix visibility.
168            assume(self.ptr_inv());
169            assume(other.ptr_inv());
170        }
171        self.start_paddr() == other.start_paddr()
172    }
173}
174
175#[verus_verify]
176impl<M: AnyFrameMeta + Repr<MetaSlotStorage> + ?Sized> Eq for Frame<M> {
177
178}
179
180#[verus_verify]
181impl<M: AnyFrameMeta + Repr<MetaSlotStorage> + OwnerOf> Frame<M> {
182    /// Gets a [`Frame`] with a specific usage from a raw, unused page.
183    ///
184    /// The caller should provide the initial metadata of the page.
185    ///
186    /// If the provided frame is not truly unused at the moment, it will return
187    /// an error. If wanting to acquire a frame that is already in use, use
188    /// [`Frame::from_in_use`] instead.
189    /// # Verified Properties
190    /// ## Preconditions
191    /// - **Safety Invariant**: Metaslot region invariants must hold.
192    /// ## Postconditions
193    /// - **Safety Invariant**: Metaslot region invariants hold after the call.
194    /// - **Correctness**: If successful, the function returns a pointer to the metadata slot and a permission to the slot.
195    /// - **Correctness**: If successful, the slot is initialized with the given metadata.
196    /// - **Correctness**: If `paddr` does not have a corresponding metadata slot, the function returns an error.
197    /// - **Drop Bookkeeping**: If successful, the function returns a live frame, which is tracked correctly as needing to be dropped.
198    /// ## Safety
199    /// - This function returns an error if `paddr` does not correspond to a valid slot or the slot is in use.
200    #[verus_spec(r =>
201        with
202            Tracked(regions): Tracked<&mut MetaRegionOwners>,
203            Tracked(repr_perm): Tracked<&mut M::ReprPerm>
204        requires
205            old(regions).inv(),
206        ensures
207            final(regions).inv(),
208            r matches Ok(res) ==> {
209                &&& MetaSlot::get_from_unused_region_spec(paddr, false, *old(regions), *final(regions))
210                &&& res.inv()
211                &&& res.start_paddr_spec() == paddr
212                &&& res.wf_with_region(*final(regions))
213            },
214            r is Err ==> *final(regions) == *old(regions)
215    )]
216    pub fn from_unused(paddr: Paddr, metadata: M) -> Result<Self, GetFrameError> {
217        #[verus_spec(with
218            Tracked(regions),
219            Tracked(repr_perm) => Tracked(permissions)
220        )]
221        let from_unused = MetaSlot::get_from_unused(paddr, metadata, false);
222        if let Err(err) = from_unused {
223            Err(err)
224        } else {
225            proof_decl! {
226                let tracked slot_perm = regions.tracked_borrow_slot(paddr);
227                let ghost idx = frame_to_index(paddr);
228                assert(regions.slot_owners.contains_key(idx));
229                let tracked metadata_perm = permissions.tracked_unwrap().tracked_take_left();
230            }
231            let ptr = from_unused.unwrap();
232            Ok(
233                Self {
234                    ptr,
235                    _marker: PhantomData,
236                    #[cfg(verus_keep_ghost_body)]
237                    tracked_slot_perm: Tracked(slot_perm),
238                    #[cfg(verus_keep_ghost_body)]
239                    tracked_metadata_perm: Tracked(Some(metadata_perm)),
240                },
241            )
242        }
243    }
244
245    /// Gets the metadata of this page.
246    /// # Verified Properties
247    /// ## Preconditions
248    /// - The caller must have a valid permission for the frame.
249    /// ## Postconditions
250    /// - The function returns the borrowed metadata of the frame.
251    /// ## Safety
252    /// - By requiring the caller to provide a typed permission, we ensure that the metadata is of type `M`.
253    /// While a non-verified caller cannot be trusted to obey this interface, all functions that return a `Frame<M>` also
254    /// return an appropriate permission.
255    #[verus_spec(ret =>
256        with
257            Tracked(metadata_perm): Tracked<Option<&'a MetadataPerm>>,
258            Tracked(repr_perm): Tracked<&'a M::ReprPerm>,
259        requires
260            self.ptr_inv(),
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@.ptr.addr() == self.ptr.addr(),
456    )]
457    pub fn borrow<'a>(&self) -> FrameRef<'a, M> {
458        let tracked slot_perm = *self.tracked_slot_perm.borrow();
459        let tracked metadata_perm = self.tracked_metadata_perm.borrow().tracked_borrow();
460        // SAFETY: Both the lifetime and the type matches `self`.
461        unsafe {
462            #[verus_spec(with Tracked(slot_perm), Tracked(metadata_perm))]
463            FrameRef::borrow_paddr(self.start_paddr())
464        }
465    }
466
467    /// Borrows a frame whose owning handle is represented by an external
468    /// counting permission, as is the case for page-table nodes stored raw in
469    /// PTEs.
470    #[verus_spec(res =>
471        with
472            Tracked(frame_permission): Tracked<&FracMetadataPerm>,
473            Tracked(regions): Tracked<&MetaRegionOwners>,
474        requires
475            self.ptr_inv(),
476            regions.inv(),
477            frame_permission.frac() == 1,
478            frame_permission.id() == regions.slot_owners[self.index()].metadata_perm.id(),
479            MetaSlot::perms_related(
480                *regions.slots[self.index()],
481                frame_permission.resource(),
482            ),
483        ensures
484            res.inner@.ptr.addr() == self.ptr.addr(),
485            res.inner@.ptr_inv(),
486            res.inner@.tracked_slot_perm@ == regions.slots[self.index()],
487            res.inner@.tracked_metadata_perm@ is None,
488            MetaSlot::perms_related(res.inner@.slot_perm(), frame_permission.resource()),
489    )]
490    pub(in crate::mm) fn borrow_with_permission<'a>(&self) -> FrameRef<'a, M> {
491        unsafe {
492            proof {
493                broadcast use group_page_meta;
494
495                regions.lemma_contains_valid_frame_paddr(self.start_paddr_spec());
496            }
497            let tracked slot_perm = regions.tracked_borrow_slot(self.start_paddr_spec());
498            #[verus_spec(with Tracked(slot_perm), Tracked(frame_permission))]
499            FrameRef::borrow_paddr(self.start_paddr())
500        }
501    }
502
503    /// Forgets the handle to the frame.
504    ///
505    /// This will result in the frame being leaked without calling the custom dropper.
506    ///
507    /// A physical address to the frame is returned in case the frame needs to be
508    /// restored using [`Frame::from_raw`] later. This is useful when some architectural
509    /// data structures need to hold the frame handle such as the page table.
510    ///
511    /// # Verified Properties
512    /// ## Preconditions
513    /// - **Safety**: The frame's invariant must be satisfied.
514    /// ## Postconditions
515    /// - **Correctness**: The function returns the physical address of the frame.
516    /// ## Safety
517    /// - 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.
518    /// - The owner's raw count is incremented so that we can enforce the safety requirement on `Frame::from_raw`.
519    #[verus_spec(r =>
520        with
521            -> raw_permission: Tracked<FrameRawPerms>,
522        requires
523            self.inv(),
524        ensures
525            r == self.start_paddr_spec(),
526            raw_permission@.inv(),
527    )]
528    pub(in crate::mm) fn into_raw(mut self) -> Paddr {
529        broadcast use group_page_meta;
530
531        proof_decl!{
532            let tracked perm = FrameRawPerms {
533                slot_perm: &*self.tracked_slot_perm,
534                metadata_perm: self.tracked_metadata_perm.tracked_take(),
535            };
536        }
537
538        let this = ManuallyDrop::new(self);
539        proof_with!(|= Tracked(perm));
540        this.start_paddr()
541    }
542}
543
544#[verus_verify]
545impl<M: ?Sized> Frame<M> {
546    /// Gets the metadata slot of the frame.
547    ///
548    /// # Verified Properties
549    /// ## Preconditions
550    /// - **Safety**: The caller must have a valid permission for the frame.
551    /// ## Postconditions
552    /// - **Correctness**: The function returns a reference to the metadata slot of the frame.
553    /// ## Safety
554    /// - There is no way to mutably borrow the metadata slot, so taking an immutable reference is safe.
555    /// (The fields of the slot can be mutably borrowed, but not the slot itself.)
556    #[verus_spec(
557        requires
558            self.ptr_inv(),
559        returns
560            self.tracked_slot_perm@.value(),
561    )]
562    pub fn slot<'a>(&'a self) -> &'a MetaSlot {
563        // SAFETY: `ptr` points to a valid `MetaSlot` that will never be
564        // mutably borrowed, so taking an immutable reference to it is safe.
565        proof_decl! {
566            let tracked slot_perm = *self.tracked_slot_perm;
567        }
568        self.ptr.borrow(Tracked(slot_perm))
569    }
570}
571
572#[verus_verify]
573impl<M: AnyFrameMeta + Repr<MetaSlotStorage> + ?Sized> Frame<M> {
574    /// Restores a forgotten [`Frame`] from a physical address.
575    ///
576    /// # Safety
577    ///
578    /// The caller should only restore a `Frame` that was previously forgotten using
579    /// [`Frame::into_raw`].
580    ///
581    /// And the restoring operation should only be done once for a forgotten
582    /// [`Frame`]. Otherwise double-free will happen.
583    ///
584    /// Also, the caller ensures that the usage of the frame is correct. There's
585    /// no checking of the usage in this function.
586    #[verus_spec(r =>
587        with
588            Tracked(perm): Tracked<FrameRawPerms>,
589        requires
590            valid_frame_paddr(paddr),
591            perm.slot_vaddr() == frame_to_meta(paddr),
592            perm.inv(),
593        ensures
594            r.tracked_slot_perm@ == perm.slot_perm,
595            r.tracked_metadata_perm@ == Some(perm.metadata_perm),
596            r.start_paddr_spec() == paddr,
597            r.inv(),
598    )]
599    pub(in crate::mm) unsafe fn from_raw(paddr: Paddr) -> Self
600        no_unwind
601    {
602        // debug_assert!(paddr < max_paddr());
603        let vaddr = frame_to_meta(paddr);
604        // let ptr = vaddr as *const MetaSlot;
605        let ptr = PPtr(vaddr, PhantomData);
606
607        proof_with!{ tracked_slot_perm: Tracked(perm.slot_perm), tracked_metadata_perm: Tracked(Some(perm.metadata_perm)) }
608        Self { ptr, _marker: PhantomData }
609    }
610}
611
612#[verus_verify]
613impl<M: AnyFrameMeta + Repr<MetaSlotStorage>> RCClone for Frame<M> {
614    open spec fn clone_requires(self, regions: MetaRegionOwners) -> bool {
615        let paddr = self.start_paddr_spec();
616        let ref_count = regions.slot_owner(paddr).ref_count();
617        &&& self.inv()
618        &&& regions.inv()
619        &&& self.wf_with_region(regions)
620        &&& ref_count > 0
621        &&& ref_count != REF_COUNT_UNUSED
622        &&& ref_count >= REF_COUNT_MAX ==> may_panic()
623    }
624
625    open spec fn clone_ensures(
626        self,
627        pre: MetaRegionOwners,
628        post: MetaRegionOwners,
629        res: Self,
630    ) -> bool {
631        let idx = self.index();
632        &&& pre.inc_frame_reference_region_spec(self.start_paddr_spec(), post)
633        &&& post.inv()
634        &&& res.tracked_metadata_perm@ is Some
635        &&& res.tracked_metadata_perm@->0.frac() == 1
636        &&& res.tracked_metadata_perm@->0.id() == post.slot_owners[idx].metadata_perm.id()
637        &&& res.tracked_slot_perm@ == post.slots[idx]
638        &&& res.ptr == self.ptr
639    }
640
641    fn clone(&self, Tracked(regions): Tracked<&mut MetaRegionOwners>) -> Self {
642        proof_decl! {
643            regions.lemma_contains_valid_frame_paddr(self.start_paddr_spec());
644            let ghost paddr = self.start_paddr_spec();
645            let tracked slot_perm = regions.tracked_borrow_slot(paddr);
646            let tracked slot_own = regions.tracked_borrow_mut_slot_owner(paddr);
647        }
648
649        // SAFETY: We have already held a reference to the frame.
650        unsafe {
651            #[verus_spec(with Tracked(&mut slot_own.ref_count_perm))]
652            self.slot().inc_ref_count();
653        }
654
655        proof_decl!{
656            let tracked metadata_perm = slot_own.metadata_perm.split_one();
657        };
658
659        Self {
660            ptr: PPtr::<MetaSlot>::from_addr(self.ptr.0),
661            _marker: PhantomData,
662            #[cfg(verus_keep_ghost_body)]
663            tracked_slot_perm: Tracked(slot_perm),
664            #[cfg(verus_keep_ghost_body)]
665            tracked_metadata_perm: Tracked(Some(metadata_perm)),
666        }
667    }
668}
669
670/*
671impl<M: AnyFrameMeta + ?Sized> Drop for Frame<M> {
672    fn drop(&mut self) {
673        let last_ref_cnt = self.slot().ref_count.fetch_sub(1, Ordering::Release);
674        debug_assert!(last_ref_cnt != 0 && last_ref_cnt != REF_COUNT_UNUSED);
675
676        if last_ref_cnt == 1 {
677            // A fence is needed here with the same reasons stated in the implementation of
678            // `Arc::drop`: <https://doc.rust-lang.org/std/sync/struct.Arc.html#method.drop>.
679            core::sync::atomic::fence(Ordering::Acquire);
680
681            // SAFETY: this is the last reference and is about to be dropped.
682            unsafe { self.slot().drop_last_in_place() };
683
684            allocator::get_global_frame_allocator().dealloc(self.start_paddr(), PAGE_SIZE);
685        }
686    }
687}*/
688
689impl<M: ?Sized> Frame<M> {
690    pub fn drop(mut self, Tracked(regions): Tracked<&mut MetaRegionOwners>)
691        requires
692            self.drop_requires(*old(regions)),
693        ensures
694            self.drop_ensures(*old(regions), *final(regions)),
695    {
696        proof_decl!{
697            let ghost idx = self.index();
698            let tracked slot_own = regions.tracked_borrow_mut_slot_owner(self.start_paddr_spec());
699            let tracked frame_permission = self.tracked_metadata_perm.tracked_take();
700            slot_own.metadata_perm.combine(frame_permission);
701        }
702
703        let last_ref_cnt = self.slot().ref_count.fetch_sub(
704            Tracked(&mut slot_own.ref_count_perm),
705            1,
706        );
707
708        if last_ref_cnt == 1 {
709            // A fence is needed here with the same reasons stated in the implementation of
710            // `Arc::drop`: <https://doc.rust-lang.org/std/sync/struct.Arc.html#method.drop>.
711            acquire_fence();
712            // SAFETY: this is the last reference and is about to be dropped.
713            unsafe {
714                #[verus_spec(with Tracked(slot_own))]
715                self.slot().drop_last_in_place()
716            };
717
718            // TODO: return page to allocator
719            // allocator::get_global_frame_allocator().dealloc(paddr, PAGE_SIZE);
720        }
721        proof {
722            assert_maps_equal!(
723                regions.slot_owners,
724                old(regions).slot_owners.insert(idx, regions.slot_owners[idx]),
725                i => {
726                    if i != idx {
727                        assert(regions.slot_owners[i] == old(regions).slot_owners[i]);
728                    }
729                }
730            );
731        }
732    }
733}
734
735/*
736
737impl<M: AnyFrameMeta> TryFrom<Frame<dyn AnyFrameMeta>> for Frame<M> {
738    type Error = Frame<dyn AnyFrameMeta>;
739
740    /// Tries converting a [`Frame<dyn AnyFrameMeta>`] into the statically-typed [`Frame`].
741    ///
742    /// If the usage of the frame is not the same as the expected usage, it will
743    /// return the dynamic frame itself as is.
744    fn try_from(dyn_frame: Frame<dyn AnyFrameMeta>) -> Result<Self, Self::Error> {
745        if (dyn_frame.dyn_meta() as &dyn core::any::Any).is::<M>() {
746            // SAFETY: The metadata is coerceable and the struct is transmutable.
747            Ok(unsafe { core::mem::transmute::<Frame<dyn AnyFrameMeta>, Frame<M>>(dyn_frame) })
748        } else {
749            Err(dyn_frame)
750        }
751    }
752}*/
753
754/*impl<M: AnyFrameMeta> From<UFrame> for Frame<M> {
755    fn from(frame: UFrame) -> Self {
756        // SAFETY: The metadata is coerceable and the struct is transmutable.
757        unsafe { core::mem::transmute(frame) }
758    }
759}*/
760
761/*impl TryFrom<Frame<FrameMeta>> for UFrame {
762    type Error = Frame<FrameMeta>;
763}*/
764
765#[verifier::external]
766impl<M: AnyUFrameMeta> From<Frame<M>> for UFrame {
767    fn from(frame: Frame<M>) -> Self {
768        // SAFETY: The metadata is coerceable and the struct is transmutable.
769        unsafe { core::mem::transmute(frame) }
770    }
771}
772
773/*
774impl From<UFrame> for Frame<dyn AnyFrameMeta> {
775    fn from(frame: UFrame) -> Self {
776        // SAFETY: The metadata is coerceable and the struct is transmutable.
777        unsafe { core::mem::transmute(frame) }
778    }
779}
780
781impl TryFrom<Frame<dyn AnyFrameMeta>> for UFrame {
782    type Error = Frame<dyn AnyFrameMeta>;
783
784    /// Tries converting a [`Frame<dyn AnyFrameMeta>`] into [`UFrame`].
785    ///
786    /// If the usage of the frame is not the same as the expected usage, it will
787    /// return the dynamic frame itself as is.
788    fn try_from(dyn_frame: Frame<dyn AnyFrameMeta>) -> Result<Self, Self::Error> {
789        if dyn_frame.dyn_meta().is_untyped() {
790            // SAFETY: The metadata is coerceable and the struct is transmutable.
791            Ok(unsafe { core::mem::transmute::<Frame<dyn AnyFrameMeta>, UFrame>(dyn_frame) })
792        } else {
793            Err(dyn_frame)
794        }
795    }
796}*/
797
798/// Increases the reference count of the frame by one.
799///
800/// # Safety
801///
802/// The caller should ensure the following conditions:
803///  1. The physical address must represent a valid frame;
804///  2. The caller must have already held a reference to the frame.
805///
806/// # Verified Properties
807/// ## Preconditions
808/// - **Safety Invariant**: Metaslot region invariants must hold.
809/// - **Safety**: The physical address must represent a valid frame.
810/// ## Postconditions
811/// - **Safety Invariant**: Metaslot region invariants hold after the call.
812/// - **Correctness**: The reference count of the frame is increased by one.
813/// - **Safety**: Frames other than this one are not affected by the call.
814#[verus_spec(permission =>
815    with
816        Tracked(regions): Tracked<&mut MetaRegionOwners>,
817    requires
818        old(regions).inv(),
819        old(regions).contains(frame_to_index(paddr)),
820        valid_frame_paddr(paddr),
821        old(regions).slot_owner(paddr).ref_count() > 0,
822        old(regions).slot_owner(paddr).ref_count()
823            != REF_COUNT_UNUSED,
824        old(regions).slot_owner(paddr).ref_count()
825            >= REF_COUNT_MAX ==> may_panic(),
826    ensures
827        final(regions).inv(),
828        permission@.frac() == 1,
829        permission@.id() == final(regions).slot_owner(paddr).metadata_perm.id(),
830        permission@.resource() == old(regions).slot_owner(paddr).metadata_perm@,
831        old(regions).inc_frame_reference_region_spec(paddr, *final(regions)),
832)]
833pub(in crate::mm) unsafe fn inc_frame_ref_count(paddr: Paddr) -> (permission: Tracked<
834    FracMetadataPerm,
835>) {
836    let tracked perm = regions.tracked_borrow_slot(paddr);
837    let tracked slot_own = regions.tracked_borrow_mut_slot_owner(paddr);
838
839    let vaddr: Vaddr = frame_to_meta(paddr);
840    // SAFETY: `vaddr` points to a valid `MetaSlot` that will never be mutably borrowed, so taking
841    // an immutable reference to it is always safe.
842    let slot = PPtr::<MetaSlot>(vaddr, PhantomData);
843
844    unsafe {
845        #[verus_spec(with Tracked(&mut slot_own.ref_count_perm))]
846        slot.borrow(Tracked(perm)).inc_ref_count()
847    };
848    let tracked frame_permission = slot_own.metadata_perm.split_one();
849
850    Tracked(frame_permission)
851}
852
853/// A dynamically-typed frame is represented by a frame of the underlying metadata type,
854/// which can be cast from any other type.
855pub type DynFrame = Frame<MetaSlotStorage>;
856
857impl<M: AnyFrameMeta + Repr<MetaSlotStorage> + 'static> Frame<M> {
858    /// Erases the static metadata type, yielding a `Frame<dyn AnyFrameMeta>`.
859    ///
860    /// Inherent method rather than `From`/`Into` to avoid trait-inference
861    /// ambiguity at call sites that previously relied on the blanket
862    /// `From<T> for T` (e.g. `frame.into()` for `Frame<UFrame>`).
863    ///
864    /// Axiomatized (`external_body`) because the body is `transmute`, which
865    /// Verus has no built-in spec for.
866    #[verifier::external_body]
867    pub fn into_dyn(self) -> Frame<dyn AnyFrameMeta> {
868        // SAFETY: `Frame<M>` is `#[repr(transparent)]` over `PPtr<MetaSlot>`
869        // plus a zero-size `PhantomData<M>`. `Frame<dyn AnyFrameMeta>` has
870        // the same runtime layout (thin pointer + ZST phantom).
871        unsafe { core::mem::transmute(self) }
872    }
873}
874
875} // verus!