1pub mod cursor;
32pub mod frame;
33pub mod io;
34pub mod kvirt_store;
35pub mod list_store;
36pub mod segment;
37pub mod trace;
38pub mod unique;
39pub mod vm_space;
40
41use vstd::prelude::*;
42use vstd_extra::{ownership::*, set_extra::*};
43
44use crate::specs::{
45 arch::*,
46 mm::{
47 frame::{
48 mapping::*,
49 meta_owners::{MetaSlotOwner, PageUsage},
50 meta_region_owners::MetaRegionOwners,
51 },
52 io::VmIoOwner,
53 page_table::{
54 cursor::owners::{CursorContinuation, CursorOwner},
55 node::Guards,
56 },
57 tlb::TlbModel,
58 },
59};
60
61use crate::mm::{
62 MAX_USERSPACE_VADDR, Paddr, Vaddr,
63 frame::{
64 MetaSlot, UFrame,
65 meta::{REF_COUNT_MAX, REF_COUNT_UNIQUE, REF_COUNT_UNUSED},
66 },
67 page_prop::PageProperty,
68 vm_space::{UserPtConfig, vm_space_specs::VmSpaceOwner},
69};
70use core::ops::Range;
71
72verus! {
73
74broadcast use crate::specs::mm::frame::mapping::lemma_index_to_frame_biinjective;
75pub type VmSpaceId = int;
81
82pub type CursorId = int;
84
85pub type VmIoId = int;
87
88pub type FrameId = int;
90
91pub type SegmentId = int;
94
95pub type UniqueId = int;
98
99pub tracked struct FrameEntry {
106 pub ghost paddr: Paddr,
107}
108
109pub tracked struct SegmentEntry {
118 pub ghost range: Range<Paddr>,
119}
120
121pub tracked struct UniqueEntry {
123 pub ghost paddr: Paddr,
124}
125
126pub open spec fn segment_cover_count(segments: Map<SegmentId, SegmentEntry>, paddr: Paddr) -> nat {
129 segments.dom().filter(
130 |sid: SegmentId| segments[sid].range.start <= paddr && paddr < segments[sid].range.end,
131 ).len()
132}
133
134pub proof fn lemma_segment_cover_witness(
137 segments: Map<SegmentId, SegmentEntry>,
138 paddr: Paddr,
139) -> (sid: SegmentId)
140 requires
141 segment_cover_count(segments, paddr) > 0,
142 ensures
143 segments.contains_key(sid),
144 segments[sid].range.start <= paddr < segments[sid].range.end,
145{
146 let covering = segments.dom().filter(
147 |sid: SegmentId| segments[sid].range.start <= paddr && paddr < segments[sid].range.end,
148 );
149 let sid = covering.choose();
150 assert(covering.contains(sid));
151 sid
152}
153
154pub open spec fn handle_count(frames: Map<FrameId, FrameEntry>, idx: int) -> nat {
157 frames.dom().filter(|fid: FrameId| frame_to_index(frames[fid].paddr) == idx).len()
158}
159
160pub proof fn lemma_handle_count_insert_fresh(
165 frames: Map<FrameId, FrameEntry>,
166 id: FrameId,
167 entry: FrameEntry,
168 idx: int,
169)
170 requires
171 !frames.contains_key(id),
172 ensures
173 handle_count(frames.insert(id, entry), idx) == handle_count(frames, idx) + (
174 if frame_to_index(entry.paddr) == idx {
175 1nat
176 } else {
177 0nat
178 }),
179{
180 let frames2 = frames.insert(id, entry);
181 let new_filt = frames2.dom().filter(|fid: FrameId| frame_to_index(frames2[fid].paddr) == idx);
182 let old_filt = frames.dom().filter(|fid: FrameId| frame_to_index(frames[fid].paddr) == idx);
183 if frame_to_index(entry.paddr) == idx {
184 assert(new_filt == old_filt.insert(id)) by {};
185 } else {
186 assert(new_filt == old_filt) by {};
187 }
188}
189
190pub proof fn lemma_handle_count_remove(frames: Map<FrameId, FrameEntry>, fid: FrameId, idx: int)
194 requires
195 frames.contains_key(fid),
196 ensures
197 handle_count(frames.remove(fid), idx) == handle_count(frames, idx) - (if frame_to_index(
198 frames[fid].paddr,
199 ) == idx {
200 1nat
201 } else {
202 0nat
203 }),
204{
205 let frames2 = frames.remove(fid);
206 let new_filt = frames2.dom().filter(|gid: FrameId| frame_to_index(frames2[gid].paddr) == idx);
207 let old_filt = frames.dom().filter(|gid: FrameId| frame_to_index(frames[gid].paddr) == idx);
208 if frame_to_index(frames[fid].paddr) == idx {
209 assert(new_filt == old_filt.remove(fid)) by {};
210 } else {
211 assert(new_filt == old_filt) by {};
212 }
213}
214
215pub proof fn lemma_frame_drop_pre_derivable<'rcu>(s: VmStore<'rcu>, fid: FrameId)
216 requires
217 s.inv(),
218 s.frames.contains_key(fid),
219 segment_cover_count(s.segments, s.frames[fid].paddr) == 0,
220 ensures
221 frame::drop_pre(s.regions, s.frames[fid].paddr),
222 s.regions.slot_owner(s.frames[fid].paddr).ref_count() == 1 ==> handle_count(
223 s.frames,
224 frame_to_index(s.frames[fid].paddr),
225 ) == 1,
226{
227 reveal(VmStore::structural_inv);
228 reveal(VmStore::accounting_inv);
229 let paddr = s.frames[fid].paddr;
230 let idx = frame_to_index(paddr);
231 s.regions.lemma_contains_valid_frame_paddr(paddr);
232 assert(s.regions.ref_count(idx) == s.regions.slot_owners[idx].ref_count_perm.value());
233
234 assert(s.frames.dom().filter(
235 |gid: FrameId| frame_to_index(s.frames[gid].paddr) == idx,
236 ).contains(fid));
237}
238
239pub enum VmIoKind {
241 Reader,
242 Writer,
243}
244
245pub tracked struct VmIoEntry {
247 pub ghost vm_space: Option<VmSpaceId>,
248 pub ghost kind: VmIoKind,
249 pub ghost vaddr: Vaddr,
250 pub ghost len: usize,
251 pub owner: VmIoOwner,
252}
253
254impl VmIoEntry {
255 pub open spec fn inv(self) -> bool {
257 &&& self.owner.inv()
258 &&& match self.vm_space {
259 Some(_) => self.owner.mem_view is None,
260 None => match self.kind {
261 VmIoKind::Reader => self.owner.read_view_initialized(),
262 VmIoKind::Writer => self.owner.has_write_view(),
263 },
264 }
265 }
266
267 pub open spec fn is_kernel_reader(self) -> bool {
268 &&& self.vm_space is None
269 &&& self.kind == VmIoKind::Reader
270 }
271
272 pub open spec fn is_kernel_writer(self) -> bool {
273 &&& self.vm_space is None
274 &&& self.kind == VmIoKind::Writer
275 }
276}
277
278pub ghost enum CursorKind {
283 ReadOnly,
284 Mutable,
285}
286
287pub tracked struct CursorEntry<'rcu> {
293 pub ghost vm_space: VmSpaceId,
294 pub ghost kind: CursorKind,
295 pub ghost va: Range<Vaddr>,
296 pub owner: CursorOwner<'rcu, UserPtConfig>,
297 pub guards: Guards,
298}
299
300impl<'rcu> CursorEntry<'rcu> {
301 pub open spec fn inv(self) -> bool {
302 &&& self.owner.inv()
303 &&& self.owner.children_not_locked(self.guards)
304 &&& self.owner.nodes_locked(self.guards)
305 &&& !self.owner.popped_too_high
306 }
307}
308
309pub tracked struct VmStore<'rcu> {
312 pub regions: MetaRegionOwners,
313 pub tlb_model: TlbModel,
314 pub vm_spaces: Map<VmSpaceId, VmSpaceOwner>,
315 pub cursors: Map<CursorId, CursorEntry<'rcu>>,
316 pub vm_ios: Map<VmIoId, VmIoEntry>,
317 pub frames: Map<FrameId, FrameEntry>,
318 pub segments: Map<SegmentId, SegmentEntry>,
319 pub unique_frames: Map<UniqueId, UniqueEntry>,
320}
321
322impl<'a, 'rcu> VmStore<'rcu> {
323 pub open spec fn inv(self) -> bool {
325 self.structural_inv() && self.accounting_inv() && self.regions.inv()
326 }
327
328 #[verifier::opaque]
334 pub open spec fn structural_inv(self) -> bool {
335 &&& forall|idx: int|
336 0 <= idx < max_meta_slots() ==> #[trigger] self.regions.slots.contains_key(idx) || (
337 self.regions.slot_owners[idx].usage is PageTable && self.regions.ref_count(idx)
338 != REF_COUNT_UNUSED)
339 &&& forall|idx: int|
340 0 <= idx < max_meta_slots()
341 ==> #[trigger] self.regions.slot_owners[idx].in_list_perm.value() == 0
342 &&& self.tlb_model.inv()
343 &&& forall|id: VmSpaceId| #[trigger]
344 self.vm_spaces.contains_key(id) ==> self.vm_spaces[id].inv()
345 &&& forall|id: CursorId| #[trigger] self.cursors.contains_key(id) ==> self.cursors[id].inv()
346 &&& forall|id: CursorId| #[trigger]
347 self.cursors.contains_key(id) ==> self.cursors[id].owner.metaregion_sound(self.regions)
348 &&& forall|id: CursorId| #[trigger]
349 self.cursors.contains_key(id) ==> self.vm_spaces.contains_key(self.cursors[id].vm_space)
350 &&& forall|id: VmIoId| #[trigger] self.vm_ios.contains_key(id) ==> self.vm_ios[id].inv()
351 &&& forall|id: VmIoId| #[trigger]
352 self.vm_ios.contains_key(id) ==> (self.vm_ios[id].vm_space matches Some(vs)
353 ==> self.vm_spaces.contains_key(vs))
354 &&& forall|id: VmIoId| #[trigger]
355 self.vm_ios.contains_key(id) ==> self.vm_ios[id].vm_space is Some ==> (
356 self.vm_ios[id].vaddr as nat) + (self.vm_ios[id].len as nat)
357 <= MAX_USERSPACE_VADDR as nat
358 &&& forall|fid: FrameId| #[trigger]
359 self.frames.contains_key(fid) ==> valid_frame_paddr(self.frames[fid].paddr)
360 &&& forall|fid: FrameId| #[trigger]
361 self.frames.contains_key(fid) ==> self.regions.slot_owner(
362 self.frames[fid].paddr,
363 ).usage is Frame
364 &&& forall|sid: SegmentId| #[trigger]
365 self.segments.contains_key(sid) ==> {
366 let r = self.segments[sid].range;
367 &&& r.start % PAGE_SIZE == 0
368 &&& r.end % PAGE_SIZE == 0
369 &&& r.start < r.end
370 &&& r.end <= MAX_PADDR
371 }
372 &&& forall|sid: SegmentId, paddr: Paddr|
373 #![trigger
374 self.segments.contains_key(sid),
375 frame_to_index(paddr)]
376 self.segments.contains_key(sid) && self.segments[sid].range.start <= paddr
377 < self.segments[sid].range.end && paddr % PAGE_SIZE == 0
378 ==> self.regions.slot_owner(paddr).usage is Frame
379 &&& forall|uid: UniqueId| #[trigger]
380 self.unique_frames.contains_key(uid) ==> valid_frame_paddr(
381 self.unique_frames[uid].paddr,
382 )
383 &&& forall|uid: UniqueId| #[trigger]
384 self.unique_frames.contains_key(uid) ==> {
385 let so = self.regions.slot_owner(self.unique_frames[uid].paddr);
386 &&& so.usage is Frame
387 &&& so.ref_count() == REF_COUNT_UNIQUE
388 &&& so.in_list_perm.value() == 0
389 &&& so.paths_in_pt.is_empty()
390 }
391 &&& forall|uid1: UniqueId, uid2: UniqueId|
392 #![trigger
393 self.unique_frames.contains_key(uid1),
394 self.unique_frames.contains_key(uid2)]
395 self.unique_frames.contains_key(uid1) && self.unique_frames.contains_key(uid2)
396 && self.unique_frames[uid1].paddr == self.unique_frames[uid2].paddr ==> uid1 == uid2
397 }
398
399 #[verifier::opaque]
400 pub open spec fn accounting_inv(self) -> bool {
401 &&& forall|idx: int|
402 #![trigger self.regions.slot_owners[idx]]
403 0 <= idx < max_meta_slots() && self.regions.ref_count(idx) == REF_COUNT_UNUSED
404 ==> handle_count(self.frames, idx) == 0
405 && self.regions.slot_owners[idx].paths_in_pt.is_empty() && segment_cover_count(
406 self.segments,
407 index_to_frame(idx),
408 ) == 0
409 &&& forall|idx: int|
410 #![trigger self.regions.slot_owners[idx]]
411 0 <= idx < max_meta_slots() && self.regions.slot_owners[idx].usage is Frame
412 && self.regions.ref_count(idx) != REF_COUNT_UNUSED && self.regions.ref_count(idx)
413 != REF_COUNT_UNIQUE ==> handle_count(self.frames, idx) > 0
414 || self.regions.slot_owners[idx].paths_in_pt.len() > 0 || segment_cover_count(
415 self.segments,
416 index_to_frame(idx),
417 ) > 0
418 &&& forall|idx: int|
419 #![trigger self.regions.slot_owners[idx]]
420 0 <= idx < max_meta_slots() && self.regions.slot_owners[idx].usage is Frame && (
421 handle_count(self.frames, idx) > 0 || self.regions.slot_owners[idx].paths_in_pt.len()
422 > 0 || segment_cover_count(self.segments, index_to_frame(idx)) > 0) ==> {
423 let so = self.regions.slot_owners[idx];
424 let rc = so.ref_count();
425 &&& rc != REF_COUNT_UNUSED
426 &&& rc != REF_COUNT_UNIQUE
427 &&& rc == handle_count(self.frames, idx) + so.paths_in_pt.len()
428 + segment_cover_count(self.segments, index_to_frame(idx))
429 }
430 }
431}
432
433pub enum Op {
439 NewVmSpace,
440 DropVmSpace { vs: VmSpaceId },
441 OpenCursor { vs: VmSpaceId, va: Range<Vaddr> },
442 OpenCursorMut { vs: VmSpaceId, va: Range<Vaddr> },
443 DropCursor { c: CursorId },
444 Query { c: CursorId },
445 FindNext { c: CursorId, len: usize },
446 Jump { c: CursorId, va: Vaddr },
447 VirtAddr { c: CursorId },
448 Map { c: CursorId, fid: FrameId, prop: PageProperty },
449 Unmap { c: CursorId, len: usize },
450 ProtectNext { c: CursorId, len: usize },
451 NewReader { vs: VmSpaceId, vaddr: Vaddr, len: usize },
452 NewWriter { vs: VmSpaceId, vaddr: Vaddr, len: usize },
453 NewKernelReader { vaddr: Vaddr, len: usize },
454 NewKernelWriter { vaddr: Vaddr, len: usize },
455 DropReader { vio: VmIoId },
456 DropWriter { vio: VmIoId },
457 ReaderReadVal { source: VmIoId },
459 ReaderCollect { source: VmIoId },
461 ReaderLimit { vio: VmIoId, max: usize },
462 ReaderSkip { vio: VmIoId, n: usize },
463 ReaderQuery { vio: VmIoId },
464 WriterWriteVal { writer: VmIoId },
466 WriterFillZeros { vio: VmIoId, len: usize },
467 WriterLimit { vio: VmIoId, max: usize },
468 WriterSkip { vio: VmIoId, n: usize },
469 WriterQuery { vio: VmIoId },
470 Read { source: VmIoId, dest: VmIoId },
473 Write { source: VmIoId, dest: VmIoId },
475 FrameFromUnused { paddr: Paddr },
478 FrameFromInUse { paddr: Paddr },
481 FrameDrop { fid: FrameId },
483 SegmentFromUnused { range: Range<Paddr> },
486 SegmentDrop { sid: SegmentId },
488 SegmentSplit { sid: SegmentId, offset: usize },
492 SegmentNext { sid: SegmentId },
495 SegmentClone { sid: SegmentId },
498 SegmentSlice { sid: SegmentId, sub_range: Range<Paddr> },
501 UniqueFromUnused { paddr: Paddr },
504 UniqueDrop { uid: UniqueId },
506 FromUnique { uid: UniqueId },
509 TryFromShared { fid: FrameId },
512}
513
514pub open spec fn op_pre<'rcu>(s: VmStore<'rcu>, op: Op) -> bool {
525 match op {
526 Op::NewVmSpace => true,
527 Op::DropVmSpace { vs } => s.vm_spaces.contains_key(vs) && (forall|c: CursorId| #[trigger]
528 s.cursors.contains_key(c) ==> s.cursors[c].vm_space != vs) && (forall|v: VmIoId|
529 #[trigger]
530 s.vm_ios.contains_key(v) ==> s.vm_ios[v].vm_space != Some(vs)),
531 Op::OpenCursor { vs, va: _ } => s.vm_spaces.contains_key(vs),
532 Op::OpenCursorMut { vs, va: _ } => s.vm_spaces.contains_key(vs),
533 Op::DropCursor { c } => s.cursors.contains_key(c),
534 Op::Query { c } => s.cursors.contains_key(c),
535 Op::FindNext { c, len: _ } => s.cursors.contains_key(c),
536 Op::Jump { c, va: _ } => s.cursors.contains_key(c),
537 Op::VirtAddr { c } => s.cursors.contains_key(c),
538 Op::Map { c, fid, prop: _ } => s.cursors.contains_key(c) && s.frames.contains_key(fid),
539 Op::Unmap { c, len: _ } => s.cursors.contains_key(c),
540 Op::ProtectNext { c, len: _ } => s.cursors.contains_key(c),
541 Op::NewReader { vs, vaddr: _, len: _ } => s.vm_spaces.contains_key(vs),
542 Op::NewWriter { vs, vaddr: _, len: _ } => s.vm_spaces.contains_key(vs),
543 Op::NewKernelReader { vaddr: _, len: _ } => true,
544 Op::NewKernelWriter { vaddr: _, len: _ } => true,
545 Op::DropReader { vio } => s.vm_ios.contains_key(vio),
546 Op::DropWriter { vio } => s.vm_ios.contains_key(vio),
547 Op::ReaderReadVal { source } => s.vm_ios.contains_key(source),
548 Op::ReaderCollect { source } => s.vm_ios.contains_key(source),
549 Op::ReaderLimit { vio, max: _ } => s.vm_ios.contains_key(vio),
550 Op::ReaderSkip { vio, n: _ } => s.vm_ios.contains_key(vio),
551 Op::ReaderQuery { vio } => s.vm_ios.contains_key(vio),
552 Op::WriterWriteVal { writer } => s.vm_ios.contains_key(writer),
553 Op::WriterFillZeros { vio, len: _ } => s.vm_ios.contains_key(vio),
554 Op::WriterLimit { vio, max: _ } => s.vm_ios.contains_key(vio),
555 Op::WriterSkip { vio, n: _ } => s.vm_ios.contains_key(vio),
556 Op::WriterQuery { vio } => s.vm_ios.contains_key(vio),
557 Op::Read { source, dest } => s.vm_ios.contains_key(source) && s.vm_ios.contains_key(dest)
558 && source != dest && s.vm_ios[source].is_kernel_reader()
559 && s.vm_ios[dest].is_kernel_writer(),
560 Op::Write { source, dest } => s.vm_ios.contains_key(source) && s.vm_ios.contains_key(dest)
561 && source != dest && s.vm_ios[source].is_kernel_reader()
562 && s.vm_ios[dest].is_kernel_writer(),
563 Op::FrameFromUnused { paddr: _ } => true,
564 Op::FrameFromInUse { paddr: _ } => true,
565 Op::FrameDrop { fid } => s.frames.contains_key(fid) && segment_cover_count(
566 s.segments,
567 s.frames[fid].paddr,
568 ) == 0,
569 Op::SegmentFromUnused { range: _ } => true,
570 Op::SegmentDrop { sid } => s.segments.contains_key(sid),
571 Op::SegmentSplit { sid, offset } => s.segments.contains_key(sid) && offset % PAGE_SIZE == 0
572 && 0 < offset && offset < (s.segments[sid].range.end - s.segments[sid].range.start),
573 Op::SegmentNext { sid } => s.segments.contains_key(sid),
574 Op::SegmentClone { sid } => s.segments.contains_key(sid) && forall|paddr: Paddr|
575 #![trigger frame_to_index(paddr)]
576 (s.segments[sid].range.start <= paddr < s.segments[sid].range.end && paddr % PAGE_SIZE
577 == 0) ==> s.regions.slot_owner(paddr).ref_count() + 1 <= REF_COUNT_MAX,
578 Op::SegmentSlice { sid, sub_range } => s.segments.contains_key(sid) && sub_range.start
579 % PAGE_SIZE == 0 && sub_range.end % PAGE_SIZE == 0 && s.segments[sid].range.start
580 <= sub_range.start && sub_range.start < sub_range.end && sub_range.end
581 <= s.segments[sid].range.end && forall|paddr: Paddr|
582 #![trigger frame_to_index(paddr)]
583 (sub_range.start <= paddr < sub_range.end && paddr % PAGE_SIZE == 0)
584 ==> s.regions.slot_owner(paddr).ref_count() + 1 <= REF_COUNT_MAX,
585 Op::UniqueFromUnused { paddr: _ } => true,
586 Op::UniqueDrop { uid } => s.unique_frames.contains_key(uid),
587 Op::FromUnique { uid } => s.unique_frames.contains_key(uid),
588 Op::TryFromShared { fid } => s.frames.contains_key(fid),
589 }
590}
591
592impl<'rcu> VmStore<'rcu> {
597 pub proof fn tracked_extract_vm_space(tracked &mut self, vs: VmSpaceId) -> (tracked res:
602 VmSpaceOwner)
603 requires
604 old(self).inv(),
605 old(self).vm_spaces.contains_key(vs),
606 forall|c: CursorId| #[trigger]
607 old(self).cursors.contains_key(c) ==> old(self).cursors[c].vm_space != vs,
608 forall|v: VmIoId| #[trigger]
609 old(self).vm_ios.contains_key(v) ==> old(self).vm_ios[v].vm_space != Some(vs),
610 ensures
611 final(self).regions == old(self).regions,
612 final(self).tlb_model == old(self).tlb_model,
613 final(self).vm_spaces == old(self).vm_spaces.remove(vs),
614 final(self).cursors == old(self).cursors,
615 final(self).vm_ios == old(self).vm_ios,
616 final(self).frames == old(self).frames,
617 final(self).segments == old(self).segments,
618 final(self).unique_frames == old(self).unique_frames,
619 res == old(self).vm_spaces[vs],
620 final(self).inv(),
621 {
622 reveal(VmStore::structural_inv);
623 reveal(VmStore::accounting_inv);
624 self.vm_spaces.tracked_remove(vs)
625 }
626
627 pub proof fn lemma_insert_vm_space(
630 tracked &mut self,
631 vs: VmSpaceId,
632 tracked owner: VmSpaceOwner,
633 )
634 requires
635 old(self).inv(),
636 !old(self).vm_spaces.contains_key(vs),
637 owner.inv(),
638 ensures
639 final(self).regions == old(self).regions,
640 final(self).tlb_model == old(self).tlb_model,
641 final(self).vm_spaces == old(self).vm_spaces.insert(vs, owner),
642 final(self).cursors == old(self).cursors,
643 final(self).vm_ios == old(self).vm_ios,
644 final(self).frames == old(self).frames,
645 final(self).segments == old(self).segments,
646 final(self).unique_frames == old(self).unique_frames,
647 final(self).inv(),
648 {
649 reveal(VmStore::structural_inv);
650 reveal(VmStore::accounting_inv);
651 self.vm_spaces.tracked_insert(vs, owner);
652 }
653
654 pub proof fn tracked_extract_cursor(tracked &mut self, c: CursorId) -> (tracked res:
656 CursorEntry<'rcu>)
657 requires
658 old(self).inv(),
659 old(self).cursors.contains_key(c),
660 ensures
661 final(self).regions == old(self).regions,
662 final(self).tlb_model == old(self).tlb_model,
663 final(self).vm_spaces == old(self).vm_spaces,
664 final(self).cursors == old(self).cursors.remove(c),
665 final(self).vm_ios == old(self).vm_ios,
666 final(self).frames == old(self).frames,
667 final(self).segments == old(self).segments,
668 final(self).unique_frames == old(self).unique_frames,
669 res == old(self).cursors[c],
670 final(self).inv(),
671 {
672 reveal(VmStore::structural_inv);
673 reveal(VmStore::accounting_inv);
674 self.cursors.tracked_remove(c)
675 }
676
677 pub proof fn lemma_insert_cursor(
682 tracked &mut self,
683 c: CursorId,
684 tracked entry: CursorEntry<'rcu>,
685 )
686 requires
687 old(self).inv(),
688 !old(self).cursors.contains_key(c),
689 entry.inv(),
690 entry.owner.metaregion_sound(old(self).regions),
691 old(self).vm_spaces.contains_key(entry.vm_space),
692 ensures
693 final(self).regions == old(self).regions,
694 final(self).tlb_model == old(self).tlb_model,
695 final(self).vm_spaces == old(self).vm_spaces,
696 final(self).cursors == old(self).cursors.insert(c, entry),
697 final(self).vm_ios == old(self).vm_ios,
698 final(self).frames == old(self).frames,
699 final(self).segments == old(self).segments,
700 final(self).unique_frames == old(self).unique_frames,
701 final(self).inv(),
702 {
703 reveal(VmStore::structural_inv);
704 reveal(VmStore::accounting_inv);
705 self.cursors.tracked_insert(c, entry);
706 }
707
708 pub proof fn tracked_extract_vm_io(tracked &mut self, vio: VmIoId) -> (tracked res: VmIoEntry)
710 requires
711 old(self).inv(),
712 old(self).vm_ios.contains_key(vio),
713 ensures
714 final(self).regions == old(self).regions,
715 final(self).tlb_model == old(self).tlb_model,
716 final(self).vm_spaces == old(self).vm_spaces,
717 final(self).cursors == old(self).cursors,
718 final(self).vm_ios == old(self).vm_ios.remove(vio),
719 final(self).frames == old(self).frames,
720 final(self).segments == old(self).segments,
721 final(self).unique_frames == old(self).unique_frames,
722 res == old(self).vm_ios[vio],
723 final(self).inv(),
724 {
725 reveal(VmStore::structural_inv);
726 reveal(VmStore::accounting_inv);
727 self.vm_ios.tracked_remove(vio)
728 }
729
730 pub proof fn lemma_insert_vm_io(tracked &mut self, vio: VmIoId, tracked entry: VmIoEntry)
732 requires
733 old(self).inv(),
734 !old(self).vm_ios.contains_key(vio),
735 entry.inv(),
736 entry.vm_space matches Some(vs) ==> old(self).vm_spaces.contains_key(vs),
737 entry.vm_space is Some ==> (entry.vaddr as nat) + (entry.len as nat)
738 <= MAX_USERSPACE_VADDR as nat,
739 ensures
740 final(self).regions == old(self).regions,
741 final(self).tlb_model == old(self).tlb_model,
742 final(self).vm_spaces == old(self).vm_spaces,
743 final(self).cursors == old(self).cursors,
744 final(self).vm_ios == old(self).vm_ios.insert(vio, entry),
745 final(self).frames == old(self).frames,
746 final(self).segments == old(self).segments,
747 final(self).unique_frames == old(self).unique_frames,
748 final(self).inv(),
749 {
750 reveal(VmStore::structural_inv);
751 reveal(VmStore::accounting_inv);
752 self.vm_ios.tracked_insert(vio, entry);
753 }
754
755 pub proof fn tracked_extract_frame(tracked &mut self, fid: FrameId) -> (tracked res: FrameEntry)
757 requires
758 old(self).structural_inv(),
759 old(self).frames.contains_key(fid),
760 ensures
761 final(self).regions == old(self).regions,
762 final(self).tlb_model == old(self).tlb_model,
763 final(self).vm_spaces == old(self).vm_spaces,
764 final(self).cursors == old(self).cursors,
765 final(self).vm_ios == old(self).vm_ios,
766 final(self).frames == old(self).frames.remove(fid),
767 final(self).segments == old(self).segments,
768 final(self).unique_frames == old(self).unique_frames,
769 res == old(self).frames[fid],
770 final(self).structural_inv(),
771 {
772 reveal(VmStore::structural_inv);
773 self.frames.tracked_remove(fid)
774 }
775
776 pub proof fn lemma_insert_frame(tracked &mut self, fid: FrameId, tracked entry: FrameEntry)
778 requires
779 old(self).structural_inv(),
780 !old(self).frames.contains_key(fid),
781 valid_frame_paddr(entry.paddr),
782 old(self).regions.slot_owner(entry.paddr).usage is Frame,
783 ensures
784 final(self).regions == old(self).regions,
785 final(self).tlb_model == old(self).tlb_model,
786 final(self).vm_spaces == old(self).vm_spaces,
787 final(self).cursors == old(self).cursors,
788 final(self).vm_ios == old(self).vm_ios,
789 final(self).frames == old(self).frames.insert(fid, entry),
790 final(self).segments == old(self).segments,
791 final(self).unique_frames == old(self).unique_frames,
792 final(self).structural_inv(),
793 {
794 reveal(VmStore::structural_inv);
795 self.frames.tracked_insert(fid, entry);
796 }
797
798 pub proof fn tracked_extract_unique(tracked &mut self, uid: UniqueId) -> (tracked res:
800 UniqueEntry)
801 requires
802 old(self).unique_frames.contains_key(uid),
803 ensures
804 final(self).regions == old(self).regions,
805 final(self).tlb_model == old(self).tlb_model,
806 final(self).vm_spaces == old(self).vm_spaces,
807 final(self).cursors == old(self).cursors,
808 final(self).vm_ios == old(self).vm_ios,
809 final(self).frames == old(self).frames,
810 final(self).segments == old(self).segments,
811 final(self).unique_frames == old(self).unique_frames.remove(uid),
812 res == old(self).unique_frames[uid],
813 {
814 self.unique_frames.tracked_remove(uid)
815 }
816
817 pub proof fn lemma_insert_unique(tracked &mut self, uid: UniqueId, tracked entry: UniqueEntry)
819 requires
820 !old(self).unique_frames.contains_key(uid),
821 ensures
822 final(self).regions == old(self).regions,
823 final(self).tlb_model == old(self).tlb_model,
824 final(self).vm_spaces == old(self).vm_spaces,
825 final(self).cursors == old(self).cursors,
826 final(self).vm_ios == old(self).vm_ios,
827 final(self).frames == old(self).frames,
828 final(self).segments == old(self).segments,
829 final(self).unique_frames == old(self).unique_frames.insert(uid, entry),
830 {
831 self.unique_frames.tracked_insert(uid, entry);
832 }
833
834 pub proof fn tracked_extract_segment(tracked &mut self, sid: SegmentId) -> (tracked res:
836 SegmentEntry)
837 requires
838 old(self).segments.contains_key(sid),
839 ensures
840 final(self).regions == old(self).regions,
841 final(self).tlb_model == old(self).tlb_model,
842 final(self).vm_spaces == old(self).vm_spaces,
843 final(self).cursors == old(self).cursors,
844 final(self).vm_ios == old(self).vm_ios,
845 final(self).frames == old(self).frames,
846 final(self).segments == old(self).segments.remove(sid),
847 final(self).unique_frames == old(self).unique_frames,
848 res == old(self).segments[sid],
849 {
850 self.segments.tracked_remove(sid)
851 }
852
853 pub proof fn lemma_insert_segment(
855 tracked &mut self,
856 sid: SegmentId,
857 tracked entry: SegmentEntry,
858 )
859 requires
860 !old(self).segments.contains_key(sid),
861 ensures
862 final(self).regions == old(self).regions,
863 final(self).tlb_model == old(self).tlb_model,
864 final(self).vm_spaces == old(self).vm_spaces,
865 final(self).cursors == old(self).cursors,
866 final(self).vm_ios == old(self).vm_ios,
867 final(self).frames == old(self).frames,
868 final(self).segments == old(self).segments.insert(sid, entry),
869 final(self).unique_frames == old(self).unique_frames,
870 {
871 self.segments.tracked_insert(sid, entry);
872 }
873}
874
875proof fn lemma_structural_inv_cursor_frame<'rcu>(s: VmStore<'rcu>, c: CursorId, fid: FrameId)
877 requires
878 s.structural_inv(),
879 s.cursors.contains_key(c),
880 s.frames.contains_key(fid),
881 ensures
882 s.tlb_model.inv(),
883 s.cursors[c].inv(),
884 s.cursors[c].owner.metaregion_sound(s.regions),
885 s.vm_spaces.contains_key(s.cursors[c].vm_space),
886 valid_frame_paddr(s.frames[fid].paddr),
887 s.regions.slot_owner(s.frames[fid].paddr).usage is Frame,
888{
889 reveal(VmStore::structural_inv);
890}
891
892proof fn lemma_structural_inv_frame<'rcu>(s: VmStore<'rcu>, fid: FrameId)
893 requires
894 s.structural_inv(),
895 s.frames.contains_key(fid),
896 ensures
897 valid_frame_paddr(s.frames[fid].paddr),
898 s.regions.slot_owner(s.frames[fid].paddr).usage is Frame,
899{
900 reveal(VmStore::structural_inv);
901}
902
903proof fn lemma_structural_inv_segment<'rcu>(s: VmStore<'rcu>, sid: SegmentId, paddr: Paddr)
904 requires
905 s.structural_inv(),
906 s.segments.contains_key(sid),
907 s.segments[sid].range.start <= paddr < s.segments[sid].range.end,
908 paddr % PAGE_SIZE == 0,
909 ensures
910 valid_frame_paddr(paddr),
911 s.regions.slot_owner(paddr).usage is Frame,
912{
913 reveal(VmStore::structural_inv);
914}
915
916proof fn lemma_accounting_inv_at<'rcu>(s: VmStore<'rcu>, idx: int)
917 requires
918 s.accounting_inv(),
919 0 <= idx < max_meta_slots(),
920 ensures
921 s.regions.ref_count(idx) == REF_COUNT_UNUSED ==> handle_count(s.frames, idx) == 0
922 && s.regions.slot_owners[idx].paths_in_pt.is_empty() && segment_cover_count(
923 s.segments,
924 index_to_frame(idx),
925 ) == 0,
926 s.regions.slot_owners[idx].usage is Frame && s.regions.ref_count(idx) != REF_COUNT_UNUSED
927 && s.regions.ref_count(idx) != REF_COUNT_UNIQUE ==> handle_count(s.frames, idx) > 0
928 || s.regions.slot_owners[idx].paths_in_pt.len() > 0 || segment_cover_count(
929 s.segments,
930 index_to_frame(idx),
931 ) > 0,
932 s.regions.slot_owners[idx].usage is Frame && (handle_count(s.frames, idx) > 0
933 || s.regions.slot_owners[idx].paths_in_pt.len() > 0 || segment_cover_count(
934 s.segments,
935 index_to_frame(idx),
936 ) > 0) ==> {
937 let so = s.regions.slot_owners[idx];
938 let rc = so.ref_count();
939 &&& rc != REF_COUNT_UNUSED
940 &&& rc != REF_COUNT_UNIQUE
941 &&& rc == handle_count(s.frames, idx) + so.paths_in_pt.len() + segment_cover_count(
942 s.segments,
943 index_to_frame(idx),
944 )
945 },
946{
947 reveal(VmStore::accounting_inv);
948}
949
950pub proof fn lemma_step<'rcu>(tracked s: &mut VmStore<'rcu>, op: Op)
960 requires
961 old(s).inv(),
962 op_pre(*old(s), op),
963 ensures
964 final(s).inv(),
965{
966 match op {
967 Op::NewVmSpace => lemma_step_new_vm_space(s),
968 Op::DropVmSpace { vs } => lemma_step_drop_vm_space(s, vs),
969 Op::OpenCursor { vs, va } => lemma_step_open_cursor(s, vs, va),
970 Op::OpenCursorMut { vs, va } => lemma_step_open_cursor_mut(s, vs, va),
971 Op::DropCursor { c } => lemma_step_drop_cursor(s, c),
972 Op::Query { c } => lemma_step_query(s, c),
973 Op::FindNext { c, len } => lemma_step_find_next(s, c, len),
974 Op::Jump { c, va } => lemma_step_jump(s, c, va),
975 Op::VirtAddr { c: _ } => {},
976 Op::Map { c, fid, prop } => lemma_step_map(s, c, fid, prop),
977 Op::Unmap { c, len } => lemma_step_unmap(s, c, len),
978 Op::ProtectNext { c, len } => lemma_step_protect_next(s, c, len),
979 Op::NewReader { vs, vaddr, len } => lemma_step_new_vm_io(
980 s,
981 vs,
982 vaddr,
983 len,
984 VmIoKind::Reader,
985 ),
986 Op::NewWriter { vs, vaddr, len } => lemma_step_new_vm_io(
987 s,
988 vs,
989 vaddr,
990 len,
991 VmIoKind::Writer,
992 ),
993 Op::NewKernelReader { vaddr, len } => lemma_step_new_kernel_vm_io(
994 s,
995 vaddr,
996 len,
997 VmIoKind::Reader,
998 ),
999 Op::NewKernelWriter { vaddr, len } => lemma_step_new_kernel_vm_io(
1000 s,
1001 vaddr,
1002 len,
1003 VmIoKind::Writer,
1004 ),
1005 Op::DropReader { vio } => lemma_step_drop_vm_io(s, vio),
1006 Op::DropWriter { vio } => lemma_step_drop_vm_io(s, vio),
1007 Op::ReaderReadVal { source: _ } => {},
1009 Op::ReaderCollect { source: _ } => {},
1010 Op::WriterWriteVal { writer: _ } => {},
1011 Op::ReaderLimit { vio, max } => lemma_step_vm_io_method(
1012 s,
1013 vio,
1014 io::VmIoMethod::ReaderLimit(max),
1015 ),
1016 Op::ReaderSkip { vio, n } => lemma_step_vm_io_method(s, vio, io::VmIoMethod::ReaderSkip(n)),
1017 Op::ReaderQuery { vio: _ } => {},
1018 Op::WriterFillZeros { vio, len } => lemma_step_vm_io_method(
1019 s,
1020 vio,
1021 io::VmIoMethod::WriterFillZeros(len),
1022 ),
1023 Op::WriterLimit { vio, max } => lemma_step_vm_io_method(
1024 s,
1025 vio,
1026 io::VmIoMethod::WriterLimit(max),
1027 ),
1028 Op::WriterSkip { vio, n } => lemma_step_vm_io_method(s, vio, io::VmIoMethod::WriterSkip(n)),
1029 Op::WriterQuery { vio: _ } => {},
1030 Op::Read { source, dest } => lemma_step_read(s, source, dest),
1032 Op::Write { source, dest } => lemma_step_write(s, source, dest),
1035 Op::FrameFromUnused { paddr } => lemma_step_frame_from_unused(s, paddr),
1036 Op::FrameFromInUse { paddr } => lemma_step_frame_from_in_use(s, paddr),
1037 Op::FrameDrop { fid } => lemma_step_frame_drop(s, fid),
1038 Op::SegmentFromUnused { range } => lemma_step_segment_from_unused(s, range),
1039 Op::SegmentDrop { sid } => lemma_step_segment_drop(s, sid),
1040 Op::SegmentSplit { sid, offset } => lemma_step_segment_split(s, sid, offset),
1041 Op::SegmentNext { sid } => lemma_step_segment_next(s, sid),
1042 Op::SegmentClone { sid } => lemma_step_segment_clone(s, sid),
1043 Op::SegmentSlice { sid, sub_range } => lemma_step_segment_slice(s, sid, sub_range),
1044 Op::UniqueFromUnused { paddr } => lemma_step_unique_from_unused(s, paddr),
1045 Op::UniqueDrop { uid } => lemma_step_unique_drop(s, uid),
1046 Op::FromUnique { uid } => lemma_step_from_unique(s, uid),
1047 Op::TryFromShared { fid } => lemma_step_try_from_shared(s, fid),
1048 }
1049}
1050
1051proof fn lemma_accounting_preserved_by_pt_alloc<'rcu>(s_old: VmStore<'rcu>, s_new: VmStore<'rcu>)
1052 requires
1053 s_old.inv(),
1054 s_new.frames == s_old.frames,
1055 s_new.segments == s_old.segments,
1057 forall|i: int|
1058 #![trigger s_new.regions.slot_owners[i]]
1059 s_new.regions.slot_owners[i] != s_old.regions.slot_owners[i] ==> {
1060 &&& s_old.regions.ref_count(i) == REF_COUNT_UNUSED
1061 &&& s_new.regions.ref_count(i) != REF_COUNT_UNUSED
1062 &&& s_new.regions.slot_owners[i].usage !is Frame
1063 },
1064 ensures
1065 s_new.accounting_inv(),
1066 forall|fid: FrameId| #[trigger]
1067 s_new.frames.contains_key(fid) ==> s_new.regions.slot_owner(
1068 s_new.frames[fid].paddr,
1069 ).usage is Frame,
1070 forall|sid: SegmentId, paddr: Paddr|
1071 #![trigger
1072 s_new.segments.contains_key(sid),
1073 frame_to_index(paddr)]
1074 s_new.segments.contains_key(sid) && s_new.segments[sid].range.start <= paddr
1075 < s_new.segments[sid].range.end && paddr % PAGE_SIZE == 0
1076 ==> s_new.regions.slot_owner(paddr).usage is Frame,
1077{
1078 reveal(VmStore::structural_inv);
1079 reveal(VmStore::accounting_inv);
1080 assert forall|sid: SegmentId, paddr: Paddr|
1081 #![trigger
1082 s_new.segments.contains_key(sid),
1083 frame_to_index(paddr)]
1084 s_new.segments.contains_key(sid) && s_new.segments[sid].range.start <= paddr
1085 < s_new.segments[sid].range.end && paddr % PAGE_SIZE
1086 == 0 implies s_new.regions.slot_owner(paddr).usage is Frame by {
1087 let idx = frame_to_index(paddr);
1088 lemma_segment_cover_contains(s_old.segments, sid, paddr);
1089 };
1090 assert forall|fid: FrameId| #[trigger]
1091 s_new.frames.contains_key(fid) implies s_new.regions.slot_owner(
1092 s_new.frames[fid].paddr,
1093 ).usage is Frame by {
1094 let idx = frame_to_index(s_new.frames[fid].paddr);
1095 assert(s_old.frames.dom().filter(
1096 |gid: FrameId| frame_to_index(s_old.frames[gid].paddr) == idx,
1097 ).contains(fid));
1098 };
1099}
1100
1101proof fn lemma_coverage_preserved_slots_eq<'rcu>(s_old: VmStore<'rcu>, s_new: VmStore<'rcu>)
1105 requires
1106 s_old.structural_inv(),
1107 s_new.regions.slots == s_old.regions.slots,
1108 forall|idx: int|
1109 #![trigger s_new.regions.slot_owners[idx]]
1110 !s_old.regions.slots.contains_key(idx) ==> s_new.regions.slot_owners[idx]
1111 == s_old.regions.slot_owners[idx],
1112 ensures
1113 forall|idx: int|
1114 0 <= idx < max_meta_slots() ==> #[trigger] s_new.regions.slots.contains_key(idx) || (
1115 s_new.regions.slot_owners[idx].usage is PageTable && s_new.regions.ref_count(idx)
1116 != REF_COUNT_UNUSED),
1117{
1118 reveal(VmStore::structural_inv);
1119}
1120
1121proof fn lemma_step_new_vm_space<'rcu>(tracked s: &mut VmStore<'rcu>)
1122 requires
1123 old(s).inv(),
1124 ensures
1125 final(s).inv(),
1126{
1127 reveal(VmStore::structural_inv);
1128 let ghost s_before = *s;
1129 let tracked owner = vm_space::new_vm_space_step(&mut s.regions);
1130 lemma_accounting_preserved_by_pt_alloc(s_before, *s);
1131}
1132
1133proof fn lemma_step_drop_vm_space<'rcu>(tracked s: &mut VmStore<'rcu>, vs: VmSpaceId)
1134 requires
1135 old(s).inv(),
1136 old(s).vm_spaces.contains_key(vs),
1137 forall|c: CursorId| #[trigger]
1138 old(s).cursors.contains_key(c) ==> old(s).cursors[c].vm_space != vs,
1139 forall|v: VmIoId| #[trigger]
1140 old(s).vm_ios.contains_key(v) ==> old(s).vm_ios[v].vm_space != Some(vs),
1141 ensures
1142 final(s).inv(),
1143{
1144 let tracked owner = s.tracked_extract_vm_space(vs);
1145}
1146
1147proof fn lemma_step_open_cursor<'rcu>(
1148 tracked s: &mut VmStore<'rcu>,
1149 vs: VmSpaceId,
1150 va: Range<Vaddr>,
1151)
1152 requires
1153 old(s).inv(),
1154 old(s).vm_spaces.contains_key(vs),
1155 ensures
1156 final(s).inv(),
1157{
1158 reveal(VmStore::structural_inv);
1159 let ghost s_before = *s;
1160 let tracked vm_space_ref = s.vm_spaces.tracked_borrow(vs);
1161 let tracked res = cursor::open_cursor_step(vm_space_ref, &mut s.regions, vs, va);
1162 lemma_accounting_preserved_by_pt_alloc(s_before, *s);
1165 match res {
1166 Option::Some(entry) => {},
1167 Option::None => {},
1168 }
1169}
1170
1171proof fn lemma_step_open_cursor_mut<'rcu>(
1172 tracked s: &mut VmStore<'rcu>,
1173 vs: VmSpaceId,
1174 va: Range<Vaddr>,
1175)
1176 requires
1177 old(s).inv(),
1178 old(s).vm_spaces.contains_key(vs),
1179 ensures
1180 final(s).inv(),
1181{
1182 reveal(VmStore::structural_inv);
1183 let ghost s_before = *s;
1184 let tracked vm_space_ref = s.vm_spaces.tracked_borrow(vs);
1185 let tracked res = cursor::open_cursor_mut_step(vm_space_ref, &mut s.regions, vs, va);
1186 lemma_accounting_preserved_by_pt_alloc(s_before, *s);
1189 match res {
1190 Option::Some(entry) => {},
1191 Option::None => {},
1192 }
1193}
1194
1195proof fn lemma_step_drop_cursor<'rcu>(tracked s: &mut VmStore<'rcu>, c: CursorId)
1196 requires
1197 old(s).inv(),
1198 old(s).cursors.contains_key(c),
1199 ensures
1200 final(s).inv(),
1201{
1202 let tracked entry = s.tracked_extract_cursor(c);
1203}
1204
1205proof fn lemma_step_query<'rcu>(tracked s: &mut VmStore<'rcu>, c: CursorId)
1206 requires
1207 old(s).inv(),
1208 old(s).cursors.contains_key(c),
1209 ensures
1210 final(s).inv(),
1211{
1212 reveal(VmStore::structural_inv);
1213 reveal(VmStore::accounting_inv);
1214 let ghost old_frames = s.frames;
1215 let ghost old_regions = s.regions;
1216 let tracked mut entry = s.tracked_extract_cursor(c);
1217 let ghost res = cursor::cursor_query_step(&mut entry, &mut s.regions);
1218 match res {
1219 Option::None => {},
1220 Option::Some(paddr) => {
1221 let ghost target_idx = frame_to_index(paddr);
1222 let ghost id = fresh_frame_id(s.frames);
1223 lemma_fresh_frame_id_not_in_dom(s.frames);
1224 let tracked frame_entry = tracked_frame_entry_new(paddr);
1225 s.lemma_insert_frame(id, frame_entry);
1226 assert forall|idx: int|
1227 #![trigger s.regions.slot_owners[idx]]
1228 0 <= idx < max_meta_slots() && s.regions.ref_count(idx)
1229 == REF_COUNT_UNUSED implies handle_count(s.frames, idx) == 0
1230 && s.regions.slot_owners[idx].paths_in_pt.is_empty() && segment_cover_count(
1231 s.segments,
1232 index_to_frame(idx),
1233 ) == 0 by {
1234 lemma_handle_count_insert_fresh(old_frames, id, frame_entry, idx);
1235 if idx == target_idx {
1236 assert(false);
1237 }
1238 };
1239 assert forall|idx: int|
1240 #![trigger s.regions.slot_owners[idx]]
1241 0 <= idx < max_meta_slots() && s.regions.slot_owners[idx].usage is Frame
1242 && s.regions.ref_count(idx) != REF_COUNT_UNUSED && s.regions.ref_count(idx)
1243 != REF_COUNT_UNIQUE implies handle_count(s.frames, idx) > 0
1244 || s.regions.slot_owners[idx].paths_in_pt.len() > 0 || segment_cover_count(
1245 s.segments,
1246 index_to_frame(idx),
1247 ) > 0 by {
1248 lemma_handle_count_insert_fresh(old_frames, id, frame_entry, idx);
1249 };
1250 assert forall|idx: int|
1251 #![trigger s.regions.slot_owners[idx]]
1252 0 <= idx < max_meta_slots() && s.regions.slot_owners[idx].usage is Frame && (
1253 handle_count(s.frames, idx) > 0 || s.regions.slot_owners[idx].paths_in_pt.len() > 0
1254 || segment_cover_count(s.segments, index_to_frame(idx)) > 0) implies {
1255 let so = s.regions.slot_owners[idx];
1256 let rc = so.ref_count();
1257 &&& rc != REF_COUNT_UNUSED
1258 &&& rc != REF_COUNT_UNIQUE
1259 &&& rc == handle_count(s.frames, idx) + so.paths_in_pt.len() + segment_cover_count(
1260 s.segments,
1261 index_to_frame(idx),
1262 )
1263 } by {
1264 lemma_handle_count_insert_fresh(old_frames, id, frame_entry, idx);
1265 if idx == target_idx {
1266 if old_regions.slot_owners[target_idx].ref_count() == REF_COUNT_UNUSED {
1267 } else if old_regions.slot_owners[target_idx].ref_count() == REF_COUNT_UNIQUE {
1268 assert(false);
1269 } else {
1270 let pre_so = old_regions.slot_owners[target_idx];
1271 let pre_rc = pre_so.ref_count();
1272 let pre_paths = pre_so.paths_in_pt.len();
1273 let pre_H = handle_count(old_frames, target_idx);
1274 let pre_cover = segment_cover_count(s.segments, index_to_frame(target_idx));
1275 if pre_H == 0 && pre_paths == 0 && pre_cover == 0 {
1276 assert(false);
1277 }
1278 }
1279 }
1280 };
1281 },
1282 }
1283}
1284
1285proof fn lemma_step_find_next<'rcu>(tracked s: &mut VmStore<'rcu>, c: CursorId, len: usize)
1286 requires
1287 old(s).inv(),
1288 old(s).cursors.contains_key(c),
1289 ensures
1290 final(s).inv(),
1291{
1292 reveal(VmStore::structural_inv);
1293 reveal(VmStore::accounting_inv);
1294 let tracked mut entry = s.tracked_extract_cursor(c);
1295}
1296
1297proof fn lemma_step_jump<'rcu>(tracked s: &mut VmStore<'rcu>, c: CursorId, va: Vaddr)
1298 requires
1299 old(s).inv(),
1300 old(s).cursors.contains_key(c),
1301 ensures
1302 final(s).inv(),
1303{
1304 reveal(VmStore::structural_inv);
1305 reveal(VmStore::accounting_inv);
1306 let tracked mut entry = s.tracked_extract_cursor(c);
1307}
1308
1309proof fn lemma_step_protect_next<'rcu>(tracked s: &mut VmStore<'rcu>, c: CursorId, len: usize)
1310 requires
1311 old(s).inv(),
1312 old(s).cursors.contains_key(c),
1313 ensures
1314 final(s).inv(),
1315{
1316 reveal(VmStore::structural_inv);
1317 reveal(VmStore::accounting_inv);
1318 let tracked mut entry = s.tracked_extract_cursor(c);
1319}
1320
1321#[verifier::spinoff_prover]
1322proof fn lemma_step_map<'rcu>(
1323 tracked s: &mut VmStore<'rcu>,
1324 c: CursorId,
1325 fid: FrameId,
1326 prop: PageProperty,
1327)
1328 requires
1329 old(s).inv(),
1330 old(s).cursors.contains_key(c),
1331 old(s).frames.contains_key(fid),
1332 ensures
1333 final(s).inv(),
1334{
1335 let ghost s_before = *s;
1336 lemma_structural_inv_cursor_frame(*s, c, fid);
1337 let ghost paddr = s.frames[fid].paddr;
1340 let ghost target_idx = frame_to_index(paddr);
1341 let ghost old_frames = s.frames;
1342 s.regions.lemma_contains_valid_frame_paddr(paddr);
1343 assert(old_frames.dom().filter(
1344 |gid: FrameId| frame_to_index(old_frames[gid].paddr) == target_idx,
1345 ).contains(fid));
1346 lemma_accounting_inv_at(*s, target_idx);
1347 let tracked mut entry = s.tracked_extract_cursor(c);
1348 let tracked _frame_entry = s.tracked_extract_frame(fid);
1349 cursor::map_step(&mut entry, &mut s.regions, &mut s.tlb_model, paddr, prop);
1350 assert forall|idx: int|
1351 #![trigger s.regions.slot_owners[idx]]
1352 0 <= idx < max_meta_slots() && s.regions.ref_count(idx)
1353 == REF_COUNT_UNUSED implies handle_count(s.frames, idx) == 0
1354 && s.regions.slot_owners[idx].paths_in_pt.is_empty() && segment_cover_count(
1355 s.segments,
1356 index_to_frame(idx),
1357 ) == 0 by {
1358 lemma_accounting_inv_at(s_before, idx);
1359 lemma_handle_count_remove(old_frames, fid, idx);
1361 if idx == target_idx {
1362 assert(false);
1365 }
1366 };
1367 assert forall|idx: int|
1368 #![trigger s.regions.slot_owners[idx]]
1369 0 <= idx < max_meta_slots() && s.regions.slot_owners[idx].usage is Frame
1370 && s.regions.ref_count(idx) != REF_COUNT_UNUSED && s.regions.ref_count(idx)
1371 != REF_COUNT_UNIQUE implies handle_count(s.frames, idx) > 0
1372 || s.regions.slot_owners[idx].paths_in_pt.len() > 0 || segment_cover_count(
1373 s.segments,
1374 index_to_frame(idx),
1375 ) > 0 by {
1376 lemma_accounting_inv_at(s_before, idx);
1377 lemma_handle_count_remove(old_frames, fid, idx);
1378 };
1379 assert forall|idx: int|
1380 #![trigger s.regions.slot_owners[idx]]
1381 0 <= idx < max_meta_slots() && s.regions.slot_owners[idx].usage is Frame && (handle_count(
1382 s.frames,
1383 idx,
1384 ) > 0 || s.regions.slot_owners[idx].paths_in_pt.len() > 0 || segment_cover_count(
1385 s.segments,
1386 index_to_frame(idx),
1387 ) > 0) implies {
1388 let so = s.regions.slot_owners[idx];
1389 let rc = so.ref_count();
1390 &&& rc != REF_COUNT_UNUSED
1391 &&& rc != REF_COUNT_UNIQUE
1392 &&& rc == handle_count(s.frames, idx) + so.paths_in_pt.len() + segment_cover_count(
1393 s.segments,
1394 index_to_frame(idx),
1395 )
1396 } by {
1397 lemma_accounting_inv_at(s_before, idx);
1398 lemma_handle_count_remove(old_frames, fid, idx);
1399 };
1400 assert forall|fid_other: FrameId| #[trigger]
1401 s.frames.contains_key(fid_other) implies s.regions.slot_owner(
1402 s.frames[fid_other].paddr,
1403 ).usage is Frame by {
1404 let other_idx = frame_to_index(s.frames[fid_other].paddr);
1405 lemma_structural_inv_frame(s_before, fid_other);
1406 lemma_accounting_inv_at(s_before, other_idx);
1407 if other_idx == target_idx {
1408 } else {
1409 assert(old_frames.dom().filter(
1410 |gid: FrameId| frame_to_index(old_frames[gid].paddr) == other_idx,
1411 ).contains(fid_other));
1412 }
1413 };
1414 assert forall|sid: SegmentId, paddr_c: Paddr|
1415 #![trigger
1416 s.segments.contains_key(sid),
1417 frame_to_index(paddr_c)]
1418 s.segments.contains_key(sid) && s.segments[sid].range.start <= paddr_c
1419 < s.segments[sid].range.end && paddr_c % PAGE_SIZE == 0 implies s.regions.slot_owner(
1420 paddr_c,
1421 ).usage is Frame by {
1422 let cov_idx = frame_to_index(paddr_c);
1423 lemma_structural_inv_segment(s_before, sid, paddr_c);
1424 lemma_accounting_inv_at(s_before, cov_idx);
1425 lemma_segment_cover_contains(old_regions_segments_helper(s), sid, paddr_c);
1427 };
1428 lemma_accounting_inv_intro(*s);
1429 assert(s.structural_inv()) by {
1430 reveal(VmStore::structural_inv);
1431 };
1432}
1433
1434spec fn old_regions_segments_helper<'rcu>(s: &VmStore<'rcu>) -> Map<SegmentId, SegmentEntry> {
1438 s.segments
1439}
1440
1441proof fn lemma_step_unmap<'rcu>(tracked s: &mut VmStore<'rcu>, c: CursorId, len: usize)
1442 requires
1443 old(s).inv(),
1444 old(s).cursors.contains_key(c),
1445 ensures
1446 final(s).inv(),
1447{
1448 reveal(VmStore::structural_inv);
1449 reveal(VmStore::accounting_inv);
1450 let tracked mut entry = s.tracked_extract_cursor(c);
1451}
1452
1453proof fn lemma_step_new_vm_io<'rcu>(
1454 tracked s: &mut VmStore<'rcu>,
1455 vs: VmSpaceId,
1456 vaddr: Vaddr,
1457 len: usize,
1458 kind: VmIoKind,
1459)
1460 requires
1461 old(s).inv(),
1462 old(s).vm_spaces.contains_key(vs),
1463 ensures
1464 final(s).inv(),
1465{
1466 reveal(VmStore::structural_inv);
1467 let tracked vm_space_ref = s.vm_spaces.tracked_borrow(vs);
1468 let tracked res = io::new_vm_io_step(vm_space_ref, Some(vs), vaddr, len, kind);
1469 match res {
1470 Option::Some(entry) => {},
1471 Option::None => {},
1472 }
1473}
1474
1475proof fn lemma_step_new_kernel_vm_io<'rcu>(
1476 tracked s: &mut VmStore<'rcu>,
1477 vaddr: Vaddr,
1478 len: usize,
1479 kind: VmIoKind,
1480)
1481 requires
1482 old(s).inv(),
1483 ensures
1484 final(s).inv(),
1485{
1486 let tracked entry = io::new_kernel_vm_io_step(vaddr, len, kind);
1487}
1488
1489proof fn lemma_step_drop_vm_io<'rcu>(tracked s: &mut VmStore<'rcu>, vio: VmIoId)
1490 requires
1491 old(s).inv(),
1492 old(s).vm_ios.contains_key(vio),
1493 ensures
1494 final(s).inv(),
1495{
1496 let tracked entry = s.tracked_extract_vm_io(vio);
1497}
1498
1499proof fn lemma_step_vm_io_method<'rcu>(
1500 tracked s: &mut VmStore<'rcu>,
1501 vio: VmIoId,
1502 method: io::VmIoMethod,
1503)
1504 requires
1505 old(s).inv(),
1506 old(s).vm_ios.contains_key(vio),
1507 ensures
1508 final(s).inv(),
1509{
1510 reveal(VmStore::structural_inv);
1511 let tracked mut entry = s.tracked_extract_vm_io(vio);
1512}
1513
1514proof fn lemma_step_read<'rcu>(tracked s: &mut VmStore<'rcu>, source: VmIoId, dest: VmIoId)
1515 requires
1516 old(s).inv(),
1517 old(s).vm_ios.contains_key(source),
1518 old(s).vm_ios.contains_key(dest),
1519 source != dest,
1520 old(s).vm_ios[source].vm_space is None,
1521 old(s).vm_ios[source].kind == VmIoKind::Reader,
1522 old(s).vm_ios[dest].vm_space is None,
1523 old(s).vm_ios[dest].kind == VmIoKind::Writer,
1524 ensures
1525 final(s).inv(),
1526{
1527 reveal(VmStore::structural_inv);
1528 let tracked mut src = s.tracked_extract_vm_io(source);
1529 let tracked mut dst = s.tracked_extract_vm_io(dest);
1530 let tracked val = io::read_step(&mut src, &mut dst);
1531}
1532
1533proof fn lemma_step_write<'rcu>(tracked s: &mut VmStore<'rcu>, source: VmIoId, dest: VmIoId)
1534 requires
1535 old(s).inv(),
1536 old(s).vm_ios.contains_key(source),
1537 old(s).vm_ios.contains_key(dest),
1538 source != dest,
1539 old(s).vm_ios[source].vm_space is None,
1540 old(s).vm_ios[source].kind == VmIoKind::Reader,
1541 old(s).vm_ios[dest].vm_space is None,
1542 old(s).vm_ios[dest].kind == VmIoKind::Writer,
1543 ensures
1544 final(s).inv(),
1545{
1546 reveal(VmStore::structural_inv);
1547 let tracked mut src = s.tracked_extract_vm_io(source);
1548 let tracked mut dst = s.tracked_extract_vm_io(dest);
1549}
1550
1551proof fn lemma_step_frame_from_unused<'rcu>(tracked s: &mut VmStore<'rcu>, paddr: Paddr)
1552 requires
1553 old(s).inv(),
1554 ensures
1555 final(s).inv(),
1556{
1557 reveal(VmStore::structural_inv);
1558 reveal(VmStore::accounting_inv);
1559 let ghost old_frames = s.frames;
1560 if !valid_frame_paddr(paddr) || s.regions.slots.contains_key(frame_to_index(paddr)) {
1561 let tracked res = frame::from_unused_step(&mut s.regions, paddr);
1562 match res {
1563 Option::Some(entry) => {
1564 let ghost id = fresh_frame_id(s.frames);
1565 lemma_fresh_frame_id_not_in_dom(s.frames);
1566 let ghost target_idx = frame_to_index(paddr);
1567 s.lemma_insert_frame(id, entry);
1568
1569 assert forall|idx: int|
1570 #![trigger s.regions.slot_owners[idx]]
1571 0 <= idx < max_meta_slots() && s.regions.ref_count(idx)
1572 == REF_COUNT_UNUSED implies handle_count(s.frames, idx) == 0
1573 && s.regions.slot_owners[idx].paths_in_pt.is_empty() by {
1574 lemma_handle_count_insert_fresh(old_frames, id, entry, idx);
1575 if idx == target_idx {
1576 assert(false);
1577 }
1578 };
1579 assert forall|idx: int|
1580 #![trigger s.regions.slot_owners[idx]]
1581 0 <= idx < max_meta_slots() && s.regions.slot_owners[idx].usage is Frame
1582 && s.regions.ref_count(idx) != REF_COUNT_UNUSED && s.regions.ref_count(idx)
1583 != REF_COUNT_UNIQUE implies handle_count(s.frames, idx) > 0
1584 || s.regions.slot_owners[idx].paths_in_pt.len() > 0 || segment_cover_count(
1585 s.segments,
1586 index_to_frame(idx),
1587 ) > 0 by {
1588 lemma_handle_count_insert_fresh(old_frames, id, entry, idx);
1589 };
1590
1591 assert forall|idx: int|
1593 #![trigger s.regions.slot_owners[idx]]
1594 0 <= idx < max_meta_slots() && s.regions.slot_owners[idx].usage is Frame && (
1595 handle_count(s.frames, idx) > 0 || s.regions.slot_owners[idx].paths_in_pt.len()
1596 > 0 || segment_cover_count(s.segments, index_to_frame(idx)) > 0) implies {
1597 let so = s.regions.slot_owners[idx];
1598 let rc = so.ref_count();
1599 &&& rc != REF_COUNT_UNUSED
1600 &&& rc != REF_COUNT_UNIQUE
1601 &&& rc == handle_count(s.frames, idx) + so.paths_in_pt.len()
1602 + segment_cover_count(s.segments, index_to_frame(idx))
1603 } by {
1604 lemma_handle_count_insert_fresh(old_frames, id, entry, idx);
1605 };
1606 },
1607 Option::None => {},
1608 }
1609 }
1610}
1611
1612proof fn lemma_step_frame_from_in_use<'rcu>(tracked s: &mut VmStore<'rcu>, paddr: Paddr)
1613 requires
1614 old(s).inv(),
1615 ensures
1616 final(s).inv(),
1617{
1618 reveal(VmStore::structural_inv);
1619 reveal(VmStore::accounting_inv);
1620 let ghost old_frames = s.frames;
1621 if !valid_frame_paddr(paddr) || s.regions.slots.contains_key(frame_to_index(paddr)) {
1622 let tracked res = frame::from_in_use_step(&mut s.regions, paddr);
1623 match res {
1624 Option::Some(entry) => {
1625 let ghost id = fresh_frame_id(s.frames);
1626 lemma_fresh_frame_id_not_in_dom(s.frames);
1627 let ghost target_idx = frame_to_index(paddr);
1628 s.lemma_insert_frame(id, entry);
1629
1630 assert forall|idx: int|
1631 #![trigger s.regions.slot_owners[idx]]
1632 0 <= idx < max_meta_slots() && s.regions.ref_count(idx)
1633 == REF_COUNT_UNUSED implies handle_count(s.frames, idx) == 0
1634 && s.regions.slot_owners[idx].paths_in_pt.is_empty() by {
1635 lemma_handle_count_insert_fresh(old_frames, id, entry, idx);
1636 if idx == target_idx {
1637 assert(false);
1638 }
1639 };
1640
1641 assert forall|idx: int|
1642 #![trigger s.regions.slot_owners[idx]]
1643 0 <= idx < max_meta_slots() && s.regions.slot_owners[idx].usage is Frame
1644 && s.regions.ref_count(idx) != REF_COUNT_UNUSED && s.regions.ref_count(idx)
1645 != REF_COUNT_UNIQUE implies handle_count(s.frames, idx) > 0
1646 || s.regions.slot_owners[idx].paths_in_pt.len() > 0 || segment_cover_count(
1647 s.segments,
1648 index_to_frame(idx),
1649 ) > 0 by {
1650 lemma_handle_count_insert_fresh(old_frames, id, entry, idx);
1651 };
1652
1653 assert forall|idx: int|
1654 #![trigger s.regions.slot_owners[idx]]
1655 0 <= idx < max_meta_slots() && s.regions.slot_owners[idx].usage is Frame && (
1656 handle_count(s.frames, idx) > 0 || s.regions.slot_owners[idx].paths_in_pt.len()
1657 > 0 || segment_cover_count(s.segments, index_to_frame(idx)) > 0) implies {
1658 let so = s.regions.slot_owners[idx];
1659 let rc = so.ref_count();
1660 &&& rc != REF_COUNT_UNUSED
1661 &&& rc != REF_COUNT_UNIQUE
1662 &&& rc == handle_count(s.frames, idx) + so.paths_in_pt.len()
1663 + segment_cover_count(s.segments, index_to_frame(idx))
1664 } by {
1665 lemma_handle_count_insert_fresh(old_frames, id, entry, idx);
1666 };
1667 },
1668 Option::None => {},
1669 }
1670 }
1671}
1672
1673proof fn lemma_step_frame_drop<'rcu>(tracked s: &mut VmStore<'rcu>, fid: FrameId)
1674 requires
1675 old(s).inv(),
1676 old(s).frames.contains_key(fid),
1677 segment_cover_count(old(s).segments, old(s).frames[fid].paddr) == 0,
1678 ensures
1679 final(s).inv(),
1680{
1681 reveal(VmStore::structural_inv);
1682 reveal(VmStore::accounting_inv);
1683 let ghost p = s.frames[fid].paddr;
1684 s.regions.lemma_contains_valid_frame_paddr(p);
1685 let ghost idx_p = frame_to_index(p);
1686 assert(s.frames.dom().filter(
1687 |gid: FrameId| frame_to_index(s.frames[gid].paddr) == idx_p,
1688 ).contains(fid));
1689 let ghost target_idx = frame_to_index(p);
1690 let ghost old_frames = s.frames;
1691 let ghost old_regions = s.regions;
1692 let tracked entry = s.tracked_extract_frame(fid);
1693 frame::drop_step(&mut s.regions, entry);
1694 assert forall|idx: int|
1695 #![trigger s.regions.slot_owners[idx]]
1696 0 <= idx < max_meta_slots() && s.regions.ref_count(idx)
1697 == REF_COUNT_UNUSED implies handle_count(s.frames, idx) == 0
1698 && s.regions.slot_owners[idx].paths_in_pt.is_empty() && segment_cover_count(
1699 s.segments,
1700 index_to_frame(idx),
1701 ) == 0 by {
1702 lemma_handle_count_remove(old_frames, fid, idx);
1703 };
1704
1705 assert forall|idx: int|
1706 #![trigger s.regions.slot_owners[idx]]
1707 0 <= idx < max_meta_slots() && s.regions.slot_owners[idx].usage is Frame
1708 && s.regions.ref_count(idx) != REF_COUNT_UNUSED && s.regions.ref_count(idx)
1709 != REF_COUNT_UNIQUE implies handle_count(s.frames, idx) > 0
1710 || s.regions.slot_owners[idx].paths_in_pt.len() > 0 || segment_cover_count(
1711 s.segments,
1712 index_to_frame(idx),
1713 ) > 0 by {
1714 lemma_handle_count_remove(old_frames, fid, idx);
1715 };
1716
1717 assert forall|idx: int|
1718 #![trigger s.regions.slot_owners[idx]]
1719 0 <= idx < max_meta_slots() && s.regions.slot_owners[idx].usage is Frame && (handle_count(
1720 s.frames,
1721 idx,
1722 ) > 0 || s.regions.slot_owners[idx].paths_in_pt.len() > 0 || segment_cover_count(
1723 s.segments,
1724 index_to_frame(idx),
1725 ) > 0) implies {
1726 let so = s.regions.slot_owners[idx];
1727 let rc = so.ref_count();
1728 &&& rc != REF_COUNT_UNUSED
1729 &&& rc != REF_COUNT_UNIQUE
1730 &&& rc == handle_count(s.frames, idx) + so.paths_in_pt.len() + segment_cover_count(
1731 s.segments,
1732 index_to_frame(idx),
1733 )
1734 } by {
1735 lemma_handle_count_remove(old_frames, fid, idx);
1736 if idx == target_idx {
1737 let ghost pre_rc = old_regions.ref_count(idx);
1738 if pre_rc > 1 {
1739 } else {
1740 assert(false);
1741 }
1742 }
1743 };
1744}
1745
1746#[verifier::spinoff_prover]
1748proof fn lemma_step_segment_from_unused_accounting<'rcu>(
1749 s_after: VmStore<'rcu>,
1750 old_store: VmStore<'rcu>,
1751 range: Range<Paddr>,
1752 id: SegmentId,
1753 entry: SegmentEntry,
1754)
1755 requires
1756 s_after.regions.inv(),
1757 s_after.frames == old_store.frames,
1758 !old_store.segments.contains_key(id),
1759 s_after.segments == old_store.segments.insert(id, entry),
1760 entry.range == range,
1761 range.start % PAGE_SIZE == 0,
1762 range.end % PAGE_SIZE == 0,
1763 range.start < range.end,
1764 range.end <= MAX_PADDR,
1765 old_store.accounting_inv(),
1766 old_store.regions.inv(),
1767 forall|paddr: Paddr|
1768 #![trigger frame_to_index(paddr)]
1769 (range.start <= paddr < range.end && paddr % PAGE_SIZE == 0) ==> {
1770 let idx = frame_to_index(paddr);
1771 let so = s_after.regions.slot_owners[idx];
1772 &&& so.usage is Frame
1773 &&& so.ref_count() == 1
1774 &&& so.paths_in_pt.is_empty()
1775 &&& so.storage_perm().is_init()
1776 },
1777 forall|i: int|
1778 #![trigger s_after.regions.slot_owners[i]]
1779 i < max_meta_slots() && !(range.start <= index_to_frame(i) < range.end)
1780 ==> s_after.regions.slot_owners[i] == old_store.regions.slot_owners[i],
1781 forall|paddr: Paddr|
1782 #![trigger frame_to_index(paddr)]
1783 (range.start <= paddr < range.end && paddr % PAGE_SIZE == 0)
1784 ==> old_store.regions.slot_owner(paddr).ref_count() == REF_COUNT_UNUSED,
1785 ensures
1786 s_after.accounting_inv(),
1787{
1788 reveal(VmStore::accounting_inv);
1789 let old_regions = old_store.regions;
1790 let old_frames = old_store.frames;
1791 let old_segments = old_store.segments;
1792 assert forall|idx: int|
1793 #![trigger s_after.regions.slot_owners[idx]]
1794 0 <= idx < max_meta_slots() && s_after.regions.ref_count(idx)
1795 == REF_COUNT_UNUSED implies handle_count(s_after.frames, idx) == 0
1796 && s_after.regions.slot_owners[idx].paths_in_pt.is_empty() && segment_cover_count(
1797 s_after.segments,
1798 index_to_frame(idx),
1799 ) == 0 by {
1800 let paddr = index_to_frame(idx);
1801 if range.start <= paddr < range.end {
1802 assert(false);
1803 } else {
1804 lemma_segment_cover_insert_outside(old_segments, id, entry, paddr);
1805 }
1806 };
1807 assert forall|idx: int|
1808 #![trigger s_after.regions.slot_owners[idx]]
1809 0 <= idx < max_meta_slots() && s_after.regions.slot_owners[idx].usage is Frame
1810 && s_after.regions.ref_count(idx) != REF_COUNT_UNUSED && s_after.regions.ref_count(idx)
1811 != REF_COUNT_UNIQUE implies handle_count(s_after.frames, idx) > 0
1812 || s_after.regions.slot_owners[idx].paths_in_pt.len() > 0 || segment_cover_count(
1813 s_after.segments,
1814 index_to_frame(idx),
1815 ) > 0 by {
1816 let paddr = index_to_frame(idx);
1817 if range.start <= paddr < range.end {
1818 lemma_segment_cover_insert_inside(old_segments, id, entry, paddr);
1819 } else {
1820 lemma_segment_cover_insert_outside(old_segments, id, entry, paddr);
1821 }
1822 };
1823 assert forall|idx: int|
1824 #![trigger s_after.regions.slot_owners[idx]]
1825 0 <= idx < max_meta_slots() && s_after.regions.slot_owners[idx].usage is Frame && (
1826 handle_count(s_after.frames, idx) > 0 || s_after.regions.slot_owners[idx].paths_in_pt.len()
1827 > 0 || segment_cover_count(s_after.segments, index_to_frame(idx)) > 0) implies {
1828 let so = s_after.regions.slot_owners[idx];
1829 let rc = so.ref_count();
1830 &&& rc != REF_COUNT_UNUSED
1831 &&& rc != REF_COUNT_UNIQUE
1832 &&& rc == handle_count(s_after.frames, idx) + so.paths_in_pt.len() + segment_cover_count(
1833 s_after.segments,
1834 index_to_frame(idx),
1835 )
1836 } by {
1837 let paddr = index_to_frame(idx);
1838 if range.start <= paddr < range.end {
1839 lemma_segment_cover_insert_inside(old_segments, id, entry, paddr);
1840 } else {
1841 lemma_segment_cover_insert_outside(old_segments, id, entry, paddr);
1842 }
1843 };
1844}
1845
1846#[verifier::spinoff_prover]
1848proof fn lemma_step_segment_from_unused<'rcu>(tracked s: &mut VmStore<'rcu>, range: Range<Paddr>)
1849 requires
1850 old(s).inv(),
1851 ensures
1852 final(s).inv(),
1853{
1854 reveal(VmStore::structural_inv);
1855 if range.start % PAGE_SIZE == 0 && range.end % PAGE_SIZE == 0 && range.start < range.end
1856 && range.end <= MAX_PADDR && (forall|paddr: Paddr|
1857 #![trigger frame_to_index(paddr)]
1858 (range.start <= paddr < range.end && paddr % PAGE_SIZE == 0) ==> s.regions.slot_owner(
1859 paddr,
1860 ).ref_count() == REF_COUNT_UNUSED) {
1861 let ghost s_before = *s;
1862 let tracked res = segment::from_unused_step(&mut s.regions, range);
1863 match res {
1864 Option::Some(entry) => {
1865 let ghost id = fresh_segment_id(s.segments);
1866 lemma_fresh_segment_id_not_in_dom(s.segments);
1867 s.lemma_insert_segment(id, entry);
1868 lemma_step_segment_from_unused_accounting(*s, s_before, range, id, entry);
1869 },
1870 Option::None => {},
1871 }
1872 }
1873 assert forall|idx: int|
1874 #![trigger final(s).regions.slot_owners[idx]]
1875 0 <= idx < max_meta_slots() ==> final(s).regions.slots.contains_key(idx) || (
1876 final(s).regions.slot_owners[idx].usage is PageTable && final(s).regions.ref_count(idx)
1877 != REF_COUNT_UNUSED) by {};
1878}
1879
1880proof fn lemma_accounting_inv_intro<'rcu>(store: VmStore<'rcu>)
1881 requires
1882 forall|idx: int|
1883 #![trigger store.regions.slot_owners[idx]]
1884 0 <= idx < max_meta_slots() && store.regions.ref_count(idx) == REF_COUNT_UNUSED
1885 ==> handle_count(store.frames, idx) == 0
1886 && store.regions.slot_owners[idx].paths_in_pt.is_empty() && segment_cover_count(
1887 store.segments,
1888 index_to_frame(idx),
1889 ) == 0,
1890 forall|idx: int|
1891 #![trigger store.regions.slot_owners[idx]]
1892 0 <= idx < max_meta_slots() && store.regions.slot_owners[idx].usage is Frame
1893 && store.regions.ref_count(idx) != REF_COUNT_UNUSED && store.regions.ref_count(idx)
1894 != REF_COUNT_UNIQUE ==> handle_count(store.frames, idx) > 0
1895 || store.regions.slot_owners[idx].paths_in_pt.len() > 0 || segment_cover_count(
1896 store.segments,
1897 index_to_frame(idx),
1898 ) > 0,
1899 forall|idx: int|
1900 #![trigger store.regions.slot_owners[idx]]
1901 0 <= idx < max_meta_slots() && store.regions.slot_owners[idx].usage is Frame && (
1902 handle_count(store.frames, idx) > 0 || store.regions.slot_owners[idx].paths_in_pt.len()
1903 > 0 || segment_cover_count(store.segments, index_to_frame(idx)) > 0) ==> {
1904 let so = store.regions.slot_owners[idx];
1905 let rc = so.ref_count();
1906 &&& rc != REF_COUNT_UNUSED
1907 &&& rc != REF_COUNT_UNIQUE
1908 &&& rc == handle_count(store.frames, idx) + so.paths_in_pt.len()
1909 + segment_cover_count(store.segments, index_to_frame(idx))
1910 },
1911 ensures
1912 store.accounting_inv(),
1913{
1914 reveal(VmStore::accounting_inv);
1915}
1916
1917#[verifier::spinoff_prover]
1918proof fn lemma_drop_segment_with_store_inv<'rcu>(
1919 tracked regions: &mut MetaRegionOwners,
1920 tracked entry: SegmentEntry,
1921 store: VmStore<'rcu>,
1922 sid: SegmentId,
1923)
1924 requires
1925 store.inv(),
1926 store.segments.contains_key(sid),
1927 entry == store.segments[sid],
1928 *old(regions) == store.regions,
1929 ensures
1930 final(regions).inv(),
1931 final(regions).slots == old(regions).slots,
1932 forall|paddr: Paddr|
1933 #![trigger frame_to_index(paddr)]
1934 (entry.range.start <= paddr < entry.range.end && paddr % PAGE_SIZE == 0) ==> {
1935 let idx = frame_to_index(paddr);
1936 let so_old = old(regions).slot_owners[idx];
1937 let so_new = final(regions).slot_owners[idx];
1938 &&& so_old.ref_count() >= 1
1939 &&& so_old.ref_count() <= REF_COUNT_MAX
1940 &&& so_old.usage is Frame
1941 &&& so_old.ref_count() == 1 ==> so_old.paths_in_pt.is_empty()
1942 &&& so_new.usage == so_old.usage
1943 &&& so_new.paths_in_pt == so_old.paths_in_pt
1944 &&& so_new.slot_vaddr == so_old.slot_vaddr
1945 &&& so_new.in_list_perm == so_old.in_list_perm
1946 &&& so_old.ref_count() == 1 ==> so_new.ref_count() == REF_COUNT_UNUSED
1947 &&& so_old.ref_count() > 1 ==> so_new.ref_count() == (so_old.ref_count() - 1) as u64
1948 },
1949 forall|i: int|
1950 #![trigger final(regions).slot_owners[i]]
1951 i < max_meta_slots() && !(entry.range.start <= index_to_frame(i) < entry.range.end)
1952 ==> final(regions).slot_owners[i] == old(regions).slot_owners[i],
1953 forall|i: int|
1954 #![trigger final(regions).slot_owners[i]]
1955 !old(regions).slots.contains_key(i) ==> final(regions).slot_owners[i] == old(
1956 regions,
1957 ).slot_owners[i],
1958 forall|c: CursorOwner<'_, UserPtConfig>|
1959 #![auto]
1960 c.metaregion_sound(*old(regions)) ==> c.metaregion_sound(*final(regions)),
1961{
1962 reveal(VmStore::structural_inv);
1963 reveal(VmStore::accounting_inv);
1964 assert forall|paddr: Paddr|
1965 #![trigger store.regions.slot_owner(paddr)]
1966 (entry.range.start <= paddr < entry.range.end && paddr % PAGE_SIZE == 0) implies {
1967 let so = store.regions.slot_owner(paddr);
1968 &&& so.ref_count() >= 1
1969 &&& so.ref_count() <= REF_COUNT_MAX
1970 &&& so.usage is Frame
1971 &&& so.ref_count() == 1 ==> so.paths_in_pt.is_empty()
1972 } by {
1973 let idx = frame_to_index(paddr);
1974 lemma_segment_cover_contains(store.segments, sid, paddr);
1975 let so = store.regions.slot_owners[idx];
1976 let rc = so.ref_count();
1977 assert(store.regions.contains(idx));
1978 };
1979 segment::drop_step(regions, entry);
1980}
1981
1982#[verifier::spinoff_prover]
1984#[verifier::rlimit(50)]
1985proof fn lemma_step_segment_drop<'rcu>(tracked s: &mut VmStore<'rcu>, sid: SegmentId)
1986 requires
1987 old(s).inv(),
1988 old(s).segments.contains_key(sid),
1989 ensures
1990 final(s).inv(),
1991{
1992 let ghost s_before = *s;
1993 let ghost old_regions = s.regions;
1994 let ghost old_frames = s.frames;
1995 let ghost old_segments = s.segments;
1996 let ghost range = s.segments[sid].range;
1997 let tracked entry = s.tracked_extract_segment(sid);
1998 assert(entry.range == range);
1999 lemma_drop_segment_with_store_inv(&mut s.regions, entry, s_before, sid);
2000 lemma_coverage_preserved_slots_eq(s_before, *s);
2001
2002 assert forall|idx: int|
2003 0 <= idx
2004 < max_meta_slots() implies #[trigger] s.regions.slot_owners[idx].in_list_perm.value()
2005 == 0 by {
2006 reveal(VmStore::structural_inv);
2007 let paddr = index_to_frame(idx);
2008 if range.start <= paddr < range.end {
2009 } else {
2010 assert(s.regions.slot_owners[idx] == old_regions.slot_owners[idx]);
2011 }
2012 };
2013 assert forall|idx: int|
2014 #![trigger s.regions.slot_owners[idx]]
2015 0 <= idx < max_meta_slots() && s.regions.ref_count(idx)
2016 == REF_COUNT_UNUSED implies handle_count(s.frames, idx) == 0
2017 && s.regions.slot_owners[idx].paths_in_pt.is_empty() && segment_cover_count(
2018 s.segments,
2019 index_to_frame(idx),
2020 ) == 0 by {
2021 lemma_accounting_inv_at(s_before, idx);
2022 let paddr = index_to_frame(idx);
2023 if range.start <= paddr < range.end {
2024 lemma_segment_cover_contains(old_segments, sid, paddr);
2025 lemma_segment_cover_remove_inside(old_segments, sid, paddr);
2026 assert(old_regions.ref_count(idx) == 1);
2027 } else {
2028 assert(s.regions.slot_owners[idx] == old_regions.slot_owners[idx]);
2029 assert(!(entry.range.start <= paddr < entry.range.end));
2030 lemma_segment_cover_remove_outside(old_segments, sid, paddr);
2031 }
2032 };
2033 assert forall|idx: int|
2034 #![trigger s.regions.slot_owners[idx]]
2035 0 <= idx < max_meta_slots() && s.regions.slot_owners[idx].usage is Frame
2036 && s.regions.ref_count(idx) != REF_COUNT_UNUSED && s.regions.ref_count(idx)
2037 != REF_COUNT_UNIQUE implies handle_count(s.frames, idx) > 0
2038 || s.regions.slot_owners[idx].paths_in_pt.len() > 0 || segment_cover_count(
2039 s.segments,
2040 index_to_frame(idx),
2041 ) > 0 by {
2042 lemma_accounting_inv_at(s_before, idx);
2043 let paddr = index_to_frame(idx);
2044 if range.start <= paddr < range.end {
2045 lemma_segment_cover_remove_inside(old_segments, sid, paddr);
2046 } else {
2047 assert(s.regions.slot_owners[idx] == old_regions.slot_owners[idx]);
2048 lemma_segment_cover_remove_outside(old_segments, sid, paddr);
2049 }
2050 };
2051 assert forall|idx: int|
2052 #![trigger s.regions.slot_owners[idx]]
2053 0 <= idx < max_meta_slots() && s.regions.slot_owners[idx].usage is Frame && (handle_count(
2054 s.frames,
2055 idx,
2056 ) > 0 || s.regions.slot_owners[idx].paths_in_pt.len() > 0 || segment_cover_count(
2057 s.segments,
2058 index_to_frame(idx),
2059 ) > 0) implies {
2060 let so = s.regions.slot_owners[idx];
2061 let rc = so.ref_count();
2062 &&& rc != REF_COUNT_UNUSED
2063 &&& rc != REF_COUNT_UNIQUE
2064 &&& rc == handle_count(s.frames, idx) + so.paths_in_pt.len() + segment_cover_count(
2065 s.segments,
2066 index_to_frame(idx),
2067 )
2068 } by {
2069 lemma_accounting_inv_at(s_before, idx);
2070 let paddr = index_to_frame(idx);
2071 if range.start <= paddr < range.end {
2072 lemma_segment_cover_contains(old_segments, sid, paddr);
2073 lemma_segment_cover_remove_inside(old_segments, sid, paddr);
2074 let pre_rc = old_regions.ref_count(idx);
2076 let pre_H = handle_count(old_frames, idx);
2077 let pre_P = old_regions.slot_owners[idx].paths_in_pt.len();
2078 let pre_cover = segment_cover_count(old_segments, paddr);
2079 let post_rc = s.regions.ref_count(idx);
2080 assert(s.regions.contains(idx));
2081 } else {
2082 assert(!(entry.range.start <= paddr < entry.range.end));
2083 lemma_segment_cover_remove_outside(old_segments, sid, paddr);
2084 }
2085 };
2086 lemma_accounting_inv_intro(*s);
2087 assert(s.structural_inv()) by {
2088 reveal(VmStore::structural_inv);
2089 };
2090}
2091
2092proof fn lemma_step_segment_split<'rcu>(
2094 tracked s: &mut VmStore<'rcu>,
2095 sid: SegmentId,
2096 offset: usize,
2097)
2098 requires
2099 old(s).inv(),
2100 old(s).segments.contains_key(sid),
2101 offset % PAGE_SIZE == 0,
2102 0 < offset,
2103 offset < (old(s).segments[sid].range.end - old(s).segments[sid].range.start),
2104 ensures
2105 final(s).inv(),
2106{
2107 let ghost s_before = *s;
2108 let ghost old_regions = s.regions;
2109 let ghost old_segments = s.segments;
2110 let ghost range = s.segments[sid].range;
2111 assert(range.start % PAGE_SIZE == 0 && range.end % PAGE_SIZE == 0) by {
2112 reveal(VmStore::structural_inv);
2113 };
2114 let ghost mid = (range.start + offset) as Paddr;
2115 let ghost entry_left = SegmentEntry { range: range.start..mid };
2116 let ghost entry_right = SegmentEntry { range: mid..range.end };
2117 let ghost id_left = fresh_segment_id(s.segments);
2118 lemma_fresh_segment_id_not_in_dom(s.segments);
2119 assert(id_left != sid);
2120 let ghost stub_entry = SegmentEntry { range: range.start..mid };
2121 let ghost id_right = fresh_segment_id(s.segments.insert(id_left, stub_entry));
2122 lemma_fresh_segment_id_not_in_dom(s.segments.insert(id_left, stub_entry));
2123 assert(id_right != sid);
2124 assert(id_right != id_left);
2125 let tracked _orig = s.tracked_extract_segment(sid);
2127 assert(!s.segments.contains_key(id_left));
2128 let tracked entry_l = tracked_segment_entry_new(range.start..mid);
2129 s.lemma_insert_segment(id_left, entry_l);
2130 assert(!s.segments.contains_key(id_right));
2131 let tracked entry_r = tracked_segment_entry_new(mid..range.end);
2132 s.lemma_insert_segment(id_right, entry_r);
2133 assert(s.regions == old_regions);
2134 assert forall|paddr: Paddr| #[trigger]
2135 frame_to_index(paddr) < max_meta_slots() implies segment_cover_count(s.segments, paddr)
2136 == segment_cover_count(old_segments, paddr) by {
2137 lemma_segment_cover_split(
2138 old_segments,
2139 sid,
2140 id_left,
2141 id_right,
2142 entry_left,
2143 entry_right,
2144 paddr,
2145 );
2146 };
2147 assert(entry_left.range.start % PAGE_SIZE == 0);
2148 assert(entry_right.range.start % PAGE_SIZE == 0);
2149 assert(entry_left.range.end % PAGE_SIZE == 0);
2150 assert(entry_right.range.end % PAGE_SIZE == 0);
2151 assert forall|sid_other: SegmentId, paddr_c: Paddr|
2152 #![trigger
2153 s.segments.contains_key(sid_other),
2154 frame_to_index(paddr_c)]
2155 s.segments.contains_key(sid_other) && s.segments[sid_other].range.start <= paddr_c
2156 < s.segments[sid_other].range.end && paddr_c % PAGE_SIZE
2157 == 0 implies s.regions.slot_owner(paddr_c).usage is Frame by {
2158 if sid_other == id_left {
2159 assert(old_segments.contains_key(sid));
2160 assert(old_segments[sid].range.start <= paddr_c < old_segments[sid].range.end);
2161 lemma_structural_inv_segment(s_before, sid, paddr_c);
2162 } else if sid_other == id_right {
2163 assert(old_segments.contains_key(sid));
2164 assert(old_segments[sid].range.start <= paddr_c < old_segments[sid].range.end);
2165 lemma_structural_inv_segment(s_before, sid, paddr_c);
2166 } else {
2167 assert(old_segments.contains_key(sid_other));
2168 assert(old_segments[sid_other] == s.segments[sid_other]);
2169 lemma_structural_inv_segment(s_before, sid_other, paddr_c);
2170 }
2171 };
2172 assert forall|idx: int|
2173 #![trigger s.regions.slot_owners[idx]]
2174 0 <= idx < max_meta_slots() && s.regions.ref_count(idx)
2175 == REF_COUNT_UNUSED implies handle_count(s.frames, idx) == 0
2176 && s.regions.slot_owners[idx].paths_in_pt.is_empty() && segment_cover_count(
2177 s.segments,
2178 index_to_frame(idx),
2179 ) == 0 by {
2180 lemma_accounting_inv_at(s_before, idx);
2181 let paddr = index_to_frame(idx);
2182 assert(paddr == (idx * PAGE_SIZE) as usize);
2183 assert(frame_to_index(paddr) == idx);
2184 lemma_segment_cover_split(
2185 old_segments,
2186 sid,
2187 id_left,
2188 id_right,
2189 entry_left,
2190 entry_right,
2191 paddr,
2192 );
2193 };
2194 assert forall|idx: int|
2195 #![trigger s.regions.slot_owners[idx]]
2196 0 <= idx < max_meta_slots() && s.regions.slot_owners[idx].usage is Frame
2197 && s.regions.ref_count(idx) != REF_COUNT_UNUSED && s.regions.ref_count(idx)
2198 != REF_COUNT_UNIQUE implies handle_count(s.frames, idx) > 0
2199 || s.regions.slot_owners[idx].paths_in_pt.len() > 0 || segment_cover_count(
2200 s.segments,
2201 index_to_frame(idx),
2202 ) > 0 by {
2203 lemma_accounting_inv_at(s_before, idx);
2204 let paddr = index_to_frame(idx);
2205 assert(paddr == (idx * PAGE_SIZE) as usize);
2206 assert(frame_to_index(paddr) == idx);
2207 lemma_segment_cover_split(
2208 old_segments,
2209 sid,
2210 id_left,
2211 id_right,
2212 entry_left,
2213 entry_right,
2214 paddr,
2215 );
2216 };
2217 assert forall|idx: int|
2218 #![trigger s.regions.slot_owners[idx]]
2219 0 <= idx < max_meta_slots() && s.regions.slot_owners[idx].usage is Frame && (handle_count(
2220 s.frames,
2221 idx,
2222 ) > 0 || s.regions.slot_owners[idx].paths_in_pt.len() > 0 || segment_cover_count(
2223 s.segments,
2224 index_to_frame(idx),
2225 ) > 0) implies {
2226 let so = s.regions.slot_owners[idx];
2227 let rc = so.ref_count();
2228 &&& rc != REF_COUNT_UNUSED
2229 &&& rc != REF_COUNT_UNIQUE
2230 &&& rc == handle_count(s.frames, idx) + so.paths_in_pt.len() + segment_cover_count(
2231 s.segments,
2232 index_to_frame(idx),
2233 )
2234 } by {
2235 lemma_accounting_inv_at(s_before, idx);
2236 let paddr = index_to_frame(idx);
2237 assert(paddr == (idx * PAGE_SIZE) as usize);
2238 assert(frame_to_index(paddr) == idx);
2239 lemma_segment_cover_split(
2240 old_segments,
2241 sid,
2242 id_left,
2243 id_right,
2244 entry_left,
2245 entry_right,
2246 paddr,
2247 );
2248 };
2249 lemma_accounting_inv_intro(*s);
2250 assert(s.structural_inv()) by {
2251 reveal(VmStore::structural_inv);
2252 };
2253}
2254
2255#[verifier::spinoff_prover]
2257#[verifier::rlimit(200)]
2258proof fn lemma_step_segment_next<'rcu>(tracked s: &mut VmStore<'rcu>, sid: SegmentId)
2259 requires
2260 old(s).inv(),
2261 old(s).segments.contains_key(sid),
2262 ensures
2263 final(s).inv(),
2264{
2265 reveal(VmStore::structural_inv);
2266 reveal(VmStore::accounting_inv);
2267 let ghost old_frames = s.frames;
2268 let ghost old_segments = s.segments;
2269 let ghost range = s.segments[sid].range;
2270 let ghost paddr = range.start;
2271 let ghost target_idx = frame_to_index(paddr);
2272 let ghost new_range_start = (paddr + PAGE_SIZE) as Paddr;
2273 let ghost new_range_end = range.end;
2274 let ghost will_become_empty = new_range_start >= new_range_end;
2275 let ghost new_entry_ghost = SegmentEntry { range: new_range_start..new_range_end };
2276
2277 s.regions.lemma_contains_valid_frame_paddr(paddr);
2279
2280 let ghost fid = fresh_frame_id(s.frames);
2282 lemma_fresh_frame_id_not_in_dom(s.frames);
2283 let tracked frame_entry = tracked_frame_entry_new(paddr);
2284 s.lemma_insert_frame(fid, frame_entry);
2285 let tracked _old_entry = s.tracked_extract_segment(sid);
2287 if !will_become_empty {
2288 let tracked new_entry = tracked_segment_entry_new(new_range_start..new_range_end);
2289 s.lemma_insert_segment(sid, new_entry);
2290 }
2291 assert(s.frames == old_frames.insert(fid, frame_entry));
2292
2293 assert forall|paddr_c: Paddr|
2294 paddr_c % PAGE_SIZE == 0 && paddr_c == paddr implies #[trigger] segment_cover_count(
2295 s.segments,
2296 paddr_c,
2297 ) + 1 == segment_cover_count(old_segments, paddr_c) by {
2298 lemma_segment_cover_shrink_front(old_segments, sid, new_entry_ghost, paddr_c);
2299 };
2300 assert forall|paddr_c: Paddr|
2301 paddr_c % PAGE_SIZE == 0 && paddr_c != paddr implies #[trigger] segment_cover_count(
2302 s.segments,
2303 paddr_c,
2304 ) == segment_cover_count(old_segments, paddr_c) by {
2305 lemma_segment_cover_shrink_front(old_segments, sid, new_entry_ghost, paddr_c);
2306 };
2307
2308 assert forall|idx: int|
2309 #![trigger s.regions.slot_owners[idx]]
2310 0 <= idx < max_meta_slots() && s.regions.ref_count(idx)
2311 == REF_COUNT_UNUSED implies handle_count(s.frames, idx) == 0
2312 && s.regions.slot_owners[idx].paths_in_pt.is_empty() && segment_cover_count(
2313 s.segments,
2314 index_to_frame(idx),
2315 ) == 0 by {
2316 let paddr_c = index_to_frame(idx);
2317 if idx == target_idx {
2318 lemma_segment_cover_contains(old_segments, sid, paddr);
2319 assert(false);
2320 } else {
2321 lemma_handle_count_insert_fresh(old_frames, fid, frame_entry, idx);
2322 }
2323 };
2324 assert forall|idx: int|
2325 #![trigger s.regions.slot_owners[idx]]
2326 0 <= idx < max_meta_slots() && s.regions.slot_owners[idx].usage is Frame
2327 && s.regions.ref_count(idx) != REF_COUNT_UNUSED && s.regions.ref_count(idx)
2328 != REF_COUNT_UNIQUE implies handle_count(s.frames, idx) > 0
2329 || s.regions.slot_owners[idx].paths_in_pt.len() > 0 || segment_cover_count(
2330 s.segments,
2331 index_to_frame(idx),
2332 ) > 0 by {
2333 let paddr_c = index_to_frame(idx);
2334 if idx == target_idx {
2335 lemma_handle_count_insert_fresh(old_frames, fid, frame_entry, idx);
2337 } else {
2338 lemma_handle_count_insert_fresh(old_frames, fid, frame_entry, idx);
2339 }
2340 };
2341 assert forall|idx: int|
2342 #![trigger s.regions.slot_owners[idx]]
2343 0 <= idx < max_meta_slots() && s.regions.slot_owners[idx].usage is Frame && (handle_count(
2344 s.frames,
2345 idx,
2346 ) > 0 || s.regions.slot_owners[idx].paths_in_pt.len() > 0 || segment_cover_count(
2347 s.segments,
2348 index_to_frame(idx),
2349 ) > 0) implies {
2350 let so = s.regions.slot_owners[idx];
2351 let rc = so.ref_count();
2352 &&& rc != REF_COUNT_UNUSED
2353 &&& rc != REF_COUNT_UNIQUE
2354 &&& rc == handle_count(s.frames, idx) + so.paths_in_pt.len() + segment_cover_count(
2355 s.segments,
2356 index_to_frame(idx),
2357 )
2358 } by {
2359 let paddr_c = index_to_frame(idx);
2360 lemma_handle_count_insert_fresh(old_frames, fid, frame_entry, idx);
2361 };
2362 assert forall|u: UniqueId| #[trigger] s.unique_frames.contains_key(u) implies {
2363 let so = s.regions.slot_owner(s.unique_frames[u].paddr);
2364 &&& so.usage is Frame
2365 &&& so.ref_count() == REF_COUNT_UNIQUE
2366 &&& so.in_list_perm.value() == 0
2367 &&& so.paths_in_pt.is_empty()
2368 } by {};
2369 reveal(CursorContinuation::map_children);
2370 reveal(CursorOwner::path_metaregion_sound);
2371}
2372
2373#[verifier::spinoff_prover]
2374proof fn lemma_step_segment_clone_range<'rcu>(
2375 tracked s: &mut VmStore<'rcu>,
2376 sid: SegmentId,
2377 sub_range: Range<Paddr>,
2378)
2379 requires
2380 old(s).inv(),
2381 old(s).segments.contains_key(sid),
2382 sub_range.start % PAGE_SIZE == 0,
2383 sub_range.end % PAGE_SIZE == 0,
2384 old(s).segments[sid].range.start <= sub_range.start,
2385 sub_range.start < sub_range.end,
2386 sub_range.end <= old(s).segments[sid].range.end,
2387 forall|paddr: Paddr|
2388 #![trigger frame_to_index(paddr)]
2389 (sub_range.start <= paddr < sub_range.end && paddr % PAGE_SIZE == 0) ==> old(
2390 s,
2391 ).regions.slot_owner(paddr).ref_count() + 1 <= REF_COUNT_MAX,
2392 ensures
2393 final(s).inv(),
2394{
2395 let ghost s_before = *s;
2397 let ghost old_regions = s.regions;
2398 let ghost old_segments = s.segments;
2399 let ghost sid_range = s.segments[sid].range;
2400 let ghost new_entry_ghost = SegmentEntry { range: sub_range };
2401
2402 assert(sid_range.end <= MAX_PADDR) by {
2403 reveal(VmStore::structural_inv);
2404 };
2405
2406 assert forall|paddr: Paddr|
2407 #![trigger frame_to_index(paddr)]
2408 (sub_range.start <= paddr < sub_range.end && paddr % PAGE_SIZE == 0) implies {
2409 let so = old_regions.slot_owner(paddr);
2410 &&& so.usage is Frame
2411 &&& so.ref_count() >= 1
2412 &&& so.ref_count() + 1 <= REF_COUNT_MAX
2413 } by {
2414 lemma_structural_inv_segment(s_before, sid, paddr);
2416 let idx = frame_to_index(paddr);
2417 lemma_accounting_inv_at(s_before, idx);
2418 };
2420
2421 segment::segment_clone_embedded(&mut s.regions, sub_range);
2423
2424 let ghost sid2 = fresh_segment_id(s.segments);
2426 lemma_fresh_segment_id_not_in_dom(s.segments);
2427 let tracked new_entry = tracked_segment_entry_new(sub_range);
2428 s.lemma_insert_segment(sid2, new_entry);
2429
2430 assert forall|paddr_c: Paddr|
2432 paddr_c % PAGE_SIZE == 0 && sub_range.start <= paddr_c
2433 < sub_range.end implies #[trigger] segment_cover_count(s.segments, paddr_c)
2434 == segment_cover_count(old_segments, paddr_c) + 1 by {
2435 lemma_segment_cover_insert_inside(old_segments, sid2, new_entry_ghost, paddr_c);
2436 };
2437 assert forall|paddr_c: Paddr|
2438 paddr_c % PAGE_SIZE == 0 && !(sub_range.start <= paddr_c
2439 < sub_range.end) implies #[trigger] segment_cover_count(s.segments, paddr_c)
2440 == segment_cover_count(old_segments, paddr_c) by {
2441 lemma_segment_cover_insert_outside(old_segments, sid2, new_entry_ghost, paddr_c);
2442 };
2443
2444 assert forall|idx: int|
2446 #![trigger s.regions.slot_owners[idx]]
2447 0 <= idx < max_meta_slots() && s.regions.ref_count(idx)
2448 == REF_COUNT_UNUSED implies handle_count(s.frames, idx) == 0
2449 && s.regions.slot_owners[idx].paths_in_pt.is_empty() && segment_cover_count(
2450 s.segments,
2451 index_to_frame(idx),
2452 ) == 0 by {
2453 lemma_accounting_inv_at(s_before, idx);
2454 let aligned = index_to_frame(idx);
2455 if sub_range.start <= aligned < sub_range.end {
2456 assert(false);
2458 }
2459 };
2460 assert forall|idx: int|
2462 #![trigger s.regions.slot_owners[idx]]
2463 0 <= idx < max_meta_slots() && s.regions.slot_owners[idx].usage is Frame
2464 && s.regions.ref_count(idx) != REF_COUNT_UNUSED && s.regions.ref_count(idx)
2465 != REF_COUNT_UNIQUE implies handle_count(s.frames, idx) > 0
2466 || s.regions.slot_owners[idx].paths_in_pt.len() > 0 || segment_cover_count(
2467 s.segments,
2468 index_to_frame(idx),
2469 ) > 0 by {
2470 lemma_accounting_inv_at(s_before, idx);
2471 let aligned = index_to_frame(idx);
2472 };
2473 assert forall|idx: int|
2475 #![trigger s.regions.slot_owners[idx]]
2476 0 <= idx < max_meta_slots() && s.regions.slot_owners[idx].usage is Frame && (handle_count(
2477 s.frames,
2478 idx,
2479 ) > 0 || s.regions.slot_owners[idx].paths_in_pt.len() > 0 || segment_cover_count(
2480 s.segments,
2481 index_to_frame(idx),
2482 ) > 0) implies {
2483 let so = s.regions.slot_owners[idx];
2484 let rc = so.ref_count();
2485 &&& rc != REF_COUNT_UNUSED
2486 &&& rc != REF_COUNT_UNIQUE
2487 &&& rc == handle_count(s.frames, idx) + so.paths_in_pt.len() + segment_cover_count(
2488 s.segments,
2489 index_to_frame(idx),
2490 )
2491 } by {
2492 lemma_accounting_inv_at(s_before, idx);
2493 let aligned = index_to_frame(idx);
2494 let so = s.regions.slot_owners[idx];
2495 let rc = so.ref_count();
2496 };
2497 lemma_accounting_inv_intro(*s);
2498 assert(s.structural_inv()) by {
2499 reveal(VmStore::structural_inv);
2500 };
2501}
2502
2503proof fn lemma_step_segment_clone<'rcu>(tracked s: &mut VmStore<'rcu>, sid: SegmentId)
2505 requires
2506 old(s).inv(),
2507 old(s).segments.contains_key(sid),
2508 forall|paddr: Paddr|
2509 #![trigger frame_to_index(paddr)]
2510 (old(s).segments[sid].range.start <= paddr < old(s).segments[sid].range.end && paddr
2511 % PAGE_SIZE == 0) ==> old(s).regions.slot_owner(paddr).ref_count() + 1
2512 <= REF_COUNT_MAX,
2513 ensures
2514 final(s).inv(),
2515{
2516 reveal(VmStore::structural_inv);
2517}
2518
2519proof fn lemma_step_segment_slice<'rcu>(
2521 tracked s: &mut VmStore<'rcu>,
2522 sid: SegmentId,
2523 sub_range: Range<Paddr>,
2524)
2525 requires
2526 old(s).inv(),
2527 old(s).segments.contains_key(sid),
2528 sub_range.start % PAGE_SIZE == 0,
2529 sub_range.end % PAGE_SIZE == 0,
2530 old(s).segments[sid].range.start <= sub_range.start,
2531 sub_range.start < sub_range.end,
2532 sub_range.end <= old(s).segments[sid].range.end,
2533 forall|paddr: Paddr|
2534 #![trigger frame_to_index(paddr)]
2535 (sub_range.start <= paddr < sub_range.end && paddr % PAGE_SIZE == 0) ==> old(
2536 s,
2537 ).regions.slot_owner(paddr).ref_count() + 1 <= REF_COUNT_MAX,
2538 ensures
2539 final(s).inv(),
2540{
2541}
2542
2543proof fn lemma_step_unique_from_unused<'rcu>(tracked s: &mut VmStore<'rcu>, paddr: Paddr)
2544 requires
2545 old(s).inv(),
2546 ensures
2547 final(s).inv(),
2548{
2549 reveal(VmStore::structural_inv);
2550 reveal(VmStore::accounting_inv);
2551 if valid_frame_paddr(paddr) && s.regions.slots.contains_key(frame_to_index(paddr))
2552 && s.regions.slot_owner(paddr).usage is Unused && s.regions.slot_owner(paddr).ref_count()
2553 == REF_COUNT_UNUSED {
2554 unique::unique_from_unused_embedded(&mut s.regions, paddr);
2556
2557 let ghost uid = fresh_unique_id(s.unique_frames);
2559 lemma_fresh_unique_id_not_in_dom(s.unique_frames);
2560 let tracked entry = tracked_unique_entry_new(paddr);
2561 s.lemma_insert_unique(uid, entry);
2562
2563 }
2564}
2565
2566proof fn lemma_step_unique_drop<'rcu>(tracked s: &mut VmStore<'rcu>, uid: UniqueId)
2571 requires
2572 old(s).inv(),
2573 old(s).unique_frames.contains_key(uid),
2574 ensures
2575 final(s).inv(),
2576{
2577 reveal(VmStore::structural_inv);
2578 reveal(VmStore::accounting_inv);
2579
2580}
2581
2582proof fn lemma_step_from_unique<'rcu>(tracked s: &mut VmStore<'rcu>, uid: UniqueId)
2584 requires
2585 old(s).inv(),
2586 old(s).unique_frames.contains_key(uid),
2587 ensures
2588 final(s).inv(),
2589{
2590 reveal(VmStore::structural_inv);
2591 reveal(VmStore::accounting_inv);
2592 let ghost old_frames = s.frames;
2593 let ghost paddr = s.unique_frames[uid].paddr;
2594 let ghost idx = frame_to_index(paddr);
2595
2596 let tracked _ue = s.tracked_extract_unique(uid);
2598 unique::from_unique_embedded(&mut s.regions, paddr);
2599
2600 let ghost fid = fresh_frame_id(s.frames);
2602 lemma_fresh_frame_id_not_in_dom(s.frames);
2603 let tracked fe = tracked_frame_entry_new(paddr);
2604 s.lemma_insert_frame(fid, fe);
2605
2606 assert forall|i: int|
2608 #![trigger s.regions.slot_owners[i]]
2609 0 <= i < max_meta_slots() && s.regions.ref_count(i)
2610 == REF_COUNT_UNUSED implies handle_count(s.frames, i) == 0
2611 && s.regions.slot_owners[i].paths_in_pt.is_empty() && segment_cover_count(
2612 s.segments,
2613 index_to_frame(i),
2614 ) == 0 by {
2615 lemma_handle_count_insert_fresh(old_frames, fid, fe, i);
2616 if i == idx {
2617 assert(false);
2618 }
2619 };
2620 assert forall|i: int|
2622 #![trigger s.regions.slot_owners[i]]
2623 0 <= i < max_meta_slots() && s.regions.slot_owners[i].usage is Frame && s.regions.ref_count(
2624 i,
2625 ) != REF_COUNT_UNUSED && s.regions.ref_count(i) != REF_COUNT_UNIQUE implies handle_count(
2626 s.frames,
2627 i,
2628 ) > 0 || s.regions.slot_owners[i].paths_in_pt.len() > 0 || segment_cover_count(
2629 s.segments,
2630 index_to_frame(i),
2631 ) > 0 by {
2632 lemma_handle_count_insert_fresh(old_frames, fid, fe, i);
2633 };
2634 assert forall|i: int|
2636 #![trigger s.regions.slot_owners[i]]
2637 0 <= i < max_meta_slots() && s.regions.slot_owners[i].usage is Frame && (handle_count(
2638 s.frames,
2639 i,
2640 ) > 0 || s.regions.slot_owners[i].paths_in_pt.len() > 0 || segment_cover_count(
2641 s.segments,
2642 index_to_frame(i),
2643 ) > 0) implies {
2644 let so = s.regions.slot_owners[i];
2645 let rc = so.ref_count();
2646 &&& rc != REF_COUNT_UNUSED
2647 &&& rc != REF_COUNT_UNIQUE
2648 &&& rc == handle_count(s.frames, i) + so.paths_in_pt.len() + segment_cover_count(
2649 s.segments,
2650 index_to_frame(i),
2651 )
2652 } by {
2653 lemma_handle_count_insert_fresh(old_frames, fid, fe, i);
2654 };
2655}
2656
2657proof fn lemma_step_try_from_shared<'rcu>(tracked s: &mut VmStore<'rcu>, fid: FrameId)
2664 requires
2665 old(s).inv(),
2666 old(s).frames.contains_key(fid),
2667 ensures
2668 final(s).inv(),
2669{
2670 reveal(VmStore::structural_inv);
2671 reveal(VmStore::accounting_inv);
2672 let ghost paddr = s.frames[fid].paddr;
2673 let ghost idx = frame_to_index(paddr);
2674 assert(s.frames.dom().filter(
2677 |gid: FrameId| frame_to_index(s.frames[gid].paddr) == idx,
2678 ).contains(fid));
2679
2680 if s.regions.ref_count(idx) == 1 {
2681 let ghost old_frames = s.frames;
2682
2683 let tracked _fe = s.tracked_extract_frame(fid);
2688 unique::try_from_shared_embedded(&mut s.regions, paddr);
2689
2690 let ghost uid = fresh_unique_id(s.unique_frames);
2692 lemma_fresh_unique_id_not_in_dom(s.unique_frames);
2693 let tracked ue = tracked_unique_entry_new(paddr);
2694 s.lemma_insert_unique(uid, ue);
2695 assert forall|i: int|
2697 #![trigger s.regions.slot_owners[i]]
2698 0 <= i < max_meta_slots() && s.regions.ref_count(i)
2699 == REF_COUNT_UNUSED implies handle_count(s.frames, i) == 0
2700 && s.regions.slot_owners[i].paths_in_pt.is_empty() && segment_cover_count(
2701 s.segments,
2702 index_to_frame(i),
2703 ) == 0 by {
2704 lemma_handle_count_remove(old_frames, fid, i);
2705 if i == idx {
2706 assert(false);
2708 }
2709 };
2710 assert forall|i: int|
2712 #![trigger s.regions.slot_owners[i]]
2713 0 <= i < max_meta_slots() && s.regions.slot_owners[i].usage is Frame
2714 && s.regions.ref_count(i) != REF_COUNT_UNUSED && s.regions.ref_count(i)
2715 != REF_COUNT_UNIQUE implies handle_count(s.frames, i) > 0
2716 || s.regions.slot_owners[i].paths_in_pt.len() > 0 || segment_cover_count(
2717 s.segments,
2718 index_to_frame(i),
2719 ) > 0 by {
2720 lemma_handle_count_remove(old_frames, fid, i);
2721 if i == idx {
2722 assert(false);
2724 }
2725 };
2726 assert forall|i: int|
2728 #![trigger s.regions.slot_owners[i]]
2729 0 <= i < max_meta_slots() && s.regions.slot_owners[i].usage is Frame && (handle_count(
2730 s.frames,
2731 i,
2732 ) > 0 || s.regions.slot_owners[i].paths_in_pt.len() > 0 || segment_cover_count(
2733 s.segments,
2734 index_to_frame(i),
2735 ) > 0) implies {
2736 let so = s.regions.slot_owners[i];
2737 let rc = so.ref_count();
2738 &&& rc != REF_COUNT_UNUSED
2739 &&& rc != REF_COUNT_UNIQUE
2740 &&& rc == handle_count(s.frames, i) + so.paths_in_pt.len() + segment_cover_count(
2741 s.segments,
2742 index_to_frame(i),
2743 )
2744 } by {
2745 lemma_handle_count_remove(old_frames, fid, i);
2746 };
2747 }
2748}
2749
2750pub proof fn lemma_segment_cover_insert_inside(
2753 segments: Map<SegmentId, SegmentEntry>,
2754 sid: SegmentId,
2755 entry: SegmentEntry,
2756 paddr: Paddr,
2757)
2758 requires
2759 !segments.contains_key(sid),
2760 entry.range.start <= paddr < entry.range.end,
2761 ensures
2762 segment_cover_count(segments.insert(sid, entry), paddr) == segment_cover_count(
2763 segments,
2764 paddr,
2765 ) + 1,
2766{
2767 let segments2 = segments.insert(sid, entry);
2768 let pred = |s: SegmentId| segments[s].range.start <= paddr && paddr < segments[s].range.end;
2769 let pred2 = |s: SegmentId| segments2[s].range.start <= paddr && paddr < segments2[s].range.end;
2770 let old_filt = segments.dom().filter(pred);
2771 let new_filt = segments2.dom().filter(pred2);
2772 assert(new_filt == old_filt.insert(sid)) by {};
2773}
2774
2775pub proof fn lemma_segment_cover_insert_outside(
2778 segments: Map<SegmentId, SegmentEntry>,
2779 sid: SegmentId,
2780 entry: SegmentEntry,
2781 paddr: Paddr,
2782)
2783 requires
2784 !segments.contains_key(sid),
2785 !(entry.range.start <= paddr < entry.range.end),
2786 ensures
2787 segment_cover_count(segments.insert(sid, entry), paddr) == segment_cover_count(
2788 segments,
2789 paddr,
2790 ),
2791{
2792 let segments2 = segments.insert(sid, entry);
2793 let pred = |s: SegmentId| segments[s].range.start <= paddr && paddr < segments[s].range.end;
2794 let pred2 = |s: SegmentId| segments2[s].range.start <= paddr && paddr < segments2[s].range.end;
2795 let old_filt = segments.dom().filter(pred);
2796 let new_filt = segments2.dom().filter(pred2);
2797 assert(new_filt == old_filt) by {};
2798}
2799
2800pub proof fn lemma_segment_cover_contains(
2803 segments: Map<SegmentId, SegmentEntry>,
2804 sid: SegmentId,
2805 paddr: Paddr,
2806)
2807 requires
2808 segments.contains_key(sid),
2809 segments[sid].range.start <= paddr < segments[sid].range.end,
2810 ensures
2811 segment_cover_count(segments, paddr) >= 1,
2812{
2813 let filt = segments.dom().filter(
2814 |s: SegmentId| segments[s].range.start <= paddr && paddr < segments[s].range.end,
2815 );
2816 assert(filt.contains(sid));
2817}
2818
2819pub proof fn lemma_segment_cover_remove_inside(
2822 segments: Map<SegmentId, SegmentEntry>,
2823 sid: SegmentId,
2824 paddr: Paddr,
2825)
2826 requires
2827 segments.contains_key(sid),
2828 segments[sid].range.start <= paddr < segments[sid].range.end,
2829 ensures
2830 segment_cover_count(segments.remove(sid), paddr) == (segment_cover_count(segments, paddr)
2831 - 1) as nat,
2832{
2833 let segments2 = segments.remove(sid);
2834 let pred = |s: SegmentId| segments[s].range.start <= paddr && paddr < segments[s].range.end;
2835 let pred2 = |s: SegmentId| segments2[s].range.start <= paddr && paddr < segments2[s].range.end;
2836 let old_filt = segments.dom().filter(pred);
2837 let new_filt = segments2.dom().filter(pred2);
2838 assert(new_filt == old_filt.remove(sid)) by {};
2839}
2840
2841pub proof fn lemma_segment_cover_shrink_front(
2852 segments: Map<SegmentId, SegmentEntry>,
2853 sid: SegmentId,
2854 new_entry: SegmentEntry,
2855 paddr_check: Paddr,
2856)
2857 requires
2858 segments.contains_key(sid),
2859 segments[sid].range.start < segments[sid].range.end,
2862 segments[sid].range.start % PAGE_SIZE == 0,
2864 segments[sid].range.start + PAGE_SIZE <= MAX_PADDR,
2866 new_entry.range.start == (segments[sid].range.start + PAGE_SIZE) as Paddr,
2867 new_entry.range.end == segments[sid].range.end,
2868 new_entry.range.start <= new_entry.range.end,
2869 paddr_check % PAGE_SIZE == 0,
2870 ensures
2871new_entry.range.start < new_entry.range.end ==> ({
2875 let new_segments = segments.remove(sid).insert(sid, new_entry);
2876 paddr_check == segments[sid].range.start ==> segment_cover_count(
2877 new_segments,
2878 paddr_check,
2879 ) + 1 == segment_cover_count(segments, paddr_check)
2880 }),
2881 new_entry.range.start < new_entry.range.end ==> ({
2882 let new_segments = segments.remove(sid).insert(sid, new_entry);
2883 paddr_check != segments[sid].range.start ==> segment_cover_count(
2884 new_segments,
2885 paddr_check,
2886 ) == segment_cover_count(segments, paddr_check)
2887 }),
2888 new_entry.range.start >= new_entry.range.end ==> ({
2890 let new_segments = segments.remove(sid);
2891 paddr_check == segments[sid].range.start ==> segment_cover_count(
2892 new_segments,
2893 paddr_check,
2894 ) + 1 == segment_cover_count(segments, paddr_check)
2895 }),
2896 new_entry.range.start >= new_entry.range.end ==> ({
2897 let new_segments = segments.remove(sid);
2898 paddr_check != segments[sid].range.start ==> segment_cover_count(
2899 new_segments,
2900 paddr_check,
2901 ) == segment_cover_count(segments, paddr_check)
2902 }),
2903{
2904 let popped = segments[sid].range.start;
2905 let range = segments[sid].range;
2906 let sid_pre_covers = range.start <= paddr_check < range.end;
2909 let new_covers = new_entry.range.start <= paddr_check < new_entry.range.end;
2910 if sid_pre_covers {
2912 lemma_segment_cover_remove_inside(segments, sid, paddr_check);
2913 } else {
2914 lemma_segment_cover_remove_outside(segments, sid, paddr_check);
2915 }
2916 if new_entry.range.start < new_entry.range.end {
2917 let new_segments = segments.remove(sid).insert(sid, new_entry);
2918 if paddr_check == popped {
2919 lemma_segment_cover_insert_outside(segments.remove(sid), sid, new_entry, paddr_check);
2922 lemma_segment_cover_contains(segments, sid, paddr_check);
2923 } else if sid_pre_covers {
2924 lemma_segment_cover_contains(segments, sid, paddr_check);
2925 lemma_segment_cover_insert_inside(segments.remove(sid), sid, new_entry, paddr_check);
2926 } else {
2927 lemma_segment_cover_insert_outside(segments.remove(sid), sid, new_entry, paddr_check);
2928 }
2929 } else {
2930 let new_segments = segments.remove(sid);
2931 if paddr_check == popped {
2932 lemma_segment_cover_contains(segments, sid, paddr_check);
2933 } else if sid_pre_covers {
2934 assert(false);
2935 }
2936 }
2937}
2938
2939pub proof fn lemma_segment_cover_split(
2945 segments: Map<SegmentId, SegmentEntry>,
2946 sid: SegmentId,
2947 new_left: SegmentId,
2948 new_right: SegmentId,
2949 entry_left: SegmentEntry,
2950 entry_right: SegmentEntry,
2951 paddr: Paddr,
2952)
2953 requires
2954 segments.contains_key(sid),
2955 new_left != sid,
2958 new_right != sid,
2959 new_left != new_right,
2960 !segments.remove(sid).contains_key(new_left),
2961 !segments.remove(sid).contains_key(new_right),
2962 entry_left.range.start == segments[sid].range.start,
2964 entry_left.range.end == entry_right.range.start,
2965 entry_right.range.end == segments[sid].range.end,
2966 entry_left.range.start < entry_left.range.end,
2967 entry_right.range.start < entry_right.range.end,
2968 ensures
2969 segment_cover_count(
2970 segments.remove(sid).insert(new_left, entry_left).insert(new_right, entry_right),
2971 paddr,
2972 ) == segment_cover_count(segments, paddr),
2973{
2974 let mid_segments = segments.remove(sid);
2975 let with_left = mid_segments.insert(new_left, entry_left);
2976 let sid_covers = segments[sid].range.start <= paddr && paddr < segments[sid].range.end;
2977 let left_covers = entry_left.range.start <= paddr && paddr < entry_left.range.end;
2978 let right_covers = entry_right.range.start <= paddr && paddr < entry_right.range.end;
2979 if sid_covers {
2981 lemma_segment_cover_remove_inside(segments, sid, paddr);
2982 } else {
2983 lemma_segment_cover_remove_outside(segments, sid, paddr);
2984 }
2985 if left_covers {
2987 lemma_segment_cover_insert_inside(mid_segments, new_left, entry_left, paddr);
2988 } else {
2989 lemma_segment_cover_insert_outside(mid_segments, new_left, entry_left, paddr);
2990 }
2991 let final_segments = with_left.insert(new_right, entry_right);
2993 if right_covers {
2994 lemma_segment_cover_insert_inside(with_left, new_right, entry_right, paddr);
2995 } else {
2996 lemma_segment_cover_insert_outside(with_left, new_right, entry_right, paddr);
2997 }
2998 let orig = segment_cover_count(segments, paddr);
3000 if sid_covers {
3001 lemma_segment_cover_contains(segments, sid, paddr);
3003 }
3004}
3005
3006pub proof fn lemma_segment_cover_remove_outside(
3009 segments: Map<SegmentId, SegmentEntry>,
3010 sid: SegmentId,
3011 paddr: Paddr,
3012)
3013 requires
3014 segments.contains_key(sid),
3015 !(segments[sid].range.start <= paddr < segments[sid].range.end),
3016 ensures
3017 segment_cover_count(segments.remove(sid), paddr) == segment_cover_count(segments, paddr),
3018{
3019 let segments2 = segments.remove(sid);
3020 let pred = |s: SegmentId| segments[s].range.start <= paddr && paddr < segments[s].range.end;
3021 let pred2 = |s: SegmentId| segments2[s].range.start <= paddr && paddr < segments2[s].range.end;
3022 let old_filt = segments.dom().filter(pred);
3023 let new_filt = segments2.dom().filter(pred2);
3024 assert(new_filt == old_filt) by {};
3025}
3026
3027pub open spec fn fresh_vm_space_id<'a>(m: Map<VmSpaceId, VmSpaceOwner>) -> VmSpaceId {
3033 choose|id: VmSpaceId| !m.contains_key(id)
3034}
3035
3036pub open spec fn fresh_cursor_id<'rcu>(m: Map<CursorId, CursorEntry<'rcu>>) -> CursorId {
3038 choose|id: CursorId| !m.contains_key(id)
3039}
3040
3041pub open spec fn fresh_vm_io_id<'a>(m: Map<VmIoId, VmIoEntry>) -> VmIoId {
3043 choose|id: VmIoId| !m.contains_key(id)
3044}
3045
3046pub open spec fn fresh_frame_id(m: Map<FrameId, FrameEntry>) -> FrameId {
3048 choose|id: FrameId| !m.contains_key(id)
3049}
3050
3051pub proof fn lemma_fresh_vm_space_id_not_in_dom<'a>(m: Map<VmSpaceId, VmSpaceOwner>)
3052 ensures
3053 !m.contains_key(fresh_vm_space_id(m)),
3054{
3055 lemma_finite_int_set_has_unused(m.dom());
3056}
3057
3058pub proof fn lemma_fresh_cursor_id_not_in_dom<'rcu>(m: Map<CursorId, CursorEntry<'rcu>>)
3059 ensures
3060 !m.contains_key(fresh_cursor_id(m)),
3061{
3062 lemma_finite_int_set_has_unused(m.dom());
3063}
3064
3065pub proof fn lemma_fresh_vm_io_id_not_in_dom<'a>(m: Map<VmIoId, VmIoEntry>)
3066 ensures
3067 !m.contains_key(fresh_vm_io_id(m)),
3068{
3069 lemma_finite_int_set_has_unused(m.dom());
3070}
3071
3072pub proof fn lemma_fresh_frame_id_not_in_dom(m: Map<FrameId, FrameEntry>)
3073 ensures
3074 !m.contains_key(fresh_frame_id(m)),
3075{
3076 lemma_finite_int_set_has_unused(m.dom());
3077}
3078
3079pub proof fn tracked_cursor_entry_new<'rcu>(
3081 vm_space: VmSpaceId,
3082 kind: CursorKind,
3083 va: Range<Vaddr>,
3084 tracked owner: CursorOwner<'rcu, UserPtConfig>,
3085 tracked guards: Guards,
3086) -> (tracked res: CursorEntry<'rcu>)
3087 ensures
3088 res.vm_space == vm_space,
3089 res.kind == kind,
3090 res.va == va,
3091 res.owner == owner,
3092 res.guards == guards,
3093{
3094 let tracked res = CursorEntry { vm_space, kind, va, owner, guards };
3095 res
3096}
3097
3098pub proof fn tracked_vm_io_entry_new<'a>(
3100 vm_space: Option<VmSpaceId>,
3101 kind: VmIoKind,
3102 vaddr: Vaddr,
3103 len: usize,
3104 tracked owner: VmIoOwner,
3105) -> tracked VmIoEntry
3106 returns
3107 (VmIoEntry { vm_space, kind, vaddr, len, owner }),
3108{
3109 let tracked res = VmIoEntry { vm_space, kind, vaddr, len, owner };
3110 res
3111}
3112
3113pub proof fn tracked_frame_entry_new(paddr: Paddr) -> tracked FrameEntry
3115 returns
3116 (FrameEntry { paddr }),
3117{
3118 let tracked res = FrameEntry { paddr };
3119 res
3120}
3121
3122pub proof fn tracked_segment_entry_new(range: Range<Paddr>) -> tracked SegmentEntry
3124 returns
3125 (SegmentEntry { range }),
3126{
3127 let tracked res = SegmentEntry { range };
3128 res
3129}
3130
3131pub open spec fn fresh_segment_id(m: Map<SegmentId, SegmentEntry>) -> SegmentId {
3133 choose|id: SegmentId| !m.contains_key(id)
3134}
3135
3136pub proof fn lemma_fresh_segment_id_not_in_dom(m: Map<SegmentId, SegmentEntry>)
3137 ensures
3138 !m.contains_key(fresh_segment_id(m)),
3139{
3140 lemma_finite_int_set_has_unused(m.dom());
3141}
3142
3143pub proof fn tracked_unique_entry_new(paddr: Paddr) -> tracked UniqueEntry
3145 returns
3146 (UniqueEntry { paddr }),
3147{
3148 let tracked res = UniqueEntry { paddr };
3149 res
3150}
3151
3152pub open spec fn fresh_unique_id(m: Map<UniqueId, UniqueEntry>) -> UniqueId {
3154 choose|id: UniqueId| !m.contains_key(id)
3155}
3156
3157pub proof fn lemma_fresh_unique_id_not_in_dom(m: Map<UniqueId, UniqueEntry>)
3158 ensures
3159 !m.contains_key(fresh_unique_id(m)),
3160{
3161 lemma_finite_int_set_has_unused(m.dom());
3162}
3163
3164}