Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf0c3fd3e0 | ||
|
|
3af0110483 | ||
|
|
757c6f14c3 |
@@ -63,7 +63,7 @@ zig build release-x86-64
|
|||||||
|
|
||||||
Produces `zig-out/danos-x86-64.iso`, a hybrid ISO that boots flashed raw to a
|
Produces `zig-out/danos-x86-64.iso`, a hybrid ISO that boots flashed raw to a
|
||||||
USB stick (balenaEtcher, dd) or burned to optical media — see
|
USB stick (balenaEtcher, dd) or burned to optical media — see
|
||||||
[docs/release-iso.md](docs/os-development-guide/release-iso.md). `zig build check-iso-image`
|
[docs/release-iso.md](docs/os-development/release-iso.md). `zig build check-iso-image`
|
||||||
validates it without booting.
|
validates it without booting.
|
||||||
|
|
||||||
## Run
|
## Run
|
||||||
|
|||||||
@@ -191,6 +191,7 @@ fn addKernel(
|
|||||||
.sanitize_c = .off, // the UBSan runtime needs f128/SSE support we don't provide
|
.sanitize_c = .off, // the UBSan runtime needs f128/SSE support we don't provide
|
||||||
.stack_check = false, // stack-probe calls have no runtime to land in
|
.stack_check = false, // stack-probe calls have no runtime to land in
|
||||||
.stack_protector = false,
|
.stack_protector = false,
|
||||||
|
.strip = optimize != .Debug, // DWARF info doubles the flashable image; keep it only for debug builds
|
||||||
.imports = &.{
|
.imports = &.{
|
||||||
.{ .name = "boot-handoff", .module = modules.boot_handoff },
|
.{ .name = "boot-handoff", .module = modules.boot_handoff },
|
||||||
.{ .name = "abi", .module = modules.abi },
|
.{ .name = "abi", .module = modules.abi },
|
||||||
|
|||||||
+63
-63
@@ -3,102 +3,102 @@
|
|||||||
Notes on how danos boots and draws, written to explain the *why* behind the code
|
Notes on how danos boots and draws, written to explain the *why* behind the code
|
||||||
rather than restate it. Roughly in the order things happen at runtime:
|
rather than restate it. Roughly in the order things happen at runtime:
|
||||||
|
|
||||||
1. **[efi.md](os-development-guide/efi.md) — EFI / the boot process.** How UEFI firmware finds and
|
1. **[efi.md](os-development/efi.md) — EFI / the boot process.** How UEFI firmware finds and
|
||||||
runs the bootloader, what the loader gathers before `ExitBootServices`, how it
|
runs the bootloader, what the loader gathers before `ExitBootServices`, how it
|
||||||
loads the kernel ELF, and the ABI contract for the jump into the kernel. Start
|
loads the kernel ELF, and the ABI contract for the jump into the kernel. Start
|
||||||
here.
|
here.
|
||||||
2. **[system-image.md](os-development-guide/system-image.md) — system.img, the boot capsule.** The
|
2. **[system-image.md](os-development/system-image.md) — system.img, the boot capsule.** The
|
||||||
bundled user binaries packed into one file in the initial-ramdisk wire
|
bundled user binaries packed into one file in the initial-ramdisk wire
|
||||||
format, because one open + one sequential read is the only file I/O shape
|
format, because one open + one sequential read is the only file I/O shape
|
||||||
firmware is fast at. The trivial container format, the three artifacts one
|
firmware is fast at. The trivial container format, the three artifacts one
|
||||||
build list derives (tree, manifest, capsule), the loader's three-strategy
|
build list derives (tree, manifest, capsule), the loader's three-strategy
|
||||||
fallback chain, and the capsule's kernel-side life as both the spawn table
|
fallback chain, and the capsule's kernel-side life as both the spawn table
|
||||||
and the read-only `/system` mount.
|
and the read-only `/system` mount.
|
||||||
3. **[gop.md](os-development-guide/gop.md) — the Graphics Output Protocol.** How UEFI exposes graphics
|
3. **[gop.md](os-development/gop.md) — the Graphics Output Protocol.** How UEFI exposes graphics
|
||||||
modes (unlike fixed VGA modes), how we detect the monitor's native resolution
|
modes (unlike fixed VGA modes), how we detect the monitor's native resolution
|
||||||
from EDID and switch to it, and the pixel formats we accept or reject.
|
from EDID and switch to it, and the pixel formats we accept or reject.
|
||||||
4. **[framebuffer.md](os-development-guide/framebuffer.md) — the framebuffer.** What the linear
|
4. **[framebuffer.md](os-development/framebuffer.md) — the framebuffer.** What the linear
|
||||||
framebuffer the loader hands over actually is, and what **pitch** (stride)
|
framebuffer the loader hands over actually is, and what **pitch** (stride)
|
||||||
means versus width — the detail you have to get right to avoid a skewed image.
|
means versus width — the detail you have to get right to avoid a skewed image.
|
||||||
5. **[memory-map.md](os-development-guide/memory-map.md) — the memory map.** How the loader learns what
|
5. **[memory-map.md](os-development/memory-map.md) — the memory map.** How the loader learns what
|
||||||
physical RAM exists and hands it to the kernel in danos's own neutral format,
|
physical RAM exists and hands it to the kernel in danos's own neutral format,
|
||||||
rather than leaking UEFI's memory descriptors across the boundary.
|
rather than leaking UEFI's memory descriptors across the boundary.
|
||||||
6. **[frame-allocator.md](os-development-guide/frame-allocator.md) — the physical frame allocator.** The
|
6. **[frame-allocator.md](os-development/frame-allocator.md) — the physical frame allocator.** The
|
||||||
bitmap allocator that hands out and reclaims 4 KiB physical frames from that
|
bitmap allocator that hands out and reclaims 4 KiB physical frames from that
|
||||||
map — the primitive page tables and the heap are built on.
|
map — the primitive page tables and the heap are built on.
|
||||||
7. **[interrupts.md](os-development-guide/interrupts.md) — interrupts and exceptions.** The GDT, IDT and
|
7. **[interrupts.md](os-development/interrupts.md) — interrupts and exceptions.** The GDT, IDT and
|
||||||
TSS, the exception stubs, and the handler that reports a CPU fault in red instead
|
TSS, the exception stubs, and the handler that reports a CPU fault in red instead
|
||||||
of letting it triple-fault into a silent reset.
|
of letting it triple-fault into a silent reset.
|
||||||
8. **[paging.md](os-development-guide/paging.md) — the kernel's page tables.** Building our own 4-level
|
8. **[paging.md](os-development/paging.md) — the kernel's page tables.** Building our own 4-level
|
||||||
page tables, identity-mapping the low 4 GiB, and switching CR3 off the firmware's
|
page tables, identity-mapping the low 4 GiB, and switching CR3 off the firmware's
|
||||||
tables onto ours.
|
tables onto ours.
|
||||||
9. **[device-interrupts.md](device-driver-development-guide/device-interrupts.md) — device interrupts.** The Local
|
9. **[device-interrupts.md](device-driver-development/device-interrupts.md) — device interrupts.** The Local
|
||||||
APIC and its timer — the kernel's first interrupt that is *handled and returned
|
APIC and its timer — the kernel's first interrupt that is *handled and returned
|
||||||
from*, giving it a heartbeat.
|
from*, giving it a heartbeat.
|
||||||
10. **[heap.md](os-development-guide/heap.md) — the kernel heap.** A growable free-list allocator built on
|
10. **[heap.md](os-development/heap.md) — the kernel heap.** A growable free-list allocator built on
|
||||||
the VMM, exposed as a `std.mem.Allocator` so std containers work — dynamic
|
the VMM, exposed as a `std.mem.Allocator` so std containers work — dynamic
|
||||||
allocation for the kernel.
|
allocation for the kernel.
|
||||||
11. **[scheduling.md](os-development-guide/scheduling.md) — the scheduler.** Fixed-priority preemptive
|
11. **[scheduling.md](os-development/scheduling.md) — the scheduler.** Fixed-priority preemptive
|
||||||
multitasking: kernel threads, the context switch, O(1) priority selection, and
|
multitasking: kernel threads, the context switch, O(1) priority selection, and
|
||||||
blocking (sleep, wait queues) — the leap to a running system.
|
blocking (sleep, wait queues) — the leap to a running system.
|
||||||
12. **[ipc.md](device-driver-development-guide/ipc.md) — inter-process communication.** Bounded blocking
|
12. **[ipc.md](device-driver-development/ipc.md) — inter-process communication.** Bounded blocking
|
||||||
message-passing channels, then synchronous call/reply between *processes* over
|
message-passing channels, then synchronous call/reply between *processes* over
|
||||||
endpoints — the backbone the microkernel's isolated servers talk over.
|
endpoints — the backbone the microkernel's isolated servers talk over.
|
||||||
13. **[syscall.md](os-development-guide/syscall.md) — system calls.** How ring 3 asks the kernel for
|
13. **[syscall.md](os-development/syscall.md) — system calls.** How ring 3 asks the kernel for
|
||||||
something: the `syscall`/`sysret` fast path, the trap frame, and why the table is
|
something: the `syscall`/`sysret` fast path, the trap frame, and why the table is
|
||||||
deliberately tiny. The numbers are a **private** ABI — [vdso.md](os-development-guide/vdso.md) designs
|
deliberately tiny. The numbers are a **private** ABI — [vdso.md](os-development/vdso.md) designs
|
||||||
the public boundary that will hide them.
|
the public boundary that will hide them.
|
||||||
14. **[vfs-protocol.md](file-system-development/vfs-protocol.md) — the VFS wire protocol.** The language-neutral
|
14. **[vfs-protocol.md](file-system-development/vfs-protocol.md) — the VFS wire protocol.** The language-neutral
|
||||||
byte-level spec of the file protocol spoken over IPC: request/reply headers,
|
byte-level spec of the file protocol spoken over IPC: request/reply headers,
|
||||||
the operation table, mount routing, and the append-only evolution rules — the
|
the operation table, mount routing, and the append-only evolution rules — the
|
||||||
first IPC protocol documented as public ABI.
|
first IPC protocol documented as public ABI.
|
||||||
15. **[drivers.md](device-driver-development-guide/drivers.md) — writing a driver.** The payoff: a driver is an
|
15. **[drivers.md](device-driver-development/drivers.md) — writing a driver.** The payoff: a driver is an
|
||||||
ordinary ring-3 process that claims a device, maps its registers, and **sleeps
|
ordinary ring-3 process that claims a device, maps its registers, and **sleeps
|
||||||
until its hardware interrupts it**. The claim is the capability; `irq_ack` is the
|
until its hardware interrupts it**. The claim is the capability; `irq_ack` is the
|
||||||
unmask.
|
unmask.
|
||||||
16. **[driver-model.md](device-driver-development-guide/driver-model.md) — buses, classes and host controllers.** How
|
16. **[driver-model.md](device-driver-development/driver-model.md) — buses, classes and host controllers.** How
|
||||||
real driver stacks factor into three shapes and how families share code. The
|
real driver stacks factor into three shapes and how families share code. The
|
||||||
three primitives it proposed are long since built (M13 capability passing,
|
three primitives it proposed are long since built (M13 capability passing,
|
||||||
M14 DMA + barriers, M15 MSI), and the driver *contract* on top of them —
|
M14 DMA + barriers, M15 MSI), and the driver *contract* on top of them —
|
||||||
hello, supervision, restart — is built too (device-manager.md, M18).
|
hello, supervision, restart — is built too (device-manager.md, M18).
|
||||||
17. **[usb-hub.md](device-driver-development-guide/usb-hub.md) — USB hubs.** Built (M22): why hub topology is handled
|
17. **[usb-hub.md](device-driver-development/usb-hub.md) — USB hubs.** Built (M22): why hub topology is handled
|
||||||
*inside* the `usb-xhci-bus` driver rather than a separate hub class driver — a
|
*inside* the `usb-xhci-bus` driver rather than a separate hub class driver — a
|
||||||
device behind a hub is reached by the **controller**, programmed with a route
|
device behind a hub is reached by the **controller**, programmed with a route
|
||||||
string in its slot context — plus the compound-hub reality (a USB 3.0 hub is
|
string in its slot context — plus the compound-hub reality (a USB 3.0 hub is
|
||||||
physically two hubs) and detection via the hub's status-change interrupt endpoint.
|
physically two hubs) and detection via the hub's status-change interrupt endpoint.
|
||||||
18. **[process-management.md](os-development-guide/process-management.md) — process management.** The
|
18. **[process-management.md](os-development/process-management.md) — process management.** The
|
||||||
microkernel's `ps`/`kill`/SIGCHLD: enumerate as a table snapshot, the
|
microkernel's `ps`/`kill`/SIGCHLD: enumerate as a table snapshot, the
|
||||||
supervision link as the kill authority, and child-exit notifications over the
|
supervision link as the kill authority, and child-exit notifications over the
|
||||||
same endpoints IRQs arrive on.
|
same endpoints IRQs arrive on.
|
||||||
19. **[process-lifecycle.md](os-development-guide/process-lifecycle.md) — the process lifecycle.** Built
|
19. **[process-lifecycle.md](os-development/process-lifecycle.md) — the process lifecycle.** Built
|
||||||
(M17): signals over IPC as the one lifecycle vocabulary every process speaks — the
|
(M17): signals over IPC as the one lifecycle vocabulary every process speaks — the
|
||||||
POSIX.1-1990 words with message delivery instead of stack hijack, the stable
|
POSIX.1-1990 words with message delivery instead of stack hijack, the stable
|
||||||
`process` module interface, exit reasons, published exit events any stateful
|
`process` module interface, exit reasons, published exit events any stateful
|
||||||
service can subscribe to (the VFS releasing dead clients' handles), and the two
|
service can subscribe to (the VFS releasing dead clients' handles), and the two
|
||||||
iron rules (cleanup is the kernel's job; kill is not a signal).
|
iron rules (cleanup is the kernel's job; kill is not a signal).
|
||||||
20. **[device-manager.md](device-driver-development-guide/device-manager.md) — the device manager.** Built (M18,
|
20. **[device-manager.md](device-driver-development/device-manager.md) — the device manager.** Built (M18,
|
||||||
through the app surface): the
|
through the app surface): the
|
||||||
tree, the matcher, and the supervisor. Tree structure lives in the manager,
|
tree, the matcher, and the supervisor. Tree structure lives in the manager,
|
||||||
authority stays in the kernel; bus drivers report what they see; drivers are
|
authority stays in the kernel; bus drivers report what they see; drivers are
|
||||||
restarted through the lifecycle vocabulary — the plan that turns
|
restarted through the lifecycle vocabulary — the plan that turns
|
||||||
[resilience.md](os-development-guide/resilience.md)'s restart goal into increments.
|
[resilience.md](os-development/resilience.md)'s restart goal into increments.
|
||||||
21. **[input.md](device-driver-development-guide/input.md) — the input module.** Broadcasting input events (keyboard,
|
21. **[input.md](device-driver-development/input.md) — the input module.** Broadcasting input events (keyboard,
|
||||||
mouse, joystick): why a synchronous rendezvous can't fan out to many listeners, the
|
mouse, joystick): why a synchronous rendezvous can't fan out to many listeners, the
|
||||||
asynchronous `ipc_send` primitive built to fix it, and the per-device subscribe/publish
|
asynchronous `ipc_send` primitive built to fix it, and the per-device subscribe/publish
|
||||||
service layered on top.
|
service layered on top.
|
||||||
22. **[display.md](device-driver-development-guide/display.md) — the display service.** The display half of the GUI
|
22. **[display.md](device-driver-development/display.md) — the display service.** The display half of the GUI
|
||||||
track: a user-space compositor that owns the framebuffer, composes a layer stack into
|
track: a user-space compositor that owns the framebuffer, composes a layer stack into
|
||||||
a double buffer, and presents it. Why GOP and the PCI display device are two views of
|
a double buffer, and presents it. Why GOP and the PCI display device are two views of
|
||||||
one controller, the device-node + write-combining handoff, and what flicker-free buys
|
one controller, the device-node + write-combining handoff, and what flicker-free buys
|
||||||
that tear-free doesn't. Plan: [display-plan.md](device-driver-development-guide/display-plan.md). **v2** (complete) makes
|
that tear-free doesn't. Plan: [display-plan.md](device-driver-development/display-plan.md). **v2** (complete) makes
|
||||||
scanout a pluggable backend — GOP floor + a native virtio-gpu driver, hot-attached, with
|
scanout a pluggable backend — GOP floor + a native virtio-gpu driver, hot-attached, with
|
||||||
runtime mode-set, EDID, fenced vsync presents, and restart re-attach:
|
runtime mode-set, EDID, fenced vsync presents, and restart re-attach:
|
||||||
[display-v2.md](device-driver-development-guide/display-v2.md), plan [display-v2-plan.md](device-driver-development-guide/display-v2-plan.md). Looking
|
[display-v2.md](device-driver-development/display-v2.md), plan [display-v2-plan.md](device-driver-development/display-v2-plan.md). Looking
|
||||||
further out, three research snapshots survey what a *native* driver for real GPU silicon
|
further out, three research snapshots survey what a *native* driver for real GPU silicon
|
||||||
would take as another `.scanout` backend: [nvidia-gpus.md](device-driver-development-guide/nvidia-gpus.md) (RTX 3060 /
|
would take as another `.scanout` backend: [nvidia-gpus.md](device-driver-development/nvidia-gpus.md) (RTX 3060 /
|
||||||
Ampere), [amd-gpus.md](device-driver-development-guide/amd-gpus.md) (RX 6600 / RDNA2), and [intel-igpu.md](device-driver-development-guide/intel-igpu.md)
|
Ampere), [amd-gpus.md](device-driver-development/amd-gpus.md) (RX 6600 / RDNA2), and [intel-igpu.md](device-driver-development/intel-igpu.md)
|
||||||
(Intel iGPU).
|
(Intel iGPU).
|
||||||
23. **[halting.md](os-development-guide/halting.md) — halting.** Why a kernel can't just "exit", and
|
23. **[halting.md](os-development/halting.md) — halting.** Why a kernel can't just "exit", and
|
||||||
how `while (true) hlt` parks the CPU safely once there's nothing left to do.
|
how `while (true) hlt` parks the CPU safely once there's nothing left to do.
|
||||||
|
|
||||||
Start with the north star:
|
Start with the north star:
|
||||||
@@ -108,7 +108,7 @@ Start with the north star:
|
|||||||
**resilience** (restartable components). Win condition: runs on the author's PC and
|
**resilience** (restartable components). Win condition: runs on the author's PC and
|
||||||
both Raspberry Pis, ideally with a GUI. Real-time is an option to explore, not a
|
both Raspberry Pis, ideally with a GUI. Real-time is an option to explore, not a
|
||||||
requirement. The *why* that shapes everything below.
|
requirement. The *why* that shapes everything below.
|
||||||
- **[resilience.md](os-development-guide/resilience.md) — resilience.** A design note (not built yet) on
|
- **[resilience.md](os-development/resilience.md) — resilience.** A design note (not built yet) on
|
||||||
fault isolation + live restart — the reincarnation-server + capability model that
|
fault isolation + live restart — the reincarnation-server + capability model that
|
||||||
makes "if I break it, I can restart it" real. danos's core motivation.
|
makes "if I break it, I can restart it" real. danos's core motivation.
|
||||||
- **[zig-self-hosting.md](zig-self-hosting.md) — running Zig on danos.** A design note
|
- **[zig-self-hosting.md](zig-self-hosting.md) — running Zig on danos.** A design note
|
||||||
@@ -117,14 +117,14 @@ Start with the north star:
|
|||||||
port to **one seam** (`std.os.danos`), so we build an `os` seam module (→ that seam) plus
|
port to **one seam** (`std.os.danos`), so we build an `os` seam module (→ that seam) plus
|
||||||
the thin `file-system` module, retire the `posix` shim, and follow a phased path to
|
the thin `file-system` module, retire the `posix` shim, and follow a phased path to
|
||||||
`zig build-exe hello.zig` running on danos — **not** Linux-ABI emulation.
|
`zig build-exe hello.zig` running on danos — **not** Linux-ABI emulation.
|
||||||
- **[threading.md](os-development-guide/threading.md) — threads, the std-shaped way.** **Built** (M1–M6):
|
- **[threading.md](os-development/threading.md) — threads, the std-shaped way.** **Built** (M1–M6):
|
||||||
the `thread` module's `Thread` mirrors `std.Thread`'s API (spawn/join/detach, Mutex/Condition/
|
the `thread` module's `Thread` mirrors `std.Thread`'s API (spawn/join/detach, Mutex/Condition/
|
||||||
Semaphore) over a **private** thread ABI — several tasks sharing one address space via
|
Semaphore) over a **private** thread ABI — several tasks sharing one address space via
|
||||||
a `thread_spawn` syscall, futex-backed blocking, address-space refcounting. Why it's the
|
a `thread_spawn` syscall, futex-backed blocking, address-space refcounting. Why it's the
|
||||||
native type and not literal `std.Thread` (the [private ABI](os-development-guide/syscall.md)), and why
|
native type and not literal `std.Thread` (the [private ABI](os-development/syscall.md)), and why
|
||||||
threads stay a narrow opt-in against the [resilience](os-development-guide/resilience.md) default. Build
|
threads stay a narrow opt-in against the [resilience](os-development/resilience.md) default. Build
|
||||||
plan + gates: [threading-plan.md](os-development-guide/threading-plan.md).
|
plan + gates: [threading-plan.md](os-development/threading-plan.md).
|
||||||
- **[vdso.md](os-development-guide/vdso.md) — the vDSO, the public system-call boundary.** A design note
|
- **[vdso.md](os-development/vdso.md) — the vDSO, the public system-call boundary.** A design note
|
||||||
(not built yet) on keeping `abi.zig` genuinely private: a kernel-supplied, C-ABI
|
(not built yet) on keeping `abi.zig` genuinely private: a kernel-supplied, C-ABI
|
||||||
entry blob mapped into every process as the *only* way into the kernel — so the
|
entry blob mapped into every process as the *only* way into the kernel — so the
|
||||||
syscall numbers can be renumbered or randomised at will, and Rust/C binaries get a
|
syscall numbers can be renumbered or randomised at will, and Rust/C binaries get a
|
||||||
@@ -137,69 +137,69 @@ Cutting across all of these:
|
|||||||
hardware needed to run danos: minimum specs (UEFI x86-64, ACPI, PCIe ECAM,
|
hardware needed to run danos: minimum specs (UEFI x86-64, ACPI, PCIe ECAM,
|
||||||
xHCI, ~128 MiB RAM) grounded in what the boot path actually assumes, plus a
|
xHCI, ~128 MiB RAM) grounded in what the boot path actually assumes, plus a
|
||||||
plain-language guide matching Intel/AMD CPU generations by name.
|
plain-language guide matching Intel/AMD CPU generations by name.
|
||||||
- **[release-iso.md](os-development-guide/release-iso.md) — the release ISO.** The flashable boot
|
- **[release-iso.md](os-development/release-iso.md) — the release ISO.** The flashable boot
|
||||||
media: `zig build release-x86-64` wraps the FAT32 boot volume in a hybrid ISO
|
media: `zig build release-x86-64` wraps the FAT32 boot volume in a hybrid ISO
|
||||||
(MBR ESP partition + El Torito EFI entry, one embedded image) that Etcher/dd
|
(MBR ESP partition + El Torito EFI entry, one embedded image) that Etcher/dd
|
||||||
flash to USB or a burner writes to disc — built by an in-repo pure-Python
|
flash to USB or a burner writes to disc — built by an in-repo pure-Python
|
||||||
tool, like the FAT image itself.
|
tool, like the FAT image itself.
|
||||||
- **[architecture.md](os-development-guide/architecture.md) — the architecture split.** How CPU-specific code is kept
|
- **[architecture.md](os-development/architecture.md) — the architecture split.** How CPU-specific code is kept
|
||||||
behind a build-time `arch` module so the generic kernel never names x86_64,
|
behind a build-time `arch` module so the generic kernel never names x86_64,
|
||||||
leaving room for other systems (e.g. an AArch64 Raspberry Pi) later.
|
leaving room for other systems (e.g. an AArch64 Raspberry Pi) later.
|
||||||
- **[arm.md](os-development-guide/arm.md) — ARM targets.** The Raspberry Pi landscape the arch split is
|
- **[arm.md](os-development/arm.md) — ARM targets.** The Raspberry Pi landscape the arch split is
|
||||||
aiming at: `arm` (32-bit, Pi Zero W) vs `aarch64` (64-bit, Pi 3-5), UEFI vs
|
aiming at: `arm` (32-bit, Pi Zero W) vs `aarch64` (64-bit, Pi 3-5), UEFI vs
|
||||||
device-tree boot, and what each layer needs.
|
device-tree boot, and what each layer needs.
|
||||||
- **[discovery.md](os-development-guide/discovery.md) — device discovery.** A design note on learning what
|
- **[discovery.md](os-development/discovery.md) — device discovery.** A design note on learning what
|
||||||
hardware exists via ACPI (x86) or device tree (ARM) behind one neutral device model —
|
hardware exists via ACPI (x86) or device tree (ARM) behind one neutral device model —
|
||||||
when to build it, and how to keep it architecture-agnostic.
|
when to build it, and how to keep it architecture-agnostic.
|
||||||
- **[acpi.md](os-development-guide/acpi.md) — finding the ACPI tables.** The concrete x86 locator chain:
|
- **[acpi.md](os-development/acpi.md) — finding the ACPI tables.** The concrete x86 locator chain:
|
||||||
how the loader captures the **RSDP**, hands its physical address across in `BootInformation`,
|
how the loader captures the **RSDP**, hands its physical address across in `BootInformation`,
|
||||||
and how the platform derives the **RSDT/XSDT** from it and walks the SDTs — plus the
|
and how the platform derives the **RSDT/XSDT** from it and walks the SDTs — plus the
|
||||||
live event side (the SCI, the power button, GPE/Notify) the ring-3 acpi service runs.
|
live event side (the SCI, the power button, GPE/Notify) the ring-3 acpi service runs.
|
||||||
- **[power.md](os-development-guide/power.md) — the power service.** System power as a domain-named
|
- **[power.md](os-development/power.md) — the power service.** System power as a domain-named
|
||||||
service: button/lid/battery events published to subscribers, and init's orderly
|
service: button/lid/battery events published to subscribers, and init's orderly
|
||||||
shutdown composing the [lifecycle](os-development-guide/process-lifecycle.md) stop sequence with an ACPI
|
shutdown composing the [lifecycle](os-development/process-lifecycle.md) stop sequence with an ACPI
|
||||||
S5 write. Firmware-neutral — a PSCI backend drops in on ARM.
|
S5 write. Firmware-neutral — a PSCI backend drops in on ARM.
|
||||||
- **[timers.md](os-development-guide/timers.md) — timers and time.** The ring-3 surface for reading the
|
- **[timers.md](os-development/timers.md) — timers and time.** The ring-3 surface for reading the
|
||||||
clock and waiting: why `now()` is a syscall rather than a service, and the one-shot
|
clock and waiting: why `now()` is a syscall rather than a service, and the one-shot
|
||||||
timer notification (`timer_bind`) that gives supervisors a timed wait — built on the
|
timer notification (`timer_bind`) that gives supervisors a timed wait — built on the
|
||||||
LAPIC heartbeat and calibrated TSC of [device-interrupts.md](device-driver-development-guide/device-interrupts.md).
|
LAPIC heartbeat and calibrated TSC of [device-interrupts.md](device-driver-development/device-interrupts.md).
|
||||||
- **[smp.md](os-development-guide/smp.md) — multiple cores.** A design/research note on how microkernels
|
- **[smp.md](os-development/smp.md) — multiple cores.** A design/research note on how microkernels
|
||||||
(L4, seL4) handle SMP — big kernel lock vs per-CPU vs multikernel — and how the
|
(L4, seL4) handle SMP — big kernel lock vs per-CPU vs multikernel — and how the
|
||||||
right choice depends on whether danos is chasing real-time or resilience.
|
right choice depends on whether danos is chasing real-time or resilience.
|
||||||
- **[coding-standards.md](coding-standards.md) — coding standards.** The naming rule the
|
- **[coding-standards.md](coding-standards.md) — coding standards.** The naming rule the
|
||||||
tree follows: non-acronyms are spelled out in full (`message`, not `msg`), files are
|
tree follows: non-acronyms are spelled out in full (`message`, not `msg`), files are
|
||||||
`kebab-case`, code follows Zig's case conventions, and the handful of exceptions
|
`kebab-case`, code follows Zig's case conventions, and the handful of exceptions
|
||||||
(POSIX/C ABI names, `init`/`len`/`ptr`, acronyms).
|
(POSIX/C ABI names, `init`/`len`/`ptr`, acronyms).
|
||||||
- **[sysv.md](os-development-guide/sysv.md) — the calling convention.** What "the kernel is SysV" means,
|
- **[sysv.md](os-development/sysv.md) — the calling convention.** What "the kernel is SysV" means,
|
||||||
and why the loader→kernel boundary has to pin it (the RDI-vs-RCX handoff).
|
and why the loader→kernel boundary has to pin it (the RDI-vs-RCX handoff).
|
||||||
- **[testing.md](testing.md) — testing.** How the kernel is tested by booting it in
|
- **[testing.md](testing.md) — testing.** How the kernel is tested by booting it in
|
||||||
QEMU and asserting on its serial output — reproducibly, and structured so the
|
QEMU and asserting on its serial output — reproducibly, and structured so the
|
||||||
same tests run across architectures.
|
same tests run across architectures.
|
||||||
- **[logging.md](os-development-guide/logging.md) — logging.** The multi-sink diagnostic log (serial,
|
- **[logging.md](os-development/logging.md) — logging.** The multi-sink diagnostic log (serial,
|
||||||
0xE9 debugcon, file later) kept separate from the framebuffer display, plus the
|
0xE9 debugcon, file later) kept separate from the framebuffer display, plus the
|
||||||
robustness path: optional framebuffer, POST-code checkpoints, and a persistent
|
robustness path: optional framebuffer, POST-code checkpoints, and a persistent
|
||||||
panic breadcrumb so the kernel survives — and can be diagnosed — with no output.
|
panic breadcrumb so the kernel survives — and can be diagnosed — with no output.
|
||||||
|
|
||||||
## How the pieces relate
|
## How the pieces relate
|
||||||
|
|
||||||
The boot flow ties them together: UEFI runs the loader ([efi.md](os-development-guide/efi.md)), which
|
The boot flow ties them together: UEFI runs the loader ([efi.md](os-development/efi.md)), which
|
||||||
queries the **GOP** to pick a graphics mode ([gop.md](os-development-guide/gop.md)), hands the kernel a
|
queries the **GOP** to pick a graphics mode ([gop.md](os-development/gop.md)), hands the kernel a
|
||||||
**framebuffer** to draw into ([framebuffer.md](os-development-guide/framebuffer.md)) and a **memory
|
**framebuffer** to draw into ([framebuffer.md](os-development/framebuffer.md)) and a **memory
|
||||||
map** of physical RAM ([memory-map.md](os-development-guide/memory-map.md)); the kernel turns that map
|
map** of physical RAM ([memory-map.md](os-development/memory-map.md)); the kernel turns that map
|
||||||
into a **frame allocator** ([frame-allocator.md](os-development-guide/frame-allocator.md)), installs
|
into a **frame allocator** ([frame-allocator.md](os-development/frame-allocator.md)), installs
|
||||||
its **descriptor tables** so CPU faults are caught ([interrupts.md](os-development-guide/interrupts.md)),
|
its **descriptor tables** so CPU faults are caught ([interrupts.md](os-development/interrupts.md)),
|
||||||
builds its own **page tables** and switches onto them ([paging.md](os-development-guide/paging.md)),
|
builds its own **page tables** and switches onto them ([paging.md](os-development/paging.md)),
|
||||||
brings up the **heap** for dynamic allocation ([heap.md](os-development-guide/heap.md)), starts the
|
brings up the **heap** for dynamic allocation ([heap.md](os-development/heap.md)), starts the
|
||||||
**scheduler** ([scheduling.md](os-development-guide/scheduling.md)) and the **timer** that preempts it
|
**scheduler** ([scheduling.md](os-development/scheduling.md)) and the **timer** that preempts it
|
||||||
([device-interrupts.md](device-driver-development-guide/device-interrupts.md)) — with tasks blocking, sleeping and
|
([device-interrupts.md](device-driver-development/device-interrupts.md)) — with tasks blocking, sleeping and
|
||||||
passing messages over **[IPC](device-driver-development-guide/ipc.md)** channels — runs, its CPU-specific bits
|
passing messages over **[IPC](device-driver-development/ipc.md)** channels — runs, its CPU-specific bits
|
||||||
behind the [architecture](os-development-guide/architecture.md) boundary, and when idle, or on a panic, it **halts**
|
behind the [architecture](os-development/architecture.md) boundary, and when idle, or on a panic, it **halts**
|
||||||
([halting.md](os-development-guide/halting.md)).
|
([halting.md](os-development/halting.md)).
|
||||||
|
|
||||||
Above that line the microkernel proper begins: **discovery** ([discovery.md](os-development-guide/discovery.md),
|
Above that line the microkernel proper begins: **discovery** ([discovery.md](os-development/discovery.md),
|
||||||
[acpi.md](os-development-guide/acpi.md)) learns what hardware exists, ring-3 processes ask the kernel for
|
[acpi.md](os-development/acpi.md)) learns what hardware exists, ring-3 processes ask the kernel for
|
||||||
things through the small **[syscall](os-development-guide/syscall.md)** table, isolated servers reach each
|
things through the small **[syscall](os-development/syscall.md)** table, isolated servers reach each
|
||||||
other over IPC **endpoints** ([ipc.md](device-driver-development-guide/ipc.md)), and a **[driver](device-driver-development-guide/drivers.md)** claims
|
other over IPC **endpoints** ([ipc.md](device-driver-development/ipc.md)), and a **[driver](device-driver-development/drivers.md)** claims
|
||||||
a device, maps its registers, and sleeps until the hardware interrupts it — which is
|
a device, maps its registers, and sleeps until the hardware interrupts it — which is
|
||||||
the whole reason for the arrangement ([vision.md](vision.md)).
|
the whole reason for the arrangement ([vision.md](vision.md)).
|
||||||
|
|
||||||
|
|||||||
+8
-8
@@ -1,6 +1,6 @@
|
|||||||
# Device interrupts
|
# Device interrupts
|
||||||
|
|
||||||
CPU exceptions ([interrupts.md](../os-development-guide/interrupts.md)) are the kernel reacting to its own
|
CPU exceptions ([interrupts.md](../os-development/interrupts.md)) are the kernel reacting to its own
|
||||||
mistakes. **Device interrupts** are the opposite: hardware asking for attention —
|
mistakes. **Device interrupts** are the opposite: hardware asking for attention —
|
||||||
a timer firing, a key pressed, a packet arriving. They share the IDT, but differ
|
a timer firing, a key pressed, a packet arriving. They share the IDT, but differ
|
||||||
in one fundamental way: an exception here is terminal (we report and halt), while a
|
in one fundamental way: an exception here is terminal (we report and halt), while a
|
||||||
@@ -10,7 +10,7 @@ back — the same mechanism a scheduler will later use to preempt tasks.
|
|||||||
|
|
||||||
The first device we bring up is the **timer**, because it's the simplest: it lives
|
The first device we bring up is the **timer**, because it's the simplest: it lives
|
||||||
entirely on the CPU's local interrupt controller, needing no external routing.
|
entirely on the CPU's local interrupt controller, needing no external routing.
|
||||||
It's all x86_64-specific, behind the [architecture](../os-development-guide/architecture.md) boundary.
|
It's all x86_64-specific, behind the [architecture](../os-development/architecture.md) boundary.
|
||||||
|
|
||||||
## The APIC, not the PIC
|
## The APIC, not the PIC
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ a missing PIT would hang the boot):
|
|||||||
|
|
||||||
1. **CPUID leaf 0x15** — the CPU's TSC frequency directly, needing no external timer
|
1. **CPUID leaf 0x15** — the CPU's TSC frequency directly, needing no external timer
|
||||||
at all (the LAPIC is then measured against the TSC).
|
at all (the LAPIC is then measured against the TSC).
|
||||||
2. The **HPET**, discovered via ACPI (see [discovery](../os-development-guide/discovery.md) / [acpi](../os-development-guide/acpi.md)).
|
2. The **HPET**, discovered via ACPI (see [discovery](../os-development/discovery.md) / [acpi](../os-development/acpi.md)).
|
||||||
3. The **ACPI PM timer** (a fixed 3.579545 MHz counter from the FADT).
|
3. The **ACPI PM timer** (a fixed 3.579545 MHz counter from the FADT).
|
||||||
4. The **PIT** (legacy 8254, 1.193182 MHz) — last resort, and bounded so it can't hang.
|
4. The **PIT** (legacy 8254, 1.193182 MHz) — last resort, and bounded so it can't hang.
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ values (a second socket, some firmware), so a thread migrating from a core readi
|
|||||||
check** as each application processor comes online (`checkWarpSource`, adapted from
|
check** as each application processor comes online (`checkWarpSource`, adapted from
|
||||||
Linux's): the waking core and the BSP hammer a shared "highest seen" TSC under a lock,
|
Linux's): the waking core and the BSP hammer a shared "highest seen" TSC under a lock,
|
||||||
and if either ever reads below it, the cores' TSCs are skewed. It's pairwise because APs
|
and if either ever reads below it, the cores' TSCs are skewed. It's pairwise because APs
|
||||||
come up one at a time ([smp.md](../os-development-guide/smp.md)).
|
come up one at a time ([smp.md](../os-development/smp.md)).
|
||||||
|
|
||||||
**The fallback.** When the TSC fails either test — non-invariant (a bare VM such as the
|
**The fallback.** When the TSC fails either test — non-invariant (a bare VM such as the
|
||||||
default qemu64), or warped between cores — danos moves the monotonic clock onto the
|
default qemu64), or warped between cores — danos moves the monotonic clock onto the
|
||||||
@@ -160,7 +160,7 @@ A device handler is a plain `fn () void` — a timer or keyboard handler doesn't
|
|||||||
the interrupted registers. (The stubs originally didn't save the SSE/vector
|
the interrupted registers. (The stubs originally didn't save the SSE/vector
|
||||||
registers, so a handler couldn't use them; `isr_common` now does an
|
registers, so a handler couldn't use them; `isr_common` now does an
|
||||||
`fxsave`/`fxrstor` of the full SSE/x87 state around dispatch — see
|
`fxsave`/`fxrstor` of the full SSE/x87 state around dispatch — see
|
||||||
[interrupts.md](../os-development-guide/interrupts.md).)
|
[interrupts.md](../os-development/interrupts.md).)
|
||||||
|
|
||||||
## Turning them on
|
## Turning them on
|
||||||
|
|
||||||
@@ -168,7 +168,7 @@ Exceptions can't be masked, which is why they worked all along. Maskable device
|
|||||||
interrupts don't fire until the CPU's interrupt flag is set — so the final step is
|
interrupts don't fire until the CPU's interrupt flag is set — so the final step is
|
||||||
`sti` (`arch.enableInterrupts()`), after the APIC and timer are configured. From
|
`sti` (`arch.enableInterrupts()`), after the APIC and timer are configured. From
|
||||||
that instant the kernel has a heartbeat, and its idle `hlt` loop
|
that instant the kernel has a heartbeat, and its idle `hlt` loop
|
||||||
([halting.md](../os-development-guide/halting.md)) wakes on every tick and dozes off again.
|
([halting.md](../os-development/halting.md)) wakes on every tick and dozes off again.
|
||||||
|
|
||||||
## Verifying it
|
## Verifying it
|
||||||
|
|
||||||
@@ -188,13 +188,13 @@ spinning in unrelated code — is the whole mechanism working end to end.
|
|||||||
## Since (done elsewhere)
|
## Since (done elsewhere)
|
||||||
|
|
||||||
- **Preemption**: the timer handler is where the scheduler decides to switch — the
|
- **Preemption**: the timer handler is where the scheduler decides to switch — the
|
||||||
reason a *returning* interrupt matters. See [scheduling.md](../os-development-guide/scheduling.md).
|
reason a *returning* interrupt matters. See [scheduling.md](../os-development/scheduling.md).
|
||||||
- **`sleep()` / timeouts** built on the calibrated clock.
|
- **`sleep()` / timeouts** built on the calibrated clock.
|
||||||
- **The I/O APIC, routed**: external device lines now reach a vector, and the
|
- **The I/O APIC, routed**: external device lines now reach a vector, and the
|
||||||
interrupt is delivered onward to a *user-space* driver as an IPC message. See
|
interrupt is delivered onward to a *user-space* driver as an IPC message. See
|
||||||
[drivers.md](drivers.md).
|
[drivers.md](drivers.md).
|
||||||
- **Uncacheable MMIO**: device grants are mapped `PCD|PWT` (strong-uncacheable) for
|
- **Uncacheable MMIO**: device grants are mapped `PCD|PWT` (strong-uncacheable) for
|
||||||
user drivers — see [paging.md](../os-development-guide/paging.md).
|
user drivers — see [paging.md](../os-development/paging.md).
|
||||||
|
|
||||||
## What's next (partly done since)
|
## What's next (partly done since)
|
||||||
|
|
||||||
+9
-9
@@ -10,18 +10,18 @@ mirrors them and prunes a dead reporter's children, and the `usb-report`
|
|||||||
scenario proves report → prune → respawn → re-report. The application surface is built (M18.3, 2026-07-13):
|
scenario proves report → prune → respawn → re-report. The application surface is built (M18.3, 2026-07-13):
|
||||||
`enumerate` and `subscribe` over IPC, with `device-list` as the first client —
|
`enumerate` and `subscribe` over IPC, with `device-list` as the first client —
|
||||||
the manager is now the one answer to "what devices exist" for applications.
|
the manager is now the one answer to "what devices exist" for applications.
|
||||||
The primitives underneath are real ([process-management.md](../os-development-guide/process-management.md):
|
The primitives underneath are real ([process-management.md](../os-development/process-management.md):
|
||||||
spawn/supervise/kill/exit-notification; [driver-model.md](driver-model.md): the device
|
spawn/supervise/kill/exit-notification; [driver-model.md](driver-model.md): the device
|
||||||
table as a capability system; [drivers.md](drivers.md): claim/map/IRQ), and the first
|
table as a capability system; [drivers.md](drivers.md): claim/map/IRQ), and the first
|
||||||
per-device driver spawn works (the device manager matches the xHCI controller by PCI
|
per-device driver spawn works (the device manager matches the xHCI controller by PCI
|
||||||
class and spawns `usb-xhci-bus` with the device id as argv[1]). This document designs
|
class and spawns `usb-xhci-bus` with the device id as argv[1]). This document designs
|
||||||
the rest: the device manager as **the tree, the matcher, and the supervisor** — the
|
the rest: the device manager as **the tree, the matcher, and the supervisor** — the
|
||||||
policy process that turns [resilience.md](../os-development-guide/resilience.md)'s restart goal into practice
|
policy process that turns [resilience.md](../os-development/resilience.md)'s restart goal into practice
|
||||||
for drivers.
|
for drivers.
|
||||||
|
|
||||||
How processes stop, reload, and report their deaths is deliberately **not** in this
|
How processes stop, reload, and report their deaths is deliberately **not** in this
|
||||||
document: that is the universal lifecycle every danos process speaks —
|
document: that is the universal lifecycle every danos process speaks —
|
||||||
[process-lifecycle.md](../os-development-guide/process-lifecycle.md), signals over IPC and the stable
|
[process-lifecycle.md](../os-development/process-lifecycle.md), signals over IPC and the stable
|
||||||
`process` interface. The device manager is that design's first serious
|
`process` interface. The device manager is that design's first serious
|
||||||
customer, not its owner. Its own protocol contains nothing lifecycle-shaped; a
|
customer, not its owner. Its own protocol contains nothing lifecycle-shaped; a
|
||||||
driver is stopped, health-checked, and buried exactly like any other process.
|
driver is stopped, health-checked, and buried exactly like any other process.
|
||||||
@@ -35,7 +35,7 @@ The device tree is two things fused: *information* (what exists, how it nests) a
|
|||||||
claims, resource containment on `device_register`, the
|
claims, resource containment on `device_register`, the
|
||||||
`mmio_map`/`irq_bind`/`msi_bind` gates — and **cleans all of it up when a process
|
`mmio_map`/`irq_bind`/`msi_bind` gates — and **cleans all of it up when a process
|
||||||
dies** (settled; it is increment 1 of
|
dies** (settled; it is increment 1 of
|
||||||
[process-lifecycle.md](../os-development-guide/process-lifecycle.md)). The three invariants in
|
[process-lifecycle.md](../os-development/process-lifecycle.md)). The three invariants in
|
||||||
[driver-model.md](driver-model.md) stay exactly where they are. A device manager
|
[driver-model.md](driver-model.md) stay exactly where they are. A device manager
|
||||||
that could mint MMIO mappings by its own say-so would be a second kernel, and a
|
that could mint MMIO mappings by its own say-so would be a second kernel, and a
|
||||||
buggy one would un-earn everything the microkernel bought.
|
buggy one would un-earn everything the microkernel bought.
|
||||||
@@ -51,7 +51,7 @@ enumeration is a **pci-bus driver**: the manager spawns it against the host brid
|
|||||||
like any bus reports children. ACPI becomes an **acpi service** that interprets the
|
like any bus reports children. ACPI becomes an **acpi service** that interprets the
|
||||||
tables and reports the namespace. The manager only orchestrates and merges. Moving
|
tables and reports the namespace. The manager only orchestrates and merges. Moving
|
||||||
AML interpretation out of ring 0 is its own project on its own track; nothing here
|
AML interpretation out of ring 0 is its own project on its own track; nothing here
|
||||||
depends on when it lands. (It landed: [discovery.md](../os-development-guide/discovery.md), M19–M20.)
|
depends on when it lands. (It landed: [discovery.md](../os-development/discovery.md), M19–M20.)
|
||||||
|
|
||||||
`device_register` is **idempotent on exact match**: a re-registration with an
|
`device_register` is **idempotent on exact match**: a re-registration with an
|
||||||
identical (parent, class, identity, resources) tuple returns the existing id
|
identical (parent, class, identity, resources) tuple returns the existing id
|
||||||
@@ -82,7 +82,7 @@ one world.
|
|||||||
deadline means wrong binary, wrong protocol version, or wedged before main — apply
|
deadline means wrong binary, wrong protocol version, or wedged before main — apply
|
||||||
the stop sequence and the restart policy. Everything else lifecycle-shaped
|
the stop sequence and the restart policy. Everything else lifecycle-shaped
|
||||||
(terminate, the common `ping` liveness call, exit reasons) arrives through
|
(terminate, the common `ping` liveness call, exit reasons) arrives through
|
||||||
[process-lifecycle.md](../os-development-guide/process-lifecycle.md)'s vocabulary, not this protocol.
|
[process-lifecycle.md](../os-development/process-lifecycle.md)'s vocabulary, not this protocol.
|
||||||
|
|
||||||
Assignment stays argv (`usb-xhci-bus <device id>`) for now — simple, and it works.
|
Assignment stays argv (`usb-xhci-bus <device id>`) for now — simple, and it works.
|
||||||
The step after `hello` exists is delegation: the manager claims (or is granted) the
|
The step after `hello` exists is delegation: the manager claims (or is granted) the
|
||||||
@@ -97,7 +97,7 @@ from usb-ids.zig — each bus's native language, decoded by the shared ids modul
|
|||||||
Every driver is spawned with the manager's exit endpoint (`spawnSupervised` — built).
|
Every driver is spawned with the manager's exit endpoint (`spawnSupervised` — built).
|
||||||
On a death notification:
|
On a death notification:
|
||||||
|
|
||||||
1. **Read the reason** ([process-lifecycle.md](../os-development-guide/process-lifecycle.md) increment 2).
|
1. **Read the reason** ([process-lifecycle.md](../os-development/process-lifecycle.md) increment 2).
|
||||||
Clean exit → it meant to; don't restart. Fault or missed `hello` deadline →
|
Clean exit → it meant to; don't restart. Fault or missed `hello` deadline →
|
||||||
restart with **backoff**, and a crash-loop cap (three fast deaths → mark failed,
|
restart with **backoff**, and a crash-loop cap (three fast deaths → mark failed,
|
||||||
stop respawning, log loudly; a later `reload` to the manager can retry).
|
stop respawning, log loudly; a later `reload` to the manager can retry).
|
||||||
@@ -136,7 +136,7 @@ way.
|
|||||||
## Increments
|
## Increments
|
||||||
|
|
||||||
Increments 1–4 are the lifecycle prerequisites and live in
|
Increments 1–4 are the lifecycle prerequisites and live in
|
||||||
[process-lifecycle.md](../os-development-guide/process-lifecycle.md) (claim cleanup on death, exit reasons,
|
[process-lifecycle.md](../os-development/process-lifecycle.md) (claim cleanup on death, exit reasons,
|
||||||
published exit events, signals + `process`). On top of those:
|
published exit events, signals + `process`). On top of those:
|
||||||
|
|
||||||
5. **device-manager-protocol**: `hello`, supervised spawn with restart policy;
|
5. **device-manager-protocol**: `hello`, supervised spawn with restart policy;
|
||||||
@@ -147,7 +147,7 @@ published exit events, signals + `process`). On top of those:
|
|||||||
to a manager-internal seam.
|
to a manager-internal seam.
|
||||||
8. **Discovery migration** — DONE (M19–M20, 2026-07-13): enumeration moved to
|
8. **Discovery migration** — DONE (M19–M20, 2026-07-13): enumeration moved to
|
||||||
ring 3 as swappable per-firmware discoverers — the pci-bus driver (M19) then
|
ring 3 as swappable per-firmware discoverers — the pci-bus driver (M19) then
|
||||||
the acpi service (M20), see [discovery.md](../os-development-guide/discovery.md); of the enumerable
|
the acpi service (M20), see [discovery.md](../os-development/discovery.md); of the enumerable
|
||||||
devices, the kernel seeds only the host bridge and the acpi-tables node (the
|
devices, the kernel seeds only the host bridge and the acpi-tables node (the
|
||||||
non-enumerable platform nodes — processors, interrupt controllers, the HPET,
|
non-enumerable platform nodes — processors, interrupt controllers, the HPET,
|
||||||
the loader's framebuffer — stay kernel-seeded too). Matching moved with it:
|
the loader's framebuffer — stay kernel-seeded too). Matching moved with it:
|
||||||
+1
-1
@@ -144,4 +144,4 @@ path in VMs**, where danos development happens. The framebuffer floor never goes
|
|||||||
- [display.md](display.md) — v1: the compositor, the GOP-vs-device split, the WC discipline.
|
- [display.md](display.md) — v1: the compositor, the GOP-vs-device split, the WC discipline.
|
||||||
- [display-v2-plan.md](display-v2-plan.md) — the ordered build-out.
|
- [display-v2-plan.md](display-v2-plan.md) — the ordered build-out.
|
||||||
- [driver-model.md](driver-model.md) — claim / `mmio_map` / MSI / capability passing (M13).
|
- [driver-model.md](driver-model.md) — claim / `mmio_map` / MSI / capability passing (M13).
|
||||||
- [resilience.md](../os-development-guide/resilience.md) — the restart machinery the hot-attach leans on.
|
- [resilience.md](../os-development/resilience.md) — the restart machinery the hot-attach leans on.
|
||||||
+12
-12
@@ -1,6 +1,6 @@
|
|||||||
# The display service: a framebuffer compositor
|
# The display service: a framebuffer compositor
|
||||||
|
|
||||||
The [framebuffer](../os-development-guide/framebuffer.md) the loader hands over is a flat block of pixel
|
The [framebuffer](../os-development/framebuffer.md) the loader hands over is a flat block of pixel
|
||||||
memory, and the kernel's [bootstrap console](../../system/kernel/console.zig) draws text
|
memory, and the kernel's [bootstrap console](../../system/kernel/console.zig) draws text
|
||||||
into it directly. That console is a stop-gap. The **display service**
|
into it directly. That console is a stop-gap. The **display service**
|
||||||
(`system/services/display/`) is the real thing: an ordinary ring-3 process that *owns*
|
(`system/services/display/`) is the real thing: an ordinary ring-3 process that *owns*
|
||||||
@@ -22,7 +22,7 @@ which one you're holding decides what you can do.
|
|||||||
linear framebuffer pointer and can set video modes — but only until
|
linear framebuffer pointer and can set video modes — but only until
|
||||||
`ExitBootServices`. The loader already leans on this: [`queryFramebuffer`](../../boot/efi.zig)
|
`ExitBootServices`. The loader already leans on this: [`queryFramebuffer`](../../boot/efi.zig)
|
||||||
reads the monitor's EDID, picks the native mode, and calls `set_mode` **before**
|
reads the monitor's EDID, picks the native mode, and calls `set_mode` **before**
|
||||||
exiting ([gop.md](../os-development-guide/gop.md)). Once the kernel runs, GOP is **gone** — no `set_mode`, no
|
exiting ([gop.md](../os-development/gop.md)). Once the kernel runs, GOP is **gone** — no `set_mode`, no
|
||||||
mode list, no EDID. What survives is the frozen snapshot in
|
mode list, no EDID. What survives is the frozen snapshot in
|
||||||
[`BootInformation.framebuffer`](../../system/boot-handoff.zig): `{base, width, height,
|
[`BootInformation.framebuffer`](../../system/boot-handoff.zig): `{base, width, height,
|
||||||
pitch, format, refresh_hz}`, and nothing more.
|
pitch, format, refresh_hz}`, and nothing more.
|
||||||
@@ -66,7 +66,7 @@ rest of the system hasn't had to face:
|
|||||||
[`console.zig`](../../system/kernel/console.zig). It is *not* a
|
[`console.zig`](../../system/kernel/console.zig). It is *not* a
|
||||||
[devices-broker](../../system/kernel/devices-broker.zig) node, so
|
[devices-broker](../../system/kernel/devices-broker.zig) node, so
|
||||||
`device.claim`/`mmio_map` cannot reach it, and there is no framebuffer
|
`device.claim`/`mmio_map` cannot reach it, and there is no framebuffer
|
||||||
[syscall](../os-development-guide/syscall.md). A user-space display service needs a **new mechanism just to
|
[syscall](../os-development/syscall.md). A user-space display service needs a **new mechanism just to
|
||||||
touch the pixels**. (See "The handoff" below — this is built.)
|
touch the pixels**. (See "The handoff" below — this is built.)
|
||||||
|
|
||||||
2. **danos had no cross-process shared memory.** At v1 the memory syscalls were `mmap`
|
2. **danos had no cross-process shared memory.** At v1 the memory syscalls were `mmap`
|
||||||
@@ -119,7 +119,7 @@ second backend or a second monitor appears; until then it is complexity with no
|
|||||||
|
|
||||||
The framebuffer crosses into user space through the machinery that already exists for
|
The framebuffer crosses into user space through the machinery that already exists for
|
||||||
every other device, rather than a bespoke syscall — so it inherits ownership,
|
every other device, rather than a bespoke syscall — so it inherits ownership,
|
||||||
release-on-death, and re-claim-on-restart for free (the [resilience](../os-development-guide/resilience.md)
|
release-on-death, and re-claim-on-restart for free (the [resilience](../os-development/resilience.md)
|
||||||
story: a crashed display service returns the LFB to the kernel, and its restart
|
story: a crashed display service returns the LFB to the kernel, and its restart
|
||||||
re-claims it).
|
re-claims it).
|
||||||
|
|
||||||
@@ -160,8 +160,8 @@ Two buffers, with deliberately different memory types:
|
|||||||
|
|
||||||
So a frame is: compose every dirty layer into the cacheable back buffer, then **present**
|
So a frame is: compose every dirty layer into the cacheable back buffer, then **present**
|
||||||
— copy the changed regions back→front in sequential, WC-friendly writes. Two details the
|
— copy the changed regions back→front in sequential, WC-friendly writes. Two details the
|
||||||
[framebuffer](../os-development-guide/framebuffer.md) note already establishes carry over: step rows by `pitch`,
|
[framebuffer](../os-development/framebuffer.md) note already establishes carry over: step rows by `pitch`,
|
||||||
not `width*4`; and handle both `rgbx` and `bgrx` [pixel formats](../os-development-guide/gop.md).
|
not `width*4`; and handle both `rgbx` and `bgrx` [pixel formats](../os-development/gop.md).
|
||||||
|
|
||||||
## Flicker vs. tearing — what double buffering does and doesn't buy
|
## Flicker vs. tearing — what double buffering does and doesn't buy
|
||||||
|
|
||||||
@@ -234,7 +234,7 @@ client module, as with every other danos service.
|
|||||||
|
|
||||||
The compositor is the single owner of the framebuffer — only the main `service.run` loop
|
The compositor is the single owner of the framebuffer — only the main `service.run` loop
|
||||||
touches the backend and the layer stack. Tracking the mouse without breaking that
|
touches the backend and the layer stack. Tracking the mouse without breaking that
|
||||||
ownership is the display's first use of [threads](../os-development-guide/threading.md): the service is built
|
ownership is the display's first use of [threads](../os-development/threading.md): the service is built
|
||||||
multi-threaded (`addThreadedUserBinary`) and, at startup, spawns a **mouse-listener
|
multi-threaded (`addThreadedUserBinary`) and, at startup, spawns a **mouse-listener
|
||||||
thread** beside the compositor loop.
|
thread** beside the compositor loop.
|
||||||
|
|
||||||
@@ -242,7 +242,7 @@ thread** beside the compositor loop.
|
|||||||
(`input.subscribeMouse()`), accumulates the relative `dx`/`dy` motion into an absolute
|
(`input.subscribeMouse()`), accumulates the relative `dx`/`dy` motion into an absolute
|
||||||
cursor position clamped to the screen, and hands it to the compositor. It never touches
|
cursor position clamped to the screen, and hands it to the compositor. It never touches
|
||||||
the compositor — so no lock guards the framebuffer. A parked `next()` leaves its core
|
the compositor — so no lock guards the framebuffer. A parked `next()` leaves its core
|
||||||
free to halt ([halting.md](../os-development-guide/halting.md)).
|
free to halt ([halting.md](../os-development/halting.md)).
|
||||||
- **The channel.** A single-slot *latest-value* cell (`CursorChannel`) guarded by a
|
- **The channel.** A single-slot *latest-value* cell (`CursorChannel`) guarded by a
|
||||||
`Thread.Mutex`: the renderer wants where the cursor *is now*, not a replay of
|
`Thread.Mutex`: the renderer wants where the cursor *is now*, not a replay of
|
||||||
every delta, so a new position overwrites the old. The listener also **pokes** the
|
every delta, so a new position overwrites the old. The listener also **pokes** the
|
||||||
@@ -254,13 +254,13 @@ thread** beside the compositor loop.
|
|||||||
which is just a top-z compositor layer — with the existing `configure` + `present` path
|
which is just a top-z compositor layer — with the existing `configure` + `present` path
|
||||||
(it damages the old and new footprints, so only those two rectangles repaint).
|
(it damages the old and new footprints, so only those two rectangles repaint).
|
||||||
|
|
||||||
Two threading facts shape this (both in [threading.md](../os-development-guide/threading.md)). IPC **handles do
|
Two threading facts shape this (both in [threading.md](../os-development/threading.md)). IPC **handles do
|
||||||
not cross threads**, so the listener can't reuse the main loop's endpoint handle — it
|
not cross threads**, so the listener can't reuse the main loop's endpoint handle — it
|
||||||
`ipc.lookup(.display)`s its *own* handle to the same endpoint to poke through. And a
|
`ipc.lookup(.display)`s its *own* handle to the same endpoint to poke through. And a
|
||||||
multi-threaded service doing concurrent IPC is why the kernel's endpoint-create / register
|
multi-threaded service doing concurrent IPC is why the kernel's endpoint-create / register
|
||||||
/ lookup syscalls now serialize under the big kernel lock. Shared fate applies: a fault in
|
/ lookup syscalls now serialize under the big kernel lock. Shared fate applies: a fault in
|
||||||
the listener takes the whole display down, and the supervisor restarts the process
|
the listener takes the whole display down, and the supervisor restarts the process
|
||||||
([resilience.md](../os-development-guide/resilience.md)).
|
([resilience.md](../os-development/resilience.md)).
|
||||||
|
|
||||||
## What v1 does not do (and why that's fine)
|
## What v1 does not do (and why that's fine)
|
||||||
|
|
||||||
@@ -317,8 +317,8 @@ packing are additionally covered by pure host unit tests under `zig build test`.
|
|||||||
|
|
||||||
## See also
|
## See also
|
||||||
|
|
||||||
- [framebuffer.md](../os-development-guide/framebuffer.md) — the linear framebuffer, pitch vs. width, `volatile`.
|
- [framebuffer.md](../os-development/framebuffer.md) — the linear framebuffer, pitch vs. width, `volatile`.
|
||||||
- [gop.md](../os-development-guide/gop.md) — GOP, and why only linear RGBX/BGRX modes are paintable.
|
- [gop.md](../os-development/gop.md) — GOP, and why only linear RGBX/BGRX modes are paintable.
|
||||||
- [input.md](input.md) — the sibling service; the async `ipc_send` fan-out.
|
- [input.md](input.md) — the sibling service; the async `ipc_send` fan-out.
|
||||||
- [driver-model.md](driver-model.md) — claim / `mmio_map`, capability passing, the trust model.
|
- [driver-model.md](driver-model.md) — claim / `mmio_map`, capability passing, the trust model.
|
||||||
- [device-manager.md](device-manager.md) — matching and supervision (the native backend's route).
|
- [device-manager.md](device-manager.md) — matching and supervision (the native backend's route).
|
||||||
+4
-4
@@ -34,7 +34,7 @@ plain bus driver with no controller — a USB hub — is also a real thing.
|
|||||||
|
|
||||||
danos already has the right central structure. `system/kernel/devices-broker.zig` holds a table of
|
danos already has the right central structure. `system/kernel/devices-broker.zig` holds a table of
|
||||||
`DeviceDescriptor`, each with a parent, a class, and a set of resources. Firmware discovery
|
`DeviceDescriptor`, each with a parent, a class, and a set of resources. Firmware discovery
|
||||||
seeds it ([discovery.md](../os-development-guide/discovery.md)); `device_register` grows it.
|
seeds it ([discovery.md](../os-development/discovery.md)); `device_register` grows it.
|
||||||
|
|
||||||
Three invariants make it a capability system rather than a directory:
|
Three invariants make it a capability system rather than a directory:
|
||||||
|
|
||||||
@@ -199,7 +199,7 @@ class driver, the device manager, or the kernel may share them freely.
|
|||||||
`system_spawn(name, arguments)` loads a binary bundled in the initial-ramdisk as a
|
`system_spawn(name, arguments)` loads a binary bundled in the initial-ramdisk as a
|
||||||
fresh ring-3 process; `name` becomes the child's argv[0] and the optional
|
fresh ring-3 process; `name` becomes the child's argv[0] and the optional
|
||||||
NUL-separated `arguments` blob its argv[1..], delivered on a SysV entry stack
|
NUL-separated `arguments` blob its argv[1..], delivered on a SysV entry stack
|
||||||
([sysv.md](../os-development-guide/sysv.md)). This is what
|
([sysv.md](../os-development/sysv.md)). This is what
|
||||||
turned the device manager from "log the match" into "run the driver": the kernel now
|
turned the device manager from "log the match" into "run the driver": the kernel now
|
||||||
spawns only `init`, `init` spawns the services, and the **device-manager** discovers
|
spawns only `init`, `init` spawns the services, and the **device-manager** discovers
|
||||||
the hardware and spawns each driver ([drivers.md](drivers.md)). Ungated for now — a
|
the hardware and spawns each driver ([drivers.md](drivers.md)). Ungated for now — a
|
||||||
@@ -398,6 +398,6 @@ from hand-rolling `*volatile` and getting ARM wrong.
|
|||||||
## See also
|
## See also
|
||||||
|
|
||||||
- [drivers.md](drivers.md) — how to write one, concretely.
|
- [drivers.md](drivers.md) — how to write one, concretely.
|
||||||
- [discovery.md](../os-development-guide/discovery.md) / [acpi.md](../os-development-guide/acpi.md) — where the device table comes from.
|
- [discovery.md](../os-development/discovery.md) / [acpi.md](../os-development/acpi.md) — where the device table comes from.
|
||||||
- [ipc.md](ipc.md) — endpoints, badges, and the notification path an IRQ arrives on.
|
- [ipc.md](ipc.md) — endpoints, badges, and the notification path an IRQ arrives on.
|
||||||
- [resilience.md](../os-development-guide/resilience.md) — restart, the reason any of this is worth the trouble.
|
- [resilience.md](../os-development/resilience.md) — restart, the reason any of this is worth the trouble.
|
||||||
+5
-5
@@ -4,13 +4,13 @@ In a monolithic kernel a driver is a function call away from everything: it runs
|
|||||||
ring 0, dereferences any physical address, and its interrupt handler *is* the ISR. In
|
ring 0, dereferences any physical address, and its interrupt handler *is* the ISR. In
|
||||||
danos a driver is **an ordinary ring-3 process**. It has its own address space, it
|
danos a driver is **an ordinary ring-3 process**. It has its own address space, it
|
||||||
can crash without taking the kernel with it, and — the point of this document — it
|
can crash without taking the kernel with it, and — the point of this document — it
|
||||||
can be restarted ([resilience](../os-development-guide/resilience.md)).
|
can be restarted ([resilience](../os-development/resilience.md)).
|
||||||
|
|
||||||
That leaves three questions the kernel has to answer, because a process can't answer
|
That leaves three questions the kernel has to answer, because a process can't answer
|
||||||
them for itself:
|
them for itself:
|
||||||
|
|
||||||
1. **What hardware exists?** → `device_enumerate`, over the device table discovery built
|
1. **What hardware exists?** → `device_enumerate`, over the device table discovery built
|
||||||
([discovery](../os-development-guide/discovery.md), [acpi](../os-development-guide/acpi.md)).
|
([discovery](../os-development/discovery.md), [acpi](../os-development/acpi.md)).
|
||||||
2. **How do I touch its registers?** → `device_claim` + `mmio_map`: the kernel maps the
|
2. **How do I touch its registers?** → `device_claim` + `mmio_map`: the kernel maps the
|
||||||
device's physical MMIO window into your address space, and from then on it's plain
|
device's physical MMIO window into your address space, and from then on it's plain
|
||||||
memory. No syscall per register access.
|
memory. No syscall per register access.
|
||||||
@@ -51,7 +51,7 @@ the optional arguments its argv[1..], on a SysV entry stack, see sysv.md). Every
|
|||||||
is the **driver supervisor**. It does the three steps a monolithic kernel would do in
|
is the **driver supervisor**. It does the three steps a monolithic kernel would do in
|
||||||
its probe path, entirely from ring 3:
|
its probe path, entirely from ring 3:
|
||||||
1. **Discover** — `device_enumerate` snapshots the device table the kernel built from
|
1. **Discover** — `device_enumerate` snapshots the device table the kernel built from
|
||||||
ACPI/PCI ([discovery](../os-development-guide/discovery.md)).
|
ACPI/PCI ([discovery](../os-development/discovery.md)).
|
||||||
2. **Match** — for each device it looks up a driver. The match policy is code, a few
|
2. **Match** — for each device it looks up a driver. The match policy is code, a few
|
||||||
small per-bus tables: from the boot snapshot only the PCI host bridge matches
|
small per-bus tables: from the boot snapshot only the PCI host bridge matches
|
||||||
(→ `pci-bus`); everything else arrives later as bus reports and matches on
|
(→ `pci-bus`); everything else arrives later as bus reports and matches on
|
||||||
@@ -293,7 +293,7 @@ the device's `io_port` resource — direct ring-3 `in`/`out` is still a #GP, so
|
|||||||
uncacheable, physical address exposed), **memory barriers** (`library/device/mmio`'s
|
uncacheable, physical address exposed), **memory barriers** (`library/device/mmio`'s
|
||||||
`memoryBarrier`/`readMemoryBarrier`/`writeMemoryBarrier`, imported as the `mmio` module), **fault isolation** (a ring-3 fault kills only the faulting
|
`memoryBarrier`/`readMemoryBarrier`/`writeMemoryBarrier`, imported as the `mmio` module), **fault isolation** (a ring-3 fault kills only the faulting
|
||||||
process — `killCurrentProcess` — and the machine keeps running,
|
process — `killCurrentProcess` — and the machine keeps running,
|
||||||
[resilience](../os-development-guide/resilience.md)), and **reclaim + restart on death** (every path out of a
|
[resilience](../os-development/resilience.md)), and **reclaim + restart on death** (every path out of a
|
||||||
process releases its claims and IRQ/MSI bindings — `releaseAllOwnedBy`,
|
process releases its claims and IRQ/MSI bindings — `releaseAllOwnedBy`,
|
||||||
`irq.releaseOwner` — and the device manager respawns the driver with backoff,
|
`irq.releaseOwner` — and the device manager respawns the driver with backoff,
|
||||||
[device-manager.md](device-manager.md)). What remains:
|
[device-manager.md](device-manager.md)). What remains:
|
||||||
@@ -394,7 +394,7 @@ Claiming and mapping is half of being a danos driver; the other half is the
|
|||||||
- Build on `service.run` — one replyWait loop folding protocol
|
- Build on `service.run` — one replyWait loop folding protocol
|
||||||
requests, signals, and notifications into callbacks. The harness answers the
|
requests, signals, and notifications into callbacks. The harness answers the
|
||||||
universal zero-length ping and turns `terminate` into a clean exit for you
|
universal zero-length ping and turns `terminate` into a clean exit for you
|
||||||
([process-lifecycle.md](../os-development-guide/process-lifecycle.md)).
|
([process-lifecycle.md](../os-development/process-lifecycle.md)).
|
||||||
- A driver spawned with an assignment (its device id as argv[1]) sends the
|
- A driver spawned with an assignment (its device id as argv[1]) sends the
|
||||||
versioned `hello` to the device manager inside the deadline, and a **bus**
|
versioned `hello` to the device manager inside the deadline, and a **bus**
|
||||||
driver reports what it discovers with `child_added`
|
driver reports what it discovers with `child_added`
|
||||||
+1
-1
@@ -160,5 +160,5 @@ serial line names the class received, so the log shows all three arriving on one
|
|||||||
## See also
|
## See also
|
||||||
|
|
||||||
- [ipc.md](ipc.md) — the synchronous rendezvous and the notification path `ipc_send` extends.
|
- [ipc.md](ipc.md) — the synchronous rendezvous and the notification path `ipc_send` extends.
|
||||||
- [syscall.md](../os-development-guide/syscall.md) — the system-call surface, including `ipc_send`.
|
- [syscall.md](../os-development/syscall.md) — the system-call surface, including `ipc_send`.
|
||||||
- [driver-model.md](driver-model.md) — class drivers, capability passing (M13), the trust model.
|
- [driver-model.md](driver-model.md) — class drivers, capability passing (M13), the trust model.
|
||||||
@@ -18,7 +18,7 @@ There are two layers, built a milestone apart:
|
|||||||
|
|
||||||
The first form is a **bounded blocking channel** (`system/kernel/ipc.zig`): a fixed-size
|
The first form is a **bounded blocking channel** (`system/kernel/ipc.zig`): a fixed-size
|
||||||
ring buffer of messages with a producer/consumer rendezvous, built on the
|
ring buffer of messages with a producer/consumer rendezvous, built on the
|
||||||
scheduler's [wait queues](../os-development-guide/scheduling.md).
|
scheduler's [wait queues](../os-development/scheduling.md).
|
||||||
|
|
||||||
`Channel(T, capacity)` is generic over the message type and buffer size. It holds a
|
`Channel(T, capacity)` is generic over the message type and buffer size. It holds a
|
||||||
ring buffer, a count, and two wait queues:
|
ring buffer, a count, and two wait queues:
|
||||||
@@ -37,7 +37,7 @@ Two details make it correct:
|
|||||||
rather than assuming the slot is still available — another waiter may have taken
|
rather than assuming the slot is still available — another waiter may have taken
|
||||||
it first. This is the standard guard against spurious or racing wakeups.
|
it first. This is the standard guard against spurious or racing wakeups.
|
||||||
- **One critical section.** `send`/`receive` run under the [big kernel
|
- **One critical section.** `send`/`receive` run under the [big kernel
|
||||||
lock](../os-development-guide/smp.md) (`sync.enter` / `sync.leave`), which disables interrupts on this
|
lock](../os-development/smp.md) (`sync.enter` / `sync.leave`), which disables interrupts on this
|
||||||
core *and* takes the kernel's one spinlock — since SMP, the interrupt flag alone
|
core *and* takes the kernel's one spinlock — since SMP, the interrupt flag alone
|
||||||
is not atomicity, because `cli` on one core does nothing to another. So checking
|
is not atomicity, because `cli` on one core does nothing to another. So checking
|
||||||
the condition and committing the block/enqueue happen atomically both with respect
|
the condition and committing the block/enqueue happen atomically both with respect
|
||||||
@@ -114,7 +114,7 @@ This is what makes a user-space driver possible at all, and it's the subject of
|
|||||||
|
|
||||||
## Lifecycle conventions over IPC (M17)
|
## Lifecycle conventions over IPC (M17)
|
||||||
|
|
||||||
Three conventions from [process-lifecycle.md](../os-development-guide/process-lifecycle.md) ride the
|
Three conventions from [process-lifecycle.md](../os-development/process-lifecycle.md) ride the
|
||||||
notification mechanism:
|
notification mechanism:
|
||||||
|
|
||||||
- **Signals** arrive as notifications on the endpoint a process nominated with
|
- **Signals** arrive as notifications on the endpoint a process nominated with
|
||||||
@@ -49,7 +49,7 @@ addressed by device id. `/dev` is the much smaller set of devices that have a dr
|
|||||||
willing to serve them, addressed by name.
|
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
|
A device node is not a file the VFS can read. The bytes live in a driver process
|
||||||
([drivers.md](../device-driver-development-guide/drivers.md)), so opening a `/dev` name has to resolve to that driver's
|
([drivers.md](../device-driver-development/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. Resolve-to-endpoint
|
IPC endpoint, and subsequent reads and writes are calls against it. Resolve-to-endpoint
|
||||||
is exactly what the kernel's `fs_resolve` already does for any mounted backend, and
|
is exactly what the kernel's `fs_resolve` already does for any mounted backend, and
|
||||||
`FileStatus.kind` is the field that marks a device node; **what is not implemented today
|
`FileStatus.kind` is the field that marks a device node; **what is not implemented today
|
||||||
@@ -92,7 +92,7 @@ descriptor ring and left to read and write memory on its own. That ring is exact
|
|||||||
**`dma_alloc`** now provides — physically contiguous, pinned, uncacheable, with its
|
**`dma_alloc`** now provides — physically contiguous, pinned, uncacheable, with its
|
||||||
physical address disclosed — and **`/lib/device/mmio`**'s barriers order the descriptor writes
|
physical address disclosed — and **`/lib/device/mmio`**'s barriers order the descriptor writes
|
||||||
against the doorbell, and **`msi_bind`** delivers completions. So an AHCI or NVMe driver
|
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](../device-driver-development-guide/driver-model.md); the earlier
|
can be written today (the M14/M15 work in [driver-model.md](../device-driver-development/driver-model.md); the earlier
|
||||||
"cannot host a block driver at all" is no longer true).
|
"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
|
What is *not* yet true is that it is safe. A device programmed with an arbitrary physical
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
> backend, unchanged. The Zig source of truth is `library/protocol/vfs/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
|
> (the `vfs-protocol` module), whose unit test pins a sample of the sizes
|
||||||
> and values below. This page is the **language-neutral wire specification**
|
> and values below. This page is the **language-neutral wire specification**
|
||||||
> of that contract — what a Rust or C client implements ([vdso.md](../os-development-guide/vdso.md)
|
> of that contract — what a Rust or C client implements ([vdso.md](../os-development/vdso.md)
|
||||||
> explains why the IPC protocols, not the syscall numbers, are danos's
|
> explains why the IPC protocols, not the syscall numbers, are danos's
|
||||||
> public ABI).
|
> public ABI).
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
# OS Developer Guide
|
|
||||||
|
|
||||||
This document is for those who need to understand the architectural decisions behind the OS.
|
|
||||||
|
|
||||||
## Written in Zig?
|
|
||||||
|
|
||||||
The os was initially written in zig because it has excellent support for EFI. With zig, we could forgo using a third party bootloader, reducing the time to boot up the kernel. Following the "Zen of Zig", helped to produce the most readable codebase for an operating system ever created. So those, new to OS development could quickly get up to speed.
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# OS Development
|
||||||
|
|
||||||
|
This document explains the architectural decisions behind the operating system.
|
||||||
|
|
||||||
|
## Written in Zig?
|
||||||
|
|
||||||
|
The OS is written in Zig because it has excellent EFI support, so the OS boots quickly without a third-party bootloader.
|
||||||
|
|
||||||
|
Zig comes batteries included for systems work — cross-compilation, a build system, and a test runner are all part of the toolchain. Building with `-Doptimize=ReleaseSafe` keeps runtime safety checks on in the shipped kernel, which removes entire classes of bugs. The built-in test suite, combined with a QEMU integration harness, means every feature is proven to work, before it is shipped.
|
||||||
|
|
||||||
|
The codebase of the OS prioritizes readability. The aim is a codebase where someone new to OS development can find their way around without a guide.
|
||||||
|
|
||||||
|
## A microkernel?
|
||||||
|
|
||||||
|
The kernel is a thin layer: it schedules processes and manages memory. Everything else — drivers, file systems, the display — runs in user space as separate, isolated processes.
|
||||||
|
|
||||||
|
The payoff is resilience. When a driver crashes, it doesn't take the OS down with it; it gets restarted. That makes this an ideal environment for *developing* an operating system, because a buggy driver is an ordinary bug: patch it, restart the service, and keep going.
|
||||||
|
|
||||||
|
There is a security benefit too. Processes are isolated and talk over Inter-Process Communication (IPC) channels, so compromising one service doesn't hand an attacker the whole machine. Vulnerabilities tend to stay contained in the process they started in.
|
||||||
|
|
||||||
|
Other operating systems choose to pack all of these duties into one binary as a Monolithic kernel, mostly for performance: a function call inside the kernel is faster than passing a message between isolated processes. That cost is real — an IPC round-trip is a few microseconds where a function call is nanoseconds — but it is also workload-shaped. Compute-bound programs don't notice it at all. For bulk data like file contents and pixels, the design moves data through shared memory and DMA so it is copied once, the same as a monolithic kernel; only small control messages cross the IPC boundary. What remains is the per-message cost on chatty paths, and the scheduler and memory management are designed to keep that small.
|
||||||
|
|
||||||
|
## Private ABI
|
||||||
|
|
||||||
|
The syscall layer is private. The numbers and structures in `abi.zig` are an internal detail shared between the kernel and the system's own libraries, and they are free to change between builds.
|
||||||
|
|
||||||
|
The public boundary sits one level up: the [vDSO](vdso.md) that programs call into, and the documented IPC protocols such as the [VFS protocol](../file-system-development/vfs-protocol.md). Programs that stick to those interfaces keep working while the kernel rearranges itself underneath. This is the opposite of the Linux approach, where raw syscall numbers are frozen forever; here, stability is promised at the library and protocol level, and nowhere below it.
|
||||||
|
|
||||||
|
## Steal the best bits and dump the legacy
|
||||||
|
|
||||||
|
The OS is Unix-like, but selectively. It borrows the ideas that have aged well — everything is a file, small services composed over clean interfaces — and skips the parts of POSIX that have caused decades of headaches.
|
||||||
|
|
||||||
|
Some concrete choices:
|
||||||
|
|
||||||
|
- **`spawn`, not `fork`.** Creating a process starts a fresh program and returns the child's id. There is no clone-the-whole-address-space-then-immediately-throw-it-away dance, and none of the subtle state-inheritance bugs that come with it.
|
||||||
|
- **Time is a syscall.** The kernel owns the clock and timers directly. There is no time daemon to keep alive and no ambiguity about where the truth lives.
|
||||||
|
- **Lifecycle events arrive as messages.** A supervisor learns that a child exited through an IPC message on an endpoint it already owns — delivered like any other message, not as an interrupt that can fire between any two instructions.
|
||||||
|
|
||||||
|
The test for keeping an idea is simple: does it still pull its weight, or is it only there because it was there in 1979?
|
||||||
@@ -75,7 +75,7 @@ There are really two independent questions, and it's worth not conflating them:
|
|||||||
- **`system/kernel/architecture/x86_64/paging.zig`** — the kernel's page tables and address-space
|
- **`system/kernel/architecture/x86_64/paging.zig`** — the kernel's page tables and address-space
|
||||||
management (see [paging.md](paging.md)).
|
management (see [paging.md](paging.md)).
|
||||||
- **`system/kernel/architecture/x86_64/apic.zig`** / **`ioapic.zig`** — the Local APIC, its timer,
|
- **`system/kernel/architecture/x86_64/apic.zig`** / **`ioapic.zig`** — the Local APIC, its timer,
|
||||||
and the I/O APIC for device interrupts (see [device-interrupts.md](../device-driver-development-guide/device-interrupts.md)).
|
and the I/O APIC for device interrupts (see [device-interrupts.md](../device-driver-development/device-interrupts.md)).
|
||||||
- **`system/kernel/architecture/x86_64/serial.zig`** / **`io.zig`** — the COM1 UART (the kernel's
|
- **`system/kernel/architecture/x86_64/serial.zig`** / **`io.zig`** — the COM1 UART (the kernel's
|
||||||
machine-readable log channel, see [testing.md](../testing.md)) and the shared port-I/O + MSR primitives.
|
machine-readable log channel, see [testing.md](../testing.md)) and the shared port-I/O + MSR primitives.
|
||||||
- **`system/kernel/architecture/x86_64/smp.zig`** / **`per-cpu.zig`** — application-processor bring-up
|
- **`system/kernel/architecture/x86_64/smp.zig`** / **`per-cpu.zig`** — application-processor bring-up
|
||||||
@@ -132,7 +132,7 @@ when*:
|
|||||||
- **User-space enumeration: a device-manager server.** Everything else — PCI devices,
|
- **User-space enumeration: a device-manager server.** Everything else — PCI devices,
|
||||||
peripherals — is parsed (or queried from the kernel's parse) by a privileged
|
peripherals — is parsed (or queried from the kernel's parse) by a privileged
|
||||||
user-space server that hands each driver process its MMIO regions and IRQ rights
|
user-space server that hands each driver process its MMIO regions and IRQ rights
|
||||||
over [IPC](../device-driver-development-guide/ipc.md). Combined with **interrupts-as-messages** (an IRQ delivered to a
|
over [IPC](../device-driver-development/ipc.md). Combined with **interrupts-as-messages** (an IRQ delivered to a
|
||||||
driver as a message on a channel — a natural extension of the wait queues and
|
driver as a message on a channel — a natural extension of the wait queues and
|
||||||
channels already built), that's what makes drivers genuinely isolated.
|
channels already built), that's what makes drivers genuinely isolated.
|
||||||
|
|
||||||
@@ -144,7 +144,7 @@ slice is unavoidably in-kernel.
|
|||||||
On ARMv8 the generic timer exposes its frequency directly via the `CNTFRQ` register —
|
On ARMv8 the generic timer exposes its frequency directly via the `CNTFRQ` register —
|
||||||
no calibration needed. That's cleaner than the x86 side, where we measure the LAPIC
|
no calibration needed. That's cleaner than the x86 side, where we measure the LAPIC
|
||||||
and TSC against the PIT because nothing tells us their frequency (see
|
and TSC against the PIT because nothing tells us their frequency (see
|
||||||
[device-interrupts.md](../device-driver-development-guide/device-interrupts.md)). Discovery on ARM hands you more for
|
[device-interrupts.md](../device-driver-development/device-interrupts.md)). Discovery on ARM hands you more for
|
||||||
free; discovery on x86 is partly about *finding* what ARM just tells you.
|
free; discovery on x86 is partly about *finding* what ARM just tells you.
|
||||||
|
|
||||||
## Suggested ordering
|
## Suggested ordering
|
||||||
@@ -166,9 +166,9 @@ free; discovery on x86 is partly about *finding* what ARM just tells you.
|
|||||||
- [arm.md](arm.md) — the aarch64 target that forces genuine discovery (DTB, GIC).
|
- [arm.md](arm.md) — the aarch64 target that forces genuine discovery (DTB, GIC).
|
||||||
- [memory-map.md](memory-map.md) — the same loader-captures / kernel-consumes seam,
|
- [memory-map.md](memory-map.md) — the same loader-captures / kernel-consumes seam,
|
||||||
and the note about grabbing the RSDP before `ExitBootServices`.
|
and the note about grabbing the RSDP before `ExitBootServices`.
|
||||||
- [device-interrupts.md](../device-driver-development-guide/device-interrupts.md) — the LAPIC/timer bring-up that
|
- [device-interrupts.md](../device-driver-development/device-interrupts.md) — the LAPIC/timer bring-up that
|
||||||
discovery will eventually feed (IOAPIC, real IRQ routing).
|
discovery will eventually feed (IOAPIC, real IRQ routing).
|
||||||
- [ipc.md](../device-driver-development-guide/ipc.md) — the channels that interrupts-as-messages and the device manager
|
- [ipc.md](../device-driver-development/ipc.md) — the channels that interrupts-as-messages and the device manager
|
||||||
will ride on.
|
will ride on.
|
||||||
- [vision.md](../vision.md) — why drivers belong in isolated user space at all.
|
- [vision.md](../vision.md) — why drivers belong in isolated user space at all.
|
||||||
|
|
||||||
@@ -177,7 +177,7 @@ free; discovery on x86 is partly about *finding* what ARM just tells you.
|
|||||||
The kernel now seeds only the `pci_host_bridge` node (ECAM window, MMIO
|
The kernel now seeds only the `pci_host_bridge` node (ECAM window, MMIO
|
||||||
apertures derived from the memory map's holes, bus range, and the 16-bit I/O
|
apertures derived from the memory map's holes, bus range, and the 16-bit I/O
|
||||||
window). The per-function walk moved to the ring-3 `pci-bus` driver
|
window). The per-function walk moved to the ring-3 `pci-bus` driver
|
||||||
([device-manager.md](../device-driver-development-guide/device-manager.md)): it claims the bridge, repeats the
|
([device-manager.md](../device-driver-development/device-manager.md)): it claims the bridge, repeats the
|
||||||
ECAM scan through its mmio grant, and `device_register`s what it finds, which
|
ECAM scan through its mmio grant, and `device_register`s what it finds, which
|
||||||
the device manager mirrors and matches. The ACPI namespace walk follows in M20;
|
the device manager mirrors and matches. The ACPI namespace walk follows in M20;
|
||||||
the static tables (MADT, HPET, MCFG, FADT + `\\_S5`) stay kernel-side.
|
the static tables (MADT, HPET, MCFG, FADT + `\\_S5`) stay kernel-side.
|
||||||
@@ -190,7 +190,7 @@ for the host bridge, FADT); at this point it also still built the AML namespace
|
|||||||
but only to read the `\\_S5` sleep type for poweroff. (That remnant is gone too:
|
but only to read the `\\_S5` sleep type for poweroff. (That remnant is gone too:
|
||||||
the kernel now runs no AML at all — soft-off belongs to the acpi service, and the
|
the kernel now runs no AML at all — soft-off belongs to the acpi service, and the
|
||||||
kernel keeps only the AML-free reboot path.) Device discovery is the ring-3 **acpi
|
kernel keeps only the AML-free reboot path.) Device discovery is the ring-3 **acpi
|
||||||
service** ([device-manager.md](../device-driver-development-guide/device-manager.md)): it claims the `acpi-tables`
|
service** ([device-manager.md](../device-driver-development/device-manager.md)): it claims the `acpi-tables`
|
||||||
node the kernel publishes (the AML blobs, a broad io_port grant, the SCI),
|
node the kernel publishes (the AML blobs, a broad io_port grant, the SCI),
|
||||||
re-parses the same blobs with the shared AML module, evaluates `_STA`/`_CRS`,
|
re-parses the same blobs with the shared AML module, evaluates `_STA`/`_CRS`,
|
||||||
and registers + reports each `_HID` device — the device manager matches drivers
|
and registers + reports each `_HID` device — the device manager matches drivers
|
||||||
@@ -206,7 +206,7 @@ ring 0.)
|
|||||||
Moving PCI and ACPI enumeration out of ring 0 was not just a relocation — it
|
Moving PCI and ACPI enumeration out of ring 0 was not just a relocation — it
|
||||||
made discovery **firmware-neutral by construction**, which is the whole reason
|
made discovery **firmware-neutral by construction**, which is the whole reason
|
||||||
to do it before the second architecture rather than after. Everything at and
|
to do it before the second architecture rather than after. Everything at and
|
||||||
above the [device-manager](../device-driver-development-guide/device-manager.md) protocol — descriptors,
|
above the [device-manager](../device-driver-development/device-manager.md) protocol — descriptors,
|
||||||
containment, reports, matching, supervision — is generic and may never become
|
containment, reports, matching, supervision — is generic and may never become
|
||||||
x86-specific. Discovery is the single firmware-specific piece, and it is
|
x86-specific. Discovery is the single firmware-specific piece, and it is
|
||||||
isolated as **one swappable process per firmware**:
|
isolated as **one swappable process per firmware**:
|
||||||
@@ -136,7 +136,7 @@ Both items originally deferred here have landed:
|
|||||||
- **The IO-APIC**: [ioapic.zig](../../system/kernel/architecture/x86_64/ioapic.zig)
|
- **The IO-APIC**: [ioapic.zig](../../system/kernel/architecture/x86_64/ioapic.zig)
|
||||||
routes external device lines onto vectors — discovered via ACPI's MADT, every
|
routes external device lines onto vectors — discovered via ACPI's MADT, every
|
||||||
input masked at init, lines unmasked one at a time as user-space drivers bind
|
input masked at init, lines unmasked one at a time as user-space drivers bind
|
||||||
them (see [device-interrupts.md](../device-driver-development-guide/device-interrupts.md)). The keyboard followed
|
them (see [device-interrupts.md](../device-driver-development/device-interrupts.md)). The keyboard followed
|
||||||
exactly as predicted: the PS/2 bus driver (`system/drivers/ps2-bus/`) claims
|
exactly as predicted: the PS/2 bus driver (`system/drivers/ps2-bus/`) claims
|
||||||
the 8042 controller and binds its IRQ 1 (and the aux mouse's IRQ 12) through
|
the 8042 controller and binds its IRQ 1 (and the aux mouse's IRQ 12) through
|
||||||
this routing. USB HID keyboards arrive over xHCI instead, which interrupts via
|
this routing. USB HID keyboards arrive over xHCI instead, which interrupts via
|
||||||
@@ -138,8 +138,8 @@ Four tests (see [testing.md](../testing.md)) pin down the guarantees:
|
|||||||
processes own the low half.
|
processes own the low half.
|
||||||
- **Per-address-space tables** — done: each user process gets its own root with
|
- **Per-address-space tables** — done: each user process gets its own root with
|
||||||
the kernel half shared, and refcounted shared-memory mappings exist
|
the kernel half shared, and refcounted shared-memory mappings exist
|
||||||
([ipc.md](../device-driver-development-guide/ipc.md)). Copy-on-write remains unbuilt — nothing has needed it yet.
|
([ipc.md](../device-driver-development/ipc.md)). Copy-on-write remains unbuilt — nothing has needed it yet.
|
||||||
- **Uncacheable MMIO** — half done: user-space device and DMA mappings are
|
- **Uncacheable MMIO** — half done: user-space device and DMA mappings are
|
||||||
strong-uncacheable and the framebuffer is write-combining via the PAT, but the
|
strong-uncacheable and the framebuffer is write-combining via the PAT, but the
|
||||||
kernel's own `mapMmio` path is still writeback — the LAPIC included (see
|
kernel's own `mapMmio` path is still writeback — the LAPIC included (see
|
||||||
[device-interrupts.md](../device-driver-development-guide/device-interrupts.md)).
|
[device-interrupts.md](../device-driver-development/device-interrupts.md)).
|
||||||
@@ -7,7 +7,7 @@ them owns the hardware that reported the event, and the reporter should not know
|
|||||||
who is listening. So system power is a **service**: an event source **publishes**
|
who is listening. So system power is a **service**: an event source **publishes**
|
||||||
button/lid/battery/AC events, interested processes **subscribe**, and one
|
button/lid/battery/AC events, interested processes **subscribe**, and one
|
||||||
privileged caller — init — can ask it to power the machine off. It is the same
|
privileged caller — init — can ask it to power the machine off. It is the same
|
||||||
publish/subscribe shape as the [input service](../device-driver-development-guide/input.md), applied to power.
|
publish/subscribe shape as the [input service](../device-driver-development/input.md), applied to power.
|
||||||
|
|
||||||
## Why a service, and why it is named for the domain, not the firmware
|
## Why a service, and why it is named for the domain, not the firmware
|
||||||
|
|
||||||
@@ -126,5 +126,5 @@ until laptop sleep), and thermal zones.
|
|||||||
firmware neutrality that makes a PSCI backend drop-in on ARM.
|
firmware neutrality that makes a PSCI backend drop-in on ARM.
|
||||||
- [process-lifecycle.md](process-lifecycle.md) — the stop sequence
|
- [process-lifecycle.md](process-lifecycle.md) — the stop sequence
|
||||||
(`terminate → deadline → kill`) and signals init composes into shutdown.
|
(`terminate → deadline → kill`) and signals init composes into shutdown.
|
||||||
- [device-manager.md](../device-driver-development-guide/device-manager.md) — the supervision model init mirrors for
|
- [device-manager.md](../device-driver-development/device-manager.md) — the supervision model init mirrors for
|
||||||
its own children.
|
its own children.
|
||||||
+3
-3
@@ -9,7 +9,7 @@ the layer above them — the standard vocabulary a danos process speaks about it
|
|||||||
life, and the stable `process` interface that carries it. Nothing here is
|
life, and the stable `process` interface that carries it. Nothing here is
|
||||||
device- or driver-specific: a driver, the VFS, and a user application all stop,
|
device- or driver-specific: a driver, the VFS, and a user application all stop,
|
||||||
reload, and die the same way. The device manager is simply this design's first
|
reload, and die the same way. The device manager is simply this design's first
|
||||||
serious customer ([device-manager.md](../device-driver-development-guide/device-manager.md)).
|
serious customer ([device-manager.md](../device-driver-development/device-manager.md)).
|
||||||
|
|
||||||
**"POSIX" in this document means the concepts, never the letter of the standard.**
|
**"POSIX" in this document means the concepts, never the letter of the standard.**
|
||||||
danos borrows the ideas and the hard-won lessons (what SIGTERM *means*, why SIGPIPE
|
danos borrows the ideas and the hard-won lessons (what SIGTERM *means*, why SIGPIPE
|
||||||
@@ -172,7 +172,7 @@ zombie state or privileged snooping:
|
|||||||
the server's reply with `-EPEER`; a server that dies fails its waiting clients
|
the server's reply with `-EPEER`; a server that dies fails its waiting clients
|
||||||
the same way. This covers the *synchronous* case only.
|
the same way. This covers the *synchronous* case only.
|
||||||
3. **The subscribers** — the new piece, and it is the input service's
|
3. **The subscribers** — the new piece, and it is the input service's
|
||||||
publish/subscribe shape ([input.md](../device-driver-development-guide/input.md)) applied to exits. A stateful
|
publish/subscribe shape ([input.md](../device-driver-development/input.md)) applied to exits. A stateful
|
||||||
service accumulates per-client state across many requests: a filesystem server
|
service accumulates per-client state across many requests: a filesystem server
|
||||||
(FAT today) holds a dead client's open file handles, the input service holds
|
(FAT today) holds a dead client's open file handles, the input service holds
|
||||||
its subscriptions, a future network stack holds its sockets. None of these
|
its subscriptions, a future network stack holds its sockets. None of these
|
||||||
@@ -320,7 +320,7 @@ get POSIX; danos-native programs never pay for it.
|
|||||||
`process` grows the interface above; the service harness handles
|
`process` grows the interface above; the service harness handles
|
||||||
`terminate` and answers the common `ping`; `stop()` for supervisors.
|
`terminate` and answers the common `ping`; `stop()` for supervisors.
|
||||||
|
|
||||||
[device-manager.md](../device-driver-development-guide/device-manager.md) builds directly on all four.
|
[device-manager.md](../device-driver-development/device-manager.md) builds directly on all four.
|
||||||
|
|
||||||
## Settled questions (2026-07-12)
|
## Settled questions (2026-07-12)
|
||||||
|
|
||||||
@@ -5,7 +5,7 @@ isolation; fault → kill the process → keep the core (`onException`; the
|
|||||||
`fault-recovery` test); the supervisor notification **with exit reasons**
|
`fault-recovery` test); the supervisor notification **with exit reasons**
|
||||||
([process-lifecycle.md](process-lifecycle.md) — clean exit, fault class, or
|
([process-lifecycle.md](process-lifecycle.md) — clean exit, fault class, or
|
||||||
killed, recorded before the notice posts); and the **restart policy itself**
|
killed, recorded before the notice posts); and the **restart policy itself**
|
||||||
([device-manager.md](../device-driver-development-guide/device-manager.md)): the device manager supervises every
|
([device-manager.md](../device-driver-development/device-manager.md)): the device manager supervises every
|
||||||
driver, restarts crashes with backoff, caps crash loops, and re-claims work
|
driver, restarts crashes with backoff, caps crash loops, and re-claims work
|
||||||
because the kernel releases a dead process's claims. The `driver-restart` and
|
because the kernel releases a dead process's claims. The `driver-restart` and
|
||||||
`usb-report` scenarios prove kill → release → respawn → re-claim → re-report
|
`usb-report` scenarios prove kill → release → respawn → re-claim → re-report
|
||||||
@@ -81,7 +81,7 @@ Detecting and killing is the easy half. The genuinely tricky questions are about
|
|||||||
- **In-flight IPC**: messages sent to the dead component, or replies its clients are
|
- **In-flight IPC**: messages sent to the dead component, or replies its clients are
|
||||||
blocked waiting for. The channel has to break cleanly and unblock the waiters with
|
blocked waiting for. The channel has to break cleanly and unblock the waiters with
|
||||||
an error rather than hang them forever (a design constraint that reaches back into
|
an error rather than hang them forever (a design constraint that reaches back into
|
||||||
[ipc.md](../device-driver-development-guide/ipc.md) — channels need a "peer died" outcome).
|
[ipc.md](../device-driver-development/ipc.md) — channels need a "peer died" outcome).
|
||||||
- **Clients**: how does a client discover the service it was talking to is gone and
|
- **Clients**: how does a client discover the service it was talking to is gone and
|
||||||
has been replaced? Options: capability revocation makes stale handles fail; or a
|
has been replaced? Options: capability revocation makes stale handles fail; or a
|
||||||
**name server** re-binds clients to the new instance; or clients retry through a
|
**name server** re-binds clients to the new instance; or clients retry through a
|
||||||
@@ -159,7 +159,7 @@ real-time work without owing anyone a timing *guarantee*.
|
|||||||
- [vision.md](../vision.md) — the goals this serves (learning by doing; resilience over
|
- [vision.md](../vision.md) — the goals this serves (learning by doing; resilience over
|
||||||
hard real-time).
|
hard real-time).
|
||||||
- [scheduling.md](scheduling.md) — preemption, which makes runaway components killable.
|
- [scheduling.md](scheduling.md) — preemption, which makes runaway components killable.
|
||||||
- [ipc.md](../device-driver-development-guide/ipc.md) — channels that need a "peer died" outcome for clean restart.
|
- [ipc.md](../device-driver-development/ipc.md) — channels that need a "peer died" outcome for clean restart.
|
||||||
- [interrupts.md](interrupts.md) — fault reporting that user mode turns into "kill and
|
- [interrupts.md](interrupts.md) — fault reporting that user mode turns into "kill and
|
||||||
restart" instead of "halt".
|
restart" instead of "halt".
|
||||||
- [smp.md](smp.md) — the real-time-vs-resilience fork, in the SMP context.
|
- [smp.md](smp.md) — the real-time-vs-resilience fork, in the SMP context.
|
||||||
@@ -37,7 +37,7 @@ down a return address pointing at `task_trampoline` and zeroed callee-saved slot
|
|||||||
`schedule()` — pick the best task and switch — runs from two places:
|
`schedule()` — pick the best task and switch — runs from two places:
|
||||||
|
|
||||||
- **`yield()`** — a task voluntarily gives up the CPU.
|
- **`yield()`** — a task voluntarily gives up the CPU.
|
||||||
- **`tick()`** — the 1000 Hz [timer](../device-driver-development-guide/device-interrupts.md) preempts the running
|
- **`tick()`** — the 1000 Hz [timer](../device-driver-development/device-interrupts.md) preempts the running
|
||||||
task. This is what lets a task that never yields still share the CPU.
|
task. This is what lets a task that never yields still share the CPU.
|
||||||
|
|
||||||
The subtlety in mixing them is the **interrupt flag (IF)**. The rule: `switch_context`
|
The subtlety in mixing them is the **interrupt flag (IF)**. The rule: `switch_context`
|
||||||
@@ -97,7 +97,7 @@ marks the task blocked with a wake deadline and switches away. On every tick the
|
|||||||
timer wakes any task whose deadline has passed (a bounded scan, so it stays
|
timer wakes any task whose deadline has passed (a bounded scan, so it stays
|
||||||
deterministic), which makes it ready again; the scheduler then runs it when its
|
deterministic), which makes it ready again; the scheduler then runs it when its
|
||||||
priority comes up. `sleep` measures its deadline on the [calibrated
|
priority comes up. `sleep` measures its deadline on the [calibrated
|
||||||
clock](../device-driver-development-guide/device-interrupts.md), so it's real time.
|
clock](../device-driver-development/device-interrupts.md), so it's real time.
|
||||||
|
|
||||||
When *every* task is blocked, something still has to run — so there's an **idle
|
When *every* task is blocked, something still has to run — so there's an **idle
|
||||||
task** at the lowest priority that just `hlt`s until the next interrupt (see
|
task** at the lowest priority that just `hlt`s until the next interrupt (see
|
||||||
@@ -111,7 +111,7 @@ The other form of blocking is waiting for an **event** rather than a duration. A
|
|||||||
the caller on it, `wake(wq)` moves the highest-priority waiter back to ready
|
the caller on it, `wake(wq)` moves the highest-priority waiter back to ready
|
||||||
(preempting if it now outranks the running task). A task links into a wait queue
|
(preempting if it now outranks the running task). A task links into a wait queue
|
||||||
through the same field the ready queues use — it's in exactly one queue at a time.
|
through the same field the ready queues use — it's in exactly one queue at a time.
|
||||||
These are the primitives locks, semaphores and [IPC](../device-driver-development-guide/ipc.md) are built on.
|
These are the primitives locks, semaphores and [IPC](../device-driver-development/ipc.md) are built on.
|
||||||
|
|
||||||
Blocking safely needs **composable critical sections**. A blanket `cli`/`sti` pair
|
Blocking safely needs **composable critical sections**. A blanket `cli`/`sti` pair
|
||||||
doesn't nest: an IPC channel that `cli`s and then calls `wait` would have `wait`'s
|
doesn't nest: an IPC channel that `cli`s and then calls `wait` would have `wait`'s
|
||||||
+1
-1
@@ -307,4 +307,4 @@ refcount, and no group-kill special case is needed at all.
|
|||||||
Then update [threading.md](threading.md) (the shared-fate gap note),
|
Then update [threading.md](threading.md) (the shared-fate gap note),
|
||||||
[process-lifecycle.md](process-lifecycle.md),
|
[process-lifecycle.md](process-lifecycle.md),
|
||||||
[process-management.md](process-management.md), and
|
[process-management.md](process-management.md), and
|
||||||
[ipc.md](../device-driver-development-guide/ipc.md)/[drivers.md](../device-driver-development-guide/drivers.md) mentions.
|
[ipc.md](../device-driver-development/ipc.md)/[drivers.md](../device-driver-development/drivers.md) mentions.
|
||||||
@@ -270,6 +270,6 @@ next lands.
|
|||||||
|
|
||||||
- [scheduling.md](scheduling.md) — the single-core scheduler SMP would extend.
|
- [scheduling.md](scheduling.md) — the single-core scheduler SMP would extend.
|
||||||
- [discovery.md](discovery.md) — enumerating cores is a device-discovery problem.
|
- [discovery.md](discovery.md) — enumerating cores is a device-discovery problem.
|
||||||
- [ipc.md](../device-driver-development-guide/ipc.md) — the message passing cross-core coordination rides on.
|
- [ipc.md](../device-driver-development/ipc.md) — the message passing cross-core coordination rides on.
|
||||||
- [vision.md](../vision.md) — the goals question (real-time vs resilience) this note
|
- [vision.md](../vision.md) — the goals question (real-time vs resilience) this note
|
||||||
keeps bumping into.
|
keeps bumping into.
|
||||||
@@ -51,7 +51,7 @@ Everything else---including`read()`,`write()`,`malloc()`, and`fork()`---will run
|
|||||||
3. **`Yield()`/`Thread_Ctrl()`**
|
3. **`Yield()`/`Thread_Ctrl()`**
|
||||||
- **What it does:**Allows a thread to voluntarily give up its CPU time slice, or allows a root task to spawn/kill threads.
|
- **What it does:**Allows a thread to voluntarily give up its CPU time slice, or allows a root task to spawn/kill threads.
|
||||||
4. **`ipc_send(endpoint, message_buffer)`(Asynchronous Send)**
|
4. **`ipc_send(endpoint, message_buffer)`(Asynchronous Send)**
|
||||||
- **What it does:**Posts a small payload to an endpoint's bounded queue and returns *without* blocking — no rendezvous, no reply. The receiver picks it up through the same `IPC_ReplyWait`, as a buffered message. It is the async counterpart of `IPC_Call`, for one-to-many broadcasts where a synchronous rendezvous would let one dead or slow receiver hang the sender. The [input service](../device-driver-development-guide/input.md) — keyboard-event fan-out — is its first user. A full queue drops the oldest message (a buffered message is discrete data, unlike a coalescing interrupt notification).
|
- **What it does:**Posts a small payload to an endpoint's bounded queue and returns *without* blocking — no rendezvous, no reply. The receiver picks it up through the same `IPC_ReplyWait`, as a buffered message. It is the async counterpart of `IPC_Call`, for one-to-many broadcasts where a synchronous rendezvous would let one dead or slow receiver hang the sender. The [input service](../device-driver-development/input.md) — keyboard-event fan-out — is its first user. A full queue drops the oldest message (a buffered message is discrete data, unlike a coalescing interrupt notification).
|
||||||
|
|
||||||
* * * * *
|
* * * * *
|
||||||
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
The ordered, checkpointable build-out for [threading.md](threading.md). Each milestone
|
The ordered, checkpointable build-out for [threading.md](threading.md). Each milestone
|
||||||
lands on its own and ends in a **verifiable gate** — shaped for a `/loop` run, like
|
lands on its own and ends in a **verifiable gate** — shaped for a `/loop` run, like
|
||||||
[display-v2-plan.md](../device-driver-development-guide/display-v2-plan.md). Read threading.md first for the *why*.
|
[display-v2-plan.md](../device-driver-development/display-v2-plan.md). Read threading.md first for the *why*.
|
||||||
|
|
||||||
## Locked decisions (do not relitigate)
|
## Locked decisions (do not relitigate)
|
||||||
|
|
||||||
@@ -459,7 +459,7 @@ clean.
|
|||||||
## Deferred (explicitly not in this plan)
|
## Deferred (explicitly not in this plan)
|
||||||
|
|
||||||
- **Cross-process shared-memory futex** — the `(address_space, virtual_address)` key can become a
|
- **Cross-process shared-memory futex** — the `(address_space, virtual_address)` key can become a
|
||||||
physical-address key so two processes share a futex through a [shared-memory](../device-driver-development-guide/display-v2.md)
|
physical-address key so two processes share a futex through a [shared-memory](../device-driver-development/display-v2.md)
|
||||||
region. Not needed for intra-process threads.
|
region. Not needed for intra-process threads.
|
||||||
- **Per-thread priorities / affinity distinct from the process** — threads inherit the
|
- **Per-thread priorities / affinity distinct from the process** — threads inherit the
|
||||||
process priority ([scheduling.md](scheduling.md)); revisit only if it earns its keep.
|
process priority ([scheduling.md](scheduling.md)); revisit only if it earns its keep.
|
||||||
@@ -36,7 +36,7 @@ implementation underneath, not the API above.
|
|||||||
[Why not literal std.Thread](#why-not-literal-stdthread).
|
[Why not literal std.Thread](#why-not-literal-stdthread).
|
||||||
- **Threads are a narrow, opt-in capability — not the default concurrency tool.** The
|
- **Threads are a narrow, opt-in capability — not the default concurrency tool.** The
|
||||||
default for resilience stays **process + IPC** ([resilience.md](resilience.md),
|
default for resilience stays **process + IPC** ([resilience.md](resilience.md),
|
||||||
[ipc.md](../device-driver-development-guide/ipc.md)). See [Where threads fit](#where-threads-fit-the-resilience-tension).
|
[ipc.md](../device-driver-development/ipc.md)). See [Where threads fit](#where-threads-fit-the-resilience-tension).
|
||||||
- **Blocking synchronization is futex-backed, never spin-backed.** Waiters sleep in
|
- **Blocking synchronization is futex-backed, never spin-backed.** Waiters sleep in
|
||||||
the kernel so an idle core still halts ([halting.md](halting.md)).
|
the kernel so an idle core still halts ([halting.md](halting.md)).
|
||||||
- **Per-binary opt-in to multi-threaded codegen.** Only a service that asks for
|
- **Per-binary opt-in to multi-threaded codegen.** Only a service that asks for
|
||||||
@@ -214,7 +214,7 @@ Keying: threads share an address space, so a **virtual address within that addre
|
|||||||
identifies a futex uniquely; the kernel keys its wait queue by `(address_space_root, virtual_address)`.
|
identifies a futex uniquely; the kernel keys its wait queue by `(address_space_root, virtual_address)`.
|
||||||
Keying by the **physical** address instead (translate `virtual_address -> physical_address` on entry) is a
|
Keying by the **physical** address instead (translate `virtual_address -> physical_address` on entry) is a
|
||||||
deliberate forward door: it lets two *processes* share a futex through an
|
deliberate forward door: it lets two *processes* share a futex through an
|
||||||
[shared-memory](../device-driver-development-guide/display-v2.md) region later, without changing the API. We start with the
|
[shared-memory](../device-driver-development/display-v2.md) region later, without changing the API. We start with the
|
||||||
private-per-address-space key and note the physical-key upgrade.
|
private-per-address-space key and note the physical-key upgrade.
|
||||||
|
|
||||||
No spinning: a contended lock parks the task in the kernel and the core is free to run
|
No spinning: a contended lock parks the task in the kernel and the core is free to run
|
||||||
@@ -264,7 +264,7 @@ stays single-threaded and lean.
|
|||||||
**process**, which respawns its threads from a known-good state — restart
|
**process**, which respawns its threads from a known-good state — restart
|
||||||
granularity stays the process. The leader's recorded exit reason carries the fault
|
granularity stays the process. The leader's recorded exit reason carries the fault
|
||||||
class even when a worker faulted, so restart policy is unchanged.
|
class even when a worker faulted, so restart policy is unchanged.
|
||||||
- **IPC — two consequences threads forced ([ipc.md](../device-driver-development-guide/ipc.md)):**
|
- **IPC — two consequences threads forced ([ipc.md](../device-driver-development/ipc.md)):**
|
||||||
- *Handles do not cross threads.* The handle table lives on the `Task`
|
- *Handles do not cross threads.* The handle table lives on the `Task`
|
||||||
([scheduler.zig](../../system/kernel/scheduler.zig)), so a handle number is meaningful
|
([scheduler.zig](../../system/kernel/scheduler.zig)), so a handle number is meaningful
|
||||||
only to the thread that created it — thread A's endpoint handle `3` is not thread B's.
|
only to the thread that created it — thread A's endpoint handle `3` is not thread B's.
|
||||||
@@ -284,7 +284,7 @@ stays single-threaded and lean.
|
|||||||
|
|
||||||
The ordered, `/loop`-runnable milestones live in
|
The ordered, `/loop`-runnable milestones live in
|
||||||
**[threading-plan.md](threading-plan.md)** (shaped like
|
**[threading-plan.md](threading-plan.md)** (shaped like
|
||||||
[display-v2-plan.md](../device-driver-development-guide/display-v2-plan.md)): every milestone lands on its own and ends in
|
[display-v2-plan.md](../device-driver-development/display-v2-plan.md)): every milestone lands on its own and ends in
|
||||||
a verifiable gate (`python3 test/qemu_test.py <case>`, asserting serial markers;
|
a verifiable gate (`python3 test/qemu_test.py <case>`, asserting serial markers;
|
||||||
`zig build test` for host unit tests). The stages below are the shape it expands.
|
`zig build test` for host unit tests). The stages below are the shape it expands.
|
||||||
|
|
||||||
@@ -343,7 +343,7 @@ the later self-hosting lift cheap.
|
|||||||
- [scheduling.md](scheduling.md), [smp.md](smp.md) — the task model these threads join.
|
- [scheduling.md](scheduling.md), [smp.md](smp.md) — the task model these threads join.
|
||||||
- [resilience.md](resilience.md), [vision.md](../vision.md) — why isolation is the default
|
- [resilience.md](resilience.md), [vision.md](../vision.md) — why isolation is the default
|
||||||
and threads are the exception.
|
and threads are the exception.
|
||||||
- [syscall.md](syscall.md), [ipc.md](../device-driver-development-guide/ipc.md) — the private ABI and the messaging model
|
- [syscall.md](syscall.md), [ipc.md](../device-driver-development/ipc.md) — the private ABI and the messaging model
|
||||||
threads sit beside.
|
threads sit beside.
|
||||||
- [halting.md](halting.md) — the idle/halt property futex-backed blocking preserves.
|
- [halting.md](halting.md) — the idle/halt property futex-backed blocking preserves.
|
||||||
- [zig-self-hosting.md](../zig-self-hosting.md) — the target this bends toward.
|
- [zig-self-hosting.md](../zig-self-hosting.md) — the target this bends toward.
|
||||||
@@ -7,7 +7,7 @@ Two different needs hide under the word "timer", and danos keeps them apart:
|
|||||||
|
|
||||||
Both are answered by the **kernel**, because the kernel already owns a timer: it has
|
Both are answered by the **kernel**, because the kernel already owns a timer: it has
|
||||||
to, to preempt tasks. The LAPIC heartbeat and the calibrated TSC that back all of this
|
to, to preempt tasks. The LAPIC heartbeat and the calibrated TSC that back all of this
|
||||||
are built in [device-interrupts.md](../device-driver-development-guide/device-interrupts.md); the scheduler's blocking and
|
are built in [device-interrupts.md](../device-driver-development/device-interrupts.md); the scheduler's blocking and
|
||||||
wait queues are in [scheduling.md](scheduling.md). This page is about the surface a
|
wait queues are in [scheduling.md](scheduling.md). This page is about the surface a
|
||||||
ring-3 program actually uses, and one deliberate absence: **there is no user-space time
|
ring-3 program actually uses, and one deliberate absence: **there is no user-space time
|
||||||
service.**
|
service.**
|
||||||
@@ -32,11 +32,11 @@ danos checks both — the invariant-TSC CPUID bit (`0x80000007` EDX[8], set on I
|
|||||||
AMD), and a cross-core "warp" check as the cores come up — and falls back to the HPET
|
AMD), and a cross-core "warp" check as the cores come up — and falls back to the HPET
|
||||||
counter when either fails. So `now()` stays accurate on a real Intel box, a real AMD box,
|
counter when either fails. So `now()` stays accurate on a real Intel box, a real AMD box,
|
||||||
and inside a VM alike; only the source behind it differs. The mechanism is in
|
and inside a VM alike; only the source behind it differs. The mechanism is in
|
||||||
[device-interrupts.md](../device-driver-development-guide/device-interrupts.md).
|
[device-interrupts.md](../device-driver-development/device-interrupts.md).
|
||||||
|
|
||||||
So the timer hardware lives in the kernel, and there is **no `hpet` driver and no time
|
So the timer hardware lives in the kernel, and there is **no `hpet` driver and no time
|
||||||
server** to consume. (An earlier HPET driver existed only to *demonstrate* the driver
|
server** to consume. (An earlier HPET driver existed only to *demonstrate* the driver
|
||||||
model; that role now lives in [drivers.md](../device-driver-development-guide/drivers.md), as documentation.) The one place
|
model; that role now lives in [drivers.md](../device-driver-development/drivers.md), as documentation.) The one place
|
||||||
a user-space time service *is* justified — **wall-clock / calendar time** — is discussed
|
a user-space time service *is* justified — **wall-clock / calendar time** — is discussed
|
||||||
at the end; it is deliberately not built yet.
|
at the end; it is deliberately not built yet.
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ Time and waiting are three entries in the small syscall table ([syscall.md](sysc
|
|||||||
service can keep answering messages on the same endpoint while a deadline is pending.
|
service can keep answering messages on the same endpoint while a deadline is pending.
|
||||||
This is the timed wait that stop-sequence escalation, hello deadlines, and restart
|
This is the timed wait that stop-sequence escalation, hello deadlines, and restart
|
||||||
backoff are built from ([process-lifecycle.md](process-lifecycle.md),
|
backoff are built from ([process-lifecycle.md](process-lifecycle.md),
|
||||||
[device-manager.md](../device-driver-development-guide/device-manager.md)).
|
[device-manager.md](../device-driver-development/device-manager.md)).
|
||||||
|
|
||||||
The kernel's own scheduling timer (the LAPIC, vector 32) is never exposed to user space;
|
The kernel's own scheduling timer (the LAPIC, vector 32) is never exposed to user space;
|
||||||
programs read the TSC through `clock` and get timed wakeups through `sleep`/`timer_bind`,
|
programs read the TSC through `clock` and get timed wakeups through `sleep`/`timer_bind`,
|
||||||
@@ -118,7 +118,7 @@ hypervisor configured for UEFI firmware and an xHCI USB controller.
|
|||||||
(`system/kernel/acpi.zig:3`)
|
(`system/kernel/acpi.zig:3`)
|
||||||
- The loader reads `/system/kernel` off the FAT boot volume, then loads user
|
- The loader reads `/system/kernel` off the FAT boot volume, then loads user
|
||||||
space: a prebuilt `boot\system.img` capsule
|
space: a prebuilt `boot\system.img` capsule
|
||||||
([system-image.md](os-development-guide/system-image.md)) when present, otherwise it walks
|
([system-image.md](os-development/system-image.md)) when present, otherwise it walks
|
||||||
the volume's `/system` and optional `/test` trees (init included) into the
|
the volume's `/system` and optional `/test` trees (init included) into the
|
||||||
initial ramdisk. The kernel can boot "kernel-only" without either.
|
initial ramdisk. The kernel can boot "kernel-only" without either.
|
||||||
(`efi.zig:16`, `efi.zig:68`)
|
(`efi.zig:16`, `efi.zig:68`)
|
||||||
|
|||||||
+2
-2
@@ -27,7 +27,7 @@ boot log, memory summary, exception reports — appears on serial as plain text.
|
|||||||
|
|
||||||
QEMU captures that with `-serial file:serial.log`, giving a machine-readable
|
QEMU captures that with `-serial file:serial.log`, giving a machine-readable
|
||||||
transcript. Serial is per-architecture (x86 uses port I/O; an ARM board uses a
|
transcript. Serial is per-architecture (x86 uses port I/O; an ARM board uses a
|
||||||
memory-mapped UART), so it lives behind the [architecture](os-development-guide/architecture.md) boundary — and adding
|
memory-mapped UART), so it lives behind the [architecture](os-development/architecture.md) boundary — and adding
|
||||||
a new architecture's UART is what makes the same tests run there.
|
a new architecture's UART is what makes the same tests run there.
|
||||||
|
|
||||||
The serial log sink is **compiled in only under `-Dserial`** (off by default).
|
The serial log sink is **compiled in only under `-Dserial`** (off by default).
|
||||||
@@ -88,7 +88,7 @@ table in `test/qemu_test.py`):
|
|||||||
| `fault-recovery` | a ring-3 process that faults is killed and reaped while init keeps heartbeating — the OS survives | `DANOS-TEST-RESULT: PASS` |
|
| `fault-recovery` | a ring-3 process that faults is killed and reaped while init keeps heartbeating — the OS survives | `DANOS-TEST-RESULT: PASS` |
|
||||||
|
|
||||||
The faulting cases don't print a result line — they deliberately raise a CPU
|
The faulting cases don't print a result line — they deliberately raise a CPU
|
||||||
exception, and the harness asserts on the [exception report](os-development-guide/interrupts.md) the
|
exception, and the harness asserts on the [exception report](os-development/interrupts.md) the
|
||||||
handler prints (which also reaches serial). This reuses the real fault path as the
|
handler prints (which also reaches serial). This reuses the real fault path as the
|
||||||
test oracle: if the IDT/TSS weren't wired up, `fault-df` would triple-fault and the
|
test oracle: if the IDT/TSS weren't wired up, `fault-df` would triple-fault and the
|
||||||
marker would never appear.
|
marker would never appear.
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ localised (below).
|
|||||||
## The architecture decision: `runtime.os` + `runtime.fs`, and retire `posix`
|
## The architecture decision: `runtime.os` + `runtime.fs`, and retire `posix`
|
||||||
|
|
||||||
danos already has the right split ([the private-ABI boundary](../README.md)): the
|
danos already has the right split ([the private-ABI boundary](../README.md)): the
|
||||||
kernel exposes a minimal syscall ABI ([syscall.md](os-development-guide/syscall.md)); the **`runtime`**
|
kernel exposes a minimal syscall ABI ([syscall.md](os-development/syscall.md)); the **`runtime`**
|
||||||
library is the stable, danos-native application ABI. What this roadmap adds:
|
library is the stable, danos-native application ABI. What this roadmap adds:
|
||||||
|
|
||||||
- **`runtime.os` — the seam.** A C-ABI-shaped module of the ~30 operations
|
- **`runtime.os` — the seam.** A C-ABI-shaped module of the ~30 operations
|
||||||
@@ -165,7 +165,7 @@ What the seam needs, and what danos already provides:
|
|||||||
| mmap / munmap | native syscalls ([abi.zig](../system/abi.zig)) | none |
|
| mmap / munmap | native syscalls ([abi.zig](../system/abi.zig)) | none |
|
||||||
| page allocator | over `mmap`, via `root.os.heap.page_allocator` override | ~30-line hook |
|
| page allocator | over `mmap`, via `root.os.heap.page_allocator` override | ~30-line hook |
|
||||||
| monotonic clock | `clock` syscall | none |
|
| monotonic clock | `clock` syscall | none |
|
||||||
| args / argv | SysV entry stack ([sysv.md](os-development-guide/sysv.md)), `runtime.process.Init` | none |
|
| args / argv | SysV entry stack ([sysv.md](os-development/sysv.md)), `runtime.process.Init` | none |
|
||||||
| stdout / stderr | `debug_write` today | wire fd 1/2 to a console **byte** stream |
|
| stdout / stderr | `debug_write` today | wire fd 1/2 to a console **byte** stream |
|
||||||
| mkdir / unlink / rename / truncate | done — engine + VFS + `runtime.fs` (Phase 2) | — |
|
| mkdir / unlink / rename / truncate | done — engine + VFS + `runtime.fs` (Phase 2) | — |
|
||||||
| stat fields | `{size, kind, mtime}` | **mode / inode** still missing (cache validity) |
|
| stat fields | `{size, kind, mtime}` | **mode / inode** still missing (cache validity) |
|
||||||
@@ -209,7 +209,7 @@ build); point danos's `build.zig`/CI at the resulting binary. Four localised pat
|
|||||||
plan9/serenity;
|
plan9/serenity;
|
||||||
- add `danos` to the freestanding/other **no-op `_start` list** in `std`'s `start.zig`,
|
- add `danos` to the freestanding/other **no-op `_start` list** in `std`'s `start.zig`,
|
||||||
so std does *not* emit its own System-V `_start` — danos keeps owning the entry shim
|
so std does *not* emit its own System-V `_start` — danos keeps owning the entry shim
|
||||||
and `Init`/argv construction it already builds ([sysv.md](os-development-guide/sysv.md));
|
and `Init`/argv construction it already builds ([sysv.md](os-development/sysv.md));
|
||||||
- wire the `system` selector `.danos => std.os.danos` in `std.posix`;
|
- wire the `system` selector `.danos => std.os.danos` in `std.posix`;
|
||||||
- add `std/os/danos.zig` — **the seam itself**, promoted near-verbatim from the
|
- add `std/os/danos.zig` — **the seam itself**, promoted near-verbatim from the
|
||||||
`runtime.os` developed first in Phase 1 (against the stock toolchain, so the fork is
|
`runtime.os` developed first in Phase 1 (against the stock toolchain, so the fork is
|
||||||
@@ -344,9 +344,9 @@ Two current decisions fall out of this roadmap:
|
|||||||
## Related
|
## Related
|
||||||
|
|
||||||
- [vision.md](vision.md) — the north star this serves.
|
- [vision.md](vision.md) — the north star this serves.
|
||||||
- [syscall.md](os-development-guide/syscall.md) — the kernel↔runtime ABI `runtime.os` is built on.
|
- [syscall.md](os-development/syscall.md) — the kernel↔runtime ABI `runtime.os` is built on.
|
||||||
- [sysv.md](os-development-guide/sysv.md) — the entry stack (`argc/argv/envp/auxv`) danos already constructs.
|
- [sysv.md](os-development/sysv.md) — the entry stack (`argc/argv/envp/auxv`) danos already constructs.
|
||||||
- [ipc.md](device-driver-development-guide/ipc.md) — the IPC the VFS/FAT operations travel over.
|
- [ipc.md](device-driver-development/ipc.md) — the IPC the VFS/FAT operations travel over.
|
||||||
- [danos-file-system-hierarchy-FSH.md](file-system-development/danos-file-system-hierarchy-FSH.md) — the
|
- [danos-file-system-hierarchy-FSH.md](file-system-development/danos-file-system-hierarchy-FSH.md) — the
|
||||||
filesystem layout the file surface serves.
|
filesystem layout the file surface serves.
|
||||||
- [coding-standards.md](coding-standards.md) — danos naming (why the compat spellings
|
- [coding-standards.md](coding-standards.md) — danos naming (why the compat spellings
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# xkeyboard-config — X11 keyboard layouts, compiled to Zig
|
# xkeyboard-config — X11 keyboard layouts, compiled to Zig
|
||||||
|
|
||||||
This module turns a physical key (a **USB HID usage**, as the [input module](../../docs/device-driver-development-guide/input.md)
|
This module turns a physical key (a **USB HID usage**, as the [input module](../../docs/device-driver-development/input.md)
|
||||||
delivers in `KeyEvent.keycode`) plus a modifier state into a **keysym** and, when the key
|
delivers in `KeyEvent.keycode`) plus a modifier state into a **keysym** and, when the key
|
||||||
produces one, a **character** (a Unicode scalar). It is what lets a `keycode` become a
|
produces one, a **character** (a Unicode scalar). It is what lets a `keycode` become a
|
||||||
`character` — a keymap — without danos shipping an X11 runtime.
|
`character` — a keymap — without danos shipping an X11 runtime.
|
||||||
|
|||||||
Reference in New Issue
Block a user