feat(userspace): Call/ReplyRecv IPC, malformed-request replies, servers exit on dead endpoint; drop send/receive compat, select!, async_syscall

This commit is contained in:
2026-10-27 12:06:10 +01:00
parent 2a8a5b0c27
commit e8a2963b24
18 changed files with 96 additions and 101 deletions

View File

@@ -10,8 +10,9 @@ use libkos::syscall;
extern "C" fn main() { extern "C" fn main() {
let ep = CapSlot::const_new(0); let ep = CapSlot::const_new(0);
let (w0, w1, w2, w3) = syscall::receive(ep); if let Ok(syscall::Received::Call { words: [w0, w1, w2, w3], .. }) = syscall::receive_msg(ep) {
syscall::send(ep, w0.wrapping_add(1), w1, w2, w3); let _ = syscall::reply([w0.wrapping_add(1), w1, w2, w3], None);
}
syscall::exit(); syscall::exit();
} }

View File

@@ -18,9 +18,7 @@ const TICK_DELAY: u64 = 5_000_000;
const MAX_RENDER_CMDS: usize = 140; const MAX_RENDER_CMDS: usize = 140;
fn display_send(req: &DisplayRequest) { fn display_send(req: &DisplayRequest) {
let words = req.encode(); let _ = syscall::request(DISPLAY_EP, req.encode());
syscall::send(DISPLAY_EP, words[0], words[1], words[2], words[3]);
syscall::receive(DISPLAY_EP);
} }
fn draw_rect(x: u16, y: u16, w: u16, h: u16, color: u32) { 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) { fn handle_input(snake: &mut Snake) {
// Drain all pending events, apply the latest direction // Drain all pending events, apply the latest direction
loop { loop {
let _ = syscall::send(INPUT_EP, 0, 0, 0, 0); let Ok([tag, kind, _, _]) = syscall::request(INPUT_EP, [0; 4]) else {
let (tag, kind, _, _) = syscall::receive(INPUT_EP); break;
};
if tag == 0 { break; } // NoEvent — queue drained if tag == 0 { break; } // NoEvent — queue drained
if tag == 1 { if tag == 1 {
match kind { match kind {

View File

@@ -14,25 +14,23 @@ extern "C" fn main() {
} else { } else {
run_mock(ep); run_mock(ep);
} }
loop { libkos::syscall::yield_now(); }
} }
#[inline(never)] #[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; use port_framebuffer::Framebuffer;
fb.clear(types::display::Color::rgb(30, 30, 60)); fb.clear(types::display::Color::rgb(30, 30, 60));
fb.flush(); fb.flush();
let mut srv = display_server::server::DisplayServer::new(fb); let mut srv = display_server::server::DisplayServer::new(fb);
server_framework::serve(&mut srv, ep); server_framework::serve(&mut srv, ep)
} }
#[inline(never)] #[inline(never)]
fn run_mock(ep: CapSlot) { fn run_mock(ep: CapSlot) -> ! {
let fb = driver_framebuffer_mock::MockFramebuffer::new( let fb = driver_framebuffer_mock::MockFramebuffer::new(
types::display::Dimension::new(640), types::display::Dimension::new(640),
types::display::Dimension::new(480), types::display::Dimension::new(480),
); );
let mut srv = display_server::server::DisplayServer::new(fb); let mut srv = display_server::server::DisplayServer::new(fb);
server_framework::serve(&mut srv, ep); server_framework::serve(&mut srv, ep)
} }

View File

@@ -14,19 +14,17 @@ extern "C" fn main() {
} else { } else {
run_uart(ep); run_uart(ep);
} }
loop { libkos::syscall::yield_now(); }
} }
#[inline(never)] #[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); let mut srv = input_server::server::InputServer::new(device);
server_framework::serve(&mut srv, ep); server_framework::serve(&mut srv, ep)
} }
#[inline(never)] #[inline(never)]
fn run_uart(ep: CapSlot) { fn run_uart(ep: CapSlot) -> ! {
let device = driver_input_uart::UartInputDevice::new(); let device = driver_input_uart::UartInputDevice::new();
let mut srv = input_server::server::InputServer::new(device); let mut srv = input_server::server::InputServer::new(device);
server_framework::serve(&mut srv, ep); server_framework::serve(&mut srv, ep)
} }

View File

