//! 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.