165 lines
6.9 KiB
Rust
165 lines
6.9 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 was started by boot, before init; programs may use it too.
|
|
let mut served = [None; MAX_SERVED];
|
|
served[0] = Some(("storage", crate::STORAGE_EP));
|
|
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 copy = match *name {
|
|
// Storage issues its own badges, each naming a scope.
|
|
"storage" => self.storage.grant(b"", true).map_err(|_| "storage refused a grant")?,
|
|
_ => {
|
|
let badge = Badge::new(self.next_badge);
|
|
self.next_badge += 1;
|
|
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")?;
|
|
}
|
|
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<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);
|
|
}
|