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, ProcessManager, ProcessState}; use crate::syscall::{SyscallResult, try_syscall}; use crate::types::Pid; use lancer_core::object_layout::{EndpointObject, NotificationObject}; 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.write_as::(notif_id, notif_gen) .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.exec_mut(pid).unwrap().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.exec(pid).unwrap().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.exec(pid).unwrap().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.exec_mut(pid).unwrap().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::u32_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.exec(pid).unwrap().reply_target == Some(reply_pid); let target_sched = &ptable[reply_pid]; let is_call_target = reply_target_ok && target_sched.state() == ProcessState::Blocked && matches!( target_sched.blocked_reason(), Some(BlockedReason::Calling(id, _)) if id == cap.phys() ); if is_call_target { let proof = ptable[reply_pid].blocked_proof(); { let mut pool = POOL.lock_after(&ptable); if let Ok(ep) = pool.write_as::(cap.phys(), cap.generation()) { endpoint::remove_from_recv(ep, reply_pid, &mut ptable); } } let full_msg = msg.into_full(); let exec_target = ptable.exec_mut(reply_pid).unwrap(); exec_target.ipc_message = full_msg; message::inject_into_context(&mut exec_target.saved_context, &full_msg); exec_target.saved_context.rax = SyscallResult::success(pid.raw() as u64).raw(); exec_target.seal_context(); let unblocked = ptable.unblock_and_enqueue(reply_pid, proof).is_ok(); if unblocked { ptable.exec_mut(pid).unwrap().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.exec(pid).unwrap().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(), } }