boot: the /system tree is the system image — loader-built ramdisk, spawn by path
Retire the build-time ramdisk packer and the packed initial-ramdisk.img. make-fat-image.py now lays every user binary out at its FHS path on the boot volume (system/services, system/drivers, system/tests), and the EFI loader walks \system at boot, packing what it finds into an in-RAM v2 initial_ramdisk whose entry names are full FHS paths. init rides the table like every other binary: its dedicated handoff fields are gone and the kernel spawns PID 1 via the same lookup as everyone else (process.spawnBundled). system_spawn resolves names by exact path first, then unique basename, and normalizes argv[0] to the stored path — so task names (and, next, the tagged log ring's attribution) are honest binary paths everywhere. initrd v2 rejects the old magic so a stale image fails loudly. Groundwork for per-process logging (/var/log/<boot-stamp>/<binary-path>.log) and the kernel-VFS /system mount.
This commit is contained in:
@@ -56,8 +56,8 @@ const virtio_gpu_pci_class: u64 = pci_class.ClassCode.pack(.{
|
||||
/// its registered id as argv[1].
|
||||
fn pciDriverForIdentity(identity: u64) ?[]const u8 {
|
||||
return switch (identity) {
|
||||
xhci_pci_class => "usb-xhci-bus",
|
||||
virtio_gpu_pci_class => "virtio-gpu",
|
||||
xhci_pci_class => "/system/drivers/usb-xhci-bus",
|
||||
virtio_gpu_pci_class => "/system/drivers/virtio-gpu",
|
||||
else => null,
|
||||
};
|
||||
}
|
||||
@@ -67,8 +67,8 @@ fn pciDriverForIdentity(identity: u64) ?[]const u8 {
|
||||
/// nodes the kernel used to build). ps2-bus is a singleton that finds both its
|
||||
/// devices by hid once spawned, so keyboard and mouse map to the same name.
|
||||
fn hidDriverFor(hid: []const u8) ?[]const u8 {
|
||||
if (std.mem.eql(u8, hid, "PNP0303")) return "ps2-bus"; // PS/2 keyboard
|
||||
if (std.mem.eql(u8, hid, "PNP0F13")) return "ps2-bus"; // PS/2 mouse
|
||||
if (std.mem.eql(u8, hid, "PNP0303")) return "/system/drivers/ps2-bus"; // PS/2 keyboard
|
||||
if (std.mem.eql(u8, hid, "PNP0F13")) return "/system/drivers/ps2-bus"; // PS/2 mouse
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -95,9 +95,9 @@ fn usbDriverForIdentity(identity: u64) ?[]const u8 {
|
||||
@intFromEnum(usb_ids.mass_storage.Protocol.bulk_only),
|
||||
);
|
||||
return switch (identity) {
|
||||
keyboard => "usb-hid-keyboard",
|
||||
mouse => "usb-hid-mouse",
|
||||
storage => "usb-storage",
|
||||
keyboard => "/system/drivers/usb-hid-keyboard",
|
||||
mouse => "/system/drivers/usb-hid-mouse",
|
||||
storage => "/system/drivers/usb-storage",
|
||||
else => null,
|
||||
};
|
||||
}
|
||||
@@ -429,7 +429,7 @@ fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?runtime
|
||||
writeLine("/system/services/device-manager: hello from {s} (device {d})\n", .{ driver.name(), hello.device_id });
|
||||
// Resilience drill (V6): once, kill the virtio-gpu driver a moment after it hellos, so
|
||||
// the normal restart policy respawns it — the compositor must survive and re-attach.
|
||||
if (test_scanout_restart_mode and !test_scanout_killed and std.mem.eql(u8, driver.name(), "virtio-gpu")) {
|
||||
if (test_scanout_restart_mode and !test_scanout_killed and std.mem.eql(u8, driver.name(), "/system/drivers/virtio-gpu")) {
|
||||
test_scanout_killed = true;
|
||||
test_kill_pid = sender;
|
||||
test_kill_due_ns = system.clock() + 1_500_000_000;
|
||||
@@ -498,7 +498,7 @@ fn onChildAdded(message: []const u8, reply: []u8, sender: u32) usize {
|
||||
// Only the xHCI reporter is the drill's victim — pci-bus also reports
|
||||
// now, and whichever finishes second must not trigger the kill.
|
||||
if (driverByProcess(sender)) |driver| {
|
||||
if (std.mem.eql(u8, driver.name(), "usb-xhci-bus")) {
|
||||
if (std.mem.eql(u8, driver.name(), "/system/drivers/usb-xhci-bus")) {
|
||||
// Delayed, not immediate: the device-list scenario's subscriber
|
||||
// needs a window to enumerate and subscribe before the events.
|
||||
test_usb_killed = true;
|
||||
|
||||
@@ -27,11 +27,19 @@ const build_options = @import("build_options");
|
||||
/// the log-flush one-shot writes it once at boot.
|
||||
const log_path = "/mnt/usb/DANOS.LOG";
|
||||
|
||||
/// The system services init brings up at boot, in order. This is init's policy — the
|
||||
/// microkernel keeps such choices in user space, not the kernel. Drivers are absent
|
||||
/// on purpose: the device manager owns those. (A future init reads this from a
|
||||
/// manifest under /system/services instead of a hardcoded list.)
|
||||
const boot_services = [_][]const u8{ "vfs", "input", "device-manager", "fat", "display", "display-demo" };
|
||||
/// The system services init brings up at boot, in order, by binary path. This is
|
||||
/// init's policy — the microkernel keeps such choices in user space, not the
|
||||
/// kernel. Drivers are absent on purpose: the device manager owns those. (A
|
||||
/// future init reads this from a manifest under /system/services instead of a
|
||||
/// hardcoded list.)
|
||||
const boot_services = [_][]const u8{
|
||||
"/system/services/vfs",
|
||||
"/system/services/input",
|
||||
"/system/services/device-manager",
|
||||
"/system/services/fat",
|
||||
"/system/services/display",
|
||||
"/system/services/display-demo",
|
||||
};
|
||||
|
||||
/// The live process id of each boot service (0 = not running), indexed by its position
|
||||
/// in `boot_services`, plus how many times init has restarted it. init supervises these:
|
||||
@@ -84,7 +92,7 @@ pub fn main() void {
|
||||
// and forget: it polls for the mount itself, and is deliberately NOT one of
|
||||
// init's supervised children (a transient one-shot must not be stopped-and-
|
||||
// waited-for during shutdown).
|
||||
_ = runtime.system.spawn("log-flush");
|
||||
_ = runtime.system.spawn("/system/services/log-flush");
|
||||
|
||||
// Subscribe to power events (retry: the power service registers well after
|
||||
// init starts). Best-effort — without it, a `terminate` signal still
|
||||
|
||||
@@ -147,8 +147,8 @@ pub fn main(init: runtime.process.Init) void {
|
||||
const spinner = runtime.system.spawnSupervised("process-test", &.{"spinner"}, endpoint) orelse fail("spawn spinner");
|
||||
|
||||
runtime.system.sleep(100); // let the sleeper block and the spinner get a core
|
||||
if (!listed(sleeper, "process-test")) fail("sleeper not in process_enumerate");
|
||||
if (!listed(spinner, "process-test")) fail("spinner not in process_enumerate");
|
||||
if (!listed(sleeper, "/system/tests/process-test")) fail("sleeper not in process_enumerate");
|
||||
if (!listed(spinner, "/system/tests/process-test")) fail("spinner not in process_enumerate");
|
||||
|
||||
// Kills that must be refused: a kernel task (id 0), and an id that was never
|
||||
// issued — both -ESRCH. (-EPERM needs a second supervisor; the kernel-level
|
||||
@@ -167,8 +167,8 @@ pub fn main(init: runtime.process.Init) void {
|
||||
if (!runtime.system.kill(spinner)) fail("kill spinner");
|
||||
if (awaitChildExit(endpoint) != spinner) fail("spinner exit notification");
|
||||
|
||||
if (listed(sleeper, "process-test")) fail("sleeper still listed after kill");
|
||||
if (listed(spinner, "process-test")) fail("spinner still listed after kill");
|
||||
if (listed(sleeper, "/system/tests/process-test")) fail("sleeper still listed after kill");
|
||||
if (listed(spinner, "/system/tests/process-test")) fail("spinner still listed after kill");
|
||||
|
||||
// M17.2: both children were killed by us, and the reason says so — the whole
|
||||
// restart-policy input, read through the runtime like a real supervisor would.
|
||||
|
||||
Reference in New Issue
Block a user