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:
@@ -14,6 +14,7 @@ pub mod mapping;
|
||||
pub mod permission;
|
||||
#[cfg(kani)]
|
||||
pub mod proofs;
|
||||
pub mod spawn;
|
||||
pub mod start_arg;
|
||||
pub mod storage;
|
||||
pub mod syscall_abi;
|
||||
|
||||
110
crates/types/src/spawn.rs
Normal file
110
crates/types/src/spawn.rs
Normal file
@@ -0,0 +1,110 @@
|
||||
//! Init's spawn protocol, shared by init and `libkos::spawn`: a client asks
|
||||
//! init to load a program into a new, not yet running process; init replies
|
||||
//! with the process capability and the client wires and starts it.
|
||||
//!
|
||||
//! Request: `[SPAWN | name_len << 8, packed limits, name bytes 0..8, 8..16]`.
|
||||
//! Reply: `[reply::OK, entry point, child's log slot or NONE, 0]` carrying
|
||||
//! the process capability, or `[error tag, 0, 0, 0]`.
|
||||
|
||||
use crate::budget::Limits;
|
||||
|
||||
pub const SPAWN: usize = 0;
|
||||
/// Program file names: FAT 8.3, with room to spare.
|
||||
pub const MAX_NAME: usize = 16;
|
||||
/// Where init puts the child's stack top (its stack ends at the image).
|
||||
pub const STACK_TOP: usize = 0x10000;
|
||||
/// A slot word meaning "none".
|
||||
pub const NONE: usize = usize::MAX;
|
||||
|
||||
pub mod reply {
|
||||
pub const OK: usize = 0;
|
||||
pub const NOT_FOUND: usize = 1;
|
||||
/// The limits do not fit in what is left of the client's allowance.
|
||||
pub const OVER_ALLOWANCE: usize = 2;
|
||||
pub const ERROR: usize = 3;
|
||||
}
|
||||
|
||||
/// Pages in bits 0..32, endpoints, notifications and regions a byte each
|
||||
/// above; `None` if a part does not fit.
|
||||
fn pack(l: Limits) -> Option<usize> {
|
||||
let byte = |v: usize| u8::try_from(v).ok().map(usize::from);
|
||||
let pages = u32::try_from(l.pages).ok()? as u64;
|
||||
let rest = (byte(l.endpoints)? | byte(l.notifications)? << 8 | byte(l.regions)? << 16) as u64;
|
||||
usize::try_from(pages | rest << 32).ok()
|
||||
}
|
||||
|
||||
fn unpack(w: usize) -> Limits {
|
||||
let w = w as u64;
|
||||
let byte = |shift: u32| ((w >> shift) & 0xFF) as usize;
|
||||
Limits {
|
||||
pages: (w & 0xFFFF_FFFF) as usize,
|
||||
endpoints: byte(32),
|
||||
notifications: byte(40),
|
||||
regions: byte(48),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn request(name: &[u8], limits: Limits) -> Option<[usize; 4]> {
|
||||
if name.is_empty() || name.len() > MAX_NAME {
|
||||
return None;
|
||||
}
|
||||
let mut bytes = [0u8; MAX_NAME];
|
||||
bytes[..name.len()].copy_from_slice(name);
|
||||
let (lo, hi) = bytes.as_chunks::<8>().0.split_at(1);
|
||||
Some([
|
||||
SPAWN | name.len() << 8,
|
||||
pack(limits)?,
|
||||
u64::from_le_bytes(lo[0]) as usize,
|
||||
u64::from_le_bytes(hi[0]) as usize,
|
||||
])
|
||||
}
|
||||
|
||||
/// The name (and how much of it counts) and limits of a spawn request.
|
||||
pub fn parse(words: [usize; 4]) -> Option<([u8; MAX_NAME], usize, Limits)> {
|
||||
let len = words[0] >> 8;
|
||||
if words[0] & 0xFF != SPAWN || len == 0 || len > MAX_NAME {
|
||||
return None;
|
||||
}
|
||||
let mut name = [0u8; MAX_NAME];
|
||||
for (b, w) in name.as_chunks_mut::<8>().0.iter_mut().zip(&words[2..]) {
|
||||
*b = (*w as u64).to_le_bytes();
|
||||
}
|
||||
Some((name, len, unpack(words[1])))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn requests_round_trip() {
|
||||
let l = Limits {
|
||||
pages: 4096,
|
||||
endpoints: 2,
|
||||
notifications: 3,
|
||||
regions: 255,
|
||||
};
|
||||
let (name, len, back) = parse(request(b"hello.elf", l).unwrap()).unwrap();
|
||||
assert_eq!(&name[..len], b"hello.elf");
|
||||
assert_eq!(back, l);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn what_cannot_be_said_is_refused() {
|
||||
let l = Limits::default();
|
||||
assert!(request(b"", l).is_none());
|
||||
assert!(request(&[b'a'; 17], l).is_none());
|
||||
assert!(
|
||||
request(
|
||||
b"x",
|
||||
Limits {
|
||||
endpoints: 256,
|
||||
..l
|
||||
}
|
||||
)
|
||||
.is_none()
|
||||
);
|
||||
assert!(parse([SPAWN | 17 << 8, 0, 0, 0]).is_none());
|
||||
assert!(parse([1 | 3 << 8, 0, 0, 0]).is_none());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user