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