Files
danos/lib/ipc.zig
T
Daniel Samson b0f894f50c M9: user-space VFS server + client file API (open/read/write/stat + stdio)
The payoff milestone: files are served by a user-space process, reached over
IPC — the kernel never sees a path or an fd.

- lib/vfs_proto.zig: the VFS wire protocol (Op, Request/Reply fixed header +
  inline payload, Stat), shared by client and server; one message <= MSG_MAX.
- lib/ipc.zig: replyWait() — the server-side dual-return stub (length in rax,
  badge in rdx via a "+{rdx}" read-write operand), deferred from M7.
- sbin/vfs.zig: the real VFS server — an in-heap ramfs (open creates a node)
  with an IPC_ReplyWait dispatch loop serving open/read/write/stat/close.
  Registers its endpoint under the well-known vfs id at startup.
- lib/unistd.zig: POSIX-style client API in rt — a per-process fd table +
  open/close/read/write/lseek/stat, each an IPC_Call to the VFS. The kernel
  knows nothing of fds; the table lives here.
- lib/stdio.zig: C stdio over unistd — FILE + fopen/fclose/fread/fwrite/
  fseek/ftell/rewind/feof/ferror/fputs/fputc/fgetc (unbuffered for now).
- sbin/vfstest.zig: a client that opens/writes/seeks/reads a file and only
  heartbeats "vfstest: ok" if the round trip matched. Packed in the initrd.
- New `vfs` test drives it end to end. initrd test relaxed to generic
  liveness. Suite 31/31.
2026-07-09 07:48:24 +01:00

79 lines
3.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;
}
/// The result of a `replyWait`: the request length and the sender's badge (a
/// task id, or an IRQ notification if the high bit is set).
pub const Received = struct {
len: usize,
badge: u64,
};
/// Server side of IPC_ReplyWait: deliver `reply` to the client last received (if
/// any), then block until the next request arrives in `recv`. Returns its length
/// and the sender badge. This syscall returns two values — the length in rax and
/// the badge in rdx — so it needs a hand-written stub: rdx is a read-write
/// operand (input = reply length, arg #3; output = badge).
pub fn replyWait(h: Handle, reply: []const u8, recv: []u8) Received {
var rax: usize = undefined;
var rdx: usize = reply.len; // in: reply_len (arg #3 -> rdx); out: badge
asm volatile ("syscall"
: [rax] "={rax}" (rax),
[rdx] "+{rdx}" (rdx),
: [n] "{rax}" (@intFromEnum(danos.Syscall.ipc_reply_wait)),
[a0] "{rdi}" (h),
[a1] "{rsi}" (@intFromPtr(reply.ptr)),
[a3] "{r10}" (@intFromPtr(recv.ptr)),
[a4] "{r8}" (recv.len),
: .{ .rcx = true, .r11 = true, .memory = true });
return .{ .len = rax, .badge = rdx };
}