Skip to main content

ostd/specs/mm/page_table/
vaddr_range_proofs.rs

1// SPDX-License-Identifier: MPL-2.0
2//! Bounded-arithmetic proofs for `vaddr_range`'s body.
3//!
4//! Factored into a leaf module because the `nonlinear_arith` tactic
5//! triggers a Verus internal panic when used in `page_table/mod.rs`
6//! alongside `largest_pages` (which has an `impl Iterator` return type).
7//! Same workaround pattern as the older `vaddr_range_bv_lemmas`.
8use vstd::{
9    arithmetic::power2::{lemma_pow2_adds, lemma_pow2_pos, pow2},
10    prelude::*,
11};
12
13use crate::specs::mm::page_table::pte_index_bit_offset_spec;
14
15use crate::mm::{PagingConstsTrait, Vaddr, page_table::PageTableConfig};
16
17verus! {
18
19/// Facts needed to turn `idx.start << offset` into
20/// `idx.start * 2^offset` for `pt_va_range_start`.
21pub proof fn lemma_pt_va_range_start_shift_facts<C: PageTableConfig>(
22    idx_start: usize,
23    offset: usize,
24)
25    requires
26        idx_start == C::TOP_LEVEL_INDEX_RANGE().start,
27        offset == pte_index_bit_offset_spec::<C>(C::NR_LEVELS()),
28    ensures
29        offset < usize::BITS,
30        idx_start * pow2(offset as nat) <= usize::MAX,
31        offset == pte_index_bit_offset_spec::<C>(C::NR_LEVELS()),
32{
33    C::lemma_paging_consts_properties();
34    C::lemma_page_table_config_constant_properties();
35    vstd::layout::unsigned_int_max_values();
36
37    let off = pte_index_bit_offset_spec::<C>(C::NR_LEVELS()) as nat;
38    let aw = C::ADDRESS_WIDTH() as nat;
39    let top_w = (aw - off) as nat;
40    lemma_pow2_adds(top_w, off);
41    lemma_pow2_pos(off);
42    lemma_pow2_pos(top_w);
43    lemma_pow2_pos(aw);
44
45    let i_start = C::TOP_LEVEL_INDEX_RANGE().start as int;
46    let p_off = pow2(off) as int;
47    let p_top = pow2(top_w) as int;
48
49    assert(i_start * p_off < p_top * p_off) by (nonlinear_arith)
50        requires
51            i_start < p_top,
52            p_off > 0,
53    ;
54
55    if aw < 64 {
56        vstd::arithmetic::power2::lemma_pow2_strictly_increases(aw, 64);
57    }
58}
59
60/// Facts needed to turn `idx.end << offset` into
61/// `idx.end * 2^offset` for `pt_va_range_end`.
62pub proof fn lemma_pt_va_range_end_shift_facts<C: PageTableConfig>(idx_end: usize, offset: usize)
63    requires
64        idx_end == C::TOP_LEVEL_INDEX_RANGE().end,
65        offset == pte_index_bit_offset_spec::<C>(C::NR_LEVELS()),
66    ensures
67        offset < usize::BITS,
68        idx_end * pow2(offset as nat) <= usize::MAX,
69        0 < idx_end * pow2(offset as nat),
70        offset == pte_index_bit_offset_spec::<C>(C::NR_LEVELS()),
71{
72    C::lemma_paging_consts_properties();
73    C::lemma_page_table_config_constant_properties();
74    lemma_pow2_pos(offset as nat);
75
76    let i_end = C::TOP_LEVEL_INDEX_RANGE().end as int;
77    let p_off = pow2(offset as nat) as int;
78    assert(i_end * p_off > 0) by (nonlinear_arith)
79        requires
80            i_end > 0,
81            p_off > 0,
82    ;
83}
84
85/// Connect `wrapping_sub(1)` to the modulo end-of-range spec after the
86/// shift result is known to be non-zero.
87pub proof fn lemma_pt_va_range_end_wrapping_sub<C: PageTableConfig>(
88    idx_end: usize,
89    offset: usize,
90    shifted: usize,
91    ret: usize,
92)
93    requires
94        idx_end == C::TOP_LEVEL_INDEX_RANGE().end,
95        offset == pte_index_bit_offset_spec::<C>(C::NR_LEVELS()),
96        shifted == idx_end * pow2(offset as nat),
97        ret == vstd::wrapping::usize_specs::wrapping_sub(shifted, 1usize),
98    ensures
99        ret == (C::TOP_LEVEL_INDEX_RANGE().end * pow2(
100            pte_index_bit_offset_spec::<C>(C::NR_LEVELS()) as nat,
101        ) - 1) % 0x1_0000_0000_0000_0000int,
102{
103    lemma_pt_va_range_end_shift_facts::<C>(idx_end, offset);
104    vstd::layout::unsigned_int_max_values();
105}
106
107/// Two-in-one: `start = idx.start * 2^off < 2^ADDRESS_WIDTH` and
108/// `end = (idx.end * 2^off - 1) % 2^64 < 2^ADDRESS_WIDTH`.
109///
110/// The first follows from `idx.start < 2^(ADDRESS_WIDTH - off)`. The
111/// second from `idx.end <= 2^(ADDRESS_WIDTH - off)` plus the no-wrap
112/// condition: `idx.end * 2^off <= 2^ADDRESS_WIDTH ≤ 2^64`, so the `% 2^64`
113/// is a no-op when the value is positive.
114pub proof fn lemma_idx_times_pow2_bound<C: PageTableConfig>(start: Vaddr, end: Vaddr)
115    requires
116        start == C::TOP_LEVEL_INDEX_RANGE().start * (pow2(
117            pte_index_bit_offset_spec::<C>(C::NR_LEVELS()) as nat,
118        ) as int),
119        end == (C::TOP_LEVEL_INDEX_RANGE().end * (pow2(
120            pte_index_bit_offset_spec::<C>(C::NR_LEVELS()) as nat,
121        ) as int) - 1) % 0x1_0000_0000_0000_0000int,
122    ensures
123        start < pow2(C::ADDRESS_WIDTH() as nat),
124        end < pow2(C::ADDRESS_WIDTH() as nat),
125        // For the end-of-range arithmetic ensures of `vaddr_range`:
126        end == C::TOP_LEVEL_INDEX_RANGE().end * pow2(
127            pte_index_bit_offset_spec::<C>(C::NR_LEVELS()) as nat,
128        ) - 1,
129{
130    C::lemma_paging_consts_properties();
131    C::lemma_page_table_config_constant_properties();
132    let off = pte_index_bit_offset_spec::<C>(C::NR_LEVELS()) as nat;
133    let aw = C::ADDRESS_WIDTH() as nat;
134    let top_w = (aw - off) as nat;
135    lemma_pow2_adds(top_w, off);
136    lemma_pow2_pos(off);
137    lemma_pow2_pos(top_w);
138    lemma_pow2_pos(aw);
139    // Constants for clarity.
140    let i_start = C::TOP_LEVEL_INDEX_RANGE().start as int;
141    let i_end = C::TOP_LEVEL_INDEX_RANGE().end as int;
142    let p_off = pow2(off) as int;
143    let p_top = pow2(top_w) as int;
144    let p_aw = pow2(aw) as int;
145    // start < p_top * p_off = p_aw.
146    assert(start < (p_top * p_off)) by (nonlinear_arith)
147        requires
148            start == i_start * p_off,
149            i_start < p_top,
150            p_off > 0,
151    ;
152    // end pre-wrap value.
153    let e_pre = i_end * p_off;
154    assert(e_pre <= p_top * p_off) by (nonlinear_arith)
155        requires
156            e_pre == i_end * p_off,
157            i_end <= p_top,
158            p_off > 0,
159    ;
160    // i_end > 0 — from `lemma_page_table_config_constant_properties`'s
161    // `idx.start < idx.end` plus `idx.start >= 0` (usize).
162    assert(e_pre > 0) by (nonlinear_arith)
163        requires
164            e_pre == i_end * p_off,
165            i_end > 0,
166            p_off > 0,
167    ;
168}
169
170} // verus!