Orderly shutdown: init's stop cascade into ring-3 S5 (M21.3)
The capstone. init becomes a real supervisor: it spawns its boot services supervised against one endpoint that also carries its signals, a re-arming heartbeat timer, and the power events it subscribes to. On the power button (or a terminate signal — same path) it logs the shutdown, runs the M17 stop sequence over its children in reverse spawn order (vfs last), then asks the power service for S5. The acpi service honors a shutdown request from a power subscriber — init is the one subscriber, a soft gate that stands in for 'only the system supervisor may power off' and, unlike a PID-1 check, survives the test harness where the kernel's idle tasks take the early ids. The power service is mechanism (write S5); deciding when to shut down and stopping everything else first is init's policy — the microkernel split applied to poweroff. The orderly-shutdown scenario injects a real QMP power-button event and watches the whole chain compose: button pressed -> init shutting down -> entering S5 -> QEMU powers off. That single scenario proves the M17 lifecycle and the M21 event side compose into a clean shutdown. Suite 60/60.
This commit is contained in:
@@ -59,9 +59,14 @@ const sci_en_bit: u32 = 1 << 0;
|
||||
const slp_en: u32 = 1 << 13;
|
||||
|
||||
// The `.power` subscribers: endpoints handed over as capabilities, each
|
||||
// receiving events as buffered messages. Dropped on a failed send.
|
||||
// receiving events as buffered messages. Dropped on a failed send. The
|
||||
// subscriber's task id is kept too — a shutdown request is honored only from a
|
||||
// subscriber (init subscribes; a stray process does not), the soft gate that
|
||||
// stands in for "only the system supervisor may power off" without hardcoding
|
||||
// a pid the kernel's idle tasks would have taken.
|
||||
const maximum_subscribers = 8;
|
||||
var subscribers: [maximum_subscribers]?runtime.ipc.Handle = .{null} ** maximum_subscribers;
|
||||
var subscriber_tasks: [maximum_subscribers]u32 = .{0} ** maximum_subscribers;
|
||||
|
||||
// Pass-1 registration record (see main): what pass 2 reports.
|
||||
const Registered = struct { hid: [8]u8 = .{0} ** 8, hid_len: usize = 0, device_id: u64 = 0, resource_count: u64 = 0 };
|
||||
@@ -390,6 +395,13 @@ fn publishEvent(bytes: []const u8) void {
|
||||
}
|
||||
}
|
||||
|
||||
fn isSubscriber(task: u32) bool {
|
||||
for (&subscribers, 0..) |*slot, si| {
|
||||
if (slot.* != null and subscriber_tasks[si] == task) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Enter S5 (soft off): write SLP_TYP|SLP_EN to the PM1 control register(s).
|
||||
/// Mirrors the kernel's power.zig sleepValue. Only reached from a PID-1
|
||||
/// shutdown request (M21.3).
|
||||
@@ -423,9 +435,10 @@ fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?runtime
|
||||
@intFromEnum(power.Operation.subscribe) => {
|
||||
var status: i32 = -1;
|
||||
if (capability) |handle| {
|
||||
for (&subscribers) |*slot| {
|
||||
for (&subscribers, 0..) |*slot, si| {
|
||||
if (slot.* == null) {
|
||||
slot.* = handle;
|
||||
subscriber_tasks[si] = sender;
|
||||
status = 0;
|
||||
break;
|
||||
}
|
||||
@@ -436,11 +449,14 @@ fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?runtime
|
||||
return @sizeOf(power.Reply);
|
||||
},
|
||||
@intFromEnum(power.Operation.shutdown) => {
|
||||
// Only PID 1 (init), which has already stopped everything else.
|
||||
const status: i32 = if (sender == 1) 0 else -1;
|
||||
const r = power.Reply{ .status = status };
|
||||
// Honored only from a power subscriber — init, which has already run
|
||||
// the stop sequence over everything else. The power service is
|
||||
// mechanism (write S5); deciding *when* to shut down and stopping
|
||||
// the rest of the system first is init's policy.
|
||||
const allowed = isSubscriber(sender);
|
||||
const r = power.Reply{ .status = if (allowed) 0 else -1 };
|
||||
@memcpy(reply[0..@sizeOf(power.Reply)], std.mem.asBytes(&r));
|
||||
if (sender == 1) enterS5();
|
||||
if (allowed) enterS5();
|
||||
return @sizeOf(power.Reply);
|
||||
},
|
||||
else => return 0,
|
||||
|
||||
Reference in New Issue
Block a user