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:
+30
-28
@@ -10,7 +10,9 @@
|
||||
//! exception report the handler prints (which also reaches serial).
|
||||
|
||||
const std = @import("std");
|
||||
const system = @import("system");
|
||||
const boot_handoff = @import("boot-handoff");
|
||||
const abi = @import("abi");
|
||||
const device_abi = @import("device-abi");
|
||||
const architecture = @import("architecture");
|
||||
const devices_broker = @import("devices-broker.zig");
|
||||
const platform = @import("platform");
|
||||
@@ -155,7 +157,7 @@ fn powerTest(comptime action: enum { off, reboot }) void {
|
||||
result();
|
||||
}
|
||||
|
||||
const BootInformation = system.BootInformation;
|
||||
const BootInformation = boot_handoff.BootInformation;
|
||||
|
||||
fn eql(a: []const u8, b: []const u8) bool {
|
||||
return std.mem.eql(u8, a, b);
|
||||
@@ -167,7 +169,7 @@ fn smoke(boot_information: *const BootInformation) void {
|
||||
|
||||
// The memory map has some usable RAM.
|
||||
const mm = boot_information.memory_map;
|
||||
const regions = @as([*]const system.MemoryRegion, @ptrFromInt(system.physicalToVirtual(mm.regions)))[0..mm.len];
|
||||
const regions = @as([*]const boot_handoff.MemoryRegion, @ptrFromInt(boot_handoff.physicalToVirtual(mm.regions)))[0..mm.len];
|
||||
var usable: u64 = 0;
|
||||
for (regions) |r| {
|
||||
if (r.kind == .usable) usable += r.pages;
|
||||
@@ -179,7 +181,7 @@ fn smoke(boot_information: *const BootInformation) void {
|
||||
const b = pmm.alloc();
|
||||
check("alloc returns a frame", a != null);
|
||||
check("alloc returns distinct frames", a != null and b != null and a.? != b.?);
|
||||
check("frames are page-aligned", (a orelse 1) % system.page_size == 0);
|
||||
check("frames are page-aligned", (a orelse 1) % abi.page_size == 0);
|
||||
|
||||
// Freeing restores the count.
|
||||
const before = pmm.stats().free_frames;
|
||||
@@ -189,7 +191,7 @@ fn smoke(boot_information: *const BootInformation) void {
|
||||
|
||||
// Paging is active on our own tables (the root is non-zero and page-aligned).
|
||||
const root = architecture.activePageTable();
|
||||
check("paging active (page-table root set)", root != 0 and root % system.page_size == 0);
|
||||
check("paging active (page-table root set)", root != 0 and root % abi.page_size == 0);
|
||||
|
||||
result();
|
||||
}
|
||||
@@ -575,7 +577,7 @@ fn smpTest() void {
|
||||
const tramp = architecture.trampolinePage();
|
||||
check("trampoline frame reserved", tramp != 0);
|
||||
if (tramp != 0) {
|
||||
const bytes: [*]const u8 = @ptrFromInt(system.physicalToVirtual(tramp));
|
||||
const bytes: [*]const u8 = @ptrFromInt(boot_handoff.physicalToVirtual(tramp));
|
||||
var zeroed = true;
|
||||
for (0..4096) |b| {
|
||||
if (bytes[b] != 0) zeroed = false;
|
||||
@@ -759,7 +761,7 @@ fn userMemTest() void {
|
||||
var mapped: usize = 0;
|
||||
while (mapped < npages) : (mapped += 1) {
|
||||
frames[mapped] = pmm.alloc() orelse break;
|
||||
architecture.mapUserPageInto(aspace, arena + mapped * system.page_size, frames[mapped], true, false);
|
||||
architecture.mapUserPageInto(aspace, arena + mapped * abi.page_size, frames[mapped], true, false);
|
||||
}
|
||||
check("granted three user pages", mapped == npages);
|
||||
|
||||
@@ -767,13 +769,13 @@ fn userMemTest() void {
|
||||
var translate_ok = true;
|
||||
var rw_ok = true;
|
||||
for (0..npages) |i| {
|
||||
const va = arena + i * system.page_size;
|
||||
const va = arena + i * abi.page_size;
|
||||
const physical = architecture.translate(aspace, va) orelse {
|
||||
translate_ok = false;
|
||||
continue;
|
||||
};
|
||||
if (physical != frames[i]) translate_ok = false;
|
||||
const p: [*]u8 = @ptrFromInt(system.physicalToVirtual(physical));
|
||||
const p: [*]u8 = @ptrFromInt(boot_handoff.physicalToVirtual(physical));
|
||||
p[0] = 0xA5;
|
||||
if (p[0] != 0xA5) rw_ok = false;
|
||||
}
|
||||
@@ -782,7 +784,7 @@ fn userMemTest() void {
|
||||
|
||||
// Release them the way munmap does, then tear down the address space.
|
||||
for (0..npages) |i| {
|
||||
const va = arena + i * system.page_size;
|
||||
const va = arena + i * abi.page_size;
|
||||
if (architecture.translate(aspace, va)) |physical| {
|
||||
architecture.unmapUserPageInto(aspace, va);
|
||||
pmm.free(physical);
|
||||
@@ -879,7 +881,7 @@ fn processTest(boot_information: *const BootInformation) void {
|
||||
result();
|
||||
return;
|
||||
}
|
||||
const image = @as([*]const u8, @ptrFromInt(system.physicalToVirtual(boot_information.init_base)))[0..boot_information.init_len];
|
||||
const image = @as([*]const u8, @ptrFromInt(boot_handoff.physicalToVirtual(boot_information.init_base)))[0..boot_information.init_len];
|
||||
|
||||
process.write_count = 0;
|
||||
process.write_from_user = false;
|
||||
@@ -930,7 +932,7 @@ fn initTest(boot_information: *const BootInformation) void {
|
||||
result();
|
||||
return;
|
||||
}
|
||||
const image = @as([*]const u8, @ptrFromInt(system.physicalToVirtual(boot_information.init_base)))[0..boot_information.init_len];
|
||||
const image = @as([*]const u8, @ptrFromInt(boot_handoff.physicalToVirtual(boot_information.init_base)))[0..boot_information.init_len];
|
||||
process.write_count = 0;
|
||||
const spawned = if (process.spawnProcess(image, 4)) true else |err| blk: {
|
||||
log("DANOS-INIT-ERR: {s}\n", .{@errorName(err)});
|
||||
@@ -964,7 +966,7 @@ fn initialRamdiskTest(boot_information: *const BootInformation) void {
|
||||
result();
|
||||
return;
|
||||
}
|
||||
const image = @as([*]const u8, @ptrFromInt(system.physicalToVirtual(boot_information.initial_ramdisk_base)))[0..boot_information.initial_ramdisk_len];
|
||||
const image = @as([*]const u8, @ptrFromInt(boot_handoff.physicalToVirtual(boot_information.initial_ramdisk_base)))[0..boot_information.initial_ramdisk_len];
|
||||
const rd = initial_ramdisk.Reader.init(image) orelse {
|
||||
check("initial_ramdisk image is valid", false);
|
||||
result();
|
||||
@@ -1008,7 +1010,7 @@ fn vfsTest(boot_information: *const BootInformation) void {
|
||||
result();
|
||||
return;
|
||||
}
|
||||
const image = @as([*]const u8, @ptrFromInt(system.physicalToVirtual(boot_information.initial_ramdisk_base)))[0..boot_information.initial_ramdisk_len];
|
||||
const image = @as([*]const u8, @ptrFromInt(boot_handoff.physicalToVirtual(boot_information.initial_ramdisk_base)))[0..boot_information.initial_ramdisk_len];
|
||||
const rd = initial_ramdisk.Reader.init(image) orelse {
|
||||
check("initial_ramdisk image is valid", false);
|
||||
result();
|
||||
@@ -1071,7 +1073,7 @@ fn hpetTest(boot_information: *const BootInformation) void {
|
||||
result();
|
||||
return;
|
||||
}
|
||||
const image = @as([*]const u8, @ptrFromInt(system.physicalToVirtual(boot_information.initial_ramdisk_base)))[0..boot_information.initial_ramdisk_len];
|
||||
const image = @as([*]const u8, @ptrFromInt(boot_handoff.physicalToVirtual(boot_information.initial_ramdisk_base)))[0..boot_information.initial_ramdisk_len];
|
||||
const rd = initial_ramdisk.Reader.init(image) orelse {
|
||||
check("initial_ramdisk image is valid", false);
|
||||
result();
|
||||
@@ -1115,14 +1117,14 @@ fn hpetRouteOk() bool {
|
||||
|
||||
/// The GSI discovery recorded for the HPET, from the same device table the driver saw.
|
||||
fn hpetGsi() ?u32 {
|
||||
var buffer: [16]system.DeviceDescriptor = undefined;
|
||||
var buffer: [16]device_abi.DeviceDescriptor = undefined;
|
||||
const n = @min(devices_broker.enumerate(&buffer), buffer.len);
|
||||
for (buffer[0..n]) |d| {
|
||||
if (d.class != @intFromEnum(system.DeviceClass.timer)) continue;
|
||||
if (d.parent != system.no_parent) continue; // the block, not a comparator child
|
||||
if (d.class != @intFromEnum(device_abi.DeviceClass.timer)) continue;
|
||||
if (d.parent != device_abi.no_parent) continue; // the block, not a comparator child
|
||||
for (0..d.resource_count) |j| {
|
||||
const r = d.resources[j];
|
||||
if (r.kind == @intFromEnum(system.ResourceKind.irq)) return @intCast(r.start);
|
||||
if (r.kind == @intFromEnum(device_abi.ResourceKind.irq)) return @intCast(r.start);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -1148,7 +1150,7 @@ fn busTest(boot_information: *const BootInformation) void {
|
||||
result();
|
||||
return;
|
||||
}
|
||||
const image = @as([*]const u8, @ptrFromInt(system.physicalToVirtual(boot_information.initial_ramdisk_base)))[0..boot_information.initial_ramdisk_len];
|
||||
const image = @as([*]const u8, @ptrFromInt(boot_handoff.physicalToVirtual(boot_information.initial_ramdisk_base)))[0..boot_information.initial_ramdisk_len];
|
||||
const rd = initial_ramdisk.Reader.init(image) orelse {
|
||||
check("initial_ramdisk image is valid", false);
|
||||
result();
|
||||
@@ -1187,7 +1189,7 @@ fn deviceManagerTest(boot_information: *const BootInformation) void {
|
||||
result();
|
||||
return;
|
||||
}
|
||||
const image = @as([*]const u8, @ptrFromInt(system.physicalToVirtual(boot_information.initial_ramdisk_base)))[0..boot_information.initial_ramdisk_len];
|
||||
const image = @as([*]const u8, @ptrFromInt(boot_handoff.physicalToVirtual(boot_information.initial_ramdisk_base)))[0..boot_information.initial_ramdisk_len];
|
||||
const rd = initial_ramdisk.Reader.init(image) orelse {
|
||||
check("initial_ramdisk image is valid", false);
|
||||
result();
|
||||
@@ -1221,7 +1223,7 @@ fn deviceManagerTest(boot_information: *const BootInformation) void {
|
||||
/// trusted and doesn't obey containment: a PCI function's BAR is not inside its host
|
||||
/// bridge's `bus_range`, because a bus-number range isn't an address window.
|
||||
fn childrenContained() bool {
|
||||
var buffer: [64]system.DeviceDescriptor = undefined;
|
||||
var buffer: [64]device_abi.DeviceDescriptor = undefined;
|
||||
const n = @min(devices_broker.enumerate(&buffer), buffer.len);
|
||||
|
||||
const bus_id = hpetDeviceId() orelse return false;
|
||||
@@ -1237,7 +1239,7 @@ fn childrenContained() bool {
|
||||
for (0..p.resource_count) |j| {
|
||||
const pr = p.resources[j];
|
||||
if (pr.kind != r.kind) continue;
|
||||
if (r.kind == @intFromEnum(system.ResourceKind.irq)) {
|
||||
if (r.kind == @intFromEnum(device_abi.ResourceKind.irq)) {
|
||||
if (pr.start == r.start) ok = true;
|
||||
} else if (r.len != 0 and r.start >= pr.start and
|
||||
r.start + r.len <= pr.start + pr.len) ok = true;
|
||||
@@ -1250,13 +1252,13 @@ fn childrenContained() bool {
|
||||
|
||||
/// Device id of the HPET (the bus bus claims), from the same table drivers see.
|
||||
fn hpetDeviceId() ?u64 {
|
||||
var buffer: [64]system.DeviceDescriptor = undefined;
|
||||
var buffer: [64]device_abi.DeviceDescriptor = undefined;
|
||||
const n = @min(devices_broker.enumerate(&buffer), buffer.len);
|
||||
for (buffer[0..n]) |d| {
|
||||
if (d.class != @intFromEnum(system.DeviceClass.timer)) continue;
|
||||
if (d.parent != system.no_parent) continue; // a comparator child, not the block
|
||||
if (d.class != @intFromEnum(device_abi.DeviceClass.timer)) continue;
|
||||
if (d.parent != device_abi.no_parent) continue; // a comparator child, not the block
|
||||
for (0..d.resource_count) |j| {
|
||||
if (d.resources[j].kind == @intFromEnum(system.ResourceKind.memory)) return d.id;
|
||||
if (d.resources[j].kind == @intFromEnum(device_abi.ResourceKind.memory)) return d.id;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -1350,7 +1352,7 @@ fn ioPassTest() void {
|
||||
return;
|
||||
};
|
||||
// Map it the way mmio_map does (device grant), then tear the space down.
|
||||
architecture.mapUserDeviceInto(aspace, process.device_arena_base, frame, system.page_size);
|
||||
architecture.mapUserDeviceInto(aspace, process.device_arena_base, frame, abi.page_size);
|
||||
architecture.destroyAddressSpace(aspace);
|
||||
|
||||
// The page tables were reclaimed; the device-granted frame must not have been.
|
||||
|
||||
Reference in New Issue
Block a user