Nothing to see here, move along
1use crate::cap_table::{CapRef, CapSlot};
2use crate::error::KernelError;
3use crate::object_tag::ObjectTag;
4use crate::types::{Generation, ObjPhys};
5
6pub const MIN_CNODE_BITS: u8 = 1;
7pub const MAX_CNODE_BITS: u8 = 15;
8pub const MAX_RESOLVE_DEPTH: u8 = 16;
9pub const CAPSLOT_SIZE: usize = core::mem::size_of::<CapSlot>();
10pub const PAGE_SIZE: usize = 4096;
11
12const _: () = {
13 let max_slots = 1usize << MAX_CNODE_BITS;
14 let max_bytes = max_slots * CAPSLOT_SIZE;
15 let max_frames = max_bytes.div_ceil(PAGE_SIZE);
16 assert!(max_frames <= u16::MAX as usize);
17};
18
19pub fn frames_for_cnode(size_bits: u8) -> u8 {
20 let slots = 1usize << size_bits;
21 let bytes = slots * CAPSLOT_SIZE;
22 bytes.div_ceil(PAGE_SIZE) as u8
23}
24
25pub fn extract_index(address: u64, remaining_depth: u8, size_bits: u8) -> u64 {
26 let shift = remaining_depth - size_bits;
27 let mask = (1u64 << size_bits) - 1;
28 (address >> shift) & mask
29}
30
31pub fn extract_guard(address: u64, depth: u8, guard_bits: u8) -> u64 {
32 match guard_bits {
33 0 => 0,
34 64 => address,
35 _ => {
36 let shift = depth - guard_bits;
37 let mask = (1u64 << guard_bits) - 1;
38 (address >> shift) & mask
39 }
40 }
41}
42
43pub fn validate_size_bits(size_bits: u8) -> Result<(), KernelError> {
44 match (MIN_CNODE_BITS..=MAX_CNODE_BITS).contains(&size_bits) {
45 true => Ok(()),
46 false => Err(KernelError::InvalidParameter),
47 }
48}
49
50pub trait CNodeStore {
51 fn read_slot(
52 &self,
53 cnode_phys: ObjPhys,
54 cnode_gen: Generation,
55 index: usize,
56 ) -> Result<CapSlot, KernelError>;
57
58 fn size_bits(&self, cnode_phys: ObjPhys, cnode_gen: Generation) -> Result<u8, KernelError>;
59}
60
61pub fn resolve<S: CNodeStore>(
62 store: &S,
63 cnode_phys: ObjPhys,
64 cnode_gen: Generation,
65 address: u64,
66 depth: u8,
67 guard_value: u64,
68 guard_bits: u8,
69) -> Result<CapSlot, KernelError> {
70 resolve_inner(
71 store,
72 cnode_phys,
73 cnode_gen,
74 address,
75 depth,
76 guard_value,
77 guard_bits,
78 0,
79 )
80}
81
82#[allow(clippy::too_many_arguments)]
83fn resolve_inner<S: CNodeStore>(
84 store: &S,
85 cnode_phys: ObjPhys,
86 cnode_gen: Generation,
87 address: u64,
88 depth: u8,
89 guard_value: u64,
90 guard_bits: u8,
91 recursion: u8,
92) -> Result<CapSlot, KernelError> {
93 if recursion >= MAX_RESOLVE_DEPTH {
94 return Err(KernelError::InvalidSlot);
95 }
96
97 if depth < guard_bits {
98 return Err(KernelError::InvalidSlot);
99 }
100
101 let extracted = extract_guard(address, depth, guard_bits);
102 if extracted != guard_value {
103 return Err(KernelError::GuardMismatch);
104 }
105
106 let post_guard_depth = depth - guard_bits;
107
108 let size_bits = store.size_bits(cnode_phys, cnode_gen)?;
109
110 if post_guard_depth < size_bits {
111 return Err(KernelError::InvalidSlot);
112 }
113
114 let index = extract_index(address, post_guard_depth, size_bits) as usize;
115 let remaining = post_guard_depth - size_bits;
116 let slot = store.read_slot(cnode_phys, cnode_gen, index)?;
117
118 match remaining {
119 0 => Ok(slot),
120 _ => match slot {
121 CapSlot::Active(cap) if cap.tag() == ObjectTag::CNode => resolve_inner(
122 store,
123 cap.phys(),
124 cap.generation(),
125 address,
126 remaining,
127 cap.guard_value(),
128 cap.guard_bits(),
129 recursion + 1,
130 ),
131 _ => Err(KernelError::InvalidSlot),
132 },
133 }
134}
135
136#[allow(clippy::too_many_arguments)]
137pub fn resolve_and_validate<S: CNodeStore>(
138 store: &S,
139 cnode_phys: ObjPhys,
140 cnode_gen: Generation,
141 address: u64,
142 depth: u8,
143 guard_value: u64,
144 guard_bits: u8,
145 expected_tag: ObjectTag,
146 required_rights: crate::cap_table::Rights,
147) -> Result<CapRef, KernelError> {
148 match resolve(
149 store,
150 cnode_phys,
151 cnode_gen,
152 address,
153 depth,
154 guard_value,
155 guard_bits,
156 )? {
157 CapSlot::Empty => Err(KernelError::SlotEmpty),
158 CapSlot::Active(cap) => {
159 if cap.tag() != expected_tag {
160 return Err(KernelError::InvalidType);
161 }
162 if !cap.rights().contains(required_rights) {
163 return Err(KernelError::InsufficientRights);
164 }
165 Ok(cap)
166 }
167 }
168}
169
170pub fn resolve_and_read<S: CNodeStore>(
171 store: &S,
172 cnode_phys: ObjPhys,
173 cnode_gen: Generation,
174 address: u64,
175 depth: u8,
176 guard_value: u64,
177 guard_bits: u8,
178) -> Result<CapRef, KernelError> {
179 match resolve(
180 store,
181 cnode_phys,
182 cnode_gen,
183 address,
184 depth,
185 guard_value,
186 guard_bits,
187 )? {
188 CapSlot::Empty => Err(KernelError::SlotEmpty),
189 CapSlot::Active(cap) => Ok(cap),
190 }
191}
192
193#[cfg(kani)]
194mod kani_proofs {
195 use super::*;
196 use crate::cap_table::Rights;
197
198 struct SingleCNode {
199 slots: [CapSlot; 16],
200 size_bits: u8,
201 expected_phys: ObjPhys,
202 expected_gen: Generation,
203 }
204
205 impl SingleCNode {
206 fn new(phys: ObjPhys, generation: Generation) -> Self {
207 Self {
208 slots: [CapSlot::Empty; 16],
209 size_bits: 4,
210 expected_phys: phys,
211 expected_gen: generation,
212 }
213 }
214 }
215
216 impl CNodeStore for SingleCNode {
217 fn read_slot(
218 &self,
219 cnode_phys: ObjPhys,
220 cnode_gen: Generation,
221 index: usize,
222 ) -> Result<CapSlot, KernelError> {
223 if cnode_phys != self.expected_phys || cnode_gen != self.expected_gen {
224 return Err(KernelError::StaleGeneration);
225 }
226 match index < (1usize << self.size_bits) {
227 true => Ok(self.slots[index]),
228 false => Err(KernelError::InvalidSlot),
229 }
230 }
231
232 fn size_bits(&self, cnode_phys: ObjPhys, cnode_gen: Generation) -> Result<u8, KernelError> {
233 if cnode_phys != self.expected_phys || cnode_gen != self.expected_gen {
234 return Err(KernelError::StaleGeneration);
235 }
236 Ok(self.size_bits)
237 }
238 }
239
240 #[kani::proof]
241 fn extract_index_bounded() {
242 let address: u64 = kani::any();
243 let size_bits: u8 = kani::any();
244 let remaining_depth: u8 = kani::any();
245
246 kani::assume(size_bits >= MIN_CNODE_BITS && size_bits <= MAX_CNODE_BITS);
247 kani::assume(remaining_depth >= size_bits && remaining_depth <= 64);
248
249 let idx = extract_index(address, remaining_depth, size_bits);
250 assert!(idx < (1u64 << size_bits));
251 }
252
253 #[kani::proof]
254 fn extract_index_shift_correctness() {
255 let size_bits: u8 = kani::any();
256 let remaining_depth: u8 = kani::any();
257 let address: u64 = kani::any();
258
259 kani::assume(size_bits >= 1 && size_bits <= 8);
260 kani::assume(remaining_depth >= size_bits && remaining_depth <= 16);
261
262 let idx = extract_index(address, remaining_depth, size_bits);
263 let shift = remaining_depth - size_bits;
264 let reconstructed = idx << shift;
265
266 let mask = ((1u64 << size_bits) - 1) << shift;
267 assert!((reconstructed ^ (address & mask)) == 0);
268 }
269
270 #[kani::proof]
271 fn two_level_address_partitioning() {
272 let outer_bits: u8 = kani::any();
273 let inner_bits: u8 = kani::any();
274
275 kani::assume(outer_bits >= 1 && outer_bits <= 6);
276 kani::assume(inner_bits >= 1 && inner_bits <= 6);
277
278 let total_depth = outer_bits + inner_bits;
279 kani::assume(total_depth <= 12);
280
281 let outer_idx: u64 = kani::any();
282 let inner_idx: u64 = kani::any();
283 kani::assume(outer_idx < (1u64 << outer_bits));
284 kani::assume(inner_idx < (1u64 << inner_bits));
285
286 let address = (outer_idx << inner_bits) | inner_idx;
287
288 let got_outer = extract_index(address, total_depth, outer_bits);
289 let remaining = total_depth - outer_bits;
290 let got_inner = extract_index(address, remaining, inner_bits);
291
292 assert!(got_outer == outer_idx);
293 assert!(got_inner == inner_idx);
294 }
295
296 #[kani::proof]
297 fn frames_for_cnode_monotonic() {
298 let a: u8 = kani::any();
299 let b: u8 = kani::any();
300 kani::assume(a >= MIN_CNODE_BITS && a <= MAX_CNODE_BITS);
301 kani::assume(b >= MIN_CNODE_BITS && b <= MAX_CNODE_BITS);
302 kani::assume(a <= b);
303 assert!(frames_for_cnode(a) <= frames_for_cnode(b));
304 }
305
306 #[kani::proof]
307 fn resolve_empty_cnode_returns_empty() {
308 let phys = ObjPhys::new(0x1000);
309 let generation = Generation::new(0);
310 let store = SingleCNode::new(phys, generation);
311
312 let address: u8 = kani::any();
313 kani::assume(address < 16);
314
315 let result = resolve(&store, phys, generation, address as u64, 4, 0, 0);
316 assert!(matches!(result, Ok(CapSlot::Empty)));
317 }
318
319 #[kani::proof]
320 fn resolve_finds_inserted_cap() {
321 let phys = ObjPhys::new(0x1000);
322 let generation = Generation::new(0);
323 let mut store = SingleCNode::new(phys, generation);
324
325 let slot_idx: u8 = kani::any();
326 kani::assume(slot_idx < 16);
327
328 let cap = CapRef::new(
329 ObjectTag::Endpoint,
330 ObjPhys::new(0x2000),
331 Rights::ALL,
332 Generation::new(0),
333 );
334 store.slots[slot_idx as usize] = CapSlot::Active(cap);
335
336 let result = resolve(&store, phys, generation, slot_idx as u64, 4, 0, 0);
337 match result {
338 Ok(CapSlot::Active(found)) => {
339 assert!(found.tag() == ObjectTag::Endpoint);
340 assert!(found.phys() == ObjPhys::new(0x2000));
341 }
342 _ => panic!("expected Active slot"),
343 }
344 }
345
346 #[kani::proof]
347 fn resolve_stale_generation_rejected() {
348 let phys = ObjPhys::new(0x1000);
349 let generation = Generation::new(0);
350 let store = SingleCNode::new(phys, generation);
351
352 let stale = Generation::new(1);
353 let result = resolve(&store, phys, stale, 0, 4, 0, 0);
354 assert!(matches!(result, Err(KernelError::StaleGeneration)));
355 }
356
357 #[kani::proof]
358 fn resolve_depth_less_than_size_bits_rejected() {
359 let phys = ObjPhys::new(0x1000);
360 let generation = Generation::new(0);
361 let store = SingleCNode::new(phys, generation);
362
363 let depth: u8 = kani::any();
364 kani::assume(depth < 4);
365 let result = resolve(&store, phys, generation, 0, depth, 0, 0);
366 assert!(matches!(result, Err(KernelError::InvalidSlot)));
367 }
368
369 #[kani::proof]
370 fn extract_guard_bounded() {
371 let address: u64 = kani::any();
372 let depth: u8 = kani::any();
373 let guard_bits: u8 = kani::any();
374
375 kani::assume(guard_bits <= 64);
376 kani::assume(depth >= guard_bits);
377 kani::assume(depth <= 64);
378
379 let val = extract_guard(address, depth, guard_bits);
380 match guard_bits {
381 0 => assert!(val == 0),
382 64 => assert!(val == address),
383 _ => assert!(val < (1u64 << guard_bits)),
384 }
385 }
386
387 #[kani::proof]
388 fn guard_mismatch_detected() {
389 let phys = ObjPhys::new(0x1000);
390 let generation = Generation::new(0);
391 let store = SingleCNode::new(phys, generation);
392
393 let result = resolve(&store, phys, generation, 0, 8, 1, 4);
394 assert!(matches!(result, Err(KernelError::GuardMismatch)));
395 }
396
397 #[kani::proof]
398 fn zero_guard_identity() {
399 let phys = ObjPhys::new(0x1000);
400 let generation = Generation::new(0);
401 let store = SingleCNode::new(phys, generation);
402
403 let address: u8 = kani::any();
404 kani::assume(address < 16);
405
406 let with_guard = resolve(&store, phys, generation, address as u64, 4, 0, 0);
407 assert!(matches!(with_guard, Ok(CapSlot::Empty)));
408 }
409
410 #[kani::proof]
411 fn guard_plus_size_partitioning() {
412 let guard_bits: u8 = kani::any();
413 let size_bits: u8 = kani::any();
414
415 kani::assume(guard_bits <= 8);
416 kani::assume(size_bits >= 1 && size_bits <= 8);
417 let total = guard_bits + size_bits;
418 kani::assume(total <= 16);
419
420 let guard_val: u64 = kani::any();
421 let index_val: u64 = kani::any();
422 kani::assume(guard_bits == 0 || guard_val < (1u64 << guard_bits));
423 kani::assume(index_val < (1u64 << size_bits));
424
425 let address = (guard_val << size_bits) | index_val;
426
427 let got_guard = extract_guard(address, total, guard_bits);
428 let got_index = extract_index(address, total - guard_bits, size_bits);
429
430 assert!(got_guard == guard_val);
431 assert!(got_index == index_val);
432 }
433
434 #[kani::proof]
435 fn validate_size_bits_range() {
436 let s: u8 = kani::any();
437 let result = validate_size_bits(s);
438 match (MIN_CNODE_BITS..=MAX_CNODE_BITS).contains(&s) {
439 true => assert!(result.is_ok()),
440 false => assert!(matches!(result, Err(KernelError::InvalidParameter))),
441 }
442 }
443}