Commit Graph

39 Commits

Author SHA1 Message Date
Daniel Samson 4231301896
display: runtime mode-setting, EDID, and fenced (vsync) present (v2 V5)
The native backend can now change resolution and presents tear-free.

Mode-setting without churn. The driver sizes its scanout resource + shared surface to the
largest mode it offers and treats a mode change as re-pointing the scanout rectangle within
that surface — so the resource, its backing, and the shared mapping never change, and the
surface's row stride (the max width) is fixed while the active width/height move. The
compositor is handed that stride in the announce and composes at it; a smaller mode just
paints the top-left rectangle. This sidesteps the surface re-share a true resolution change
would otherwise need (the service harness can't reply with a capability).

- scanout-protocol gains get_modes + set_mode; the driver offers {640x480, 800x600} and
  re-points set_scanout on set_mode.
- backend.VirtioGpu carries the surface stride, exposes modes()/setMode(), and reports
  canModeSet = hasVsync = true.
- runtime.display gains modes()/setMode() (display-protocol get_modes/set_mode, forwarded to
  the backend) — the client-facing API.
- EDID: the driver negotiates VIRTIO_GPU_F_EDID when the device offers it, reads the monitor's
  EDID, and logs its preferred mode (parsed from the first detailed timing descriptor).
- vsync: every resource_flush is fenced (VIRTIO_GPU_FLAG_FENCE); the device signals the fence
  when the frame is on screen, which the used-ring ack the synchronous present already waits
  on gates — so a completed present is a tear-free one.

After the native upgrade the compositor runs a one-shot mode-set self-check: query the modes,
switch to a different one, re-composite, and confirm the backend reports the new geometry —
the gate's markers.

Gate: python3 test/qemu_test.py display-modeset (reuses the display-native boot) — "display:
mode set to 800x600, verified" + "display: vsync present ok", passing 3/3. host tests,
display-service, display-demo, shm, virtio-gpu, and display-native still pass.
2026-07-14 12:44:21 +01:00
Daniel Samson 58927ed7e5
display: hot-attach a virtio-gpu native backend over the GOP floor (v2 V4)
The compositor now boots on the GOP framebuffer and upgrades to the virtio-gpu driver
the moment it announces itself — the pluggable-scanout payoff.

The shared surface. The scanout resource is an shm region the driver creates
(shm_physical, a new syscall, hands it the guest-physical for attach_backing) and passes
to the compositor as a capability. The compositor maps it and composites straight into
it: on x86 DMA is cache-coherent, so the cacheable shared pages the CPU paints are exactly
what the device transfers-and-flushes — no copy, no explicit flush.

The handshake. After bring-up the driver looks up .display and sends attach_scanout with
the geometry + the surface capability. The compositor maps the surface, looks up the
driver's .scanout endpoint itself (the driver registered it — no need to pass it), switches
to backend.VirtioGpu, and re-composites the current frame. present() over the native
backend is a present request on .scanout -> transfer-to-host + resource flush. The first
native present is deferred to a one-shot timer: presenting inline from the announce handler
would deadlock, since the driver is still blocked on our reply and not yet serving .scanout.
After it lands, the compositor reads a pixel back from the shared surface to confirm the
frame reached the device's backing.

- shm_physical (syscall 36) + runtime.shm.physical.
- scanout-protocol (the compositor->driver present channel), separate from the
  client-facing display protocol; the display protocol gains attach_scanout.
- backend.VirtioGpu joins backend.Gop in the tagged union; select() still boots GOP.
- the virtio-gpu driver's scanout backing is now shm (was DMA); it announces + serves
  .scanout present requests (transfer-to-host + flush of the shared surface).

Also fixes a latent framebuffer-geometry corruption the display service hit only when it
enumerated the device tree alongside a busy device-manager: Gop.init now captures the
geometry into a small value the instant device_enumerate returns (rather than re-reading
the 328-byte descriptor across the later claim/mmio_map syscalls) and retries on a zero
geometry. The underlying device-table clobber is a separate kernel bug, tracked apart.

Gate: python3 test/qemu_test.py display-native (QEMU -device virtio-gpu-pci) — "display:
scanout upgraded to virtio-gpu" + "display: native present verified" + "display-demo: ok",
passing 3/3. host tests, display-service, display-demo, shm, and virtio-gpu still pass.
2026-07-14 12:21:37 +01:00
Daniel Samson 6e0e0a62c6
virtio-gpu: a modern virtio 1.0 display driver, bring-up to a flushed frame (v2 V3)
The first native scanout backend's driver half. The device manager matches the
virtio-gpu PCI function by its Display/Other class triple and spawns the driver,
which claims the device, confirms vendor 0x1AF4/device 0x1050 from config space,
enables memory-space + bus mastering, and walks the virtio vendor capabilities to
find the common-config and notify structures in its BAR.

From there it is the standard modern-virtio bring-up: reset, negotiate VERSION_1,
stand up the control virtqueue in coherent DMA, then drive the GPU end to end —
RESOURCE_CREATE_2D, ATTACH_BACKING (a coherent DMA buffer; V4 swaps in the shm-
shared surface), SET_SCANOUT, paint a known pattern, TRANSFER_TO_HOST_2D,
RESOURCE_FLUSH, and wait for the device's used-ring ack. Reading the backing back
proves it is CPU-visible RAM; the ack proves the device consumed the frame —
together the automated stand-in for "it's on screen", no screenshot.

- system/drivers/virtio-gpu/: the driver, plus virtio-gpu-protocol.zig (control
  commands) and virtio-pci.zig (the 1.0 PCI transport + split-virtqueue), both with
  host-tested struct sizes.
- device-manager matches the display/other class triple to "virtio-gpu"; the driver
  self-confirms the vendor/device id, since the class alone cannot distinguish it.
- ServiceId.scanout (11): the driver registers it so the compositor finds it in V4.

Gate: python3 test/qemu_test.py virtio-gpu (QEMU -device virtio-gpu-pci) — the
device-manager stack discovers the function, the driver brings up a 640x480 scanout
and flushes a test pattern: "virtio-gpu: scanout 640x480 online" + "flush acked,
pixel check ok". host tests, display-service, shm, and device-list still pass.

Note: the pre-existing pci-scan case triple-faults on main (verified at 88ad432,
before this change); it is unrelated and tracked separately.
2026-07-14 11:29:53 +01:00
Daniel Samson f157a93c9c
fat/usb-storage: flush the device cache on close so writes survive power-off
init writes /mnt/usb/DANOS.LOG at shutdown and then enters S5 — but the write sat in the USB flash controller's write cache and was lost when power was cut, because nothing issued SCSI SYNCHRONIZE CACHE. Invisible in QEMU (its backing file commits immediately); real on hardware. The boot-time flush survived only because the machine kept running afterward and the cache drained on its own.

- block protocol gains a flush op; usb-storage serves it with SYNCHRONIZE CACHE (10); runtime.block gains Device.flush()
- the FAT server tracks whether blocks were written and, on a file close, commits the device cache (durable-on-close — the right default for removable media, and it makes init's existing shutdown close() persist the log before S5, no init/VFS change needed)
- verified the SYNCHRONIZE CACHE actually reaches the driver on each dirty close; usb-storage/fat-mount/mutations/rename/mtime/log-flush all green
2026-07-13 23:29:15 +01:00
Daniel Samson 3fb9d5936a
USB driver stack: xHCI transfers, HID keyboard/mouse, mass storage
Flesh out the xHCI host-controller driver into a full transfer engine and build
the three USB class drivers on top, all verified end to end under QEMU.

- xHCI engine (usb-xhci-library.zig): controller reset, command/event rings with
  cycle-bit bookkeeping (gated on a No-Op-command proof), device slots, Address
  Device, control transfers, full chapter-9 enumeration, Configure Endpoint, and
  interrupt/bulk transfers. Each interface is device_registered with its
  (class,subclass,protocol) identity, unique per (port,interface).
- Bus<->class transfer protocol (usb-transfer-protocol.zig + runtime.usb): open /
  control / interrupt-subscribe (async report pump on a poll timer) / bulk-by-
  physical-address, so sector data never crosses the 256-byte IPC limit.
- USB HID keyboard + mouse (usb-hid/): decode boot-protocol reports and publish
  to the input service. A USB usage is already the input protocol's keycode.
- USB mass storage (usb-storage/): Bulk-Only Transport + transparent SCSI,
  serving a block device under the new .block service id (block-protocol).
- device-manager matches USB interfaces to class drivers (usbDriverForIdentity).
- usb-abi / usb-ids made importable modules; add HID and mass-storage class
  requests, packTriple, and a usb_device DeviceClass.
- Fix test/qemu_test.py on macOS: the QMP unix-socket path was built from the
  deep worktree path and exceeded the 104-byte sun_path limit, so QEMU exited
  before booting. It now lives under a short temp path.

Tests: usb-report, usb-hid, usb-storage pass under python3 test/qemu_test.py;
host units (usb-abi, usb-ids, hid-report, bulk-only-transport, scsi) green.
2026-07-13 14:11:00 +01:00
Daniel Samson 452080e997
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).
2026-07-13 11:56:43 +01:00
Daniel Samson 6e60daed6a
Fix the drviers typo and make tests robust to source-path debug prefixes
The debug-message refactor prefixed each service/driver line with its
source path (system/drivers/hpet:, ...) for readability, but two things
left main red: a 'drviers' typo in hpet.zig and pci-bus.zig, and five
kernel tests (init, hpet, pci-scan, device-manager, vfs-client-death)
that starts-with-matched the old short markers, which no longer sit at
the front of the prefixed lines.

Fix the typo, and convert the fragile starts-with matchers to substring
matching via a bufferHas helper — 'hpet: ok' now matches inside
'system/drivers/hpet: ok' regardless of prefix. Future-proof against
further prefix changes and harmless for the tests that already passed.
Suite 58/58.
2026-07-13 06:23:21 +01:00
Daniel Samson dd044fb115
fix / debug 2026-07-13 05:41:57 +01:00
Daniel Samson 3ec14509a0
fix / debug 2026-07-13 05:41:05 +01:00
Daniel Samson 77a3ccd33d
fix hpet: debug prefix 2026-07-13 05:22:27 +01:00
Daniel Samson 07da27dc39
fix pci-bus: debug prefix 2026-07-13 05:21:47 +01:00
Daniel Samson 8589bf713b
fix usb-xhci-bus debug prefix 2026-07-13 05:18:49 +01:00
Daniel Samson d5d15cefcb
Decode PCI/ACPI device identities and name their class codes as enums
Two related changes to make device identities legible in the boot log and in
the code that matches on them.

Logging: the pci-bus driver decodes each function's class/subclass/prog-IF
triple to human names (via the existing pci-class module), and the acpi
service appends each _HID's human name (via acpi-ids) to its report line. So
"class 0x01 (Mass Storage Controller) subclass 0x06 (Serial ATA Controller)
progif 0x01 (AHCI 1.0)" reads straight off the log when writing a driver.

Naming: a new coding standard ("Named values, not magic numbers") says a value
with meaning gets a name, prefer an enum for value sets. Applied:
- pci-class is refactored from u8-switch tables into a BaseClass enum plus
  per-class SubClass/ProgIf enums with name() methods (the usb-ids shape). The
  public className/subclassName/progIfName(u8...) API is unchanged, so the
  hardware-byte decoders (pci-bus, the kernel dump) are untouched; output is
  byte-identical.
- the device-manager builds the xHCI class triple from named parts instead of
  a bare 0x0C0330.
- the acpi service's _CRS walk names its resource-descriptor tags as
  SmallResourceType/LargeResourceType enums, and the _HID integer decode uses
  the AML module's existing *_opcode constants (now re-exported from aml.zig)
  rather than bare 0x0A/0xFF/... literals.
2026-07-13 05:05:25 +01:00
Daniel Samson fd96a35eb9
Decode the xHCI port speed in the usb-xhci-bus log
The root-hub scan logged the raw PORTSC port-speed class ("speed class 3").
Decode it to a human name — Low/Full/High/SuperSpeed/SuperSpeedPlus with the
USB generation and line rate — so the boot log says what enumerated on each
port, the USB analog of the pci-bus class line. This is the link speed only;
the device class/subclass/protocol needs descriptor reads (the USB track).
2026-07-13 05:05:13 +01:00
Daniel Samson af2c766f42
The flip: PCI enumeration leaves the kernel (M19.3)
enumeratePci, addBars, pciConfigurationPtr, and the PciHeader struct are
deleted; the kernel seeds only the host bridge, and the ring-3 pci-bus
driver's reports are the sole source of PCI function nodes. The manager
matches PCI drivers from reported identity, deduped by registered device
id so a bus restart never double-spawns.

The flip did its job by exposing a latent SMP race: ring-3
device_register made the broker table concurrent for the first time, and
mmio_map read it lock-free — under load a torn resource length mapped
hpet's window wrong (its user fault) and underflowed r.len-1 into a
kernel integer-overflow panic. Fixed: the broker read in mmio_map (and
claim) runs under the big kernel lock, the arithmetic rejects
zero-length and wrapping windows cleanly, and pci-bus no longer registers
unimplemented size-0 BARs. driver-restart hammered 6x, suite 55/55.
2026-07-13 02:54:50 +01:00
Daniel Samson d26262bf56
pci-bus registers and reports what it scans (M19.2)
Each function is registered under the bridge with the config-space slice
and BARs sized by the same all-ones probe the kernel uses — byte-for-byte
equal descriptors, so the idempotent register returns the kernel's
existing node ids during coexistence instead of duplicating the tree.
The bridge gained the 16-bit io_port aperture that functions' I/O BARs
need to pass containment. Reports carry the registered device_id, and
the pci-scan scenario drills a forced restart: kill the enumerator after
its reports, watch the respawn re-scan, and assert the broker's PCI node
count never grew. The usb-restart test trigger is pinned to the xHCI
reporter (pci-bus racing it to two reports used to steal the kill).
Harness hardening: failing cases preserve their serial logs; the heavy
scenarios run at 150s.
2026-07-13 02:22:19 +01:00
Daniel Samson 10b89c06ff
The PCI scan from ring 3: pci-bus walks the ECAM it mapped (M19.1)
The manager matches the pci_host_bridge node and spawns pci-bus with the
bridge id as its assignment — hello, supervision, restart, all the M18
contract for free. The driver claims the bridge, maps the ECAM window
(resource 0) through the ordinary mmio grant, and repeats the kernel's
brute-force bus/device/function walk from user space. The pci-scan
scenario builds its expected marker from the kernel's own function count,
so the two enumerations must agree exactly — the equivalence that
licenses retiring the kernel walk in M19.3.
2026-07-13 02:05:32 +01:00
Daniel Samson d8778b4b70
The application surface: enumerate, subscribe, and device-list (M18.3)
Applications ask the device manager for the tree (enumerate: a header
plus ChildEntry records) and subscribe to published add/remove events by
handing their endpoint over as the call's capability — the input-service
pattern; events are the same ChildAdded/ChildRemoved structs the bus
drivers send, one encoding in both directions. device-list is the first
client: it prints the tree, subscribes, and narrates the events through
a driver restart. The protocol's message maximum is capped at the
kernel's IPC MESSAGE_MAXIMUM (256 bytes, ten entries per reply; paging
joins the protocol when a tree outgrows one message). The startUserTask
debug print is gone: it wrote to serial unserialized against user-space
lines and sheared concurrent log markers in half — the root cause of the
scenario flakes.
2026-07-13 00:49:03 +01:00
Daniel Samson 79d859a111
The xHCI driver scans its root-hub ports and reports the tree (M18.2)
child_added/child_removed join the device-manager protocol. The driver
maps its register BAR (resource 0 is the ECAM config space; the walk
starts at 1), reads CAPLENGTH and HCSPARAMS1, and reads one PORTSC per
port: the connect bit and speed class come straight from hardware, no
rings needed to see the devices. The manager mirrors reported children
keyed by (parent, port), remembers which instance reported each, and
prunes a dead reporter's children before deciding the restart — the
children describe protocol state that died with the process. The
usb-report scenario drives the whole loop: two QEMU devices reported,
reporter killed, children pruned, driver respawned with backoff, and the
new instance re-claims, re-scans, and re-reports.
2026-07-13 00:28:29 +01:00
Daniel Samson 3cc1d38dd0
The device manager supervises: hello, backoff, and the crash-loop cap (M18.1)
The manager is now a harness service on the well-known .device_manager
endpoint. Every driver spawns supervised; drivers with an assignment must
hello (device-manager-protocol, versioned) within a deadline enforced by
a timer sweep. Exit reasons drive the restart decision: clean exits stay
down, faults restart with 300/600/1200ms backoff, and three fast deaths
mark a driver failed instead of respawning forever. usb-xhci-bus is the
first conforming driver; the crash-test fixture claims a device, hellos,
and faults on purpose — each respawn re-proving claim release on death
through the manager's own path. maximum_tasks grows 16 -> 32: the
initial-ramdisk sweep (15 binaries at once) was intermittently
overflowing the static pool.
2026-07-13 00:19:30 +01:00
Daniel Samson 140229b88d
Rename usb-xhci-libary.zig to usb-xhci-library.zig (naming typo) 2026-07-12 23:13:33 +01:00
Daniel Samson 77901bbba6
WIP: USB 2026-07-12 22:24:47 +01:00
Daniel Samson 78582d24d2
code lint 2026-07-12 19:36:10 +01:00
Daniel Samson 1cdffe21b1
fixing comments 2026-07-12 16:19:09 +01:00
Daniel Samson f5f0e15769
zig fmt 2026-07-12 16:04:58 +01:00
Daniel Samson 88e92254e9
Name ACPI hardware IDs instead of magic _HID strings
Turn acpi-ids.zig's flat name table into a HardwareId enum modeled on
ps2-library's Port: one entry() switch holds the registry (variant ->
_HID string + human-readable name), with hid(), description(), and
fromHid() methods. The free description(hid) lookup the kernel's
device-tree dump uses survives, implemented over the enum, and a new
test round-trips every variant through fromHid.

