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

View File

@@ -8,7 +8,14 @@ pub mod server;
mod tests;
pub fn run() -> ! {
let mut srv = server::LogServer::new();
let console = libkos::start::console();
let mut srv = server::LogServer::with_output(|bytes: &[u8]| {
if let Some(console) = console {
for &b in bytes {
libkos::syscall::debug_putchar(console, b);
}
}
});
let ep = types::bounded::CapSlot::new(0).unwrap();
server_framework::serve(&mut srv, ep)
}

View File

@@ -13,6 +13,8 @@ pub enum LogRequest {
Write { level: LogLevel, tag: u16, payload: usize },
GetCount,
GetEntry { index: usize },
/// Console text (`types::log`): `len` bytes of `bytes`.
Text { bytes: [u8; types::log::TEXT_BYTES], len: usize },
}
#[derive(Debug, PartialEq)]
@@ -50,6 +52,7 @@ impl Encode for LogRequest {
}
LogRequest::GetCount => [1, 0, 0, 0],
LogRequest::GetEntry { index } => [2, *index, 0, 0],
LogRequest::Text { bytes, len } => types::log::text_words(&bytes[..*len]),
}
}
}
@@ -64,7 +67,7 @@ impl Decode for LogRequest {
}
1 => Some(LogRequest::GetCount),
2 => Some(LogRequest::GetEntry { index: words[1] }),
_ => None,
_ => types::log::text_of(words).map(|(bytes, len)| LogRequest::Text { bytes, len }),
}
}
}

View File

@@ -10,18 +10,28 @@ struct LogEntry {
payload: usize,
}
pub struct LogServer {
/// Holds the console: every process's text goes out through `out`.
pub struct LogServer<O: FnMut(&[u8]) = fn(&[u8])> {
entries: [Option<LogEntry>; MAX_ENTRIES],
count: usize,
write_pos: usize,
out: O,
}
impl LogServer {
/// Text goes nowhere.
pub fn new() -> Self {
Self::with_output(|_| {})
}
}
impl<O: FnMut(&[u8])> LogServer<O> {
pub fn with_output(out: O) -> Self {
Self {
entries: [const { None }; MAX_ENTRIES],
count: 0,
write_pos: 0,
out,
}
}
@@ -30,7 +40,7 @@ impl LogServer {
}
}
impl Server for LogServer {
impl<O: FnMut(&[u8])> Server for LogServer<O> {
type Request = LogRequest;
type Response = LogResponse;
@@ -44,6 +54,10 @@ impl Server for LogServer {
}
LogResponse::Ok
}
LogRequest::Text { bytes, len } => {
(self.out)(&bytes[..len.min(bytes.len())]);
LogResponse::Ok
}
LogRequest::GetCount => LogResponse::Count(self.count),
LogRequest::GetEntry { index } => {
if index >= self.count {

View File

@@ -1,6 +1,6 @@
use crate::protocol::*;
use crate::server::LogServer;
use server_framework::Server;
use server_framework::{Decode, Server};
#[test]
fn empty_server_has_zero_count() {
@@ -41,3 +41,20 @@ fn ring_buffer_wraps() {
}
assert_eq!(server.count(), 128);
}
#[test]
fn text_goes_out_through_the_console() {
let mut seen = [0u8; 64];
let mut n = 0;
{
let mut server = LogServer::with_output(|b: &[u8]| {
seen[n..n + b.len()].copy_from_slice(b);
n += b.len();
});
for chunk in [&b"init: "[..], b"started\n"] {
let words = types::log::text_words(chunk);
server.handle(LogRequest::decode(words).unwrap());
}
}
assert_eq!(&seen[..n], b"init: started\n");
}

View File

@@ -23,6 +23,8 @@ pub enum StorageRequest {
Create { path_len: usize },
Delete { path_len: usize },
Mkdir { path_len: usize },
/// Carries the endpoint to print to.
SetLog,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -61,6 +63,7 @@ impl Encode for StorageRequest {
Create { path_len } => [op::CREATE, path_len, 0, 0],
Delete { path_len } => [op::DELETE, path_len, 0, 0],
Mkdir { path_len } => [op::MKDIR, path_len, 0, 0],
SetLog => [op::SET_LOG, 0, 0, 0],
}
}
}
@@ -79,6 +82,7 @@ impl Decode for StorageRequest {
op::CREATE => Create { path_len: w[1] },
op::DELETE => Delete { path_len: w[1] },
op::MKDIR => Mkdir { path_len: w[1] },
op::SET_LOG => SetLog,
_ => return None,
})
}

View File

@@ -31,6 +31,7 @@ pub struct StorageServer<F: FileSystem, B: Buffers> {
clients: [Option<Client>; MAX_CLIENTS],
/// The badge of the client that opened each handle.
owners: [Option<usize>; MAX_HANDLES],
log: Option<CapSlot>,
}
impl<F: FileSystem, B: Buffers> StorageServer<F, B> {
@@ -40,6 +41,26 @@ impl<F: FileSystem, B: Buffers> StorageServer<F, B> {
buffers,
clients: [const { None }; MAX_CLIENTS],
owners: [None; MAX_HANDLES],
log: None,
}
}
/// Where this server prints, once init has said.
pub fn log(&self) -> Option<CapSlot> {
self.log
}
/// Clients hold badged copies of the endpoint, so the unbadged sender is
/// whoever was given the endpoint itself: init.
fn set_log(&mut self, badge: usize, cap: Option<CapSlot>) -> StorageResponse {
match (badge, cap, self.log) {
(0, Some(cap), None) => {
self.log = Some(cap);
libkos::start::set_log(Some(cap));
libkos::println!("storage: printing through the log server");
StorageResponse::Ok
}
_ => StorageResponse::Error,
}
}
@@ -117,7 +138,7 @@ impl<F: FileSystem, B: Buffers> StorageServer<F, B> {
None => R::NotFound,
};
let response = match request {
Attach => R::Error,
Attach | SetLog => R::Error,
Open { path_len } => {
let h = fs.open(&buf[path(path_len)]);
opened(owners, h)
@@ -195,9 +216,10 @@ impl<F: FileSystem, B: Buffers> Server for StorageServer<F, B> {
&mut self,
badge: usize,
words: [usize; 4],
_cap: Option<CapSlot>,
cap: Option<CapSlot>,
) -> ([usize; 4], Option<CapSlot>) {
match StorageRequest::decode(words) {
Some(StorageRequest::SetLog) => (self.set_log(badge, cap).encode(), None),
Some(request) => {
let (response, cap) = self.serve(badge, request);
(response.encode(), cap)

View File

@@ -180,3 +180,18 @@ fn garbage_gets_a_malformed_reply() {
let (words, cap) = s.handle_call(0, [99, 0, 0, 0], None);
assert_eq!((words[0], cap), (MALFORMED, None));
}
#[test]
fn only_the_unbadged_holder_sets_the_log_and_only_once() {
let mut s = server();
let log = CapSlot::new(7);
let set = |s: &mut Srv, badge| {
let (words, _) = s.handle_call(badge, StorageRequest::SetLog.encode(), log);
StorageResponse::decode(words).unwrap()
};
assert_eq!(set(&mut s, 5), StorageResponse::Error);
assert_eq!(s.log(), None);
assert_eq!(set(&mut s, 0), StorageResponse::Ok);
assert_eq!(s.log(), log);
assert_eq!(set(&mut s, 0), StorageResponse::Error);
}