feat(userspace): log server sole console holder; init boots from manifest; adapter loads only init+storage
This commit is contained in:
115
crates/init/src/launch.rs
Normal file
115
crates/init/src/launch.rs
Normal file
@@ -0,0 +1,115 @@
|
||||
//! 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::bounded::CapSlot;
|
||||
use types::permission::{Permission, PermissionSet};
|
||||
|
||||
use crate::devices::Devices;
|
||||
use crate::load::{self, LoadedProcess};
|
||||
use crate::manifest::{Device, Program};
|
||||
|
||||
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],
|
||||
}
|
||||
|
||||
impl<'a> Launcher<'a> {
|
||||
pub(crate) fn new(storage: &'a mut Storage, scratch: &'a mut [u8], devices: Devices) -> Self {
|
||||
Self { storage, scratch, devices, served: [None; MAX_SERVED] }
|
||||
}
|
||||
|
||||
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 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")?;
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
/// 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::encode(console, log);
|
||||
syscall::spawn_thread_in(program.process_cap, program.entry_point, USER_STACK_TOP, arg);
|
||||
}
|
||||
Reference in New Issue
Block a user