Callers now name the device instead of quoting its id:

- ps2-library's DeviceType.hid() and ps2-bus's descriptor lookups use
  HardwareId.ps2_keyboard / .ps2_mouse.
- device-manager's driverFor parses the HID once with fromHid and
  switches on named values.
- acpi.zig's isPciRootNode carried the same ids twice, as strings and
  as packed-EISA integers (0x030AD041/0x080AD041); both branches now
  decode to the string form and answer through one isPciRootHid helper
  using .pci_bus / .pci_express_root_bridge.
- build.zig threads the acpi-ids module (previously kernel-only) into
  every user binary, like xkeyboard-config.
2026-07-11 23:23:16 +01:00
Daniel Samson 5725d35e5b
Name the attach reply statuses instead of magic numbers
Add an AttachStatus enum to ps2-library.zig for AttachReply.status,
distinguishing the three failure causes handleAttach previously
collapsed into a bare -1: invalid_request (message too short),
missing_endpoint (no capability passed), and no_such_device (no port
identified the requested device type). The keyboard and mouse drivers
check against AttachStatus.ok rather than a literal 0.
2026-07-11 23:11:45 +01:00
Daniel Samson 8c95525793
Wire real PS/2 mouse packets through to input events
Replace the mouse driver's synthetic stream with the real path, the
way the keyboard was wired:

