pub struct CursorOwner<'rcu, C: PageTableConfig> {
pub level: PagingLevel,
pub continuations: Map<int, CursorContinuation<'rcu, C>>,
pub va: Vaddr,
pub barrier_va: Range<Vaddr>,
pub guard_level: PagingLevel,
pub prefix: Vaddr,
pub popped_too_high: bool,
}Fields§
§level: PagingLevel§continuations: Map<int, CursorContinuation<'rcu, C>>§va: Vaddr§barrier_va: Range<Vaddr>§guard_level: PagingLevel§prefix: Vaddr§popped_too_high: boolImplementations§
Source§impl<'rcu, C: PageTableConfig> CursorOwner<'rcu, C>
impl<'rcu, C: PageTableConfig> CursorOwner<'rcu, C>
Sourcepub proof fn lemma_replaced_child_preserves_map_full_tree(
self,
old: Self,
f: FnSpec<(EntryOwner<C>, TreePath<NR_ENTRIES>), bool>,
g: FnSpec<(EntryOwner<C>, TreePath<NR_ENTRIES>), bool>,
)
pub proof fn lemma_replaced_child_preserves_map_full_tree( self, old: Self, f: FnSpec<(EntryOwner<C>, TreePath<NR_ENTRIES>), bool>, g: FnSpec<(EntryOwner<C>, TreePath<NR_ENTRIES>), bool>, )
self.inv(),old.inv(),self.level == old.level,old.map_full_tree(f),OwnerSubtree::implies(f, g),forall |i: int| {
self.level <= i < NR_LEVELS
==> #[trigger] self.continuations[i] == old.continuations[i]
},self.continuations[self.level - 1].path() == old.continuations[old.level - 1].path(),self.index() == old.index(),forall |j: int| {
0 <= j < NR_ENTRIES && j != old.index()
==> #[trigger] self.continuations[self.level - 1].children[j]
== old.continuations[old.level - 1].children[j]
},self.cur_subtree().subtree_satisfies(self.cur_entry_owner().path, g),ensuresself.map_full_tree(g),Restore a tree predicate after replacing only the current child subtree.
Sourcepub proof fn lemma_split_current_mappings(self, old: Self)
pub proof fn lemma_split_current_mappings(self, old: Self)
self.inv(),old.inv(),self.in_barrier_va(),old.in_barrier_va(),self.level == old.level,self.va == old.va,old.cur_entry_owner().is_frame(),self.cur_entry_owner().is_node(),self.index() == old.index(),self.continuations[self.level - 1].path() == old.continuations[old.level - 1].path(),forall |i: int| {
self.level <= i < NR_LEVELS
==> #[trigger] self.continuations[i] == old.continuations[i]
},forall |j: int| {
0 <= j < NR_ENTRIES && j != old.index()
==> #[trigger] self.continuations[self.level - 1].children[j]
== old.continuations[old.level - 1].children[j]
},1 < old.level < NR_LEVELS,PageTableOwner(self.cur_subtree()).view_rec(self.cur_entry_owner().path)
== Set::<int>::range(0, NR_ENTRIES as int)
.map(|i: int| CursorView::<
C,
>::split_index(
PageTableOwner(old.cur_subtree())
.view_rec(old.cur_entry_owner().path)
.choose(),
page_size::<PagingConsts>((old.level - 1) as PagingLevel),
i as usize,
)),ensuresself@
== old@
.split_if_mapped_huge_spec(
page_size::<PagingConsts>((old.level - 1) as PagingLevel),
),old@.inv(),old@.present(),old@.query_mapping().page_size == page_size::<PagingConsts>(old.level),Replace the huge current mapping with the proven view of its split subtree.
Sourcepub proof fn lemma_replace_preserves_cursor_inv_metaregion(
self,
other: Self,
regions: MetaRegionOwners,
)
pub proof fn lemma_replace_preserves_cursor_inv_metaregion( self, other: Self, regions: MetaRegionOwners, )
self.inv(),self.in_barrier_va(),!self.popped_too_high,self.metaregion_sound(regions),self.level == other.level,self.guard_level == other.guard_level,self.va == other.va,self.barrier_va == other.barrier_va,self.prefix == other.prefix,self.popped_too_high == other.popped_too_high,forall |i: int| {
self.level <= i < NR_LEVELS
==> #[trigger] self.continuations[i] == other.continuations[i]
},other.continuations[self.level - 1].inv(),other.continuations[self.level - 1].all_some(),other.continuations[self.level - 1].idx == self.continuations[self.level - 1].idx,other.continuations[self.level - 1].entry_own.parent_level
== self.continuations[self.level - 1].entry_own.parent_level,other.continuations[self.level - 1].guard.inner.inner@.slot_vaddr()
== self.continuations[self.level - 1].guard.inner.inner@.slot_vaddr(),other.continuations[self.level - 1].path() == self.continuations[self.level - 1].path(),other.continuations.dom() =~= self.continuations.dom(),forall |j: int| {
0 <= j < NR_ENTRIES && j != self.continuations[self.level - 1].idx
==> #[trigger] other.continuations[self.level - 1].children[j]
== self.continuations[self.level - 1].children[j]
},({
let new_child = other
.continuations[self.level - 1]
.children[other.continuations[self.level - 1].idx as int]->0;
let new_path = other
.continuations[self.level - 1]
.path()
.push_tail(other.continuations[self.level - 1].idx as int);
new_child
.subtree_satisfies(new_path, PageTableOwner::<C>::metaregion_sound_pred(regions))
}),other.continuations[self.level - 1].entry_own.metaregion_sound(regions),ensuresother.inv(),other.metaregion_sound(regions),Restoring a changed child and parent PTE preserves cursor and metadata invariants.
Sourcepub proof fn lemma_map_branch_none_inv_holds(self, owner0: Self)
pub proof fn lemma_map_branch_none_inv_holds(self, owner0: Self)
owner0.inv(),self.in_barrier_va(),!self.popped_too_high,forall |j: int| {
0 <= j < NR_ENTRIES && j != owner0.continuations[owner0.level - 1].idx
==> (#[trigger] self.continuations[self.level - 1].children[j])
== owner0.continuations[owner0.level - 1].children[j]
},self.level == owner0.level,self.va == owner0.va,self.barrier_va == owner0.barrier_va,self.guard_level == owner0.guard_level,self.prefix == owner0.prefix,self.popped_too_high == owner0.popped_too_high,forall |i: int| {
self.level <= i < NR_LEVELS
==> #[trigger] self.continuations[i] == owner0.continuations[i]
},self.continuations[self.level - 1].inv(),self.continuations[self.level - 1].all_some(),self.continuations[self.level - 1].idx == owner0.continuations[owner0.level - 1].idx,self.continuations[self.level - 1].entry_own.parent_level
== owner0.continuations[owner0.level - 1].entry_own.parent_level,self.continuations[self.level - 1].guard.inner.inner@.slot_vaddr()
== owner0.continuations[owner0.level - 1].guard.inner.inner@.slot_vaddr(),self.continuations[self.level - 1].path()
== owner0.continuations[owner0.level - 1].path(),pte_index_spec::<C>(self.cur_va(), self.level as PagingLevel)
== self.continuations[self.level - 1].idx,self.continuations.dom() =~= owner0.continuations.dom(),ensuresself.inv(),Sourcepub proof fn map_branch_none_no_new_mappings(self, owner0: Self)
pub proof fn map_branch_none_no_new_mappings(self, owner0: Self)
owner0.inv(),owner0.in_barrier_va(),self.inv(),self.in_barrier_va(),self.level == owner0.level,self.va == owner0.va,forall |i: int| {
self.level <= i < NR_LEVELS
==> #[trigger] self.continuations[i] == owner0.continuations[i]
},owner0
.continuations[owner0.level - 1]
.children[owner0.continuations[owner0.level - 1].idx as int] is Some,owner0
.continuations[owner0.level - 1]
.children[owner0.continuations[owner0.level - 1].idx as int]->0
.value()
.is_absent(),self
.continuations[self.level - 1]
.children[self.continuations[self.level - 1].idx as int] is Some,self
.continuations[self.level - 1]
.children[self.continuations[self.level - 1].idx as int]->0
.value()
.is_node(),self.continuations[self.level - 1].path()
== owner0.continuations[owner0.level - 1].path(),forall |j: int| {
0 <= j < NR_ENTRIES && j != owner0.continuations[owner0.level - 1].idx as int
==> #[trigger] self.continuations[self.level - 1].children[j]
== owner0.continuations[owner0.level - 1].children[j]
},PageTableOwner(
self
.continuations[self.level - 1]
.children[self.continuations[self.level - 1].idx as int]->0,
)
.view_rec(
self
.continuations[self.level - 1]
.path()
.push_tail(self.continuations[self.level - 1].idx as int),
) =~= Set::<Mapping>::empty(),ensuresself.view_mappings() == owner0.view_mappings(),After alloc_if_none (absent->node), view_mappings is unchanged (both contribute zero mappings).
Source§impl<'rcu, C: PageTableConfig> CursorOwner<'rcu, C>
impl<'rcu, C: PageTableConfig> CursorOwner<'rcu, C>
Sourcepub proof fn lemma_barrier_va_bounds(self, va: Vaddr)
pub proof fn lemma_barrier_va_bounds(self, va: Vaddr)
self.inv(),self.barrier_va.start <= va < self.barrier_va.end,va % C::BASE_PAGE_SIZE() == 0,ensuresself.locked_range().start <= va < self.locked_range().end,vaddr_leading_part_spec::<C>(va) == page_table_coverage_start_spec::<C>(),C::TOP_LEVEL_INDEX_RANGE().start <= pte_index_spec::<C>(va, C::NR_LEVELS())
< C::TOP_LEVEL_INDEX_RANGE().end,Request addresses lie inside the locked node and satisfy configuration bounds.
Source§impl<'rcu, C: PageTableConfig> CursorOwner<'rcu, C>
impl<'rcu, C: PageTableConfig> CursorOwner<'rcu, C>
Sourcepub open spec fn max_steps_subtree(level: usize) -> nat
pub open spec fn max_steps_subtree(level: usize) -> nat
{
if level <= 1 {
NR_ENTRIES as nat
} else {
(NR_ENTRIES as nat) * (Self::max_steps_subtree((level - 1) as usize) + 1)
}
}The number of steps it will take to walk through every node of a full
page table at level level
Sourcepub open spec fn max_steps_partial(self, level: usize) -> nat
pub open spec fn max_steps_partial(self, level: usize) -> nat
{
if level > NR_LEVELS {
0
} else {
let cont = self.continuations[level - 1];
let count: nat = (NR_ENTRIES - cont.idx - 1) as nat;
let steps = Self::max_steps_subtree(level) * count;
let remaining_steps = self.max_steps_partial((level + 1) as usize);
steps + remaining_steps
}
}Per-level “above-current” contribution: count NR_ENTRIES - cont.idx - 1
at every level (the entry at cont.idx is being descended into; its
work is captured at lower levels in the recursion). max_steps()
adds back one subtree(self.level) to count the current level’s
in-progress entry.
The base case is level > NR_LEVELS (not == NR_LEVELS) so that
level == NR_LEVELS itself contributes a non-zero term. This avoids
degenerate behavior at the root: without it, max_steps collapses
to 0 at the root and push_level from the root cannot decrease
(and the popped_too_high q at NR_LEVELS would dominate self).
Sourcepub open spec fn max_steps(self) -> nat
pub open spec fn max_steps(self) -> nat
{
if self.at_or_past_barrier_end() {
0
} else {
(self.max_steps_partial(self.level as usize)
+ Self::max_steps_subtree(self.level as usize)) as nat
}
}Sourcepub proof fn lemma_max_steps_partial_eq(self, other: Self, start: usize)
pub proof fn lemma_max_steps_partial_eq(self, other: Self, start: usize)
1 <= start <= NR_LEVELS + 1,forall |k: int| {
start - 1 <= k < NR_LEVELS
==> #[trigger] self.continuations[k].idx == other.continuations[k].idx
},ensuresself.max_steps_partial(start) == other.max_steps_partial(start),Two owners with the same idx values from start upward have the same max_steps_partial.
Sourcepub open spec fn push_level_owner(self, guard: PageTableGuard<'rcu, C>) -> Self
pub open spec fn push_level_owner(self, guard: PageTableGuard<'rcu, C>) -> Self
{
let cont = self.continuations[self.level - 1];
let (child, cont) = cont
.make_cont(
pte_index_spec::<C>(self.cur_va(), (self.level - 1) as PagingLevel),
guard,
);
let new_continuations = self
.continuations
.insert(self.level - 1, cont)
.insert(self.level - 2, child);
let new_level = (self.level - 1) as u8;
Self {
continuations: new_continuations,
level: new_level,
popped_too_high: false,
..self
}
}Sourcepub proof fn push_level_owner_decreases_steps(self, guard: PageTableGuard<'rcu, C>)
pub proof fn push_level_owner_decreases_steps(self, guard: PageTableGuard<'rcu, C>)
self.inv(),self.in_barrier_va(),self.level > 1,ensuresself.push_level_owner(guard).max_steps() < self.max_steps(),Sourcepub proof fn push_level_owner_preserves_mappings(self, guard: PageTableGuard<'rcu, C>)
pub proof fn push_level_owner_preserves_mappings(self, guard: PageTableGuard<'rcu, C>)
self.inv(),self.level > 1,self.cur_entry_owner().is_node(),ensuresself.push_level_owner(guard)@.mappings == self@.mappings,Sourcepub proof fn push_level_owner_preserves_inv(self, guard: PageTableGuard<'rcu, C>)
pub proof fn push_level_owner_preserves_inv(self, guard: PageTableGuard<'rcu, C>)
self.inv(),self.level > 1,!self.popped_too_high,self.level <= self.guard_level,self.in_barrier_va(),self.cur_entry_owner().is_node(),self.cur_entry_owner().node().relate_guard(guard),forall |i: int| {
self.level - 1 <= i < NR_LEVELS
==> self.continuations[i].guard.inner.inner@.slot_vaddr()
!= guard.inner.inner@.slot_vaddr()
},ensuresself.push_level_owner(guard).inv(),Sourcepub proof fn push_level_owner_preserves_invs(
self,
guard: PageTableGuard<'rcu, C>,
regions: MetaRegionOwners,
guards: Guards,
)
pub proof fn push_level_owner_preserves_invs( self, guard: PageTableGuard<'rcu, C>, regions: MetaRegionOwners, guards: Guards, )
self.inv(),self.level > 1,!self.popped_too_high,self.level <= self.guard_level,self.in_barrier_va(),self.only_current_locked(guards),self.nodes_locked(guards),self.metaregion_sound(regions),self.cur_entry_owner().is_node(),self.cur_entry_owner().node().relate_guard(guard),guards.lock_held(guard.inner.inner@.slot_vaddr()),ensuresself.push_level_owner(guard).inv(),self.push_level_owner(guard).children_not_locked(guards),self.push_level_owner(guard).nodes_locked(guards),self.push_level_owner(guard).metaregion_sound(regions),Sourcepub proof fn tracked_push_level_owner(tracked &mut self, guard: PageTableGuard<'rcu, C>)
pub proof fn tracked_push_level_owner(tracked &mut self, guard: PageTableGuard<'rcu, C>)
old(self).inv(),old(self).level > 1,ensures*final(self) == old(self).push_level_owner(guard),Sourcepub open spec fn pop_level_owner(self) -> (Self, PageTableGuard<'rcu, C>)
pub open spec fn pop_level_owner(self) -> (Self, PageTableGuard<'rcu, C>)
{
let child = self.continuations[self.level - 1];
let cont = self.continuations[self.level as int];
let (new_cont, guard) = cont.restore(child);
let new_continuations = self
.continuations
.insert(self.level as int, new_cont)
.remove(self.level - 1);
let new_level = (self.level + 1) as u8;
let popped_too_high = if new_level > self.guard_level { true } else { false };
(
Self {
continuations: new_continuations,
level: new_level,
popped_too_high: popped_too_high,
..self
},
guard,
)
}Sourcepub proof fn pop_level_owner_preserves_invs(
self,
guards: Guards,
regions: MetaRegionOwners,
)
pub proof fn pop_level_owner_preserves_invs( self, guards: Guards, regions: MetaRegionOwners, )
self.inv(),self.level < NR_LEVELS,self.children_not_locked(guards),self.nodes_locked(guards),self.metaregion_sound(regions),ensuresself.pop_level_owner().0.inv(),self.pop_level_owner().0.only_current_locked(guards),self.pop_level_owner().0.nodes_locked(guards),self.pop_level_owner().0.metaregion_sound(regions),Sourcepub open spec fn move_forward_owner_spec(self) -> Self
pub open spec fn move_forward_owner_spec(self) -> Self
{
if self.index() + 1 == NR_ENTRIES && self.level == self.guard_level {
Self {
va: (nat_align_down(self.va as nat, page_size::<C>(self.level) as nat)
+ page_size::<C>(self.level)) as Vaddr,
popped_too_high: false,
..self
}
} else if self.index() + 1 < NR_ENTRIES {
self.inc_index().zero_below_level()
} else if self.level < NR_LEVELS {
self.pop_level_owner().0.move_forward_owner_spec()
} else {
Self {
va: (self.va + page_size::<C>(C::NR_LEVELS())) as Vaddr,
popped_too_high: false,
..self
}
}
}Sourcepub proof fn lemma_move_forward_increases_va(self)
pub proof fn lemma_move_forward_increases_va(self)
self.inv(),self.level <= NR_LEVELS,self.in_barrier_va(),!self.popped_too_high,ensuresself.move_forward_owner_spec().va > self.va,Sourcepub proof fn lemma_move_forward_not_popped_too_high(self)
pub proof fn lemma_move_forward_not_popped_too_high(self)
self.inv(),self.level <= NR_LEVELS,self.in_barrier_va(),ensures!self.move_forward_owner_spec().popped_too_high,Sourcepub proof fn lemma_move_forward_owner_decreases_steps(self)
pub proof fn lemma_move_forward_owner_decreases_steps(self)
self.inv(),self.level <= NR_LEVELS,self.in_barrier_va(),!self.popped_too_high,ensuresself.move_forward_owner_spec().max_steps() + Self::max_steps_subtree(self.level as usize)
<= self.max_steps(),self.move_forward_owner_spec().max_steps() < self.max_steps(),Sourcepub proof fn lemma_move_forward_va_is_align_up(self)
pub proof fn lemma_move_forward_va_is_align_up(self)
self.inv(),self.level <= NR_LEVELS,self.in_barrier_va(),!self.popped_too_high,ensuresself.move_forward_owner_spec().va
== self@.align_up_spec(page_size::<PagingConsts>(self.level)),Advancing the cursor reaches the next slot boundary, including carries to a parent.
Sourcepub proof fn lemma_pop_level_owner_preserves_mappings(self)
pub proof fn lemma_pop_level_owner_preserves_mappings(self)
self.inv(),self.level < NR_LEVELS,self.in_barrier_va(),ensuresself.pop_level_owner().0@.mappings == self@.mappings,After popping a level, the total view_mappings is preserved. The restored parent at index self.level absorbs the child’s mappings, and both are within the view_mappings range [self.level, NR_LEVELS).
Sourcepub proof fn lemma_move_forward_owner_preserves_mappings(self)
pub proof fn lemma_move_forward_owner_preserves_mappings(self)
self.inv(),self.in_barrier_va(),ensuresself.move_forward_owner_spec()@.mappings == self@.mappings,Sourcepub proof fn lemma_move_forward_guard_end(self)
pub proof fn lemma_move_forward_guard_end(self)
self.inv(),self.in_barrier_va(),self.level == self.guard_level,self.index() + 1 == NR_ENTRIES,ensuresself.move_forward_owner_spec().va == self.locked_range().end,self.move_forward_owner_spec().at_or_past_barrier_end(),self.move_forward_owner_spec().inv(),self.move_forward_owner_spec().max_steps() == 0,self.move_forward_owner_spec().va
== self@.align_up_spec(page_size::<PagingConsts>(self.level)),Advancing the guard’s final entry retains its continuation at the node-end sentinel.
Source§impl<'rcu, C: PageTableConfig> CursorOwner<'rcu, C>
impl<'rcu, C: PageTableConfig> CursorOwner<'rcu, C>
Sourcepub open spec fn no_node_at_idx(self, idx: int) -> bool
pub open spec fn no_node_at_idx(self, idx: int) -> bool
{
&&& self
.map_full_tree(|e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>| {
e.is_node() && e.meta_slot_paddr() is Some
==> frame_to_index(e.meta_slot_paddr()->0) != idx
})
&&& forall |i: int| {
self.level - 1 <= i < NR_LEVELS
==> {
let e = self.continuations[i].entry_own;
e.is_node() && e.meta_slot_paddr() is Some
==> frame_to_index(e.meta_slot_paddr()->0) != idx
}
}
}Tree-wide predicate: no page-table node in either the cursor’s tree
children or its continuation path has metadata slot index idx.
Used as the “sanity” precondition of the path-insert preservation
lemma: the only kind of entry it can touch with the new path is a
frame. Callers satisfy it by observing that page-table node metadata
lives in FRAME_METADATA_RANGE, which is disjoint from any data-frame
paddr (where paths_in_pt inserts happen).
Sourcepub proof fn and_map_full_tree(
self,
f: FnSpec<(EntryOwner<C>, TreePath<NR_ENTRIES>), bool>,
guard: FnSpec<(EntryOwner<C>, TreePath<NR_ENTRIES>), bool>,
)
pub proof fn and_map_full_tree( self, f: FnSpec<(EntryOwner<C>, TreePath<NR_ENTRIES>), bool>, guard: FnSpec<(EntryOwner<C>, TreePath<NR_ENTRIES>), bool>, )
self.inv(),self.map_full_tree(f),self.map_full_tree(guard),ensuresself.map_full_tree(|e: EntryOwner<C>, p: TreePath<NR_ENTRIES>| f(e, p) && guard(e, p)),Pointwise conjunction of two tree-wide predicates: if self.map_full_tree(f)
and self.map_full_tree(guard) hold, then self.map_full_tree(f && guard)
holds. A thin wrapper around OwnerSubtree::lemma_subtree_satisfies_implies_and applied per
continuation + per child; extracted so callers don’t have to re-derive
the same nested assert forall ... by { lemma_subtree_satisfies_implies_and(...) } block.
Sourcepub proof fn no_node_at_idx_from_slot_key(
self,
regions: MetaRegionOwners,
changed_idx: int,
)
pub proof fn no_node_at_idx_from_slot_key( self, regions: MetaRegionOwners, changed_idx: int, )
self.inv(),regions.inv(),self.metaregion_sound(regions),regions.contains(changed_idx),regions.slot_owners[changed_idx].usage !is PageTable,ensuresself.no_node_at_idx(changed_idx),Discharge no_node_at_idx(changed_idx) from the observation that
changed_idx is not a page-table slot. Active node entries always
occupy metadata slots whose usage is PageTable, so any node in the
cursor tree must have a different slot index than changed_idx.
Callers doing a paths_in_pt.insert at a frame’s data-slot
(e.g., map and the huge-page split) use this helper to
satisfy the precondition of
Self::metaregion_preserved_under_path_insert.
Sourcepub proof fn metaregion_preserved_under_path_insert(
self,
regions0: MetaRegionOwners,
regions1: MetaRegionOwners,
changed_idx: int,
new_path: TreePath<NR_ENTRIES>,
)
pub proof fn metaregion_preserved_under_path_insert( self, regions0: MetaRegionOwners, regions1: MetaRegionOwners, changed_idx: int, new_path: TreePath<NR_ENTRIES>, )
self.inv(),self.metaregion_sound(regions0),regions0.inv(),regions0.contains(changed_idx),regions1.slots == regions0.slots,regions1.slot_owners.dom() =~= regions0.slot_owners.dom(),forall |i: int| i != changed_idx ==> regions0.slot_owners[i] == regions1.slot_owners[i],regions1.slot_owners[changed_idx].same_permissions(regions0.slot_owners[changed_idx]),regions1.slot_owners[changed_idx].slot_vaddr
== regions0.slot_owners[changed_idx].slot_vaddr,regions1.slot_owners[changed_idx].usage == regions0.slot_owners[changed_idx].usage,regions1.slot_owners[changed_idx].paths_in_pt
== regions0.slot_owners[changed_idx].paths_in_pt.insert(new_path),self.no_node_at_idx(changed_idx),ensuresself.metaregion_sound(regions1),Preservation of the cursor-level metaregion invariants when the only
change to regions is the insertion of a new path into the
paths_in_pt set at a single slot.
Sourcepub open spec fn path_removable_at_idx(
self,
idx: int,
removed_path: TreePath<NR_ENTRIES>,
) -> bool
pub open spec fn path_removable_at_idx( self, idx: int, removed_path: TreePath<NR_ENTRIES>, ) -> bool
{
&&& self
.map_full_tree(|e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>| (
e.meta_slot_paddr() is Some && frame_to_index(e.meta_slot_paddr()->0) == idx
==> !e.is_node() && (e.is_frame() ==> e.path != removed_path)
))
&&& forall |i: int| {
self.level - 1 <= i < NR_LEVELS
==> {
let e = self.continuations[i].entry_own;
e.meta_slot_paddr() is Some
&& frame_to_index(e.meta_slot_paddr()->0) == idx
==> !e.is_node() && (e.is_frame() ==> e.path != removed_path)
}
}
}Tree-wide guard for the removal of removed_path from
paths_in_pt[changed_idx]. Mirror of Self::no_node_at_idx but
also rules out any frame entry that still maps changed_idx
through exactly removed_path — removing that path would break
such an entry’s metaregion_sound (paths_in_pt.contains(path)).
A node at changed_idx is also excluded (its paths_in_pt is a
singleton, so removal would falsify == set![path]). Callers
(the unmap site) satisfy this because the entry whose path is
being removed has just left the cursor tree.
Sourcepub open spec fn no_frame_with_path(self, removed_path: TreePath<NR_ENTRIES>) -> bool
pub open spec fn no_frame_with_path(self, removed_path: TreePath<NR_ENTRIES>) -> bool
{
&&& self
.map_full_tree(|e: EntryOwner<C>, _p: TreePath<NR_ENTRIES>| {
e.is_frame() ==> e.path != removed_path
})
&&& forall |i: int| {
self.level - 1 <= i < NR_LEVELS
==> {
let e = self.continuations[i].entry_own;
e.is_frame() ==> e.path != removed_path
}
}
}Tree-wide predicate: no frame entry in the cursor tree has
tree path exactly removed_path. After unmap’s
replace_cur_entry(Child::None), the entry at the cursor path
(== removed_path) is absent, so by tree-path correctness no
frame entry can carry that path. This is the one structural
residual of the paths_in_pt-removal refactor.
Sourcepub proof fn no_frame_with_path_from_no_view_mapping(
self,
removed_path: TreePath<NR_ENTRIES>,
)
pub proof fn no_frame_with_path_from_no_view_mapping( self, removed_path: TreePath<NR_ENTRIES>, )
self.inv(),forall |m: Mapping| (
self@.mappings.contains(m) ==> m.va_range.start != vaddr_of::<C>(removed_path)
),ensuresself.no_frame_with_path(removed_path),Establishes Self::no_frame_with_path from the observation that
no current view mapping starts at vaddr_of(removed_path).
This is the standalone “lift path_correct_pred + uniqueness over
the cursor tree” lemma the unmap site needs. Each continuation
child subtree is pt_inv + path-correct (via
PageTableOwner::lemma_pt_inv_implies_path_correct), and its mappings
all bubble up into self@.mappings; so by
PageTableOwner::lemma_no_frame_with_path_rec a frame entry carrying
removed_path would force a mapping starting at
vaddr_of(removed_path) into self@.mappings — exactly what the
hypothesis forbids. Continuation entries are nodes, so the
continuations conjunct is vacuous.
Sourcepub proof fn path_removable_from_no_node_and_no_frame_path(
self,
idx: int,
removed_path: TreePath<NR_ENTRIES>,
)
pub proof fn path_removable_from_no_node_and_no_frame_path( self, idx: int, removed_path: TreePath<NR_ENTRIES>, )
self.inv(),self.no_node_at_idx(idx),self.no_frame_with_path(removed_path),ensuresself.path_removable_at_idx(idx, removed_path),Bridge: path_removable_at_idx follows from the (mechanically
dischargeable) no-node-at-idx fact plus the structural
no-frame-with-removed_path fact. Per entry mapping idx:
no_node_at_idx forces !is_node, and no_frame_with_path
forces is_frame ==> path != removed_path — exactly the
path_removable_at_idx conjunction.
Sourcepub proof fn metaregion_preserved_under_path_remove(
self,
regions0: MetaRegionOwners,
regions1: MetaRegionOwners,
changed_idx: int,
removed_path: TreePath<NR_ENTRIES>,
)
pub proof fn metaregion_preserved_under_path_remove( self, regions0: MetaRegionOwners, regions1: MetaRegionOwners, changed_idx: int, removed_path: TreePath<NR_ENTRIES>, )
self.inv(),self.metaregion_sound(regions0),regions0.inv(),regions0.contains(changed_idx),regions1.slots == regions0.slots,regions1.slot_owners.dom() =~= regions0.slot_owners.dom(),forall |i: int| i != changed_idx ==> regions0.slot_owners[i] == regions1.slot_owners[i],regions1.slot_owners[changed_idx].same_permissions(regions0.slot_owners[changed_idx]),regions1.slot_owners[changed_idx].slot_vaddr
== regions0.slot_owners[changed_idx].slot_vaddr,regions1.slot_owners[changed_idx].usage == regions0.slot_owners[changed_idx].usage,regions1.slot_owners[changed_idx].paths_in_pt
== regions0.slot_owners[changed_idx].paths_in_pt.remove(removed_path),self.path_removable_at_idx(changed_idx, removed_path),ensuresself.metaregion_sound(regions1),Preservation of the cursor-level metaregion invariants when the
only change to regions is the removal of removed_path
from the paths_in_pt set at a single slot. Dual of
Self::metaregion_preserved_under_path_insert, used by frame unmapping.
Removal is not monotone (unlike insert): the guard
Self::path_removable_at_idx is what makes it sound — no live
tree entry still needs removed_path at changed_idx.
Sourcepub proof fn take_next_remove_path_preserves_metaregion(
self,
owner_before_replace: Self,
regions0: MetaRegionOwners,
regions1: MetaRegionOwners,
removed_idx: int,
removed_path: TreePath<NR_ENTRIES>,
target: Mapping,
)
pub proof fn take_next_remove_path_preserves_metaregion( self, owner_before_replace: Self, regions0: MetaRegionOwners, regions1: MetaRegionOwners, removed_idx: int, removed_path: TreePath<NR_ENTRIES>, target: Mapping, )
self.inv(),owner_before_replace.inv(),owner_before_replace.in_barrier_va(),self.metaregion_sound(regions0),regions0.inv(),regions0.contains(removed_idx),regions0.slot_owners[removed_idx].usage !is PageTable,self@.mappings
== owner_before_replace@.mappings
- PageTableOwner(owner_before_replace.cur_subtree())@.mappings,PageTableOwner(owner_before_replace.cur_subtree())@.mappings == set![target],owner_before_replace.cur_subtree().value().path == removed_path,regions1.slots == regions0.slots,regions1.slot_owners.dom() =~= regions0.slot_owners.dom(),forall |i: int| i != removed_idx ==> regions0.slot_owners[i] == regions1.slot_owners[i],regions1.slot_owners[removed_idx].same_permissions(regions0.slot_owners[removed_idx]),regions1.slot_owners[removed_idx].slot_vaddr
== regions0.slot_owners[removed_idx].slot_vaddr,regions1.slot_owners[removed_idx].usage == regions0.slot_owners[removed_idx].usage,regions1.slot_owners[removed_idx].paths_in_pt
== regions0.slot_owners[removed_idx].paths_in_pt.remove(removed_path),ensuresself.metaregion_sound(regions1),Packages the take_next frame-unmap preservation proof after
paths_in_pt removes the unmapped frame path from one metadata slot.
Source§impl<'rcu, C: PageTableConfig> CursorOwner<'rcu, C>
impl<'rcu, C: PageTableConfig> CursorOwner<'rcu, C>
Sourcepub proof fn lemma_cur_subtree_eq_filtered_mappings_path(self)
pub proof fn lemma_cur_subtree_eq_filtered_mappings_path(self)
self.inv(),self.in_barrier_va(),ensures({
let subtree_va = vaddr_of::<C>(self.cur_subtree().value().path) as int;
let size = page_size::<PagingConsts>(self.level) as int;
PageTableOwner(self.cur_subtree())@.mappings
== self@
.mappings
.filter(|m: Mapping| subtree_va <= m.va_range.start < subtree_va + size)
}),The current subtree’s mappings equal the filter over [subtree_va, subtree_va + page_size(level)) where subtree_va = vaddr(cur_subtree path).
Sourcepub proof fn lemma_cur_subtree_eq_filtered_mappings(self)
pub proof fn lemma_cur_subtree_eq_filtered_mappings(self)
self.inv(),self.in_barrier_va(),ensures({
let start = nat_align_down(
self@.cur_va as nat,
page_size::<PagingConsts>(self.level) as nat,
) as Vaddr;
let size = page_size::<PagingConsts>(self.level);
PageTableOwner(self.cur_subtree())@.mappings
== self@.mappings.filter(|m: Mapping| start <= m.va_range.start < start + size)
}),Version using nat_align_down(cur_va, page_size(level)) in the filter. Uses the shared canonical subtree boundary rather than reconstructing the VA path.
Sourcepub proof fn subtree_va_ranges_disjoint(self, j: int)
pub proof fn subtree_va_ranges_disjoint(self, j: int)
self.inv(),self.in_barrier_va(),0 <= j < NR_ENTRIES,j != self.index(),self.continuations[self.level - 1].children[j] is Some,ensuresvaddr(self.continuations[self.level - 1].path().push_tail(j))
+ vaddr_leading_part_spec::<C>(self.cur_va())
+ page_size::<PagingConsts>(self.level as PagingLevel) <= self.cur_va()
|| self.cur_va()
< vaddr(self.continuations[self.level - 1].path().push_tail(j))
+ vaddr_leading_part_spec::<C>(self.cur_va()),Subtrees at different indices have disjoint VA ranges.
Sourcepub proof fn higher_level_children_disjoint(self, i: int, j: int)
pub proof fn higher_level_children_disjoint(self, i: int, j: int)
self.inv(),self.in_barrier_va(),self.level - 1 < i < NR_LEVELS,0 <= j < NR_ENTRIES,j != self.continuations[i].idx,self.continuations[i].children[j] is Some,ensuresvaddr(self.continuations[i].path().push_tail(j))
+ vaddr_leading_part_spec::<C>(self.cur_va())
+ page_size::<PagingConsts>((i + 1) as PagingLevel) <= self.cur_va()
|| self.cur_va()
< vaddr(self.continuations[i].path().push_tail(j))
+ vaddr_leading_part_spec::<C>(self.cur_va()),Children of higher-level continuations have VA ranges that don’t include cur_va, because cur_va’s indices at those levels match the path to the current position.
Sourcepub proof fn mapping_covering_cur_va_from_cur_subtree(self, m: Mapping)
pub proof fn mapping_covering_cur_va_from_cur_subtree(self, m: Mapping)
self.inv(),self.in_barrier_va(),self.view_mappings().contains(m),m.va_range.start <= self.cur_va() < m.va_range.end,ensuresPageTableOwner(self.cur_subtree()).view_rec(self.cur_subtree().value().path).contains(m),Any mapping that covers cur_va must come from the current subtree. This follows from the disjointness of VA ranges and the fact that cur_va falls within the current subtree’s VA range.
Sourcepub proof fn view_mappings_replace_lowest(
old_self: Self,
new_self: Self,
old_cont: CursorContinuation<'rcu, C>,
new_cont: CursorContinuation<'rcu, C>,
)
pub proof fn view_mappings_replace_lowest( old_self: Self, new_self: Self, old_cont: CursorContinuation<'rcu, C>, new_cont: CursorContinuation<'rcu, C>, )
old_self.inv(),old_self.in_barrier_va(),new_self.inv(),old_self.level == new_self.level,old_self.continuations[old_self.level - 1] == old_cont,new_self.continuations[new_self.level - 1] == new_cont,forall |i: int| {
old_self.level <= i < NR_LEVELS
==> old_self.continuations[i] == new_self.continuations[i]
},ensuresnew_self.view_mappings()
== (old_self.view_mappings() - old_cont.view_mappings())
.union(new_cont.view_mappings()),Combined replace: swap the lowest continuation, relating old and new mapping sets. Avoids the Map::remove phantom key issue by requiring both old and new to have inv().
Sourcepub proof fn as_page_table_owner_preserves_view_mappings(self)
pub proof fn as_page_table_owner_preserves_view_mappings(self)
self.inv(),ensuresself.as_page_table_owner().view_rec(self.continuations[3].path()) == self.view_mappings(),self.as_page_table_owner().0.inv(),self.as_page_table_owner().0.level() == self.continuations[3].tree_level,self.as_page_table_owner().pt_inv(),Sourcepub proof fn lemma_view_mapping_inv(self)
pub proof fn lemma_view_mapping_inv(self)
self.inv(),ensuresforall |m: Mapping| self.view_mappings().contains(m) ==> #[trigger] m.inv(),Every mapping in the cursor view satisfies Mapping::inv().
Sourcepub proof fn as_page_table_owner_view_non_overlapping(self)
pub proof fn as_page_table_owner_view_non_overlapping(self)
self.inv(),ensuresself@.non_overlapping(),Non-overlapping mappings in the cursor view.
Collapses the union-over-continuations view_mappings into a single
view_rec rooted at the reconstructed root page table, then applies
lemma_view_rec_disjoint_vaddrs on that single subtree.
Follows from tree structure alone.
Source§impl<'rcu, C: PageTableConfig> CursorOwner<'rcu, C>
impl<'rcu, C: PageTableConfig> CursorOwner<'rcu, C>
Sourcepub open spec fn node_unlocked(
guards: Guards,
) -> FnSpec<(EntryOwner<C>, TreePath<NR_ENTRIES>), bool>
pub open spec fn node_unlocked( guards: Guards, ) -> FnSpec<(EntryOwner<C>, TreePath<NR_ENTRIES>), bool>
{
|owner: EntryOwner<C>, path: TreePath<NR_ENTRIES>| {
owner.is_node() ==> guards.unlocked(owner.node().slot_vaddr())
}
}Sourcepub open spec fn node_unlocked_except(
guards: Guards,
addr: usize,
) -> FnSpec<(EntryOwner<C>, TreePath<NR_ENTRIES>), bool>
pub open spec fn node_unlocked_except( guards: Guards, addr: usize, ) -> FnSpec<(EntryOwner<C>, TreePath<NR_ENTRIES>), bool>
{
|owner: EntryOwner<C>, path: TreePath<NR_ENTRIES>| {
owner.is_node()
==> (owner.node().slot_vaddr() != addr
==> guards.unlocked(owner.node().slot_vaddr()))
}
}Sourcepub open spec fn map_full_tree(
self,
f: FnSpec<(EntryOwner<C>, TreePath<NR_ENTRIES>), bool>,
) -> bool
pub open spec fn map_full_tree( self, f: FnSpec<(EntryOwner<C>, TreePath<NR_ENTRIES>), bool>, ) -> bool
{
forall |i: int| {
self.level - 1 <= i < NR_LEVELS ==> { self.continuations[i].map_children(f) }
}
}Sourcepub open spec fn children_not_locked(self, guards: Guards) -> bool
pub open spec fn children_not_locked(self, guards: Guards) -> bool
{ self.map_full_tree(Self::node_unlocked(guards)) }Sourcepub proof fn lemma_children_not_locked_unroll(self, guards: Guards)
pub proof fn lemma_children_not_locked_unroll(self, guards: Guards)
self.children_not_locked(guards),ensuresforall |i: int| {
self.level - 1 <= i < NR_LEVELS
==> self.continuations[i].map_children(Self::node_unlocked(guards))
},Sourcepub open spec fn only_current_locked(self, guards: Guards) -> bool
pub open spec fn only_current_locked(self, guards: Guards) -> bool
{
self.map_full_tree(
Self::node_unlocked_except(guards, self.cur_entry_owner().node().slot_vaddr()),
)
}Sourcepub proof fn lemma_never_drop_restores_children_not_locked(
self,
guard: PageTableGuard<'rcu, C>,
guards0: Guards,
guards1: Guards,
)
pub proof fn lemma_never_drop_restores_children_not_locked( self, guard: PageTableGuard<'rcu, C>, guards0: Guards, guards1: Guards, )
self.inv(),self.only_current_locked(guards0),guards0.lock_held(guard.inner.inner@.slot_vaddr()),guards1.guards == guards0.guards.remove(guard.inner.inner@.slot_vaddr()),self.cur_entry_owner().is_node(),guard.inner.inner@.slot_vaddr() == self.cur_entry_owner().node().slot_vaddr(),ensuresself.children_not_locked(guards1),Sourcepub open spec fn nodes_locked(self, guards: Guards) -> bool
pub open spec fn nodes_locked(self, guards: Guards) -> bool
{
forall |i: int| {
self.level - 1 <= i < self.guard_level
==> { self.continuations[i].node_locked(guards) }
}
}Sourcepub open spec fn inc_index(self) -> Self
pub open spec fn inc_index(self) -> Self
{
Self {
continuations: self
.continuations
.insert(self.level - 1, self.continuations[self.level - 1].inc_index()),
va: vaddr_replace_pte_index_spec::<
C,
>(
self.va,
self.level,
self.continuations[self.level - 1].inc_index().idx as int,
),
popped_too_high: false,
..self
}
}Sourcepub proof fn do_inc_index_and_zero(tracked &mut self)
pub proof fn do_inc_index_and_zero(tracked &mut self)
old(self).inv(),old(self).level <= old(self).guard_level,old(self).in_barrier_va(),old(self).continuations[old(self).level - 1].idx + 1 < NR_ENTRIES,old(self).level == NR_LEVELS
==> (old(self).continuations[old(self).level - 1].idx + 1)
<= C::TOP_LEVEL_INDEX_RANGE().end,ensuresfinal(self).inv(),*final(self) == old(self).inc_index().zero_below_level(),Advances to the next slot boundary in one ghost update, without exposing an unaligned intermediate VA.
Sourcepub proof fn lemma_inv_continuation(self, i: int)
pub proof fn lemma_inv_continuation(self, i: int)
self.inv(),self.level - 1 <= i <= NR_LEVELS - 1,ensuresself.continuations.contains_key(i),self.continuations[i].inv(),self.continuations[i].children.len() == NR_ENTRIES,Sourcepub open spec fn view_mappings(self) -> Set<Mapping>
pub open spec fn view_mappings(self) -> Set<Mapping>
{
self.continuations
.filter_keys(|k| self.level - 1 <= k < NR_LEVELS)
.map_values(|cont: CursorContinuation<'rcu, C>| cont.view_mappings())
.values()
.flatten()
}Sourcepub broadcast proof fn lemma_view_mappings_contains(self)
pub broadcast proof fn lemma_view_mappings_contains(self)
1 <= self.level <= NR_LEVELS,ensuresforall |m: Mapping| {
#[trigger] self.view_mappings().contains(m)
==> exists |i: int| {
self.level - 1 <= i < NR_LEVELS
&& self.continuations[i].view_mappings().contains(m)
}
},Sourcepub broadcast proof fn lemma_view_mappings_intro(self, m: Mapping, i: int)
pub broadcast proof fn lemma_view_mappings_intro(self, m: Mapping, i: int)
1 <= self.level <= NR_LEVELS,self.level - 1 <= i < NR_LEVELS,self.continuations.contains_key(i),#[trigger] self.continuations[i].view_mappings().contains(m),ensuresself.view_mappings().contains(m),Sourcepub open spec fn as_page_table_owner(self) -> PageTableOwner<C>
pub open spec fn as_page_table_owner(self) -> PageTableOwner<C>
{
if self.level == 1 {
let l1 = self.continuations[0];
let l2 = self.continuations[1].restore(l1).0;
let l3 = self.continuations[2].restore(l2).0;
let l4 = self.continuations[3].restore(l3).0;
l4.as_page_table_owner()
} else if self.level == 2 {
let l2 = self.continuations[1];
let l3 = self.continuations[2].restore(l2).0;
let l4 = self.continuations[3].restore(l3).0;
l4.as_page_table_owner()
} else if self.level == 3 {
let l3 = self.continuations[2];
let l4 = self.continuations[3].restore(l3).0;
l4.as_page_table_owner()
} else {
let l4 = self.continuations[3];
l4.as_page_table_owner()
}
}Sourcepub open spec fn cur_entry_owner(self) -> EntryOwner<C>
pub open spec fn cur_entry_owner(self) -> EntryOwner<C>
{ self.cur_subtree().value() }Sourcepub open spec fn cur_subtree(self) -> OwnerSubtree<C>
pub open spec fn cur_subtree(self) -> OwnerSubtree<C>
{ self.continuations[self.level - 1].children[self.index() as int]->0 }Sourcepub proof fn lemma_cur_frame_clone_requires(
self,
item: C::Item,
pa: Paddr,
level: PagingLevel,
prop: PageProperty,
regions: MetaRegionOwners,
)
pub proof fn lemma_cur_frame_clone_requires( self, item: C::Item, pa: Paddr, level: PagingLevel, prop: PageProperty, regions: MetaRegionOwners, )
self.inv(),regions.inv(),self.metaregion_sound(regions),self.cur_entry_owner().is_frame(),pa == self.cur_entry_owner().frame().mapped_pa,C::item_from_raw(pa, level, prop, C::item_into_raw(item).3) == item,C::item_into_raw(item).3@ == self.cur_entry_owner().frame_permission(),valid_frame_paddr(pa),C::raw_item_well_formed((pa, level, prop, C::item_into_raw(item).3)),(C::item_into_raw(item).3@ is Some) == self.cur_entry_owner().frame_is_tracked(),C::item_into_raw(item).3@ is Some
==> (regions.slot_owner(pa).ref_count() < REF_COUNT_MAX || may_panic()),ensuresitem.clone_requires(regions),Proves clone_requires for the item reconstructed from the current frame entry.
Sourcepub proof fn lemma_clone_item_preserves_invariants(
self,
old_regions: MetaRegionOwners,
new_regions: MetaRegionOwners,
idx: int,
)
pub proof fn lemma_clone_item_preserves_invariants( self, old_regions: MetaRegionOwners, new_regions: MetaRegionOwners, idx: int, )
self.inv(),self.metaregion_sound(old_regions),old_regions.inv(),self.cur_entry_owner().is_frame(),idx == frame_to_index(self.cur_entry_owner().frame().mapped_pa),old_regions.slot_owners.contains_key(idx),new_regions.slot_owners.contains_key(idx),new_regions.ref_count(idx) == old_regions.ref_count(idx) + 1,new_regions.slot_owners[idx].ref_count_perm.id()
== old_regions.slot_owners[idx].ref_count_perm.id(),new_regions.slot_owners[idx].metadata_perm.id()
== old_regions.slot_owners[idx].metadata_perm.id(),new_regions.slot_owners[idx].metadata_perm.frac() + 1
== old_regions.slot_owners[idx].metadata_perm.frac(),new_regions.slot_owners[idx].in_list_perm == old_regions.slot_owners[idx].in_list_perm,new_regions.slot_owners[idx].paths_in_pt == old_regions.slot_owners[idx].paths_in_pt,new_regions.slot_owners[idx].slot_vaddr == old_regions.slot_owners[idx].slot_vaddr,new_regions.slot_owners[idx].usage == old_regions.slot_owners[idx].usage,new_regions.slot_owners.dom() == old_regions.slot_owners.dom(),forall |i: int| {
i != idx && old_regions.slot_owners.contains_key(i)
==> new_regions.slot_owners[i] == old_regions.slot_owners[i]
},new_regions.slots == old_regions.slots,new_regions.inv(),0 < old_regions.ref_count(idx),old_regions.ref_count(idx) + 1 <= REF_COUNT_MAX,ensuresnew_regions.inv(),self.metaregion_sound(new_regions),Incrementing the ref count of the current frame preserves regions.inv() and
self.metaregion_sound(new_regions).
Sourcepub proof fn lemma_new_child_mappings_eq_target(
self,
new_subtree: OwnerSubtree<C>,
pa: Paddr,
level: PagingLevel,
prop: PageProperty,
)
pub proof fn lemma_new_child_mappings_eq_target( self, new_subtree: OwnerSubtree<C>, pa: Paddr, level: PagingLevel, prop: PageProperty, )
self.inv(),self.in_barrier_va(),level == self.level,new_subtree.inv(),new_subtree.value().is_frame(),new_subtree.value().path
== self
.continuations[self.level - 1]
.path()
.push_tail(self.continuations[self.level - 1].idx as int),new_subtree.value().frame().mapped_pa == pa,new_subtree.value().frame().prop == prop,ensuresPageTableOwner(new_subtree)@.mappings
== set![
Mapping { va_range : self @.cur_slot_range(page_size::< PagingConsts > (level)),
pa_range : pa.. (pa + page_size::< PagingConsts > (level)) as usize, page_size :
page_size::< PagingConsts > (level), property : prop, }
],A new frame subtree at the current position has mappings equal to the singleton mapping covering the current slot range.
Sourcepub open spec fn locked_range(self) -> Range<int> ⓘ
pub open spec fn locked_range(self) -> Range<int> ⓘ
{
let size = page_size::<C>((self.guard_level + 1) as PagingLevel) as int;
Range {
start: self.prefix as int,
end: self.prefix as int + size,
}
}VA interval covered by the locked node at guard_level, starting at prefix.
Integer endpoints can represent the end of the address space.
Sourcepub open spec fn in_locked_range(self) -> bool
pub open spec fn in_locked_range(self) -> bool
{ self.locked_range().start <= self.va < self.locked_range().end }Whether the current VA lies in the guard node’s VA interval.
Sourcepub open spec fn in_barrier_va(self) -> bool
pub open spec fn in_barrier_va(self) -> bool
{ self.barrier_va.start <= self.va < self.barrier_va.end }Whether the current VA belongs to the range requested when creating the cursor.
Sourcepub open spec fn at_or_past_barrier_end(self) -> bool
pub open spec fn at_or_past_barrier_end(self) -> bool
{ self.va >= self.barrier_va.end }Whether the current VA has reached or passed the requested range’s end.
Sourcepub proof fn lemma_in_barrier_va_top_index_bounds(self)
pub proof fn lemma_in_barrier_va_top_index_bounds(self)
self.inv(),self.in_barrier_va(),!self.popped_too_high,ensuresC::TOP_LEVEL_INDEX_RANGE().start <= pte_index_spec::<C>(self.va, C::NR_LEVELS())
< C::TOP_LEVEL_INDEX_RANGE().end,Active cursor addresses select a root entry in the configured index range.
Sourcepub proof fn lemma_in_barrier_va_level_le_guard_level(self)
pub proof fn lemma_in_barrier_va_level_le_guard_level(self)
self.inv(),self.in_barrier_va(),!self.popped_too_high,ensuresself.level <= self.guard_level,Sourcepub proof fn lemma_locked_range_span(self)
pub proof fn lemma_locked_range_span(self)
self.inv(),ensuresself.locked_range().start == self.prefix,self.locked_range().end
== self.prefix + page_size::<PagingConsts>((self.guard_level + 1) as PagingLevel),self.prefix % page_size::<PagingConsts>((self.guard_level + 1) as PagingLevel) == 0,The structural locked range spans the full guard node.
Sourcepub proof fn lemma_prefix_aligned_to_guard_level(self)
pub proof fn lemma_prefix_aligned_to_guard_level(self)
self.inv(),ensuresself.prefix as nat % page_size::<PagingConsts>(self.guard_level) as nat == 0,Sourcepub proof fn lemma_in_node_holds_at_top(
self,
self_va: Vaddr,
va: Vaddr,
node_size: usize,
)
pub proof fn lemma_in_node_holds_at_top( self, self_va: Vaddr, va: Vaddr, node_size: usize, )
self.inv(),self_va == self.va,self.guard_level == NR_LEVELS,node_size == page_size::<PagingConsts>((NR_LEVELS + 1) as PagingLevel),self.locked_range().start <= va < self.locked_range().end,ensuresnat_align_down(self_va as nat, node_size as nat) <= va as nat,(va as nat) - nat_align_down(self_va as nat, node_size as nat) < node_size as nat,At the top guard level, the node determined by the cursor’s upper address bits contains the entire locked range, even after the cursor leaves that range.
Sourcepub proof fn lemma_va_plus_page_size_no_overflow(self, level: PagingLevel)
pub proof fn lemma_va_plus_page_size_no_overflow(self, level: PagingLevel)
self.inv(),self.in_barrier_va(),1 <= level <= self.guard_level,ensuresself.va + page_size::<PagingConsts>(level) <= usize::MAX,self.va + page_size::<PagingConsts>(level) < page_table_coverage_end_spec::<C>(),Guard-level headroom bounds active slot arithmetic without excluding the final top-level slot.
Sourcepub proof fn lemma_locked_range_page_aligned(self)
pub proof fn lemma_locked_range_page_aligned(self)
self.inv(),ensuresself.locked_range().end % (PAGE_SIZE as int) == 0,self.locked_range().start % (PAGE_SIZE as int) == 0,Sourcepub proof fn lemma_cur_subtree_inv(self)
pub proof fn lemma_cur_subtree_inv(self)
self.inv(),ensuresself.cur_subtree().inv(),Sourcepub proof fn tracked_new_absent_subtree(self) -> tracked res : OwnerSubtree<C>
pub proof fn tracked_new_absent_subtree(self) -> tracked res : OwnerSubtree<C>
self.inv(),ensuresres.inv(),res.value().is_absent(),res.level() == self.continuations[self.level - 1].tree_level + 1,res.value().path.len() <= INC_LEVELS - 1,res.value().parent_level
== self.continuations[self.level - 1].child().value().parent_level,res.value().path
== self
.continuations[self.level - 1]
.path()
.push_tail(self.continuations[self.level - 1].idx as int),res == OwnerSubtree::new_val(res.value(), res.level() as nat),Constructs an absent subtree that fits the cursor’s current slot.
Sourcepub proof fn lemma_cur_entry_absent_not_present(self)
pub proof fn lemma_cur_entry_absent_not_present(self)
self.inv(),self.in_barrier_va(),self.cur_entry_owner().is_absent(),ensures!self@.present(),If the current entry is absent, !self@.present().
Sourcepub proof fn lemma_cur_subtree_empty_not_present(self)
pub proof fn lemma_cur_subtree_empty_not_present(self)
self.inv(),self.in_barrier_va(),PageTableOwner(self.cur_subtree()).view_rec(self.cur_subtree().value().path) =~= set![],ensures!self@.present(),Generalises lemma_cur_entry_absent_not_present to any empty subtree.
Sourcepub proof fn lemma_cur_entry_frame_present(self)
pub proof fn lemma_cur_entry_frame_present(self)
self.inv(),self.in_barrier_va(),self.cur_entry_owner().is_frame(),ensuresself@.present(),self@
.query(
self.cur_entry_owner().frame().mapped_pa,
page_size::<PagingConsts>(self.cur_entry_owner().parent_level),
self.cur_entry_owner().frame().prop,
),Sourcepub open spec fn path_metaregion_sound(self, regions: MetaRegionOwners) -> bool
pub open spec fn path_metaregion_sound(self, regions: MetaRegionOwners) -> bool
{
forall |i: int| {
self.level - 1 <= i < NR_LEVELS
==> self.continuations[i].entry_own.metaregion_sound(regions)
}
}The entry_own at each continuation level satisfies metaregion_sound.
Sourcepub open spec fn metaregion_sound(self, regions: MetaRegionOwners) -> bool
pub open spec fn metaregion_sound(self, regions: MetaRegionOwners) -> bool
{
&&& self
.map_full_tree(|entry_owner: EntryOwner<C>, path: TreePath<NR_ENTRIES>| {
entry_owner.metaregion_sound(regions)
})
&&& self.path_metaregion_sound(regions)
}Sourcepub proof fn lemma_metaregion_preserved(
self,
other: Self,
regions0: MetaRegionOwners,
regions1: MetaRegionOwners,
)
pub proof fn lemma_metaregion_preserved( self, other: Self, regions0: MetaRegionOwners, regions1: MetaRegionOwners, )
self.inv(),self.metaregion_sound(regions0),self.level == other.level,self.continuations =~= other.continuations,OwnerSubtree::implies(
PageTableOwner::<C>::metaregion_sound_pred(regions0),
PageTableOwner::<C>::metaregion_sound_pred(regions1),
),ensuresother.metaregion_sound(regions1),Sourcepub proof fn lemma_metaregion_slot_owners_preserved(
self,
regions0: MetaRegionOwners,
regions1: MetaRegionOwners,
)
pub proof fn lemma_metaregion_slot_owners_preserved( self, regions0: MetaRegionOwners, regions1: MetaRegionOwners, )
self.inv(),self.metaregion_sound(regions0),regions0.slot_owners =~= regions1.slot_owners,forall |k: int| {
regions0.slots.contains_key(k) ==> #[trigger] regions1.slots.contains_key(k)
},forall |k: int| {
regions0.slots.contains_key(k) ==> regions0.slots[k] == #[trigger] regions1.slots[k]
},ensuresself.metaregion_sound(regions1),Transfers metaregion_sound when slot_owners is preserved.
Sourcepub proof fn lemma_metaregion_slot_owners_rc_increment(
self,
regions0: MetaRegionOwners,
regions1: MetaRegionOwners,
idx: int,
)
pub proof fn lemma_metaregion_slot_owners_rc_increment( self, regions0: MetaRegionOwners, regions1: MetaRegionOwners, idx: int, )
self.inv(),self.metaregion_sound(regions0),regions0.inv(),regions1.slots == regions0.slots,regions1.slot_owners.dom() == regions0.slot_owners.dom(),regions1.ref_count(idx) == regions0.ref_count(idx) + 1,regions1.slot_owners[idx].ref_count_perm.id()
== regions0.slot_owners[idx].ref_count_perm.id(),regions1.slot_owners[idx].metadata_perm.id()
== regions0.slot_owners[idx].metadata_perm.id(),regions1.slot_owners[idx].metadata_perm.frac() + 1
== regions0.slot_owners[idx].metadata_perm.frac(),regions1.slot_owners[idx].in_list_perm == regions0.slot_owners[idx].in_list_perm,regions1.slot_owners[idx].paths_in_pt == regions0.slot_owners[idx].paths_in_pt,regions1.slot_owners[idx].slot_vaddr == regions0.slot_owners[idx].slot_vaddr,regions1.slot_owners[idx].usage == regions0.slot_owners[idx].usage,regions1.ref_count(idx) != REF_COUNT_UNUSED,regions1.ref_count(idx) <= REF_COUNT_MAX,regions1.inv(),forall |i: int| {
i != idx && regions0.slot_owners.contains_key(i)
==> regions1.slot_owners[i] == regions0.slot_owners[i]
},ensuresself.metaregion_sound(regions1),Sourcepub proof fn lemma_cont_entry_metaregion_at(self, regions: MetaRegionOwners, i: int)
pub proof fn lemma_cont_entry_metaregion_at(self, regions: MetaRegionOwners, i: int)
self.inv(),self.metaregion_sound(regions),self.level - 1 <= i < NR_LEVELS,ensuresself.continuations[i].entry_own.metaregion_sound(regions),The continuation entry at i satisfies metaregion_sound.
Sourcepub open spec fn new(
owner_subtree: OwnerSubtree<C>,
barrier_va: Range<Vaddr>,
guard: PageTableGuard<'rcu, C>,
) -> Self
pub open spec fn new( owner_subtree: OwnerSubtree<C>, barrier_va: Range<Vaddr>, guard: PageTableGuard<'rcu, C>, ) -> Self
{
let va = barrier_va.start;
let idx = pte_index_spec::<C>(va, C::NR_LEVELS());
Self {
level: C::NR_LEVELS(),
continuations: Map::empty()
.insert(
C::NR_LEVELS() - 1,
CursorContinuation::new(owner_subtree, idx, guard),
),
va,
barrier_va,
guard_level: C::NR_LEVELS(),
prefix: nat_align_down(
va as nat,
page_size::<C>((C::NR_LEVELS() + 1) as PagingLevel) as nat,
) as Vaddr,
popped_too_high: false,
}
}Sourcepub proof fn tracked_new(
tracked owner_subtree: OwnerSubtree<C>,
barrier_va: Range<Vaddr>,
guard: PageTableGuard<'rcu, C>,
) -> tracked Self
pub proof fn tracked_new( tracked owner_subtree: OwnerSubtree<C>, barrier_va: Range<Vaddr>, guard: PageTableGuard<'rcu, C>, ) -> tracked Self
Self::new(owner_subtree, barrier_va, guard),Sourcepub broadcast group fn group_lemmas()
pub broadcast group fn group_lemmas()
Source§impl<'rcu, C: PageTableConfig> CursorOwner<'rcu, C>
impl<'rcu, C: PageTableConfig> CursorOwner<'rcu, C>
Sourcepub proof fn view_non_overlapping(self)
pub proof fn view_non_overlapping(self)
self.inv(),ensuresself@.non_overlapping(),The cursor’s view has non-overlapping mappings.
Source§impl<'rcu, C: PageTableConfig> CursorOwner<'rcu, C>
impl<'rcu, C: PageTableConfig> CursorOwner<'rcu, C>
Sourcepub proof fn query_mapping_from_subtree(self, qm: Mapping)
pub proof fn query_mapping_from_subtree(self, qm: Mapping)
self.inv(),self.in_barrier_va(),self@.inv(),self@.present(),qm == self@.query_mapping(),ensuresPageTableOwner(self.cur_subtree()).view_rec(self.cur_subtree().value().path).contains(qm),When the current entry is a PT node at level self.level, any mapping at cur_va has
page_size <= page_size(self.level - 1). Therefore split_while_huge at
page_size(self.level - 1) does not split anything and is a no-op on the abstract view.
When present, the query_mapping is from the current subtree’s view_rec.
Sourcepub proof fn split_while_huge_node_noop(self)
pub proof fn split_while_huge_node_noop(self)
self.inv(),self.in_barrier_va(),self.cur_entry_owner().is_node(),self.level > 1,ensuresself@.split_while_huge(page_size::<PagingConsts>((self.level - 1) as PagingLevel))
== self@,Sourcepub proof fn split_while_huge_absent_noop(self, size: usize)
pub proof fn split_while_huge_absent_noop(self, size: usize)
self.inv(),self.in_barrier_va(),self.cur_entry_owner().is_absent(),ensuresself@.split_while_huge(size) == self@,When the current entry is absent, there is no mapping at cur_va in the abstract view,
so split_while_huge finds nothing to split and is a no-op for any target size.
Sourcepub proof fn lemma_split_while_huge_at_level_noop(self)
pub proof fn lemma_split_while_huge_at_level_noop(self)
self.inv(),self.in_barrier_va(),ensuresself@.split_while_huge(page_size::<PagingConsts>(self.level as PagingLevel)) == self@,Sourcepub proof fn find_next_split_push_equals_split_while_huge(
self,
old_view: CursorView<C>,
)
pub proof fn find_next_split_push_equals_split_while_huge( self, old_view: CursorView<C>, )
self.inv(),old_view.inv(),self.cur_entry_owner().is_frame(),self@.cur_va == old_view.cur_va,old_view.present(),old_view.query_mapping().page_size > page_size::<PagingConsts>(self.level as PagingLevel),old_view.query_mapping().page_size / NR_ENTRIES
== page_size::<PagingConsts>(self.level as PagingLevel),old_view.query_mapping().page_size % page_size::<PagingConsts>(self.level as PagingLevel)
== 0,self@.mappings
=~= old_view
.split_if_mapped_huge_spec(page_size::<PagingConsts>(self.level as PagingLevel))
.mappings,ensuresself@ == old_view.split_while_huge(page_size::<PagingConsts>(self.level as PagingLevel)),After split_if_mapped_huge + push_level, the mappings equal
old_view.split_while_huge(page_size(current_level)).
Source§impl<'rcu, C: PageTableConfig> CursorOwner<'rcu, C>
impl<'rcu, C: PageTableConfig> CursorOwner<'rcu, C>
Sourcepub proof fn lemma_map_children_implies(
self,
f: FnSpec<(EntryOwner<C>, TreePath<NR_ENTRIES>), bool>,
g: FnSpec<(EntryOwner<C>, TreePath<NR_ENTRIES>), bool>,
)
pub proof fn lemma_map_children_implies( self, f: FnSpec<(EntryOwner<C>, TreePath<NR_ENTRIES>), bool>, g: FnSpec<(EntryOwner<C>, TreePath<NR_ENTRIES>), bool>, )
self.inv(),OwnerSubtree::implies(f, g),forall |i: int| self.level - 1 <= i < NR_LEVELS ==> self.continuations[i].map_children(f),ensuresforall |i: int| self.level - 1 <= i < NR_LEVELS ==> self.continuations[i].map_children(g),Sourcepub proof fn lemma_cur_entry_node_implies_level_gt_1(self)
pub proof fn lemma_cur_entry_node_implies_level_gt_1(self)
self.inv(),self.cur_entry_owner().is_node(),ensuresself.level > 1,Sourcepub proof fn frame_not_fits_implies_level_gt_1(
self,
cur_entry_fits_range: bool,
cur_va: Vaddr,
end: Vaddr,
)
pub proof fn frame_not_fits_implies_level_gt_1( self, cur_entry_fits_range: bool, cur_va: Vaddr, end: Vaddr, )
self.inv(),self.cur_entry_owner().is_frame(),!cur_entry_fits_range,cur_va < end,cur_va == self.cur_va(),cur_entry_fits_range
== (cur_va == self.cur_va_range().start && self.cur_va_range().end <= end),cur_va as nat % PAGE_SIZE as nat == 0,end as nat % PAGE_SIZE as nat == 0,ensuresself.level > 1,A frame entry at the cursor’s current level that doesn’t fit the aligned range
[cur_va, end) must be at level > 1.
Justification: At level 1, page_size(1) == BASE_PAGE_SIZE. Since the cursor VA
and end are BASE_PAGE_SIZE-aligned and cur_va < end, we have
cur_va + page_size(1) <= end, so a level-1 frame always fits. Therefore
!cur_entry_fits_range implies level > 1.
Source§impl<'rcu, C: PageTableConfig> CursorOwner<'rcu, C>
impl<'rcu, C: PageTableConfig> CursorOwner<'rcu, C>
Sourcepub open spec fn zero_below_level(self) -> Self
pub open spec fn zero_below_level(self) -> Self
1 <= self.level <= C::NR_LEVELS(),{
Self {
va: nat_align_down(self.va as nat, page_size::<C>(self.level) as nat) as Vaddr,
..self
}
}Sourcepub open spec fn prefix_vaddr(self) -> Vaddr
pub open spec fn prefix_vaddr(self) -> Vaddr
{ self.prefix }Sourcepub open spec fn cur_va_range(self) -> Range<Vaddr> ⓘ
pub open spec fn cur_va_range(self) -> Range<Vaddr> ⓘ
{
let size = page_size::<PagingConsts>(self.level);
let start = nat_align_down(self.cur_va() as nat, size as nat) as Vaddr;
Range {
start,
end: (start + size) as Vaddr,
}
}Sourcepub open spec fn set_va_in_node(self, new_va: Vaddr) -> Self
pub open spec fn set_va_in_node(self, new_va: Vaddr) -> Self
{
let old_cont = self.continuations[self.level - 1];
Self {
va: new_va,
continuations: self
.continuations
.insert(
self.level - 1,
CursorContinuation {
idx: pte_index_spec::<C>(new_va, self.level),
..old_cont
},
),
popped_too_high: false,
..self
}
}Sourcepub proof fn lemma_inc_index_va(self)
pub proof fn lemma_inc_index_va(self)
self.inv(),self.in_barrier_va(),self.index() + 1 < NR_ENTRIES,ensuresself.inc_index().va == self.va + page_size::<C>(self.level),Incrementing the selected nonterminal entry advances by one slot without overflow.
Sourcepub proof fn inc_and_zero_increases_va(self)
pub proof fn inc_and_zero_increases_va(self)
self.inv(),self.in_barrier_va(),self.index() + 1 < NR_ENTRIES,ensuresself.inc_index().zero_below_level().va > self.va,self.inc_index().zero_below_level().va
== self@.align_up_spec(page_size::<PagingConsts>(self.level)),Incrementing a nonterminal index and aligning down reaches the next slot boundary.
Sourcepub proof fn cur_va_in_subtree_range(self)
pub proof fn cur_va_in_subtree_range(self)
self.inv(),self.in_barrier_va(),ensuresvaddr_of::<C>(self.cur_subtree().value().path) <= self.cur_va()
< vaddr_of::<C>(self.cur_subtree().value().path)
+ page_size::<PagingConsts>(self.level),vaddr_of::<C>(self.cur_subtree().value().path)
== nat_align_down(self.cur_va() as nat, page_size::<PagingConsts>(self.level) as nat),The current subtree starts at the cursor’s aligned slot boundary and contains its VA.
Sourcepub proof fn lemma_locked_range_vaddr_prefix_match(self, new_va: Vaddr)
pub proof fn lemma_locked_range_vaddr_prefix_match(self, new_va: Vaddr)
self.inv(),new_va % C::BASE_PAGE_SIZE() == 0,vaddr_leading_part_spec::<C>(new_va) == vaddr_leading_part_spec::<C>(self.prefix_vaddr()),self.locked_range().start <= new_va < self.locked_range().end,ensuresforall |i: int| {
self.guard_level <= i < C::NR_LEVELS()
==> (#[trigger] pte_index_spec::<C>(new_va, (i + 1) as PagingLevel))
== pte_index_spec::<C>(self.prefix, (i + 1) as PagingLevel)
},Addresses in the same guard node share indices strictly above its level.
Sourcepub proof fn lemma_cur_pte_index(self)
pub proof fn lemma_cur_pte_index(self)
self.inv(),self.in_barrier_va(),ensures1 <= self.level <= C::NR_LEVELS(),pte_index_spec::<C>(self.cur_va(), self.level) == self.continuations[self.level - 1].idx,pte_index_spec::<C>(self.cur_va(), self.level) < NR_ENTRIES,The entry selected by the cursor’s concrete VA is the entry owned by its current
continuation. This is the architecture-parameterized replacement for exposing the
decomposed address stored in CursorOwner.
Sourcepub proof fn tracked_set_vaddr_in_node(tracked &mut self, new_va: Vaddr)
pub proof fn tracked_set_vaddr_in_node(tracked &mut self, new_va: Vaddr)
old(self).inv(),new_va % C::BASE_PAGE_SIZE() == 0,vaddr_leading_part_spec::<C>(new_va) == vaddr_leading_part_spec::<C>(old(self).cur_va()),forall |i: int| {
old(self).level <= i < C::NR_LEVELS()
==> (#[trigger] pte_index_spec::<C>(new_va, (i + 1) as PagingLevel))
== old(self).continuations[i].idx
},old(self).locked_range().start <= new_va < old(self).locked_range().end,old(self).set_va_in_node(new_va).in_barrier_va(),old(self).level <= old(self).guard_level,ensures*final(self) == old(self).set_va_in_node(new_va),final(self).inv(),Architecture-parameterized entry point for repositioning a cursor inside its current page-table node.
Trait Implementations§
Source§impl<'rcu, C: PageTableConfig> Inv for CursorOwner<'rcu, C>
impl<'rcu, C: PageTableConfig> Inv for CursorOwner<'rcu, C>
Source§open spec fn inv(self) -> bool
open spec fn inv(self) -> bool
{
&&& self.va % C::BASE_PAGE_SIZE() == 0
&&& 1 <= self.level <= NR_LEVELS
&&& 1 <= self.guard_level <= NR_LEVELS
&&& page_table_managed_start_spec::<C>() <= self.va
<= page_table_managed_end_spec::<C>()
&&& self.locked_range().start <= self.barrier_va.start < self.barrier_va.end
<= self.locked_range().end
&&& self.barrier_va.start % C::BASE_PAGE_SIZE() == 0
&&& self.barrier_va.end % C::BASE_PAGE_SIZE() == 0
&&& is_valid_range_spec::<C>(self.barrier_va)
&&& self.barrier_va.end + page_size::<C>(self.guard_level)
<= page_table_coverage_end_spec::<C>()
&&& self.popped_too_high ==> self.level >= self.guard_level
&&& !self.popped_too_high
==> self.level <= self.guard_level || self.at_or_past_barrier_end()
&&& self.continuations[self.level - 1].all_some()
&&& forall |i: int| {
self.level <= i < NR_LEVELS
==> { (#[trigger] self.continuations[i]).all_but_index_some() }
}
&&& self.level <= NR_LEVELS - 1
==> {
&&& C::TOP_LEVEL_INDEX_RANGE().start <= self.continuations[NR_LEVELS - 1].idx
&&& self.continuations[NR_LEVELS - 1].idx < C::TOP_LEVEL_INDEX_RANGE().end
}
&&& forall |j: int| {
0 <= j < NR_ENTRIES
&& !(C::TOP_LEVEL_INDEX_RANGE().start <= j < C::TOP_LEVEL_INDEX_RANGE().end)
==> (self.continuations[NR_LEVELS - 1].children[j] is Some
==> (self
.continuations[NR_LEVELS - 1]
.children[j]
.unwrap()
.value()
.is_borrowed()
|| self
.continuations[NR_LEVELS - 1]
.children[j]
.unwrap()
.value()
.is_absent()))
}
&&& self.prefix % C::BASE_PAGE_SIZE() == 0
&&& self.prefix <= self.va
&&& forall |i: int| {
1 <= i <= self.guard_level
==> #[trigger] pte_index_spec::<C>(self.prefix, i as PagingLevel) == 0
}
&&& self.guard_level < NR_LEVELS
==> page_table_managed_start_spec::<C>() <= self.prefix
< page_table_managed_end_spec::<C>()
&&& vaddr_leading_part_spec::<C>(self.va)
== vaddr_leading_part_spec::<C>(self.prefix)
&&& vaddr_leading_part_spec::<C>(self.prefix)
== page_table_coverage_start_spec::<C>()
&&& self.level <= self.guard_level
==> forall |i: int| {
self.guard_level <= i < NR_LEVELS
==> self.continuations[i].idx
== pte_index_spec::<C>(self.prefix, (i + 1) as PagingLevel)
}
&&& !self.popped_too_high
&& (self.in_locked_range() || self.level < self.guard_level)
==> forall |i: int| {
self.guard_level <= i < NR_LEVELS
==> #[trigger] pte_index_spec::<C>(self.va, (i + 1) as PagingLevel)
== pte_index_spec::<C>(self.prefix, (i + 1) as PagingLevel)
}
&&& self.level <= 4
==> {
&&& self.continuations.contains_key(3)
&&& self.continuations[3].inv()
&&& self.continuations[3].level() == 4
&&& self.continuations[3].entry_own.parent_level == 5
&&& self.in_locked_range()
==> pte_index_spec::<C>(self.va, (3 + 1) as PagingLevel)
== self.continuations[3].idx
}
&&& self.level <= 3
==> {
&&& self.continuations.contains_key(2)
&&& self.continuations[2].inv()
&&& self.continuations[2].level() == 3
&&& self.continuations[2].entry_own.parent_level == 4
&&& self.in_locked_range()
==> pte_index_spec::<C>(self.va, (2 + 1) as PagingLevel)
== self.continuations[2].idx
&&& self.continuations[2].guard.inner.inner@.slot_vaddr()
!= self.continuations[3].guard.inner.inner@.slot_vaddr()
&&& self.continuations[2].path()
== self
.continuations[3]
.path()
.push_tail(self.continuations[3].idx as int)
&&& self.continuations[2].entry_own.path.len()
== self.continuations[3].entry_own.node().tree_level + 1
&&& self
.continuations[2]
.entry_own
.match_pte(
self
.continuations[3]
.entry_own
.node()
.children_perm
.value()[self.continuations[3].idx as int],
self.continuations[3].entry_own.node().level(),
)
&&& self.continuations[2].entry_own.parent_level
== self.continuations[3].entry_own.node().level()
}
&&& self.level <= 2
==> {
&&& self.continuations.contains_key(1)
&&& self.continuations[1].inv()
&&& self.continuations[1].level() == 2
&&& self.continuations[1].entry_own.parent_level == 3
&&& self.in_locked_range()
==> pte_index_spec::<C>(self.va, (1 + 1) as PagingLevel)
== self.continuations[1].idx
&&& self.continuations[1].guard.inner.inner@.slot_vaddr()
!= self.continuations[2].guard.inner.inner@.slot_vaddr()
&&& self.continuations[1].guard.inner.inner@.slot_vaddr()
!= self.continuations[3].guard.inner.inner@.slot_vaddr()
&&& self.continuations[1].path()
== self
.continuations[2]
.path()
.push_tail(self.continuations[2].idx as int)
&&& self.continuations[1].entry_own.path.len()
== self.continuations[2].entry_own.node().tree_level + 1
&&& self
.continuations[1]
.entry_own
.match_pte(
self
.continuations[2]
.entry_own
.node()
.children_perm
.value()[self.continuations[2].idx as int],
self.continuations[2].entry_own.node().level(),
)
&&& self.continuations[1].entry_own.parent_level
== self.continuations[2].entry_own.node().level()
}
&&& self.level == 1
==> {
&&& self.continuations.contains_key(0)
&&& self.continuations[0].inv()
&&& self.continuations[0].level() == 1
&&& self.continuations[0].entry_own.parent_level == 2
&&& self.in_locked_range()
==> pte_index_spec::<C>(self.va, (0 + 1) as PagingLevel)
== self.continuations[0].idx
&&& self.continuations[0].guard.inner.inner@.slot_vaddr()
!= self.continuations[1].guard.inner.inner@.slot_vaddr()
&&& self.continuations[0].guard.inner.inner@.slot_vaddr()
!= self.continuations[2].guard.inner.inner@.slot_vaddr()
&&& self.continuations[0].guard.inner.inner@.slot_vaddr()
!= self.continuations[3].guard.inner.inner@.slot_vaddr()
&&& self.continuations[0].path()
== self
.continuations[1]
.path()
.push_tail(self.continuations[1].idx as int)
&&& self.continuations[0].entry_own.path.len()
== self.continuations[1].entry_own.node().tree_level + 1
&&& self
.continuations[0]
.entry_own
.match_pte(
self
.continuations[1]
.entry_own
.node()
.children_perm
.value()[self.continuations[1].idx as int],
self.continuations[1].entry_own.node().level(),
)
&&& self.continuations[0].entry_own.parent_level
== self.continuations[1].entry_own.node().level()
}
}Source§impl<'rcu, C: PageTableConfig> InvView for CursorOwner<'rcu, C>
impl<'rcu, C: PageTableConfig> InvView for CursorOwner<'rcu, C>
Source§proof fn view_preserves_inv(self)
proof fn view_preserves_inv(self)
Source§impl<'rcu, C: PageTableConfig> View for CursorOwner<'rcu, C>
impl<'rcu, C: PageTableConfig> View for CursorOwner<'rcu, C>
Auto Trait Implementations§
impl<'rcu, C> !RefUnwindSafe for CursorOwner<'rcu, C>
impl<'rcu, C> !UnwindSafe for CursorOwner<'rcu, C>
impl<'rcu, C> Freeze for CursorOwner<'rcu, C>
impl<'rcu, C> Send for CursorOwner<'rcu, C>
impl<'rcu, C> Sync for CursorOwner<'rcu, C>
impl<'rcu, C> Unpin for CursorOwner<'rcu, C>
impl<'rcu, C> UnsafeUnpin for CursorOwner<'rcu, C>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
§impl<T> Conv for T
impl<T> Conv for T
§impl<T> FmtForward for T
impl<T> FmtForward for T
§fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
self to use its Binary implementation when Debug-formatted.§fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
self to use its Display implementation when
Debug-formatted.§fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
self to use its LowerExp implementation when
Debug-formatted.§fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
self to use its LowerHex implementation when
Debug-formatted.§fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
self to use its Octal implementation when Debug-formatted.§fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
self to use its Pointer implementation when
Debug-formatted.§fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
self to use its UpperExp implementation when
Debug-formatted.§fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
self to use its UpperHex implementation when
Debug-formatted.§fn fmt_list(self) -> FmtList<Self>where
&'a Self: for<'a> IntoIterator,
fn fmt_list(self) -> FmtList<Self>where
&'a Self: for<'a> IntoIterator,
§impl<T, VERUS_SPEC__A> FromSpec<T> for VERUS_SPEC__Awhere
VERUS_SPEC__A: From<T>,
impl<T, VERUS_SPEC__A> FromSpec<T> for VERUS_SPEC__Awhere
VERUS_SPEC__A: From<T>,
fn obeys_from_spec() -> bool
fn from_spec(v: T) -> VERUS_SPEC__A
§impl<T, VERUS_SPEC__A> IntoSpec<T> for VERUS_SPEC__Awhere
VERUS_SPEC__A: Into<T>,
impl<T, VERUS_SPEC__A> IntoSpec<T> for VERUS_SPEC__Awhere
VERUS_SPEC__A: Into<T>,
fn obeys_into_spec() -> bool
fn into_spec(self) -> T
§impl<T, U> IntoSpecImpl<U> for Twhere
U: From<T>,
impl<T, U> IntoSpecImpl<U> for Twhere
U: From<T>,
fn obeys_into_spec() -> bool
fn into_spec(self) -> U
§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read more§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read more§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
self, then passes self.as_ref() into the pipe function.§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
self, then passes self.as_mut() into the pipe
function.§fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self, then passes self.deref() into the pipe function.impl<A> SpecEq<&A> for Awhere
A: ?Sized,
impl<A> SpecEq<&mut A> for Awhere
A: ?Sized,
impl<A> SpecEq<A> for Awhere
A: ?Sized,
impl<A> SpecEq<Ghost<A>> for A
impl<A> SpecEq<Tracked<A>> for A
§impl<T> Tap for T
impl<T> Tap for T
§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B> of a value. Read more§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B> of a value. Read more§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R> view of a value. Read more§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R> view of a value. Read more§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target of a value. Read more§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target of a value. Read more§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap() only in debug builds, and is erased in release builds.§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut() only in debug builds, and is erased in release
builds.§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.tap_borrow() only in debug builds, and is erased in release
builds.§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.tap_borrow_mut() only in debug builds, and is erased in release
builds.§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.tap_ref() only in debug builds, and is erased in release
builds.§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.tap_ref_mut() only in debug builds, and is erased in release
builds.§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref() only in debug builds, and is erased in release
builds.