feat(init): spawn service over IPC with per-client allowance refunded on child death; up to 4 death watchers; spawntest on test image

This commit is contained in:
2026-10-27 16:21:08 +01:00
parent 817a38993b
commit 8587661684
23 changed files with 564 additions and 28 deletions

View File

@@ -0,0 +1,53 @@
//! Running a program through init (protocol: `types::spawn`). Init loads it
//! and gives it a log endpoint; the caller hands it any other capabilities
//! (`delegate` to the process) and starts it.
use crate::syscall;
use types::address::{EntryPoint, StackPointer};
use types::bounded::CapSlot;
use types::budget::Limits;
use types::spawn::{STACK_TOP, reply, request};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SpawnError {
NotFound,
/// More than what is left of this process's spawn allowance.
OverAllowance,
Failed,
}
/// A loaded program, not yet running.
pub struct Spawned {
pub process: CapSlot,
entry: EntryPoint,
log: Option<CapSlot>,
}
pub fn spawn(init: CapSlot, name: &[u8], limits: Limits) -> Result<Spawned, SpawnError> {
let words = request(name, limits).ok_or(SpawnError::Failed)?;
let reply = syscall::call(init, words, None).map_err(|_| SpawnError::Failed)?;
let [tag, entry, log, _] = reply.words();
let cap = match reply {
syscall::Received::Message { cap, .. } | syscall::Received::Call { cap, .. } => cap,
syscall::Received::Signal { .. } => None,
};
match (tag, cap) {
(reply::OK, Some(process)) => Ok(Spawned {
process,
entry: EntryPoint::from_raw(entry),
log: u8::try_from(log).ok().and_then(CapSlot::new),
}),
(reply::NOT_FOUND, _) => Err(SpawnError::NotFound),
(reply::OVER_ALLOWANCE, _) => Err(SpawnError::OverAllowance),
_ => Err(SpawnError::Failed),
}
}
impl Spawned {
/// Runs the program, telling it where its log endpoint is.
pub fn start(&self) -> bool {
let arg = types::start_arg::encode(None, self.log);
let stack = StackPointer::from_raw(STACK_TOP);
syscall::spawn_thread_in(self.process, self.entry, stack, arg) != usize::MAX
}
}