refactor(kernel): split ipc/memory/lib/thread by concern; trim comments
This commit is contained in:
@@ -1,121 +1,22 @@
|
||||
//! Endpoint IPC, notifications and interrupt delivery. docs/ipc.md is the
|
||||
//! specification; the comments below only point at the non-obvious rules.
|
||||
//! Endpoint send/receive, call/reply, capability transfer and minting.
|
||||
|
||||
use domain::{
|
||||
capability::Capability,
|
||||
error::KernelError,
|
||||
message::Message,
|
||||
notification::SignalEffect,
|
||||
resource::Resource,
|
||||
scheduler::Scheduler,
|
||||
thread::{BlockReason, ThreadState},
|
||||
capability::Capability, error::KernelError, message::Message, resource::Resource,
|
||||
thread::BlockReason,
|
||||
};
|
||||
use platform::Platform;
|
||||
use types::{
|
||||
badge::Badge,
|
||||
bounded::CapSlot,
|
||||
generation::Generation,
|
||||
id::{EndpointId, IrqId, NotificationId, ProcessId, ThreadId},
|
||||
id::{EndpointId, ProcessId, ThreadId},
|
||||
permission::{Permission, PermissionSet},
|
||||
};
|
||||
|
||||
use super::{Delivery, DeliveryKind, SyscallOutcome, SyscallResult};
|
||||
use super::super::{Delivery, DeliveryKind, SyscallOutcome, SyscallResult};
|
||||
use super::Outgoing;
|
||||
use crate::Kernel;
|
||||
|
||||
/// A thread named by id and generation, so a stale reference can be told
|
||||
/// apart from a new thread that reused the id.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct ThreadRef {
|
||||
pub tid: ThreadId,
|
||||
pub generation: Generation,
|
||||
}
|
||||
|
||||
/// A message a blocked sender is waiting to hand over.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub(crate) struct Outgoing {
|
||||
message: Message,
|
||||
badge: Badge,
|
||||
/// The sender's endpoint cap had `Delegate`, so a capability may travel.
|
||||
grant: bool,
|
||||
}
|
||||
|
||||
/// Per-thread IPC bookkeeping.
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct IpcState {
|
||||
pub(crate) outgoing: Option<Outgoing>,
|
||||
/// The caller this thread owes a reply to.
|
||||
pub(crate) reply_to: Option<ThreadRef>,
|
||||
pub(crate) bound: Option<NotificationId>,
|
||||
}
|
||||
|
||||
fn rw_delegate() -> PermissionSet {
|
||||
PermissionSet::empty()
|
||||
.with(Permission::Read)
|
||||
.with(Permission::Write)
|
||||
.with(Permission::Delegate)
|
||||
}
|
||||
|
||||
impl<P: Platform> Kernel<P> {
|
||||
pub(crate) fn ipc(&self, tid: ThreadId) -> Option<&IpcState> {
|
||||
self.ipc.get(tid.raw() as usize)
|
||||
}
|
||||
|
||||
pub(crate) fn ipc_mut(&mut self, tid: ThreadId) -> Option<&mut IpcState> {
|
||||
self.ipc.get_mut(tid.raw() as usize)
|
||||
}
|
||||
|
||||
pub(crate) fn set_pending_result(&mut self, tid: ThreadId, result: Option<SyscallResult>) {
|
||||
if let Some(r) = self.pending_results.get_mut(tid.raw() as usize) {
|
||||
*r = result;
|
||||
}
|
||||
}
|
||||
|
||||
fn thread_ref(&self, tid: ThreadId) -> Option<ThreadRef> {
|
||||
Some(ThreadRef {
|
||||
tid,
|
||||
generation: self.threads.generation(tid)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn is_live(&self, r: ThreadRef) -> bool {
|
||||
self.threads.generation(r.tid) == Some(r.generation)
|
||||
}
|
||||
|
||||
fn is_blocked_as(&self, tid: ThreadId, f: impl Fn(&BlockReason) -> bool) -> bool {
|
||||
matches!(self.threads.get(tid).map(|t| *t.state()), Ok(ThreadState::Blocked(r)) if f(&r))
|
||||
}
|
||||
|
||||
/// Blocks the running caller.
|
||||
pub(crate) fn block_caller(&mut self, tid: ThreadId, reason: BlockReason) -> SyscallOutcome {
|
||||
if let Ok(t) = self.threads.get_mut(tid) {
|
||||
let _ = t.block(reason);
|
||||
}
|
||||
self.scheduler.remove(tid);
|
||||
SyscallOutcome::BlockCaller(reason)
|
||||
}
|
||||
|
||||
/// Makes a blocked thread runnable with `result` as its syscall result.
|
||||
pub(crate) fn wake(&mut self, tid: ThreadId, result: SyscallResult) {
|
||||
let Ok(thread) = self.threads.get_mut(tid) else {
|
||||
return;
|
||||
};
|
||||
let priority = thread.priority();
|
||||
if thread.unblock().is_ok() {
|
||||
self.set_pending_result(tid, Some(result));
|
||||
self.make_ready(tid, priority);
|
||||
}
|
||||
}
|
||||
|
||||
/// Releases a caller waiting on `reply` with `NoReply`, if it is still
|
||||
/// that caller and still waiting.
|
||||
pub(crate) fn release_caller(&mut self, reply: ThreadRef) {
|
||||
if self.is_live(reply)
|
||||
&& self.is_blocked_as(reply.tid, |r| *r == BlockReason::AwaitingReply)
|
||||
{
|
||||
self.wake(reply.tid, SyscallResult::Error(KernelError::NoReply));
|
||||
}
|
||||
}
|
||||
|
||||
fn endpoint_cap(
|
||||
&self,
|
||||
pid: ProcessId,
|
||||
@@ -133,22 +34,8 @@ impl<P: Platform> Kernel<P> {
|
||||
}
|
||||
}
|
||||
|
||||
fn notification_cap(
|
||||
&self,
|
||||
pid: ProcessId,
|
||||
slot: CapSlot,
|
||||
needed: Permission,
|
||||
) -> Result<(NotificationId, Badge, Generation), KernelError> {
|
||||
let cap = self.check_cap(pid, slot, needed)?;
|
||||
match cap.resource() {
|
||||
Resource::Notification(id) => Ok((*id, cap.badge(), *cap.generation())),
|
||||
_ => Err(KernelError::WrongObject),
|
||||
}
|
||||
}
|
||||
|
||||
/// Copies the capability in `slot` of `from` into `to`'s table, if the
|
||||
/// transfer is allowed and still valid. Returns where it landed.
|
||||
fn transfer_cap(
|
||||
pub(super) fn transfer_cap(
|
||||
&mut self,
|
||||
from: ProcessId,
|
||||
slot: Option<CapSlot>,
|
||||
@@ -169,7 +56,7 @@ impl<P: Platform> Kernel<P> {
|
||||
|
||||
/// Hands `out` from `sender` to `receiver`. For a call, the receiver's
|
||||
/// reply slot now names the sender.
|
||||
fn deliver(
|
||||
pub(super) fn deliver(
|
||||
&mut self,
|
||||
sender: ThreadId,
|
||||
out: Outgoing,
|
||||
@@ -413,157 +300,4 @@ impl<P: Platform> Kernel<P> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---- notifications ----
|
||||
|
||||
pub fn create_notification(&mut self, owner: ProcessId) -> Result<NotificationId, KernelError> {
|
||||
self.processes.get(owner)?;
|
||||
self.notifications
|
||||
.create(owner)
|
||||
.ok_or(KernelError::NoFreeSlot)
|
||||
}
|
||||
|
||||
pub(crate) fn create_notification_cap(
|
||||
&mut self,
|
||||
pid: ProcessId,
|
||||
) -> Result<CapSlot, KernelError> {
|
||||
let id = self.create_notification(pid)?;
|
||||
self.grant_cap(pid, Resource::Notification(id), rw_delegate())
|
||||
.inspect_err(|_| {
|
||||
self.notifications.destroy(id);
|
||||
})
|
||||
}
|
||||
|
||||
/// Signals `id` with `badge`; shared by `Signal` and interrupts.
|
||||
fn signal_notification(&mut self, id: NotificationId, badge: Badge) {
|
||||
let bound_receiving = self
|
||||
.notifications
|
||||
.get(id)
|
||||
.and_then(|n| n.bound())
|
||||
.is_some_and(|t| self.is_blocked_as(t, |r| matches!(r, BlockReason::Receiving(_))));
|
||||
let Some(ntfn) = self.notifications.get_mut(id) else {
|
||||
return;
|
||||
};
|
||||
match ntfn.signal(badge, bound_receiving) {
|
||||
SignalEffect::WakeWaiter(t, bits) => {
|
||||
self.wake(t, SyscallResult::Received(Delivery::signal(bits)));
|
||||
}
|
||||
SignalEffect::WakeBound(t, bits) => {
|
||||
self.leave_queue(t);
|
||||
self.wake(t, SyscallResult::Received(Delivery::signal(bits)));
|
||||
}
|
||||
SignalEffect::Stored => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn signal(&mut self, pid: ProcessId, slot: CapSlot) -> Result<(), KernelError> {
|
||||
let (id, badge, _) = self.notification_cap(pid, slot, Permission::Write)?;
|
||||
self.signal_notification(id, badge);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn wait(&mut self, tid: ThreadId, pid: ProcessId, slot: CapSlot) -> SyscallOutcome {
|
||||
let id = match self.notification_cap(pid, slot, Permission::Read) {
|
||||
Ok((id, _, _)) => id,
|
||||
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
|
||||
};
|
||||
let Some(ntfn) = self.notifications.get_mut(id) else {
|
||||
return SyscallOutcome::Complete(SyscallResult::Error(KernelError::NoSuchObject));
|
||||
};
|
||||
if ntfn.has_bits() {
|
||||
let bits = ntfn.take_bits();
|
||||
return SyscallOutcome::Complete(SyscallResult::Received(Delivery::signal(bits)));
|
||||
}
|
||||
if !ntfn.add_waiter(tid) {
|
||||
return SyscallOutcome::Complete(SyscallResult::Error(KernelError::EndpointFull));
|
||||
}
|
||||
self.block_caller(tid, BlockReason::WaitingNotification(id))
|
||||
}
|
||||
|
||||
pub(crate) fn poll(&mut self, pid: ProcessId, slot: CapSlot) -> Result<usize, KernelError> {
|
||||
let (id, _, _) = self.notification_cap(pid, slot, Permission::Read)?;
|
||||
self.notifications
|
||||
.get_mut(id)
|
||||
.map(|n| n.take_bits())
|
||||
.ok_or(KernelError::NoSuchObject)
|
||||
}
|
||||
|
||||
pub(crate) fn bind_notification(
|
||||
&mut self,
|
||||
tid: ThreadId,
|
||||
pid: ProcessId,
|
||||
slot: CapSlot,
|
||||
) -> Result<(), KernelError> {
|
||||
let (id, _, _) = self.notification_cap(pid, slot, Permission::Read)?;
|
||||
if self.ipc(tid).is_none_or(|s| s.bound.is_some()) {
|
||||
return Err(KernelError::InvalidArgument);
|
||||
}
|
||||
let ntfn = self
|
||||
.notifications
|
||||
.get_mut(id)
|
||||
.ok_or(KernelError::NoSuchObject)?;
|
||||
if !ntfn.bind(tid) {
|
||||
return Err(KernelError::InvalidArgument);
|
||||
}
|
||||
if let Some(s) = self.ipc_mut(tid) {
|
||||
s.bound = Some(id);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// ---- interrupts ----
|
||||
|
||||
fn interrupt_cap(&self, pid: ProcessId, slot: CapSlot) -> Result<IrqId, KernelError> {
|
||||
match self.check_cap(pid, slot, Permission::Execute)?.resource() {
|
||||
Resource::Interrupt(irq) => Ok(*irq),
|
||||
_ => Err(KernelError::WrongObject),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_irq_notification(
|
||||
&mut self,
|
||||
pid: ProcessId,
|
||||
irq_slot: CapSlot,
|
||||
ntfn_slot: CapSlot,
|
||||
) -> Result<(), KernelError> {
|
||||
let irq = self.interrupt_cap(pid, irq_slot)?;
|
||||
let (notification, badge, generation) =
|
||||
self.notification_cap(pid, ntfn_slot, Permission::Write)?;
|
||||
let route = domain::irq::IrqRoute {
|
||||
notification,
|
||||
generation,
|
||||
badge,
|
||||
};
|
||||
if self.irqs.set(irq, route) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(KernelError::InvalidIrq(irq))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn ack_interrupt(
|
||||
&mut self,
|
||||
pid: ProcessId,
|
||||
slot: CapSlot,
|
||||
) -> Result<(), KernelError> {
|
||||
let irq = self.interrupt_cap(pid, slot)?;
|
||||
self.platform.unmask_irq(irq);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// An interrupt fired: mask the line and signal its notification. The
|
||||
/// line stays masked until the handler acknowledges it.
|
||||
pub fn handle_interrupt(&mut self, irq: IrqId) {
|
||||
self.platform.mask_irq(irq);
|
||||
let Some(route) = self.irqs.route(irq) else {
|
||||
return;
|
||||
};
|
||||
// A route set through a capability that has since been revoked, or
|
||||
// to a notification whose slot was reused, is dead.
|
||||
if self.notifications.generation(route.notification) != Some(route.generation) {
|
||||
self.irqs.clear(irq);
|
||||
return;
|
||||
}
|
||||
self.signal_notification(route.notification, route.badge);
|
||||
}
|
||||
}
|
||||
67
crates/kernel/src/syscall/ipc/irq.rs
Normal file
67
crates/kernel/src/syscall/ipc/irq.rs
Normal file
@@ -0,0 +1,67 @@
|
||||
//! Interrupt routing to notifications.
|
||||
|
||||
use domain::{error::KernelError, resource::Resource};
|
||||
use platform::Platform;
|
||||
use types::{
|
||||
bounded::CapSlot,
|
||||
id::{IrqId, ProcessId},
|
||||
permission::Permission,
|
||||
};
|
||||
|
||||
use crate::Kernel;
|
||||
|
||||
impl<P: Platform> Kernel<P> {
|
||||
fn interrupt_cap(&self, pid: ProcessId, slot: CapSlot) -> Result<IrqId, KernelError> {
|
||||
match self.check_cap(pid, slot, Permission::Execute)?.resource() {
|
||||
Resource::Interrupt(irq) => Ok(*irq),
|
||||
_ => Err(KernelError::WrongObject),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_irq_notification(
|
||||
&mut self,
|
||||
pid: ProcessId,
|
||||
irq_slot: CapSlot,
|
||||
ntfn_slot: CapSlot,
|
||||
) -> Result<(), KernelError> {
|
||||
let irq = self.interrupt_cap(pid, irq_slot)?;
|
||||
let (notification, badge, generation) =
|
||||
self.notification_cap(pid, ntfn_slot, Permission::Write)?;
|
||||
let route = domain::irq::IrqRoute {
|
||||
notification,
|
||||
generation,
|
||||
badge,
|
||||
};
|
||||
if self.irqs.set(irq, route) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(KernelError::InvalidIrq(irq))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn ack_interrupt(
|
||||
&mut self,
|
||||
pid: ProcessId,
|
||||
slot: CapSlot,
|
||||
) -> Result<(), KernelError> {
|
||||
let irq = self.interrupt_cap(pid, slot)?;
|
||||
self.platform.unmask_irq(irq);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// An interrupt fired: mask the line and signal its notification. The
|
||||
/// line stays masked until the handler acknowledges it.
|
||||
pub fn handle_interrupt(&mut self, irq: IrqId) {
|
||||
self.platform.mask_irq(irq);
|
||||
let Some(route) = self.irqs.route(irq) else {
|
||||
return;
|
||||
};
|
||||
// A route set through a capability that has since been revoked, or
|
||||
// to a notification whose slot was reused, is dead.
|
||||
if self.notifications.generation(route.notification) != Some(route.generation) {
|
||||
self.irqs.clear(irq);
|
||||
return;
|
||||
}
|
||||
self.signal_notification(route.notification, route.badge);
|
||||
}
|
||||
}
|
||||
109
crates/kernel/src/syscall/ipc/mod.rs
Normal file
109
crates/kernel/src/syscall/ipc/mod.rs
Normal file
@@ -0,0 +1,109 @@
|
||||
//! Endpoint IPC, notifications and interrupt delivery (see docs/ipc.md).
|
||||
|
||||
mod endpoint;
|
||||
mod irq;
|
||||
mod notification;
|
||||
|
||||
use domain::{
|
||||
error::KernelError,
|
||||
message::Message,
|
||||
scheduler::Scheduler,
|
||||
thread::{BlockReason, ThreadState},
|
||||
};
|
||||
use platform::Platform;
|
||||
use types::{
|
||||
badge::Badge,
|
||||
generation::Generation,
|
||||
id::{NotificationId, ThreadId},
|
||||
};
|
||||
|
||||
use super::{SyscallOutcome, SyscallResult};
|
||||
use crate::Kernel;
|
||||
|
||||
/// A thread named by id and generation, so a stale reference can be told
|
||||
/// apart from a new thread that reused the id.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct ThreadRef {
|
||||
pub tid: ThreadId,
|
||||
pub generation: Generation,
|
||||
}
|
||||
|
||||
/// A message a blocked sender is waiting to hand over.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub(crate) struct Outgoing {
|
||||
message: Message,
|
||||
badge: Badge,
|
||||
/// The sender's endpoint cap had `Delegate`, so a capability may travel.
|
||||
grant: bool,
|
||||
}
|
||||
|
||||
/// Per-thread IPC bookkeeping.
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct IpcState {
|
||||
pub(crate) outgoing: Option<Outgoing>,
|
||||
/// The caller this thread owes a reply to.
|
||||
pub(crate) reply_to: Option<ThreadRef>,
|
||||
pub(crate) bound: Option<NotificationId>,
|
||||
}
|
||||
|
||||
impl<P: Platform> Kernel<P> {
|
||||
pub(crate) fn ipc(&self, tid: ThreadId) -> Option<&IpcState> {
|
||||
self.ipc.get(tid.raw() as usize)
|
||||
}
|
||||
|
||||
pub(crate) fn ipc_mut(&mut self, tid: ThreadId) -> Option<&mut IpcState> {
|
||||
self.ipc.get_mut(tid.raw() as usize)
|
||||
}
|
||||
|
||||
pub(crate) fn set_pending_result(&mut self, tid: ThreadId, result: Option<SyscallResult>) {
|
||||
if let Some(r) = self.pending_results.get_mut(tid.raw() as usize) {
|
||||
*r = result;
|
||||
}
|
||||
}
|
||||
|
||||
fn thread_ref(&self, tid: ThreadId) -> Option<ThreadRef> {
|
||||
Some(ThreadRef {
|
||||
tid,
|
||||
generation: self.threads.generation(tid)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn is_live(&self, r: ThreadRef) -> bool {
|
||||
self.threads.generation(r.tid) == Some(r.generation)
|
||||
}
|
||||
|
||||
fn is_blocked_as(&self, tid: ThreadId, f: impl Fn(&BlockReason) -> bool) -> bool {
|
||||
matches!(self.threads.get(tid).map(|t| *t.state()), Ok(ThreadState::Blocked(r)) if f(&r))
|
||||
}
|
||||
|
||||
/// Blocks the running caller.
|
||||
pub(crate) fn block_caller(&mut self, tid: ThreadId, reason: BlockReason) -> SyscallOutcome {
|
||||
if let Ok(t) = self.threads.get_mut(tid) {
|
||||
let _ = t.block(reason);
|
||||
}
|
||||
self.scheduler.remove(tid);
|
||||
SyscallOutcome::BlockCaller(reason)
|
||||
}
|
||||
|
||||
/// Makes a blocked thread runnable with `result` as its syscall result.
|
||||
pub(crate) fn wake(&mut self, tid: ThreadId, result: SyscallResult) {
|
||||
let Ok(thread) = self.threads.get_mut(tid) else {
|
||||
return;
|
||||
};
|
||||
let priority = thread.priority();
|
||||
if thread.unblock().is_ok() {
|
||||
self.set_pending_result(tid, Some(result));
|
||||
self.make_ready(tid, priority);
|
||||
}
|
||||
}
|
||||
|
||||
/// Releases a caller waiting on `reply` with `NoReply`, if it is still
|
||||
/// that caller and still waiting.
|
||||
pub(crate) fn release_caller(&mut self, reply: ThreadRef) {
|
||||
if self.is_live(reply)
|
||||
&& self.is_blocked_as(reply.tid, |r| *r == BlockReason::AwaitingReply)
|
||||
{
|
||||
self.wake(reply.tid, SyscallResult::Error(KernelError::NoReply));
|
||||
}
|
||||
}
|
||||
}
|
||||
133
crates/kernel/src/syscall/ipc/notification.rs
Normal file
133
crates/kernel/src/syscall/ipc/notification.rs
Normal file
@@ -0,0 +1,133 @@
|
||||
//! Notification objects: signal, wait, poll, bind.
|
||||
|
||||
use domain::{
|
||||
error::KernelError, notification::SignalEffect, resource::Resource, thread::BlockReason,
|
||||
};
|
||||
use platform::Platform;
|
||||
use types::{
|
||||
badge::Badge,
|
||||
bounded::CapSlot,
|
||||
generation::Generation,
|
||||
id::{NotificationId, ProcessId, ThreadId},
|
||||
permission::{Permission, PermissionSet},
|
||||
};
|
||||
|
||||
use super::super::{Delivery, SyscallOutcome, SyscallResult};
|
||||
use crate::Kernel;
|
||||
|
||||
fn rw_delegate() -> PermissionSet {
|
||||
PermissionSet::empty()
|
||||
.with(Permission::Read)
|
||||
.with(Permission::Write)
|
||||
.with(Permission::Delegate)
|
||||
}
|
||||
|
||||
impl<P: Platform> Kernel<P> {
|
||||
pub(super) fn notification_cap(
|
||||
&self,
|
||||
pid: ProcessId,
|
||||
slot: CapSlot,
|
||||
needed: Permission,
|
||||
) -> Result<(NotificationId, Badge, Generation), KernelError> {
|
||||
let cap = self.check_cap(pid, slot, needed)?;
|
||||
match cap.resource() {
|
||||
Resource::Notification(id) => Ok((*id, cap.badge(), *cap.generation())),
|
||||
_ => Err(KernelError::WrongObject),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_notification(&mut self, owner: ProcessId) -> Result<NotificationId, KernelError> {
|
||||
self.processes.get(owner)?;
|
||||
self.notifications
|
||||
.create(owner)
|
||||
.ok_or(KernelError::NoFreeSlot)
|
||||
}
|
||||
|
||||
pub(crate) fn create_notification_cap(
|
||||
&mut self,
|
||||
pid: ProcessId,
|
||||
) -> Result<CapSlot, KernelError> {
|
||||
let id = self.create_notification(pid)?;
|
||||
self.grant_cap(pid, Resource::Notification(id), rw_delegate())
|
||||
.inspect_err(|_| {
|
||||
self.notifications.destroy(id);
|
||||
})
|
||||
}
|
||||
|
||||
/// Signals `id` with `badge`; shared by `Signal` and interrupts.
|
||||
pub(super) fn signal_notification(&mut self, id: NotificationId, badge: Badge) {
|
||||
let bound_receiving = self
|
||||
.notifications
|
||||
.get(id)
|
||||
.and_then(|n| n.bound())
|
||||
.is_some_and(|t| self.is_blocked_as(t, |r| matches!(r, BlockReason::Receiving(_))));
|
||||
let Some(ntfn) = self.notifications.get_mut(id) else {
|
||||
return;
|
||||
};
|
||||
match ntfn.signal(badge, bound_receiving) {
|
||||
SignalEffect::WakeWaiter(t, bits) => {
|
||||
self.wake(t, SyscallResult::Received(Delivery::signal(bits)));
|
||||
}
|
||||
SignalEffect::WakeBound(t, bits) => {
|
||||
self.leave_queue(t);
|
||||
self.wake(t, SyscallResult::Received(Delivery::signal(bits)));
|
||||
}
|
||||
SignalEffect::Stored => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn signal(&mut self, pid: ProcessId, slot: CapSlot) -> Result<(), KernelError> {
|
||||
let (id, badge, _) = self.notification_cap(pid, slot, Permission::Write)?;
|
||||
self.signal_notification(id, badge);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn wait(&mut self, tid: ThreadId, pid: ProcessId, slot: CapSlot) -> SyscallOutcome {
|
||||
let id = match self.notification_cap(pid, slot, Permission::Read) {
|
||||
Ok((id, _, _)) => id,
|
||||
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
|
||||
};
|
||||
let Some(ntfn) = self.notifications.get_mut(id) else {
|
||||
return SyscallOutcome::Complete(SyscallResult::Error(KernelError::NoSuchObject));
|
||||
};
|
||||
if ntfn.has_bits() {
|
||||
let bits = ntfn.take_bits();
|
||||
return SyscallOutcome::Complete(SyscallResult::Received(Delivery::signal(bits)));
|
||||
}
|
||||
if !ntfn.add_waiter(tid) {
|
||||
return SyscallOutcome::Complete(SyscallResult::Error(KernelError::EndpointFull));
|
||||
}
|
||||
self.block_caller(tid, BlockReason::WaitingNotification(id))
|
||||
}
|
||||
|
||||
pub(crate) fn poll(&mut self, pid: ProcessId, slot: CapSlot) -> Result<usize, KernelError> {
|
||||
let (id, _, _) = self.notification_cap(pid, slot, Permission::Read)?;
|
||||
self.notifications
|
||||
.get_mut(id)
|
||||
.map(|n| n.take_bits())
|
||||
.ok_or(KernelError::NoSuchObject)
|
||||
}
|
||||
|
||||
pub(crate) fn bind_notification(
|
||||
&mut self,
|
||||
tid: ThreadId,
|
||||
pid: ProcessId,
|
||||
slot: CapSlot,
|
||||
) -> Result<(), KernelError> {
|
||||
let (id, _, _) = self.notification_cap(pid, slot, Permission::Read)?;
|
||||
if self.ipc(tid).is_none_or(|s| s.bound.is_some()) {
|
||||
return Err(KernelError::InvalidArgument);
|
||||
}
|
||||
let ntfn = self
|
||||
.notifications
|
||||
.get_mut(id)
|
||||
.ok_or(KernelError::NoSuchObject)?;
|
||||
if !ntfn.bind(tid) {
|
||||
return Err(KernelError::InvalidArgument);
|
||||
}
|
||||
if let Some(s) = self.ipc_mut(tid) {
|
||||
s.bound = Some(id);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -51,9 +51,7 @@ impl<P: Platform> Kernel<P> {
|
||||
)
|
||||
}
|
||||
|
||||
/// Moves one of the caller's pages into the process named by
|
||||
/// `process_cap`. Executable pages are mapped read-execute, others
|
||||
/// read-write.
|
||||
/// Moves one of the caller's pages into `process_cap`'s process (exec pages RX, others RW).
|
||||
pub(crate) fn handle_map_page(
|
||||
&mut self,
|
||||
caller_pid: ProcessId,
|
||||
@@ -99,9 +97,7 @@ impl<P: Platform> Kernel<P> {
|
||||
self.grant_cap(pid, Resource::Memory(id), all)
|
||||
}
|
||||
|
||||
/// Maps the region behind `slot` into the caller. The capability must
|
||||
/// allow every access `perms` grants; the mapping lasts as long as
|
||||
/// the capability.
|
||||
/// Maps the region behind `slot` into the caller; the cap must allow `perms`, mapping lasts as long as the cap.
|
||||
pub(crate) fn map_region_cap(
|
||||
&mut self,
|
||||
pid: ProcessId,
|
||||
@@ -125,9 +121,7 @@ impl<P: Platform> Kernel<P> {
|
||||
self.map_region_via(id, pid, va, perms, via)
|
||||
}
|
||||
|
||||
/// The physical base of the region behind `slot`. Needs Read on the
|
||||
/// capability; pool regions are physically contiguous, so the base is
|
||||
/// enough to program a device.
|
||||
/// Physical base of the region behind `slot` (needs Read); pool regions are contiguous.
|
||||
pub(crate) fn region_address_cap(
|
||||
&self,
|
||||
pid: ProcessId,
|
||||
|
||||
Reference in New Issue
Block a user