The `system` module (formerly `danos`) had become a grab-bag: it held the
loader<->kernel handoff *and* the kernel<->user ABI *and* the device wire types, in
one module three different audiences imported. Usage proved the seam — the
bootloader never touched the syscall/device ABI, and user space never touched the
boot handoff — so split it by audience, one module per contract:
system/boot-handoff.zig loader <-> kernel: BootInformation, Framebuffer,
MemoryMap, the VM layout + physicalToVirtual, kernel_abi
system/abi.zig kernel <-> user, core: SystemCall, mmap prot flags,
page_size, notify_badge_bit, ServiceId
system/devices/device-abi.zig kernel <-> user, devices: DeviceDescriptor,
DeviceClass, ResourceDescriptor, ResourceKind, ...
device-abi is the devices sub-project's public interface, exposed as its own module
the way vfs exposes vfs-protocol — importable by user space, unlike the
kernel-internal device model it also feeds. That collapses a real duplication:
DeviceClass and ResourceKind were defined twice (device-model.zig and the contract,
kept "in sync by hand"); device-model now re-exports them from device-abi, so the
enum a driver matches on and the one the kernel classifies with are one type.
Each import now declares which contract it speaks: the bootloader imports only
boot-handoff; a driver only abi + device-abi (via the runtime); the kernel all
three. This also retires the `system` / `runtime.system` name overlap. page_size
lands in abi (it's part of the mmap contract user space aligns to); the bootloader
keeps its own local 4 KiB constant so it depends on nothing but the handoff.
All 21 importers rewired, docs updated to keep /system mapping to source. Build,
host tests, and the QEMU suite (36/36) all green.
53 lines
1.9 KiB
Zig
53 lines
1.9 KiB
Zig
//! Typed system_call surface for user space — thin wrappers over the raw `system_call`
|
|
//! stubs, one per kernel call. Numbers come from `abi.SystemCall`, the single
|
|
//! source of truth shared with the kernel dispatcher.
|
|
|
|
const abi = @import("abi");
|
|
const sc = @import("system-call.zig");
|
|
|
|
/// `mmap` protection flags (matching the usual C bit values). Grants are always
|
|
/// readable+writable today; the kernel does not yet honour finer prot.
|
|
pub const PROT_READ: usize = abi.prot_read;
|
|
pub const PROT_WRITE: usize = abi.prot_write;
|
|
pub const PROT_EXEC: usize = abi.prot_exec;
|
|
|
|
/// Give up the rest of this quantum.
|
|
pub fn yield() void {
|
|
_ = sc.systemCall0(.yield);
|
|
}
|
|
|
|
/// Write raw bytes to the kernel log (a bring-up diagnostic; real output goes
|
|
/// through the console/VFS later). Returns the byte count, or a wrapped -1.
|
|
pub fn write(message: []const u8) usize {
|
|
return sc.systemCall2(.debug_write, @intFromPtr(message.ptr), message.len);
|
|
}
|
|
|
|
/// Block the caller for `ms` milliseconds.
|
|
pub fn sleep(ms: usize) void {
|
|
_ = sc.systemCall1(.sleep, ms);
|
|
}
|
|
|
|
/// End the process. Never returns.
|
|
pub fn exit(code: usize) noreturn {
|
|
_ = sc.systemCall1(.exit, code);
|
|
unreachable; // the kernel never returns from exit
|
|
}
|
|
|
|
/// Grant `len` bytes (rounded up to whole pages) of fresh, zeroed, writable
|
|
/// memory and return the base virtual address. On failure returns a value in the
|
|
/// top page (see `mmapFailed`). The user heap grows through this call.
|
|
pub fn mmap(len: usize, prot: usize) usize {
|
|
return sc.systemCall2(.mmap, len, prot);
|
|
}
|
|
|
|
/// Release a range previously handed out by `mmap`.
|
|
pub fn munmap(base: usize, len: usize) usize {
|
|
return sc.systemCall2(.munmap, base, len);
|
|
}
|
|
|
|
/// Whether an `mmap` return value is an error (the kernel returns a wrapped
|
|
/// -errno, which lands in the top page — no real grant base is ever that high).
|
|
pub inline fn mmapFailed(ret: usize) bool {
|
|
return ret > ~@as(usize, 0) - 4095;
|
|
}
|