Skip to main content

ostd/mm/frame/
segment.rs

1// SPDX-License-Identifier: MPL-2.0
2//! A contiguous range of frames.
3use vstd::modes::tracked_swap;
4use vstd::prelude::*;
5use vstd::proph::ProphecyGhost;
6
7use vstd::std_specs::iter::{IteratorSpec, IteratorSpecImpl};
8use vstd_extra::assert;
9use vstd_extra::cast_ptr::*;
10use vstd_extra::drop_tracking::*;
11use vstd_extra::ownership::*;
12use vstd_extra::panic::may_panic;
13use vstd_extra::prelude::*;
14
15use crate::mm::page_table::RCClone;
16use crate::mm::{PagingLevel, Vaddr, frame::MetaSlot, paddr_to_vaddr};
17use crate::specs::arch::*;
18use crate::specs::mm::frame::{
19    mapping::{frame_to_index, group_page_meta, meta_addr},
20    meta_owners::*,
21    meta_region_owners::MetaRegionOwners,
22    segment::*,
23};
24
25use core::{fmt::Debug, /*mem::ManuallyDrop,*/ ops::Range};
26
27use super::{
28    Frame, Paddr,
29    meta::mapping::frame_to_meta,
30    meta::{AnyFrameMeta, GetFrameError},
31};
32use crate::mm::frame::{meta::REF_COUNT_MAX, untyped::AnyUFrameMeta};
33
34verus! {
35
36/// A contiguous range of homogeneous physical memory frames.
37///
38/// This is a handle to multiple contiguous frames. It will be more lightweight
39/// than owning an array of frame handles.
40///
41/// The ownership is achieved by the reference counting mechanism of frames.
42/// When constructing a [`Segment`], the frame handles are created then
43/// forgotten, leaving the reference count. When dropping a it, the frame
44/// handles are restored and dropped, decrementing the reference count.
45///
46/// All the metadata of the frames are homogeneous, i.e., they are of the same
47/// type.
48// FIXME: field visibility
49#[repr(transparent)]
50pub struct Segment<M: AnyFrameMeta + ?Sized> {
51    /// The physical address range of the segment.
52    pub range: Range<Paddr>,
53    pub _marker: core::marker::PhantomData<M>,
54}
55
56/*
57impl<M: AnyFrameMeta + ?Sized> Debug for Segment<M> {
58    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
59        write!(f, "Segment({:#x}..{:#x})", self.range.start, self.range.end)
60    }
61}
62*/
63
64/*impl<M: AnyFrameMeta + ?Sized> Drop for Segment<M> {
65    fn drop(&mut self) {
66        for paddr in self.range.clone().step_by(PAGE_SIZE) {
67            // SAFETY: for each frame there would be a forgotten handle
68            // when creating the `Segment` object.
69            drop(unsafe { Frame::<M>::from_raw(paddr) });
70        }
71    }
72}*/
73
74/// A contiguous range of homogeneous untyped physical memory frames that have any metadata.
75///
76/// In other words, the metadata of the frames are of the same type, and they
77/// are untyped, but the type of metadata is not known at compile time. An
78/// [`USegment`] as a parameter accepts any untyped segments.
79///
80/// The usage of this frame will not be changed while this object is alive.
81pub type USegment = Segment<dyn AnyUFrameMeta>;
82
83/* impl<M: AnyFrameMeta + ?Sized> Clone for Segment<M> {
84    fn clone(&self) -> Self {
85        for paddr in self.range.clone().step_by(PAGE_SIZE) {
86            // SAFETY: for each frame there would be a forgotten handle
87            // when creating the `Segment` object, so we already have
88            // reference counts for the frames.
89            unsafe { inc_frame_ref_count(paddr) };
90        }
91        Self {
92            range: self.range.clone(),
93            _marker: core::marker::PhantomData,
94        }
95    }
96} */
97
98impl<M: AnyFrameMeta + Repr<MetaSlotStorage> + OwnerOf> RCClone for Segment<M> {
99    open spec fn clone_requires(self, perm: MetaRegionOwners) -> bool {
100        &&& self.inv()
101        &&& perm.inv()
102        &&& forall|pa: Paddr|
103            #![trigger frame_to_index(pa)]
104            (self.start_paddr() <= pa < self.end_paddr() && pa % PAGE_SIZE == 0) ==> {
105                let idx = frame_to_index(pa);
106                &&& perm.slots.contains_key(idx)
107                &&& has_safe_slot(pa)
108                &&& perm.slot_owners[idx].inner_perms.ref_count.value() > 0
109                &&& perm.slot_owners[idx].inner_perms.ref_count.value() + 1
110                    < super::meta::REF_COUNT_MAX
111                &&& !MetaSlot::inc_ref_count_panic_cond(perm.slot_owners[idx].inner_perms.ref_count)
112            }
113    }
114
115    open spec fn clone_ensures(
116        self,
117        old_perm: MetaRegionOwners,
118        new_perm: MetaRegionOwners,
119        res: Self,
120    ) -> bool {
121        &&& res.range() == self.range()
122        &&& res.inv()
123        &&& new_perm.inv()
124        // `Segment::clone` bumps each page's refcount via
125        // `inc_frame_ref_count` (which preserves the ledger), not through
126        // `Frame::clone`, so it is net-zero on `frame_obligations`. (The
127        // trait no longer hardcodes this; each impl states its own effect.)
128        &&& new_perm.frame_obligations =~= old_perm.frame_obligations
129    }
130
131    #[verifier::loop_isolation(false)]
132    fn clone(&self, Tracked(perm): Tracked<&mut MetaRegionOwners>) -> (res: Self) {
133        let mut paddr = self.range.start;
134
135        loop
136            invariant
137                perm.inv(),
138                self.inv(),
139                perm.slots == old(perm).slots,
140                perm.slot_owners.dom() == old(perm).slot_owners.dom(),
141                perm.frame_obligations == old(perm).frame_obligations,
142                self.range.start <= paddr <= self.range.end,
143                paddr % PAGE_SIZE == 0,
144                paddr <= MAX_PADDR,
145                forall|pa: Paddr|
146                    #![trigger frame_to_index(pa)]
147                    (paddr <= pa < self.range.end && pa % PAGE_SIZE == 0) ==> {
148                        let idx = frame_to_index(pa);
149                        &&& perm.slots.contains_key(idx)
150                        &&& has_safe_slot(pa)
151                        &&& perm.slot_owners[idx].inner_perms.ref_count.value() > 0
152                        &&& perm.slot_owners[idx].inner_perms.ref_count.value() + 1
153                            < super::meta::REF_COUNT_MAX
154                        &&& !MetaSlot::inc_ref_count_panic_cond(
155                            perm.slot_owners[idx].inner_perms.ref_count,
156                        )
157                    },
158            decreases self.range.end - paddr,
159        {
160            if paddr >= self.range.end {
161                break;
162            }
163            unsafe {
164                #[verus_spec(with Tracked(perm))]
165                crate::mm::frame::inc_frame_ref_count(paddr)
166            };
167
168            paddr = paddr + PAGE_SIZE;
169        }
170
171        Self { range: self.range.start..self.range.end, _marker: core::marker::PhantomData }
172    }
173}
174
175#[verus_verify]
176impl<M: AnyFrameMeta + Repr<MetaSlotStorage> + OwnerOf> Segment<M> {
177    /// Creates a new [`Segment`] from unused frames.
178    ///
179    /// The caller must provide a closure to initialize metadata for all the frames.
180    /// The closure receives the physical address of the frame and returns the
181    /// metadata, which is similar to [`core::array::from_fn`].
182    ///
183    /// It returns an error if:
184    ///  - the physical address is invalid or not aligned;
185    ///  - any of the frames cannot be created with a specific reason.
186    ///
187    /// # Panics
188    ///
189    /// It panics if the range is empty.
190    ///
191    /// # Verified Properties
192    /// ## Preconditions
193    /// - the metadata function must be well-formed and valid for all frames in the range;
194    /// - the metadata function must ensure that the frames can be created and owned by the segment;
195    /// - for any frame created via the closure `metadata_fn`, the corresponding slot in `regions`
196    ///   must be unused and not dropped in the owner ([`MetaRegionOwners`]).
197    ///
198    /// Range constraints (alignment, `range.end <= MAX_PADDR`, non-emptiness) are runtime-checked
199    /// in the body — see the postconditions below for the corresponding error variants.
200    /// ## Postconditions
201    /// - if the result is `Ok`, the returned segment satisfies its invariant with the owner,
202    ///   has the same physical address range as the input, and the owner is `Some`;
203    /// - if the input range is misaligned, the result is `Err(NotAligned)`;
204    /// - if the input range exceeds `MAX_PADDR`, the result is `Err(OutOfBound)`;
205    /// - if the input is aligned and within `MAX_PADDR` and the function terminated,
206    ///   then `range.start < range.end` (the runtime `assert!` would otherwise diverge).
207    #[verus_spec(r =>
208        with
209            Tracked(regions): Tracked<&mut MetaRegionOwners>,
210                -> owner: Tracked<Option<SegmentOwner<M>>>,
211        requires
212            old(regions).inv(),
213            forall|paddr_in: Paddr|
214                (range.start <= paddr_in < range.end && paddr_in % PAGE_SIZE == 0) ==> {
215                    &&& metadata_fn.requires((paddr_in,))
216                },
217            forall|paddr_in: Paddr, paddr_out: Paddr, m: M|
218                metadata_fn.ensures((paddr_in,), (paddr_out, m)) ==> paddr_in == paddr_out,
219            !(range.end <= MAX_PADDR ==> range.start < range.end) ==> may_panic(),
220        ensures
221            final(regions).inv(),
222            r is Err ==> final(regions).frame_obligations == old(regions).frame_obligations,
223            (range.start % PAGE_SIZE != 0 || range.end % PAGE_SIZE != 0)
224                ==> r == Err::<Self, _>(GetFrameError::NotAligned),
225            (range.start % PAGE_SIZE == 0 && range.end % PAGE_SIZE == 0 && range.end > MAX_PADDR)
226                ==> r == Err::<Self, _>(GetFrameError::OutOfBound),
227            r matches Ok(seg) ==> {
228                &&& seg.start_paddr() == range.start
229                &&& seg.end_paddr() == range.end
230                &&& seg.start_paddr() < seg.end_paddr()
231                &&& crate::specs::mm::frame::segment::seg_obligations_minted(
232                    *old(regions),
233                    *final(regions),
234                    range.start,
235                    crate::specs::mm::frame::segment::seg_nframes(range),
236                )
237                &&& owner@ matches Some(owner) && {
238                    &&& seg.inv()
239                    &&& seg.wf(&owner)
240                }
241                &&& forall|paddr: Paddr|
242                    #![trigger frame_to_index(paddr)]
243                    (range.start <= paddr < range.end && paddr % PAGE_SIZE == 0)
244                        ==> final(regions).slots.contains_key(frame_to_index(paddr))
245                &&& range.start < range.end <= MAX_PADDR
246            },
247    )]
248    pub fn from_unused(range: Range<Paddr>, metadata_fn: impl Fn(Paddr) -> (Paddr, M)) -> (res:
249        Result<Self, GetFrameError>) {
250        proof_decl! {
251            let tracked mut owner: Option<SegmentOwner<M>> = None;
252            let tracked mut addrs = Seq::<usize>::tracked_empty();
253        }
254
255        if range.start % PAGE_SIZE != 0 || range.end % PAGE_SIZE != 0 {
256            return {
257                proof_with!(|= Tracked(owner));
258                Err(GetFrameError::NotAligned)
259            };
260        }
261        if range.end > MAX_PADDR {
262            return {
263                proof_with!(|= Tracked(owner));
264                Err(GetFrameError::OutOfBound)
265            };
266        }
267        assert!(range.start < range.end);
268
269        let mut segment = Self {
270            range: range.start..range.start,
271            _marker: core::marker::PhantomData,
272        };
273
274        let mut i = 0;
275        let addr_len = (range.end - range.start) / PAGE_SIZE;
276
277        while i < addr_len
278            invariant
279                i <= addr_len,
280                i == addrs.len(),
281                range.start % PAGE_SIZE == 0,
282                range.end % PAGE_SIZE == 0,
283                range.end <= MAX_PADDR,
284                range.start <= range.start + i * PAGE_SIZE <= range.end,
285                range.end == range.start + addr_len * PAGE_SIZE,
286                addr_len == (range.end - range.start) / PAGE_SIZE as int,
287                i <= addr_len,
288                forall|paddr_in: Paddr|
289                    (range.start + i * PAGE_SIZE <= paddr_in < range.end && paddr_in % PAGE_SIZE
290                        == 0) ==> {
291                        &&& metadata_fn.requires((paddr_in,))
292                    },
293                forall|paddr_in: Paddr, paddr_out: Paddr, m: M|
294                    range.start + i * PAGE_SIZE <= paddr_in < range.end && paddr_in % PAGE_SIZE == 0
295                        && metadata_fn.ensures((paddr_in,), (paddr_out, m)) ==> paddr_in
296                        == paddr_out,
297                forall|j: int|
298                    #![trigger addrs[j]]
299                    0 <= j < addrs.len() ==> {
300                        let idx = frame_to_index(addrs[j]);
301                        &&& regions.slots.contains_key(idx)
302                        &&& regions.slot_owners.contains_key(idx)
303                        &&& regions.slot_owners[idx].slot_vaddr == meta_addr(idx)
304                        &&& regions.slot_owners[idx].inner_perms.ref_count.value() > 0
305                        &&& regions.slot_owners[idx].inner_perms.ref_count.value()
306                            <= crate::mm::frame::meta::REF_COUNT_MAX
307                        &&& regions.slot_owners[idx].paths_in_pt.is_empty()
308                        &&& addrs[j] % PAGE_SIZE == 0
309                        &&& addrs[j] < MAX_PADDR
310                        &&& addrs[j] == range.start + (j as u64) * PAGE_SIZE
311                    },
312                regions.inv(),
313                regions.frame_obligations == old(regions).frame_obligations,
314                regions.slot_owners.dom() == old(regions).slot_owners.dom(),
315                segment.range.start == range.start,
316                segment.range.end == range.start + i * PAGE_SIZE,
317            ensures
318                i == addr_len,
319            decreases addr_len - i,
320        {
321            let paddr_in = range.start + i * PAGE_SIZE;
322            let (paddr, meta) = metadata_fn(paddr_in);
323
324            let ghost regions_pre = *regions;
325            let res = #[verus_spec(with Tracked(regions))]
326            Frame::<M>::from_unused(paddr, meta);
327            let frame = match res {
328                Ok(f) => f,
329                Err(e) => {
330                    let mut p = range.start;
331                    let ghost mut k: int = 0;
332                    while p < segment.range.end
333                        invariant
334                            regions.inv(),
335                            regions.frame_obligations == old(regions).frame_obligations,
336                            regions.slot_owners.dom() == old(regions).slot_owners.dom(),
337                            range.start % PAGE_SIZE == 0,
338                            i == addrs.len(),
339                            segment.range.end == range.start + i * PAGE_SIZE,
340                            segment.range.end <= MAX_PADDR,
341                            range.start <= p <= segment.range.end,
342                            p == range.start + k * PAGE_SIZE,
343                            p % PAGE_SIZE == 0,
344                            0 <= k <= i,
345                            forall|j: int|
346                                #![trigger addrs[j]]
347                                k <= j < addrs.len() ==> {
348                                    let idx = frame_to_index(addrs[j]);
349                                    &&& regions.slots.contains_key(idx)
350                                    &&& regions.slot_owners.contains_key(idx)
351                                    &&& regions.slot_owners[idx].slot_vaddr == meta_addr(idx)
352                                    &&& regions.slot_owners[idx].inner_perms.ref_count.value() > 0
353                                    &&& regions.slot_owners[idx].inner_perms.ref_count.value()
354                                        <= crate::mm::frame::meta::REF_COUNT_MAX
355                                    &&& regions.slot_owners[idx].paths_in_pt.is_empty()
356                                    &&& addrs[j] % PAGE_SIZE == 0
357                                    &&& addrs[j] < MAX_PADDR
358                                    &&& addrs[j] == range.start + (j as u64) * PAGE_SIZE
359                                },
360                        decreases segment.range.end - p,
361                    {
362                        let ghost reclaim_pre = *regions;
363                        let ghost idx_k = frame_to_index(p);
364                        proof {
365                            broadcast use group_page_meta;
366
367                            assert(addrs[k] == p);
368                            assert(meta_addr(idx_k) == frame_to_meta(p));
369                            assert(regions.slots.contains_key(idx_k));
370                        }
371                        proof_decl! {
372                            let tracked from_raw_obl: vstd_extra::drop_tracking::DropObligation<
373                                usize,
374                            >;
375                        }
376                        let frame = unsafe {
377                            #[verus_spec(with Tracked(regions) => Tracked(from_raw_obl))]
378                            Frame::<M>::from_raw(p)
379                        };
380                        frame.drop(Tracked(regions), Tracked(from_raw_obl));
381                        proof {
382                            assert forall|j: int|
383                                #![trigger addrs[j]]
384                                (k + 1) <= j < addrs.len() implies ({
385                                let idx = frame_to_index(addrs[j]);
386                                &&& regions.slots.contains_key(idx)
387                                &&& regions.slot_owners.contains_key(idx)
388                                &&& regions.slot_owners[idx] == reclaim_pre.slot_owners[idx]
389                            }) by {
390                                assert(addrs[j] != p);
391                                crate::specs::mm::frame::mapping::lemma_frame_to_index_injective(
392                                    addrs[j],
393                                    p,
394                                );
395                            };
396                        }
397                        p = p + PAGE_SIZE;
398                        proof {
399                            k = k + 1;
400                        }
401                    }
402                    return {
403                        proof_with!(|= Tracked(owner));
404                        Err(e)
405                    };
406                },
407            };
408
409            let _ = ManuallyDrop::new(frame, Tracked(regions));
410            segment.range.end = paddr + PAGE_SIZE;
411            proof {
412                broadcast use group_page_meta;
413
414                regions.inv_implies_correct_addr(paddr);
415                let idx = frame_to_index(paddr);
416                axiom_mmio_usage_iff_mmio_paddr(regions.slot_owners[idx]);
417                axiom_mmio_usage_iff_mmio_paddr(regions_pre.slot_owners[idx]);
418                assert(regions_pre.slot_owners[idx].paths_in_pt.is_empty());
419                assert(regions.slot_owners[idx].paths_in_pt
420                    == regions_pre.slot_owners[idx].paths_in_pt);
421                assert(regions.frame_obligations == regions_pre.frame_obligations);
422                addrs.tracked_push(paddr);
423            }
424
425            i += 1;
426        }
427
428        proof {
429            // Per-frame migration: record one forgotten reference per frame in
430            // `frame_obligations`. The construction loop above is net-zero on
431            // the ledger (each `Frame::from_unused` mint is cancelled by its
432            // `ManuallyDrop`); this post-loop pass records the segment's
433            // retained per-frame references, replacing the old single
434            // range-keyed `obligations` entry.
435            // The construction loop preserved `frame_obligations`, so the mint
436            // helper's exact delta (stated against the pre-mint state) telescopes
437            // to the function's entry state for the postcondition.
438            assert(regions.frame_obligations == old(regions).frame_obligations);
439            crate::specs::mm::frame::segment::tracked_mint_seg_obligations(
440                regions,
441                range.start,
442                addr_len as int,
443            );
444            owner = Some(SegmentOwner { range, _marker: core::marker::PhantomData });
445
446            assert forall|addr: usize|
447                #![trigger frame_to_index(addr)]
448                range.start <= addr < range.end && addr % PAGE_SIZE == 0 implies {
449                regions.slots.contains_key(frame_to_index(addr))
450            } by {
451                let j = (addr - range.start) / PAGE_SIZE as int;
452                assert(addrs[j as int] == addr);
453            }
454        }
455
456        proof_with!(|= Tracked(owner));
457        Ok(segment)
458    }
459
460    /// Restores the [`Segment`] from the raw physical address range.
461    ///
462    /// # Verified Properties
463    /// ## Preconditions
464    /// - the meta region must satisfy its invariant;
465    /// - the segment-to-be (with the supplied `range`) must satisfy its invariant
466    ///   ([`Self::inv`]) and the well-formedness relation with `owner` ([`Self::wf`]);
467    /// - `owner` must relate correctly to `regions`.
468    ///
469    /// Following the [`UniqueFrame::from_raw`] pattern, the contract here ties
470    /// `range` and `owner` together up front so that the postcondition can
471    /// directly advertise `r.inv()` and `r.wf(&owner)` for the caller.
472    ///
473    /// ## Postconditions
474    /// - the returned segment satisfies its invariant and is well-formed with `owner`;
475    /// - the returned segment has the same physical address range as the input;
476    /// - the meta region is unchanged (preserving the relation with `owner`).
477    ///
478    /// # Safety
479    ///
480    /// The range must be a forgotten [`Segment`] that matches the type `M`.
481    /// The caller must provide the permissions that were returned by [`Self::into_raw`].
482    #[verus_spec(r =>
483        with
484            Tracked(regions): Tracked<&mut MetaRegionOwners>,
485            Tracked(owner): Tracked<SegmentOwner<M>>,
486        requires
487            old(regions).inv(),
488            owner.inv(),
489            range == owner.range,
490            owner.relate_regions(*old(regions)),
491        ensures
492            r.range() == range,
493            r.inv(),
494            r.wf(&owner),
495            final(regions).inv(),
496            *final(regions) == *old(regions),
497    )]
498    pub(crate) unsafe fn from_raw(range: Range<Paddr>) -> Self {
499        Self { range, _marker: core::marker::PhantomData }
500    }
501}
502
503#[verus_verify]
504impl<M: AnyFrameMeta + ?Sized> Segment<M> {
505    /// Gets the start physical address of the contiguous frames.
506    #[verus_verify(dual_spec)]
507    #[verus_spec(
508        returns
509            self.start_paddr(),
510    )]
511    pub fn start_paddr(&self) -> Paddr {
512        self.range.start
513    }
514
515    /// Gets the end physical address of the contiguous frames.
516    #[verus_verify(dual_spec)]
517    #[verus_spec(
518        returns
519            self.end_paddr(),
520    )]
521    pub fn end_paddr(&self) -> Paddr {
522        self.range.end
523    }
524
525    /// Gets the length in bytes of the contiguous frames.
526    #[verus_verify(dual_spec)]
527    #[verus_spec(r =>
528        requires
529            self.inv(),
530        ensures
531            r == self.end_paddr() - self.start_paddr(),
532        returns
533            self.size()
534    )]
535    pub fn size(&self) -> usize {
536        self.range.end - self.range.start
537    }
538
539    pub open spec fn range(&self) -> Range<Paddr> {
540        self.start_paddr()..self.end_paddr()
541    }
542}
543
544#[verus_verify]
545impl<M: AnyFrameMeta + Repr<MetaSlotStorage> + OwnerOf> Segment<M> {
546    /// Splits the frames into two at the given byte offset from the start.
547    ///
548    /// The resulting frames cannot be empty. So the offset cannot be neither
549    /// zero nor the length of the frames.
550    ///
551    /// # Verified Properties
552    /// ## Preconditions
553    /// - the segment must satisfy its invariant ([`Self::inv`]) and the well-formedness
554    ///   relation with the owner ([`Self::wf`]);
555    /// - the offset must be aligned and within bounds;
556    /// - the meta region must be valid and the owner must relate to it.
557    ///
558    /// ## Postconditions
559    /// - the resulting segments satisfy their invariants with the corresponding owners;
560    /// - they match [`Self::split_spec`];
561    /// - the original owner's permissions are split between the two new owners;
562    /// - both halves continue to relate correctly to `regions` (which is unchanged).
563    #[verus_spec(r =>
564        with
565            Tracked(owner): Tracked<SegmentOwner<M>>,
566            Tracked(regions): Tracked<&mut MetaRegionOwners>,
567                -> result_owners: (Tracked<SegmentOwner<M>>, Tracked<SegmentOwner<M>>),
568        requires
569            self.invariants(&owner, *old(regions)),
570            offset % PAGE_SIZE != 0 ==> may_panic(),
571            !(0 < offset && offset < self.size()) ==> may_panic(),
572        ensures
573            final(regions).slots == old(regions).slots,
574            final(regions).slot_owners == old(regions).slot_owners,
575            final(regions).frame_obligations == old(regions).frame_obligations,
576            (r.0, r.1) == self.split_spec(offset),
577            r.0.invariants(&result_owners.0@, *final(regions)),
578            r.1.invariants(&result_owners.1@, *final(regions)),
579    )]
580    #[verifier::spinoff_prover]
581    pub fn split(self, offset: usize) -> (Self, Self) {
582        assert!(offset % PAGE_SIZE == 0);
583        assert!(0 < offset && offset < self.size());
584
585        let ghost old_regions = *regions;
586
587        let old = ManuallyDrop::new(self, Tracked(regions));
588        let at = old.range.start + offset;
589
590        let ghost old_start = old@.start_paddr();
591        let ghost old_end = old@.end_paddr();
592
593        let tracked frame_own1 = SegmentOwner {
594            range: old_start..at,
595            _marker: core::marker::PhantomData,
596        };
597        let tracked frame_own2 = SegmentOwner {
598            range: at..old_end,
599            _marker: core::marker::PhantomData,
600        };
601        proof {
602            assert forall|i: int|
603                #![trigger frame_to_index((frame_own1.range.start + i * PAGE_SIZE) as usize)]
604                0 <= i < crate::specs::mm::frame::segment::seg_nframes(frame_own1.range) implies {
605                let idx = frame_to_index((frame_own1.range.start + i * PAGE_SIZE) as usize);
606                &&& regions.frame_obligations.count(idx) >= 1
607                &&& regions.slot_owners.contains_key(idx)
608                &&& regions.slots.contains_key(idx)
609                &&& regions.slot_owners[idx].slot_vaddr == meta_addr(idx)
610                &&& regions.slot_owners[idx].inner_perms.ref_count.value() > 0
611                &&& regions.slot_owners[idx].inner_perms.ref_count.value()
612                    <= crate::mm::frame::meta::REF_COUNT_MAX
613                &&& regions.slot_owners[idx].paths_in_pt.is_empty()
614                &&& regions.slot_owners[idx].usage is Frame
615            } by {
616                owner.relate_regions_at(old_regions, i);
617            }
618            assert forall|i: int|
619                #![trigger frame_to_index((frame_own2.range.start + i * PAGE_SIZE) as usize)]
620                0 <= i < crate::specs::mm::frame::segment::seg_nframes(frame_own2.range) implies {
621                let idx = frame_to_index((frame_own2.range.start + i * PAGE_SIZE) as usize);
622                &&& regions.frame_obligations.count(idx) >= 1
623                &&& regions.slot_owners.contains_key(idx)
624                &&& regions.slots.contains_key(idx)
625                &&& regions.slot_owners[idx].slot_vaddr == meta_addr(idx)
626                &&& regions.slot_owners[idx].inner_perms.ref_count.value() > 0
627                &&& regions.slot_owners[idx].inner_perms.ref_count.value()
628                    <= crate::mm::frame::meta::REF_COUNT_MAX
629                &&& regions.slot_owners[idx].paths_in_pt.is_empty()
630                &&& regions.slot_owners[idx].usage is Frame
631            } by {
632                owner.relate_regions_at(old_regions, i + (offset / PAGE_SIZE));
633            }
634
635            assert forall|i: int, j: int|
636                #![trigger frame_to_index((frame_own1.range.start + i * PAGE_SIZE) as usize),
637                    frame_to_index((frame_own1.range.start + j * PAGE_SIZE) as usize)]
638                0 <= i < j < crate::specs::mm::frame::segment::seg_nframes(
639                    frame_own1.range,
640                ) implies frame_to_index((frame_own1.range.start + i * PAGE_SIZE) as usize)
641                != frame_to_index((frame_own1.range.start + j * PAGE_SIZE) as usize) by {
642                owner.relate_regions_distinct(old_regions, i, j);
643            }
644            assert forall|i: int, j: int|
645                #![trigger frame_to_index((frame_own2.range.start + i * PAGE_SIZE) as usize),
646                    frame_to_index((frame_own2.range.start + j * PAGE_SIZE) as usize)]
647                0 <= i < j < crate::specs::mm::frame::segment::seg_nframes(
648                    frame_own2.range,
649                ) implies frame_to_index((frame_own2.range.start + i * PAGE_SIZE) as usize)
650                != frame_to_index((frame_own2.range.start + j * PAGE_SIZE) as usize) by {
651                owner.relate_regions_distinct(
652                    old_regions,
653                    i + (offset / PAGE_SIZE) as int,
654                    j + (offset / PAGE_SIZE),
655                );
656            }
657        }
658        proof_with!(|= (Tracked(frame_own1), Tracked(frame_own2)));
659        (
660            Self { range: old.range.start..at, _marker: core::marker::PhantomData },
661            Self { range: at..old.range.end, _marker: core::marker::PhantomData },
662        )
663    }
664
665    /// Precise panic condition for [`Self::slice`]. `slice` diverges iff:
666    ///  - the slice range is misaligned, reversed, or out of the segment's
667    ///    bounds (the diverging `assert!`s at the top of `slice`), or
668    ///  - **the specific per-frame slot that `slice` bumps** is already
669    ///    saturated (`inc_ref_count` would overflow). Unlike `query` which
670    ///    clones one item, `slice` bumps one refcount per page in the
671    ///    slice range, so the saturation disjunct is an *exists* over those
672    ///    specific paddrs `self.range.start + j * PAGE_SIZE` for
673    ///    `j ∈ [range.start/PAGE_SIZE, range.end/PAGE_SIZE)`.
674    pub open spec fn page_in_range_saturated(
675        self,
676        range: &Range<usize>,
677        regions: MetaRegionOwners,
678    ) -> bool {
679        exists|j: int|
680            #![trigger frame_to_index((self.range.start + j * PAGE_SIZE) as usize)]
681            (range.start as int) / (PAGE_SIZE as int) <= j < (range.end as int) / (PAGE_SIZE as int)
682                && regions.slot_owners[frame_to_index(
683                (self.start_paddr() + j * PAGE_SIZE) as usize,
684            )].inner_perms.ref_count.value() >= REF_COUNT_MAX
685    }
686
687    /// Gets an extra handle to the frames in the byte offset range.
688    ///
689    /// The sliced byte offset range in indexed by the offset from the start of
690    /// the contiguous frames. The resulting frames holds extra reference counts.
691    ///
692    /// # Verified Properties
693    /// ## Postconditions
694    /// - the resulting slice's range matches the slicing range and is in-bounds
695    ///   (the in-bounds check follows from the diverging `assert!` in the body);
696    /// - `regions` preserves invariants and key domains. Per-frame state — e.g.
697    ///   that a hypothetical `sub_owner.relate_regions(regions)` would hold —
698    ///   is *not* exposed; threading that through the per-frame ref-count bump
699    ///   loop would require a much heavier proof. Mirrors [`Segment::clone`].
700    ///
701    /// See also [`vstd::seq::Seq::subrange`].
702    #[verus_spec(r =>
703        with
704            Tracked(owner): Tracked<&SegmentOwner<M>>,
705            Tracked(regions): Tracked<&mut MetaRegionOwners>,
706        requires
707            self.invariants(owner, *old(regions)),
708            range.start % PAGE_SIZE != 0 ==> may_panic(),
709            range.end % PAGE_SIZE != 0 ==> may_panic(),
710            range.start > range.end ==> may_panic(),
711            self.range.start + range.end > self.range.end ==> may_panic(),
712            self.page_in_range_saturated(range, *old(regions)) ==> may_panic(),
713        ensures
714            range.start % PAGE_SIZE == 0,
715            range.end % PAGE_SIZE == 0,
716            range.start <= range.end,
717            self.range.start + range.end <= self.range.end,
718            !self.page_in_range_saturated(range, *old(regions)),
719            r.inv(),
720            r.start_paddr() == self.start_paddr() + range.start,
721            r.end_paddr() == self.start_paddr() + range.end,
722            r.end_paddr() <= self.end_paddr(),
723            final(regions).inv(),
724            final(regions).slots == old(regions).slots,
725            final(regions).slot_owners.dom() == old(regions).slot_owners.dom(),
726            final(regions).frame_obligations == old(regions).frame_obligations,
727    )]
728    #[verifier::spinoff_prover]
729    #[verifier::loop_isolation(false)]
730    pub fn slice(&self, range: &Range<usize>) -> Self {
731        // KNOWN BUG: potential overflows https://github.com/asterinas/asterinas/issues/3165
732        assume(self.range.start + range.start <= usize::MAX);
733        assume(self.range.start + range.end <= usize::MAX);
734
735        assert!(range.start % PAGE_SIZE == 0 && range.end % PAGE_SIZE == 0);
736        let start = self.range.start + range.start;
737        let end = self.range.start + range.end;
738        assert!(start <= end && end <= self.range.end);
739
740        let mut paddr = start;
741        let ghost addr_len = (end - start) / PAGE_SIZE as int;
742        let ghost first_perm_idx: int = (range.start / PAGE_SIZE) as int;
743        let ghost last_perm_idx: int = (range.end / PAGE_SIZE) as int;
744        let ghost mut i: int = 0;
745        loop
746            invariant
747                self.page_in_range_saturated(range, *old(regions)) ==> may_panic(),
748                regions.inv(),
749                regions.slots == old(regions).slots,
750                regions.slot_owners.dom() == old(regions).slot_owners.dom(),
751                regions.frame_obligations == old(regions).frame_obligations,
752                paddr == (start + i * PAGE_SIZE) as usize,
753                paddr <= end,
754                0 <= i <= addr_len,
755                paddr < end <==> i < addr_len,
756                first_perm_idx + i <= last_perm_idx,
757                forall|j: int|
758                    #![trigger frame_to_index((self.range.start + j * PAGE_SIZE) as usize)]
759                    first_perm_idx + i <= j < last_perm_idx ==> (
760                    *regions).slot_owners[frame_to_index(
761                        (self.range.start + j * PAGE_SIZE) as usize,
762                    )] == old(regions).slot_owners[frame_to_index(
763                        (self.range.start + j * PAGE_SIZE) as usize,
764                    )],
765                forall|j: int|
766                    #![trigger frame_to_index((self.range.start + j * PAGE_SIZE) as usize)]
767                    first_perm_idx <= j < first_perm_idx + i ==> old(
768                        regions,
769                    ).slot_owners[frame_to_index(
770                        (self.range.start + j * PAGE_SIZE) as usize,
771                    )].inner_perms.ref_count.value() < REF_COUNT_MAX,
772            decreases addr_len - i,
773        {
774            if paddr >= end {
775                break;
776            }
777            let ghost perm_idx: int = first_perm_idx + i;
778
779            proof {
780                owner.relate_regions_at(*old(regions), perm_idx);
781            }
782
783            unsafe {
784                #[verus_spec(with Tracked(regions))]
785                crate::mm::frame::inc_frame_ref_count(paddr)
786            };
787
788            paddr = paddr + PAGE_SIZE;
789
790            proof {
791                i = i + 1;
792                assert forall|j: int|
793                    #![trigger frame_to_index((self.range.start + j * PAGE_SIZE) as usize)]
794                    first_perm_idx + i <= j < last_perm_idx implies (
795                *regions).slot_owners[frame_to_index((self.range.start + j * PAGE_SIZE) as usize)]
796                    == old(regions).slot_owners[frame_to_index(
797                    (self.range.start + j * PAGE_SIZE) as usize,
798                )] by {};
799            }
800        }
801
802        Self { range: start..end, _marker: core::marker::PhantomData }
803    }
804
805    /// Forgets the [`Segment`] and gets a raw range of physical addresses.
806    ///
807    /// The segment's permissions are returned to the caller via `frame_perms`.
808    /// The caller is responsible for holding onto the permissions and providing
809    /// them back when restoring the segment with [`Self::from_raw`].
810    ///
811    /// # Verified Properties
812    /// ## Preconditions
813    /// - the segment must satisfy the invariant with the owner;
814    /// - the meta region in `regions` must satisfy the invariant;
815    /// - the owner must relate correctly to `regions`.
816    ///
817    /// ## Postconditions
818    /// - the returned physical address range matches the segment's range;
819    /// - the meta region is unchanged (preserving the relation with the returned owner).
820    #[verus_spec(r =>
821        with
822            Tracked(regions): Tracked<&mut MetaRegionOwners>,
823            Tracked(owner): Tracked<SegmentOwner<M>>,
824                -> frame_perms: Tracked<SegmentOwner<M>>,
825        requires
826            self.invariants(&owner, *old(regions)),
827        ensures
828            r == self.range(),
829            final(regions).inv(),
830            *final(regions) == *old(regions),
831            frame_perms@ == owner,
832    )]
833    pub(crate) fn into_raw(self) -> Range<Paddr> {
834        let range = self.range.clone();
835        let _ = ManuallyDrop::new(self, Tracked(regions));
836
837        proof_with!(|= Tracked(owner));
838        range
839    }
840
841    /// Returns the number of pages of the contiguous frames.
842    #[verifier::inline]
843    pub open spec fn nrpage_spec(&self) -> usize
844        recommends
845            self.inv(),
846    {
847        self.size() / PAGE_SIZE
848    }
849
850    /// Splits the contiguous frames into two at the given byte offset from the start in spec mode.
851    pub closed spec fn split_spec(self, offset: usize) -> (Self, Self)
852        recommends
853            self.inv(),
854            offset % PAGE_SIZE == 0,
855            0 < offset < self.size(),
856    {
857        let at = (self.start_paddr() + offset) as usize;
858        let idx = at / PAGE_SIZE;
859        (
860            Self { range: self.start_paddr()..at, _marker: core::marker::PhantomData },
861            Self { range: at..self.end_paddr(), _marker: core::marker::PhantomData },
862        )
863    }
864}
865
866/// A frame yielded from a [`SegmentIterator`] together with its drop obligation.
867pub type SegmentIteratorItem<M> = (Frame<M>, Tracked<DropObligation<usize>>);
868
869/// Prophetic sequence state used to specify the frames that a segment iterator will yield.
870/// FIXME: Do we need another iterator struct?
871#[verifier::reject_recursive_types(T)]
872pub tracked struct SegmentIteratorProphecySeq<T> {
873    tracked var: ProphecyGhost<Seq<T>>,
874    ghost done: bool,
875}
876
877impl<T> SegmentIteratorProphecySeq<T> {
878    #[verifier::prophetic]
879    closed spec fn seq(&self) -> Seq<T> {
880        if self.done {
881            Seq::empty()
882        } else {
883            self.var.value()
884        }
885    }
886
887    proof fn new() -> (tracked res: Self)
888        ensures
889            !res.done,
890    {
891        SegmentIteratorProphecySeq { var: ProphecyGhost::new(), done: false }
892    }
893
894    proof fn resolve_cons(tracked &mut self, value: T)
895        requires
896            !old(self).done,
897        ensures
898            !final(self).done,
899            old(self).seq() == seq![value] + final(self).seq(),
900    {
901        let tracked mut var = ProphecyGhost::new();
902        tracked_swap(&mut var, &mut self.var);
903        var.resolve_dependent(&self.var, |tail| seq![value] + tail);
904    }
905
906    proof fn resolve_nil(tracked &mut self)
907        ensures
908            old(self).seq() == Seq::<T>::empty(),
909            final(self).seq() == Seq::<T>::empty(),
910            final(self).done,
911    {
912        if !self.done {
913            let tracked mut var = ProphecyGhost::new();
914            tracked_swap(&mut var, &mut self.var);
915            var.resolve(seq![]);
916            self.done = true;
917        }
918    }
919}
920
921/// Verified iterator over the frames owned by a [`Segment`].
922///
923/// The iterator advances an owned suffix of the segment while threading the
924/// metadata region owner and segment owner permissions.
925#[verifier::reject_recursive_types(M)]
926pub struct SegmentIterator<'a, M: AnyFrameMeta + Repr<MetaSlotStorage> + OwnerOf> {
927    segment: &'a Segment<M>,
928    range: Range<Paddr>,
929    tracked_regions: Tracked<&'a mut MetaRegionOwners>,
930    tracked_owner: Tracked<&'a mut SegmentOwner<M>>,
931    tracked_remaining: Tracked<SegmentIteratorProphecySeq<SegmentIteratorItem<M>>>,
932}
933
934#[verus_verify]
935impl<'a, M: AnyFrameMeta + Repr<MetaSlotStorage> + OwnerOf> SegmentIterator<'a, M> {
936    pub closed spec fn segment_ref(&self) -> &'a Segment<M> {
937        self.segment
938    }
939
940    pub closed spec fn range_spec(&self) -> Range<Paddr> {
941        self.range
942    }
943
944    pub closed spec fn current_segment(&self) -> Segment<M> {
945        Segment { range: self.range.start..self.range.end, _marker: core::marker::PhantomData }
946    }
947
948    #[verifier::prophetic]
949    pub closed spec fn remaining_spec(&self) -> Seq<SegmentIteratorItem<M>> {
950        self.tracked_remaining@.seq()
951    }
952
953    #[verifier::type_invariant]
954    pub closed spec fn type_inv(self) -> bool {
955        &&& self.segment.inv()
956        &&& self.segment.range.start <= self.range.start
957        &&& self.range.end == self.segment.range.end
958        &&& self.current_segment().invariants(self.tracked_owner@, *self.tracked_regions@)
959        &&& self.range.start < self.range.end ==> !self.tracked_remaining@.done
960    }
961
962    #[verus_spec(res =>
963        with
964            Tracked(regions): Tracked<&'a mut MetaRegionOwners>,
965            Tracked(owner): Tracked<&'a mut SegmentOwner<M>>,
966        requires
967            segment.invariants(owner, *regions),
968        ensures
969            res.segment_ref() == segment,
970            res.range_spec() == segment.range,
971            IteratorSpec::decrease(&res) is Some,
972            IteratorSpec::initial_value_relation(&res, &res),
973    )]
974    pub fn new(segment: &'a Segment<M>) -> Self {
975        Self {
976            segment,
977            range: segment.range.start..segment.range.end,
978            tracked_regions: Tracked(regions),
979            tracked_owner: Tracked(owner),
980            tracked_remaining: Tracked(SegmentIteratorProphecySeq::new()),
981        }
982    }
983
984    /// Advances the verified segment iterator by one frame.
985    ///
986    /// This helper is the proof-carrying body behind [`Iterator::next`]. The
987    /// tracked metadata region, segment owner, and prophecy state are supplied
988    /// through `#[verus_spec]`, keeping the executable signature free of tracked
989    /// arguments.
990    ///
991    /// # Verified Properties
992    /// ## Preconditions
993    /// - the segment must satisfy its invariant;
994    /// - `range` must be a suffix of `segment.range`;
995    /// - the segment represented by `range` must satisfy its invariant with the
996    ///   tracked owner and meta regions, which includes the owner/region relation;
997    /// - if `range` is non-empty, the remaining prophecy sequence must not have
998    ///   been resolved to `done`.
999    ///
1000    /// ## Postconditions
1001    /// - the segment must satisfy its invariant;
1002    /// - `range` remains a suffix of `segment.range`;
1003    /// - the segment represented by the final `range` satisfies its invariant
1004    ///   with the final tracked owner and meta regions;
1005    /// - if the result is `None`, `range` is unchanged and both the old and final
1006    ///   remaining prophecy sequences are empty;
1007    /// - if the result is `Some(item)`, `range.start` advances by one page, the
1008    ///   yielded frame starts at the old `range.start`, and the old remaining
1009    ///   prophecy sequence is exactly `item` followed by the final one;
1010    /// - if the final `range` is still non-empty, the remaining prophecy sequence
1011    ///   is still unresolved.
1012    #[verus_spec(res =>
1013        with
1014            Tracked(regions_ref): Tracked<&mut &'a mut MetaRegionOwners>,
1015            Tracked(owner_ref): Tracked<&mut &'a mut SegmentOwner<M>>,
1016            Tracked(remaining): Tracked<&mut SegmentIteratorProphecySeq<SegmentIteratorItem<M>>>,
1017        requires
1018            segment.inv(),
1019            segment.range.start <= old(range).start,
1020            old(range).end == segment.range.end,
1021            (Segment {
1022                range: old(range).start..old(range).end,
1023                _marker: core::marker::PhantomData,
1024            }).invariants(*old(owner_ref), **old(regions_ref)),
1025            old(range).start < old(range).end ==> !old(remaining).done,
1026        ensures
1027            segment.inv(),
1028            segment.range.start <= final(range).start,
1029            final(range).end == segment.range.end,
1030            (Segment {
1031                range: final(range).start..final(range).end,
1032                _marker: core::marker::PhantomData,
1033            }).invariants(*final(owner_ref), **final(regions_ref)),
1034            final(range).start < final(range).end ==> !final(remaining).done,
1035            match res {
1036                None => {
1037                    &&& final(range).start == old(range).start
1038                    &&& final(range).end == old(range).end
1039                    &&& old(remaining).seq() == Seq::<SegmentIteratorItem<M>>::empty()
1040                    &&& final(remaining).seq() == Seq::<SegmentIteratorItem<M>>::empty()
1041                },
1042                Some(item) => {
1043                    &&& final(range).start == old(range).start + PAGE_SIZE
1044                    &&& final(range).end == old(range).end
1045                    &&& item.0.paddr() == old(range).start
1046                    &&& old(remaining).seq() == seq![item] + final(remaining).seq()
1047                },
1048            },
1049        no_unwind
1050    )]
1051    fn next_inner(segment: &'a Segment<M>, range: &mut Range<Paddr>) -> (res: Option<
1052        SegmentIteratorItem<M>,
1053    >) {
1054        if range.start < range.end {
1055            let ghost old_remaining = remaining.seq();
1056            let ghost old_range = range.start..range.end;
1057            let ghost old_owner = **owner_ref;
1058            let ghost old_regions = **regions_ref;
1059            let paddr = range.start;
1060
1061            proof {
1062                old_owner.relate_regions_at(old_regions, 0);
1063                assert(paddr == old_range.start);
1064            }
1065
1066            proof_decl! {
1067                let tracked from_raw_obl: DropObligation<usize>;
1068            }
1069            let frame = unsafe {
1070                #[verus_spec(with Tracked(*regions_ref) => Tracked(from_raw_obl))]
1071                Frame::<M>::from_raw(paddr)
1072            };
1073
1074            proof {
1075                let new_range = ((old_range.start + PAGE_SIZE) as usize)..old_range.end;
1076                let tracked redeem_tok = DropObligation::tracked_mint(frame.index());
1077                (*regions_ref).tracked_redeem_frame_obligation(redeem_tok);
1078                assert((**regions_ref).frame_obligations == old_regions.frame_obligations);
1079                (**owner_ref).range = new_range;
1080
1081                assert forall|i: int|
1082                    #![trigger frame_to_index(((**owner_ref).range.start + i * PAGE_SIZE) as usize)]
1083                    0 <= i < crate::specs::mm::frame::segment::seg_nframes(
1084                        (**owner_ref).range,
1085                    ) implies {
1086                    let idx = frame_to_index(((**owner_ref).range.start + i * PAGE_SIZE) as usize);
1087                    &&& (**regions_ref).frame_obligations.count(idx) >= 1
1088                    &&& (**regions_ref).slot_owners.contains_key(idx)
1089                    &&& (**regions_ref).slots.contains_key(idx)
1090                    &&& (**regions_ref).slot_owners[idx].slot_vaddr == meta_addr(idx)
1091                    &&& (**regions_ref).slot_owners[idx].inner_perms.ref_count.value() > 0
1092                    &&& (**regions_ref).slot_owners[idx].inner_perms.ref_count.value()
1093                        <= crate::mm::frame::meta::REF_COUNT_MAX
1094                    &&& (**regions_ref).slot_owners[idx].paths_in_pt.is_empty()
1095                    &&& (**regions_ref).slot_owners[idx].usage is Frame
1096                } by {
1097                    old_owner.relate_regions_at(old_regions, i + 1);
1098                    old_owner.relate_regions_distinct(old_regions, 0, i + 1);
1099                }
1100                assert forall|i: int, j: int|
1101                    #![trigger frame_to_index(((**owner_ref).range.start + i * PAGE_SIZE) as usize),
1102                        frame_to_index(((**owner_ref).range.start + j * PAGE_SIZE) as usize)]
1103                    0 <= i < j < crate::specs::mm::frame::segment::seg_nframes(
1104                        (**owner_ref).range,
1105                    ) implies frame_to_index(((**owner_ref).range.start + i * PAGE_SIZE) as usize)
1106                    != frame_to_index(((**owner_ref).range.start + j * PAGE_SIZE) as usize) by {
1107                    old_owner.relate_regions_distinct(old_regions, i + 1, j + 1);
1108                }
1109                broadcast use group_page_meta;
1110
1111                assert((**regions_ref).slots[frame.index()].pptr() == frame.ptr);
1112                assert(frame.wf_with_region(**regions_ref));
1113            }
1114
1115            range.start = range.start + PAGE_SIZE;
1116            let item = (frame, Tracked(from_raw_obl));
1117            proof {
1118                remaining.resolve_cons(item);
1119                broadcast use vstd::seq::group_seq_axioms;
1120
1121                assert(remaining.seq() == old_remaining.drop_first());
1122                assert(item == old_remaining[0]);
1123            }
1124            Some(item)
1125        } else {
1126            let ghost old_remaining = remaining.seq();
1127            proof {
1128                remaining.resolve_nil();
1129                assert(remaining.seq() == old_remaining);
1130            }
1131            None
1132        }
1133    }
1134}
1135
1136impl<M: AnyFrameMeta + Repr<MetaSlotStorage> + OwnerOf> IteratorSpecImpl for SegmentIterator<
1137    '_,
1138    M,
1139> {
1140    open spec fn obeys_prophetic_iter_laws(&self) -> bool {
1141        true
1142    }
1143
1144    #[verifier::prophetic]
1145    closed spec fn remaining(&self) -> Seq<Self::Item> {
1146        self.remaining_spec()
1147    }
1148
1149    #[verifier::prophetic]
1150    closed spec fn will_return_none(&self) -> bool {
1151        true
1152    }
1153
1154    closed spec fn decrease(&self) -> Option<nat> {
1155        Some((self.range.end - self.range.start) as nat)
1156    }
1157
1158    #[verifier::prophetic]
1159    open spec fn initial_value_relation(&self, init: &Self) -> bool {
1160        &&& IteratorSpec::remaining(init) == IteratorSpec::remaining(self)
1161        &&& init.remaining_spec() == self.remaining_spec()
1162        &&& init.segment_ref() == self.segment_ref()
1163        &&& init.range_spec() == self.range_spec()
1164    }
1165
1166    open spec fn peek(&self, index: int) -> Option<Self::Item> {
1167        None
1168    }
1169}
1170
1171impl<M: AnyFrameMeta + Repr<MetaSlotStorage> + OwnerOf> Iterator for SegmentIterator<'_, M> {
1172    type Item = SegmentIteratorItem<M>;
1173
1174    /// Gets the next frame in the segment.
1175    fn next(&mut self) -> Option<Self::Item> {
1176        proof {
1177            use_type_invariant(&*self);
1178        }
1179
1180        #[verus_spec(with
1181            Tracked(self.tracked_regions.borrow_mut()),
1182            Tracked(self.tracked_owner.borrow_mut()),
1183            Tracked(self.tracked_remaining.borrow_mut()),
1184        )]
1185        SegmentIterator::next_inner(self.segment, &mut self.range)
1186    }
1187}
1188
1189#[verus_verify]
1190impl<M: AnyFrameMeta + Repr<MetaSlotStorage>> From<Frame<M>> for Segment<M> {
1191    /// Converts a single [`Frame`] into a one-page [`Segment`] by forgetting
1192    /// the frame and recording its paddr range. Symmetric to vostd's
1193    /// `From<Frame<M>> for Segment<M>`.
1194    //
1195    // Trusted at the trait boundary: the `From::from` signature can't thread
1196    // `Tracked` metadata to bump the frame's `raw_count` via the verified
1197    // `vstd_extra::drop_tracking::ManuallyDrop`, so we use `core::mem`'s
1198    // version. Same trust pattern as the `Iterator` impl.
1199    #[verifier::external_body]
1200    fn from(frame: Frame<M>) -> Self {
1201        let pa = frame.start_paddr();
1202        let _ = core::mem::ManuallyDrop::new(frame);
1203        Self { range: pa..(pa + PAGE_SIZE), _marker: core::marker::PhantomData }
1204    }
1205}
1206
1207impl<M: AnyFrameMeta + Repr<MetaSlotStorage> + OwnerOf> Iterator for Segment<M> {
1208    type Item = Frame<M>;
1209
1210    /// Gets the next frame in the segment.
1211    //
1212    // Verus's `core::iter::Iterator` support doesn't allow threading `Tracked`
1213    // metadata through the trait method's fixed signature, so the verified
1214    // `next` lives as an inherent method on `Segment<M>` and the trait body
1215    // is trusted at the trait boundary.
1216    #[verifier::external_body]
1217    fn next(&mut self) -> Option<Self::Item> {
1218        if self.range.start < self.range.end {
1219            // SAFETY: each frame in the range was a forgotten handle when
1220            // creating the `Segment` object.
1221            let frame = unsafe { Frame::<M>::from_raw(self.range.start) };
1222            self.range.start = self.range.start + PAGE_SIZE;
1223            Some(frame)
1224        } else {
1225            None
1226        }
1227    }
1228}
1229
1230impl<M: AnyFrameMeta + Repr<MetaSlotStorage>> Segment<M> {
1231    /// Verified drop: iterates over each frame in the segment, decrements its
1232    /// reference count, and (when last ref) tears down the metadata.
1233    ///
1234    /// Per-frame linear-drop: before the teardown loop, this redeems the one
1235    /// `frame_obligations` entry the segment retained per frame (minted by
1236    /// `Segment::from_unused`). Failing to drop the segment leaves those
1237    /// per-frame counts outstanding and breaks
1238    /// [`MetaRegionOwners::clean_inv`] at the enclosing function's exit.
1239    #[verus_spec(
1240        with Tracked(owner): Tracked<SegmentOwner<M>>,
1241            Tracked(regions): Tracked<&mut MetaRegionOwners>
1242        requires
1243            self.invariants(&owner, *old(regions)),
1244            forall|i: int|
1245                #![trigger frame_to_index((self.range.start + i * PAGE_SIZE) as usize)]
1246                0 <= i < crate::specs::mm::frame::segment::seg_nframes(self.range) ==> {
1247                    let idx = frame_to_index((self.range.start + i * PAGE_SIZE) as usize);
1248                    old(regions).slot_owners[idx].inner_perms.ref_count.value() == 1 ==> {
1249                        &&& old(regions).slot_owners[idx].inner_perms.storage.is_init()
1250                        &&& old(regions).slot_owners[idx].inner_perms.in_list.value() == 0
1251                    }
1252                },
1253        ensures
1254            final(regions).inv(),
1255    )]
1256    pub fn drop(self) {
1257        let tracked owner = owner;
1258        let ghost old_owner = owner;
1259        let ghost n = crate::specs::mm::frame::segment::seg_nframes(self.range);
1260        let mut paddr = self.range.start;
1261
1262        let ghost mut k: int = 0;
1263
1264        let ghost old_owner = owner;
1265
1266        assert forall|i: int| #![trigger frame_idx_at(self.range.start, i)] 0 <= i < n implies {
1267            let idx = frame_idx_at(self.range.start, i);
1268            old(regions).slot_owners[idx].inner_perms.ref_count.value() == 1 ==> {
1269                &&& old(regions).slot_owners[idx].inner_perms.storage.is_init()
1270                &&& old(regions).slot_owners[idx].inner_perms.in_list.value() == 0
1271            }
1272        } by {};
1273
1274        proof {
1275            assert(old_owner.range == self.range);
1276            assert forall|i: int|
1277                #![trigger frame_to_index((self.range.start + i * PAGE_SIZE) as usize)]
1278                0 <= i < n implies old(regions).frame_obligations.count(
1279                frame_to_index((self.range.start + i * PAGE_SIZE) as usize),
1280            ) >= 1 by {
1281                old_owner.relate_regions_at(*old(regions), i);
1282            };
1283            assert forall|i: int, j: int|
1284                #![trigger frame_to_index((self.range.start + i * PAGE_SIZE) as usize),
1285                    frame_to_index((self.range.start + j * PAGE_SIZE) as usize)]
1286                0 <= i < j < n implies frame_to_index((self.range.start + i * PAGE_SIZE) as usize)
1287                != frame_to_index((self.range.start + j * PAGE_SIZE) as usize) by {
1288                old_owner.relate_regions_distinct(*old(regions), i, j);
1289            };
1290            crate::specs::mm::frame::segment::tracked_redeem_seg_obligations(
1291                regions,
1292                self.range.start,
1293                n,
1294            );
1295        }
1296
1297        loop
1298            invariant
1299                regions.inv(),
1300                self.inv(),
1301                self.range.start <= paddr <= self.range.end,
1302                paddr == (self.range.start + k * PAGE_SIZE) as usize,
1303                paddr % PAGE_SIZE == 0,
1304                paddr <= MAX_PADDR,
1305                0 <= k <= n,
1306                n == (self.range.end - self.range.start) / PAGE_SIZE as int,
1307                paddr < self.range.end <==> k < n,
1308                forall|j: int|
1309                    #![trigger frame_to_index((self.range.start + j * PAGE_SIZE) as usize)]
1310                    k <= j < n ==> {
1311                        let idx = frame_to_index((self.range.start + j * PAGE_SIZE) as usize);
1312                        &&& regions.slot_owners.contains_key(idx)
1313                        &&& regions.slots.contains_key(idx)
1314                        &&& regions.slot_owners[idx] == old(regions).slot_owners[idx]
1315                    },
1316                forall|j: int|
1317                    #![trigger frame_idx_at(self.range.start, j)]
1318                    k <= j < n ==> regions.slot_owners.contains_key(
1319                        frame_idx_at(self.range.start, j),
1320                    ) && regions.slot_owners[frame_idx_at(self.range.start, j)] == old(
1321                        regions,
1322                    ).slot_owners[frame_idx_at(self.range.start, j)],
1323                regions.slot_owners.dom() == old(regions).slot_owners.dom(),
1324                self.wf(&old_owner),
1325                old_owner.inv(),
1326                old(regions).inv(),
1327                old_owner.relate_regions(*old(regions)),
1328                old_owner.range == self.range,
1329                forall|i: int|
1330                    #![trigger frame_to_index((self.range.start + i * PAGE_SIZE) as usize)]
1331                    0 <= i < n ==> {
1332                        let idx = frame_to_index((self.range.start + i * PAGE_SIZE) as usize);
1333                        old(regions).slot_owners[idx].inner_perms.ref_count.value() == 1 ==> {
1334                            &&& old(regions).slot_owners[idx].inner_perms.storage.is_init()
1335                            &&& old(regions).slot_owners[idx].inner_perms.in_list.value() == 0
1336                        }
1337                    },
1338            decreases n - k,
1339        {
1340            if paddr >= self.range.end {
1341                break;
1342            }
1343            proof {
1344                old_owner.relate_regions_at(*old(regions), k);
1345            }
1346
1347            proof_decl! {
1348                let tracked from_raw_obl: vstd_extra::drop_tracking::DropObligation<usize>;
1349            }
1350
1351            // SAFETY: each segment frame holds a forgotten reference;
1352            // `from_raw` mints the obligation and `frame.drop` consumes
1353            // it directly. The old "redeem-then-mint-then-drop" dance
1354            // is gone — `from_raw`'s freshly minted obligation feeds
1355            // straight into `frame.drop`.
1356            let frame = unsafe {
1357                #[verus_spec(with Tracked(regions) => Tracked(from_raw_obl))]
1358                Frame::<M>::from_raw(paddr)
1359            };
1360
1361            frame.drop(Tracked(regions), Tracked(from_raw_obl));
1362
1363            proof {
1364                assert forall|j: int|
1365                    #![trigger frame_to_index((self.range.start + j * PAGE_SIZE) as usize)]
1366                    (k + 1) <= j < n implies {
1367                    let idx = frame_to_index((self.range.start + j * PAGE_SIZE) as usize);
1368                    &&& regions.slot_owners.contains_key(idx)
1369                    &&& regions.slots.contains_key(idx)
1370                    &&& regions.slot_owners[idx] == old(regions).slot_owners[idx]
1371                } by {
1372                    old_owner.relate_regions_distinct(*old(regions), k, j);
1373                };
1374            }
1375
1376            paddr = paddr + PAGE_SIZE;
1377
1378            proof {
1379                k = k + 1;
1380            }
1381        }
1382    }
1383}
1384
1385/*impl<M: AnyFrameMeta> TryFrom<Segment<dyn AnyFrameMeta>> for Segment<M> {
1386    type Error = Segment<dyn AnyFrameMeta>;
1387
1388    open spec fn clone_ensures(
1389        self,
1390        old_perm: MetaRegionOwners,
1391        new_perm: MetaRegionOwners,
1392        res: Self,
1393    ) -> bool {
1394        &&& res.range == self.range
1395        &&& res.inv()
1396        &&& new_perm.inv()
1397    }
1398
1399    fn clone(&self, Tracked(perm): Tracked<&mut MetaRegionOwners>) -> (res: Self) {
1400        let mut paddr = self.range.start;
1401
1402        let ghost old_perm = *perm;
1403        loop
1404            invariant
1405                perm.inv(),
1406                self.inv(),
1407                perm.slots == old_perm.slots,
1408                perm.slot_owners.dom() == old_perm.slot_owners.dom(),
1409                // Linear-drop pilot: cloning a Segment doesn't mint or
1410                // redeem its obligation — the per-frame ref-count bump is
1411                // an Arc-style operation.
1412                perm.frame_obligations == old_perm.frame_obligations,
1413                self.range.start <= paddr <= self.range.end,
1414                paddr % PAGE_SIZE == 0,
1415                paddr <= MAX_PADDR,
1416                forall|pa: Paddr|
1417                    #![trigger frame_to_index(pa)]
1418                    (paddr <= pa < self.range.end && pa % PAGE_SIZE == 0) ==> {
1419                        let idx = frame_to_index(pa);
1420                        &&& perm.slots.contains_key(idx)
1421                        &&& has_safe_slot(pa)
1422                        &&& perm.slot_owners[idx].inner_perms.ref_count.value() > 0
1423                        &&& perm.slot_owners[idx].inner_perms.ref_count.value() + 1
1424                            < super::meta::REF_COUNT_MAX
1425                        &&& !MetaSlot::inc_ref_count_panic_cond(
1426                            perm.slot_owners[idx].inner_perms.ref_count,
1427                        )
1428                    },
1429            decreases self.range.end - paddr,
1430        {
1431            if paddr >= self.range.end {
1432                break;
1433            }
1434            #[verus_spec(with Tracked(perm))]
1435            crate::mm::frame::inc_frame_ref_count(paddr);
1436
1437            paddr = paddr + PAGE_SIZE;
1438        }
1439        // Since segments are homogeneous, we can safely assume that the rest
1440        // of the frames are of the same type. We just debug-check here.
1441        #[cfg(debug_assertions)]
1442        {
1443            for paddr in seg.range.clone().step_by(PAGE_SIZE) {
1444                let frame = unsafe { Frame::<dyn AnyFrameMeta>::from_raw(paddr) };
1445                let frame = ManuallyDrop::new(frame);
1446                debug_assert!((frame.dyn_meta() as &dyn core::any::Any).is::<M>());
1447            }
1448        }
1449        // SAFETY: The metadata is coerceable and the struct is transmutable.
1450        Ok(unsafe { core::mem::transmute::<Segment<dyn AnyFrameMeta>, Segment<M>>(seg) })
1451    }
1452}
1453
1454impl<M: AnyUFrameMeta> From<Segment<M>> for USegment {
1455    fn from(seg: Segment<M>) -> Self {
1456        // SAFETY: The metadata is coerceable and the struct is transmutable.
1457        unsafe { core::mem::transmute(seg) }
1458    }
1459}
1460
1461impl TryFrom<Segment<dyn AnyFrameMeta>> for USegment {
1462    type Error = Segment<dyn AnyFrameMeta>;
1463
1464    /// Try converting a [`Segment<dyn AnyFrameMeta>`] into [`USegment`].
1465    ///
1466    /// If the usage of the page is not the same as the expected usage, it will
1467    /// return the dynamic page itself as is.
1468    fn try_from(seg: Segment<dyn AnyFrameMeta>) -> core::result::Result<Self, Self::Error> {
1469        // SAFETY: for each page there would be a forgotten handle
1470        // when creating the `Segment` object.
1471        let first_frame = unsafe { Frame::<dyn AnyFrameMeta>::from_raw(seg.range.start) };
1472        let first_frame = ManuallyDrop::new(first_frame);
1473        if !first_frame.dyn_meta().is_untyped() {
1474            return Err(seg);
1475        }
1476        // Since segments are homogeneous, we can safely assume that the rest
1477        // of the frames are of the same type. We just debug-check here.
1478        #[cfg(debug_assertions)]
1479        {
1480            for paddr in seg.range.clone().step_by(PAGE_SIZE) {
1481                let frame = unsafe { Frame::<dyn AnyFrameMeta>::from_raw(paddr) };
1482                let frame = ManuallyDrop::new(frame);
1483                debug_assert!(frame.dyn_meta().is_untyped());
1484            }
1485        }
1486        // SAFETY: The metadata is coerceable and the struct is transmutable.
1487        Ok(unsafe { core::mem::transmute::<Segment<dyn AnyFrameMeta>, USegment>(seg) })
1488    }
1489} */
1490
1491impl<M: AnyFrameMeta + ?Sized> Inv for Segment<M> {
1492    /// The invariant of a [`Segment`]:
1493    ///
1494    /// - the physical addresses of the frames are aligned and within bounds.
1495    /// - the range is well-formed, i.e., the start is less than or equal to the end.
1496    open spec fn inv(self) -> bool {
1497        &&& self.start_paddr() % PAGE_SIZE == 0
1498        &&& self.end_paddr() % PAGE_SIZE == 0
1499        &&& self.start_paddr() <= self.end_paddr() <= MAX_PADDR
1500    }
1501}
1502
1503} // verus!