An AMD Ryzen booted to a working compositor with no USB and no storage, and the log said only "register refused". A tree-wide audit of every compile-time ceiling followed: 235 of them, 139 on quantities the machine or a file decides rather than us, 5 documented anywhere, 171 silent when reached. docs/fixed-bounds-audit.md has the inventory. Errno attribution. The errno space was split between the kernel and the envelope, free to drift; it is now one list in system/abi.zig, restated on both sides, with a comptime check in library/device/driver where the two halves are visible. device_register's six refusals and device_claim's three are distinct codes, so a bus driver can say which rule stopped it, and BadParent splits into NoSuchParent and NotYourParent. pci-bus reconciles found against registered instead of counting refused functions as found. Idempotency ordering. The child cap was checked before the identity match, so a restarted bus was refused its own devices — the supervision restart the system leans on ratcheted toward a degraded machine. A re-registration consumes no slot and is now admitted first. IOMMU fail-closed. confineDevice returned success for a device id past the confinement table, leaving the device outside every domain while the caller believed it confined — unreachable only while ids stop at 64, which both the inventory move and a hardware-reported domain count would change. It refuses now, and the coupling to the broker's device cap is a comptime assert rather than a sentence in a comment. PCI apertures. The bridge's MMIO apertures are derived from the holes in the firmware memory map, and the derivation copied sub-4 GiB entries into a fixed [64] array and skipped the rest. A skipped region is not merely lost: the gap finder concludes it is free, so a real machine's 60-200 entry map yields an aperture over live RAM, and containment then admits a child BAR covering kernel memory. Rewritten to walk the map in place, with the hole finder extracted as a pure function and driven by a synthetic 100-entry map in a new test case. Both new tests were verified to fail on the old code. parameters.zig gains the rationale it was missing and loses a stale sentence pointing at the wrong file; vdso.md documents the errno space, including EPEER, which had no written meaning anywhere. docs/os-development/bounds.md is how a ceiling is declared from here. docs/bounds-track-plan.md is the plan to remove the ones we invented. Suite 114 -> 115.
53 lines
2.3 KiB
Zig
53 lines
2.3 KiB
Zig
//! crash-test — a test fixture, not a driver: claims the device it is assigned,
|
|
//! hellos the device manager, announces itself, then faults on purpose. The
|
|
//! driver-restart scenario drives the manager's whole restart machinery with
|
|
//! it: fault → exit reason → backoff → respawn → the **same claim succeeding
|
|
//! again** (claim release on death, M17.1, through the manager's path) → the
|
|
//! crash-loop cap. Spawned bare (the initial-ramdisk sweep starts every bundled
|
|
//! binary), it exits silently so it cannot derange other tests.
|
|
|
|
const std = @import("std");
|
|
const channel = @import("channel");
|
|
const ipc = @import("ipc");
|
|
const process = @import("process");
|
|
const time = @import("time");
|
|
const device = @import("driver");
|
|
const logging = @import("logging");
|
|
const device_manager_protocol = @import("device-manager-protocol");
|
|
|
|
pub fn main(init: process.Init) void {
|
|
const argument = init.arguments.get(1) orelse return; // bare: stay silent
|
|
const assigned = std.fmt.parseInt(u64, argument, 10) catch return;
|
|
|
|
// The respawn only reaches this line because the kernel released the
|
|
// previous instance's claim at death. A failed claim exits cleanly — the
|
|
// manager reads "meant to stop" and the scenario fails loudly by silence.
|
|
device.claim(assigned) catch {
|
|
_ = logging.write("crash-test: claim failed\n");
|
|
return;
|
|
};
|
|
|
|
var manager: ?ipc.Handle = null;
|
|
var tries: u32 = 0;
|
|
while (manager == null and tries < 100) : (tries += 1) {
|
|
manager = channel.openEndpoint("device-manager");
|
|
if (manager == null) time.sleepMillis(20);
|
|
}
|
|
const h = manager orelse return;
|
|
// The assigned device is the packet's target, the manager's object addressing.
|
|
var packet: [device_manager_protocol.message_maximum]u8 = undefined;
|
|
const framed = device_manager_protocol.Protocol.encodeRequest(
|
|
.hello,
|
|
assigned,
|
|
.{ .role = @intFromEnum(device_manager_protocol.Role.device) },
|
|
&.{},
|
|
&packet,
|
|
) orelse return;
|
|
var reply: [device_manager_protocol.message_maximum]u8 = undefined;
|
|
_ = ipc.call(h, framed, &reply) catch return;
|
|
|
|
_ = logging.write("crash-test: faulting now\n");
|
|
const poison: *volatile u32 = @ptrFromInt(0xdead0000);
|
|
poison.* = 1; // the restart machinery's fuel: a real segmentation fault
|
|
}
|