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.
55 lines
2.0 KiB
Zig
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.
|