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