Nothing to see here, move along
1use crate::cap::cnode;
2use crate::cap::object::{
3 EndpointData, MAX_NOTIFICATION_WAITERS, NotificationData, ObjectData, ObjectTag,
4};
5use crate::cap::ops;
6use crate::cap::pool::POOL;
7use crate::cap::table::{CapRef, Rights};
8use crate::error::KernelError;
9use crate::ipc::{IpcOutcome, endpoint, notification};
10use crate::proc::context::IpcMessage;
11use crate::proc::{BlockedReason, PROCESSES, ProcessState};
12use crate::types::{Pid, Priority};
13
14fn bootstrap_test_cnode(pid: Pid, ptable: &mut crate::proc::ProcessManager) {
15 let size_bits = crate::proc::ROOT_CNODE_SIZE_BITS;
16 let allocator = &crate::mem::phys::BitmapFrameAllocator;
17 let cnode_data = crate::cap::cnode::create_cnode(size_bits, allocator).expect("create cnode");
18 let frame_count = cnode_data.frame_count;
19 let (cnode_id, cnode_gen) = POOL.lock().allocate(ObjectData::CNode(cnode_data)).expect("alloc cnode");
20 let proc = ptable.get_mut(pid).expect("get proc");
21 proc.root_cnode = Some((cnode_id, cnode_gen));
22 proc.cnode_depth = size_bits;
23 proc.charge_frames(frame_count as u16).expect("charge frames");
24}
25
26fn alloc_endpoint() -> (crate::types::ObjectId, crate::types::Generation, CapRef) {
27 let (id, generation) = POOL
28 .lock()
29 .allocate(ObjectData::Endpoint(EndpointData::new()))
30 .expect("alloc endpoint");
31 let cap = CapRef::new(ObjectTag::Endpoint, id, Rights::ALL, generation);
32 (id, generation, cap)
33}
34
35fn alloc_notification() -> (crate::types::ObjectId, crate::types::Generation, CapRef) {
36 let (id, generation) = POOL
37 .lock()
38 .allocate(ObjectData::Notification(NotificationData::new()))
39 .expect("alloc notification");
40 let cap = CapRef::new(ObjectTag::Notification, id, Rights::ALL, generation);
41 (id, generation, cap)
42}
43
44crate::kernel_test!(
45 fn notification_waiter_overflow() {
46 let (id, generation, cap) = alloc_notification();
47 let mut allocator = crate::mem::phys::BitmapFrameAllocator;
48 let mut ptable = PROCESSES.lock();
49
50 let pids: [crate::types::Pid; 5] = core::array::from_fn(|_| {
51 let created = ptable.allocate(&mut allocator).expect("alloc");
52 ptable.start(created).expect("start");
53 let pid = created.pid();
54 ptable[pid]
55 .transition_to(ProcessState::Running)
56 .expect("-> Running");
57 pid
58 });
59
60 (0..MAX_NOTIFICATION_WAITERS).for_each(|i| {
61 let result = notification::do_wait(&cap, pids[i], &mut ptable);
62 assert!(
63 matches!(result, Ok(IpcOutcome::Blocked)),
64 "waiter {} should block",
65 i
66 );
67 });
68
69 let overflow_result = notification::do_wait(&cap, pids[4], &mut ptable);
70 assert!(
71 matches!(overflow_result, Err(KernelError::ResourceExhausted)),
72 "5th waiter should fail with ResourceExhausted"
73 );
74 assert!(
75 ptable[pids[4]].state() != ProcessState::Blocked,
76 "5th process should NOT be left in Blocked state after rollback"
77 );
78
79 pids.iter().for_each(|&pid| {
80 ptable.destroy(pid, &mut allocator);
81 });
82 let _ = POOL.lock().dec_ref(id, generation);
83 }
84);
85
86crate::kernel_test!(
87 fn signal_zero_bits_preserves_word() {
88 let (id, generation, cap) = alloc_notification();
89 let mut ptable = PROCESSES.lock();
90
91 {
92 let mut pool = POOL.lock();
93 pool.get_mut(id, generation)
94 .unwrap()
95 .as_notification_mut()
96 .unwrap()
97 .word = 0x0F;
98 }
99
100 notification::do_signal(&cap, 0x00, &mut ptable).expect("signal zero bits");
101
102 let pool = POOL.lock();
103 let notif = pool.get(id, generation).unwrap().as_notification().unwrap();
104 assert!(
105 notif.word == 0x0F,
106 "word should be preserved after signal(0)"
107 );
108 drop(pool);
109 drop(ptable);
110 let _ = POOL.lock().dec_ref(id, generation);
111 }
112);
113
114crate::kernel_test!(
115 fn signal_u64_max_sets_all_bits() {
116 let (id, generation, cap) = alloc_notification();
117 let mut ptable = PROCESSES.lock();
118
119 notification::do_signal(&cap, u64::MAX, &mut ptable).expect("signal max");
120
121 let pool = POOL.lock();
122 let notif = pool.get(id, generation).unwrap().as_notification().unwrap();
123 assert!(
124 notif.word == u64::MAX,
125 "word should be u64::MAX after signal(u64::MAX)"
126 );
127 drop(pool);
128 drop(ptable);
129 let _ = POOL.lock().dec_ref(id, generation);
130 }
131);
132
133crate::kernel_test!(
134 fn revoke_unblocks_mixed_caller_and_sender() {
135 let mut allocator = crate::mem::phys::BitmapFrameAllocator;
136 let mut ptable = PROCESSES.lock();
137
138 let owner_created = ptable.allocate(&mut allocator).expect("alloc owner");
139 let sender_created = ptable.allocate(&mut allocator).expect("alloc sender");
140 let caller_created = ptable.allocate(&mut allocator).expect("alloc caller");
141 ptable.start(owner_created).expect("start owner");
142 ptable.start(sender_created).expect("start sender");
143 ptable.start(caller_created).expect("start caller");
144 let owner_pid = owner_created.pid();
145 let sender_pid = sender_created.pid();
146 let caller_pid = caller_created.pid();
147
148 bootstrap_test_cnode(owner_pid, &mut ptable);
149
150 let address = 0u64;
151 let (cnode_id, cnode_gen, depth) = cnode::cnode_coords(owner_pid, &ptable).expect("cnode coords");
152 let (ep_id, ep_gen) = {
153 let mut pool = POOL.lock_after(&ptable);
154 let (ep_id, ep_gen) = pool
155 .allocate(ObjectData::Endpoint(EndpointData::new()))
156 .expect("alloc ep");
157 let cap = CapRef::new(ObjectTag::Endpoint, ep_id, Rights::ALL, ep_gen);
158 cnode::resolve_and_insert(&pool, cnode_id, cnode_gen, address, depth, cap)
159 .expect("insert cap");
160 (ep_id, ep_gen)
161 };
162
163 ptable[sender_pid]
164 .transition_to(ProcessState::Running)
165 .expect("sender -> Running");
166 ptable[sender_pid].ipc_message = IpcMessage::from_regs([0x5E, 0, 0, 0, 0, 0]);
167 let sender_blocked = ptable[sender_pid]
168 .block_on(BlockedReason::Sending(ep_id, ep_gen))
169 .expect("block sender");
170
171 ptable[caller_pid]
172 .transition_to(ProcessState::Running)
173 .expect("caller -> Running");
174 ptable[caller_pid].ipc_message = IpcMessage::from_regs([0xCA, 0, 0, 0, 0, 0]);
175 let caller_blocked = ptable[caller_pid]
176 .block_on(BlockedReason::Calling(ep_id, ep_gen))
177 .expect("block caller");
178
179 {
180 let mut pool = POOL.lock_after(&ptable);
181 let ep = pool
182 .get_mut(ep_id, ep_gen)
183 .and_then(|d| d.as_endpoint_mut())
184 .expect("get ep");
185 endpoint::enqueue(&mut ep.senders, sender_blocked, &mut ptable)
186 .expect("enqueue sender");
187 endpoint::enqueue(&mut ep.senders, caller_blocked, &mut ptable)
188 .expect("enqueue caller");
189 }
190
191 ops::revoke_via_cnode(owner_pid, address, &mut ptable).expect("revoke");
192
193 assert!(
194 ptable[sender_pid].state() == ProcessState::Ready,
195 "sender should be unblocked after revoke"
196 );
197 assert!(
198 ptable[sender_pid].saved_context.rax == KernelError::InvalidObject.to_errno() as u64,
199 "sender rax should be InvalidObject errno"
200 );
201 assert!(
202 ptable[caller_pid].state() == ProcessState::Ready,
203 "caller should be unblocked after revoke"
204 );
205 assert!(
206 ptable[caller_pid].saved_context.rax == KernelError::InvalidObject.to_errno() as u64,
207 "caller rax should be InvalidObject errno"
208 );
209
210 ptable.destroy(owner_pid, &mut allocator);
211 ptable.destroy(sender_pid, &mut allocator);
212 ptable.destroy(caller_pid, &mut allocator);
213 }
214);
215
216crate::kernel_test!(
217 fn revoke_empty_slot_returns_error() {
218 let mut allocator = crate::mem::phys::BitmapFrameAllocator;
219 let mut ptable = PROCESSES.lock();
220
221 let created = ptable.allocate(&mut allocator).expect("alloc");
222 ptable.start(created).expect("start");
223 let pid = created.pid();
224 bootstrap_test_cnode(pid, &mut ptable);
225
226 let result = ops::revoke_via_cnode(pid, 100, &mut ptable);
227 assert!(
228 matches!(result, Err(KernelError::SlotEmpty)),
229 "revoke on empty slot should return SlotEmpty"
230 );
231
232 ptable.destroy(pid, &mut allocator);
233 }
234);
235
236crate::kernel_test!(
237 fn double_destroy_is_idempotent() {
238 let mut allocator = crate::mem::phys::BitmapFrameAllocator;
239 let mut ptable = PROCESSES.lock();
240
241 let created = ptable.allocate(&mut allocator).expect("alloc");
242 ptable.start(created).expect("start");
243 let pid = created.pid();
244
245 ptable.destroy(pid, &mut allocator);
246 ptable.destroy(pid, &mut allocator);
247
248 assert!(
249 ptable.get(pid).is_none(),
250 "slot should be freed after double destroy"
251 );
252 }
253);
254
255crate::kernel_test!(
256 fn destroy_cleans_up_endpoint_queues() {
257 let (ep_id, ep_gen, cap) = alloc_endpoint();
258 let mut allocator = crate::mem::phys::BitmapFrameAllocator;
259 let mut ptable = PROCESSES.lock();
260
261 let sender_created = ptable.allocate(&mut allocator).expect("alloc sender");
262 let owner_created = ptable.allocate(&mut allocator).expect("alloc owner");
263 ptable.start(sender_created).expect("start sender");
264 ptable.start(owner_created).expect("start owner");
265 let sender_pid = sender_created.pid();
266 let owner_pid = owner_created.pid();
267
268 bootstrap_test_cnode(owner_pid, &mut ptable);
269 let (cnode_id, cnode_gen, depth) = cnode::cnode_coords(owner_pid, &ptable).expect("cnode coords");
270 {
271 let pool = POOL.lock_after(&ptable);
272 cnode::resolve_and_insert(&pool, cnode_id, cnode_gen, 0, depth, cap)
273 .expect("insert");
274 }
275
276 ptable[sender_pid]
277 .transition_to(ProcessState::Running)
278 .expect("sender -> Running");
279 ptable[sender_pid].ipc_message = IpcMessage::from_regs([0xDE, 0, 0, 0, 0, 0]);
280 let blocked = ptable[sender_pid]
281 .block_on(BlockedReason::Sending(ep_id, ep_gen))
282 .expect("block sender");
283
284 {
285 let mut pool = POOL.lock_after(&ptable);
286 let ep = pool
287 .get_mut(ep_id, ep_gen)
288 .and_then(|d| d.as_endpoint_mut())
289 .expect("get ep");
290 endpoint::enqueue(&mut ep.senders, blocked, &mut ptable).expect("enqueue");
291 }
292
293 ptable.destroy(sender_pid, &mut allocator);
294
295 {
296 let pool = POOL.lock_after(&ptable);
297 let ep = pool.get(ep_id, ep_gen).unwrap().as_endpoint().unwrap();
298 assert!(
299 ep.senders.is_empty(),
300 "endpoint sender queue should be empty after destroying the sender"
301 );
302 }
303
304 ptable.destroy(owner_pid, &mut allocator);
305 }
306);
307
308crate::kernel_test!(
309 fn process_table_exhaustion() {
310 let mut allocator = crate::mem::phys::BitmapFrameAllocator;
311 let mut ptable = PROCESSES.lock();
312
313 let mut pids = crate::static_vec::StaticVec::<crate::types::Pid, 64>::new();
314 let mut count = 0usize;
315 (0..crate::types::MAX_PIDS).for_each(|_| {
316 if let Some(created) = ptable.allocate(&mut allocator) {
317 ptable.start(created).expect("start");
318 let _ = pids.push(created.pid());
319 count += 1;
320 }
321 });
322 assert!(count > 0, "should have allocated at least 1 process");
323
324 let overflow = ptable.allocate(&mut allocator);
325 assert!(
326 overflow.is_none(),
327 "should return None when process table is full"
328 );
329
330 pids.as_slice().iter().for_each(|&pid| {
331 ptable.destroy(pid, &mut allocator);
332 });
333 }
334);
335
336crate::kernel_test!(
337 fn block_already_blocked_fails() {
338 let (ep_id, ep_gen, _cap) = alloc_endpoint();
339 let mut allocator = crate::mem::phys::BitmapFrameAllocator;
340 let mut ptable = PROCESSES.lock();
341
342 let created = ptable.allocate(&mut allocator).expect("alloc");
343 ptable.start(created).expect("start");
344 let pid = created.pid();
345 ptable[pid]
346 .transition_to(ProcessState::Running)
347 .expect("-> Running");
348
349 let _blocked = ptable[pid]
350 .block_on(BlockedReason::Sending(ep_id, ep_gen))
351 .expect("first block");
352
353 let second = ptable[pid].block_on(BlockedReason::Receiving(ep_id, ep_gen));
354 assert!(
355 matches!(second, Err(KernelError::BadState)),
356 "blocking an already-blocked process should fail with BadState"
357 );
358
359 ptable.destroy(pid, &mut allocator);
360 let _ = POOL.lock().dec_ref(ep_id, ep_gen);
361 }
362);
363
364crate::kernel_test!(
365 fn revoke_without_revoke_right_fails() {
366 let mut allocator = crate::mem::phys::BitmapFrameAllocator;
367 let mut ptable = PROCESSES.lock();
368
369 let created = ptable.allocate(&mut allocator).expect("alloc");
370 ptable.start(created).expect("start");
371 let pid = created.pid();
372 bootstrap_test_cnode(pid, &mut ptable);
373
374 let address = 50u64;
375 let (cnode_id, cnode_gen, depth) = cnode::cnode_coords(pid, &ptable).expect("cnode coords");
376 {
377 let mut pool = POOL.lock_after(&ptable);
378 ops::create_via_cnode(&mut pool, cnode_id, cnode_gen, address, depth, ObjectTag::Endpoint)
379 .expect("create endpoint");
380 }
381
382 {
383 let pool = POOL.lock_after(&ptable);
384 let old_cap = cnode::resolve_and_clear(&pool, cnode_id, cnode_gen, address, depth)
385 .expect("clear slot");
386 let modified = old_cap.with_rights(Rights::READ | Rights::WRITE);
387 cnode::resolve_and_insert(&pool, cnode_id, cnode_gen, address, depth, modified)
388 .expect("re-insert");
389 }
390
391 let result = ops::revoke_via_cnode(pid, address, &mut ptable);
392 assert!(
393 matches!(result, Err(KernelError::InsufficientRights)),
394 "revoke without REVOKE right should fail"
395 );
396
397 ptable.destroy(pid, &mut allocator);
398 }
399);
400
401crate::kernel_test!(
402 fn derive_self_slot_returns_occupied() {
403 let mut allocator = crate::mem::phys::BitmapFrameAllocator;
404 let mut ptable = PROCESSES.lock();
405
406 let created = ptable.allocate(&mut allocator).expect("alloc");
407 ptable.start(created).expect("start");
408 let pid = created.pid();
409 bootstrap_test_cnode(pid, &mut ptable);
410
411 let address = 60u64;
412 let (cnode_id, cnode_gen, depth) = cnode::cnode_coords(pid, &ptable).expect("cnode coords");
413 {
414 let mut pool = POOL.lock_after(&ptable);
415 ops::create_via_cnode(&mut pool, cnode_id, cnode_gen, address, depth, ObjectTag::Endpoint)
416 .expect("create source");
417 }
418
419 {
420 let mut pool = POOL.lock_after(&ptable);
421 let result = ops::derive_via_cnode(&mut pool, cnode_id, cnode_gen, address, address, depth, Rights::READ);
422 assert!(
423 matches!(result, Err(KernelError::SlotOccupied)),
424 "derive into same slot should return SlotOccupied, got {:?}",
425 result
426 );
427 }
428
429 {
430 let pool = POOL.lock_after(&ptable);
431 let cap = cnode::resolve_and_read(&pool, cnode_id, cnode_gen, address, depth)
432 .expect("read slot");
433 assert!(
434 cap.tag() == ObjectTag::Endpoint,
435 "original cap should still be intact"
436 );
437 }
438
439 ptable.destroy(pid, &mut allocator);
440 }
441);
442
443crate::kernel_test!(
444 fn identify_after_revoke_fails() {
445 let mut allocator = crate::mem::phys::BitmapFrameAllocator;
446 let mut ptable = PROCESSES.lock();
447
448 let created = ptable.allocate(&mut allocator).expect("alloc");
449 ptable.start(created).expect("start");
450 let pid = created.pid();
451 bootstrap_test_cnode(pid, &mut ptable);
452
453 let address = 70u64;
454 let (cnode_id, cnode_gen, depth) = cnode::cnode_coords(pid, &ptable).expect("cnode coords");
455 {
456 let mut pool = POOL.lock_after(&ptable);
457 ops::create_via_cnode(&mut pool, cnode_id, cnode_gen, address, depth, ObjectTag::Notification)
458 .expect("create notification");
459 }
460
461 ops::revoke_via_cnode(pid, address, &mut ptable).expect("revoke");
462
463 {
464 let pool = POOL.lock_after(&ptable);
465 let result = ops::identify_via_cnode(&pool, cnode_id, cnode_gen, address, depth);
466 assert!(
467 matches!(result, Err(KernelError::SlotEmpty)),
468 "identify after revoke should return SlotEmpty"
469 );
470 }
471
472 ptable.destroy(pid, &mut allocator);
473 }
474);
475
476crate::kernel_test!(
477 fn priority_boost_only_increases() {
478 let mut allocator = crate::mem::phys::BitmapFrameAllocator;
479 let mut ptable = PROCESSES.lock();
480
481 let created = ptable.allocate(&mut allocator).expect("alloc");
482 ptable.start(created).expect("start");
483 let pid = created.pid();
484
485 ptable[pid].boost_effective_priority(Priority::new(200));
486 assert!(
487 ptable[pid].effective_priority() == Priority::new(200),
488 "priority should be boosted to 200"
489 );
490
491 ptable[pid].boost_effective_priority(Priority::new(150));
492 assert!(
493 ptable[pid].effective_priority() == Priority::new(200),
494 "lower boost should not decrease effective priority"
495 );
496
497 ptable.destroy(pid, &mut allocator);
498 }
499);
500
501crate::kernel_test!(
502 fn reset_effective_restores_base() {
503 let mut allocator = crate::mem::phys::BitmapFrameAllocator;
504 let mut ptable = PROCESSES.lock();
505
506 let created = ptable.allocate(&mut allocator).expect("alloc");
507 ptable.start(created).expect("start");
508 let pid = created.pid();
509
510 let base = ptable[pid].effective_priority();
511 ptable[pid].boost_effective_priority(Priority::new(200));
512 assert!(
513 ptable[pid].effective_priority() == Priority::new(200),
514 "should be boosted"
515 );
516
517 ptable[pid].reset_effective_priority();
518 assert!(
519 ptable[pid].effective_priority() == base,
520 "reset should restore base priority"
521 );
522
523 ptable.destroy(pid, &mut allocator);
524 }
525);
526
527crate::kernel_test!(
528 fn reply_target_cleared_on_destroy() {
529 let mut allocator = crate::mem::phys::BitmapFrameAllocator;
530 let mut ptable = PROCESSES.lock();
531
532 let a_created = ptable.allocate(&mut allocator).expect("alloc A");
533 let b_created = ptable.allocate(&mut allocator).expect("alloc B");
534 ptable.start(a_created).expect("start A");
535 ptable.start(b_created).expect("start B");
536 let a_pid = a_created.pid();
537 let b_pid = b_created.pid();
538
539 ptable[a_pid].reply_target = Some(b_pid);
540
541 ptable.destroy(b_pid, &mut allocator);
542
543 assert!(
544 ptable[a_pid].reply_target.is_none(),
545 "reply_target for pid {} should be None after target pid {} was destroyed",
546 a_pid.raw(),
547 b_pid.raw()
548 );
549
550 ptable.destroy(a_pid, &mut allocator);
551 }
552);