Nothing to see here, move along meow
1use crate::cap::cnode;
2use crate::cap::pool::POOL;
3use crate::cap::retype::kernel_retype;
4use crate::cap::table::Rights;
5use crate::mem::phys::BitmapFrameAllocator;
6use crate::proc::PROCESSES;
7use crate::proc::address_space::{self, PageAccess};
8use crate::tests::helpers;
9use lancer_core::header::NONE_SENTINEL;
10use lancer_core::object_layout::VRegionObject;
11use lancer_core::object_tag::ObjectTag;
12use x86_64::PhysAddr;
13use x86_64::structures::paging::PhysFrame;
14
15crate::kernel_test!(
16 fn vregion_retype_from_untyped() {
17 let mut allocator = BitmapFrameAllocator;
18 let mut ptable = PROCESSES.lock();
19 let created = ptable.allocate(&mut allocator).expect("alloc");
20 ptable.start(created).expect("start");
21 let pid = created.pid();
22 helpers::bootstrap_test_cnode(pid, &mut ptable);
23
24 let (ut_id, ut_gen, phys_base) = helpers::allocate_untyped(&ptable, false);
25 let (cnode_id, cnode_gen, depth, gv, gb) =
26 cnode::cnode_coords(pid, &ptable).expect("coords");
27 let dest_slot = 50u64;
28 let page_count = 4u16;
29
30 {
31 let mut pool = POOL.lock_after(&ptable);
32 kernel_retype(
33 &mut pool,
34 None,
35 ut_id,
36 ut_gen,
37 ObjectTag::VRegion,
38 0,
39 cnode_id,
40 cnode_gen,
41 dest_slot,
42 depth,
43 gv,
44 gb,
45 page_count as u32,
46 )
47 .expect("retype vregion");
48 }
49
50 {
51 let pool = POOL.lock_after(&ptable);
52 let cap =
53 cnode::resolve_and_read(&pool, cnode_id, cnode_gen, dest_slot, depth, gv, gb)
54 .expect("read slot");
55 assert!(cap.tag() == ObjectTag::VRegion);
56 assert!(cap.rights().contains(Rights::ALL));
57
58 let vr = pool
59 .read_as::<VRegionObject>(cap.phys(), cap.generation())
60 .expect("read vregion");
61 assert!(vr.phys_base >= phys_base.as_u64());
62 assert!(vr.page_count == page_count);
63 assert!(vr.owner_pid == NONE_SENTINEL);
64 assert!(vr.child_pid == NONE_SENTINEL);
65 assert!(vr.header.parent_untyped != NONE_SENTINEL);
66 }
67
68 ptable.destroy(pid, &mut allocator);
69 }
70);
71
72crate::kernel_test!(
73 fn vregion_retype_marks_bitmap() {
74 let mut allocator = BitmapFrameAllocator;
75 let mut ptable = PROCESSES.lock();
76 let created = ptable.allocate(&mut allocator).expect("alloc");
77 ptable.start(created).expect("start");
78 let pid = created.pid();
79 helpers::bootstrap_test_cnode(pid, &mut ptable);
80
81 let (ut_id, ut_gen, _phys) = helpers::allocate_untyped(&ptable, false);
82 let (cnode_id, cnode_gen, depth, gv, gb) =
83 cnode::cnode_coords(pid, &ptable).expect("coords");
84 let page_count = 8u16;
85
86 {
87 let mut pool = POOL.lock_after(&ptable);
88 kernel_retype(
89 &mut pool,
90 None,
91 ut_id,
92 ut_gen,
93 ObjectTag::VRegion,
94 0,
95 cnode_id,
96 cnode_gen,
97 55,
98 depth,
99 gv,
100 gb,
101 page_count as u32,
102 )
103 .expect("retype vregion");
104 }
105
106 {
107 let pool = POOL.lock_after(&ptable);
108 let cap = cnode::resolve_and_read(&pool, cnode_id, cnode_gen, 55, depth, gv, gb)
109 .expect("read slot");
110 let vr = pool
111 .read_as::<VRegionObject>(cap.phys(), cap.generation())
112 .expect("read vregion");
113
114 (0..page_count as u64).for_each(|i| {
115 let frame_idx = ((vr.phys_base + i * 4096) / 4096) as usize;
116 assert!(
117 BitmapFrameAllocator::is_used(frame_idx),
118 "backing frame {} must be marked used",
119 i
120 );
121 });
122 }
123
124 ptable.destroy(pid, &mut allocator);
125 }
126);
127
128crate::kernel_test!(
129 fn vregion_map_and_access() {
130 let mut allocator = BitmapFrameAllocator;
131 let mut ptable = PROCESSES.lock();
132 let created = ptable.allocate(&mut allocator).expect("alloc");
133 ptable.start(created).expect("start");
134 let pid = created.pid();
135 helpers::bootstrap_test_cnode(pid, &mut ptable);
136
137 let (ut_id, ut_gen, _phys) = helpers::allocate_untyped(&ptable, false);
138 let (cnode_id, cnode_gen, depth, gv, gb) =
139 cnode::cnode_coords(pid, &ptable).expect("coords");
140 let dest_slot = 60u64;
141 let page_count = 4u16;
142 let vaddr_base = 0x20_0000u64;
143
144 {
145 let mut pool = POOL.lock_after(&ptable);
146 kernel_retype(
147 &mut pool,
148 None,
149 ut_id,
150 ut_gen,
151 ObjectTag::VRegion,
152 0,
153 cnode_id,
154 cnode_gen,
155 dest_slot,
156 depth,
157 gv,
158 gb,
159 page_count as u32,
160 )
161 .expect("retype vregion");
162 }
163
164 let (vr_phys, vr_gen, backing_phys) = {
165 let pool = POOL.lock_after(&ptable);
166 let cap =
167 cnode::resolve_and_read(&pool, cnode_id, cnode_gen, dest_slot, depth, gv, gb)
168 .expect("read slot");
169 let vr = pool
170 .read_as::<VRegionObject>(cap.phys(), cap.generation())
171 .expect("read vregion");
172 (cap.phys(), cap.generation(), vr.phys_base)
173 };
174
175 let pml4_phys = ptable.exec(pid).map(|e| e.pml4_phys).expect("pml4");
176 let mut frame_alloc = BitmapFrameAllocator;
177
178 (0..page_count as u64).for_each(|i| {
179 let page_phys = PhysAddr::new(backing_phys + i * 4096);
180 crate::mem::addr::zero_frame(page_phys);
181 let phys_frame = PhysFrame::containing_address(page_phys);
182 let page_vaddr = x86_64::VirtAddr::new(vaddr_base + i * 4096);
183 address_space::map_user_page_inner(
184 pml4_phys,
185 page_vaddr,
186 phys_frame,
187 PageAccess::ReadWrite,
188 &mut frame_alloc,
189 )
190 .unwrap_or_else(|e| panic!("map page {} failed: {:?}", i, e));
191 });
192
193 {
194 let mut pool = POOL.lock_after(&ptable);
195 let vr = pool
196 .write_as::<VRegionObject>(vr_phys, vr_gen)
197 .expect("write vregion");
198 vr.owner_pid = pid.raw();
199 vr.owner_vaddr = vaddr_base;
200 vr.flags = lancer_core::types::VRegionFlags::try_new(lancer_core::types::VRegionFlags::WRITABLE).unwrap();
201 }
202
203 (0..page_count as u64).for_each(|i| {
204 let resolved = crate::syscall::resolve_user_page(
205 pml4_phys.raw(),
206 vaddr_base + i * 4096,
207 true,
208 false,
209 );
210 assert!(
211 resolved.is_some(),
212 "page {} must be resolvable in PML4",
213 i
214 );
215 let resolved_phys = resolved.unwrap();
216 assert!(
217 resolved_phys == backing_phys + i * 4096,
218 "page {} resolved phys {:#x} != expected {:#x}",
219 i,
220 resolved_phys,
221 backing_phys + i * 4096,
222 );
223 });
224
225 (0..page_count as u64).for_each(|i| {
226 let page_phys = PhysAddr::new(backing_phys + i * 4096);
227 let page_virt = crate::mem::addr::phys_to_virt(page_phys);
228 let ptr = page_virt.as_mut_ptr::<u64>();
229 unsafe {
230 core::ptr::write_volatile(ptr, 0xDEAD_BEEF_0000 + i);
231 let val = core::ptr::read_volatile(ptr);
232 assert!(
233 val == 0xDEAD_BEEF_0000 + i,
234 "page {} readback mismatch: got {:#x}",
235 i,
236 val
237 );
238 }
239 });
240
241 ptable.destroy(pid, &mut allocator);
242 }
243);
244
245crate::kernel_test!(
246 fn vregion_backing_pages_zeroed() {
247 let mut allocator = BitmapFrameAllocator;
248 let mut ptable = PROCESSES.lock();
249 let created = ptable.allocate(&mut allocator).expect("alloc");
250 ptable.start(created).expect("start");
251 let pid = created.pid();
252 helpers::bootstrap_test_cnode(pid, &mut ptable);
253
254 let (ut_id, ut_gen, _phys) = helpers::allocate_untyped(&ptable, false);
255 let (cnode_id, cnode_gen, depth, gv, gb) =
256 cnode::cnode_coords(pid, &ptable).expect("coords");
257
258 {
259 let mut pool = POOL.lock_after(&ptable);
260 kernel_retype(
261 &mut pool,
262 None,
263 ut_id,
264 ut_gen,
265 ObjectTag::VRegion,
266 0,
267 cnode_id,
268 cnode_gen,
269 70,
270 depth,
271 gv,
272 gb,
273 4,
274 )
275 .expect("retype vregion");
276 }
277
278 let backing_phys = {
279 let pool = POOL.lock_after(&ptable);
280 let cap = cnode::resolve_and_read(&pool, cnode_id, cnode_gen, 70, depth, gv, gb)
281 .expect("read slot");
282 pool.read_as::<VRegionObject>(cap.phys(), cap.generation())
283 .expect("read vregion")
284 .phys_base
285 };
286
287 (0..4u64).for_each(|i| {
288 let page_phys = PhysAddr::new(backing_phys + i * 4096);
289 crate::mem::addr::zero_frame(page_phys);
290
291 let page_virt = crate::mem::addr::phys_to_virt(page_phys);
292 let slice =
293 unsafe { core::slice::from_raw_parts(page_virt.as_ptr::<u8>(), 4096) };
294 assert!(
295 slice.iter().all(|&b| b == 0),
296 "backing page {} not zeroed after zero_frame",
297 i
298 );
299 });
300
301 ptable.destroy(pid, &mut allocator);
302 }
303);
304
305crate::kernel_test!(
306 fn vregion_destroy_unmaps_owner() {
307 let mut allocator = BitmapFrameAllocator;
308 let mut ptable = PROCESSES.lock();
309 let created = ptable.allocate(&mut allocator).expect("alloc");
310 ptable.start(created).expect("start");
311 let pid = created.pid();
312 helpers::bootstrap_test_cnode(pid, &mut ptable);
313
314 let (ut_id, ut_gen, _phys) = helpers::allocate_untyped(&ptable, false);
315 let (cnode_id, cnode_gen, depth, gv, gb) =
316 cnode::cnode_coords(pid, &ptable).expect("coords");
317 let dest_slot = 80u64;
318 let page_count = 2u16;
319 let vaddr_base = 0x30_0000u64;
320
321 {
322 let mut pool = POOL.lock_after(&ptable);
323 kernel_retype(
324 &mut pool,
325 None,
326 ut_id,
327 ut_gen,
328 ObjectTag::VRegion,
329 0,
330 cnode_id,
331 cnode_gen,
332 dest_slot,
333 depth,
334 gv,
335 gb,
336 page_count as u32,
337 )
338 .expect("retype vregion");
339 }
340
341 let (vr_obj_phys, vr_gen, backing_phys) = {
342 let pool = POOL.lock_after(&ptable);
343 let cap =
344 cnode::resolve_and_read(&pool, cnode_id, cnode_gen, dest_slot, depth, gv, gb)
345 .expect("read slot");
346 let vr = pool
347 .read_as::<VRegionObject>(cap.phys(), cap.generation())
348 .expect("read vregion");
349 (cap.phys(), cap.generation(), vr.phys_base)
350 };
351
352 let pml4_phys = ptable.exec(pid).map(|e| e.pml4_phys).expect("pml4");
353 let mut frame_alloc = BitmapFrameAllocator;
354
355 (0..page_count as u64).for_each(|i| {
356 let page_phys = PhysAddr::new(backing_phys + i * 4096);
357 crate::mem::addr::zero_frame(page_phys);
358 let phys_frame = PhysFrame::containing_address(page_phys);
359 let page_vaddr = x86_64::VirtAddr::new(vaddr_base + i * 4096);
360 address_space::map_user_page_inner(
361 pml4_phys,
362 page_vaddr,
363 phys_frame,
364 PageAccess::ReadWrite,
365 &mut frame_alloc,
366 )
367 .expect("map page");
368 });
369
370 {
371 let mut pool = POOL.lock_after(&ptable);
372 let vr = pool
373 .write_as::<VRegionObject>(vr_obj_phys, vr_gen)
374 .expect("write vregion");
375 vr.owner_pid = pid.raw();
376 vr.owner_vaddr = vaddr_base;
377 vr.flags = lancer_core::types::VRegionFlags::try_new(lancer_core::types::VRegionFlags::WRITABLE).unwrap();
378 }
379
380 (0..page_count as u64).for_each(|i| {
381 assert!(
382 crate::syscall::resolve_user_page(
383 pml4_phys.raw(),
384 vaddr_base + i * 4096,
385 false,
386 false,
387 )
388 .is_some(),
389 "page {} must be mapped before destroy",
390 i
391 );
392 });
393
394 {
395 let (_new_gen, freed) = POOL
396 .lock_after(&ptable)
397 .revoke_phys(vr_obj_phys, vr_gen)
398 .expect("revoke");
399 freed.inspect(|(phys, tag)| {
400 crate::cap::ops::cleanup_by_tag_with_ptable(*tag, *phys, &mut ptable);
401 });
402 }
403
404 (0..page_count as u64).for_each(|i| {
405 assert!(
406 crate::syscall::resolve_user_page(
407 pml4_phys.raw(),
408 vaddr_base + i * 4096,
409 false,
410 false,
411 )
412 .is_none(),
413 "page {} must be unmapped after destroy",
414 i
415 );
416 });
417
418 ptable.destroy(pid, &mut allocator);
419 }
420);
421
422crate::kernel_test!(
423 fn vregion_device_untyped_rejected() {
424 let mut allocator = BitmapFrameAllocator;
425 let mut ptable = PROCESSES.lock();
426 let created = ptable.allocate(&mut allocator).expect("alloc");
427 ptable.start(created).expect("start");
428 let pid = created.pid();
429 helpers::bootstrap_test_cnode(pid, &mut ptable);
430
431 let (ut_id, ut_gen, _phys) = helpers::allocate_untyped(&ptable, true);
432 let (cnode_id, cnode_gen, depth, gv, gb) =
433 cnode::cnode_coords(pid, &ptable).expect("coords");
434
435 let result = {
436 let mut pool = POOL.lock_after(&ptable);
437 kernel_retype(
438 &mut pool,
439 None,
440 ut_id,
441 ut_gen,
442 ObjectTag::VRegion,
443 0,
444 cnode_id,
445 cnode_gen,
446 90,
447 depth,
448 gv,
449 gb,
450 4,
451 )
452 };
453
454 assert!(
455 result.is_err(),
456 "device untyped must reject VRegion retype"
457 );
458
459 ptable.destroy(pid, &mut allocator);
460 }
461);
462
463crate::kernel_test!(
464 fn vregion_zero_page_count_noop() {
465 let mut allocator = BitmapFrameAllocator;
466 let mut ptable = PROCESSES.lock();
467 let created = ptable.allocate(&mut allocator).expect("alloc");
468 ptable.start(created).expect("start");
469 let pid = created.pid();
470 helpers::bootstrap_test_cnode(pid, &mut ptable);
471
472 let (ut_id, ut_gen, _phys) = helpers::allocate_untyped(&ptable, false);
473 let (cnode_id, cnode_gen, depth, gv, gb) =
474 cnode::cnode_coords(pid, &ptable).expect("coords");
475
476 let result = {
477 let mut pool = POOL.lock_after(&ptable);
478 kernel_retype(
479 &mut pool,
480 None,
481 ut_id,
482 ut_gen,
483 ObjectTag::VRegion,
484 0,
485 cnode_id,
486 cnode_gen,
487 91,
488 depth,
489 gv,
490 gb,
491 0,
492 )
493 };
494
495 assert!(
496 result.is_ok(),
497 "page_count=0 must return Ok(()) (no-op)"
498 );
499
500 ptable.destroy(pid, &mut allocator);
501 }
502);
503
504crate::kernel_test!(
505 fn vregion_contiguous_backing() {
506 let mut allocator = BitmapFrameAllocator;
507 let mut ptable = PROCESSES.lock();
508 let created = ptable.allocate(&mut allocator).expect("alloc");
509 ptable.start(created).expect("start");
510 let pid = created.pid();
511 helpers::bootstrap_test_cnode(pid, &mut ptable);
512
513 let (ut_id, ut_gen, _phys) = helpers::allocate_untyped(&ptable, false);
514 let (cnode_id, cnode_gen, depth, gv, gb) =
515 cnode::cnode_coords(pid, &ptable).expect("coords");
516 let page_count = 8u16;
517
518 {
519 let mut pool = POOL.lock_after(&ptable);
520 kernel_retype(
521 &mut pool,
522 None,
523 ut_id,
524 ut_gen,
525 ObjectTag::VRegion,
526 0,
527 cnode_id,
528 cnode_gen,
529 95,
530 depth,
531 gv,
532 gb,
533 page_count as u32,
534 )
535 .expect("retype vregion");
536 }
537
538 let backing_phys = {
539 let pool = POOL.lock_after(&ptable);
540 let cap = cnode::resolve_and_read(&pool, cnode_id, cnode_gen, 95, depth, gv, gb)
541 .expect("read slot");
542 pool.read_as::<VRegionObject>(cap.phys(), cap.generation())
543 .expect("read vregion")
544 .phys_base
545 };
546
547 assert!(
548 backing_phys % 4096 == 0,
549 "backing phys {:#x} must be page-aligned",
550 backing_phys
551 );
552
553 let pml4_phys = ptable.exec(pid).map(|e| e.pml4_phys).expect("pml4");
554 let vaddr_base = 0x40_0000u64;
555 let mut frame_alloc = BitmapFrameAllocator;
556
557 (0..page_count as u64).for_each(|i| {
558 let page_phys = PhysAddr::new(backing_phys + i * 4096);
559 crate::mem::addr::zero_frame(page_phys);
560 let phys_frame = PhysFrame::containing_address(page_phys);
561 address_space::map_user_page_inner(
562 pml4_phys,
563 x86_64::VirtAddr::new(vaddr_base + i * 4096),
564 phys_frame,
565 PageAccess::ReadWrite,
566 &mut frame_alloc,
567 )
568 .expect("map page");
569 });
570
571 (0..page_count as u64).for_each(|i| {
572 let resolved = crate::syscall::resolve_user_page(
573 pml4_phys.raw(),
574 vaddr_base + i * 4096,
575 false,
576 false,
577 )
578 .unwrap_or_else(|| panic!("page {} not mapped", i));
579 assert!(
580 resolved == backing_phys + i * 4096,
581 "page {} should map to contiguous phys: {:#x} vs expected {:#x}",
582 i,
583 resolved,
584 backing_phys + i * 4096,
585 );
586 });
587
588 ptable.destroy(pid, &mut allocator);
589 }
590);
591
592crate::kernel_test!(
593 fn vregion_exceeds_max_rejected() {
594 let mut allocator = BitmapFrameAllocator;
595 let mut ptable = PROCESSES.lock();
596 let created = ptable.allocate(&mut allocator).expect("alloc");
597 ptable.start(created).expect("start");
598 let pid = created.pid();
599 helpers::bootstrap_test_cnode(pid, &mut ptable);
600
601 let (ut_id, ut_gen, _phys) = helpers::allocate_small_untyped(&ptable, 22);
602 let (cnode_id, cnode_gen, depth, gv, gb) =
603 cnode::cnode_coords(pid, &ptable).expect("coords");
604
605 let result = {
606 let mut pool = POOL.lock_after(&ptable);
607 kernel_retype(
608 &mut pool,
609 None,
610 ut_id,
611 ut_gen,
612 ObjectTag::VRegion,
613 0,
614 cnode_id,
615 cnode_gen,
616 100,
617 depth,
618 gv,
619 gb,
620 513,
621 )
622 };
623 assert!(result.is_err(), "page_count=513 exceeds MAX_VREGION_PAGES");
624
625 ptable.destroy(pid, &mut allocator);
626 }
627);