- The auxiliary port's IRQ12 is enumerated on the mouse's own ACPI
  node (PNP0F13), and the kernel only lets a device's claimer bind or
  ack its IRQs — so ps2-bus now claims that node alongside the
  controller whenever port 2 carries a device, binds IRQ12 to its one
  endpoint, and re-arms whichever line the notification's badge names.
  The forwarding loop already routed auxiliary bytes by status bit 5.
- mouse-packet.zig (new, pure, host-tested): three-byte stream-mode
  packet assembly — bit-3 sync with resynchronization, ACK/BAT bytes
  dropped at packet start, nine-bit two's-complement movement,
  overflow packets discarded, and PS/2 positive-Y-up converted to the
  screen convention (positive down).
- mouse.zig mirrors the keyboard driver: no hardware claim, attaches
  to the bus as its mouse, and publishes button_down/button_up per
  changed button plus motion events with the pressed-button mask.

Verified end to end in QEMU via monitor mouse_move/mouse_button:
motion round-trips in screen coordinates, buttons transition with the
right mask, and keyboard events keep flowing alongside. The follow-up
is the IntelliMouse magic-knock for a scroll wheel (four-byte packets)
and scroll events.
2026-07-11 23:09:04 +01:00
Daniel Samson 5bba5d3363
Wire real PS/2 scancodes through to input events and characters
Replace the keyboard driver's synthetic stream with the real path:

