M6: user runtime library rt + C-convention heap
Add lib/ — the shared user-space runtime every user binary links against (init now, servers/drivers later): syscall wrappers, the heap, IPC stub, and the process start shim. - lib/heap.zig: the kernel first-fit free-list ported to user space, grown via the mmap syscall instead of pmm+mapPage. Dual API over one global free list: extern "C" malloc/free/calloc/realloc (C ABI for future C code) and a std.mem.Allocator adapter (with in-place resize) for Zig std containers. - lib/syscall.zig + sys.zig: raw syscall0..5 (arg3 in r10) and typed yield/write/sleep/exit/mmap/munmap over danos.Syscall. - lib/start.zig: naked _start -> rt_start -> root.main() (SysV realign via call), panic -> exit(127). - lib/user.ld: the user link script, moved from sbin/linker.ld (shared by all user binaries). - build.zig: register the `rt` module; add an addUserBinary() helper that is the one recipe for every user binary (freestanding, .large, use_lld, user.ld, image_base), replacing the bespoke init block. - sbin/init.zig: migrated onto rt; drops its hand-rolled syscall2/shims. Now proves the heap (alloc -> write from a heap pointer -> free) before the heartbeat loop. Serial shows "init: heap ok". Suite 28/28.
This commit is contained in:
+52
@@ -0,0 +1,52 @@
|
||||
//! Typed syscall surface for user space — thin wrappers over the raw `syscall`
|
||||
//! stubs, one per kernel call. Numbers come from `danos.Syscall`, the single
|
||||
//! source of truth shared with the kernel dispatcher.
|
||||
|
||||
const danos = @import("danos");
|
||||
const sc = @import("syscall.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 = danos.prot_read;
|
||||
pub const PROT_WRITE: usize = danos.prot_write;
|
||||
pub const PROT_EXEC: usize = danos.prot_exec;
|
||||
|
||||
/// Give up the rest of this quantum.
|
||||
pub fn yield() void {
|
||||
_ = sc.syscall0(.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(msg: []const u8) usize {
|
||||
return sc.syscall2(.debug_write, @intFromPtr(msg.ptr), msg.len);
|
||||
}
|
||||
|
||||
/// Block the caller for `ms` milliseconds.
|
||||
pub fn sleep(ms: usize) void {
|
||||
_ = sc.syscall1(.sleep, ms);
|
||||
}
|
||||
|
||||
/// End the process. Never returns.
|
||||
pub fn exit(code: usize) noreturn {
|
||||
_ = sc.syscall1(.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.syscall2(.mmap, len, prot);
|
||||
}
|
||||
|
||||
/// Release a range previously handed out by `mmap`.
|
||||
pub fn munmap(base: usize, len: usize) usize {
|
||||
return sc.syscall2(.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;
|
||||
}
|
||||
Reference in New Issue
Block a user