pub struct CursorOwner<'rcu, C: PageTableConfig> {
pub level: PagingLevel,
pub continuations: Map<int, CursorContinuation<'rcu, C>>,
pub va: Vaddr,
pub guard_level: PagingLevel,
pub prefix: Vaddr,
pub popped_too_high: bool,
}Fields§
§level: PagingLevel§continuations: Map<int, CursorContinuation<'rcu, C>>§va: 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 protect_preserves_cursor_inv_metaregion(
self,
other: Self,
regions: MetaRegionOwners,
)
pub proof fn protect_preserves_cursor_inv_metaregion( self, other: Self, regions: MetaRegionOwners, )
self.inv(),self.in_locked_range(),!self.popped_too_high,self.metaregion_sound(regions),self.cur_entry_owner().is_frame(),other.cur_entry_owner().is_frame(),other.cur_entry_owner().inv(),other.cur_entry_owner().frame().mapped_pa == self.cur_entry_owner().frame().mapped_pa,other.cur_entry_owner().path == self.cur_entry_owner().path,other.cur_entry_owner().parent_level == self.cur_entry_owner().parent_level,self.level == other.level,self.guard_level == other.guard_level,self.va == other.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@.ptr.addr()
== self.continuations[self.level - 1].guard.inner.inner@.ptr.addr(),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),Sourcepub proof fn map_branch_none_inv_holds(self, owner0: Self)
pub proof fn map_branch_none_inv_holds(self, owner0: Self)
owner0.inv(),self.in_locked_range(),!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.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@.ptr.addr()
== owner0.continuations[owner0.level - 1].guard.inner.inner@.ptr.addr(),self.continuations[self.level - 1].path()
== owner0.continuations[owner0.level - 1].path(),pte_index_spec::<C>(self.cur_va(), (self.level - 1 + 1) 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_locked_range(),self.inv(),self.in_locked_range(),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 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
{
(self.max_steps_partial(self.level as usize)
+ Self::max_steps_subtree(self.level as usize)) as nat
}Sourcepub proof fn max_steps_partial_eq(self, other: Self, start: usize)
pub proof fn 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 proof fn max_steps_partial_inv(self, other: Self, level: usize)
pub proof fn max_steps_partial_inv(self, other: Self, level: usize)
self.inv(),other.inv(),self.level == other.level,self.level <= level <= NR_LEVELS + 1,forall |i: int| {
self.level - 1 <= i < NR_LEVELS
==> self.continuations[i].idx == other.continuations[i].idx
},ensuresself.max_steps_partial(level) == other.max_steps_partial(level),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.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_locked_range(),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@.ptr.addr()
!= guard.inner.inner@.ptr.addr()
},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_locked_range(),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@.ptr.addr()),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.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 move_forward_increases_va(self)
pub proof fn move_forward_increases_va(self)
self.inv(),self.level <= NR_LEVELS,self.in_locked_range(),!self.popped_too_high,ensuresself.move_forward_owner_spec().va > self.va,Sourcepub proof fn move_forward_not_popped_too_high(self)
pub proof fn move_forward_not_popped_too_high(self)
self.inv(),self.level <= NR_LEVELS,self.in_locked_range(),ensures!self.move_forward_owner_spec().popped_too_high,Sourcepub proof fn move_forward_owner_popped_too_high_decreases(self)
pub proof fn move_forward_owner_popped_too_high_decreases(self)
self.inv(),self.level <= NR_LEVELS,self.in_locked_range(),self.popped_too_high,self.continuations[NR_LEVELS - 1].idx + 1 < NR_ENTRIES,ensuresself.move_forward_owner_spec().max_steps() + Self::max_steps_subtree(self.level as usize)
<= self.max_steps(),Variant of move_forward_owner_decreases_steps for the popped_too_high
case. Same postcondition, but precondition allows popped_too_high.
Used by the main lemma’s case 2b to handle the chain of pops that
move_forward_owner_spec does internally when popped_too_high.
Sourcepub proof fn move_forward_owner_decreases_steps(self)
pub proof fn move_forward_owner_decreases_steps(self)
self.inv(),self.level <= NR_LEVELS,self.in_locked_range(),!self.popped_too_high,self.continuations[NR_LEVELS - 1].idx + 1 < NR_ENTRIES,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 move_forward_va_is_align_up(self)
pub proof fn move_forward_va_is_align_up(self)
self.inv(),self.level <= NR_LEVELS,self.in_locked_range(),!self.popped_too_high,self.level == self.guard_level ==> self.index() + 1 < NR_ENTRIES,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 pop_level_owner_preserves_mappings(self)
pub proof fn pop_level_owner_preserves_mappings(self)
self.inv(),self.level < NR_LEVELS,self.in_locked_range(),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 move_forward_owner_preserves_mappings(self)
pub proof fn move_forward_owner_preserves_mappings(self)
self.inv(),self.in_locked_range(),ensuresself.move_forward_owner_spec()@.mappings == self@.mappings,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::pt_inv_implies_path_correct), and its mappings
all bubble up into self@.mappings; so by
PageTableOwner::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; the missing
half of the deferred paths_in_pt refactor that unmap needs.
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_locked_range(),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 cur_subtree_eq_filtered_mappings_path(self)
pub proof fn cur_subtree_eq_filtered_mappings_path(self)
self.inv(),self.in_locked_range(),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 cur_subtree_eq_filtered_mappings(self)
pub proof fn cur_subtree_eq_filtered_mappings(self)
self.inv(),self.in_locked_range(),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_locked_range(),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_upper_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_upper_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_locked_range(),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_upper_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_upper_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_locked_range(),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_locked_range(),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 view_mapping_inv(self)
pub proof fn 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().
Collapses the cursor view into a single-root view_rec and applies
view_rec_mapping_inv. Inherits the latter’s two narrow assumes
on vaddr(path) arithmetic.
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
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 map_only_children(
self,
f: FnSpec<(EntryOwner<C>, TreePath<NR_ENTRIES>), bool>,
) -> bool
pub open spec fn map_only_children( 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_only_children(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_only_children(
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@.ptr.addr()),guards1.guards == guards0.guards.remove(guard.inner.inner@.ptr.addr()),self.cur_entry_owner().is_node(),guard.inner.inner@.ptr.addr() == 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) }
}
}After a protect operation that only modifies frame.prop of the current entry,
CursorOwner::inv() and metaregion_sound are preserved.
Safety: protect changes only frame.prop and updates parent.children_perm to match.
EntryOwner::inv() is preserved (from protect postcondition).
metaregion_sound is preserved because it doesn’t use frame.prop.
rel_children holds via match_pte (from protect’s wf/node_matching postconditions).
The axiom requires only the semantic properties of the modified entry that are
checked by inv and metaregion_sound; the structural identity of other continuations
is trusted to hold from the tracked restore operations in the caller.
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(tracked &mut self)
pub proof fn do_inc_index(tracked &mut self)
old(self).inv(),old(self).level <= old(self).guard_level,old(self).in_locked_range(),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(),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),Axiom: the item reconstructed from the current frame’s physical address satisfies
clone_requires.
Safety: When metaregion_sound holds for a frame entry, the item reconstructed via
item_from_raw(pa, ...) is the original frame item. The frame’s slot permission
(owned by the cursor) has the correct address, is initialised, and its ref count is in the
valid clonable range (> 0, < REF_COUNT_MAX), so clone_requires is satisfied.
This is a trait-level axiom: C::Item::clone_requires is fully generic in the
PageTableConfig trait, so the postcondition cannot be discharged without knowing
the concrete item type. It holds for every PageTableConfig used in ostd because
item_from_raw always returns a freshly-constructed Frame<M> handle whose
Frame::<M>::clone_requires unfolds to slot-address equality, initialisation, and a
bounded ref-count — all delivered by metaregion_sound for frame entries.
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_locked_range(),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<Vaddr>
pub open spec fn locked_range(self) -> Range<Vaddr>
{
let size = page_size::<C>(self.guard_level) as nat;
let start = nat_align_down(self.prefix as nat, size);
Range {
start: start as Vaddr,
end: (start + size) as Vaddr,
}
}The guard-level slot containing the numeric prefix, with its end excluded.
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 }Sourcepub open spec fn above_locked_range(self) -> bool
pub open spec fn above_locked_range(self) -> bool
{ self.va >= self.locked_range().end }Sourcepub proof fn lemma_prefix_in_locked_range(self)
pub proof fn lemma_prefix_in_locked_range(self)
self.inv(),!self.popped_too_high,self.level < self.guard_level,ensuresself.in_locked_range(),Sourcepub proof fn lemma_in_locked_range_guard_index_eq_prefix(self)
pub proof fn lemma_in_locked_range_guard_index_eq_prefix(self)
self.inv(),1 <= self.guard_level <= NR_LEVELS,self.in_locked_range(),ensurespte_index_spec::<C>(self.va, (self.guard_level - 1 + 1) as PagingLevel)
== pte_index_spec::<C>(self.prefix, (self.guard_level - 1 + 1) as PagingLevel),The cursor and prefix select the same guard-level entry within the locked range.
Sourcepub proof fn lemma_in_locked_range_level_le_nr_levels(self)
pub proof fn lemma_in_locked_range_level_le_nr_levels(self)
self.inv(),self.in_locked_range(),!self.popped_too_high,ensuresself.level <= NR_LEVELS,Sourcepub proof fn lemma_in_locked_range_top_index_lt_top_end(self)
pub proof fn lemma_in_locked_range_top_index_lt_top_end(self)
self.inv(),self.in_locked_range(),!self.popped_too_high,ensurespte_index_spec::<C>(self.va, (NR_LEVELS - 1 + 1) as PagingLevel)
< C::TOP_LEVEL_INDEX_RANGE().end,When the cursor is in the locked range and not popped, its top-level
index is strictly less than TOP_LEVEL_INDEX_RANGE.end (the relaxed inv
only allows <=, but the operational state is strict).
Sourcepub proof fn lemma_in_locked_range_level_le_guard_level(self)
pub proof fn lemma_in_locked_range_level_le_guard_level(self)
self.inv(),self.in_locked_range(),!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 as nat
== nat_align_down(
self.prefix as nat,
page_size::<PagingConsts>(self.guard_level as PagingLevel) as nat,
),self.locked_range().start == self.prefix,self.locked_range().end == self.prefix + page_size::<PagingConsts>(self.guard_level),self.locked_range().start as nat
% page_size::<PagingConsts>(self.guard_level as PagingLevel) as nat == 0,self.locked_range().end - self.locked_range().start
== page_size::<PagingConsts>(self.guard_level as PagingLevel),The locked range spans exactly one guard-level node:
end - start == page_size(guard_level).
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,The cursor’s prefix is aligned to page_size(self.guard_level), since the
cursor invariant makes the prefix base-page-aligned and zeros all indices below
self.guard_level.
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_prefix_plus_ps_no_overflow(self)
pub proof fn lemma_prefix_plus_ps_no_overflow(self)
self.inv(),ensuresself.prefix + page_size::<PagingConsts>(self.guard_level) <= usize::MAX,prefix + page_size(guard_level) <= usize::MAX.
The prefix is aligned to a parent slot, leaving a complete fanout of slots before machine-word wrap.
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_locked_range(),1 <= level <= self.guard_level,ensuresself.va + page_size::<PagingConsts>(level) <= usize::MAX,self.va + page_size(level) <= usize::MAX for any
level <= self.guard_level, whenever the cursor is in the locked range.
Derived from the cursor invariant: in_locked_range says
self.va < locked_range().end = prefix + page_size(guard_level)
and parent-slot alignment leaves enough slack for one more configured slot,
since page_size(level) <= page_size(gl).
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 == 0,self.locked_range().start % PAGE_SIZE == 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 lemma_cur_entry_absent_not_present(self)
pub proof fn lemma_cur_entry_absent_not_present(self)
self.inv(),self.in_locked_range(),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_locked_range(),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_locked_range(),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>,
idx: usize,
guard: PageTableGuard<'rcu, C>,
) -> Self
pub open spec fn new( owner_subtree: OwnerSubtree<C>, idx: usize, guard: PageTableGuard<'rcu, C>, ) -> Self
{
let va = (C::LEADING_BITS_spec() as int
* pow2(page_table_vaddr_bits_spec::<C>() as nat)
+ idx * pow2(pte_index_bit_offset_spec::<C>(C::NR_LEVELS()) as nat)) as Vaddr;
Self {
level: C::NR_LEVELS(),
continuations: Map::empty()
.insert(
C::NR_LEVELS() - 1,
CursorContinuation::new(owner_subtree, idx, guard),
),
va,
guard_level: C::NR_LEVELS(),
prefix: va,
popped_too_high: false,
}
}Sourcepub proof fn tracked_new(
tracked owner_subtree: OwnerSubtree<C>,
idx: usize,
guard: PageTableGuard<'rcu, C>,
) -> tracked Self
pub proof fn tracked_new( tracked owner_subtree: OwnerSubtree<C>, idx: usize, guard: PageTableGuard<'rcu, C>, ) -> tracked Self
Self::new(owner_subtree, idx, 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. This follows from the
tree structure alone: as_page_table_owner_preserves_view_mappings
collapses the union-over-continuations view into a single root-rooted
view_rec, after which view_rec_disjoint_vaddrs gives pairwise
disjointness directly.
Source§impl<'rcu, C: PageTableConfig> CursorOwner<'rcu, C>
impl<'rcu, C: PageTableConfig> CursorOwner<'rcu, C>
Sourcepub proof fn split_while_huge_node_noop(self)
pub proof fn split_while_huge_node_noop(self)
self.inv(),self.in_locked_range(),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_locked_range(),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 split_while_huge_at_level_noop(self)
pub proof fn split_while_huge_at_level_noop(self)
self.inv(),self.in_locked_range(),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@.mappings
== old_view
.split_while_huge(page_size::<PagingConsts>(self.level as PagingLevel))
.mappings,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 map_children_implies(
self,
f: FnSpec<(EntryOwner<C>, TreePath<NR_ENTRIES>), bool>,
g: FnSpec<(EntryOwner<C>, TreePath<NR_ENTRIES>), bool>,
)
pub proof fn 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 cur_entry_node_implies_level_gt_1(self)
pub proof fn 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 do_zero_below_level(tracked &mut self)
pub proof fn do_zero_below_level(tracked &mut self)
old(self).inv(),old(self).level <= old(self).guard_level,ensures*final(self) == old(self).zero_below_level(),final(self).inv(),Sourcepub proof fn zero_preserves_all_but_va(self)
pub proof fn zero_preserves_all_but_va(self)
self.zero_below_level().level == self.level,self.zero_below_level().continuations == self.continuations,self.zero_below_level().guard_level == self.guard_level,self.zero_below_level().prefix == self.prefix,self.zero_below_level().popped_too_high == self.popped_too_high,Sourcepub proof fn lemma_inc_index_va(self)
pub proof fn lemma_inc_index_va(self)
self.inv(),self.in_locked_range(),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_locked_range(),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_locked_range(),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_upper_bits_spec::<C>(new_va) == vaddr_upper_bits_spec::<C>(self.prefix_vaddr()),self.locked_range().start <= new_va < self.locked_range().end,ensurespte_index_spec::<C>(new_va, self.guard_level)
== pte_index_spec::<C>(self.prefix, self.guard_level),forall |i: int| {
self.guard_level - 1 <= i < C::NR_LEVELS()
==> (#[trigger] pte_index_spec::<C>(new_va, (i + 1) as PagingLevel))
== pte_index_spec::<C>(self.prefix_vaddr(), (i + 1) as PagingLevel)
},Addresses in the locked block share the guard-level and higher indices.
Sourcepub proof fn lemma_cur_pte_index(self)
pub proof fn lemma_cur_pte_index(self)
self.inv(),self.in_locked_range(),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_upper_bits_spec::<C>(new_va) == vaddr_upper_bits_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))
== pte_index_spec::<C>(old(self).cur_va(), (i + 1) as PagingLevel)
},old(self).locked_range().start <= new_va < old(self).locked_range().end,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
&&& C::TOP_LEVEL_INDEX_RANGE().start
<= pte_index_spec::<C>(self.va, (NR_LEVELS - 1 + 1) as PagingLevel)
&&& pte_index_spec::<C>(self.va, (NR_LEVELS - 1 + 1) as PagingLevel)
<= C::TOP_LEVEL_INDEX_RANGE().end
&&& self.in_locked_range() || self.above_locked_range()
&&& self.popped_too_high ==> self.level >= self.guard_level
&&& !self.popped_too_high
==> self.level <= self.guard_level || self.above_locked_range()
&&& 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
&&& forall |i: int| {
1 <= i <= self.guard_level
==> #[trigger] pte_index_spec::<C>(self.prefix, i as PagingLevel) == 0
}
&&& pte_index_spec::<C>(self.prefix, (NR_LEVELS - 1 + 1) as PagingLevel)
>= C::TOP_LEVEL_INDEX_RANGE().start
&&& pte_index_spec::<C>(self.prefix, (NR_LEVELS - 1 + 1) as PagingLevel)
< C::TOP_LEVEL_INDEX_RANGE().end
&&& pte_index_spec::<C>(self.prefix, (NR_LEVELS - 1 + 1) as PagingLevel) + 1
< NR_ENTRIES
&&& self.locked_range().end <= vaddr_range_spec::<C>().end + 1
&&& self.locked_range().end <= C::LOCKED_END_BOUND_spec()
&&& vaddr_upper_bits_spec::<C>(self.va) == vaddr_upper_bits_spec::<C>(self.prefix)
&&& vaddr_upper_bits_spec::<C>(self.prefix) == C::LEADING_BITS_spec()
&&& 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.popped_too_high && self.guard_level >= 1 && self.level < self.guard_level
==> pte_index_spec::<C>(self.va, (self.guard_level - 1 + 1) as PagingLevel)
== pte_index_spec::<
C,
>(self.prefix, (self.guard_level - 1 + 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@.ptr.addr()
!= self.continuations[3].guard.inner.inner@.ptr.addr()
&&& 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@.ptr.addr()
!= self.continuations[2].guard.inner.inner@.ptr.addr()
&&& self.continuations[1].guard.inner.inner@.ptr.addr()
!= self.continuations[3].guard.inner.inner@.ptr.addr()
&&& 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@.ptr.addr()
!= self.continuations[1].guard.inner.inner@.ptr.addr()
&&& self.continuations[0].guard.inner.inner@.ptr.addr()
!= self.continuations[2].guard.inner.inner@.ptr.addr()
&&& self.continuations[0].guard.inner.inner@.ptr.addr()
!= self.continuations[3].guard.inner.inner@.ptr.addr()
&&& 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.