diff --git a/docs/README.md b/docs/README.md index 3bd9fc1..e1f1570 100644 --- a/docs/README.md +++ b/docs/README.md @@ -235,28 +235,44 @@ A sub-project's extra files are reached through the module, never as separate pa system/ → /system danos's own internals (the self-representation) boot-handoff.zig the loader↔kernel contract (the `boot-handoff` module) abi.zig the private kernel↔runtime syscall ABI (the `abi` module) - parameters.zig initial-ramdisk.zig vfs-protocol.zig shared contracts + parameters.zig initial-ramdisk.zig shared contracts kernel/ IPC, memory, scheduling, the VFS root, the private syscall dispatch architecture/x86_64/ the `architecture` module (never named by generic code) - devices/ the device model /system/devices reflects (+ aml/) - device-abi.zig the device wire types (the `device-abi` module) + devices/ the kernel-internal device model (device-model, platform, + acpi, device-tree, power) that /system/devices reflects drivers/ pci-bus/ ps2-bus/ usb-xhci-bus/ one sub-project per driver → /system/drivers services/ init/ fat/ device-manager/ system servers → /system/services (fat/ holds fat.zig, engine.zig, on-disk.zig) library/ → /lib libraries, one sub-directory each runtime/ the danos-native runtime + file API (fs) — the stable application ABI + mmio/ volatile register access + memory barriers + device/ device code by domain — model/ pci/ usb/ acpi/ — each a + shareable data module (device-abi, pci-class, usb-abi/ids, + acpi-ids) plus a logic module (pci, usb, aml) + protocol/ driver↔service wire contracts (vfs block display scanout input + power device-manager usb-transfer), one module per directory boot/ → /boot the loaders tools/ test/ host-side build + QEMU test harness ``` -A sub-project exposes its **public interface as a module**: the `usb-xhci-bus` driver -owns the USB transfer protocol (`usb-transfer-protocol.zig`, the `usb-transfer-protocol` -module), which `runtime.usb` imports by name — the USB class drivers reach the -protocol through that wrapper; `block` exposes its protocol (`block-protocol`) the -same way. The VFS wire protocol is the one that outgrew its -sub-project: the VFS root moved into the kernel (`system/kernel/vfs.zig`), so the -protocol lives as a shared contract at `system/vfs-protocol.zig` (the `vfs-protocol` -module), which the runtime's file API (`runtime.fs`) imports by name. +**Wire protocols live in `library/protocol/`**, one module per directory +(`library/protocol/vfs/vfs-protocol.zig` is the `vfs-protocol` module), imported by module +name. A protocol is the seam between a low-level driver and the higher-level service it +serves — block ↔ the filesystem, a scanout driver ↔ the compositor — so both sides depend +on the contract, not on each other, and the contract belongs to neither sub-project. A +`runtime` client may *wrap* one for application convenience (`runtime.fs` over +`vfs-protocol`, `runtime.block`, `runtime.display`, `runtime.input`), but the module is the +boundary and `runtime` re-exports no protocol. A driver's private wire to its *hardware* +(virtio-gpu's command set) is not a service seam and stays a driver-private file, beside +the transport that reaches the same device. + +**Device code lives in `library/device//`**, grouped by what it is about (pci, usb, +acpi, and the cross-cutting device model) and split by dependency weight: a data module of +enums and wire types that is `std`-only and cheap for anyone to import, and a logic module +that needs `mmio` or IPC. This is what keeps the microkernel out of device business — it +imports exactly one `library/` module, `device-abi` (the descriptor types its broker +marshals across the syscall boundary), and nothing with logic or a taxonomy in it. That +lone pure-data import is the only edge from `system/kernel/` into `library/`. There is **no POSIX/C compatibility layer today**: danos programs do file I/O through the danos-native `runtime.fs` (open/read/write/list over the VFS). A hand-rolled POSIX shim @@ -273,7 +289,7 @@ exception in [coding-standards.md](coding-standards.md) applies to that seam. | Kernel entry, panic, bring-up | `system/kernel/kernel.zig` | | Loader↔kernel handoff (`BootInformation`, `Framebuffer`, `MemoryMap`, VM layout) | `system/boot-handoff.zig` | | Private kernel↔runtime syscall ABI (`SystemCall`, mmap prot flags, `page_size`) — the runtime speaks it, not apps | `system/abi.zig` | -| Device wire types (`DeviceDescriptor`, `DeviceClass`, …) | `system/devices/device-abi.zig` | +| Device wire types (`DeviceDescriptor`, `DeviceClass`, …) | `library/device/model/device-abi.zig` | | Physical frame allocator | `system/kernel/pmm.zig` | | Kernel heap (`std.mem.Allocator`) | `system/kernel/heap.zig` | | Scheduler (fixed-priority preemptive; blocking, wait queues) | `system/kernel/scheduler.zig` | @@ -281,7 +297,7 @@ exception in [coding-standards.md](coding-standards.md) applies to that seam. | IPC channels between kernel threads (message passing) | `system/kernel/ipc.zig` | | IPC endpoints: cross-address-space call/reply, handles, notifications | `system/kernel/ipc-synchronous.zig` | | User processes: ELF loading, address spaces, the syscall table | `system/kernel/process.zig` | -| VFS root: mount table + kernel-served nodes (`fs_resolve`/`fs_node`); wire protocol in `system/vfs-protocol.zig` | `system/kernel/vfs.zig` | +| VFS root: mount table + kernel-served nodes (`fs_resolve`/`fs_node`); wire protocol in `library/protocol/vfs/vfs-protocol.zig` | `system/kernel/vfs.zig` | | Device tree + claim capability + `device_register` containment | `system/kernel/devices-broker.zig` | | IRQ-as-IPC: routing a device interrupt to a driver's endpoint | `system/kernel/irq.zig` | | Hardware discovery (ACPI/device tree) behind one neutral device model | `system/devices/` | diff --git a/docs/discovery.md b/docs/discovery.md index 09ed213..6c612fc 100644 --- a/docs/discovery.md +++ b/docs/discovery.md @@ -241,7 +241,7 @@ Two consequences of neutrality bind on later work: Two supporting decisions keep the kernel's remaining slice honest: - **The AML interpreter is a single build module** - (`system/devices/aml/aml.zig`) — one source, no fork. During the ring-3 move + (`library/device/acpi/aml/aml.zig`) — one source, no fork. During the ring-3 move it was compiled into both the kernel (which linked it just for the `\_S5` poweroff evaluation) and the acpi service, with the `acpi-parse` test asserting the two produce the same device count. Since soft-off followed diff --git a/docs/display-plan.md b/docs/display-plan.md index b8195c5..71d75b7 100644 --- a/docs/display-plan.md +++ b/docs/display-plan.md @@ -39,7 +39,7 @@ initial-ramdisk; protocols are `b.addModule("…-protocol", …)` and imported i Make the boot framebuffer reachable and mappable **write-combining** from user space. -- [x] [device-abi.zig](../system/devices/device-abi.zig): added `DeviceClass.display`; a +- [x] [device-abi.zig](../library/device/model/device-abi.zig): added `DeviceClass.display`; a `DisplayInfo{ width, height, pitch, format }` carried on the descriptor; a `flags` field on `ResourceDescriptor` + `resource_flag_write_combining`. - [x] [devices-broker.zig](../system/kernel/devices-broker.zig): `seedDisplay(base, w, h, @@ -67,7 +67,7 @@ Regression-checked: `discovery`, `ioport`, `claim-release`, `supervision`, `devi Stand up the named service and the double-buffer, no layers yet. -- [x] `system/services/display/protocol.zig`: `Operation{ info, create_layer, +- [x] `library/protocol/display/display-protocol.zig`: `Operation{ info, create_layer, configure_layer, destroy_layer, fill_rect, blit_tile, damage, present }`; `extern` `Request`/`Reply`; size + `maximum_payload` consts. (Model: block/protocol.zig.) - [x] [abi.zig](../system/abi.zig): `ServiceId.display = 9`. diff --git a/docs/display.md b/docs/display.md index 596b903..a52f835 100644 --- a/docs/display.md +++ b/docs/display.md @@ -33,7 +33,7 @@ which one you're holding decides what you can do. you *is* that device's linear-framebuffer BAR — the same physical memory, seen through a different door. On a real discrete GPU, GOP's `base` is an aperture inside the GPU's VRAM BAR. danos already decodes this device - ([pci-class.zig](../system/devices/pci-class.zig) has the full `display` namespace, and + ([pci-class.zig](../library/device/pci/pci-class.zig) has the full `display` namespace, and `pci-bus` already reports it to the [device manager](device-manager.md) with its class triple) — but nothing binds it yet. @@ -72,7 +72,7 @@ rest of the system hasn't had to face: 2. **danos had no cross-process shared memory.** At v1 the memory syscalls were `mmap` (private, zeroed), `mmio_map` (a *claimed device's* MMIO), and `dma_alloc` (new pinned physical). The block driver's "pass a buffer by physical address" trick - ([block/protocol.zig](../system/services/block/protocol.zig)) works *only because its + ([block/protocol.zig](../library/protocol/block/block-protocol.zig)) works *only because its consumer is DMA hardware*. A compositor that CPU-reads and blends client layers can't use it — it would have to *map* another process's memory, which nothing allowed. v1 sidesteps it entirely (see "What v1 does not do"); v2 has since built the primitive diff --git a/docs/driver-model.md b/docs/driver-model.md index 3f1030b..85479f4 100644 --- a/docs/driver-model.md +++ b/docs/driver-model.md @@ -95,40 +95,55 @@ A "family" is two modules, not one: - **A protocol module** — the IPC message types that let a class driver talk to *whatever* published its device. This is the part that makes class drivers portable. -danos already has one of each: `library/runtime/device.zig` is a logic module, -[`system/vfs-protocol.zig`](system/vfs-protocol.zig) is a protocol module shared by the -mount backends (today the fat server) and their clients. (The user-space VFS server it -was originally written against has since retired — path routing moved into the kernel, -`system/kernel/vfs.zig`'s `fs_resolve` — but the protocol module outlived it, which is -rather the point.) The pattern generalises directly: +danos already has one of each: `library/device/pci/pci.zig` is a logic module (the +`Function` view of a claimed PCI function), +[`library/protocol/vfs/vfs-protocol.zig`](../library/protocol/vfs/vfs-protocol.zig) is a +protocol module shared by the mount backends (today the fat server) and their clients. +(The user-space VFS server it was originally written against has since retired — path +routing moved into the kernel, `system/kernel/vfs.zig`'s `fs_resolve` — but the protocol +module outlived it, which is rather the point.) The pattern generalises directly: ``` library/ - runtime/ module "runtime" — syscalls, heap, ipc, device, stdio + runtime/ module "runtime" — syscalls, ipc, lifecycle, memory, threads, log, fs mmio/ module "mmio" — volatile register access + barriers [M14] - bus/ - pci/ module "pci" — ECAM, BAR decode, capability walk - usb/ module "usb" — descriptors, control transfers, hubs - proto/ - vfs/ module "vfs-protocol" (today: system/vfs-protocol.zig) - block/ module "block-protocol" - hid/ module "hid-protocol" + device/ device code grouped by domain; each domain splits into a shareable + data module (enums/wire types, std-only) and a logic module (mmio/IPC) + model/ module "device-abi" — DeviceDescriptor, DeviceClass, ResourceKind + pci/ "pci-class" (data) + "pci" — config/BAR/capability walk (Function) + usb/ "usb-abi" + "usb-ids" (data) + "usb" — descriptors, control/interrupt/bulk client + acpi/ "acpi-ids" (data) + "aml" — _HID names, the AML interpreter + protocol/ driver <-> service wire contracts, one module per directory + vfs/ block/ display/ scanout/ input/ power/ device-manager/ usb-transfer/ system/drivers/ one sub-project each → /system/drivers (no `d` suffix) - xhci/ HCD + bus driver imports runtime, pci, usb, mmio - usb-hid/ class driver imports runtime, usb, hid-protocol - block/ class driver imports runtime, block-protocol + usb-xhci-bus/ HCD + bus driver imports runtime, usb, mmio, usb-transfer-protocol + usb-hid/ class driver imports runtime, usb, input-protocol + virtio-gpu/ scanout driver imports runtime, pci, mmio, display-/scanout-protocol ``` +The split by *dependency weight* is what lets the microkernel stay out of device +business: it imports only the `device-abi` data module (the descriptor types its broker +marshals across the syscall boundary) — never a logic module, never a taxonomy. That one +pure-data import is the only edge from `system/kernel/` into `library/`; decoding a class +code or `_HID` to a name is user space's job (the device manager owns those taxonomies). + +A protocol lives in `library/protocol/` when it is the seam between a low-level driver and +a higher-level service (block ↔ filesystem, a scanout driver ↔ the compositor). A driver's +private wire to its *hardware* — virtio-gpu's command set — is not that; it stays a +driver-private file, like the virtio-pci transport beside it. + The build side of this has since landed: [`addUserBinary`](build.zig) injects the default modules (`runtime`, `mmio`, `xkeyboard-config`, `acpi-ids`) into every user binary, and per-binary extras — protocol modules, bus logic — are added with `programModule(exe).addImport(...)`. That's the *entire* mechanism — Zig modules already give you everything else. -The discipline that makes this work: **a class driver must not import a bus's logic -module.** `usbhid` imports `proto.hid` and `usb` (for descriptor types), never `pci`. -If a class driver needs `mmio`, it has become an HCD and should be one. +The discipline that makes this work: **a class driver must not import a bus's *hardware* +logic module.** `usb-hid` imports `usb` (the transfer client) and `input-protocol`, never +`pci` and never `mmio`. If a class driver needs `mmio`, it has become an HCD and should be +one. The domain data modules (`usb-abi`, `usb-ids`, `pci-class`) carry no such weight — a +class driver, the device manager, or the kernel may share them freely. ## What exists today diff --git a/docs/drivers.md b/docs/drivers.md index dfb2575..18bef11 100644 --- a/docs/drivers.md +++ b/docs/drivers.md @@ -75,7 +75,7 @@ capability yet. ## The capability: claim before touch The driver syscall numbers (`system/abi.zig`) with the device types they carry -(`system/devices/device-abi.zig`), dispatched in `system/kernel/process.zig`: +(`library/device/model/device-abi.zig`), dispatched in `system/kernel/process.zig`: | # | Call | Meaning | |---|------|---------| diff --git a/docs/efi.md b/docs/efi.md index 15ce503..9e49d43 100644 --- a/docs/efi.md +++ b/docs/efi.md @@ -173,7 +173,7 @@ The loader and kernel are two *separate* binaries built for two different target so everything they exchange must have an identically-defined memory layout. That's what `system/boot-handoff.zig` provides — imported by both as the `boot-handoff` module. It is *only* the handoff: the kernel↔user ABI (`system/abi.zig`) and the device types -(`system/devices/device-abi.zig`) are separate contracts the bootloader never sees. +(`library/device/model/device-abi.zig`) are separate contracts the bootloader never sees. - `BootInformation` — the top-level struct passed to the kernel: the framebuffer, the memory map, the kernel's own `PT_LOAD` segments diff --git a/docs/input.md b/docs/input.md index 1bad896..4cd53ac 100644 --- a/docs/input.md +++ b/docs/input.md @@ -12,7 +12,7 @@ what a key is. The service carries three device classes today — **keyboard**, **mouse**, and **joystick/gamepad** — and is built to take more -([protocol.zig](../system/services/input/protocol.zig)). Each class has its own typed +([protocol.zig](../library/protocol/input/input-protocol.zig)). Each class has its own typed event: - `KeyEvent` — `key_down`/`key_up` (physical make/break) and `key_press` (a character was diff --git a/docs/power.md b/docs/power.md index b7cf406..70cb2fe 100644 --- a/docs/power.md +++ b/docs/power.md @@ -27,7 +27,7 @@ unchanged. ## The protocol -The `power-protocol` module ([system/services/power/protocol.zig](../system/services/power/protocol.zig)) +The `power-protocol` module ([library/protocol/power/power-protocol.zig](../library/protocol/power/power-protocol.zig)) follows the vfs-protocol pattern — extern-struct messages, a version, reserved fields. Three operations: diff --git a/docs/testing.md b/docs/testing.md index de8780a..134a902 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -9,7 +9,7 @@ There are two layers: - **Host unit tests** (`zig build test`) — for pure, platform-independent logic. What began as the three shared contracts (`system/boot-handoff.zig`, - `system/abi.zig`, `system/devices/device-abi.zig`) now spans ~26 modules: + `system/abi.zig`, `library/device/model/device-abi.zig`) now spans ~26 modules: protocol and on-wire definitions (VFS, USB, virtio-gpu), the FAT engine, the display compositor, PS/2 and HID decoding, the kernel log ring, and the runtime's `time`/`thread` — the full list is the test step in `build.zig`. diff --git a/docs/vfs-protocol.md b/docs/vfs-protocol.md index fb7b946..a628d4b 100644 --- a/docs/vfs-protocol.md +++ b/docs/vfs-protocol.md @@ -6,7 +6,7 @@ > serves it directly (the read-only /system initrd mount, via `fs_node`) or > redirects the caller to the owning backend's endpoint plus the rewritten > mount-relative path — after which the client speaks THIS protocol to the -> backend, unchanged. The Zig source of truth is `system/vfs-protocol.zig` +> backend, unchanged. The Zig source of truth is `library/protocol/vfs/vfs-protocol.zig` > (the `vfs-protocol` module), whose unit test pins a sample of the sizes > and values below. This page is the **language-neutral wire specification** > of that contract — what a Rust or C client implements ([vdso.md](vdso.md) @@ -182,7 +182,7 @@ What a non-Zig implementation may rely on, and what it must not: - Operation values, flag bits, `NodeKind` values, and struct layouts are **append-only and frozen once shipped**. The unit test in - `system/vfs-protocol.zig` pins a sample of them (the `DirectoryEntry` + `library/protocol/vfs/vfs-protocol.zig` pins a sample of them (the `DirectoryEntry` size, `NodeKind` 0–1, `Operation` values 0, 4 and 5); this page is the full record of the frozen values. - The 256-byte message ceiling is a property of the current IPC transport, diff --git a/docs/zig-self-hosting.md b/docs/zig-self-hosting.md index ae7f6c1..339759f 100644 --- a/docs/zig-self-hosting.md +++ b/docs/zig-self-hosting.md @@ -243,7 +243,7 @@ readdir/isatty/args/exit) exists. Those are downstream and out of scope here. danos's biggest genuine gap, and the correctness-critical one: - Add **mkdir / unlink / rename / truncate** to *both* the VFS wire protocol - ([vfs-protocol.zig](../system/vfs-protocol.zig)) and the FAT engine + ([vfs-protocol.zig](../library/protocol/vfs/vfs-protocol.zig)) and the FAT engine ([engine.zig](../system/services/fat/engine.zig)), then expose them via `runtime.os`. - Extend `stat` beyond `{size, kind}` to carry **mtime + inode + mode** — `std`'s file stat needs them for build-cache validity — which in turn needs **wall-clock** time diff --git a/library/protocol/vfs/vfs-protocol.zig b/library/protocol/vfs/vfs-protocol.zig index a1b93e6..d03cda5 100644 --- a/library/protocol/vfs/vfs-protocol.zig +++ b/library/protocol/vfs/vfs-protocol.zig @@ -8,7 +8,10 @@ //! side is `runtime.fs` (library/runtime/fs.zig), which programs use directly. //! //! This is user-space only — the kernel knows nothing of files or paths; it only moves the bytes. -//! Shared by library/runtime/fs.zig (client) and system/services/vfs/vfs.zig (server). +//! Shared by library/runtime/fs.zig (the client) and the mount backends that serve it (today +//! the fat server, system/services/fat/). The standalone user-space VFS server it was first +//! written against has retired — path routing moved into the kernel (system/kernel/vfs.zig, +//! fs_resolve) — but the protocol module outlived it. pub const Operation = enum(u32) { open, // open(path) -> node id