Rename the shared contract module danos -> system; QEMU logs to /var/log/system

The shared kernel<->user ABI contract (BootInformation, the SystemCall numbers,
DeviceDescriptor, page_size, ...) is now the `system` module at
system/system.zig, following the convention that a directory's root file takes
the directory's name.

One overlap to note: the runtime's syscall wrappers are already `runtime.system`,
so the single file that uses both the contract and those wrappers
(library/runtime/heap.zig) aliases the wrappers locally as `system_calls`. The
two are distinct (top-level `system` vs `runtime.system`); everywhere else the
contract is just `system`.

Also: the QEMU run's serial capture now lands in the FHS log location,
zig-out/var/log/system/serial0-<timestamp>.log — a stand-in for the kernel's own
logging system, which will eventually write there itself.

Suite 35/35 plus host tests green.
This commit is contained in:
Daniel Samson
2026-07-10 14:09:38 +01:00
parent 3d1de37d0e
commit d19a0ae38d
38 changed files with 202 additions and 198 deletions
+12 -12
View File
@@ -21,7 +21,7 @@
const std = @import("std");
const elf = std.elf;
const danos = @import("danos");
const system = @import("system");
const architecture = @import("architecture");
const pmm = @import("pmm.zig");
const scheduler = @import("scheduler.zig");
@@ -31,8 +31,8 @@ const devices_broker = @import("devices-broker.zig");
const irq = @import("irq.zig");
const log = @import("log.zig");
const page_size = danos.page_size;
const SystemCall = danos.SystemCall;
const page_size = system.page_size;
const SystemCall = system.SystemCall;
/// User virtual addresses. PML4 index 224 — a user-exclusive region, far from
/// the identity map (low indices) and the vmm test address (index 128), so
@@ -79,7 +79,7 @@ pub var write_from_user: bool = false;
pub var write_count: u64 = 0; // total write syscalls served (for the heartbeat tests)
pub var exit_code: u64 = 0;
/// The system_call surface, dispatched on the saved system_call number (`danos.SystemCall`).
/// The system_call surface, dispatched on the saved system_call number (`system.SystemCall`).
/// This is the microkernel-minimal set — memory + scheduling only; file/device
/// I/O will arrive as IPC to user-space servers (docs/syscall.md). The result is
/// written back into the trap frame, since the entry paths restore user registers
@@ -203,9 +203,9 @@ fn systemDeviceEnumerate(state: *architecture.CpuState) void {
const maximum = architecture.systemCallArg(state, 1);
const t = scheduler.current();
if (t.aspace == 0 or buffer_ptr >= user_half_end) return fail(state);
const sz = @sizeOf(danos.DeviceDescriptor);
const sz = @sizeOf(system.DeviceDescriptor);
const cap = @min(maximum, (user_half_end - buffer_ptr) / sz); // clamp to the user half
const out: [*]danos.DeviceDescriptor = @ptrFromInt(buffer_ptr);
const out: [*]system.DeviceDescriptor = @ptrFromInt(buffer_ptr);
architecture.setSystemCallResult(state, devices_broker.enumerate(out[0..@intCast(cap)]));
}
@@ -228,7 +228,7 @@ fn systemMmioMap(state: *architecture.CpuState) void {
const owner = devices_broker.ownerOf(device_id) orelse return fail(state);
if (owner != t.id) return fail(state); // not claimed by this process
const r = devices_broker.resourceOf(device_id, resource_index) orelse return fail(state);
if (r.kind != @intFromEnum(danos.ResourceKind.memory)) return fail(state);
if (r.kind != @intFromEnum(system.ResourceKind.memory)) return fail(state);
if (t.device_map_next == 0) t.device_map_next = device_arena_base;
const first = r.start & ~@as(u64, page_size - 1);
@@ -260,7 +260,7 @@ fn systemDeviceRegister(state: *architecture.CpuState) void {
const t = scheduler.current();
if (t.aspace == 0) return fail(state);
var descriptor: danos.DeviceDescriptor = undefined;
var descriptor: system.DeviceDescriptor = undefined;
if (!ipc.copyFromUser(t.aspace, descriptor_ptr, std.mem.asBytes(&descriptor))) return fail(state);
const id = devices_broker.register(parent_id, t.id, &descriptor) catch return fail(state);
@@ -284,7 +284,7 @@ fn ownedGsi(t: *scheduler.Task, device_id: u64, resource_index: u64) ?u32 {
const owner = devices_broker.ownerOf(device_id) orelse return null;
if (owner != t.id) return null;
const r = devices_broker.resourceOf(device_id, resource_index) orelse return null;
if (r.kind != @intFromEnum(danos.ResourceKind.irq)) return null;
if (r.kind != @intFromEnum(system.ResourceKind.irq)) return null;
if (r.start >= irq.maximum_gsi) return null;
return @intCast(r.start);
}
@@ -373,7 +373,7 @@ fn systemMmap(state: *architecture.CpuState) void {
}
for (frames[0..pages], 0..) |frame, i| {
const destination: [*]u8 = @ptrFromInt(danos.physicalToVirtual(frame));
const destination: [*]u8 = @ptrFromInt(system.physicalToVirtual(frame));
@memset(destination[0..page_size], 0); // hand out zeroed memory
architecture.mapUserPageInto(t.aspace, base + i * page_size, frame, true, false); // RW + NX
}
@@ -428,7 +428,7 @@ pub fn run(blob: []const u8) RunError!void {
// Fill the code frame through the physmap (supervisor RW): the user-facing
// mapping is read-only, and this also sidesteps CR0.WP/SMAP. The tail is
// padded with int3 so a stray jump traps instead of sliding.
const code: [*]u8 = @ptrFromInt(danos.physicalToVirtual(code_frame));
const code: [*]u8 = @ptrFromInt(system.physicalToVirtual(code_frame));
@memcpy(code[0..blob.len], blob);
@memset(code[blob.len..page_size], 0xCC);
@@ -542,7 +542,7 @@ fn parseSegments(image: []const u8, segs: *[maximum_segments]Segment) InitError!
/// frame mapped into it — so no per-page rollback list is needed here.
fn loadPageInto(aspace: u64, image: []const u8, seg: Segment, page_index: u64) InitError!void {
const frame = pmm.alloc() orelse return error.OutOfMemory;
const destination: [*]u8 = @ptrFromInt(danos.physicalToVirtual(frame));
const destination: [*]u8 = @ptrFromInt(system.physicalToVirtual(frame));
@memset(destination[0..page_size], 0);
const page_off = page_index * page_size;
if (page_off < seg.filesz) {