- ps2-bus binds IRQ1 (interrupt bits set only after the bind), drains
  port 0x60 on each interrupt, and forwards every byte to the attached
  child driver over async ipc_send, routed by the status register's
  auxiliary-output bit. Children attach via the new well-known ps2_bus
  service, handing over their endpoint as a capability.
- scancode.zig (new, pure, host-tested): scancode set 2 -> USB HID
  usage decoding (F0/E0/E1 prefix state machine) plus keyboard state —
  pressed-key bitmap, typematic-repeat classification, modifier and
  caps-lock tracking.
- keyboard.zig decodes the forwarded stream and publishes real
  key_down/key_press/key_up events, filling key_press characters via
  xkeyboard-config (layout from argv[2], default us) and synthesizing
  ASCII control characters for Enter/Tab/Backspace/Escape.
- protocol.zig names the full HID usage set in Keycode; build.zig
  threads the xkeyboard-config module into user binaries.

Verified end to end in QEMU via monitor sendkey: shift, caps lock,
and control-character synthesis all decode correctly. The mouse
driver still publishes its synthetic stream; attaching it to the
bus the same way is the follow-up.
2026-07-11 22:48:59 +01:00
Daniel Samson aa0c97353a
fixing arguments 2026-07-11 21:39:27 +01:00
Daniel Samson 1bf91115dd
Generalize input module to mouse and joystick/gamepad events
Extend the input service beyond the keyboard so mouse and joystick/gamepad
drivers can broadcast too, with per-device publish and subscribe methods.

