feat(userspace): log server sole console holder; init boots from manifest; adapter loads only init+storage

This commit is contained in:
2026-10-27 15:40:19 +01:00
parent 4049ba5007
commit a4f467f45d
21 changed files with 510 additions and 325 deletions

115
crates/init/src/launch.rs Normal file
View 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);
}