Skip to main content

ostd/mm/frame/
mod.rs

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