use crate::cap::cnode; use crate::cap::object::{ EndpointData, NotificationData, ObjectData, ObjectTag, ProcessObjectData, SchedContextData, }; use crate::cap::ops; use crate::cap::pool::POOL; use crate::cap::table::{CapRef, Rights}; use crate::error::KernelError; use crate::ipc::endpoint; use crate::proc::{BlockedReason, PROCESSES, ProcessState}; use crate::types::{Pid, Priority}; fn bootstrap_test_cnode(pid: Pid, ptable: &mut crate::proc::ProcessManager) { let size_bits = crate::proc::ROOT_CNODE_SIZE_BITS; let allocator = &crate::mem::phys::BitmapFrameAllocator; let cnode_data = crate::cap::cnode::create_cnode(size_bits, allocator).expect("create cnode"); let frame_count = cnode_data.frame_count; let (cnode_id, cnode_gen) = crate::cap::pool::POOL .lock() .allocate(crate::cap::object::ObjectData::CNode(cnode_data)) .expect("alloc cnode"); let proc = ptable.get_mut(pid).expect("get proc"); proc.root_cnode = Some((cnode_id, cnode_gen)); proc.cnode_depth = size_bits; proc.charge_frames(frame_count as u16).expect("charge frames"); } crate::kernel_test!( fn create_endpoint_inserts_cap() { let mut allocator = crate::mem::phys::BitmapFrameAllocator; let mut ptable = PROCESSES.lock(); let created = ptable.allocate(&mut allocator).expect("alloc"); ptable.start(created).expect("start"); let pid = created.pid(); bootstrap_test_cnode(pid, &mut ptable); let address = 10u64; let (cnode_id, cnode_gen, depth) = cnode::cnode_coords(pid, &ptable).expect("coords"); let oid = { let mut pool = POOL.lock_after(&ptable); ops::create_via_cnode(&mut pool, cnode_id, cnode_gen, address, depth, ObjectTag::Endpoint) .expect("create endpoint") }; { let pool = POOL.lock_after(&ptable); let cap = cnode::resolve_and_read(&pool, cnode_id, cnode_gen, address, depth) .expect("read slot"); assert!(cap.tag() == ObjectTag::Endpoint); assert!(cap.rights().contains(Rights::ALL)); assert!(cap.object_id() == oid); } ptable.destroy(pid, &mut allocator); } ); crate::kernel_test!( fn create_notification_inserts_cap() { let mut allocator = crate::mem::phys::BitmapFrameAllocator; let mut ptable = PROCESSES.lock(); let created = ptable.allocate(&mut allocator).expect("alloc"); ptable.start(created).expect("start"); let pid = created.pid(); bootstrap_test_cnode(pid, &mut ptable); let address = 11u64; let (cnode_id, cnode_gen, depth) = cnode::cnode_coords(pid, &ptable).expect("coords"); let oid = { let mut pool = POOL.lock_after(&ptable); ops::create_via_cnode(&mut pool, cnode_id, cnode_gen, address, depth, ObjectTag::Notification) .expect("create notification") }; { let pool = POOL.lock_after(&ptable); let cap = cnode::resolve_and_read(&pool, cnode_id, cnode_gen, address, depth) .expect("read slot"); assert!(cap.tag() == ObjectTag::Notification); assert!(cap.object_id() == oid); } ptable.destroy(pid, &mut allocator); } ); crate::kernel_test!( fn create_memory_region_rejected() { let mut allocator = crate::mem::phys::BitmapFrameAllocator; let mut ptable = PROCESSES.lock(); let created = ptable.allocate(&mut allocator).expect("alloc"); ptable.start(created).expect("start"); let pid = created.pid(); bootstrap_test_cnode(pid, &mut ptable); let address = 12u64; let (cnode_id, cnode_gen, depth) = cnode::cnode_coords(pid, &ptable).expect("coords"); let result = { let mut pool = POOL.lock_after(&ptable); ops::create_via_cnode(&mut pool, cnode_id, cnode_gen, address, depth, ObjectTag::MemoryRegion) }; assert!( matches!(result, Err(KernelError::InvalidType)), "creating a MemoryRegion cap via create_via_cnode must fail" ); ptable.destroy(pid, &mut allocator); } ); crate::kernel_test!( fn create_invalid_tag_rejected() { let mut allocator = crate::mem::phys::BitmapFrameAllocator; let mut ptable = PROCESSES.lock(); let created = ptable.allocate(&mut allocator).expect("alloc"); ptable.start(created).expect("start"); let pid = created.pid(); bootstrap_test_cnode(pid, &mut ptable); let address = 13u64; let (cnode_id, cnode_gen, depth) = cnode::cnode_coords(pid, &ptable).expect("coords"); let result = { let mut pool = POOL.lock_after(&ptable); ops::create_via_cnode(&mut pool, cnode_id, cnode_gen, address, depth, ObjectTag::Process) }; assert!( matches!(result, Err(KernelError::InvalidType)), "creating a Process cap via ops::create must fail" ); ptable.destroy(pid, &mut allocator); } ); crate::kernel_test!( fn create_occupied_slot_rolls_back() { let mut allocator = crate::mem::phys::BitmapFrameAllocator; let mut ptable = PROCESSES.lock(); let created = ptable.allocate(&mut allocator).expect("alloc"); ptable.start(created).expect("start"); let pid = created.pid(); bootstrap_test_cnode(pid, &mut ptable); let address = 14u64; let (cnode_id, cnode_gen, depth) = cnode::cnode_coords(pid, &ptable).expect("coords"); { let mut pool = POOL.lock_after(&ptable); ops::create_via_cnode(&mut pool, cnode_id, cnode_gen, address, depth, ObjectTag::Endpoint) .expect("first create"); } let result = { let mut pool = POOL.lock_after(&ptable); ops::create_via_cnode(&mut pool, cnode_id, cnode_gen, address, depth, ObjectTag::Notification) }; assert!( matches!(result, Err(KernelError::SlotOccupied)), "second create into occupied slot must fail" ); { let pool = POOL.lock_after(&ptable); let cap = cnode::resolve_and_read(&pool, cnode_id, cnode_gen, address, depth) .expect("read slot"); assert!( cap.tag() == ObjectTag::Endpoint, "original cap should still be in the slot" ); } ptable.destroy(pid, &mut allocator); } ); crate::kernel_test!( fn derive_attenuates_rights() { let mut allocator = crate::mem::phys::BitmapFrameAllocator; let mut ptable = PROCESSES.lock(); let created = ptable.allocate(&mut allocator).expect("alloc"); ptable.start(created).expect("start"); let pid = created.pid(); bootstrap_test_cnode(pid, &mut ptable); let src_addr = 20u64; let dest_addr = 21u64; let (cnode_id, cnode_gen, depth) = cnode::cnode_coords(pid, &ptable).expect("coords"); { let mut pool = POOL.lock_after(&ptable); ops::create_via_cnode(&mut pool, cnode_id, cnode_gen, src_addr, depth, ObjectTag::Endpoint) .expect("create source"); } { let mut pool = POOL.lock_after(&ptable); ops::derive_via_cnode(&mut pool, cnode_id, cnode_gen, src_addr, dest_addr, depth, Rights::READ) .expect("derive"); } { let pool = POOL.lock_after(&ptable); let cap = cnode::resolve_and_read(&pool, cnode_id, cnode_gen, dest_addr, depth) .expect("read derived slot"); assert!(cap.rights().contains(Rights::READ)); assert!(!cap.rights().contains(Rights::WRITE)); assert!(!cap.rights().contains(Rights::REVOKE)); } ptable.destroy(pid, &mut allocator); } ); crate::kernel_test!( fn derive_without_grant_fails() { let mut allocator = crate::mem::phys::BitmapFrameAllocator; let mut ptable = PROCESSES.lock(); let created = ptable.allocate(&mut allocator).expect("alloc"); ptable.start(created).expect("start"); let pid = created.pid(); bootstrap_test_cnode(pid, &mut ptable); let src_addr = 30u64; let dest_addr = 31u64; let (cnode_id, cnode_gen, depth) = cnode::cnode_coords(pid, &ptable).expect("coords"); { let mut pool = POOL.lock_after(&ptable); ops::create_via_cnode(&mut pool, cnode_id, cnode_gen, src_addr, depth, ObjectTag::Endpoint) .expect("create source"); } { let pool = POOL.lock_after(&ptable); let old_cap = cnode::resolve_and_clear(&pool, cnode_id, cnode_gen, src_addr, depth) .expect("clear slot"); let new_cap = old_cap.with_rights(Rights::READ | Rights::WRITE); cnode::resolve_and_insert(&pool, cnode_id, cnode_gen, src_addr, depth, new_cap) .expect("reinsert cap"); } let result = { let mut pool = POOL.lock_after(&ptable); ops::derive_via_cnode(&mut pool, cnode_id, cnode_gen, src_addr, dest_addr, depth, Rights::READ) }; assert!( matches!(result, Err(KernelError::InsufficientRights)), "derive without GRANT right must fail" ); ptable.destroy(pid, &mut allocator); } ); crate::kernel_test!( fn identify_returns_tag_and_rights() { let mut allocator = crate::mem::phys::BitmapFrameAllocator; let mut ptable = PROCESSES.lock(); let created = ptable.allocate(&mut allocator).expect("alloc"); ptable.start(created).expect("start"); let pid = created.pid(); bootstrap_test_cnode(pid, &mut ptable); let address = 40u64; let (cnode_id, cnode_gen, depth) = cnode::cnode_coords(pid, &ptable).expect("coords"); { let mut pool = POOL.lock_after(&ptable); ops::create_via_cnode(&mut pool, cnode_id, cnode_gen, address, depth, ObjectTag::Notification) .expect("create notification"); } let (tag, rights) = { let pool = POOL.lock_after(&ptable); ops::identify_via_cnode(&pool, cnode_id, cnode_gen, address, depth) .expect("identify") }; assert!(tag == ObjectTag::Notification); assert!(rights.contains(Rights::ALL)); ptable.destroy(pid, &mut allocator); } ); crate::kernel_test!( fn endpoint_revoke_unblocks_senders() { let mut allocator = crate::mem::phys::BitmapFrameAllocator; let mut ptable = PROCESSES.lock(); let owner_created = ptable.allocate(&mut allocator).expect("alloc owner"); let sender_created = ptable.allocate(&mut allocator).expect("alloc sender"); ptable.start(owner_created).expect("start owner"); ptable.start(sender_created).expect("start sender"); let owner_pid = owner_created.pid(); let sender_pid = sender_created.pid(); bootstrap_test_cnode(owner_pid, &mut ptable); let (ep_id, ep_gen) = POOL .lock_after(&ptable) .allocate(ObjectData::Endpoint(EndpointData::new())) .expect("alloc ep"); let cap = CapRef::new(ObjectTag::Endpoint, ep_id, Rights::ALL, ep_gen); let address = 0u64; let (cnode_id, cnode_gen, depth) = cnode::cnode_coords(owner_pid, &ptable).expect("coords"); { let pool = POOL.lock_after(&ptable); cnode::resolve_and_insert(&pool, cnode_id, cnode_gen, address, depth, cap) .expect("insert cap"); } ptable[sender_pid] .transition_to(ProcessState::Running) .expect("sender -> Running"); let blocked = ptable[sender_pid] .block_on(BlockedReason::Sending(ep_id, ep_gen)) .expect("block sender"); { let mut pool = POOL.lock_after(&ptable); let ep = pool .get_mut(ep_id, ep_gen) .and_then(|d| d.as_endpoint_mut()) .expect("get ep"); endpoint::enqueue(&mut ep.senders, blocked, &mut ptable).expect("enqueue"); } ops::revoke_via_cnode(owner_pid, address, &mut ptable).expect("revoke"); assert!( ptable[sender_pid].state() == ProcessState::Ready, "sender should be unblocked after endpoint revoke" ); assert!( ptable[sender_pid].saved_context.rax == KernelError::InvalidObject.to_errno() as u64, "sender's rax should contain InvalidObject errno after endpoint revoke" ); ptable.destroy(owner_pid, &mut allocator); ptable.destroy(sender_pid, &mut allocator); } ); crate::kernel_test!( fn notification_revoke_unblocks_waiters() { let mut allocator = crate::mem::phys::BitmapFrameAllocator; let mut ptable = PROCESSES.lock(); let owner_created = ptable.allocate(&mut allocator).expect("alloc owner"); let waiter_created = ptable.allocate(&mut allocator).expect("alloc waiter"); ptable.start(owner_created).expect("start owner"); ptable.start(waiter_created).expect("start waiter"); let owner_pid = owner_created.pid(); let waiter_pid = waiter_created.pid(); bootstrap_test_cnode(owner_pid, &mut ptable); let (notif_id, notif_gen) = POOL .lock_after(&ptable) .allocate(ObjectData::Notification(NotificationData::new())) .expect("alloc notif"); let cap = CapRef::new(ObjectTag::Notification, notif_id, Rights::ALL, notif_gen); let address = 0u64; let (cnode_id, cnode_gen, depth) = cnode::cnode_coords(owner_pid, &ptable).expect("coords"); { let pool = POOL.lock_after(&ptable); cnode::resolve_and_insert(&pool, cnode_id, cnode_gen, address, depth, cap) .expect("insert cap"); } ptable[waiter_pid] .transition_to(ProcessState::Running) .expect("waiter -> Running"); let _ = ptable[waiter_pid] .block_on(BlockedReason::WaitingNotification(notif_id, notif_gen)) .expect("block waiter"); { let mut pool = POOL.lock_after(&ptable); let notif = pool .get_mut(notif_id, notif_gen) .and_then(|d| d.as_notification_mut()) .expect("get notif"); let _ = notif.waiters.push(waiter_pid); } ops::revoke_via_cnode(owner_pid, address, &mut ptable).expect("revoke"); assert!( ptable[waiter_pid].state() == ProcessState::Ready, "waiter should be unblocked after notification revoke" ); assert!( ptable[waiter_pid].saved_context.rax == KernelError::InvalidObject.to_errno() as u64, "waiter's rax should contain InvalidObject errno after notification revoke" ); ptable.destroy(owner_pid, &mut allocator); ptable.destroy(waiter_pid, &mut allocator); } ); crate::kernel_test!( fn proc_destroy_decrements_pool_refcount() { let mut allocator = crate::mem::phys::BitmapFrameAllocator; let mut ptable = PROCESSES.lock(); let parent_created = ptable.allocate(&mut allocator).expect("alloc parent"); let child_created = ptable.allocate(&mut allocator).expect("alloc child"); ptable.start(parent_created).expect("start parent"); ptable.start(child_created).expect("start child"); let parent_pid = parent_created.pid(); let child_pid = child_created.pid(); bootstrap_test_cnode(parent_pid, &mut ptable); let data = ObjectData::Process(ProcessObjectData { pid: child_pid }); let tag = ObjectTag::Process; let (obj_id, generation) = POOL.lock_after(&ptable).allocate(data).expect("alloc process object"); let cap = CapRef::new(tag, obj_id, Rights::ALL, generation); let address = 0u64; let (cnode_id, cnode_gen, depth) = cnode::cnode_coords(parent_pid, &ptable).expect("coords"); { let pool = POOL.lock_after(&ptable); cnode::resolve_and_insert(&pool, cnode_id, cnode_gen, address, depth, cap) .expect("insert cap"); } POOL.lock_after(&ptable).inc_ref(obj_id, generation).expect("inc_ref"); let refcount_before = POOL.lock_after(&ptable).get(obj_id, generation).is_ok(); assert!(refcount_before, "object should be alive before destroy"); ptable.destroy(child_pid, &mut allocator); POOL.lock_after(&ptable).dec_ref(obj_id, generation); { let pool = POOL.lock_after(&ptable); let _ = cnode::resolve_and_clear(&pool, cnode_id, cnode_gen, address, depth); } ptable.destroy(parent_pid, &mut allocator); } ); crate::kernel_test!( fn sched_context_revoke_detaches_process() { let mut allocator = crate::mem::phys::BitmapFrameAllocator; let mut ptable = PROCESSES.lock(); let owner_created = ptable.allocate(&mut allocator).expect("alloc owner"); ptable.start(owner_created).expect("start owner"); let owner_pid = owner_created.pid(); bootstrap_test_cnode(owner_pid, &mut ptable); let sc_data = ObjectData::SchedContext(SchedContextData::new(1000, 10000, Priority::new(100))); let (sc_id, sc_gen) = POOL.lock_after(&ptable).allocate(sc_data).expect("alloc sched context"); { let mut pool = POOL.lock_after(&ptable); let sc = pool .get_mut(sc_id, sc_gen) .and_then(|d| d.as_sched_context_mut()) .expect("get sc"); sc.attached_pid = Some(owner_pid); } let cap = CapRef::new(ObjectTag::SchedContext, sc_id, Rights::ALL, sc_gen); let address = 0u64; let (cnode_id, cnode_gen, depth) = cnode::cnode_coords(owner_pid, &ptable).expect("coords"); { let pool = POOL.lock_after(&ptable); cnode::resolve_and_insert(&pool, cnode_id, cnode_gen, address, depth, cap) .expect("insert cap"); } ptable[owner_pid].attach_sched_context(sc_id, sc_gen, Priority::new(100)); assert!( ptable[owner_pid].sched_context().is_some(), "sched_context should be attached before revoke" ); ops::revoke_via_cnode(owner_pid, address, &mut ptable).expect("revoke sched context"); assert!( ptable[owner_pid].sched_context().is_none(), "sched_context must be detached after SchedContext revoke" ); ptable.destroy(owner_pid, &mut allocator); } ); crate::kernel_test!( fn sched_context_revoke_prevents_scheduling() { let mut allocator = crate::mem::phys::BitmapFrameAllocator; let mut ptable = PROCESSES.lock(); let owner_created = ptable.allocate(&mut allocator).expect("alloc owner"); ptable.start(owner_created).expect("start owner"); let owner_pid = owner_created.pid(); bootstrap_test_cnode(owner_pid, &mut ptable); let sc_data = ObjectData::SchedContext(SchedContextData::new(1000, 10000, Priority::new(200))); let address = 0u64; let (sc_id, sc_gen) = POOL.lock_after(&ptable).allocate(sc_data).expect("alloc sc"); { let mut pool = POOL.lock_after(&ptable); let sc = pool .get_mut(sc_id, sc_gen) .and_then(|d| d.as_sched_context_mut()) .expect("get sc"); sc.attached_pid = Some(owner_pid); } let cap = CapRef::new(ObjectTag::SchedContext, sc_id, Rights::ALL, sc_gen); let (cnode_id, cnode_gen, depth) = cnode::cnode_coords(owner_pid, &ptable).expect("coords"); { let pool = POOL.lock_after(&ptable); cnode::resolve_and_insert(&pool, cnode_id, cnode_gen, address, depth, cap) .expect("insert cap"); } ptable[owner_pid].attach_sched_context(sc_id, sc_gen, Priority::new(200)); assert!( ptable[owner_pid].is_runnable(), "process should be runnable before revoke" ); ops::revoke_via_cnode(owner_pid, address, &mut ptable).expect("revoke"); assert!( ptable[owner_pid].sched_context().is_none(), "sched_context detached after revoke" ); ptable.destroy(owner_pid, &mut allocator); } ); crate::kernel_test!( fn cleanup_sched_context_with_no_attached_process() { let mut allocator = crate::mem::phys::BitmapFrameAllocator; let mut ptable = PROCESSES.lock(); let owner_created = ptable.allocate(&mut allocator).expect("alloc owner"); ptable.start(owner_created).expect("start owner"); let owner_pid = owner_created.pid(); bootstrap_test_cnode(owner_pid, &mut ptable); let sc_data = ObjectData::SchedContext(SchedContextData::new(500, 5000, Priority::new(50))); let address = 0u64; let (sc_id, sc_gen) = POOL.lock_after(&ptable).allocate(sc_data).expect("alloc sc"); let cap = CapRef::new(ObjectTag::SchedContext, sc_id, Rights::ALL, sc_gen); let (cnode_id, cnode_gen, depth) = cnode::cnode_coords(owner_pid, &ptable).expect("coords"); { let pool = POOL.lock_after(&ptable); cnode::resolve_and_insert(&pool, cnode_id, cnode_gen, address, depth, cap) .expect("insert cap"); } ops::revoke_via_cnode(owner_pid, address, &mut ptable).expect("revoke unattached sc"); ptable.destroy(owner_pid, &mut allocator); } ); crate::kernel_test!( fn sched_context_cleanup_different_holder_and_attached() { let mut allocator = crate::mem::phys::BitmapFrameAllocator; let mut ptable = PROCESSES.lock(); let holder_created = ptable.allocate(&mut allocator).expect("alloc holder"); let target_created = ptable.allocate(&mut allocator).expect("alloc target"); ptable.start(holder_created).expect("start holder"); ptable.start(target_created).expect("start target"); let holder_pid = holder_created.pid(); let target_pid = target_created.pid(); bootstrap_test_cnode(holder_pid, &mut ptable); let sc_data = ObjectData::SchedContext(SchedContextData::new(1000, 10000, Priority::new(100))); let (sc_id, sc_gen) = POOL.lock_after(&ptable).allocate(sc_data).expect("alloc sc"); { let mut pool = POOL.lock_after(&ptable); let sc = pool .get_mut(sc_id, sc_gen) .and_then(|d| d.as_sched_context_mut()) .expect("get sc"); sc.attached_pid = Some(target_pid); } ptable[target_pid].attach_sched_context(sc_id, sc_gen, Priority::new(100)); assert!( ptable[target_pid].sched_context().is_some(), "target should have sched context" ); let cap = CapRef::new(ObjectTag::SchedContext, sc_id, Rights::ALL, sc_gen); let address = 0u64; let (cnode_id, cnode_gen, depth) = cnode::cnode_coords(holder_pid, &ptable).expect("coords"); { let pool = POOL.lock_after(&ptable); cnode::resolve_and_insert(&pool, cnode_id, cnode_gen, address, depth, cap) .expect("insert cap"); } ops::revoke_via_cnode(holder_pid, address, &mut ptable).expect("revoke"); assert!( ptable[target_pid].sched_context().is_none(), "target's sched_context must be cleared when holder revokes the SchedContext" ); ptable.destroy(holder_pid, &mut allocator); ptable.destroy(target_pid, &mut allocator); } );