Nothing to see here, move along
at main 552 lines 20 kB view raw
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);