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