From e8a2963b242105a85b2cc40c8251148d8dbeabbb Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Tue, 27 Oct 2026 12:06:10 +0100 Subject: [PATCH] feat(userspace): Call/ReplyRecv IPC, malformed-request replies, servers exit on dead endpoint; drop send/receive compat, select!, async_syscall --- crates/apps/echo/src/main.rs | 5 +- crates/apps/snake/src/main.rs | 9 ++-- crates/bins/display-qemu/src/main.rs | 10 ++-- crates/bins/input-qemu/src/main.rs | 10 ++-- crates/init/src/lib.rs | 11 +++-- crates/libkos/src/display.rs | 5 +- crates/libkos/src/fs.rs | 5 +- crates/libkos/src/input.rs | 3 +- crates/libkos/src/syscall.rs | 24 ++++------ .../server-framework/src/async_syscall/mod.rs | 16 ------- crates/server-framework/src/lib.rs | 2 - crates/server-framework/src/select/mod.rs | 14 ------ crates/server-framework/src/server/mod.rs | 46 +++++++++++++++---- crates/servers/input-server/src/lib.rs | 3 +- crates/servers/log-server/src/lib.rs | 3 +- crates/servers/permission-manager/src/lib.rs | 3 +- crates/servers/session-server/src/lib.rs | 3 +- crates/servers/storage-server/src/lib.rs | 25 ++++++---- 18 files changed, 96 insertions(+), 101 deletions(-) delete mode 100644 crates/server-framework/src/async_syscall/mod.rs delete mode 100644 crates/server-framework/src/select/mod.rs diff --git a/crates/apps/echo/src/main.rs b/crates/apps/echo/src/main.rs index 54ccd2e..67fa3cb 100644 --- a/crates/apps/echo/src/main.rs +++ b/crates/apps/echo/src/main.rs @@ -10,8 +10,9 @@ use libkos::syscall; extern "C" fn main() { let ep = CapSlot::const_new(0); - let (w0, w1, w2, w3) = syscall::receive(ep); - syscall::send(ep, w0.wrapping_add(1), w1, w2, w3); + if let Ok(syscall::Received::Call { words: [w0, w1, w2, w3], .. }) = syscall::receive_msg(ep) { + let _ = syscall::reply([w0.wrapping_add(1), w1, w2, w3], None); + } syscall::exit(); } diff --git a/crates/apps/snake/src/main.rs b/crates/apps/snake/src/main.rs index c5fd63b..56cae66 100644 --- a/crates/apps/snake/src/main.rs +++ b/crates/apps/snake/src/main.rs @@ -18,9 +18,7 @@ const TICK_DELAY: u64 = 5_000_000; const MAX_RENDER_CMDS: usize = 140; fn display_send(req: &DisplayRequest) { - let words = req.encode(); - syscall::send(DISPLAY_EP, words[0], words[1], words[2], words[3]); - syscall::receive(DISPLAY_EP); + let _ = syscall::request(DISPLAY_EP, req.encode()); } fn draw_rect(x: u16, y: u16, w: u16, h: u16, color: u32) { @@ -67,8 +65,9 @@ extern "C" fn main() { fn handle_input(snake: &mut Snake) { // Drain all pending events, apply the latest direction loop { - let _ = syscall::send(INPUT_EP, 0, 0, 0, 0); - let (tag, kind, _, _) = syscall::receive(INPUT_EP); + let Ok([tag, kind, _, _]) = syscall::request(INPUT_EP, [0; 4]) else { + break; + }; if tag == 0 { break; } // NoEvent — queue drained if tag == 1 { match kind { diff --git a/crates/bins/display-qemu/src/main.rs b/crates/bins/display-qemu/src/main.rs index c24ba05..7e56211 100644 --- a/crates/bins/display-qemu/src/main.rs +++ b/crates/bins/display-qemu/src/main.rs @@ -14,25 +14,23 @@ extern "C" fn main() { } else { run_mock(ep); } - - loop { libkos::syscall::yield_now(); } } #[inline(never)] -fn run_virtio(mut fb: driver_framebuffer_virtio::VirtioFramebuffer, ep: CapSlot) { +fn run_virtio(mut fb: driver_framebuffer_virtio::VirtioFramebuffer, ep: CapSlot) -> ! { use port_framebuffer::Framebuffer; fb.clear(types::display::Color::rgb(30, 30, 60)); fb.flush(); let mut srv = display_server::server::DisplayServer::new(fb); - server_framework::serve(&mut srv, ep); + server_framework::serve(&mut srv, ep) } #[inline(never)] -fn run_mock(ep: CapSlot) { +fn run_mock(ep: CapSlot) -> ! { let fb = driver_framebuffer_mock::MockFramebuffer::new( types::display::Dimension::new(640), types::display::Dimension::new(480), ); let mut srv = display_server::server::DisplayServer::new(fb); - server_framework::serve(&mut srv, ep); + server_framework::serve(&mut srv, ep) } diff --git a/crates/bins/input-qemu/src/main.rs b/crates/bins/input-qemu/src/main.rs index 29a249d..48ac59a 100644 --- a/crates/bins/input-qemu/src/main.rs +++ b/crates/bins/input-qemu/src/main.rs @@ -14,19 +14,17 @@ extern "C" fn main() { } else { run_uart(ep); } - - loop { libkos::syscall::yield_now(); } } #[inline(never)] -fn run_input(device: driver_input_virtio::VirtioInputDevice, ep: CapSlot) { +fn run_input(device: driver_input_virtio::VirtioInputDevice, ep: CapSlot) -> ! { let mut srv = input_server::server::InputServer::new(device); - server_framework::serve(&mut srv, ep); + server_framework::serve(&mut srv, ep) } #[inline(never)] -fn run_uart(ep: CapSlot) { +fn run_uart(ep: CapSlot) -> ! { let device = driver_input_uart::UartInputDevice::new(); let mut srv = input_server::server::InputServer::new(device); - server_framework::serve(&mut srv, ep); + server_framework::serve(&mut srv, ep) } diff --git a/crates/init/src/lib.rs b/crates/init/src/lib.rs index 2c0b0fb..6563323 100644 --- a/crates/init/src/lib.rs +++ b/crates/init/src/lib.rs @@ -24,9 +24,7 @@ fn request_file(shared_buf: *mut u8, filename: &[u8]) -> usize { core::ptr::write_volatile(shared_buf.add(i), b); } } - syscall::send(STORAGE_EP, CMD_READ_FILE, filename.len(), 0, 0); - let (file_size, _, _, _) = syscall::receive(STORAGE_EP); - file_size + syscall::request(STORAGE_EP, [CMD_READ_FILE, filename.len(), 0, 0]).map_or(0, |r| r[0]) } struct LoadedProcess { @@ -137,8 +135,11 @@ pub fn run(shared_buf_va: usize) -> ! { libkos::print!("init: loading snake... "); if let Some(snake) = load_binary(shared_buf, b"snake.elf") { - syscall::delegate(INPUT_EP, snake.process_cap, ipc_perms); - syscall::delegate(DISPLAY_EP, snake.process_cap, ipc_perms); + // A client only calls: Execute, never Read, so it cannot pose as + // the server and take other clients' requests. + let client = PermissionSet::empty().with(Permission::Execute); + syscall::delegate(INPUT_EP, snake.process_cap, client); + syscall::delegate(DISPLAY_EP, snake.process_cap, client); syscall::spawn_thread_in(snake.process_cap, snake.entry_point, USER_STACK_TOP); libkos::println!("ok"); } else { diff --git a/crates/libkos/src/display.rs b/crates/libkos/src/display.rs index 6f827f0..555029f 100644 --- a/crates/libkos/src/display.rs +++ b/crates/libkos/src/display.rs @@ -2,9 +2,10 @@ use crate::syscall; use types::bounded::CapSlot; use types::display::{Color, Dimension, ScreenCoord}; +/// A failed call reads as an error reply (tag `usize::MAX`). fn ipc(ep: CapSlot, w0: usize, w1: usize, w2: usize, w3: usize) -> (usize, usize, usize, usize) { - syscall::send(ep, w0, w1, w2, w3); - syscall::receive(ep) + let r = syscall::request(ep, [w0, w1, w2, w3]).unwrap_or([usize::MAX, 0, 0, 0]); + (r[0], r[1], r[2], r[3]) } pub fn draw_rect( diff --git a/crates/libkos/src/fs.rs b/crates/libkos/src/fs.rs index c0dd432..fbfa0f2 100644 --- a/crates/libkos/src/fs.rs +++ b/crates/libkos/src/fs.rs @@ -23,9 +23,10 @@ fn pack_path(path: &[u8]) -> (usize, usize) { (usize::from_le_bytes(w0), usize::from_le_bytes(w1)) } +/// A failed call reads as an error reply (tag `usize::MAX`). fn ipc(ep: CapSlot, w0: usize, w1: usize, w2: usize, w3: usize) -> (usize, usize, usize, usize) { - syscall::send(ep, w0, w1, w2, w3); - syscall::receive(ep) + let r = syscall::request(ep, [w0, w1, w2, w3]).unwrap_or([usize::MAX, 0, 0, 0]); + (r[0], r[1], r[2], r[3]) } pub fn open(ep: CapSlot, path: &[u8]) -> Option { diff --git a/crates/libkos/src/input.rs b/crates/libkos/src/input.rs index 64e759f..718e9f6 100644 --- a/crates/libkos/src/input.rs +++ b/crates/libkos/src/input.rs @@ -38,8 +38,7 @@ fn decode_key(kind: usize, extra: usize) -> Option { } pub fn poll_event(ep: CapSlot) -> Option { - syscall::send(ep, 0, 0, 0, 0); - let (tag, kind, extra, _) = syscall::receive(ep); + let [tag, kind, extra, _] = syscall::request(ep, [0; 4]).ok()?; match tag { 0 => None, 1 => decode_key(kind, extra).map(InputEvent::KeyDown), diff --git a/crates/libkos/src/syscall.rs b/crates/libkos/src/syscall.rs index 4f2e67d..0156ef2 100644 --- a/crates/libkos/src/syscall.rs +++ b/crates/libkos/src/syscall.rs @@ -243,14 +243,6 @@ pub fn create_endpoint() -> Option { slot_of(call0(SYS_CREATE_ENDPOINT)).ok() } -/// Sends four words; returns 0 on success (kept for existing callers). -pub fn send(endpoint: CapSlot, w0: usize, w1: usize, w2: usize, w3: usize) -> usize { - match send_msg(endpoint, [w0, w1, w2, w3], None) { - Ok(()) => 0, - Err(e) => e as usize, - } -} - pub fn send_msg(endpoint: CapSlot, words: [usize; 4], cap: Option) -> Result<()> { raw( SYS_SEND, @@ -259,12 +251,6 @@ pub fn send_msg(endpoint: CapSlot, words: [usize; 4], cap: Option) -> R .map(|_| ()) } -/// Receives and returns just the words (kept for existing callers). -pub fn receive(endpoint: CapSlot) -> (usize, usize, usize, usize) { - let w = receive_msg(endpoint).map_or([0; 4], |r| r.words()); - (w[0], w[1], w[2], w[3]) -} - pub fn receive_msg(endpoint: CapSlot) -> Result { raw(SYS_RECEIVE, [endpoint.value() as usize, 0, 0, 0, 0, 0]).and_then(Received::decode) } @@ -278,6 +264,16 @@ pub fn call(endpoint: CapSlot, words: [usize; 4], cap: Option) -> Resul .and_then(Received::decode) } +/// Calls a server with four words and returns its reply's words. Needs +/// only Execute on the endpoint; the reply comes back through the +/// kernel's reply slot, so it can never reach another client. +pub fn request(endpoint: CapSlot, words: [usize; 4]) -> Result<[usize; 4]> { + match call(endpoint, words, None)? { + Received::Signal { .. } => Err(Error::InvalidArgument), + reply => Ok(reply.words()), + } +} + /// Answers the last `Call` this thread received. pub fn reply(words: [usize; 4], cap: Option) -> Result<()> { raw(SYS_REPLY, message_regs(0, words, cap)).map(|_| ()) diff --git a/crates/server-framework/src/async_syscall/mod.rs b/crates/server-framework/src/async_syscall/mod.rs deleted file mode 100644 index 6531460..0000000 --- a/crates/server-framework/src/async_syscall/mod.rs +++ /dev/null @@ -1,16 +0,0 @@ -use types::bounded::CapSlot; - -pub async fn send(endpoint: CapSlot, w0: usize, w1: usize, w2: usize, w3: usize) -> usize { - libkos::syscall::send(endpoint, w0, w1, w2, w3) -} - -pub async fn receive(endpoint: CapSlot) -> (usize, usize, usize, usize) { - libkos::syscall::receive(endpoint) -} - -pub async fn wait_for_interrupt(_irq: CapSlot) { - libkos::syscall::yield_now(); -} - -pub async fn acknowledge_interrupt(_irq: CapSlot) { -} diff --git a/crates/server-framework/src/lib.rs b/crates/server-framework/src/lib.rs index b0dfa5e..2603465 100644 --- a/crates/server-framework/src/lib.rs +++ b/crates/server-framework/src/lib.rs @@ -1,9 +1,7 @@ #![no_std] -pub mod async_syscall; pub mod codec; pub mod executor; -pub mod select; pub mod server; pub use codec::{Decode, Encode}; diff --git a/crates/server-framework/src/select/mod.rs b/crates/server-framework/src/select/mod.rs deleted file mode 100644 index db50465..0000000 --- a/crates/server-framework/src/select/mod.rs +++ /dev/null @@ -1,14 +0,0 @@ -#[macro_export] -macro_rules! select { - ( - $msg:ident = receive($ep:expr) => $ipc_body:block - $irq:ident = wait_interrupt($irq_cap:expr) => $irq_body:block - ) => { - { - let sources = types::syscall_abi::SYS_WAIT_ANY; - let wake = libkos::syscall::receive($ep as usize); - let $msg = wake; - $ipc_body - } - }; -} diff --git a/crates/server-framework/src/server/mod.rs b/crates/server-framework/src/server/mod.rs index 238c22f..a8caa5e 100644 --- a/crates/server-framework/src/server/mod.rs +++ b/crates/server-framework/src/server/mod.rs @@ -4,24 +4,52 @@ mod tests; use crate::codec::{Decode, Encode}; use types::bounded::CapSlot; +/// The reply to a request that does not decode. No protocol uses this +/// tag, so every client decodes it as an error. +pub const MALFORMED: usize = usize::MAX; + pub trait Server { type Request: Decode; type Response: Encode; fn handle(&mut self, request: Self::Request) -> Self::Response; - fn run(&mut self, endpoint: CapSlot) { + /// Serves `endpoint` until receiving on it fails (it was destroyed or + /// the capability revoked), then returns why. Every `Call` gets a + /// reply, `[MALFORMED, ..]` if it does not decode; a one-way `Send` is + /// handled without one; notification signals are ignored. + fn run(&mut self, endpoint: CapSlot) -> libkos::syscall::Error { + use libkos::syscall::{Received, receive_msg, reply_recv}; + let mut next = receive_msg(endpoint); loop { - let words = libkos::syscall::receive(endpoint); - if let Some(request) = Self::Request::decode([words.0, words.1, words.2, words.3]) { - let response = self.handle(request); - let encoded = response.encode(); - libkos::syscall::send(endpoint, encoded[0], encoded[1], encoded[2], encoded[3]); - } + let message = match next { + Ok(m) => m, + Err(e) => return e, + }; + next = match message { + Received::Call { words, .. } => { + let reply = match Self::Request::decode(words) { + Some(request) => self.handle(request).encode(), + None => [MALFORMED, 0, 0, 0], + }; + reply_recv(endpoint, reply, None) + } + Received::Message { words, .. } => { + if let Some(request) = Self::Request::decode(words) { + let _ = self.handle(request); + } + receive_msg(endpoint) + } + Received::Signal { .. } => receive_msg(endpoint), + }; } } } -pub fn serve(server: &mut S, endpoint: CapSlot) { - server.run(endpoint); +/// Runs `server` on `endpoint` for good. A server whose endpoint is gone +/// has nobody left to serve, so it says why and exits. +pub fn serve(server: &mut S, endpoint: CapSlot) -> ! { + let error = server.run(endpoint); + libkos::println!("server: endpoint {} unusable ({:?}), exiting", endpoint.value(), error); + libkos::syscall::exit() } diff --git a/crates/servers/input-server/src/lib.rs b/crates/servers/input-server/src/lib.rs index a5a27d7..a63db40 100644 --- a/crates/servers/input-server/src/lib.rs +++ b/crates/servers/input-server/src/lib.rs @@ -17,6 +17,5 @@ pub fn run() -> ! { let mut srv = server::InputServer::new(device); let ep = types::bounded::CapSlot::new(0).unwrap(); - server_framework::serve(&mut srv, ep); - loop { libkos::syscall::yield_now(); } + server_framework::serve(&mut srv, ep) } diff --git a/crates/servers/log-server/src/lib.rs b/crates/servers/log-server/src/lib.rs index a8b2011..186ec60 100644 --- a/crates/servers/log-server/src/lib.rs +++ b/crates/servers/log-server/src/lib.rs @@ -10,6 +10,5 @@ mod tests; pub fn run() -> ! { let mut srv = server::LogServer::new(); let ep = types::bounded::CapSlot::new(0).unwrap(); - server_framework::serve(&mut srv, ep); - loop { libkos::syscall::yield_now(); } + server_framework::serve(&mut srv, ep) } diff --git a/crates/servers/permission-manager/src/lib.rs b/crates/servers/permission-manager/src/lib.rs index d33b944..ca22e5a 100644 --- a/crates/servers/permission-manager/src/lib.rs +++ b/crates/servers/permission-manager/src/lib.rs @@ -10,6 +10,5 @@ mod tests; pub fn run() -> ! { let mut srv = server::PermissionManager::new(); let ep = types::bounded::CapSlot::new(0).unwrap(); - server_framework::serve(&mut srv, ep); - loop { libkos::syscall::yield_now(); } + server_framework::serve(&mut srv, ep) } diff --git a/crates/servers/session-server/src/lib.rs b/crates/servers/session-server/src/lib.rs index 50c6097..c496ce4 100644 --- a/crates/servers/session-server/src/lib.rs +++ b/crates/servers/session-server/src/lib.rs @@ -12,6 +12,5 @@ pub fn run() -> ! { let mut srv = server::SessionServer::new(create_cap); srv.register_app("snake", "/apps/snake.elf"); let ep = types::bounded::CapSlot::new(0).unwrap(); - server_framework::serve(&mut srv, ep); - loop { libkos::syscall::yield_now(); } + server_framework::serve(&mut srv, ep) } diff --git a/crates/servers/storage-server/src/lib.rs b/crates/servers/storage-server/src/lib.rs index f4dcbf6..7c57bc9 100644 --- a/crates/servers/storage-server/src/lib.rs +++ b/crates/servers/storage-server/src/lib.rs @@ -36,10 +36,17 @@ pub fn run() -> ! { let ep = types::bounded::CapSlot::new(0).unwrap(); + let mut next = syscall::receive_msg(ep); loop { - let (w0, w1, _w2, _w3) = syscall::receive(ep); - - match w0 { + let message = match next { + Ok(m) => m, + Err(e) => { + libkos::println!("storage: endpoint unusable ({:?}), exiting", e); + syscall::exit(); + } + }; + let [w0, w1, _, _] = message.words(); + let reply = match w0 { CMD_READ_FILE => { let filename_len = w1; let buf = shared_buf(); @@ -60,12 +67,14 @@ pub fn run() -> ! { libkos::println!("NOT FOUND"); } - syscall::send(ep, file_size, 0, 0, 0); + [file_size, 0, 0, 0] } - _ => { - syscall::send(ep, 0, 0, 0, 0); - } - } + _ => [0, 0, 0, 0], + }; + next = match message { + syscall::Received::Call { .. } => syscall::reply_recv(ep, reply, None), + _ => syscall::receive_msg(ep), + }; } }