use crate::cap::cnode; use crate::cap::object::ObjectTag; use crate::cap::pool::POOL; use crate::cap::table::Rights; use crate::ipc::{AlwaysBlocked, IpcOutcome, endpoint, message}; use crate::proc::BlockedReason; use crate::proc::context::CpuContext; use crate::proc::{PROCESSES, ProcessState, ProcessManager}; use crate::syscall::{SyscallResult, try_syscall}; use crate::types::Pid; fn drain_bound_notification(pid: Pid, ptable: &ProcessManager) -> Option { let (notif_id, notif_gen) = ptable.get(pid)?.bound_notification()?; let mut pool = POOL.lock(); pool.get_mut(notif_id, notif_gen) .and_then(|d| d.as_notification_mut()) .ok() .and_then(|notif| { (notif.word != 0).then(|| { let w = notif.word; notif.word = 0; w }) }) } pub fn sys_send(ctx: &mut CpuContext) { let cap_addr = ctx.rdi; let pid = crate::arch::syscall::current_pid(); let msg = message::extract_from_context(ctx); let mut ptable = PROCESSES.lock(); let cap = { let pool = POOL.lock_after(&ptable); try_syscall!( ctx, cnode::resolve_caller_validate( pid, cap_addr, ObjectTag::Endpoint, Rights::WRITE, &ptable, &pool, ) ) }; ptable[pid].ipc_message = msg; match endpoint::do_send(&cap, pid, &mut ptable) { Ok(IpcOutcome::Done(())) => ctx.rax = SyscallResult::ok().raw(), Ok(IpcOutcome::Blocked) => { ctx.rax = SyscallResult::ok().raw(); drop(ptable); crate::sched::schedule(ctx); } Err(e) => ctx.rax = SyscallResult::error(e).raw(), } } pub fn sys_recv(ctx: &mut CpuContext) { let cap_addr = ctx.rdi; let pid = crate::arch::syscall::current_pid(); let mut ptable = PROCESSES.lock(); let cap = { let pool = POOL.lock_after(&ptable); try_syscall!( ctx, cnode::resolve_caller_validate( pid, cap_addr, ObjectTag::Endpoint, Rights::READ, &ptable, &pool, ) ) }; if let Some(word) = drain_bound_notification(pid, &ptable) { ctx.rax = crate::ipc::notification::BOUND_NOTIFICATION_BADGE; ctx.rdx = word; return; } match endpoint::do_recv(&cap, pid, &mut ptable) { Ok(IpcOutcome::Done(sender_pid)) => { let msg = ptable[pid].ipc_message; message::inject_into_context(ctx, &msg); ctx.rax = SyscallResult::success(sender_pid.raw() as u64).raw(); } Ok(IpcOutcome::Blocked) => { ctx.rax = SyscallResult::ok().raw(); drop(ptable); crate::sched::schedule(ctx); } Err(e) => ctx.rax = SyscallResult::error(e).raw(), } } pub fn sys_nb_recv(ctx: &mut CpuContext) { let cap_addr = ctx.rdi; let pid = crate::arch::syscall::current_pid(); let mut ptable = PROCESSES.lock(); let cap = { let pool = POOL.lock_after(&ptable); try_syscall!( ctx, cnode::resolve_caller_validate( pid, cap_addr, ObjectTag::Endpoint, Rights::READ, &ptable, &pool, ) ) }; if let Some(word) = drain_bound_notification(pid, &ptable) { ctx.rax = crate::ipc::notification::BOUND_NOTIFICATION_BADGE; ctx.rdx = word; return; } match endpoint::do_try_recv(&cap, pid, &mut ptable) { Ok(IpcOutcome::Done(sender_pid)) => { let msg = ptable[pid].ipc_message; message::inject_into_context(ctx, &msg); ctx.rax = SyscallResult::success(sender_pid.raw() as u64).raw(); } Err(e) => ctx.rax = SyscallResult::error(e).raw(), Ok(IpcOutcome::Blocked) => unreachable!(), } } pub fn sys_call(ctx: &mut CpuContext) { let cap_addr = ctx.rdi; let pid = crate::arch::syscall::current_pid(); let msg = message::extract_from_context(ctx); let mut ptable = PROCESSES.lock(); let cap = { let pool = POOL.lock_after(&ptable); let needs = Rights::READ | Rights::WRITE; try_syscall!( ctx, cnode::resolve_caller_validate( pid, cap_addr, ObjectTag::Endpoint, needs, &ptable, &pool, ) ) }; ptable[pid].ipc_message = msg; match endpoint::do_call(&cap, pid, &mut ptable) { Ok(AlwaysBlocked) => { ctx.rax = SyscallResult::ok().raw(); drop(ptable); crate::sched::schedule(ctx); } Err(e) => ctx.rax = SyscallResult::error(e).raw(), } } pub fn sys_reply_recv(ctx: &mut CpuContext) { let cap_addr = ctx.rdi; let reply_to_raw = try_syscall!(ctx, super::u16_from_reg(ctx.rsi)); let pid = crate::arch::syscall::current_pid(); let msg = message::extract_reply_from_context(ctx); let mut ptable = PROCESSES.lock(); let cap = { let pool = POOL.lock_after(&ptable); let needs = Rights::READ | Rights::WRITE; try_syscall!( ctx, cnode::resolve_caller_validate( pid, cap_addr, ObjectTag::Endpoint, needs, &ptable, &pool, ) ) }; let reply_delivered = match Pid::try_new(reply_to_raw) { Some(reply_pid) => { let reply_target_ok = ptable[pid].reply_target == Some(reply_pid); let target = &ptable[reply_pid]; let is_call_target = reply_target_ok && target.state() == ProcessState::Blocked && matches!( target.blocked_reason(), Some(BlockedReason::Calling(id, _)) if id == cap.object_id() ); if is_call_target { let proof = ptable[reply_pid].blocked_proof(); { let mut pool = POOL.lock_after(&ptable); if let Ok(ep) = pool .get_mut(cap.object_id(), cap.generation()) .and_then(|d| d.as_endpoint_mut()) { endpoint::remove_from_recv(ep, reply_pid, &mut ptable); } } let full_msg = msg.into_full(); let unblocked = { let target = &mut ptable[reply_pid]; target.ipc_message = full_msg; message::inject_into_context(&mut target.saved_context, &full_msg); target.saved_context.rax = SyscallResult::success(pid.raw() as u64).raw(); target.seal_context(); target.unblock(proof).is_ok() }; if unblocked { ptable[pid].reply_target = None; true } else { false } } else { false } } None => false, }; ctx.rbx = if reply_delivered { 0 } else { 1 }; if let Some(word) = drain_bound_notification(pid, &ptable) { ctx.rax = crate::ipc::notification::BOUND_NOTIFICATION_BADGE; ctx.rdx = word; return; } match endpoint::do_recv(&cap, pid, &mut ptable) { Ok(IpcOutcome::Done(sender_pid)) => { let msg = ptable[pid].ipc_message; message::inject_into_context(ctx, &msg); ctx.rax = SyscallResult::success(sender_pid.raw() as u64).raw(); } Ok(IpcOutcome::Blocked) => { ctx.rax = SyscallResult::ok().raw(); drop(ptable); crate::sched::schedule(ctx); } Err(e) => ctx.rax = SyscallResult::error(e).raw(), } }