ostd/specs/mm/page_table/cursor/
cursor_fn_specs.rs1use vstd::prelude::*;
2
3use crate::specs::{
4 arch::{NR_LEVELS, PAGE_SIZE},
5 mm::{
6 frame::{
7 mapping::frame_to_index,
8 meta_owners::{PageUsage, is_mmio_paddr},
9 meta_region_owners::MetaRegionOwners,
10 },
11 page_table::{cursor::owners::*, is_valid_range_spec, *},
12 },
13 task::InAtomicMode,
14};
15
16use crate::arch::mm::PagingConsts;
17use crate::mm::{
18 PagingConstsTrait, Vaddr,
19 frame::meta::{REF_COUNT_MAX, REF_COUNT_UNUSED},
20 page_table::*,
21};
22use core::ops::Range;
23
24verus! {
25
26impl<'rcu, C: PageTableConfig, A: InAtomicMode> Cursor<'rcu, C, A> {
28 pub open spec fn cursor_new_success_conditions(va: Range<Vaddr>) -> bool {
29 &&& va.start < va.end
30 &&& va.start % C::BASE_PAGE_SIZE() == 0
31 &&& va.end % C::BASE_PAGE_SIZE() == 0
32 &&& is_valid_range_spec::<C>(va)
33 }
34
35 pub open spec fn invariants(
36 self,
37 owner: CursorOwner<'rcu, C>,
38 regions: MetaRegionOwners,
39 guards: Guards,
40 ) -> bool {
41 &&& owner.inv()
42 &&& self.inv()
43 &&& self.wf(owner)
44 &&& regions.inv()
45 &&& owner.children_not_locked(guards)
46 &&& owner.nodes_locked(guards)
47 &&& owner.metaregion_sound(regions)
48 &&& !owner.popped_too_high
49 }
50
51 pub open spec fn query_some_condition(self, owner: CursorOwner<'rcu, C>) -> bool {
52 owner@.present()
53 }
54
55 pub open spec fn query_panic_condition(
68 self,
69 owner: CursorOwner<'rcu, C>,
70 regions: MetaRegionOwners,
71 ) -> bool {
72 let pa = owner@.query_mapping().pa_range.start;
73 let idx = frame_to_index(pa);
74 &&& self.barrier_va.start <= self.va < self.barrier_va.end
75 &&& owner@.present()
76 &&& !is_mmio_paddr(pa)
77 &&& regions.ref_count(idx) >= REF_COUNT_MAX
78 }
79
80 pub open spec fn query_some_ensures(
81 self,
82 owner: CursorOwner<'rcu, C>,
83 res: PagesState<C>,
84 ) -> bool {
85 &&& owner.cur_va_range() == res.0
86 &&& res.1 is Some
87 &&& {
88 let qr = owner@.query_range();
89 owner@.query_item_spec(res.1->0) == Some(qr.start as Vaddr..qr.end as Vaddr)
90 }
91 }
92
93 pub open spec fn query_none_ensures(
94 self,
95 owner: CursorOwner<'rcu, C>,
96 res: PagesState<C>,
97 ) -> bool {
98 &&& res.1 is None
99 }
100
101 pub open spec fn jump_node_holds(self, lv: PagingLevel, va: Vaddr) -> bool {
107 let nstart = nat_align_down(
108 self.va as nat,
109 page_size::<PagingConsts>((lv + 1) as PagingLevel) as nat,
110 );
111 &&& nstart <= va as nat
112 &&& (va as nat) - nstart < page_size::<PagingConsts>((lv + 1) as PagingLevel) as nat
113 }
114
115 pub open spec fn jump_panic_condition(self, va: Vaddr) -> bool {
125 ||| va % PAGE_SIZE != 0
126 ||| (self.barrier_va.start <= va < self.barrier_va.end && forall|lv: PagingLevel|
127 #![trigger self.jump_node_holds(lv, va)]
128 self.level <= lv <= self.guard_level ==> !self.jump_node_holds(lv, va))
129 }
130
131 pub open spec fn find_next_panic_condition(self, len: usize) -> bool {
132 ||| len % PAGE_SIZE != 0
133 ||| self.va + len > self.barrier_va.end
134 }
135}
136
137impl<'rcu, C: PageTableConfig, A: InAtomicMode> CursorMut<'rcu, C, A> {
139 pub open spec fn map_panic_conditions(self, item: C::Item) -> bool {
147 ||| self.0.va >= self.0.barrier_va.end
148 ||| C::item_into_raw(item).1 > C::HIGHEST_TRANSLATION_LEVEL()
149 ||| C::item_into_raw(item).1 >= self.0.guard_level
150 ||| (!C::TOP_LEVEL_CAN_UNMAP_spec() && C::item_into_raw(item).1 >= NR_LEVELS)
151 ||| self.0.va % page_size::<PagingConsts>(C::item_into_raw(item).1) != 0
152 ||| self.0.va + page_size::<PagingConsts>(C::item_into_raw(item).1) > self.0.barrier_va.end
153 }
154
155 pub open spec fn item_wf(self, item: C::Item, entry_owner: EntryOwner<C>) -> bool {
157 let (paddr, level, prop, _perm) = C::item_into_raw(item);
158 &&& C::item_well_formed(item)
159 &&& entry_owner.inv()
160 &&& (entry_owner.is_absent() || Child::Frame(paddr, level, prop).wf(entry_owner))
161 }
162
163 pub open spec fn item_not_mapped(item: C::Item, regions: MetaRegionOwners) -> bool {
164 let (pa, level, prop, perm) = C::item_into_raw(item);
165 let size = page_size::<PagingConsts>(level);
166 let range = pa..(pa + size) as usize;
167 regions.paddr_range_not_mapped(range)
168 }
169
170 pub open spec fn item_slot_in_regions_except_perm(
171 item: C::Item,
172 regions: MetaRegionOwners,
173 ) -> bool {
174 let (pa, level, prop, perm) = C::item_into_raw(item);
175 let idx = frame_to_index(pa);
176 &&& regions.contains(idx)
177 &&& regions.slot_owners[idx].usage !is PageTable
178 &&& regions.ref_count(idx)
179 != REF_COUNT_UNUSED
180 &&& perm@ is Some ==> regions.ref_count(idx)
182 > 0
183 &&& perm@ is Some ==> regions.ref_count(idx)
188 <= REF_COUNT_MAX
189 &&& level > 1 ==> {
191 forall|j: usize|
192 #![trigger frame_to_index((pa + j * PAGE_SIZE) as usize)]
193 0 < j < page_size::<PagingConsts>(level) / PAGE_SIZE ==> {
194 let sub_idx = frame_to_index((pa + j * PAGE_SIZE) as usize);
195 &&& regions.contains(sub_idx)
196 &&& perm@ is Some ==> regions.ref_count(sub_idx) != REF_COUNT_UNUSED
197 &&& perm@ is Some ==> regions.ref_count(sub_idx)
198 > 0
199 &&& perm@ is Some ==> regions.ref_count(sub_idx) <= REF_COUNT_MAX
202 }
203 }
204 }
205
206 pub closed spec fn item_slot_in_regions(item: C::Item, regions: MetaRegionOwners) -> bool {
207 let (pa, _level, _prop, perm) = C::item_into_raw(item);
208 &&& Self::item_slot_in_regions_except_perm(item, regions)
209 &&& C::perm_well_formed_with_region(pa, perm, regions)
210 }
211
212 pub proof fn item_slot_in_regions_facts(item: C::Item, regions: MetaRegionOwners)
213 requires
214 Self::item_slot_in_regions(item, regions),
215 ensures
216 Self::item_slot_in_regions_except_perm(item, regions),
217 C::perm_well_formed_with_region(
218 C::item_into_raw(item).0,
219 C::item_into_raw(item).3,
220 regions,
221 ),
222 {
223 }
224
225 pub proof fn item_slot_in_regions_preserved(
226 item: C::Item,
227 regions0: MetaRegionOwners,
228 regions1: MetaRegionOwners,
229 )
230 requires
231 Self::item_slot_in_regions(item, regions0),
232 Self::item_slot_in_regions_except_perm(item, regions1),
233 regions0.slots[frame_to_index(C::item_into_raw(item).0)]
234 == regions1.slots[frame_to_index(C::item_into_raw(item).0)],
235 regions0.slot_owners[frame_to_index(C::item_into_raw(item).0)].metadata_perm.id()
236 == regions1.slot_owners[frame_to_index(
237 C::item_into_raw(item).0,
238 )].metadata_perm.id(),
239 ensures
240 Self::item_slot_in_regions(item, regions1),
241 {
242 C::lemma_perm_well_formed_with_region_preserved(
243 C::item_into_raw(item).0,
244 C::item_into_raw(item).3,
245 regions0,
246 regions1,
247 );
248 }
249
250 pub proof fn all_item_slots_preserved(regions0: MetaRegionOwners, regions1: MetaRegionOwners)
251 requires
252 forall|idx: int| regions0.contains(idx) ==> #[trigger] regions1.contains(idx),
253 forall|idx: int|
254 regions0.ref_count(idx) != REF_COUNT_UNUSED ==> #[trigger] regions1.slot_owners[idx]
255 == regions0.slot_owners[idx],
256 forall|idx: int|
257 regions0.contains(idx) && regions0.ref_count(idx) != REF_COUNT_UNUSED
258 ==> #[trigger] regions1.slots[idx] == regions0.slots[idx],
259 ensures
260 forall|item: C::Item|
261 #![trigger Self::item_slot_in_regions(item, regions0)]
262 Self::item_slot_in_regions(item, regions0) ==> Self::item_slot_in_regions(
263 item,
264 regions1,
265 ),
266 {
267 assert forall|item: C::Item|
268 Self::item_slot_in_regions(
269 item,
270 regions0,
271 ) implies #[trigger] Self::item_slot_in_regions(item, regions1) by {
272 let (pa, level, _prop, perm) = C::item_into_raw(item);
273 let idx = frame_to_index(pa);
274 assert(regions0.ref_count(idx) != REF_COUNT_UNUSED);
275 assert(Self::item_slot_in_regions_except_perm(item, regions1)) by {
276 if level > 1 {
277 assert forall|j: usize|
278 #![trigger frame_to_index((pa + j * PAGE_SIZE) as usize)]
279 0 < j < page_size::<PagingConsts>(level) / PAGE_SIZE implies {
280 let sub_idx = frame_to_index((pa + j * PAGE_SIZE) as usize);
281 &&& regions1.contains(sub_idx)
282 &&& perm@ is Some ==> regions1.ref_count(sub_idx) != REF_COUNT_UNUSED
283 &&& perm@ is Some ==> regions1.ref_count(sub_idx) > 0
284 &&& perm@ is Some ==> regions1.ref_count(sub_idx) <= REF_COUNT_MAX
285 } by {};
286 }
287 };
288 Self::item_slot_in_regions_preserved(item, regions0, regions1);
289 };
290 }
291
292 pub open spec fn map_item_ensures(
293 self,
294 item: C::Item,
295 old_view: CursorView<C>,
296 new_view: CursorView<C>,
297 ) -> bool {
298 let (pa, level, prop, _perm) = C::item_into_raw(item);
299 new_view == old_view.map_spec(pa, page_size::<PagingConsts>(level), prop)
300 }
301}
302
303}