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).
121 lines
11 KiB
Markdown
121 lines
11 KiB
Markdown
# DanOS Filesystem Hierarchy Standard (DFHS)
|
|
|
|
Most modern Unix and Unix-like operating systems follow the FHS. DanOS has its own FHS structure which extends the unix FHS. This is provided by virtual file system driver (VFS).
|
|
|
|
## Directory structure
|
|
|
|
| Path | Description |
|
|
|------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
| / | Primary hierarchy root and root directory of the entire file system hierarchy. |
|
|
| /bin | Essential command binaries that need to be available in single-user mode, including to bring up the system or repair it, for all users (e.g., cat, ls, cp). |
|
|
| /boot | Boot loader files (e.g., EFI, initial-ramdisk.img ). |
|
|
| /dev | POSIX Device files (e.g., /dev/null, /dev/disk0, /dev/tty, /dev/random). |
|
|
| /etc | Host-specific system-wide configuration files. |
|
|
| /home | Users' home directories, containing saved files, personal settings, etc. |
|
|
| /lib | Libraries essential for the binaries in /bin and /sbin. eg realtime, system, ipc etc. |
|
|
| /sbin | Essential system binaries (e.g init) |
|
|
| /srv | Site-specific data served by this system, such as data and scripts for web servers, data offered by FTP servers, and repositories for version control systems |
|
|
| /system | DanOS operating system files (similar idea to C:\Windows). A true representation of danos — its layout mirrors the source tree, so `/system` is what danos *is*. |
|
|
| /system/devices | danos virtual device tree e.g. similar to /sys on linux but with danos device tree conventions (the structures in the devices module) |
|
|
| /system/drivers | driver binaries, one sub-project each (e.g. /system/drivers/pci-bus, /system/drivers/ps2-bus) |
|
|
| /system/services | system-service binaries — the VFS server, init, and other user-mode servers (e.g. /system/services/vfs, /system/services/init) |
|
|
| /system/kernel | the kernel image |
|
|
| /tmp | Directory for temporary files (see also /var/tmp). Often not preserved between system reboots and may be severely size-restricted. |
|
|
| /usr | Secondary hierarchy for read-only user data; contains the majority of (multi-)user utilities and applications. Should be shareable and read-only. |
|
|
| /var | Variable files: files whose content is expected to continually change during normal operation of the system, such as logs, spool files, and temporary e-mail files. |
|
|
|
|
## File types
|
|
|
|
POSIX specifies the long format of the ls command to represent the Unix file type as the first letter for an entry.
|
|
|
|
| type | symbol | Description |
|
|
|-------------------|--------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
| regular | - | An ordinary file holding an uninterpreted byte stream. Reads and writes are positional, and the file grows on demand (e.g., a binary in /bin, a config file in /etc). |
|
|
| directory | d | A container mapping names to other files. It may only be modified through directory operations, never written to directly. |
|
|
| symbolic link | l | A file whose contents are a path that is resolved in its place. The target need not exist, and may cross mount points. |
|
|
| FIFO special | p | A named pipe: an in-order byte stream between processes, where writers block until a reader opens the other end. |
|
|
| block special | b | A device node addressed in fixed-size blocks with the kernel free to buffer and reorder access (e.g., /dev/disk0). |
|
|
| character special | c | A device node addressed as an unbuffered byte stream, delivered to the driver in order (e.g., /dev/tty, /dev/null). |
|
|
| socket | s | A named endpoint for bidirectional message-passing between processes, bound to a path rather than an address. |
|
|
|
|
## /dev
|
|
|
|
`/dev` holds the names through which processes reach devices. It is deliberately not
|
|
the device tree: the tree — every node discovered by ACPI or PCI enumeration, with its
|
|
resources and its parent — lives under [/system/devices](#directory-structure) and is
|
|
addressed by device id. `/dev` is the much smaller set of devices that have a driver
|
|
willing to serve them, addressed by name.
|
|
|
|
A device node is not a file the VFS can read. The bytes live in a driver process
|
|
([drivers.md](drivers.md)), so opening a `/dev` name has to resolve to that driver's
|
|
IPC endpoint, and subsequent reads and writes are calls against it. This is what
|
|
`system/services/vfs/vfs.zig` reserves for M10 and what the `Stat.kind` field is for; **none of it is
|
|
implemented today.** The current VFS is a flat, in-memory ramfs of eight nodes, with no
|
|
directories at all and `kind` hardcoded to zero. The three sections below describe the
|
|
intended shape, and are honest about which parts the kernel can already support.
|
|
|
|
### Character devices
|
|
|
|
A character device is a byte stream with no addressable position: bytes are delivered
|
|
to the driver in the order written, and a read consumes what is there. Terminals,
|
|
serial lines, keyboards and mice are all of this shape. These are the natural first
|
|
device nodes in danos, because a character driver needs nothing the kernel doesn't
|
|
already provide — it claims its device, maps its registers with `mmio_map`, and blocks
|
|
on `replyWait` for either an interrupt or a client request. `system/drivers/ps2-bus/ps2-bus.zig`
|
|
is already that program, minus the file-node client half.
|
|
|
|
The obstacle was never the file type; it is which hardware a ring-3 driver can reach.
|
|
Direct `in`/`out` from user space is still a #GP (no TSS I/O bitmap, IOPL never raised),
|
|
but a driver no longer needs it: **`io_read`/`io_write`** grant port access the same way
|
|
`mmio_map` grants memory — gated by `device_claim` and the device's discovered `io_port`
|
|
resource. So the 16550 UART at `0x3F8` and the PS/2 controller at `0x60`/`0x64` (and thus
|
|
`/dev/ttyS0` and a keyboard node) are now writable as ordinary ring-3 drivers; the
|
|
low-rate legacy hardware that needs port I/O is fine with a syscall per access. A
|
|
memory-mapped device such as the framebuffer, needing no port I/O at all, remains the
|
|
easiest first entry.
|
|
|
|
### Block devices
|
|
|
|
A block device is addressed in fixed-size blocks and, unlike a character device, the
|
|
layer above is free to buffer, reorder, coalesce and retry requests against it. Disks
|
|
and other persistent storage are the whole population of this class.
|
|
|
|
A block driver is now **writable, but not yet memory-safe.** Every storage controller
|
|
worth naming is a bus master: it is programmed by handing it the physical address of a
|
|
descriptor ring and left to read and write memory on its own. That ring is exactly what
|
|
**`dma_alloc`** now provides — physically contiguous, pinned, uncacheable, with its
|
|
physical address disclosed — and **`/lib/mmio`**'s barriers order the descriptor writes
|
|
against the doorbell, and **`msi_bind`** delivers completions. So an AHCI or NVMe driver
|
|
can be written today (the M14/M15 work in [driver-model.md](driver-model.md); the earlier
|
|
"cannot host a block driver at all" is no longer true).
|
|
|
|
What is *not* yet true is that it is safe. A device programmed with an arbitrary physical
|
|
address writes to arbitrary physical memory, and page tables do not sit between a device
|
|
and RAM — an IOMMU does. The IOMMU is now *detected* (M16), but no translation domains
|
|
are programmed, so granting a DMA-capable device to a driver process is still equivalent
|
|
to granting ring 0. Until per-device domains confine a driver's DMA to the buffers it
|
|
`dma_alloc`'d, a block driver works but forfeits the isolation that motivates user-space
|
|
drivers — enforcement is the next step, and lands with that first driver. A ramdisk over
|
|
the initial ramdisk remains the one block-shaped thing that needs no driver process at all.
|
|
|
|
### Pseudo-devices
|
|
|
|
A pseudo-device has the interface of a device and no hardware behind it: `/dev/null`
|
|
discarding writes and reading as end-of-file, `/dev/zero` reading as an endless run of
|
|
zero bytes, `/dev/full` failing writes with `ENOSPC`, `/dev/random` and `/dev/urandom`
|
|
yielding unpredictable bytes.
|
|
|
|
These are the only `/dev` entries danos can implement immediately, and they are the
|
|
sensible place to start, because they are exactly the entries that need no driver
|
|
process, no `device_claim`, no MMIO grant and no interrupt. The VFS server answers them
|
|
out of its own address space — `null` and `zero` are a few lines each in
|
|
`system/services/vfs/vfs.zig`'s `read` and `write` handlers. Doing so forces the two pieces of
|
|
structure that every later device node depends on and that the flat ramfs currently
|
|
lacks: a directory, so that `/dev/null` is a path rather than a name; and a populated
|
|
`Stat.kind`, so that a caller can tell a character device from a regular file.
|
|
|
|
`/dev/random` is the one that is not free. It needs an entropy source, and the honest
|
|
options on this kernel are `RDRAND`/`RDSEED` where CPUID advertises them, and the HPET
|
|
counter's low bits as a poor fallback. Neither is a seeded CSPRNG, and a `/dev/random`
|
|
that is merely unpredictable-looking is worse than none — nothing should be keyed from
|
|
it until it is a real one. |