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