Split the system contract into boot-handoff / abi / device-abi

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.
This commit is contained in:
Daniel Samson
2026-07-10 18:08:51 +01:00
parent 47610e8ee2
commit be81394be3
37 changed files with 395 additions and 337 deletions
+8 -7
View File
@@ -22,13 +22,14 @@
//! copy is a later security-track item, matching the existing debug_write gap.
const std = @import("std");
const system = @import("system");
const boot_handoff = @import("boot-handoff");
const abi = @import("abi");
const architecture = @import("architecture");
const scheduler = @import("scheduler.zig");
const sync = @import("sync.zig");
const heap = @import("heap.zig");
const page_size = system.page_size;
const page_size = abi.page_size;
const Task = scheduler.Task;
/// Largest message a single call/reply may carry. Bumping it is trivial; kept
@@ -50,8 +51,8 @@ pub const ENOMEM: i64 = 6; // out of memory
/// message from a client — there is no reply owed. The low bits carry the source
/// (a GSI for IRQs). Posted by `notifyFromIsr`, from the ISR in system/kernel/irq.zig;
/// the message path uses a plain task-id badge with this bit clear. Defined in the
/// shared contract (system/system.zig), because ring 3 has to test the same bit.
pub const notify_badge_bit: u64 = system.notify_badge_bit;
/// shared kernel↔user ABI (system/abi.zig), because ring 3 has to test the same bit.
pub const notify_badge_bit: u64 = abi.notify_badge_bit;
/// End of the user (low) canonical half — user buffers must lie below it.
const user_half_end: u64 = 0x0000_8000_0000_0000;
@@ -124,8 +125,8 @@ fn copyAcross(source_as: u64, source_va: u64, destination_as: u64, destination_v
const s_left = page_size - ((source_va + off) & (page_size - 1));
const d_left = page_size - ((destination_va + off) & (page_size - 1));
const n = @min(@min(s_left, d_left), len - off);
const source: [*]const u8 = @ptrFromInt(system.physicalToVirtual(s));
const destination: [*]u8 = @ptrFromInt(system.physicalToVirtual(d));
const source: [*]const u8 = @ptrFromInt(boot_handoff.physicalToVirtual(s));
const destination: [*]u8 = @ptrFromInt(boot_handoff.physicalToVirtual(d));
@memcpy(destination[0..n], source[0..n]);
off += n;
}
@@ -147,7 +148,7 @@ pub fn copyFromUser(user_as: u64, user_va: u64, destination: []u8) bool {
const s = architecture.translate(user_as, user_va + off) orelse return false;
const s_left = page_size - ((user_va + off) & (page_size - 1));
const n = @min(s_left, destination.len - off);
const source: [*]const u8 = @ptrFromInt(system.physicalToVirtual(s));
const source: [*]const u8 = @ptrFromInt(boot_handoff.physicalToVirtual(s));
@memcpy(destination[off..][0..n], source[0..n]);
off += n;
}