- protocol: KeyEvent joins MouseEvent (motion/buttons/scroll) and
  JoystickEvent (axes/buttons), all carried in a common InputEvent envelope
  tagged with a DeviceKind. A subscribe request carries a device_mask, so a
  subscriber names the classes it wants and the service routes each event only
  to interested subscribers (a mouse-only listener never wakes for keystrokes).
- runtime: per-device publish methods (publishKeyboardEvent/publishMouseEvent/
  publishJoystickEvent) and subscribe helpers (subscribeKeyboard/Mouse/Joystick,
  each typed, plus subscribe(mask)/subscribeAll returning the tagged envelope).
- service: subscriber table gains a device_mask; broadcast routes by the
  event's device class.
- mouse driver now publishes (synthetic) mouse events like the keyboard driver;
  input-source cycles all three classes; input-test subscribes to all and only
  emits its "ok" marker once it has received one of each class — so the passing
  test proves per-device routing, not just delivery. Real HID decoding stays a
  follow-up.

No kernel changes: ipc_send is generic and the 36-byte InputEvent fits its
64-byte payload. Full QEMU suite 48/48; serial log confirms keyboard, mouse,
and joystick all reach one subscription.
2026-07-11 15:21:09 +01:00
Daniel Samson 65244e3103
Add input module: broadcast keyboard events over IPC
Programs can now subscribe to keyboard events (key_down/key_up/key_press)
and drivers can broadcast them, through a new user-space input service.

