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 recommends
271 self.inv(),
272 {
273 &&& forall|i: int|
274 #![trigger self.writers[i]]
275 0 <= i < self.writers.len() ==> !self.writers[i].overlaps_with_range(
276 vaddr..(vaddr + len) as usize,
277 )
278 }
279
280 /// Checks if we can create a new writer under this VM space owner.
281 ///
282 /// Similar to [`can_create_reader`], but checks both active readers and writers.
283 pub open spec fn can_create_writer(&self, vaddr: Vaddr, len: usize) -> bool
284 recommends
285 self.inv(),
286 {
287 &&& forall|i: int|
288 #![trigger self.readers[i]]
289 0 <= i < self.readers.len() ==> !self.readers[i].overlaps_with_range(
290 vaddr..(vaddr + len) as usize,
291 )
292 &&& forall|i: int|
293 #![trigger self.writers[i]]
294 0 <= i < self.writers.len() ==> !self.writers[i].overlaps_with_range(
295 vaddr..(vaddr + len) as usize,
296 )
297 }
298
299 /// Generates a new unique ID for VM IO owners.
300 ///
301 /// This assumes that we always generate a fresh ID that is not used by any existing
302 /// readers or writers. This should be safe as the ID space is unbounded and only used
303 /// to reason about different VM IO owners in verification.
304 pub uninterp spec fn new_vm_io_id(&self) -> nat
305 recommends
306 self.inv(),
307 ;
308
309 /// Activates the given reader to read data from the user space of the current task.
310 /// # Verified Properties
311 /// ## Preconditions
312 /// - The [`VmSpace`] invariants must hold with respect to the [`VmSpaceOwner`], which must be active.
313 /// - The reader must be well-formed with respect to the [`VmSpaceOwner`].
314 /// - The reader's virtual address range must be mapped within the [`VmSpaceOwner`]'s memory view.
315 /// ## Postconditions
316 /// - The reader will be added to the [`VmSpace`]'s readers list.
317 /// - The reader will be activated with a view of its virtual address range taken from the [`VmSpaceOwner`]'s memory view.
318 /// ## Safety
319 /// - The function preserves all memory invariants.
320 /// - The [`MemView`] invariants ensure that the reader has a consistent view of memory.
321 /// - The [`VmSpaceOwner`] invariants ensure that the viewed memory is owned exclusively by this [`VmSpace`].
322 #[inline(always)]
323 #[verus_spec(r =>
324 requires
325 old(self).mem_view matches Some(mv) &&
326 forall |va: usize|
327 #![auto]
328 old(owner_r).range.start <= va < old(owner_r).range.end ==>
329 mv.addr_transl(va) is Some
330 ,
331 old(self).inv(),
332 old(self).active,
333 reader.wf(*old(owner_r)),
334 old(owner_r).mem_view is None,
335 reader.inv(),
336 ensures
337 reader.wf(*final(owner_r)),
338 final(owner_r).mem_view == Some(VmIoMemView::ReadView(old(self).mem_view@->0.borrow_at(
339 old(owner_r).range.start,
340 (old(owner_r).range.end - old(owner_r).range.start) as usize,
341 ))),
342 )]
343 pub proof fn activate_reader(
344 tracked &'a mut self,
345 reader: &'a VmReader<'a>,
346 tracked owner_r: &'a mut VmIoOwner,
347 ) {
348 let tracked mv = match self.mem_view {
349 Some(ref mv) => mv,
350 _ => { proof_from_false() },
351 };
352 let tracked borrowed_mv = mv.tracked_borrow_at(
353 owner_r.range.start,
354 (owner_r.range.end - owner_r.range.start) as usize,
355 );
356
357 owner_r.mem_view = Some(VmIoMemView::ReadView(borrowed_mv));
358
359 assert forall|va: usize|
360 #![auto]
361 owner_r.range.start <= va < owner_r.range.end implies borrowed_mv.addr_transl(
362 va,
363 ) is Some by {
364 if owner_r.range.start <= va && va < owner_r.range.end {
365 assert(borrowed_mv.mappings == mv.mappings.filter(
366 |m: Mapping|
367 m.va_range.start < (owner_r.range.end) && m.va_range.end
368 > owner_r.range.start,
369 ));
370 let o_borrow_mv = borrowed_mv.mappings.filter(
371 |m: Mapping| m.va_range.start <= va < m.va_range.end,
372 );
373 let o_mv = mv.mappings.filter(|m: Mapping| m.va_range.start <= va < m.va_range.end);
374 assert(mv.addr_transl(va) is Some);
375 assert(o_mv.len() > 0);
376 let m = o_mv.choose();
377 vstd::set::lemma_set_choose_len(o_mv);
378 assert(o_mv.contains(m));
379 assert(o_borrow_mv.contains(m));
380 assert(o_borrow_mv.len() > 0);
381 }
382 }
383
384 }
385
386 /// Activates the given writer to write data to the user space of the current task.
387 /// # Verified Properties
388 /// ## Preconditions
389 /// - The [`VmSpace`] invariants must hold with respect to the [`VmSpaceOwner`], which must be active.
390 /// - The writer must be well-formed with respect to the `[VmSpaceOwner`].
391 /// - The writer's virtual address range must be mapped within the [`VmSpaceOwner`]'s memory view.
392 /// ## Postconditions
393 /// - The writer will be added to the [`VmSpace`]'s writers list.
394 /// - The writer will be activated with a view of its virtual address range taken from the [`VmSpaceOwner`]'s memory view.
395 /// ## Safety
396 /// - The function preserves all memory invariants.
397 /// - The [`MemView`] invariants ensure that the writer has a consistent view of memory.
398 /// - The [`VmSpaceOwner`] invariants ensure that the viewed memory is owned exclusively by
399 /// this [`VmSpace`].
400 #[inline(always)]
401 #[verus_spec(r =>
402 requires
403 old(self).mem_view matches Some(mv) &&
404 forall |va: usize|
405 #![auto]
406 old(owner_w).range.start <= va < old(owner_w).range.end ==>
407 mv.addr_transl(va) is Some
408 ,
409 old(self).inv(),
410 old(self).active,
411 writer.wf(*old(owner_w)),
412 old(owner_w).mem_view is None,
413 writer.inv(),
414 ensures
415 writer.wf(*final(owner_w)),
416 final(owner_w).mem_view == Some(VmIoMemView::WriteView(old(self).mem_view@->0.split(
417 old(owner_w).range.start,
418 (old(owner_w).range.end - old(owner_w).range.start) as usize,
419 ).0)),
420 )]
421 pub proof fn activate_writer(
422 tracked &mut self,
423 writer: &'a VmWriter<'a>,
424 tracked owner_w: &'a mut VmIoOwner,
425 ) {
426 let tracked mut mv = self.mem_view.tracked_take();
427 let ghost old_mv = mv;
428 let tracked (lhs, rhs) = mv.tracked_split(
429 owner_w.range.start,
430 (owner_w.range.end - owner_w.range.start) as usize,
431 );
432
433 owner_w.mem_view = Some(VmIoMemView::WriteView(lhs));
434 self.mem_view = Some(rhs);
435
436 assert forall|va: usize|
437 #![auto]
438 owner_w.range.start <= va < owner_w.range.end implies lhs.addr_transl(va) is Some by {
439 if owner_w.range.start <= va && va < owner_w.range.end {
440 assert(lhs.mappings == old_mv.mappings.filter(
441 |m: Mapping|
442 m.va_range.start < (owner_w.range.end) && m.va_range.end
443 > owner_w.range.start,
444 ));
445 let o_lhs = lhs.mappings.filter(
446 |m: Mapping| m.va_range.start <= va < m.va_range.end,
447 );
448 let o_mv = old_mv.mappings.filter(
449 |m: Mapping| m.va_range.start <= va < m.va_range.end,
450 );
451
452 assert(old_mv.addr_transl(va) is Some);
453 assert(o_mv.len() > 0);
454 broadcast use vstd::set::lemma_set_choose_len;
455
456 let m = o_mv.choose();
457 assert(o_mv.contains(m));
458 assert(m.va_range.start <= va < m.va_range.end);
459 assert(o_lhs.contains(m));
460 assert(o_lhs.len() > 0);
461 }
462 }
463
464 }
465
466 /// Removes the given reader from the active readers list.
467 ///
468 /// # Verified Properties
469 /// ## Preconditions
470 /// - The [`VmSpace`] invariants must hold with respect to the [`VmSpaceOwner`], which must be active.
471 /// - The index `idx` must be a valid index into the active readers list.
472 /// ## Postconditions
473 /// - The reader at index `idx` will be removed from the active readers list.
474 /// - The invariants of the [`VmSpaceOwner`] will still hold after the removal
475 pub proof fn remove_reader(tracked &mut self, idx: int)
476 requires
477 old(self).inv(),
478 old(self).active,
479 old(self).mem_view is Some,
480 0 <= idx < old(self).readers.len(),
481 ensures
482 final(self).inv(),
483 final(self).active == old(self).active,
484 final(self).shared_reader == old(self).shared_reader,
485 final(self).readers == old(self).readers.remove(idx),
486 {
487 self.readers.tracked_remove(idx);
488 }
489
490 /// Removes the given writer from the active writers list.
491 ///
492 /// # Verified Properties
493 /// ## Preconditions
494 /// - The [`VmSpace`] invariants must hold with respect to the [`VmSpaceOwner`], which must be active.
495 /// - The index `idx` must be a valid index into the active writers list.
496 /// ## Postconditions
497 /// - The writer at index `idx` will be removed from the active writers list.
498 /// - The memory view held by the removed writer will be returned to the VM space owner,
499 /// ensuring that the overall memory view remains consistent.
500 /// - The invariants of the [`VmSpaceOwner`] will still hold after the removal.
501 pub proof fn remove_writer(tracked &mut self, idx: usize)
502 requires
503 old(self).inv(),
504 old(self).active,
505 old(self).mem_view is Some,
506 old(self).mv_range is Some,
507 0 <= idx < old(self).writers.len(),
508 ensures
509 final(self).inv(),
510 final(self).active == old(self).active,
511 final(self).shared_reader == old(self).shared_reader,
512 final(self).writers == old(self).writers.remove(idx as int),
513 {
514 let tracked writer = self.writers.tracked_remove(idx as int);
515
516 let tracked mv = match writer.mem_view {
517 Some(VmIoMemView::WriteView(mv)) => mv,
518 _ => { proof_from_false() },
519 };
520
521 let tracked mut remaining = self.mem_view.tracked_take();
522 let ghost old_remaining = remaining;
523 remaining.tracked_join(mv);
524 self.mem_view = Some(remaining);
525
526 assert(self.mem_view_wf()) by {
527 let ghost total_view = self.mv_range.unwrap();
528
529 assert(remaining.mappings == old_remaining.mappings.union(mv.mappings));
530 assert(remaining.memory == old_remaining.memory.union_prefer_right(mv.memory));
531 assert(self.mv_range == old(self).mv_range);
532 assert(self.mem_view == Some(remaining));
533
534 assert forall|va: usize|
535 #![auto]
536 { remaining.addr_transl(va) == total_view.addr_transl(va) } by {
537 let r_mappings = remaining.mappings.filter(
538 |m: Mapping| m.va_range.start <= va < m.va_range.end,
539 );
540 let t_mappings = total_view.mappings.filter(
541 |m: Mapping| m.va_range.start <= va < m.va_range.end,
542 );
543 let w_mappings = mv.mappings.filter(
544 |m: Mapping| m.va_range.start <= va < m.va_range.end,
545 );
546
547 assert(r_mappings.subset_of(t_mappings));
548 assert(w_mappings.subset_of(t_mappings));
549
550 if r_mappings.len() > 0 {
551 let r = r_mappings.choose();
552 vstd::set::lemma_set_choose_len(r_mappings);
553 assert(r_mappings.contains(r));
554 assert(t_mappings.contains(r));
555 assert(t_mappings.len() > 0);
556 }
557 }
558
559 assert forall|i: int| #![trigger self.writers[i]] 0 <= i < self.writers.len() implies {
560 let other_writer = self.writers[i];
561
562 &&& other_writer.mem_view matches Some(VmIoMemView::WriteView(writer_mv))
563 && writer_mv.mappings.disjoint(remaining.mappings)
564 } by {
565 let other_writer = self.writers[i];
566
567 assert(old(self).inv());
568 let writer_mv = match other_writer.mem_view {
569 Some(VmIoMemView::WriteView(mv)) => mv,
570 _ => { proof_from_false() },
571 };
572
573 assert(exists|i: int|
574 0 <= i < old(self).writers.len() ==> #[trigger] old(self).writers[i]
575 == other_writer);
576 assert(exists|i: int|
577 0 <= i < old(self).writers.len() ==> #[trigger] old(self).writers[i] == writer);
578 assert(mv.mappings.disjoint(writer_mv.mappings));
579 }
580 }
581 }
582
583 /// Disposes the given reader, releasing its ownership on the memory range.
584 ///
585 /// This does not mean that the owner is discarded; it indicates that someone
586 /// who finishes the reading operation can let us reclaim the permission.
587 /// The deletion of the reader is done via another API [`VmSpaceOwner::remove_reader`].
588 ///
589 /// Typically this API is called in two scenarios:
590 ///
591 /// 1. The reader has been created and we immediately move the ownership into us.
592 /// 2. The reader has finished the reading and need to return the ownership back.
593 pub proof fn dispose_reader(tracked &mut self, tracked owner: VmIoOwner)
594 requires
595 owner.inv(),
596 old(self).inv(),
597 old(self).active,
598 old(self).mv_range matches Some(total_view) && owner.mem_view matches Some(
599 VmIoMemView::ReadView(mv),
600 ) && old(self).mem_view matches Some(remaining) && {
601 forall|va: usize|
602 #![auto]
603 {
604 &&& total_view.addr_transl(va) == mv.addr_transl(va)
605 }
606 },
607 forall|i: int|
608 #![trigger old(self).writers[i]]
609 0 <= i < old(self).writers.len() ==> old(self).writers[i].disjoint(owner),
610 forall|i: int|
611 #![trigger old(self).readers[i]]
612 0 <= i < old(self).readers.len() ==> old(self).readers[i].disjoint(owner),
613 ensures
614 final(self).inv(),
615 final(self).active == old(self).active,
616 final(self).shared_reader == old(self).shared_reader,
617 owner.range.start < owner.range.end ==> final(self).readers == old(self).readers.push(
618 owner,
619 ),
620 {
621 if owner.range.start < owner.range.end {
622 // Return the memory view back to the vm space owner.
623 self.readers.tracked_push(owner);
624 }
625 }
626
627 /// Disposes the given writer, releasing its ownership on the memory range.
628 ///
629 /// This does not mean that the owner is discarded; it indicates that someone
630 /// who finishes the writing operation can let us reclaim the permission.
631 ///
632 /// The deletion of the writer is through another API [`VmSpaceOwner::remove_writer`].
633 pub proof fn dispose_writer(tracked &mut self, tracked owner: VmIoOwner)
634 requires
635 old(self).inv(),
636 old(self).active,
637 owner.inv(),
638 old(self).mv_range matches Some(total_view) && owner.mem_view matches Some(
639 VmIoMemView::WriteView(mv),
640 ) && old(self).mem_view matches Some(remaining) && {
641 &&& forall|va: usize|
642 #![auto]
643 {
644 &&& mv.addr_transl(va) == total_view.addr_transl(va)
645 &&& mv.addr_transl(va) matches Some(_) ==> {
646 &&& remaining.addr_transl(va) is None
647 &&& !remaining.memory.contains_key(va)
648 }
649 }
650 &&& mv.mappings.disjoint(remaining.mappings)
651 &&& mv.mappings.subset_of(total_view.mappings)
652 &&& mv.memory.dom().subset_of(total_view.memory.dom())
653 },
654 forall|i: int|
655 #![trigger old(self).writers[i]]
656 0 <= i < old(self).writers.len() ==> old(self).writers[i].disjoint(owner),
657 forall|i: int|
658 #![trigger old(self).readers[i]]
659 0 <= i < old(self).readers.len() ==> old(self).readers[i].disjoint(owner),
660 ensures
661 final(self).inv(),
662 final(self).active == old(self).active,
663 final(self).shared_reader == old(self).shared_reader,
664 owner.range.start < owner.range.end ==> final(self).writers == old(self).writers.push(
665 owner,
666 ),
667 {
668 // If the writer has consumed all the memory, nothing to do;
669 // just discard the writer and return the permission back to
670 // the vm space owner.
671 match owner.mem_view {
672 Some(VmIoMemView::WriteView(ref writer_mv)) => {
673 if owner.range.start < owner.range.end {
674 self.writers.tracked_push(owner);
675 }
676 },
677 _ => {
678 assert(false);
679 },
680 }
681 }
682}
683
684impl<'a> VmSpace<'a> {
685 pub open spec fn reader_success_cond(self, vaddr: Vaddr, len: usize) -> bool {
686 &&& vaddr != 0 && len > 0 && vaddr + len <= MAX_USERSPACE_VADDR
687 &&& current_page_table_paddr() == self.pt.root_paddr_spec()
688 }
689
690 pub open spec fn writer_requires(
691 &self,
692 vm_owner: VmSpaceOwner,
693 vaddr: Vaddr,
694 len: usize,
695 ) -> bool {
696 &&& vm_owner.inv()
697 }
698
699 pub open spec fn writer_success_cond(self, vaddr: Vaddr, len: usize) -> bool {
700 &&& vaddr != 0 && len > 0 && vaddr + len <= MAX_USERSPACE_VADDR
701 &&& current_page_table_paddr() == self.pt.root_paddr_spec()
702 }
703}
704
705impl<'rcu, A: InAtomicMode> Cursor<'rcu, A> {
706 pub open spec fn query_success_requires(self) -> bool {
707 self.0.barrier_va.start <= self.0.va < self.0.barrier_va.end
708 }
709
710 pub open spec fn query_success_ensures(
711 self,
712 view: CursorView<UserPtConfig>,
713 range: Range<Vaddr>,
714 item: Option<MappedItem>,
715 ) -> bool {
716 if view.present() {
717 &&& item is Some
718 &&& view.query_item_spec(item->0) == Some(range)
719 } else {
720 &&& range.start == self.0.va
721 &&& item is None
722 }
723 }
724}
725
726impl<'a, A: InAtomicMode> CursorMut<'a, A> {
727 pub open spec fn map_cursor_requires(
728 self,
729 cursor_owner: CursorOwner<'a, UserPtConfig>,
730 ) -> bool {
731 &&& cursor_owner.in_locked_range()
732 &&& self.pt_cursor.0.level < self.pt_cursor.0.guard_level
733 &&& self.pt_cursor.0.va < self.pt_cursor.0.barrier_va.end
734 }
735
736 pub open spec fn item_wf(
737 self,
738 frame: UFrame,
739 prop: PageProperty,
740 entry_owner: EntryOwner<UserPtConfig>,
741 regions: MetaRegionOwners,
742 ) -> bool {
743 let item = MappedItem { frame: frame, prop: prop };
744 let (paddr, level, prop0) = UserPtConfig::item_into_raw_spec(item);
745 &&& frame.inv()
746 &&& prop == prop0
747 &&& entry_owner.frame().mapped_pa == paddr
748 &&& entry_owner.frame().prop == prop
749 &&& level <= UserPtConfig::HIGHEST_TRANSLATION_LEVEL()
750 &&& 1 <= level <= NR_LEVELS
751 &&& level < self.pt_cursor.0.guard_level
752 &&& Child::Frame(paddr, level, prop0).wf(entry_owner)
753 &&& self.pt_cursor.0.va + page_size(level) <= self.pt_cursor.0.barrier_va.end
754 &&& entry_owner.inv()
755 &&& self.pt_cursor.0.va % page_size(level) == 0
756 &&& crate::mm::page_table::CursorMut::<'a, UserPtConfig, A>::item_slot_in_regions(
757 item,
758 regions,
759 )
760 }
761
762 pub open spec fn map_item_ensures(
763 self,
764 frame: UFrame,
765 prop: PageProperty,
766 old_cursor_view: CursorView<UserPtConfig>,
767 cursor_view: CursorView<UserPtConfig>,
768 ) -> bool {
769 let item = MappedItem { frame: frame, prop: prop };
770 let (paddr, level, prop0) = UserPtConfig::item_into_raw_spec(item);
771 cursor_view == old_cursor_view.map_spec(paddr, page_size(level), prop)
772 }
773}
774
775} // verus!