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