The delivery model is forced by danos IPC: a synchronous rendezvous holds
one pending reply, so a server cannot park N subscribers blocked in a
"wait for next event" call — delivery must be push. But a synchronous push
has no timeout and the kernel never wakes a sender parked on a dead peer's
endpoint, so one dying subscriber would hang all input. So this lands the
roadmap's planned asynchronous buffered send and builds the service on it:

- ipc_send (syscall 26): non-blocking post to an endpoint's bounded payload
  ring, delivered through reply_wait as a buffered message (notify_message_bit).
  A full ring drops the oldest. It can never hang on a dead/slow peer.
- input-protocol + runtime.input helpers (subscribe/next, connectSource/
  publish) — the first real consumer of M13 capability passing: a subscriber
  hands the service its own endpoint as a capability.
- input service (fan-out via ipc_send, dead-subscriber pruning), a synthetic
  input-source, and input-test; the ps2-bus keyboard driver publishes to it.
  Real IRQ1 scancode decoding (which must live in the bus, the PNP0303 owner)
  is a documented follow-up; the source is synthetic for now.
- build/init wiring, an `input` QEMU case, and docs/input.md.

Full QEMU suite 48/48, including the new input case and every IPC/endpoint
regression (ipc, ipc-call, ipc-cap, vfs, hpet, bus, irqfree).
2026-07-11 15:03:24 +01:00
Daniel Samson 2a583d55a8
Finishing PS/2 bus driver 2026-07-11 14:12:33 +01:00
Daniel Samson 59104dd988
refactor 2026-07-11 04:32:17 +01:00
Daniel Samson e7c7e7b94c
M14a: memory-ordering / MMIO layer (library/mmio)
The tree had zero memory barriers — correct-by-accident on x86 (TSO + strong-
uncacheable MMIO), but a landmine for the first DMA driver and for ARM, which is the
win condition. Add /lib/mmio: typed volatile register access (read/write) plus mb /
rmb / wmb, lowered per-architecture (mfence/lfence/sfence on x86_64, dsb sy/ld/st on
aarch64) so the ordering rules are a named primitive, not scattered `asm volatile`.
`volatile` is not a barrier — it says nothing about ordinary stores (a DMA descriptor
in WB RAM) relative to a volatile doorbell write; wmb() between them is the fix.

