//! The test image's last check, started by init with a spawn allowance: //! programs run through init, the allowance holds, and it comes back when //! a child dies. Reports the whole self-test and halts. #![no_std] #![no_main] extern crate libkos; use libkos::spawn::{SpawnError, spawn}; use libkos::syscall; use types::bounded::CapSlot; use types::budget::Limits; /// Where init put them (see init's manifest: `uses`, the spawn endpoint, /// then control). const STORAGE: CapSlot = CapSlot::const_new(0); const INIT: CapSlot = CapSlot::const_new(1); const CONTROL: CapSlot = CapSlot::const_new(2); /// Where storage's buffer is mapped. const STORAGE_BUF_VA: usize = 0x0180_0000; /// Read back from the disk image by scripts/test-qemu.sh. const NOTE: &[u8] = b"written by k-os on ext4\n"; /// Over half the allowance (128 pages), so a second one fits only after /// the first has died and its limits came back. const HELLO: Limits = Limits { pages: 80, endpoints: 0, notifications: 0, regions: 0 }; /// Spawns hello, runs it, and waits for it to die. fn run_hello(died: CapSlot) -> Result<(), &'static str> { let child = spawn(INIT, b"hello.elf", HELLO).map_err(|_| "init would not spawn hello")?; syscall::watch_death(child.process, died).map_err(|_| "cannot watch hello")?; if !child.start() { return Err("cannot start hello"); } syscall::wait(died).map_err(|_| "wait failed")?; Ok(()) } /// A file written through storage, which the host then checks with e2fsck. fn write_a_file() -> Result<(), &'static str> { let mut fs = libkos::fs::Storage::attach(STORAGE, STORAGE_BUF_VA).map_err(|_| "cannot attach to storage")?; let h = fs.create(b"selftest.txt").map_err(|_| "cannot create a file")?; if fs.write(h, NOTE) != Ok(NOTE.len()) { return Err("short write"); } fs.close(h).map_err(|_| "close failed")?; let h = fs.open(b"selftest.txt").map_err(|_| "cannot reopen the file")?; let mut back = [0u8; 64]; let n = fs.read(h, &mut back).map_err(|_| "read failed")?; fs.close(h).map_err(|_| "close failed")?; if &back[..n] != NOTE { return Err("read back something else"); } Ok(()) } fn check() -> Result<(), &'static str> { write_a_file()?; libkos::println!("spawntest: a file written to ext4 reads back: ok"); let died = syscall::create_notification().map_err(|_| "no notification")?; run_hello(died)?; libkos::println!("spawntest: spawned program runs and its death is heard: ok"); let greedy = Limits { pages: 200, ..HELLO }; if spawn(INIT, b"hello.elf", greedy).err() != Some(SpawnError::OverAllowance) { return Err("init spawned past the allowance"); } let tiny = Limits { pages: 1, ..HELLO }; if spawn(INIT, b"nothere.elf", tiny).err() != Some(SpawnError::NotFound) { return Err("a missing program was not reported"); } libkos::println!("spawntest: allowance and missing files refused: ok"); run_hello(died)?; libkos::println!("spawntest: allowance comes back when a child dies: ok"); Ok(()) } #[unsafe(no_mangle)] extern "C" fn main() { match check() { Ok(()) => libkos::println!("SMPTEST PASS"), Err(why) => libkos::println!("SMPTEST FAIL: {}", why), } syscall::halt(CONTROL); }