Files
k-os/crates/init/src/launch.rs

167 lines
7.1 KiB
Rust

//! 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],
/// The badge on the next client's endpoint copy (0 is init's own).
next_badge: usize,
/// 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<Self, &'static str> {
let spawn_ep = syscall::create_endpoint().ok_or("no spawn endpoint")?;
let deaths = syscall::create_notification().map_err(|_| "no death notification")?;
// Storage, started by boot, is not among them: it is reached
// through grants (a program's `storage` scope).
let served = [None; MAX_SERVED];
Ok(Self { storage, scratch, devices, served, next_badge: 1, 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<CapSlot> {
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 calls (Execute) and may send capabilities through it and
// pass it on (Delegate); 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")?;
let badge = Badge::new(self.next_badge);
self.next_badge += 1;
let copy = syscall::mint(ep, badge, perms(&[Permission::Execute, Permission::Delegate]))
.map_err(|_| "cannot mint an endpoint")?;
let given = syscall::delegate(copy, child, perms(&[Permission::Execute, Permission::Delegate]));
syscall::drop_cap(copy);
given.ok_or("cannot give an endpoint")?;
}
// Storage issues its own badges, each naming a scope.
if let Some(scope) = p.storage {
let grant = self.storage.grant(scope.path, scope.write).map_err(|_| "storage refused a grant")?;
let given = syscall::delegate(grant, child, perms(&[Permission::Execute, Permission::Delegate]));
syscall::drop_cap(grant);
given.ok_or("cannot give storage")?;
}
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")?;
// Delegate, to send the program file's grant with each request.
let given = syscall::delegate(copy, child, perms(&[Permission::Execute, Permission::Delegate]));
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<CapSlot>) {
let log = libkos::start::log()
.and_then(|l| syscall::delegate(l, program.process_cap, perms(&[Permission::Execute])));
let arg = types::start_arg::StartArg { console, log, ..Default::default() }.encode();
syscall::spawn_thread_in(program.process_cap, program.entry_point, USER_STACK_TOP, arg);
}