Commit Graph
5 Commits
Author SHA1 Message Date
Daniel Samson f52c591f5e Persist the kernel boot log to the USB FAT volume
On a headless or real board nothing captures serial, so the boot log — the whole
diagnostic stream — is lost at power-off. This retains it in the kernel and copies
it to the boot USB volume as /mnt/usb/DANOS.LOG, the on-disk equivalent of QEMU's
`-serial file:`. Pull the stick, read DANOS.LOG on another machine.

How it fits together:
- Kernel RAM sink (log.zig): a fixed 256 KiB in-image buffer registered as a log
  sink in kmain, right after serial. Because userspace debug_write funnels through
  log.write, it captures the entire stream — kernel lines and every service's
  output — from the first line. Fills linearly and stops when full (earliest boot
  output, the most valuable, is kept); no allocation, so it is panic-safe.
- klog_read syscall (32): copies that buffer out to a user buffer, the mirror of
  debug_write — same overflow-safe user-half bounds check, kernel -> user copy,
  under the kernel lock so the snapshot can't grow mid-copy. Wrapped by
  runtime.system.klogRead.
- log-flush (new one-shot, in the initial-ramdisk): waits for the fat server to
  mount /mnt/usb, then copies the whole log to /mnt/usb/DANOS.LOG. init spawns it
  once the boot services are up (fire-and-forget; it polls the mount itself). If
  no volume is mounted — no stick, or the no-VFS ramdisk sweep — it exits silently.
- init shutdown flush: init repeats the copy inline at the top of shutDown(),
  BEFORE it tears down the storage services (the fat server is stopped first), so
  a clean poweroff captures the fullest log while /mnt/usb is still writable.
- unistd.writeAll: loops write() past the 224-byte VFS payload cap; both flush
  paths use it.

The filename is 8.3 (DANOS.LOG) at the mount root — the FAT short-name rule, and
there is no mkdir on the FAT path yet. Extend-only writes mean the two same-session
flushes never leave stale bytes (the shutdown log is a superset of the boot log);
a shorter log on a later boot of the same stick can leave a stale tail — a noted,
cosmetic limitation, not worth pulling O_TRUNC into the FAT write path for now.

Verified end to end under QEMU: a new `log-flush` case (reusing the orderly-shutdown
build) asserts both markers then S5, and DANOS.LOG is read back out of the image
afterwards (11804 bytes, containing the kernel init line, the FAT mount line, and
the boot flush marker). Regression stays green: zig build, zig build test, zig
build check-fat-image, and a sequential QEMU sweep — smoke, init, initial-ramdisk
(log-flush silent in the bare sweep), orderly-shutdown, fat-mount, usb-storage,
usb-hid, vfs, input, device-manager, process, signals, dma, fault-pf.
2026-07-13 18:18:34 +01:00
Daniel Samson 35e8921de8 M5: VFS mount support — mount table + forwarding router
Turn the flat-ramfs VFS into a router: a mount table maps an absolute path prefix
(e.g. /mnt/usb) to a backend server's endpoint, and open/read/write/status/
readdir/close on a path under a mount are forwarded to that backend, which speaks
the same vfs-protocol. This is what a FAT filesystem mounts into.

- protocol: append readdir / mount / unmount operations, a NodeKind enum (the FSH
  file types) that now fills FileStatus.kind, a DirectoryEntry record, and a
  directory open flag. Appended values keep existing clients and tests unchanged.
- vfs.zig: a mount table, longest-prefix routing, forwarding of every op on a
  backend handle, mount/unmount handlers (the backend arrives as the call's
  capability), and release-on-death that also closes the backend's handles.
- path.zig: pure, host-tested mount-prefix matching that never captures a
  non-boundary like /mnt/usbextra.
- unistd: mount(), opendir / readdir / closedir clients.

Bare names still resolve in the flat ramfs — the backward-compat contract; the
vfs and vfs-client-death tests pass unchanged. End-to-end mount+read is exercised
by the FAT server (M6). Host units cover path matching and protocol sizes.
2026-07-13 14:21:30 +01:00
Daniel Samson be81394be3 Split the system contract into boot-handoff / abi / device-abi
The `system` module (formerly `danos`) had become a grab-bag: it held the
loader<->kernel handoff *and* the kernel<->user ABI *and* the device wire types, in
one module three different audiences imported. Usage proved the seam — the
bootloader never touched the syscall/device ABI, and user space never touched the
boot handoff — so split it by audience, one module per contract:

  system/boot-handoff.zig       loader <-> kernel: BootInformation, Framebuffer,
                                MemoryMap, the VM layout + physicalToVirtual, kernel_abi
  system/abi.zig                kernel <-> user, core: SystemCall, mmap prot flags,
                                page_size, notify_badge_bit, ServiceId
  system/devices/device-abi.zig kernel <-> user, devices: DeviceDescriptor,
                                DeviceClass, ResourceDescriptor, ResourceKind, ...

device-abi is the devices sub-project's public interface, exposed as its own module
the way vfs exposes vfs-protocol — importable by user space, unlike the
kernel-internal device model it also feeds. That collapses a real duplication:
DeviceClass and ResourceKind were defined twice (device-model.zig and the contract,
kept "in sync by hand"); device-model now re-exports them from device-abi, so the
enum a driver matches on and the one the kernel classifies with are one type.

Each import now declares which contract it speaks: the bootloader imports only
boot-handoff; a driver only abi + device-abi (via the runtime); the kernel all
three. This also retires the `system` / `runtime.system` name overlap. page_size
lands in abi (it's part of the mmap contract user space aligns to); the bootloader
keeps its own local 4 KiB constant so it depends on nothing but the handoff.

All 21 importers rewired, docs updated to keep /system mapping to source. Build,
host tests, and the QEMU suite (36/36) all green.
2026-07-10 18:08:51 +01:00
Daniel Samson d19a0ae38d Rename the shared contract module danos -> system; QEMU logs to /var/log/system
The shared kernel<->user ABI contract (BootInformation, the SystemCall numbers,
DeviceDescriptor, page_size, ...) is now the `system` module at
system/system.zig, following the convention that a directory's root file takes
the directory's name.

One overlap to note: the runtime's syscall wrappers are already `runtime.system`,
so the single file that uses both the contract and those wrappers
(library/runtime/heap.zig) aliases the wrappers locally as `system_calls`. The
two are distinct (top-level `system` vs `runtime.system`); everywhere else the
contract is just `system`.

Also: the QEMU run's serial capture now lands in the FHS log location,
zig-out/var/log/system/serial0-<timestamp>.log — a stand-in for the kernel's own
logging system, which will eventually write there itself.

Suite 35/35 plus host tests green.
2026-07-10 14:09:38 +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