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