Skip to main content

ostd/specs/mm/
vm_space.rs

1use core::ops::Range;
2
3use vstd::prelude::*;
4
5use vstd::pervasive::proof_from_false;
6use vstd_extra::ownership::*;
7
8use crate::specs::{
9    arch::*,
10    mm::{
11        frame::meta_region_owners::MetaRegionOwners,
12        io::{VmIoMemView, VmIoOwner},
13        page_table::{
14            Mapping, OwnerSubtree,
15            cursor::{CursorView, owners::CursorOwner},
16            node::entry_owners::EntryOwner,
17        },
18        virt_mem::MemView,
19    },
20    task::InAtomicMode,
21};
22
23use crate::arch::mm::current_page_table_paddr;
24use crate::mm::{
25    MAX_USERSPACE_VADDR, Paddr, PagingConstsTrait, PagingLevel, Vaddr,
26    frame::untyped::UFrame,
27    io::{VmReader, VmWriter},
28    page_prop::PageProperty,
29    page_size,
30    page_table::*,
31    vm_space::{Cursor, CursorMut, MappedItem, UserPtConfig, VmSpace},
32};
33
34verus! {
35
36/// This struct is used for reading/writing memories represented by the
37/// [`VmReader`] or [`VmWriter`]. We also require a valid `vmspace_owner`
38/// must be present in this struct to ensure that the reader/writer is
39/// not created out of thin air.
40pub tracked struct VmIoPermission {
41    pub vmio_owner: VmIoOwner,
42    pub vmspace_owner: VmSpaceOwner,
43}
44
45/// A tracked struct for reasoning about verification-only properties of a [`VmSpace`].
46///
47/// This struct serves as a bookkeeper for all _active_ readers/writers within a specific
48/// virtual memory space. It maintains a holistic view of the memory range covered by the
49/// VM space it is tracking using a ghost [`MemView`]. It also maintains a tracked [`MemView`]
50/// for the current memories it is holding permissions for, which is a subset of the total
51/// memory range.
52///
53/// The management of each reader/writer and their corresponding memory views and permissions
54/// must talk to this struct's APIs for properly taking and returning permissions, which ensures
55/// the consistency of the overall VM space so that, e.g., no memory-aliasing will occur during
56/// the lifetime of the readers/writers, and that no reader/writer will be created out of thin air
57/// without the permission from the VM space owner (or the verification rejects invalid requests).
58///
59/// # Lifecycle of a reader/writer
60///
61/// We briefly introduce how we manage the lifecycle of a reader/writer under the management of
62/// [`VmSpaceOwner`] in this section. In the first place we require that whenever the reader or
63/// writer is being used to read or write memory, a matching permission called [`VmIoOwner`] must
64/// be present. Thus the key is to properly manage the creation and deletion of the [`VmIoOwner`].
65///
66/// 1. **Creation**: To create a new reader/writer, we first check if the new reader/writer can be created
67///   under the current VM space owner using the APIs [`Self::can_create_reader`] and [`Self::can_create_writer`].
68///   Interestingly, this creates an empty reader/writer that doesn't hold any memory view yet.
69/// 2. **Activation**: This is the magic step where we assign a memory view to the reader/writer. Via
70///  [`Self::activate_reader`] and [`Self::activate_writer`],
71///   the permissions will be moved from the VM space owner to the reader/writer. Note that readers do not
72///   need owned permissions so they just borrow the memory view from the VM space owner, while writers need to
73///   take the ownership of the memory view from the VM space owner.
74///   After this step, the reader/writer is fully activated and can be used to read/write memory.
75/// 3. **Disposal**: After the reader/writer finishes the reading/writing operation, we can dispose it via
76///   [`Self::dispose_reader`] and [`Self::dispose_writer`]. This step is the reverse of activation, where the
77///   permissions will be moved back from the reader/writer to the VM space owner. After this step, the reader/writer
78///   is considered disposed but re-usable as long as it is properly activated again before use.
79/// 4. **Removal**: If we know for sure that the reader/writer will never be used again, we can remove it from the
80///    active list via [`Self::remove_reader`] and [`Self::remove_writer`].
81pub tracked struct VmSpaceOwner {
82    /// The owner of the page table of this VM space.
83    pub page_table_owner: OwnerSubtree<UserPtConfig>,
84    /// Whether this VM space is currently active.
85    pub active: bool,
86    /// Active readers for this VM space.
87    pub readers: Seq<VmIoOwner>,
88    /// Active writers for this VM space.
89    pub writers: Seq<VmIoOwner>,
90    /// The "actual" memory view of this VM space where some
91    /// of the mappings may be  transferred to the writers.
92    pub mem_view: Option<MemView>,
93    /// This is the holistic view of the memory range covered by this VM space owner.
94    pub ghost mv_range: Option<MemView>,
95    /// Whether we allow shared reading.
96    pub shared_reader: bool,
97}
98
99impl<'a> Inv for VmSpaceOwner {
100    /// Defines the invariant for `VmSpaceOwner`.
101    ///
102    /// This specification ensures the consistency of the VM space, particularly
103    /// regarding memory access permissions and overlapping ranges.
104    ///
105    /// # Invariants
106    /// 1. **Recursion**: The underlying `page_table_owner` must satisfy its own invariant.
107    /// 2. **Finiteness**: The sets of readers and writers must be finite.
108    /// 3. **Active State Consistency**: If the VM space is marked as `active`:
109    ///    - **ID Separation**: A handle ID cannot be both a reader and a writer simultaneously.
110    ///    - **Element Validity**: All stored `VmIoOwner` instances must be valid and
111    ///                             their stored ID must match their map key.
112    ///    - **Memory Isolation (Read-Write)**: No Reader memory range may overlap with any Writer memory range.
113    ///    - **Memory Isolation (Write-Write)**: No Writer memory range may overlap with any other Writer memory range.
114    ///    - **Conditional Read Isolation**: If `shared_reader` is set, Readers must be mutually disjoint (cannot overlap).
115    open spec fn inv(self) -> bool {
116        &&& self.page_table_owner.inv()
117        &&& self.active ==> {
118            &&& self.mem_view_wf()
119            &&& self.mem_view matches Some(mem_view) ==> {
120                // Readers and writers are valid.
121                &&& forall|i: int|
122                    #![trigger self.readers[i]]
123                    0 <= i < self.readers.len() ==> {
124                        &&& self.readers[i].inv()
125                    }
126                &&& forall|i: int|
127                    #![trigger self.writers[i]]
128                    0 <= i < self.writers.len() ==> {
129                        &&& self.writers[i].inv()
130                    }
131                    // --- Memory Range Overlap Checks ---
132                    // Readers do not overlap with other readers, and writers do not overlap with other writers.
133                &&& forall|i, j: int|
134                    #![trigger self.readers[i], self.writers[j]]
135                    0 <= i < self.readers.len() && 0 <= j < self.writers.len() ==> {
136                        let r = self.readers[i];
137                        let w = self.writers[j];
138                        r.disjoint(w)
139                    }
140                &&& !self.shared_reader ==> forall|i, j: int|
141                    #![trigger self.readers[i], self.readers[j]]
142                    0 <= i < self.readers.len() && 0 <= j < self.readers.len() && i != j ==> {
143                        let r1 = self.readers[i];
144                        let r2 = self.readers[j];
145                        r1.disjoint(r2)
146                    }
147                &&& forall|i, j: int|
148                    #![trigger self.writers[i], self.writers[j]]
149                    0 <= i < self.writers.len() && 0 <= j < self.writers.len() && i != j ==> {
150                        let w1 = self.writers[i];
151                        let w2 = self.writers[j];
152                        w1.disjoint(w2)
153                    }
154            }
155        }
156    }
157}
158
159impl<'a> VmSpaceOwner {
160    /// This specification function ensures that the `mem_view` (remaining view),
161    /// `mv_range` (total view), and the views held by active readers and writers
162    /// maintain a consistent global state.
163    ///
164    /// The key properties include:
165    ///
166    /// ### 1. Existence Invariants
167    /// * `mem_view` is present if and only if `mv_range` is present.
168    ///
169    /// ### 2. Structural Integrity
170    /// * Both the remaining view and the total view must have finite memory mappings.
171    /// * Internal mappings within each view must be disjoint (no overlapping address ranges).
172    ///
173    /// ### 3. Global Consistency
174    /// * **Subset Relation**: The remaining view's mappings and memory domain must be subsets of the total view.
175    /// * **Translation Equality**: For any virtual address (VA), the address translation in the remaining view must match the translation in the total view.
176    ///
177    /// ### 4. Writer Invariants (Exclusive Ownership)
178    /// * **Type Verification**: Every writer must hold a `WriteView`.
179    /// * **Translation Consistency**: Writer translations must match the total view.
180    /// * **Mutual Exclusion**: If a writer translates a VA, that VA must not exist in the remaining view's translation table or memory domain.
181    /// * **Mapping Isolation**: Writer mappings must be disjoint from the remaining view's mappings and must be a subset of the total view's mappings.
182    ///
183    /// ### 5. Reader Invariants (Shared Consistency)
184    /// * **Type Verification**: Every reader must hold a `ReadView`.
185    /// * **Translation Consistency**: Reader translations must be consistent with the total view.
186    pub open spec fn mem_view_wf(self) -> bool {
187        &&& self.mem_view is Some
188            <==> self.mv_range is Some
189        // This requires that TotalMapping (mvv) = mv ∪ writer mappings ∪ reader mappings
190        &&& self.mem_view matches Some(remaining_view) ==> self.mv_range matches Some(total_view)
191            ==> {
192            &&& remaining_view.mappings_are_disjoint()
193            &&& total_view.mappings_are_disjoint()
194            // ======================
195            // Remaining Consistency
196            // ======================
197            &&& remaining_view.mappings.subset_of(total_view.mappings)
198            &&& remaining_view.memory.dom().subset_of(
199                total_view.memory.dom(),
200            )
201            // =====================
202            // Total View Consistency
203            // =====================
204            &&& forall|va: usize|
205                #![trigger remaining_view.addr_transl(va)]
206                #![trigger total_view.addr_transl(va)]
207                remaining_view.addr_transl(va) == total_view.addr_transl(
208                    va,
209                )
210            // =====================
211            // Writer correctness
212            // =====================
213            &&& forall|i: int|
214                #![trigger self.writers[i]]
215                0 <= i < self.writers.len() ==> {
216                    let writer = self.writers[i];
217
218                    &&& writer.mem_view matches Some(VmIoMemView::WriteView(writer_mv)) && {
219                        &&& forall|va: usize|
220                            #![trigger writer_mv.addr_transl(va)]
221                            #![trigger total_view.addr_transl(va)]
222                            #![trigger remaining_view.addr_transl(va)]
223                            #![trigger remaining_view.memory.contains_key(va)]
224                            {
225                                // We do not enforce that the range must be the same as the
226                                // memory view it holds as the writer may not consume all the
227                                // memory in its range.
228                                //
229                                // So we cannot directly reason on `self.range` here; we need
230                                // to instead ensure that the memory view it holds is consistent
231                                // with the total view and remaining view.
232                                &&& writer_mv.addr_transl(va) == total_view.addr_transl(va)
233                                &&& writer_mv.addr_transl(va) matches Some(_) ==> {
234                                    &&& remaining_view.addr_transl(va) is None
235                                    &&& !remaining_view.memory.contains_key(va)
236                                }
237                            }
238                        &&& writer_mv.mappings.disjoint(remaining_view.mappings)
239                        &&& writer_mv.mappings.subset_of(total_view.mappings)
240                        &&& writer_mv.memory.dom().subset_of(total_view.memory.dom())
241                    }
242                }
243                // =====================
244                // Reader correctness
245                // =====================
246            &&& forall|i: int|
247                #![trigger self.readers[i]]
248                0 <= i < self.readers.len() ==> {
249                    let reader = self.readers[i];
250
251                    &&& reader.mem_view matches Some(VmIoMemView::ReadView(reader_mv)) && {
252                        forall|va: usize|
253                            #![trigger reader_mv.addr_transl(va)]
254                            #![trigger total_view.addr_transl(va)]
255                            {
256                                // For readers there is no need to check remaining_view
257                                // because it is borrowed from remaining_view directly.
258                                &&& reader_mv.addr_transl(va) == total_view.addr_transl(va)
259                            }
260                    }
261                }
262        }
263    }
264
265    /// Determines whether a new reader can be safely instantiated within the VM address space.
266    ///
267    /// This specification function enforces memory isolation by ensuring that the
268    /// requested memory range does not intersect with the domain of any active writer.
269    pub open spec fn can_create_reader(&self, vaddr: Vaddr, len: usize) -> bool {
270        &&& forall|i: int|
271            #![trigger self.writers[i]]
272            0 <= i < self.writers.len() ==> !self.writers[i].overlaps_with_range(
273                vaddr..(vaddr + len) as usize,
274            )
275    }
276
277    /// Checks if we can create a new writer under this VM space owner.
278    ///
279    /// Similar to [`can_create_reader`], but checks both active readers and writers.
280    pub open spec fn can_create_writer(&self, vaddr: Vaddr, len: usize) -> bool {
281        &&& forall|i: int|
282            #![trigger self.readers[i]]
283            0 <= i < self.readers.len() ==> !self.readers[i].overlaps_with_range(
284                vaddr..(vaddr + len) as usize,
285            )
286        &&& forall|i: int|
287            #![trigger self.writers[i]]
288            0 <= i < self.writers.len() ==> !self.writers[i].overlaps_with_range(
289                vaddr..(vaddr + len) as usize,
290            )
291    }
292
293    /// Generates a new unique ID for VM IO owners.
294    ///
295    /// This assumes that we always generate a fresh ID that is not used by any existing
296    /// readers or writers. This should be safe as the ID space is unbounded and only used
297    /// to reason about different VM IO owners in verification.
298    pub uninterp spec fn new_vm_io_id(&self) -> nat;
299
300    /// Activates the given reader to read data from the user space of the current task.
301    /// # Verified Properties
302    /// ## Preconditions
303    /// - The [`VmSpace`] invariants must hold with respect to the [`VmSpaceOwner`], which must be active.
304    /// - The reader must be well-formed with respect to the [`VmSpaceOwner`].
305    /// - The reader's virtual address range must be mapped within the [`VmSpaceOwner`]'s memory view.
306    /// ## Postconditions
307    /// - The reader will be added to the [`VmSpace`]'s readers list.
308    /// - The reader will be activated with a view of its virtual address range taken from the [`VmSpaceOwner`]'s memory view.
309    /// ## Safety
310    /// - The function preserves all memory invariants.
311    /// - The [`MemView`] invariants ensure that the reader has a consistent view of memory.
312    /// - The [`VmSpaceOwner`] invariants ensure that the viewed memory is owned exclusively by this [`VmSpace`].
313    #[inline(always)]
314    #[verus_spec(r =>
315        requires
316            old(self).mem_view matches Some(mv) &&
317                forall |va: usize|
318                #![auto]
319                    old(owner_r).range.start <= va < old(owner_r).range.end ==>
320                        mv.addr_transl(va) is Some
321            ,
322            old(self).inv(),
323            old(self).active,
324            reader.wf(*old(owner_r)),
325            old(owner_r).mem_view is None,
326            reader.inv(),
327        ensures
328            reader.wf(*final(owner_r)),
329            final(owner_r).mem_view == Some(VmIoMemView::ReadView(old(self).mem_view@->0.borrow_at(
330                old(owner_r).range.start,
331                (old(owner_r).range.end - old(owner_r).range.start) as usize,
332            ))),
333    )]
334    pub proof fn activate_reader(
335        tracked &'a mut self,
336        reader: &'a VmReader<'a>,
337        tracked owner_r: &'a mut VmIoOwner,
338    ) {
339        let tracked mv = self.mem_view.tracked_borrow();
340        let tracked borrowed_mv = mv.tracked_borrow_at(
341            owner_r.range.start,
342            (owner_r.range.end - owner_r.range.start) as usize,
343        );
344
345        owner_r.mem_view = Some(VmIoMemView::ReadView(borrowed_mv));
346
347        assert forall|va: usize|
348            #![auto]
349            owner_r.range.start <= va < owner_r.range.end implies borrowed_mv.addr_transl(
350            va,
351        ) is Some by {
352            if owner_r.range.start <= va && va < owner_r.range.end {
353                assert(borrowed_mv.mappings == mv.mappings.filter(
354                    |m: Mapping|
355                        m.va_range.start < (owner_r.range.end) && m.va_range.end
356                            > owner_r.range.start,
357                ));
358                let o_borrow_mv = borrowed_mv.mappings.filter(
359                    |m: Mapping| m.va_range.start <= va < m.va_range.end,
360                );
361                let o_mv = mv.mappings.filter(|m: Mapping| m.va_range.start <= va < m.va_range.end);
362                assert(mv.addr_transl(va) is Some);
363                assert(o_mv.len() > 0);
364                let m = o_mv.choose();
365                vstd::set::lemma_set_choose_len(o_mv);
366                assert(o_mv.contains(m));
367                assert(o_borrow_mv.contains(m));
368                assert(o_borrow_mv.len() > 0);
369            }
370        }
371
372    }
373
374    /// Activates the given writer to write data to the user space of the current task.
375    /// # Verified Properties
376    /// ## Preconditions
377    /// - The [`VmSpace`] invariants must hold with respect to the [`VmSpaceOwner`], which must be active.
378    /// - The writer must be well-formed with respect to the `[VmSpaceOwner`].
379    /// - The writer's virtual address range must be mapped within the [`VmSpaceOwner`]'s memory view.
380    /// ## Postconditions
381    /// - The writer will be added to the [`VmSpace`]'s writers list.
382    /// - The writer will be activated with a view of its virtual address range taken from the [`VmSpaceOwner`]'s memory view.
383    /// ## Safety
384    /// - The function preserves all memory invariants.
385    /// - The [`MemView`] invariants ensure that the writer has a consistent view of memory.
386    /// - The [`VmSpaceOwner`] invariants ensure that the viewed memory is owned exclusively by
387    ///   this [`VmSpace`].
388    #[inline(always)]
389    #[verus_spec(r =>
390        requires
391            old(self).mem_view matches Some(mv) &&
392                forall |va: usize|
393                #![auto]
394                    old(owner_w).range.start <= va < old(owner_w).range.end ==>
395                        mv.addr_transl(va) is Some
396            ,
397            old(self).inv(),
398            old(self).active,
399            writer.wf(*old(owner_w)),
400            old(owner_w).mem_view is None,
401            writer.inv(),
402        ensures
403            writer.wf(*final(owner_w)),
404            final(owner_w).mem_view == Some(VmIoMemView::WriteView(old(self).mem_view@->0.split(
405                old(owner_w).range.start,
406                (old(owner_w).range.end - old(owner_w).range.start) as usize,
407            ).0)),
408    )]
409    pub proof fn activate_writer(
410        tracked &mut self,
411        writer: &'a VmWriter<'a>,
412        tracked owner_w: &'a mut VmIoOwner,
413    ) {
414        let tracked mut mv = self.mem_view.tracked_take();
415        let ghost old_mv = mv;
416        let tracked (lhs, rhs) = mv.tracked_split(
417            owner_w.range.start,
418            (owner_w.range.end - owner_w.range.start) as usize,
419        );
420
421        owner_w.mem_view = Some(VmIoMemView::WriteView(lhs));
422        self.mem_view = Some(rhs);
423
424        assert forall|va: usize|
425            #![auto]
426            owner_w.range.start <= va < owner_w.range.end implies lhs.addr_transl(va) is Some by {
427            if owner_w.range.start <= va && va < owner_w.range.end {
428                assert(lhs.mappings == old_mv.mappings.filter(
429                    |m: Mapping|
430                        m.va_range.start < (owner_w.range.end) && m.va_range.end
431                            > owner_w.range.start,
432                ));
433                let o_lhs = lhs.mappings.filter(
434                    |m: Mapping| m.va_range.start <= va < m.va_range.end,
435                );
436                let o_mv = old_mv.mappings.filter(
437                    |m: Mapping| m.va_range.start <= va < m.va_range.end,
438                );
439
440                assert(old_mv.addr_transl(va) is Some);
441                assert(o_mv.len() > 0);
442                broadcast use vstd::set::lemma_set_choose_len;
443
444                let m = o_mv.choose();
445                assert(o_mv.contains(m));
446                assert(m.va_range.start <= va < m.va_range.end);
447                assert(o_lhs.contains(m));
448                assert(o_lhs.len() > 0);
449            }
450        }
451
452    }
453
454    /// Removes the given reader from the active readers list.
455    ///
456    /// # Verified Properties
457    /// ## Preconditions
458    /// - The [`VmSpace`] invariants must hold with respect to the [`VmSpaceOwner`], which must be active.
459    /// - The index `idx` must be a valid index into the active readers list.
460    /// ## Postconditions
461    /// - The reader at index `idx` will be removed from the active readers list.
462    /// - The invariants of the [`VmSpaceOwner`] will still hold after the removal
463    pub proof fn remove_reader(tracked &mut self, idx: int)
464        requires
465            old(self).inv(),
466            old(self).active,
467            old(self).mem_view is Some,
468            0 <= idx < old(self).readers.len(),
469        ensures
470            final(self).inv(),
471            final(self).active == old(self).active,
472            final(self).shared_reader == old(self).shared_reader,
473            final(self).readers == old(self).readers.remove(idx),
474    {
475        self.readers.tracked_remove(idx);
476    }
477
478    /// Removes the given writer from the active writers list.
479    ///
480    /// # Verified Properties
481    /// ## Preconditions
482    /// - The [`VmSpace`] invariants must hold with respect to the [`VmSpaceOwner`], which must be active.
483    /// - The index `idx` must be a valid index into the active writers list.
484    /// ## Postconditions
485    /// - The writer at index `idx` will be removed from the active writers list.
486    /// - The memory view held by the removed writer will be returned to the VM space owner,
487    ///   ensuring that the overall memory view remains consistent.
488    /// - The invariants of the [`VmSpaceOwner`] will still hold after the removal.
489    pub proof fn remove_writer(tracked &mut self, idx: usize)
490        requires
491            old(self).inv(),
492            old(self).active,
493            old(self).mem_view is Some,
494            old(self).mv_range is Some,
495            0 <= idx < old(self).writers.len(),
496        ensures
497            final(self).inv(),
498            final(self).active == old(self).active,
499            final(self).shared_reader == old(self).shared_reader,
500            final(self).writers == old(self).writers.remove(idx as int),
501    {
502        let tracked writer = self.writers.tracked_remove(idx as int);
503
504        let tracked mv = match writer.mem_view {
505            Some(VmIoMemView::WriteView(mv)) => mv,
506            _ => { proof_from_false() },
507        };
508
509        let tracked mut remaining = self.mem_view.tracked_take();
510        let ghost old_remaining = remaining;
511        remaining.tracked_join(mv);
512        self.mem_view = Some(remaining);
513
514        assert(self.mem_view_wf()) by {
515            let ghost total_view = self.mv_range.unwrap();
516
517            assert(remaining.mappings == old_remaining.mappings.union(mv.mappings));
518            assert(remaining.memory == old_remaining.memory.union_prefer_right(mv.memory));
519            assert(self.mv_range == old(self).mv_range);
520            assert(self.mem_view == Some(remaining));
521
522            assert forall|va: usize|
523                #![auto]
524                { remaining.addr_transl(va) == total_view.addr_transl(va) } by {
525                let r_mappings = remaining.mappings.filter(
526                    |m: Mapping| m.va_range.start <= va < m.va_range.end,
527                );
528                let t_mappings = total_view.mappings.filter(
529                    |m: Mapping| m.va_range.start <= va < m.va_range.end,
530                );
531                let w_mappings = mv.mappings.filter(
532                    |m: Mapping| m.va_range.start <= va < m.va_range.end,
533                );
534
535                assert(r_mappings.subset_of(t_mappings));
536                assert(w_mappings.subset_of(t_mappings));
537
538                if r_mappings.len() > 0 {
539                    let r = r_mappings.choose();
540                    vstd::set::lemma_set_choose_len(r_mappings);
541                    assert(r_mappings.contains(r));
542                    assert(t_mappings.contains(r));
543                    assert(t_mappings.len() > 0);
544                }
545            }
546
547            assert forall|i: int| #![trigger self.writers[i]] 0 <= i < self.writers.len() implies {
548                let other_writer = self.writers[i];
549
550                &&& other_writer.mem_view matches Some(VmIoMemView::WriteView(writer_mv))
551                    && writer_mv.mappings.disjoint(remaining.mappings)
552            } by {
553                let other_writer = self.writers[i];
554
555                assert(old(self).inv());
556                let writer_mv = match other_writer.mem_view {
557                    Some(VmIoMemView::WriteView(mv)) => mv,
558                    _ => { proof_from_false() },
559                };
560
561                assert(exists|i: int|
562                    0 <= i < old(self).writers.len() ==> #[trigger] old(self).writers[i]
563                        == other_writer);
564                assert(exists|i: int|
565                    0 <= i < old(self).writers.len() ==> #[trigger] old(self).writers[i] == writer);
566                assert(mv.mappings.disjoint(writer_mv.mappings));
567            }
568        }
569    }
570
571    /// Disposes the given reader, releasing its ownership on the memory range.
572    ///
573    /// This does not mean that the owner is discarded; it indicates that someone
574    /// who finishes the reading operation can let us reclaim the permission.
575    /// The deletion of the reader is done via another API [`VmSpaceOwner::remove_reader`].
576    ///
577    /// Typically this API is called in two scenarios:
578    ///
579    /// 1. The reader has been created and we immediately move the ownership into us.
580    /// 2. The reader has finished the reading and need to return the ownership back.
581    pub proof fn dispose_reader(tracked &mut self, tracked owner: VmIoOwner)
582        requires
583            owner.inv(),
584            old(self).inv(),
585            old(self).active,
586            old(self).mv_range matches Some(total_view) && owner.mem_view matches Some(
587                VmIoMemView::ReadView(mv),
588            ) && old(self).mem_view matches Some(remaining) && {
589                forall|va: usize|
590                    #![auto]
591                    {
592                        &&& total_view.addr_transl(va) == mv.addr_transl(va)
593                    }
594            },
595            forall|i: int|
596                #![trigger old(self).writers[i]]
597                0 <= i < old(self).writers.len() ==> old(self).writers[i].disjoint(owner),
598            forall|i: int|
599                #![trigger old(self).readers[i]]
600                0 <= i < old(self).readers.len() ==> old(self).readers[i].disjoint(owner),
601        ensures
602            final(self).inv(),
603            final(self).active == old(self).active,
604            final(self).shared_reader == old(self).shared_reader,
605            owner.range.start < owner.range.end ==> final(self).readers == old(self).readers.push(
606                owner,
607            ),
608    {
609        if owner.range.start < owner.range.end {
610            // Return the memory view back to the vm space owner.
611            self.readers.tracked_push(owner);
612        }
613    }
614
615    /// Disposes the given writer, releasing its ownership on the memory range.
616    ///
617    /// This does not mean that the owner is discarded; it indicates that someone
618    /// who finishes the writing operation can let us reclaim the permission.
619    ///
620    /// The deletion of the writer is through another API [`VmSpaceOwner::remove_writer`].
621    pub proof fn dispose_writer(tracked &mut self, tracked owner: VmIoOwner)
622        requires
623            old(self).inv(),
624            old(self).active,
625            owner.inv(),
626            old(self).mv_range matches Some(total_view) && owner.mem_view matches Some(
627                VmIoMemView::WriteView(mv),
628            ) && old(self).mem_view matches Some(remaining) && {
629                &&& forall|va: usize|
630                    #![auto]
631                    {
632                        &&& mv.addr_transl(va) == total_view.addr_transl(va)
633                        &&& mv.addr_transl(va) matches Some(_) ==> {
634                            &&& remaining.addr_transl(va) is None
635                            &&& !remaining.memory.contains_key(va)
636                        }
637                    }
638                &&& mv.mappings.disjoint(remaining.mappings)
639                &&& mv.mappings.subset_of(total_view.mappings)
640                &&& mv.memory.dom().subset_of(total_view.memory.dom())
641            },
642            forall|i: int|
643                #![trigger old(self).writers[i]]
644                0 <= i < old(self).writers.len() ==> old(self).writers[i].disjoint(owner),
645            forall|i: int|
646                #![trigger old(self).readers[i]]
647                0 <= i < old(self).readers.len() ==> old(self).readers[i].disjoint(owner),
648        ensures
649            final(self).inv(),
650            final(self).active == old(self).active,
651            final(self).shared_reader == old(self).shared_reader,
652            owner.range.start < owner.range.end ==> final(self).writers == old(self).writers.push(
653                owner,
654            ),
655    {
656        // If the writer has consumed all the memory, nothing to do;
657        // just discard the writer and return the permission back to
658        // the vm space owner.
659        match owner.mem_view {
660            Some(VmIoMemView::WriteView(ref writer_mv)) => {
661                if owner.range.start < owner.range.end {
662                    self.writers.tracked_push(owner);
663                }
664            },
665            _ => {
666                assert(false);
667            },
668        }
669    }
670}
671
672impl<'a> VmSpace<'a> {
673    pub open spec fn reader_success_cond(self, vaddr: Vaddr, len: usize) -> bool {
674        &&& vaddr != 0 && len > 0 && vaddr + len <= MAX_USERSPACE_VADDR
675        &&& current_page_table_paddr() == self.pt.root_paddr_spec()
676    }
677
678    pub open spec fn writer_requires(
679        &self,
680        vm_owner: VmSpaceOwner,
681        vaddr: Vaddr,
682        len: usize,
683    ) -> bool {
684        &&& vm_owner.inv()
685    }
686
687    pub open spec fn writer_success_cond(self, vaddr: Vaddr, len: usize) -> bool {
688        &&& vaddr != 0 && len > 0 && vaddr + len <= MAX_USERSPACE_VADDR
689        &&& current_page_table_paddr() == self.pt.root_paddr_spec()
690    }
691}
692
693impl<'rcu, A: InAtomicMode> Cursor<'rcu, A> {
694    pub open spec fn query_success_requires(self) -> bool {
695        self.0.barrier_va.start <= self.0.va < self.0.barrier_va.end
696    }
697
698    pub open spec fn query_success_ensures(
699        self,
700        view: CursorView<UserPtConfig>,
701        range: Range<Vaddr>,
702        item: Option<MappedItem>,
703    ) -> bool {
704        if view.present() {
705            &&& item is Some
706            &&& view.query_item_spec(item->0) == Some(range)
707        } else {
708            &&& range.start == self.0.va
709            &&& item is None
710        }
711    }
712}
713
714impl<'a, A: InAtomicMode> CursorMut<'a, A> {
715    pub open spec fn map_cursor_requires(
716        self,
717        cursor_owner: CursorOwner<'a, UserPtConfig>,
718    ) -> bool {
719        &&& cursor_owner.in_locked_range()
720        &&& self.pt_cursor.0.level < self.pt_cursor.0.guard_level
721        &&& self.pt_cursor.0.va < self.pt_cursor.0.barrier_va.end
722    }
723
724    pub open spec fn item_wf(
725        self,
726        frame: UFrame,
727        prop: PageProperty,
728        entry_owner: EntryOwner<UserPtConfig>,
729        regions: MetaRegionOwners,
730    ) -> bool {
731        let item = MappedItem { frame: frame, prop: prop };
732        let (paddr, level, prop0) = UserPtConfig::item_into_raw_spec(item);
733        &&& frame.inv()
734        &&& prop == prop0
735        &&& entry_owner.frame().mapped_pa == paddr
736        &&& entry_owner.frame().prop == prop
737        &&& level <= UserPtConfig::HIGHEST_TRANSLATION_LEVEL()
738        &&& 1 <= level <= NR_LEVELS
739        &&& level < self.pt_cursor.0.guard_level
740        &&& Child::Frame(paddr, level, prop0).wf(entry_owner)
741        &&& self.pt_cursor.0.va + page_size(level) <= self.pt_cursor.0.barrier_va.end
742        &&& entry_owner.inv()
743        &&& self.pt_cursor.0.va % page_size(level) == 0
744        &&& crate::mm::page_table::CursorMut::<'a, UserPtConfig, A>::item_slot_in_regions(
745            item,
746            regions,
747        )
748    }
749
750    pub open spec fn map_item_ensures(
751        self,
752        frame: UFrame,
753        prop: PageProperty,
754        old_cursor_view: CursorView<UserPtConfig>,
755        cursor_view: CursorView<UserPtConfig>,
756    ) -> bool {
757        let item = MappedItem { frame: frame, prop: prop };
758        let (paddr, level, prop0) = UserPtConfig::item_into_raw_spec(item);
759        cursor_view == old_cursor_view.map_spec(paddr, page_size(level), prop)
760    }
761}
762
763} // verus!