Skip to main content

ostd/mm/frame/
mod.rs

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