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.
This commit is contained in:
+46
-7
@@ -1,10 +1,16 @@
|
||||
//! User-space IPC helpers. The kernel's synchronous IPC syscalls
|
||||
//! (create_endpoint, ipc_register/lookup, ipc_call, ipc_reply_wait) arrive in a
|
||||
//! later milestone; this reserves the module boundary now so drivers and servers
|
||||
//! can be written against `rt.ipc` without restructuring once they light up.
|
||||
//! 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.
|
||||
|
||||
/// A fixed-size, register-friendly message payload. The wire format for the VFS
|
||||
/// and driver protocols is layered on top of this by the servers themselves.
|
||||
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,
|
||||
@@ -12,4 +18,37 @@ pub const Message = extern struct {
|
||||
c: u64 = 0,
|
||||
};
|
||||
|
||||
// call() / replyWait() / createEndpoint() land with the kernel IPC syscalls.
|
||||
/// 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.
|
||||
|
||||
Reference in New Issue
Block a user