use super::object::{EndpointData, NotificationData, ObjectData, ObjectTag}; use super::pool::POOL; use super::table::{CapRef, Rights}; use crate::error::KernelError; use crate::types::{Generation, ObjectId}; pub fn cleanup_object_data(data: &ObjectData) { match data { ObjectData::IrqHandler(irq) => { crate::irq::unbind_by_vector(irq.vector); } ObjectData::CNode(cnode) => { super::cnode::destroy_cnode(cnode, &crate::mem::phys::BitmapFrameAllocator); } ObjectData::Endpoint(_) | ObjectData::Notification(_) | ObjectData::Process(_) | ObjectData::SchedContext(_) | ObjectData::Framebuffer(_) | ObjectData::PciDevice(_) | ObjectData::Untyped(_) | ObjectData::Frame(_) => {} } } pub fn cleanup_object_data_with_ptable(data: &ObjectData, ptable: &mut crate::proc::ProcessManager) { cleanup_object_data(data); match data { ObjectData::Endpoint(ep) => { unblock_queue(&ep.senders, ptable); unblock_queue(&ep.receivers, ptable); } ObjectData::Notification(notif) => { notif.waiters.iter().for_each(|&pid| { let proof = ptable[pid].blocked_proof(); let proc = &mut ptable[pid]; match proc.unblock(proof) { Ok(()) => { proc.saved_context.rax = crate::error::KernelError::InvalidObject.to_errno() as u64; proc.seal_context(); } Err(e) => { crate::kprintln!( "[cap] BUG: notification cleanup failed to unblock pid {}: {:?}", pid.raw(), e ); } } }); } ObjectData::SchedContext(sc) => { sc.attached_pid.inspect(|&pid| { if let Some(proc) = ptable.get_mut(pid) { proc.detach_sched_context(); } }); } ObjectData::Frame(frame) => { let phys = frame.phys_addr; let owned_by_untyped = frame.from_untyped; frame.mappings.iter().for_each(|mapping| { if let Some(proc) = ptable.get(mapping.pid) { let _ = crate::proc::address_space::unmap_user_page(proc.pml4_phys, mapping.vaddr); match crate::mem::refcount::decrement(phys) { Ok(0) if !owned_by_untyped => { crate::mem::phys::BitmapFrameAllocator::free_frame_by_addr(phys) } Ok(_) => {} Err(e) => crate::kprintln!( "[cap] frame refcount decrement failed: {:#x} {:?}", phys.as_u64(), e ), } } }); } ObjectData::Process(_) | ObjectData::Framebuffer(_) | ObjectData::PciDevice(_) | ObjectData::Untyped(_) => {} ObjectData::CNode(_) | ObjectData::IrqHandler(_) => {} } } fn unblock_queue(queue: &super::object::PidQueue, ptable: &mut crate::proc::ProcessManager) { let mut cursor = queue.head; let mut steps = 0u16; let max = crate::types::MAX_PIDS as u16; core::iter::from_fn(|| { cursor.filter(|_| steps < max).inspect(|&pid| { steps += 1; cursor = ptable[pid].next_ipc; ptable[pid].next_ipc = None; let proof = ptable[pid].blocked_proof(); match ptable[pid].unblock(proof) { Ok(()) => { ptable[pid].saved_context.rax = crate::error::KernelError::InvalidObject.to_errno() as u64; ptable[pid].seal_context(); } Err(e) => { crate::kprintln!( "[cap] BUG: unblock_queue failed to unblock pid {}: {:?}", pid.raw(), e ); } } ptable.clear_reply_targets_for(pid); }) }) .count(); } pub fn resolve_process_cap( cap: &CapRef, pool: &super::pool::ObjectPool, ) -> Result { pool.get(cap.object_id(), cap.generation())? .as_process() .map(|p| p.pid) } pub fn create_via_cnode( pool: &mut super::pool::ObjectPool, cnode_id: ObjectId, cnode_gen: Generation, address: u64, depth: u8, tag: ObjectTag, ) -> Result { let data = match tag { ObjectTag::Endpoint => ObjectData::Endpoint(EndpointData::new()), ObjectTag::Notification => ObjectData::Notification(NotificationData::new()), ObjectTag::Process | ObjectTag::SchedContext | ObjectTag::IrqHandler | ObjectTag::Framebuffer | ObjectTag::PciDevice | ObjectTag::CNode | ObjectTag::Untyped | ObjectTag::Frame | ObjectTag::MemoryRegion => { return Err(KernelError::InvalidType); } }; let (object_id, obj_gen) = pool.allocate(data)?; let cap = CapRef::new(tag, object_id, Rights::ALL, obj_gen); match super::cnode::resolve_and_insert(pool, cnode_id, cnode_gen, address, depth, cap) { Ok(()) => Ok(object_id), Err(e) => { let r = pool.free(object_id, obj_gen); debug_assert!(r.is_ok()); Err(e) } } } pub fn derive_via_cnode( pool: &mut super::pool::ObjectPool, cnode_id: ObjectId, cnode_gen: Generation, src_addr: u64, dest_addr: u64, depth: u8, rights_mask: Rights, ) -> Result<(), KernelError> { let src = super::cnode::resolve_and_read(pool, cnode_id, cnode_gen, src_addr, depth)?; if !src.rights().contains(Rights::GRANT) { return Err(KernelError::InsufficientRights); } pool.inc_ref(src.object_id(), src.generation())?; let derived = src.with_rights(src.rights() & rights_mask); match super::cnode::resolve_and_insert(pool, cnode_id, cnode_gen, dest_addr, depth, derived) { Ok(()) => Ok(()), Err(e) => { pool.dec_ref(src.object_id(), src.generation()); Err(e) } } } pub fn identify_via_cnode( pool: &super::pool::ObjectPool, cnode_id: ObjectId, cnode_gen: Generation, address: u64, depth: u8, ) -> Result<(ObjectTag, Rights), KernelError> { let cap = super::cnode::resolve_and_read(pool, cnode_id, cnode_gen, address, depth)?; match pool.get(cap.object_id(), cap.generation()) { Ok(_) => Ok((cap.tag(), cap.rights())), Err(KernelError::StaleGeneration) => { let _ = super::cnode::resolve_and_clear(pool, cnode_id, cnode_gen, address, depth); Err(KernelError::StaleGeneration) } Err(e) => Err(e), } } pub fn insert_object_cap_via_cnode( pool: &mut super::pool::ObjectPool, cnode_id: ObjectId, cnode_gen: Generation, address: u64, depth: u8, data: ObjectData, rights: Rights, ) -> Result { let tag = data.tag(); let (object_id, obj_gen) = pool.allocate(data)?; let cap = CapRef::new(tag, object_id, rights, obj_gen); match super::cnode::resolve_and_insert(pool, cnode_id, cnode_gen, address, depth, cap) { Ok(()) => Ok(object_id), Err(e) => { let r = pool.free(object_id, obj_gen); debug_assert!(r.is_ok()); Err(e) } } } pub fn revoke_via_cnode( pid: crate::types::Pid, address: u64, ptable: &mut crate::proc::ProcessManager, ) -> Result<(), KernelError> { let (cnode_id, cnode_gen, depth) = super::cnode::cnode_coords(pid, ptable)?; let cap_snapshot = { let pool = POOL.lock(); super::cnode::resolve_and_read(&pool, cnode_id, cnode_gen, address, depth)? }; if !cap_snapshot.rights().contains(Rights::REVOKE) { return Err(KernelError::InsufficientRights); } let stale_oid = cap_snapshot.object_id(); let stale_gen = cap_snapshot.generation(); let (_new_gen, old_data) = POOL.lock().revoke(stale_oid, stale_gen)?; { let pool = POOL.lock(); let _ = super::cnode::resolve_and_clear(&pool, cnode_id, cnode_gen, address, depth); invalidate_stale_caps_via_cnode(ptable, &pool, stale_oid, stale_gen); } old_data.inspect(|data| cleanup_object_data_with_ptable(data, ptable)); Ok(()) } pub fn invalidate_stale_caps_via_cnode( ptable: &crate::proc::ProcessManager, pool: &super::pool::ObjectPool, object_id: ObjectId, stale_gen: Generation, ) { let cap = ptable.capacity(); (0..cap as u16) .filter_map(crate::types::Pid::try_new) .filter_map(|pid| { ptable.get(pid).and_then(|p| p.root_cnode()) }) .for_each(|(cid, cgen)| { let _ = super::cnode::invalidate_stale_in_cnode(pool, cid, cgen, object_id, stale_gen); }); }