pub struct EntryOwner<C: PageTableConfig> {
pub node: Option<NodeOwner<C>>,
pub frame: Option<FrameEntryOwner>,
pub locked: Option<Ghost<Seq<FrameView<C>>>>,
pub absent: bool,
pub in_scope: bool,
pub path: TreePath<NR_ENTRIES>,
pub parent_level: PagingLevel,
}Fields§
§node: Option<NodeOwner<C>>§frame: Option<FrameEntryOwner>§locked: Option<Ghost<Seq<FrameView<C>>>>§absent: bool§in_scope: bool§path: TreePath<NR_ENTRIES>§parent_level: PagingLevelImplementations§
Source§impl<C: PageTableConfig> EntryOwner<C>
impl<C: PageTableConfig> EntryOwner<C>
Sourcepub open spec fn from_pte_regions_spec(
self,
regions: MetaRegionOwners,
) -> MetaRegionOwners
pub open spec fn from_pte_regions_spec( self, regions: MetaRegionOwners, ) -> MetaRegionOwners
{
if self.is_node() {
let index = frame_to_index(self.meta_slot_paddr().unwrap());
let old_slot = regions.slot_owners[index];
let new_slot = MetaSlotOwner {
raw_count: 0usize,
..old_slot
};
MetaRegionOwners {
slots: regions.slots.insert(index, self.node.unwrap().meta_perm.points_to),
slot_owners: regions.slot_owners.insert(index, new_slot),
}
} else {
regions
}
}Sourcepub open spec fn into_pte_regions_spec(
self,
regions: MetaRegionOwners,
) -> MetaRegionOwners
pub open spec fn into_pte_regions_spec( self, regions: MetaRegionOwners, ) -> MetaRegionOwners
{
if self.is_node() {
let index = frame_to_index(self.meta_slot_paddr().unwrap());
let old_slot = regions.slot_owners[index];
let new_slot = MetaSlotOwner {
raw_count: (old_slot.raw_count + 1) as usize,
..old_slot
};
MetaRegionOwners {
slots: regions.slots,
slot_owners: regions.slot_owners.insert(index, new_slot),
..regions
}
} else {
regions
}
}Sourcepub open spec fn into_pte_owner_spec(self) -> EntryOwner<C>
pub open spec fn into_pte_owner_spec(self) -> EntryOwner<C>
{
EntryOwner {
in_scope: false,
..self
}
}Sourcepub open spec fn from_pte_owner_spec(self) -> EntryOwner<C>
pub open spec fn from_pte_owner_spec(self) -> EntryOwner<C>
{
EntryOwner {
in_scope: true,
..self
}
}Sourcepub open spec fn pte_invariants(self, pte: C::E, regions: MetaRegionOwners) -> bool
pub open spec fn pte_invariants(self, pte: C::E, regions: MetaRegionOwners) -> bool
{
&&& self.inv()
&&& regions.inv()
&&& self.match_pte(pte, self.parent_level)
&&& self.relate_region(regions)
&&& !self.in_scope
}This is equivalent to the other invariants relations, combining the inv predicates for each
object and the well-formedness relations between them.
Source§impl<C: PageTableConfig> EntryOwner<C>
impl<C: PageTableConfig> EntryOwner<C>
Sourcepub open spec fn new_absent_spec(
path: TreePath<NR_ENTRIES>,
parent_level: PagingLevel,
) -> Self
pub open spec fn new_absent_spec( path: TreePath<NR_ENTRIES>, parent_level: PagingLevel, ) -> Self
{
EntryOwner {
node: None,
frame: None,
locked: None,
absent: true,
in_scope: true,
path,
parent_level,
}
}Sourcepub open spec fn new_frame_spec(
paddr: Paddr,
path: TreePath<NR_ENTRIES>,
parent_level: PagingLevel,
prop: PageProperty,
slot_perm: PointsTo<MetaSlot>,
) -> Self
pub open spec fn new_frame_spec( paddr: Paddr, path: TreePath<NR_ENTRIES>, parent_level: PagingLevel, prop: PageProperty, slot_perm: PointsTo<MetaSlot>, ) -> Self
{
EntryOwner {
node: None,
frame: Some(FrameEntryOwner {
mapped_pa: paddr,
size: page_size(parent_level),
prop,
slot_perm,
}),
locked: None,
absent: false,
in_scope: true,
path,
parent_level,
}
}Sourcepub open spec fn new_node_spec(node: NodeOwner<C>, path: TreePath<NR_ENTRIES>) -> Self
pub open spec fn new_node_spec(node: NodeOwner<C>, path: TreePath<NR_ENTRIES>) -> Self
{
EntryOwner {
node: Some(node),
frame: None,
locked: None,
absent: false,
in_scope: true,
path,
parent_level: (node.level + 1) as PagingLevel,
}
}Sourcepub proof fn new_absent(path: TreePath<NR_ENTRIES>, parent_level: PagingLevel) -> tracked Self
pub proof fn new_absent(path: TreePath<NR_ENTRIES>, parent_level: PagingLevel) -> tracked Self
Self::new_absent_spec(path, parent_level),Sourcepub proof fn new_frame(
paddr: Paddr,
path: TreePath<NR_ENTRIES>,
parent_level: PagingLevel,
prop: PageProperty,
tracked slot_perm: PointsTo<MetaSlot>,
) -> tracked Self
pub proof fn new_frame( paddr: Paddr, path: TreePath<NR_ENTRIES>, parent_level: PagingLevel, prop: PageProperty, tracked slot_perm: PointsTo<MetaSlot>, ) -> tracked Self
Self::new_frame_spec(paddr, path, parent_level, prop, slot_perm),Sourcepub proof fn placeholder_slot_perm(
paddr: Paddr,
tracked regions: &MetaRegionOwners,
) -> tracked res : PointsTo<MetaSlot>
pub proof fn placeholder_slot_perm( paddr: Paddr, tracked regions: &MetaRegionOwners, ) -> tracked res : PointsTo<MetaSlot>
regions.inv(),paddr % PAGE_SIZE == 0,paddr < MAX_PADDR,ensuresres.addr() == meta_addr(frame_to_index(paddr)),res.is_init(),res.value().wf(regions.slot_owners[frame_to_index(paddr)]),regions.slot_owners[frame_to_index(paddr)].inner_perms.ref_count.value()
!= REF_COUNT_UNUSED,regions.slot_owners[frame_to_index(paddr)].path_if_in_pt is None,Produces a slot permission for a frame address without requiring it from regions.slots.
Used as a placeholder in cases (e.g. huge-page split) where the sub-frame slot perms
are not yet fully tracked. Replace with real perm threading when the split path is verified.
Sourcepub proof fn new_node(node: NodeOwner<C>, path: TreePath<NR_ENTRIES>) -> tracked Self
pub proof fn new_node(node: NodeOwner<C>, path: TreePath<NR_ENTRIES>) -> tracked Self
Self::new_node_spec(node, path),Sourcepub proof fn new_untracked_frame(
paddr: Paddr,
parent_level: PagingLevel,
prop: PageProperty,
) -> tracked res : Self
pub proof fn new_untracked_frame( paddr: Paddr, parent_level: PagingLevel, prop: PageProperty, ) -> tracked res : Self
paddr % PAGE_SIZE == 0,paddr < MAX_PADDR,1 <= parent_level,parent_level <= NR_LEVELS,ensuresres.is_frame(),res.frame.unwrap().mapped_pa == paddr,res.frame.unwrap().prop == prop,res.frame.unwrap().size == page_size(parent_level),res.parent_level == parent_level,res.path.inv(),res.in_scope,res.inv(),crate::mm::page_table::Child::<C>::Frame(paddr, parent_level, prop).wf(res),Creates a ghost entry owner for mapping an untracked (device memory) frame.
Unlike new_frame, this does not consume a slot permission from the meta region,
since device memory PAs are outside the tracked frame allocator.
The actual mapping correctness is guaranteed by the caller’s unsafe contract.
The requires reflect properties guaranteed by collect_largest_pages postconditions,
so this axiom is only ever called with values that satisfy them.
Sourcepub open spec fn match_pte(self, pte: C::E, parent_level: PagingLevel) -> bool
pub open spec fn match_pte(self, pte: C::E, parent_level: PagingLevel) -> bool
{
&&& pte.paddr() % PAGE_SIZE == 0
&&& pte.paddr() < MAX_PADDR
&&& !pte.is_present()
==> {
&&& self.is_absent()
&&& parent_level > 1 ==> !pte.is_last(parent_level)
}
&&& pte.is_present() && !pte.is_last(parent_level)
==> {
&&& self.is_node()
&&& meta_to_frame(self.node.unwrap().meta_perm.addr()) == pte.paddr()
}
&&& pte.is_present() && pte.is_last(parent_level)
==> {
&&& self.is_frame()
&&& self.frame.unwrap().mapped_pa == pte.paddr()
&&& self.frame.unwrap().prop == pte.prop()
}
}Sourcepub proof fn absent_match_pte(owner: Self, pte: C::E, parent_level: PagingLevel)
pub proof fn absent_match_pte(owner: Self, pte: C::E, parent_level: PagingLevel)
owner.is_absent(),pte == C::E::new_absent_spec(),pte.paddr() % PAGE_SIZE == 0,pte.paddr() < MAX_PADDR,ensuresowner.match_pte(pte, parent_level),When owner is absent and pte is the absent PTE with valid paddr, match_pte holds.
Sourcepub proof fn last_pte_implies_frame_match(self, pte: C::E, parent_level: PagingLevel)
pub proof fn last_pte_implies_frame_match(self, pte: C::E, parent_level: PagingLevel)
self.inv(),self.match_pte(pte, parent_level),1 < parent_level,pte.is_last(parent_level),ensuresself.is_frame(),self.frame.unwrap().mapped_pa == pte.paddr(),self.frame.unwrap().prop == pte.prop(),Sourcepub proof fn huge_frame_split_child_at(self, regions: MetaRegionOwners, idx: usize)
pub proof fn huge_frame_split_child_at(self, regions: MetaRegionOwners, idx: usize)
self.inv(),self.is_frame(),regions.inv(),1 < self.parent_level < NR_LEVELS,idx < NR_ENTRIES,ensuresself.frame.unwrap().mapped_pa + idx * page_size((self.parent_level - 1) as PagingLevel)
< MAX_PADDR,((self.frame.unwrap().mapped_pa
+ idx * page_size((self.parent_level - 1) as PagingLevel)) as Paddr)
% page_size((self.parent_level - 1) as PagingLevel) == 0,((self.frame.unwrap().mapped_pa
+ idx * page_size((self.parent_level - 1) as PagingLevel)) as Paddr)
+ page_size((self.parent_level - 1) as PagingLevel) <= MAX_PADDR,((self.frame.unwrap().mapped_pa
+ idx * page_size((self.parent_level - 1) as PagingLevel)) as Paddr) % PAGE_SIZE == 0,Sourcepub open spec fn expected_raw_count(self) -> usize
pub open spec fn expected_raw_count(self) -> usize
{ if self.in_scope { 0 } else { 1 } }Sourcepub open spec fn relate_region(self, regions: MetaRegionOwners) -> bool
pub open spec fn relate_region(self, regions: MetaRegionOwners) -> bool
{
if self.is_node() {
let idx = frame_to_index(self.meta_slot_paddr().unwrap());
&&& regions.slot_owners[idx].inner_perms.ref_count.value() != REF_COUNT_UNUSED
&&& regions.slot_owners[idx].raw_count == self.expected_raw_count()
&&& regions.slot_owners[idx].self_addr == self.node.unwrap().meta_perm.addr()
&&& self.node.unwrap().meta_perm.points_to.value().wf(regions.slot_owners[idx])
&&& regions.slot_owners[idx].path_if_in_pt is Some
==> regions.slot_owners[idx].path_if_in_pt.unwrap() == self.path
} else if self.is_frame() {
let idx = frame_to_index(self.meta_slot_paddr().unwrap());
&&& self.frame.unwrap().slot_perm.addr() == meta_addr(idx)
&&& self.frame.unwrap().slot_perm.is_init()
&&& self.frame.unwrap().slot_perm.value().wf(regions.slot_owners[idx])
&&& regions.slot_owners[idx].inner_perms.ref_count.value() != REF_COUNT_UNUSED
&&& regions.slot_owners[idx].path_if_in_pt is Some
==> regions.slot_owners[idx].path_if_in_pt.unwrap() == self.path
} else {
true
}
}Sourcepub proof fn active_entry_not_in_free_pool(
entry: Self,
regions: MetaRegionOwners,
free_idx: usize,
)
pub proof fn active_entry_not_in_free_pool( entry: Self, regions: MetaRegionOwners, free_idx: usize, )
regions.inv(),entry.inv(),entry.relate_region(regions),regions.slots.contains_key(free_idx),entry.meta_slot_paddr() is Some,ensuresframe_to_index(entry.meta_slot_paddr().unwrap()) != free_idx,PointsTo uniqueness: if meta slot free_idx is in the free pool (regions.slots),
no active page table entry can own a PointsTo at the same slot address.
Justified by Verus’s linear ownership of PointsTo<MetaSlot>:
the slot’s PointsTo is either in regions.slots OR held by an active entry, never both.
Sourcepub open spec fn meta_slot_paddr(self) -> Option<Paddr>
pub open spec fn meta_slot_paddr(self) -> Option<Paddr>
{
if self.is_node() {
Some(meta_to_frame(self.node.unwrap().meta_perm.addr()))
} else if self.is_frame() {
Some(self.frame.unwrap().mapped_pa)
} else {
None
}
}Sourcepub open spec fn meta_slot_paddr_neq(self, other: Self) -> bool
pub open spec fn meta_slot_paddr_neq(self, other: Self) -> bool
{
self.meta_slot_paddr() is Some
==> (other.meta_slot_paddr() is Some
==> self.meta_slot_paddr().unwrap() != other.meta_slot_paddr().unwrap())
}Sourcepub proof fn relate_region_slot_owners_only(
self,
r0: MetaRegionOwners,
r1: MetaRegionOwners,
)
pub proof fn relate_region_slot_owners_only( self, r0: MetaRegionOwners, r1: MetaRegionOwners, )
self.relate_region(r0),r0.slot_owners == r1.slot_owners,ensuresself.relate_region(r1),relate_region only uses regions.slot_owners, not regions.slots.
So if two MetaRegionOwners have the same slot_owners, relate_region transfers.
Sourcepub proof fn relate_region_one_slot_changed(
self,
r0: MetaRegionOwners,
r1: MetaRegionOwners,
changed_idx: usize,
)
pub proof fn relate_region_one_slot_changed( self, r0: MetaRegionOwners, r1: MetaRegionOwners, changed_idx: usize, )
self.relate_region(r0),forall |i: usize| i != changed_idx ==> r0.slot_owners[i] == r1.slot_owners[i],r0.slot_owners.dom() =~= r1.slot_owners.dom(),self.meta_slot_paddr() is Some
==> frame_to_index(self.meta_slot_paddr().unwrap()) != changed_idx,ensuresself.relate_region(r1),If relate_region(r0) holds and r1 differs from r0 only at one slot index
that this entry does not reference, then relate_region(r1) also holds.
Sourcepub proof fn same_paddr_implies_same_path(
self,
other: Self,
regions: MetaRegionOwners,
)
pub proof fn same_paddr_implies_same_path( self, other: Self, regions: MetaRegionOwners, )
self.meta_slot_paddr() is Some,self.meta_slot_paddr() == other.meta_slot_paddr(),self.relate_region(regions),other.relate_region(regions),regions
.slot_owners[frame_to_index(self.meta_slot_paddr().unwrap())]
.path_if_in_pt is Some,ensuresself.path == other.path,Under relate_region + path_if_in_pt is Some, two entries with the same physical
address must have the same path. Equivalently, different paths ↔ different paddrs.
This is the fundamental paddr-uniqueness invariant: path_if_in_pt encodes the
unique path for each physical address in the page table.
Sourcepub proof fn relate_region_rc_value_changed(
self,
r0: MetaRegionOwners,
r1: MetaRegionOwners,
)
pub proof fn relate_region_rc_value_changed( self, r0: MetaRegionOwners, r1: MetaRegionOwners, )
self.relate_region(r0),self.meta_slot_paddr() is Some,({
let idx = frame_to_index(self.meta_slot_paddr().unwrap());
&&& r1.slot_owners[idx].inner_perms.ref_count.id()
== r0.slot_owners[idx].inner_perms.ref_count.id()
&&& r1.slot_owners[idx].inner_perms.ref_count.value()
!= crate::specs::mm::frame::meta_owners::REF_COUNT_UNUSED
&&& r1.slot_owners[idx].inner_perms.storage
== r0.slot_owners[idx].inner_perms.storage
&&& r1.slot_owners[idx].inner_perms.vtable_ptr
== r0.slot_owners[idx].inner_perms.vtable_ptr
&&& r1.slot_owners[idx].inner_perms.in_list
== r0.slot_owners[idx].inner_perms.in_list
&&& r1.slot_owners[idx].path_if_in_pt == r0.slot_owners[idx].path_if_in_pt
&&& r1.slot_owners[idx].self_addr == r0.slot_owners[idx].self_addr
&&& r1.slot_owners[idx].raw_count == r0.slot_owners[idx].raw_count
}),ensuresself.relate_region(r1),relate_region is preserved when only ref_count.value() changes at this entry’s slot.
Sourcepub proof fn nodes_different_paths_different_addrs(
self,
other: Self,
regions: MetaRegionOwners,
)
pub proof fn nodes_different_paths_different_addrs( self, other: Self, regions: MetaRegionOwners, )
self.is_node(),other.is_node(),self.relate_region(regions),self.meta_slot_paddr() is Some
==> regions
.slot_owners[frame_to_index(self.meta_slot_paddr().unwrap())]
.path_if_in_pt is Some,other.meta_slot_paddr() is Some
==> regions
.slot_owners[frame_to_index(other.meta_slot_paddr().unwrap())]
.path_if_in_pt is Some,other.relate_region(regions),self.path != other.path,ensuresself.node.unwrap().meta_perm.addr() != other.node.unwrap().meta_perm.addr(),Two nodes satisfying relate_region with the same regions have different addresses if and only if they have different paths.
Source§impl<C: PageTableConfig> EntryOwner<C>
impl<C: PageTableConfig> EntryOwner<C>
Sourcepub open spec fn inv_base(self) -> bool
pub open spec fn inv_base(self) -> bool
{
&&& self.node is Some
==> {
&&& self.frame is None
&&& self.locked is None
&&& self.node.unwrap().inv()
&&& !self.absent
}
&&& self.frame is Some
==> {
&&& self.node is None
&&& self.locked is None
&&& !self.absent
&&& self.frame.unwrap().mapped_pa % PAGE_SIZE == 0
&&& self.frame.unwrap().mapped_pa < MAX_PADDR
&&& self.frame.unwrap().size == page_size(self.parent_level)
&&& self.frame.unwrap().mapped_pa % page_size(self.parent_level) == 0
&&& self.frame.unwrap().mapped_pa + page_size(self.parent_level) <= MAX_PADDR
}
&&& self.locked is Some
==> {
&&& self.frame is None
&&& self.node is None
&&& !self.absent
}
&&& self.path.inv()
}Structural invariant without !in_scope. Used by Child::invariants
for entries that have been taken out of the tree (in_scope == true).
Trait Implementations§
Source§impl<C: PageTableConfig> Inv for EntryOwner<C>
impl<C: PageTableConfig> Inv for EntryOwner<C>
Source§impl<C: PageTableConfig> InvView for EntryOwner<C>
impl<C: PageTableConfig> InvView for EntryOwner<C>
Source§proof fn view_preserves_inv(self)
proof fn view_preserves_inv(self)
Source§impl<C: PageTableConfig, const L: usize> TreeNodeValue<L> for EntryOwner<C>
impl<C: PageTableConfig, const L: usize> TreeNodeValue<L> for EntryOwner<C>
Source§open spec fn default(lv: nat) -> Self
open spec fn default(lv: nat) -> Self
{
Self {
in_scope: false,
path: TreePath::new(Seq::empty()),
parent_level: (INC_LEVELS - lv) as PagingLevel,
node: None,
frame: None,
locked: None,
absent: true,
}
}Source§proof fn default_preserves_inv()
proof fn default_preserves_inv()
Source§proof fn default_preserves_la_inv()
proof fn default_preserves_la_inv()
Source§open spec fn rel_children(self, i: int, child: Option<Self>) -> bool
open spec fn rel_children(self, i: int, child: Option<Self>) -> bool
{
if self.is_node() {
&&& child is Some
&&& child.unwrap().path.len() == self.node.unwrap().tree_level + 1
&&& child
.unwrap()
.match_pte(
self.node.unwrap().children_perm.value()[i],
self.node.unwrap().level,
)
&&& child.unwrap().path == self.path.push_tail(i as usize)
} else {
&&& child is None
}
}Source§proof fn default_preserves_rel_children(self, lv: nat)
proof fn default_preserves_rel_children(self, lv: nat)
Source§impl<C: PageTableConfig> View for EntryOwner<C>
impl<C: PageTableConfig> View for EntryOwner<C>
Source§open spec fn view(&self) -> <Self as View>::V
open spec fn view(&self) -> <Self as View>::V
{
if let Some(frame) = self.frame {
EntryView::Leaf {
leaf: LeafPageTableEntryView {
map_va: vaddr(self.path) as int,
map_to_pa: frame.mapped_pa as int,
level: self.path.len() as u8,
prop: frame.prop,
phantom: PhantomData,
},
}
} else if let Some(node) = self.node {
EntryView::Intermediate {
node: IntermediatePageTableEntryView {
map_va: vaddr(self.path) as int,
map_to_pa: meta_to_frame(node.meta_perm.addr()) as int,
level: self.path.len() as u8,
phantom: PhantomData,
},
}
} else if let Some(view) = self.locked {
EntryView::LockedSubtree {
views: view@,
}
} else {
EntryView::Absent
}
}