@@ -24,9 +24,7 @@ fn request_file(shared_buf: *mut u8, filename: &[u8]) -> usize {
core::ptr::write_volatile(shared_buf.add(i), b); core::ptr::write_volatile(shared_buf.add(i), b);
} }
} }
syscall::send(STORAGE_EP, CMD_READ_FILE, filename.len(), 0, 0); syscall::request(STORAGE_EP, [CMD_READ_FILE, filename.len(), 0, 0]).map_or(0, |r| r[0])
let (file_size, _, _, _) = syscall::receive(STORAGE_EP);
file_size
} }
struct LoadedProcess { struct LoadedProcess {
@@ -137,8 +135,11 @@ pub fn run(shared_buf_va: usize) -> ! {
libkos::print!("init: loading snake... "); libkos::print!("init: loading snake... ");
if let Some(snake) = load_binary(shared_buf, b"snake.elf") { if let Some(snake) = load_binary(shared_buf, b"snake.elf") {
syscall::delegate(INPUT_EP, snake.process_cap, ipc_perms); // A client only calls: Execute, never Read, so it cannot pose as
syscall::delegate(DISPLAY_EP, snake.process_cap, ipc_perms); // 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); syscall::spawn_thread_in(snake.process_cap, snake.entry_point, USER_STACK_TOP);
libkos::println!("ok"); libkos::println!("ok");
} else { } else {

View File

@@ -2,9 +2,10 @@ use crate::syscall;
use types::bounded::CapSlot; use types::bounded::CapSlot;
use types::display::{Color, Dimension, ScreenCoord}; 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) { fn ipc(ep: CapSlot, w0: usize, w1: usize, w2: usize, w3: usize) -> (usize, usize, usize, usize) {
syscall::send(ep, w0, w1, w2, w3); let r = syscall::request(ep, [w0, w1, w2, w3]).unwrap_or([usize::MAX, 0, 0, 0]);
syscall::receive(ep) (r[0], r[1], r[2], r[3])
} }
pub fn draw_rect( pub fn draw_rect(

View File

@@ -23,9 +23,10 @@ fn pack_path(path: &[u8]) -> (usize, usize) {
(usize::from_le_bytes(w0), usize::from_le_bytes(w1)) (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) { fn ipc(ep: CapSlot, w0: usize, w1: usize, w2: usize, w3: usize) -> (usize, usize, usize, usize) {
syscall::send(ep, w0, w1, w2, w3); let r = syscall::request(ep, [w0, w1, w2, w3]).unwrap_or([usize::MAX, 0, 0, 0]);
syscall::receive(ep) (r[0], r[1], r[2], r[3])
} }
pub fn open(ep: CapSlot, path: &[u8]) -> Option<FileHandle> { pub fn open(ep: CapSlot, path: &[u8]) -> Option<FileHandle> {

View File

@@ -38,8 +38,7 @@ fn decode_key(kind: usize, extra: usize) -> Option<Key> {
} }
pub fn poll_event(ep: CapSlot) -> Option<InputEvent> { pub fn poll_event(ep: CapSlot) -> Option<InputEvent> {
syscall::send(ep, 0, 0, 0, 0); let [tag, kind, extra, _] = syscall::request(ep, [0; 4]).ok()?;
let (tag, kind, extra, _) = syscall::receive(ep);
match tag { match tag {
0 => None, 0 => None,
1 => decode_key(kind, extra).map(InputEvent::KeyDown), 1 => decode_key(kind, extra).map(InputEvent::KeyDown),

View File

@@ -243,14 +243,6 @@ pub fn create_endpoint() -> Option<CapSlot> {
slot_of(call0(SYS_CREATE_ENDPOINT)).ok() 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<CapSlot>) -> Result<()> { pub fn send_msg(endpoint: CapSlot, words: [usize; 4], cap: Option<CapSlot>) -> Result<()> {
raw( raw(
SYS_SEND, SYS_SEND,
@@ -259,12 +251,6 @@ pub fn send_msg(endpoint: CapSlot, words: [usize; 4], cap: Option<CapSlot>) -> R
.map(|_| ()) .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<Received> { pub fn receive_msg(endpoint: CapSlot) -> Result<Received> {
raw(SYS_RECEIVE, [endpoint.value() as usize, 0, 0, 0, 0, 0]).and_then(Received::decode) 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<CapSlot>) -> Resul
.and_then(Received::decode) .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. /// Answers the last `Call` this thread received.
pub fn reply(words: [usize; 4], cap: Option<CapSlot>) -> Result<()> { pub fn reply(words: [usize; 4], cap: Option<CapSlot>) -> Result<()> {
raw(SYS_REPLY, message_regs(0, words, cap)).map(|_| ()) raw(SYS_REPLY, message_regs(0, words, cap)).map(|_| ())

View File

@@ -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) {
}

View File

@@ -1,9 +1,7 @@
#![no_std] #![no_std]
pub mod async_syscall;
pub mod codec; pub mod codec;
pub mod executor; pub mod executor;
pub mod select;
pub mod server; pub mod server;
pub use codec::{Decode, Encode}; pub use codec::{Decode, Encode};

View File

@@ -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
}
};
}

View File

@@ -4,24 +4,52 @@ mod tests;
use crate::codec::{Decode, Encode}; use crate::codec::{Decode, Encode};
use types::bounded::CapSlot; 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 { pub trait Server {
type Request: Decode; type Request: Decode;
type Response: Encode; type Response: Encode;
fn handle(&mut self, request: Self::Request) -> Self::Response; 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 { loop {
let words = libkos::syscall::receive(endpoint); let message = match next {
if let Some(request) = Self::Request::decode([words.0, words.1, words.2, words.3]) { Ok(m) => m,
let response = self.handle(request); Err(e) => return e,
let encoded = response.encode(); };
libkos::syscall::send(endpoint, encoded[0], encoded[1], encoded[2], encoded[3]); 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<S: Server>(server: &mut S, endpoint: CapSlot) { /// Runs `server` on `endpoint` for good. A server whose endpoint is gone
server.run(endpoint); /// has nobody left to serve, so it says why and exits.
pub fn serve<S: Server>(server: &mut S, endpoint: CapSlot) -> ! {
let error = server.run(endpoint);
libkos::println!("server: endpoint {} unusable ({:?}), exiting", endpoint.value(), error);
libkos::syscall::exit()
} }

View File

@@ -17,6 +17,5 @@ pub fn run() -> ! {
let mut srv = server::InputServer::new(device); let mut srv = server::InputServer::new(device);
let ep = types::bounded::CapSlot::new(0).unwrap(); let ep = types::bounded::CapSlot::new(0).unwrap();
server_framework::serve(&mut srv, ep); server_framework::serve(&mut srv, ep)
loop { libkos::syscall::yield_now(); }
} }

View File

@@ -10,6 +10,5 @@ mod tests;
pub fn run() -> ! { pub fn run() -> ! {
let mut srv = server::LogServer::new(); let mut srv = server::LogServer::new();
let ep = types::bounded::CapSlot::new(0).unwrap(); let ep = types::bounded::CapSlot::new(0).unwrap();
server_framework::serve(&mut srv, ep); server_framework::serve(&mut srv, ep)
loop { libkos::syscall::yield_now(); }
} }

View File

@@ -10,6 +10,5 @@ mod tests;
pub fn run() -> ! { pub fn run() -> ! {
let mut srv = server::PermissionManager::new(); let mut srv = server::PermissionManager::new();
let ep = types::bounded::CapSlot::new(0).unwrap(); let ep = types::bounded::CapSlot::new(0).unwrap();
server_framework::serve(&mut srv, ep); server_framework::serve(&mut srv, ep)
loop { libkos::syscall::yield_now(); }
} }

View File

@@ -12,6 +12,5 @@ pub fn run() -> ! {
let mut srv = server::SessionServer::new(create_cap); let mut srv = server::SessionServer::new(create_cap);
srv.register_app("snake", "/apps/snake.elf"); srv.register_app("snake", "/apps/snake.elf");
let ep = types::bounded::CapSlot::new(0).unwrap(); let ep = types::bounded::CapSlot::new(0).unwrap();
server_framework::serve(&mut srv, ep); server_framework::serve(&mut srv, ep)
loop { libkos::syscall::yield_now(); }
} }

View File

@@ -36,10 +36,17 @@ pub fn run() -> ! {
let ep = types::bounded::CapSlot::new(0).unwrap(); let ep = types::bounded::CapSlot::new(0).unwrap();
let mut next = syscall::receive_msg(ep);
loop { loop {
let (w0, w1, _w2, _w3) = syscall::receive(ep); let message = match next {
Ok(m) => m,
match w0 { Err(e) => {
libkos::println!("storage: endpoint unusable ({:?}), exiting", e);
syscall::exit();
}
};
let [w0, w1, _, _] = message.words();
let reply = match w0 {
CMD_READ_FILE => { CMD_READ_FILE => {
let filename_len = w1; let filename_len = w1;
let buf = shared_buf(); let buf = shared_buf();
@@ -60,12 +67,14 @@ pub fn run() -> ! {
libkos::println!("NOT FOUND"); libkos::println!("NOT FOUND");
} }
syscall::send(ep, file_size, 0, 0, 0); [file_size, 0, 0, 0]
} }
_ => { _ => [0, 0, 0, 0],
syscall::send(ep, 0, 0, 0, 0); };
} next = match message {
} syscall::Received::Call { .. } => syscall::reply_recv(ep, reply, None),
_ => syscall::receive_msg(ep),
};
} }
} }