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