Nothing to see here, move along
1use crate::cap::cnode;
2use crate::cap::pool::POOL;
3use crate::cap::table::{CapRef, Rights};
4use crate::error::KernelError;
5use crate::mem::phys::BitmapFrameAllocator;
6use crate::proc::PROCESSES;
7use lancer_core::object_layout::KernelObject;
8use lancer_core::object_tag::ObjectTag;
9
10crate::kernel_test!(
11 fn scale_10k_pids() {
12 let mut allocator = BitmapFrameAllocator;
13 let mut ptable = PROCESSES.lock();
14
15 let mut allocated = crate::static_vec::StaticVec::<crate::types::Pid, 256>::new();
16
17 let target = 256;
18 (0..target).for_each(|_| {
19 if let Some(created) = ptable.allocate(&mut allocator) {
20 let _ = allocated.push(created.pid());
21 }
22 });
23
24 let count = allocated.len();
25 assert!(count >= 200, "expected at least 200 PIDs, got {}", count);
26
27 allocated.as_slice().iter().for_each(|&pid| {
28 assert!(
29 ptable.get(pid).is_some(),
30 "allocated PID {} should be accessible",
31 pid.raw()
32 );
33 });
34
35 (1..count).for_each(|i| {
36 (0..i).for_each(|j| {
37 assert!(
38 allocated.as_slice()[i] != allocated.as_slice()[j],
39 "duplicate PID at indices {} and {}",
40 i,
41 j
42 );
43 });
44 });
45
46 allocated.as_slice().iter().for_each(|&pid| {
47 ptable.destroy(pid, &mut allocator);
48 });
49
50 let mut reallocated = crate::static_vec::StaticVec::<crate::types::Pid, 256>::new();
51 (0..target).for_each(|_| {
52 if let Some(created) = ptable.allocate(&mut allocator) {
53 let _ = reallocated.push(created.pid());
54 }
55 });
56 assert!(
57 reallocated.len() >= 200,
58 "after free, expected at least 200 re-allocated PIDs, got {}",
59 reallocated.len()
60 );
61
62 reallocated.as_slice().iter().for_each(|&pid| {
63 ptable.destroy(pid, &mut allocator);
64 });
65 }
66);
67
68crate::kernel_test!(
69 fn scale_create_destroy_loop() {
70 let ptable = PROCESSES.lock();
71 let mut pool = POOL.lock_after(&ptable);
72
73 let baseline = pool.active_count();
74
75 let iterations = 50;
76 (0..iterations).for_each(|round| {
77 let (ep_id, ep_gen) = crate::tests::helpers::alloc_endpoint(&mut pool)
78 .unwrap_or_else(|e| panic!("alloc endpoint round {}: {:?}", round, e));
79 if let Some((phys, _)) = pool.dec_ref_phys(ep_id, ep_gen) {
80 crate::cap::kernel_objects::free_slot(phys);
81 }
82 });
83
84 let after = pool.active_count();
85 assert!(
86 after == baseline,
87 "object leak: baseline={}, after={}, delta={}",
88 baseline,
89 after,
90 after as i64 - baseline as i64
91 );
92 }
93);
94
95crate::kernel_test!(
96 fn exhaust_retype() {
97 let mut ptable = PROCESSES.lock();
98 let mut allocator = BitmapFrameAllocator;
99
100 let created = ptable.allocate(&mut allocator).expect("alloc proc");
101 ptable.start(created).expect("start");
102 let pid = created.pid();
103 crate::tests::helpers::bootstrap_test_cnode(pid, &mut ptable);
104
105 let (ut_id, ut_gen, _) = crate::tests::helpers::allocate_small_untyped(&ptable, 12);
106 let (cnode_id, cnode_gen, depth, gv, gb) =
107 cnode::cnode_coords(pid, &ptable).expect("coords");
108 let mut pool = POOL.lock_after(&ptable);
109
110 let ut_cap = CapRef::new(ObjectTag::Untyped, ut_id, Rights::ALL, ut_gen);
111 cnode::resolve_and_insert(&pool, cnode_id, cnode_gen, 900, depth, gv, gb, ut_cap)
112 .expect("insert ut");
113
114 let mut dest = 100u64;
115 let mut created_count = 0u32;
116 let mut hit_exhaustion = false;
117 (0..200u32).for_each(|_| {
118 match crate::cap::retype::kernel_retype(
119 &mut pool,
120 None,
121 ut_id,
122 ut_gen,
123 ObjectTag::Endpoint,
124 0,
125 cnode_id,
126 cnode_gen,
127 dest,
128 depth,
129 gv,
130 gb,
131 1,
132 ) {
133 Ok(()) => {
134 created_count += 1;
135 dest += 1;
136 }
137 Err(KernelError::ResourceExhausted) => {
138 hit_exhaustion = true;
139 }
140 Err(_) => {
141 hit_exhaustion = true;
142 }
143 }
144 });
145
146 assert!(hit_exhaustion, "should have hit resource exhaustion");
147 assert!(created_count > 0, "should have created at least one object");
148
149 drop(pool);
150 ptable.destroy(pid, &mut allocator);
151 }
152);
153
154crate::kernel_test!(
155 fn interleaved_retype_revoke() {
156 let mut ptable = PROCESSES.lock();
157 let mut allocator = BitmapFrameAllocator;
158
159 let created = ptable.allocate(&mut allocator).expect("alloc proc");
160 ptable.start(created).expect("start");
161 let pid = created.pid();
162 crate::tests::helpers::bootstrap_test_cnode(pid, &mut ptable);
163
164 let (ut_id, ut_gen, _) = crate::tests::helpers::allocate_untyped(&ptable, false);
165 let (cnode_id, cnode_gen, depth, gv, gb) =
166 cnode::cnode_coords(pid, &ptable).expect("coords");
167
168 {
169 let pool = POOL.lock_after(&ptable);
170 let ut_cap = CapRef::new(ObjectTag::Untyped, ut_id, Rights::ALL, ut_gen);
171 cnode::resolve_and_insert(&pool, cnode_id, cnode_gen, 900, depth, gv, gb, ut_cap)
172 .expect("insert ut");
173 }
174
175 let rounds = 10u32;
176 (0..rounds).for_each(|round| {
177 let dest_base = 100u64;
178 let obj_count = 5u32;
179
180 {
181 let mut pool = POOL.lock_after(&ptable);
182 let result = crate::cap::retype::kernel_retype(
183 &mut pool,
184 None,
185 ut_id,
186 ut_gen,
187 ObjectTag::Endpoint,
188 0,
189 cnode_id,
190 cnode_gen,
191 dest_base,
192 depth,
193 gv,
194 gb,
195 obj_count,
196 );
197 assert!(
198 result.is_ok(),
199 "retype round {} failed: {:?}",
200 round,
201 result.err()
202 );
203 }
204
205 {
206 let result = crate::cap::ops::revoke_via_cnode(pid, 900, &mut ptable);
207 assert!(
208 result.is_ok(),
209 "revoke round {} failed: {:?}",
210 round,
211 result.err()
212 );
213 }
214 });
215
216 let mut allocator = BitmapFrameAllocator;
217 ptable.destroy(pid, &mut allocator);
218 }
219);
220
221crate::kernel_test!(
222 fn scale_10k_pid_cycles() {
223 let mut allocator = BitmapFrameAllocator;
224 let mut ptable = PROCESSES.lock();
225
226 let rounds = 40u32;
227 let batch = 256usize;
228
229 (0..rounds).for_each(|round| {
230 let mut allocated = crate::static_vec::StaticVec::<crate::types::Pid, 256>::new();
231 (0..batch).for_each(|_| {
232 if let Some(created) = ptable.allocate(&mut allocator) {
233 let _ = allocated.push(created.pid());
234 }
235 });
236
237 let count = allocated.len();
238 assert!(
239 count >= 200,
240 "round {}: expected >= 200 PIDs, got {}",
241 round,
242 count
243 );
244
245 allocated.as_slice().iter().for_each(|&pid| {
246 assert!(
247 ptable.get(pid).is_some(),
248 "round {}: PID {} inaccessible",
249 round,
250 pid.raw()
251 );
252 });
253
254 allocated.as_slice().iter().for_each(|&pid| {
255 ptable.destroy(pid, &mut allocator);
256 });
257 });
258
259 match ptable.allocate(&mut allocator) {
260 Some(created) => {
261 ptable.destroy(created.pid(), &mut allocator);
262 }
263 None => panic!("fresh allocation after 10K+ cycles failed"),
264 }
265 }
266);
267
268crate::kernel_test!(
269 fn scale_10k_object_cycles() {
270 let ptable = PROCESSES.lock();
271 let mut pool = POOL.lock_after(&ptable);
272
273 let baseline = pool.active_count();
274
275 let rounds = 40u32;
276 let batch = 256u32;
277
278 (0..rounds).for_each(|round| {
279 let mut ids = crate::static_vec::StaticVec::<
280 (crate::types::ObjPhys, crate::types::Generation),
281 256,
282 >::new();
283 (0..batch).for_each(|i| match crate::tests::helpers::alloc_endpoint(&mut pool) {
284 Ok((eid, egen)) => {
285 let _ = ids.push((eid, egen));
286 }
287 Err(e) => panic!("round {} alloc {}: {:?}", round, i, e),
288 });
289
290 ids.as_slice().iter().for_each(|&(eid, egen)| {
291 if let Some((phys, _)) = pool.dec_ref_phys(eid, egen) {
292 crate::cap::kernel_objects::free_slot(phys);
293 }
294 });
295 });
296
297 let after = pool.active_count();
298 assert!(
299 after == baseline,
300 "object leak after 10K+ cycles: baseline={}, after={}",
301 baseline,
302 after
303 );
304 }
305);
306
307crate::kernel_test!(
308 fn scale_10k_retype_cycles() {
309 let mut ptable = PROCESSES.lock();
310 let mut allocator = BitmapFrameAllocator;
311
312 let created = ptable.allocate(&mut allocator).expect("alloc proc");
313 ptable.start(created).expect("start");
314 let pid = created.pid();
315 crate::tests::helpers::bootstrap_test_cnode(pid, &mut ptable);
316
317 let (ut_id, ut_gen, _) = crate::tests::helpers::allocate_small_untyped(&ptable, 16);
318 let (cnode_id, cnode_gen, depth, gv, gb) =
319 cnode::cnode_coords(pid, &ptable).expect("coords");
320
321 {
322 let pool = POOL.lock_after(&ptable);
323 let ut_cap = CapRef::new(ObjectTag::Untyped, ut_id, Rights::ALL, ut_gen);
324 cnode::resolve_and_insert(&pool, cnode_id, cnode_gen, 900, depth, gv, gb, ut_cap)
325 .expect("insert ut");
326 }
327
328 let rounds = 640u32;
329 let batch_size = 16u32;
330 let mut total_retyped = 0u64;
331
332 (0..rounds).for_each(|round| {
333 {
334 let mut pool = POOL.lock_after(&ptable);
335 let result = crate::cap::retype::kernel_retype(
336 &mut pool,
337 None,
338 ut_id,
339 ut_gen,
340 ObjectTag::Endpoint,
341 0,
342 cnode_id,
343 cnode_gen,
344 100,
345 depth,
346 gv,
347 gb,
348 batch_size,
349 );
350 match result {
351 Ok(()) => {
352 total_retyped += batch_size as u64;
353 }
354 Err(KernelError::ResourceExhausted) => {}
355 Err(e) => panic!("retype round {} failed: {:?}", round, e),
356 }
357 }
358
359 {
360 let result = crate::cap::ops::revoke_via_cnode(pid, 900, &mut ptable);
361 assert!(
362 result.is_ok(),
363 "revoke round {} failed: {:?}",
364 round,
365 result.err()
366 );
367 }
368 });
369
370 assert!(
371 total_retyped >= 10_000,
372 "expected 10K+ cumulative retypes, got {}",
373 total_retyped
374 );
375
376 let mut allocator = BitmapFrameAllocator;
377 ptable.destroy(pid, &mut allocator);
378 }
379);
380
381crate::kernel_test!(
382 fn exhaust_kernel_object_slots() {
383 let ptable = PROCESSES.lock();
384 let mut pool = POOL.lock_after(&ptable);
385
386 let mut allocated = crate::static_vec::StaticVec::<
387 (crate::types::ObjPhys, crate::types::Generation, u64),
388 512,
389 >::new();
390 let mut count = 0u32;
391
392 while let Ok((eid, egen)) = crate::tests::helpers::alloc_endpoint(&mut pool) {
393 if allocated.push((eid, egen, eid.raw())).is_err() {
394 if let Some((phys, _)) = pool.dec_ref_phys(eid, egen) {
395 crate::cap::kernel_objects::free_slot(phys);
396 }
397 break;
398 }
399 count += 1;
400 }
401
402 assert!(
403 count >= 100,
404 "expected at least 100 kernel object slots, got {}",
405 count
406 );
407
408 allocated.as_slice().iter().for_each(|&(eid, egen, _)| {
409 if let Some((phys, _)) = pool.dec_ref_phys(eid, egen) {
410 crate::cap::kernel_objects::free_slot(phys);
411 }
412 });
413
414 let (eid, egen) = crate::tests::helpers::alloc_endpoint(&mut pool)
415 .unwrap_or_else(|e| panic!("alloc after free-all failed: {:?}", e));
416 if let Some((phys, _)) = pool.dec_ref_phys(eid, egen) {
417 crate::cap::kernel_objects::free_slot(phys);
418 }
419 }
420);
421
422crate::kernel_test!(
423 fn large_child_list_revoke() {
424 let mut ptable = PROCESSES.lock();
425 let mut allocator = BitmapFrameAllocator;
426
427 let created = ptable.allocate(&mut allocator).expect("alloc proc");
428 ptable.start(created).expect("start");
429 let pid = created.pid();
430 crate::tests::helpers::bootstrap_test_cnode(pid, &mut ptable);
431
432 let (ut_id, ut_gen, _) = crate::tests::helpers::allocate_small_untyped(&ptable, 20);
433 let (cnode_id, cnode_gen, depth, gv, gb) =
434 cnode::cnode_coords(pid, &ptable).expect("coords");
435
436 {
437 let pool = POOL.lock_after(&ptable);
438 let ut_cap = CapRef::new(ObjectTag::Untyped, ut_id, Rights::ALL, ut_gen);
439 cnode::resolve_and_insert(&pool, cnode_id, cnode_gen, 900, depth, gv, gb, ut_cap)
440 .expect("insert ut");
441 }
442
443 let mut total_children = 0u32;
444 let mut dest = 100u64;
445 let batch = 64u32;
446 loop {
447 let mut pool = POOL.lock_after(&ptable);
448 match crate::cap::retype::kernel_retype(
449 &mut pool,
450 None,
451 ut_id,
452 ut_gen,
453 ObjectTag::Endpoint,
454 0,
455 cnode_id,
456 cnode_gen,
457 dest,
458 depth,
459 gv,
460 gb,
461 batch,
462 ) {
463 Ok(()) => {
464 total_children += batch;
465 dest += batch as u64;
466 }
467 Err(KernelError::ResourceExhausted) => break,
468 Err(KernelError::SlotOccupied) => break,
469 Err(e) => panic!("retype failed at dest {}: {:?}", dest, e),
470 }
471 }
472
473 assert!(
474 total_children > 100,
475 "expected > 100 children from 1MB untyped, got {}",
476 total_children
477 );
478
479 {
480 let result = crate::cap::ops::revoke_via_cnode(pid, 900, &mut ptable);
481 assert!(result.is_ok(), "revoke failed: {:?}", result.err());
482 }
483
484 {
485 let pool = POOL.lock_after(&ptable);
486 let ut = pool
487 .read_as::<lancer_core::object_layout::UntypedObject>(ut_id, ut_gen)
488 .expect("untyped still valid");
489 assert!(
490 ut.watermark == 0,
491 "watermark must reset after revoke, got {}",
492 ut.watermark
493 );
494 assert!(
495 ut.child_count == 0,
496 "child_count must be 0 after revoke, got {}",
497 ut.child_count
498 );
499 }
500
501 let mut allocator = BitmapFrameAllocator;
502 ptable.destroy(pid, &mut allocator);
503 }
504);
505
506crate::kernel_test!(
507 fn object_id_recycling_generation_increases() {
508 let ptable = PROCESSES.lock();
509 let mut pool = POOL.lock_after(&ptable);
510
511 let mut last_gen_by_slot = crate::static_vec::StaticVec::<(u64, u32), 256>::new();
512
513 let cycles = 100u32;
514 (0..cycles).for_each(|round| {
515 let (eid, egen) = crate::tests::helpers::alloc_endpoint(&mut pool)
516 .unwrap_or_else(|e| panic!("alloc round {}: {:?}", round, e));
517
518 let raw_id = eid.raw();
519 let gen_val = egen.raw();
520
521 let existing = last_gen_by_slot
522 .as_mut_slice()
523 .iter_mut()
524 .find(|(slot_id, _)| *slot_id == raw_id);
525
526 match existing {
527 Some(entry) => {
528 assert!(
529 gen_val > entry.1,
530 "round {}: slot {} gen {} not > prev gen {}",
531 round,
532 raw_id,
533 gen_val,
534 entry.1
535 );
536 entry.1 = gen_val;
537 }
538 None => {
539 let _ = last_gen_by_slot.push((raw_id, gen_val));
540 }
541 }
542
543 if let Some((phys, _)) = pool.dec_ref_phys(eid, egen) {
544 crate::cap::kernel_objects::free_slot(phys);
545 }
546 });
547 }
548);
549
550crate::kernel_test!(
551 fn interleaved_retype_revoke_heavy() {
552 let mut ptable = PROCESSES.lock();
553 let mut allocator = BitmapFrameAllocator;
554
555 let created = ptable.allocate(&mut allocator).expect("alloc proc");
556 ptable.start(created).expect("start");
557 let pid = created.pid();
558 crate::tests::helpers::bootstrap_test_cnode(pid, &mut ptable);
559
560 let (ut_id, ut_gen, _) = crate::tests::helpers::allocate_untyped(&ptable, false);
561 let (cnode_id, cnode_gen, depth, gv, gb) =
562 cnode::cnode_coords(pid, &ptable).expect("coords");
563
564 {
565 let pool = POOL.lock_after(&ptable);
566 let ut_cap = CapRef::new(ObjectTag::Untyped, ut_id, Rights::ALL, ut_gen);
567 cnode::resolve_and_insert(&pool, cnode_id, cnode_gen, 900, depth, gv, gb, ut_cap)
568 .expect("insert ut");
569 }
570
571 let rounds = 100u32;
572 (0..rounds).for_each(|round| {
573 {
574 let mut pool = POOL.lock_after(&ptable);
575 let result = crate::cap::retype::kernel_retype(
576 &mut pool,
577 None,
578 ut_id,
579 ut_gen,
580 ObjectTag::Endpoint,
581 0,
582 cnode_id,
583 cnode_gen,
584 100,
585 depth,
586 gv,
587 gb,
588 5,
589 );
590 assert!(
591 result.is_ok(),
592 "retype round {} failed: {:?}",
593 round,
594 result.err()
595 );
596 }
597
598 {
599 let result = crate::cap::ops::revoke_via_cnode(pid, 900, &mut ptable);
600 assert!(
601 result.is_ok(),
602 "revoke round {} failed: {:?}",
603 round,
604 result.err()
605 );
606 }
607 });
608
609 {
610 let pool = POOL.lock_after(&ptable);
611 let ut = pool
612 .read_as::<lancer_core::object_layout::UntypedObject>(ut_id, ut_gen)
613 .expect("untyped valid");
614 assert!(ut.watermark == 0, "watermark leak after 100 rounds");
615 assert!(ut.child_count == 0, "child leak after 100 rounds");
616 }
617
618 let mut allocator = BitmapFrameAllocator;
619 ptable.destroy(pid, &mut allocator);
620 }
621);
622
623crate::kernel_test!(
624 fn mixed_tag_retype_revoke() {
625 let mut ptable = PROCESSES.lock();
626 let mut allocator = BitmapFrameAllocator;
627
628 let created = ptable.allocate(&mut allocator).expect("alloc proc");
629 ptable.start(created).expect("start");
630 let pid = created.pid();
631 crate::tests::helpers::bootstrap_test_cnode(pid, &mut ptable);
632
633 let (ut_id, ut_gen, _) = crate::tests::helpers::allocate_untyped(&ptable, false);
634 let (cnode_id, cnode_gen, depth, gv, gb) =
635 cnode::cnode_coords(pid, &ptable).expect("coords");
636
637 {
638 let pool = POOL.lock_after(&ptable);
639 let ut_cap = CapRef::new(ObjectTag::Untyped, ut_id, Rights::ALL, ut_gen);
640 cnode::resolve_and_insert(&pool, cnode_id, cnode_gen, 900, depth, gv, gb, ut_cap)
641 .expect("insert ut");
642 }
643
644 {
645 let mut pool = POOL.lock_after(&ptable);
646
647 let ep_result = crate::cap::retype::kernel_retype(
648 &mut pool,
649 None,
650 ut_id,
651 ut_gen,
652 ObjectTag::Endpoint,
653 0,
654 cnode_id,
655 cnode_gen,
656 100,
657 depth,
658 gv,
659 gb,
660 10,
661 );
662 assert!(
663 ep_result.is_ok(),
664 "retype endpoints failed: {:?}",
665 ep_result.err()
666 );
667
668 let notif_result = crate::cap::retype::kernel_retype(
669 &mut pool,
670 None,
671 ut_id,
672 ut_gen,
673 ObjectTag::Notification,
674 0,
675 cnode_id,
676 cnode_gen,
677 110,
678 depth,
679 gv,
680 gb,
681 10,
682 );
683 assert!(
684 notif_result.is_ok(),
685 "retype notifications failed: {:?}",
686 notif_result.err()
687 );
688
689 let sched_result = crate::cap::retype::kernel_retype(
690 &mut pool,
691 None,
692 ut_id,
693 ut_gen,
694 ObjectTag::SchedContext,
695 0,
696 cnode_id,
697 cnode_gen,
698 120,
699 depth,
700 gv,
701 gb,
702 5,
703 );
704 assert!(
705 sched_result.is_ok(),
706 "retype sched contexts failed: {:?}",
707 sched_result.err()
708 );
709 }
710
711 {
712 let pool = POOL.lock_after(&ptable);
713 let ut = pool
714 .read_as::<lancer_core::object_layout::UntypedObject>(ut_id, ut_gen)
715 .expect("untyped valid");
716 assert!(
717 ut.child_count == 25,
718 "expected 25 children, got {}",
719 ut.child_count
720 );
721 }
722
723 {
724 let result = crate::cap::ops::revoke_via_cnode(pid, 900, &mut ptable);
725 assert!(result.is_ok(), "revoke failed: {:?}", result.err());
726 }
727
728 {
729 let pool = POOL.lock_after(&ptable);
730 let ut = pool
731 .read_as::<lancer_core::object_layout::UntypedObject>(ut_id, ut_gen)
732 .expect("untyped valid after revoke");
733 assert!(ut.watermark == 0, "watermark must reset");
734 assert!(ut.child_count == 0, "child_count must be 0");
735 }
736
737 let mut allocator = BitmapFrameAllocator;
738 ptable.destroy(pid, &mut allocator);
739 }
740);
741
742crate::kernel_test!(
743 fn nested_untyped_retype_then_revoke_parent() {
744 let mut ptable = PROCESSES.lock();
745 let mut allocator = BitmapFrameAllocator;
746
747 let created = ptable.allocate(&mut allocator).expect("alloc proc");
748 ptable.start(created).expect("start");
749 let pid = created.pid();
750 crate::tests::helpers::bootstrap_test_cnode(pid, &mut ptable);
751
752 let (parent_ut_id, parent_ut_gen, _) =
753 crate::tests::helpers::allocate_untyped(&ptable, false);
754 let (cnode_id, cnode_gen, depth, gv, gb) =
755 cnode::cnode_coords(pid, &ptable).expect("coords");
756
757 let child_size_bits = 14u8;
758 let child_frames = 1usize << (child_size_bits - 12);
759 let child_phys = BitmapFrameAllocator
760 .allocate_contiguous(child_frames)
761 .expect("alloc child untyped backing");
762 let child_virt = crate::mem::addr::phys_to_virt(child_phys);
763 unsafe {
764 core::ptr::write_bytes(child_virt.as_mut_ptr::<u8>(), 0, child_frames * 4096);
765 }
766
767 let header = lancer_core::header::KernelObjectHeader::new(ObjectTag::Untyped, 0, 64);
768 let mut child_ut = lancer_core::object_layout::UntypedObject::init_default(header);
769 child_ut.phys_base = child_phys.as_u64();
770 child_ut.size_bits = child_size_bits;
771 child_ut.is_device = 0;
772 let child_slot_phys =
773 crate::cap::kernel_objects::alloc_slot().expect("alloc child ut slot");
774 crate::cap::kernel_objects::write_at(child_slot_phys, child_ut);
775
776 let (child_ut_id, child_ut_gen) = {
777 let mut pool = POOL.lock_after(&ptable);
778 let (cid, cgen) = pool
779 .register_object(child_slot_phys, ObjectTag::Untyped)
780 .expect("register child ut");
781 crate::cap::derivation::link_child(&mut pool, parent_ut_id, parent_ut_gen, cid)
782 .expect("link child untyped to parent");
783 let cap = CapRef::new(ObjectTag::Untyped, cid, Rights::ALL, cgen);
784 cnode::resolve_and_insert(&pool, cnode_id, cnode_gen, 80, depth, gv, gb, cap)
785 .expect("insert child untyped cap");
786 (cid, cgen)
787 };
788
789 {
790 let mut pool = POOL.lock_after(&ptable);
791 crate::cap::retype::kernel_retype(
792 &mut pool,
793 None,
794 child_ut_id,
795 child_ut_gen,
796 ObjectTag::Endpoint,
797 0,
798 cnode_id,
799 cnode_gen,
800 81,
801 depth,
802 gv,
803 gb,
804 3,
805 )
806 .expect("retype endpoints from child untyped");
807 }
808
809 let ep_ids: [(crate::types::ObjPhys, crate::types::Generation); 3] = {
810 let pool = POOL.lock_after(&ptable);
811 let c0 = cnode::resolve_and_read(&pool, cnode_id, cnode_gen, 81, depth, gv, gb)
812 .expect("ep0");
813 let c1 = cnode::resolve_and_read(&pool, cnode_id, cnode_gen, 82, depth, gv, gb)
814 .expect("ep1");
815 let c2 = cnode::resolve_and_read(&pool, cnode_id, cnode_gen, 83, depth, gv, gb)
816 .expect("ep2");
817 [
818 (c0.phys(), c0.generation()),
819 (c1.phys(), c1.generation()),
820 (c2.phys(), c2.generation()),
821 ]
822 };
823
824 {
825 let mut pool = POOL.lock_after(&ptable);
826 crate::cap::derivation::destroy_children(
827 &mut pool,
828 &mut ptable,
829 parent_ut_id,
830 parent_ut_gen,
831 )
832 .expect("destroy_children on parent");
833 }
834
835 {
836 let pool = POOL.lock_after(&ptable);
837 let ut = pool
838 .read_as::<lancer_core::object_layout::UntypedObject>(parent_ut_id, parent_ut_gen)
839 .expect("parent untyped valid");
840 assert!(ut.watermark == 0, "parent watermark must reset");
841 assert!(ut.child_count == 0, "parent child_count must be 0");
842
843 assert!(
844 pool.get_tag(child_ut_id, child_ut_gen).is_err(),
845 "child untyped must be destroyed"
846 );
847
848 ep_ids.iter().for_each(|&(eid, egen)| {
849 assert!(
850 pool.get_tag(eid, egen).is_err(),
851 "grandchild endpoint must be destroyed"
852 );
853 });
854 }
855
856 let mut allocator = BitmapFrameAllocator;
857 ptable.destroy(pid, &mut allocator);
858 }
859);
860
861crate::kernel_test!(
862 fn retype_after_revoke_with_stale_gen() {
863 let ptable = PROCESSES.lock();
864 let mut pool = POOL.lock_after(&ptable);
865
866 let (ep_id, ep_gen) =
867 crate::tests::helpers::alloc_endpoint(&mut pool).expect("alloc endpoint");
868 let stale_gen = ep_gen;
869
870 let (_new_gen, _old_phys_tag) = pool.revoke_phys(ep_id, ep_gen).expect("revoke endpoint");
871
872 let result = pool.get_tag(ep_id, stale_gen);
873 assert!(
874 matches!(result, Err(KernelError::StaleGeneration)),
875 "accessing revoked object with stale gen should return StaleGeneration, got {:?}",
876 result
877 );
878
879 let new_gen_val = pool.generation_of(ep_id);
880 assert!(
881 new_gen_val.is_none(),
882 "revoked slot should not report as active"
883 );
884 }
885);