Files
danos/lib/ipc.zig
T
Daniel Samson eabb81684a M7: synchronous IPC — endpoints, handle table, IPC_Call/ReplyWait
The microkernel message backbone the VFS server and drivers will ride on.

- src/kernel/ipc_sync.zig: Endpoint (sender FIFO threaded via Task.next +
  a recv WaitQueue for servers + a small notification ring). call() (client
  sends, wakes a server, blocks) and replyWait() (server replies to the held
  caller, then receives the next). Reply routing keys on Task.ipc_client —
  synchronous IPC owes one reply at a time. Payloads copy frame-to-frame
  through the physmap (copyAcross on arch.translate, added in M5); an unmapped
  page fails the copy instead of #PF-ing. MSG_MAX 256.
- Bootstrap naming: an integer name registry (danos.ServiceId, vfs=1) with
  create_endpoint / ipc_register / ipc_lookup — any process finds a server
  without threading a handle through spawn.
- notifyFromIsr(): ISR-safe async wake (badge with the high bit set), the
  hook M10's IRQ-as-message needs. Unused/untested until then.
- Task gains handles[16] (opaque *Endpoint, to avoid a sched<->ipc import
  cycle) + ipc_client/send/reply/status fields; sched gains
  blockCurrentLocked/readyLocked; arch gains setSyscallResult2 (rdx badge).
- Syscalls 6..10 wired in process.zig; exit() now drops the caller's endpoint
  refs. lib/ipc.zig: user-side createEndpoint/register/lookup/call
  (server-side replyWait lands with the first server in M9).
- New `ipc-call` test: two kernel tasks ping-pong 100 calls, every reply
  request+1. Suite 29/29.
2026-07-09 07:20:57 +01:00

55 lines
2.0 KiB
Zig

//! User-space IPC helpers over the kernel's synchronous IPC syscalls. A client
//! `call`s an endpoint (send + block for reply); the VFS server and drivers are
//! reached this way. The server side (`replyWait`, which returns two values) is
//! added with the first server binary.
const danos = @import("danos");
const sc = @import("syscall.zig");
/// A small-int handle into the calling process's handle table.
pub const Handle = usize;
/// A fixed-size, register-friendly message payload. Server protocols (VFS, driver)
/// layer their own wire format on top of the bytes a call carries.
pub const Message = extern struct {
tag: u64 = 0,
a: u64 = 0,
b: u64 = 0,
c: u64 = 0,
};
/// Whether a syscall return value is a wrapped -errno (lands in the top page).
inline fn failed(r: usize) bool {
return r > ~@as(usize, 0) - 4095;
}
/// Create a new endpoint owned by this process; returns its handle.
pub fn createEndpoint() ?Handle {
const r = sc.syscall0(.create_endpoint);
return if (failed(r)) null else r;
}
/// Publish endpoint `h` under a well-known service id so other processes find it.
pub fn register(id: danos.ServiceId, h: Handle) bool {
return !failed(sc.syscall2(.ipc_register, @intFromEnum(id), h));
}
/// Find the endpoint published under `id`, installing a handle to it in this
/// process.
pub fn lookup(id: danos.ServiceId) ?Handle {
const r = sc.syscall1(.ipc_lookup, @intFromEnum(id));
return if (failed(r)) null else r;
}
pub const CallError = error{Failed};
/// Send `msg` to endpoint `h` and block until the server replies into `reply`.
/// Returns the reply length.
pub fn call(h: Handle, msg: []const u8, reply: []u8) CallError!usize {
const r = sc.syscall5(.ipc_call, h, @intFromPtr(msg.ptr), msg.len, @intFromPtr(reply.ptr), reply.len);
return if (failed(r)) error.Failed else r;
}
// replyWait() (server side — returns message length + sender badge) lands with
// the first server binary (M9), where it can be exercised end to end.