Two driver-model milestones plus a tree-wide naming pass. Suite 35/35 (QEMU) + host tests green. M11 — IRQ-as-IPC. A ring-3 driver now sleeps until its device interrupts it. New src/kernel/irq.zig: per-GSI endpoint bindings, comptime per-vector trampolines, dispatch = mask GSI -> LAPIC EOI -> notifyLocked, all under one lock region. irq_bind/irq_ack syscalls, gated by the device claim like mmio_map. interruptDispatch no longer EOIs — each handler owns its EOI, because a level line must be masked before it is acknowledged (irq_ack is the unmask). Bindings are keyed on the owning task and released on exit (a shared endpoint's siblings survive). hpetd rewritten interrupt-driven. Tests: hpet (rewritten, reads back the I/O APIC routing) and irqfree. M12 — bus drivers. DeviceDesc gains a parent, making the device table a tree. dev_register (device_register) lets a process publish children below a device it claimed; the kernel enforces resource containment (a child's resources must nest in its parent's), so a descriptor can't fabricate a window over kernel RAM. Descriptor copied in via copyFromUser (physmap walk — an unmapped user pointer fails the call instead of faulting the kernel). Per-parent child cap bounds table exhaustion. sbin/busd.zig is a worked bus driver. Test: bus. Naming — per docs/coding-standards.md: non-acronym abbreviations spelled out (message, descriptor, device_service, scheduler, runtime, physical, interpreter, ...); acronyms kept (IPC, MMIO, DMA, HCD, ...); files are kebab-case (ipc-synchronous.zig, device-service.zig, vfs-protocol.zig, ...). Exceptions: POSIX/C ABI names and Zig idioms (init/len/ptr) kept. Module collisions resolved by specific naming (config -> parameters, device.zig alias -> device_model). AML op/Op disambiguated: op = opcode, Op = operation; per-opcode parse handlers renamed opX -> parseX. New driver docs: drivers.md, driver-model.md (bus/class/HCD shapes + the proposed M13–M16 ABI), coding-standards.md.
138 lines
6.5 KiB
Markdown
138 lines
6.5 KiB
Markdown
# Coding standards
|
|
|
|
Conventions for danos source. The overriding one, from which most of the rest follows:
|
|
|
|
> **Names are spelled out in full. An identifier is not abbreviated unless the
|
|
> abbreviation is an acronym.**
|
|
|
|
`interruptDispatch`, not `intDisp`. `message_len`, not `message_len` (`msg` expands, `len`
|
|
is a Zig idiom — see the exceptions). `device_service`, not `device_service`. `scheduler`, not
|
|
`sched`. The cost of a longer name is paid once, at the keyboard; the cost of a
|
|
cryptic one is paid every time the code is read, by everyone who reads it. In a
|
|
microkernel whose whole argument is that a human can hold each piece in their head,
|
|
that trade is not close.
|
|
|
|
## The rule, precisely
|
|
|
|
**Acronyms and initialisms stay.** They *are* the full name — expanding them would make
|
|
the code worse, not better. `IPC`, `MMIO`, `DMA`, `IRQ`, `TSS`, `GDT`, `IDT`, `APIC`,
|
|
`GSI`, `HPET`, `ACPI`, `PCI`, `EOI`, `BAR`, `ECAM`, `MSI`, `CPU`, `ELF`, `ABI`, `UEFI`,
|
|
`MMU`, `TLB`, `ISR`, `ISA`, `GAS`, `HAL`, `PMM`, `VMM`, `VFS`, `HID`, `HCD`, `SMP`,
|
|
`AML`, `MADT`, `MCFG`, `FADT`, `RSDP`, `XSDT`, `RSDT`, `GOP`, `EDID`, `TSC`, `PIT`,
|
|
`RTC`, `LAPIC`, `SIPI`. In code they carry whatever case the surrounding convention
|
|
demands: `Hal` the type, `hal` the variable, `mapMmio` the function.
|
|
|
|
**Everything else is spelled out.** If it's a word with letters removed, restore them:
|
|
|
|
| Abbreviation | Full |
|
|
|---|---|
|
|
| `proto` | `protocol` |
|
|
| `msg` | `message` |
|
|
| `desc` | `descriptor` |
|
|
| `res` | `resource` |
|
|
| `recv` | `receive` |
|
|
| `buf` | `buffer` |
|
|
| `cur` | `current` |
|
|
| `src` / `dst` | `source` / `destination` |
|
|
| `idx` | `index` |
|
|
| `addr` | `address` |
|
|
| `reg` | `register` |
|
|
| `prev` | `previous` |
|
|
| `cfg` / `config` | `configuration` |
|
|
| `arch` | `architecture` |
|
|
| `sched` | `scheduler` |
|
|
| `dev` | `device` |
|
|
| `sys` / `syscall` | `system` / `system_call` |
|
|
| `info` | `information` |
|
|
| `dt` | `device_tree` |
|
|
| `ep` | `endpoint` |
|
|
| `rt` | `runtime` |
|
|
| `func` | `function` |
|
|
| `phys` / `virt` | `physical` / `virtual` |
|
|
| `wq` | `wait_queue` |
|
|
|
|
This list is illustrative, not exhaustive. The rule is the rule; when you meet a new
|
|
abbreviation, expand it.
|
|
|
|
## Exceptions
|
|
|
|
Three, and only three.
|
|
|
|
1. **Foreign ABI names are spelled exactly as the ABI spells them.** A function that
|
|
*is* the C or POSIX interface keeps its name: `fopen`, `fwrite`, `fread`, `malloc`,
|
|
`calloc`, `realloc`, `free`, `memcpy`, `mmap`, `munmap`, `open`, `read`, `write`,
|
|
`close`, `lseek`, `stat`, `errno`. We don't get to rename `fwrite` to
|
|
`fileWrite` — it wouldn't be `fwrite` any more. This also covers the syscall
|
|
*wrappers* that exist to match those names. It does **not** license inventing new
|
|
abbreviated names in that style.
|
|
|
|
2. **Zig idioms are spelled the way Zig spells them.** Three names are the language's,
|
|
not ours, and are left alone:
|
|
- **`init` / `deinit`** — the constructor convention (`std.ArrayList.init`), not a
|
|
shortening of "initialize".
|
|
- **`len` / `ptr`** — the slice field names (`slice.len`, `slice.ptr`). Our own
|
|
structs use bare `len`/`ptr` fields to mirror them, so a reader carries one
|
|
mental model. (Compounds still expand: a field is `message_len`, not
|
|
`message_length` — `len` is kept, `msg` is not.)
|
|
- The builtins (`@min`, `@max`, `@memcpy`) and `allocator.alloc` / `.create` are
|
|
Zig's spelling.
|
|
|
|
The rule governs the names *we* coin.
|
|
|
|
3. **Single-letter variables in a trivial local scope.** `for (items) |item, i|` may
|
|
keep `i`; a coordinate may be `x`, `y`. The moment the scope is big enough that the
|
|
letter's meaning isn't obvious on sight, give it a real name. When in doubt, name it.
|
|
|
|
4. **Established Unix filesystem and program conventions.** Top-level directories keep
|
|
their conventional names — `src`, `lib`, `sbin`, `bin`, `docs` — as do daemon
|
|
programs by their `d` suffix (`hpetd`, `busd`, following `sshd`/`httpd`). These are
|
|
names a Unix reader already knows; expanding them fights the convention rather than
|
|
serving it.
|
|
|
|
## A note on collisions
|
|
|
|
Two identifiers can legitimately expand to the same word. When they do, keep both
|
|
meaningful by renaming one to its *specific* identity rather than the generic
|
|
expansion. Two cases resolved this way:
|
|
|
|
- The `config` module (compile-time tunables — `maximum_cpus`, `timer_hz`) would
|
|
collide with `cfg` (a `PlatformConfiguration` value) at `configuration`. The module
|
|
became **`parameters`**, which is what it holds.
|
|
- The kernel `device.zig` module would collide with `dev` (a device value) at
|
|
`device`. The module alias became **`device_model`**, which is what it is — the
|
|
device data model (`Device`, `DeviceTree`, `ResourceKind`).
|
|
- The `Namespace` module alias (`ns`/`nsp` across the AML files) collides with a
|
|
`Namespace` **instance**. Resolved by dropping the module alias entirely — the two
|
|
types it provided are imported directly (`const Node = @import("namespace.zig").Node;`)
|
|
— which frees `namespace` for the instance.
|
|
|
|
A related case is one abbreviation with two meanings. In the AML code, `op` means
|
|
**opcode** (`opcodes.zig`, the `*_opcode` constants) but `Op` in `BinaryOperation` /
|
|
`LogicOperation` means **operation** — distinguished by case. The per-opcode parser
|
|
handlers, formerly `opName`/`opField`, are `parseName`/`parseField`: they *parse* the
|
|
opcode's structure, which says what they do without overloading "op".
|
|
|
|
## Case and file names
|
|
|
|
Within those spelling rules, follow Zig's own conventions:
|
|
|
|
- **Types** — `PascalCase`: `DeviceDescriptor`, `Endpoint`, `WaitQueue`.
|
|
- **Functions** — `camelCase`: `mapUserDeviceInto`, `notifyFromIsr`.
|
|
- **Variables, fields, constants** — `snake_case`: `message_length`, `device_service`,
|
|
`notify_badge_bit`.
|
|
|
|
**File names are `kebab-case`.** A file named for a multi-word thing hyphenates it:
|
|
`device-tree.zig`, `ipc-synchronous.zig`, `vfs-protocol.zig`, `device-service.zig`. A
|
|
single word or acronym needs no hyphen: `scheduler.zig`, `paging.zig`, `apic.zig`,
|
|
`idt.zig`. (The module *alias* a file is imported under still follows the code
|
|
conventions above — `snake_case` — because it's an identifier, not a filename.)
|
|
|
|
## Why acronyms are the line
|
|
|
|
Because an acronym has no letters to restore. `MMIO` doesn't become "memory mapped
|
|
input output" in code — that expansion is what the acronym *is for*. But `msg` is just
|
|
`message` with three letters stolen, and stealing them buys nothing a reader wants. The
|
|
test for "is this an abbreviation I must expand" is simply: *is there a longer word this
|
|
is a clipped form of?* If yes, write the word. If it's an initialism standing in for a
|
|
phrase, leave it.
|