Prove it on the one existing caller: hpet now does its register access through
mmio.read/write. It needs no barriers itself (pure MMIO, no DMA, UC grant on x86) —
the point is the typed, arch-portable access every driver should use; the barriers are
there for the DMA drivers to come.

New `mmio` module injected into addUserBinary; host test asserts the barriers assemble
and a register round-trips. Suite 37/37 plus host tests.
2026-07-10 19:32:57 +01:00
Daniel Samson a581712b09
M13: IPC capability passing
ipc_call and ipc_reply_wait grow a `send_cap` argument (r9) and a `received_cap`
return (r8): an endpoint travels alongside a message, installed into the receiver's
handle table. The transfer is a share, not a move — the endpoint's refcount is bumped
and the sender keeps its handle. If the receiver's table is full the call fails
-ENOSPC and the message is NOT delivered (a half-delivered capability is worse than a
failed send); a bad handle fails -EBADF. Both directions carry a cap: a client's call
hands one to the server (seen in the server's replyWait), and the server's reply hands
one back (seen in the client's call return).

This is the "open" primitive the driver model was blocked on: a bus driver mints a
per-device endpoint and hands it to a class driver, giving it a private channel to one
device without the 8-slot global name registry.

Kernel: shareCapability in ipc-synchronous.zig at both copy points; new
setSystemCallResult3 (r8, saved/restored by the syscall stub); Task gains
ipc_send_cap / ipc_received_cap. Runtime: callCap + Reply, replyWait gains send_cap
and Received.cap; plain call/replyWait delegate with no_cap. New abi.no_cap.

New ipc-cap test (two kernel tasks exercise both directions, each verifying the
endpoint it received is the same object shared, refcount bumped to 2). No class driver
consumes callCap yet — it lands with the first one. Suite 37/37 plus host tests.
2026-07-10 19:23:19 +01:00
Daniel Samson b61b7775b9
Update stale /sbin/ references to real FHS paths
The reorg moved user binaries under /system (init -> /system/services/init,
drivers -> /system/drivers/<name>, vfs-test -> /system/services/vfs/vfs-test), but
many comments and log strings still named the old /sbin/ home. Retarget them all:
kernel/loader/test comments and the two boot log lines, plus vision.md and the
driver-model.md proposed tree (also dropped the stale `d` suffixes and rt->runtime
there). The initial-ramdisk spawn log no longer fakes a /sbin/ prefix, since those
binaries live in different homes (services vs drivers).

Left the FSH design doc's /sbin and /lib rows alone — whether /sbin stays a
directory at all is a design call for its owner, not a stale-comment fix.
2026-07-10 18:13:24 +01:00
Daniel Samson ceacc6b514
Post-reorg cleanup: POSIX layer, and naming fixes
Follow-up to the monorepo re-org. Suite 35/35 plus host tests green.

POSIX compatibility is now its own library, library/posix/ (unistd, stdio),
layered strictly over the runtime — it calls the runtime's IPC/heap, never
system calls directly. The runtime is now POSIX-free (the danos-native
application ABI). The VFS wire protocol is danos-native throughout
(Stat -> FileStatus, .stat -> .status, O_CREAT -> create); the POSIX layer
maps the POSIX spellings at the boundary. The coding standard's ABI-name
exception is scoped to one place: a file is allowed POSIX spellings only if it
lives under library/posix/ — everywhere else, danos naming with no exception.

Naming fixes, all mechanical:
- initrd -> initial-ramdisk: the source file, the module, the tool
  (make-initial-ramdisk.py), the artifact (initial-ramdisk.img, including the
  bootloader's load path), and the identifiers.
- system/kernel/device-service.zig -> devices-broker.zig: it is ring-0 kernel
  code (the trusted device table + claim capability), not a ring-3 service. The
  future user-space device *manager* (policy) will live in system/services/.
- Dropped the daemon `d` suffix: hpetd -> hpet, busd -> bus. A driver lives in
  system/drivers/, so the folder already says what it is; encoding the role in
  the name too is redundant. The coding standard drops that exception.
- system/devices/aml/interp.zig -> interpreter.zig (the type was already
  Interpreter).
2026-07-10 13:33:06 +01:00
Daniel Samson 8754d4e46a
Re-organize the source tree as a monorepo mirroring the FHS
The source layout now mirrors the runtime filesystem hierarchy
(docs/danos-file-system-hierarchy-FSH.md): what lives under system/ in the
source is what a running danos represents under /system. Each service and
driver is a sub-project directory that is its own Zig module — cross-project
references go by module name, never by a path into another project's files.

Moves (all git mv, history preserved):
- src/            -> system/            (danos internals; the self-representation)
    root.zig      -> danos.zig          (the kernel<->user contract module)
    kernel/arch/  -> kernel/architecture/   (arch -> architecture)
    device/       -> devices/           (what /system/devices reflects)
    boot/         -> /boot              (the loaders, top level)
- sbin/           -> split by role:
    init, vfs     -> system/services/<name>/<name>.zig
    hpetd, busd   -> system/drivers/<name>/<name>.zig
    vfs-test      -> system/services/vfs/vfs-test.zig  (inside the vfs project)
- lib/            -> library/runtime/   (room for other libraries beside runtime)

The VFS wire protocol becomes its own module, system/services/vfs/protocol.zig
("vfs-protocol"): the vfs sub-project exposes its interface, and the runtime's
file layer imports it by name. First instance of the "protocol module" pattern
(docs/driver-model.md); usb/block will expose theirs the same way.

Also: fix a naming-standard violation in the protocol — Op -> Operation (and
req -> request, _pad -> _padding). Docs updated: /system/services added to the
FHS doc, a repository-layout section added to the docs index, and stale source
paths swept across comments and docs.

Runtime boot paths are unchanged (the bootloader still loads /sbin/init);
aligning the runtime filesystem to the FHS is a separate follow-up. Suite 35/35
plus host tests green.
2026-07-10 12:55:56 +01:00