Add runtime.time, drop demo drivers, harden TSC timekeeping

Time is a kernel concern in danos: the kernel owns the scheduling timer and
already exposes monotonic time via the clock/sleep/timer_bind syscalls, so a
userspace time service would be a redundant, slower path. This adds the generic
runtime.time module over those syscalls, retires the two demonstration drivers,
reorganizes the milestone docs, and makes the monotonic clock correct on Intel,
AMD, and inside any VM.

runtime.time (library/runtime/time.zig)
- Instant/Duration interface: now, sleep, spin, after, monotonicNanos, available
- a thin layer over system.clock/sleep/timerOnce; unit-tested arithmetic

Remove the demo drivers hpet and bus (a teaching example belongs in the docs,
not shipped in the tree)
- system/drivers/ now holds only real drivers: pci-bus, ps2-bus, usb-xhci-bus
- device-manager end-to-end test repointed to pci-bus (asserts on kernel state:
  the process table and the device tree, not a racy serial marker)
- device_register containment moved to a new in-kernel `containment` test
- the driver-model worked example moved inline into docs/drivers.md

Reorganize milestone docs into topic docs
- m17-m18 / m19-m20 / m21 plans dissolved into process-lifecycle, device-manager,
  discovery, and acpi docs; new docs/power.md and docs/timers.md; ~20 citations
  repointed; plan docs deleted

TSC reliability (apic.zig, smp.zig, cpu.zig, kernel.zig)
- check the invariant-TSC bit (CPUID 0x80000007 EDX[8]) on Intel and AMD
- cross-core "warp" check at SMP bring-up, pairwise BSP<->AP as each core comes up
- fall back to the HPET clocksource when the TSC is not invariant (a bare VM) or
  not synchronized (a warp), switched continuously so time never jumps
- boot log reports the outcome; new tsc-sync test exercises the TSC + warp path

Verified: zig build; zig build test; 60/60 QEMU cases (incl. new containment and
tsc-sync).
This commit is contained in:
Daniel Samson
2026-07-13 11:56:43 +01:00
parent 5b63a841ba
commit 452080e997
36 changed files with 1150 additions and 1267 deletions
+54
View File
@@ -191,3 +191,57 @@ and registers + reports each `_HID` device — the device manager matches driver
(ps2-bus) from those reports. With M19's pci-bus driver, discovery now runs
entirely in user space; the kernel seeds only the host bridge and the
acpi-tables node.
## Discovery is a swappable process per firmware (M19–M20)
Moving PCI and ACPI enumeration out of ring 0 was not just a relocation — it
made discovery **firmware-neutral by construction**, which is the whole reason
to do it before the second architecture rather than after. Everything at and
above the [device-manager](device-manager.md) protocol — descriptors,
containment, reports, matching, supervision — is generic and may never become
x86-specific. Discovery is the single firmware-specific piece, and it is
isolated as **one swappable process per firmware**:
- **x86** boots describe hardware with ACPI, so the discoverer is the **acpi
service** ([acpi.md](acpi.md)): it claims the `acpi-tables` node and runs AML.
- **The Raspberry Pis** hand over a flattened device tree, so the discoverer is
an **fdt service**: it claims a `devicetree-blob` node and walks the tree —
pure data, no bytecode, so it needs neither a port grant nor an interpreter,
strictly simpler than ACPI. (A placeholder until the [aarch64](arm.md)
bring-up fills it in.)
The device manager spawns the discoverer under the **neutral ramdisk name
`discovery`** and never learns which firmware it is on; the build's
`-Ddiscovery=acpi|fdt` option fills that slot (x86 defaults to `acpi`, the
aarch64 target flips the default when it lands). The manager owns the device
tree as *data* and touches no hardware, ever — firmware bytecode runs only
inside the crashable, supervised discoverer, so an AML fault can never take
down the supervisor.
Two consequences of neutrality bind on later work:
- **Cross-firmware surfaces are named by domain, not firmware.** System power is
a [`power`](power.md) protocol, not an "ACPI events" protocol: on x86 the acpi
service registers it, on ARM a PSCI/mailbox service registers the same
`ServiceId.power`, and subscribers never learn the difference.
- **Identity must widen before the fdt service exists.** `DeviceDescriptor`'s
8-byte `hid` holds an EISA id but cannot hold an FDT `compatible` string
(`"brcm,bcm2835-aux-uart"`); the identity field grows before the ARM path can
report a real node.
Two supporting decisions keep the kernel's remaining slice honest:
- **The AML interpreter is a shared build module**, compiled into both the
kernel and the acpi service — one source, two builds, no fork. The kernel
links it for the `\_S5` poweroff evaluation, the service links it for
everything else, and the `acpi-parse` test asserts the two produce the same
device count across the ring-3 move.
- **Bridge apertures come from the firmware memory map, not AML.** Registered
PCI functions carry BAR resources, and `device_register` containment demands
the bridge own windows that cover them. Those apertures are derived
kernel-side from the boot memory map's MMIO holes (regions that are neither
RAM nor tables) — mechanical, AML-free, and available at boot regardless of
what later moved to user space. The acpi service's authority is likewise
exactly one node: the `acpi-tables` node, whose broad io_port grant is the
documented trust boundary for the one process allowed to run firmware
bytecode.