//! Starting a manifest program: load it, hand it what the manifest says //! and nothing else, run it. use libkos::device::Device as BoardDevice; use libkos::fs::Storage; use libkos::syscall; use types::address::StackPointer; use types::badge::Badge; use types::bounded::CapSlot; use types::permission::{Permission, PermissionSet}; use crate::devices::Devices; use crate::load::{self, LoadedProcess}; use crate::manifest::{Device, Program}; use crate::spawn::Spawner; const USER_STACK_TOP: StackPointer = StackPointer::from_raw(0x10000); const MAX_SERVED: usize = 8; fn perms(list: &[Permission]) -> PermissionSet { list.iter().fold(PermissionSet::empty(), |s, p| s.with(*p)) } pub(crate) struct Launcher<'a> { storage: &'a mut Storage, scratch: &'a mut [u8], devices: Devices, /// The endpoint of every server started so far, by name. served: [Option<(&'static str, CapSlot)>; MAX_SERVED], /// Init's own endpoint, for the spawn service. pub(crate) spawn_ep: CapSlot, /// Bound to init's thread: spawned children's deaths, a bit each. pub(crate) deaths: CapSlot, pub(crate) spawner: Spawner, } impl<'a> Launcher<'a> { pub(crate) fn new( storage: &'a mut Storage, scratch: &'a mut [u8], devices: Devices, ) -> Result { let spawn_ep = syscall::create_endpoint().ok_or("no spawn endpoint")?; let deaths = syscall::create_notification().map_err(|_| "no death notification")?; Ok(Self { storage, scratch, devices, served: [None; MAX_SERVED], spawn_ep, deaths, spawner: Spawner::new() }) } pub(crate) fn storage(&mut self) -> (&mut Storage, &mut [u8]) { (self.storage, self.scratch) } fn endpoint(&self, name: &str) -> Option { self.served.iter().flatten().find(|(n, _)| *n == name).map(|(_, ep)| *ep) } /// What became of the program's device, if it has one. pub(crate) fn launch(&mut self, p: &Program) -> Result<&'static str, &'static str> { let loaded = load::load_binary(self.storage, self.scratch, p.file, p.limits).ok_or("cannot load")?; let child = loaded.process_cap; let ep = match p.serves { true => { let ep = syscall::create_endpoint().ok_or("no endpoint")?; // Read to receive on it; init keeps the original. let server = perms(&[Permission::Read, Permission::Execute]); syscall::delegate(ep, child, server).ok_or("cannot give its endpoint")?; Some(ep) } false => None, }; // A client only calls: Execute, never Read, so it cannot pose as the // server and take other clients' requests. for name in p.uses { let ep = self.endpoint(name).ok_or("needs a server that is not running")?; syscall::delegate(ep, child, perms(&[Permission::Execute])).ok_or("cannot give an endpoint")?; } if let Some(allowance) = p.spawns { self.give_spawn(child, allowance)?; } if p.system_control { let control = crate::devices::system_control().ok_or("no SystemControl to give")?; syscall::delegate(control, child, perms(&[Permission::Execute])).ok_or("cannot give SystemControl")?; } let console = libkos::start::console() .filter(|_| p.console) .and_then(|c| syscall::delegate(c, child, perms(&[Permission::Write]))); start(&loaded, console); let mut note = ""; if let Some(ep) = ep { if let Some(free) = self.served.iter_mut().find(|s| s.is_none()) { *free = Some((p.name, ep)); } if let Some(device) = p.device { note = self.give(device, ep); } if console.is_some() { hand_over_console(ep); note = ", console handed over"; } } Ok(note) } /// A badged copy of init's endpoint: the badge names the client and so /// its allowance. fn give_spawn(&mut self, child: CapSlot, allowance: types::budget::Limits) -> Result<(), &'static str> { let badge: Badge = self.spawner.add_client(allowance).ok_or("too many spawn clients")?; let minted = perms(&[Permission::Execute, Permission::Delegate]); let copy = syscall::mint(self.spawn_ep, badge, minted).map_err(|_| "cannot mint")?; let given = syscall::delegate(copy, child, perms(&[Permission::Execute])); syscall::drop_cap(copy); given.map(|_| ()).ok_or("cannot give the spawn endpoint") } /// The driver waits for its device before serving, and runs without /// one (headless) if the board has none. fn give(&self, device: Device, ep: CapSlot) -> &'static str { let (dev, given, none): (BoardDevice, _, _) = match device { Device::Gpu => (self.devices.gpu, ", gpu handed over", ", no gpu"), Device::Input => (self.devices.input, ", input device handed over", ", no input device"), }; match libkos::device::give_device(ep, dev).is_ok() && dev.mmio.is_some() { true => given, false => none, } } } /// From here on init prints through the log server, like everyone else, /// and holds no console of its own. fn hand_over_console(log: CapSlot) { if let Some(console) = libkos::start::console() { syscall::drop_cap(console); } libkos::start::set_console(None); libkos::start::set_log(Some(log)); } /// Starts `program` with `console` and, once there is one, the log. pub(crate) fn start(program: &LoadedProcess, console: Option) { let log = libkos::start::log() .and_then(|l| syscall::delegate(l, program.process_cap, perms(&[Permission::Execute]))); let arg = types::start_arg::encode(console, log); syscall::spawn_thread_in(program.process_cap, program.entry_point, USER_STACK_TOP, arg); }