Ring 3 still has no direct in/out (no TSS I/O bitmap, IOPL never raised — a #GP), but a
driver no longer needs it: io_read(device_id, resource_index, offset, width) and
io_write(..., value) grant port access the same way mmio_map grants memory. The claim
plus the device's discovered io_port resource are the capability — resolveIoPort checks
the device is claimed by the caller, the resource is io_port, and [offset, offset+width)
stays inside it, then issues the in/out via architecture.pioRead/pioWrite. So a PS/2 or
16550 driver is now writable; the low-rate legacy hardware that needs port I/O is fine
with a syscall per access. io_port resources were recorded by discovery and ignored —
now they're used. Runtime: device.ioRead/ioWrite.
New `ioport` test claims QEMU's PS/2 controller (io_port 0x64, discovered via ACPI) and
checks the gate admits an in-range access, refuses over-wide / out-of-range / unclaimed,
and that the kernel actually reads the status port (0x1c). Suite 41/41 plus host tests.
Docs refreshed for the whole M13-M16 + port-I/O reality: drivers.md "Limits" no longer
lists port I/O, DMA memory, or barriers as missing (they exist) and its "what's next"
reflects that; the FSH doc's character-device and block-device sections are corrected
("cannot host a block driver at all" is no longer true — writable now, not yet memory-
safe pending IOMMU enforcement); device-interrupts.md unblocks the keyboard and narrows
the interrupt gap to MSI-X.
Two parts, both blockers for real PCI drivers.
ECAM config space per function: enumeratePci now gives every pci_device its own 4 KiB
configuration window as resource 0. A claimed PCI driver mmio_maps that to reach its
command register, BARs, and — the point — its capability list (MSI/MSI-X, PCIe extended
caps), with no new syscall. Verified in the discovery test (QEMU q35's functions each
carry it).
MSI: msi_bind(device_id, endpoint) -> address (rax), data (rdx) allocates a per-device
edge-triggered vector, binds it to the endpoint, and returns the (address, data) the
driver programs into its own MSI capability. Unlike irq_bind there's no GSI, no I/O APIC
entry, no sharing, and no ack cycle — dispatch recognises an MSI vector (vector_gsi ==
none, msi_bound set), EOIs, and notifies. Owner-keyed release drops the binding on exit.
Legacy INTx (_PRT parsing + shared lines) is deliberately skipped; MSI is the real
answer.
QEMU's HPET has no MSI, so the new `msi` test proves the vector-routing path with a
self-IPI (new apic.selfIpi) standing in for the device's MSI write: bind a vector,
fire it, the bound endpoint is notified. The msi_bind syscall wraps irq.msiBind with
the claim check and lands its first real use with the first PCI driver. Suite 39/39
plus host tests.
The `system` module (formerly `danos`) had become a grab-bag: it held the
loader<->kernel handoff *and* the kernel<->user ABI *and* the device wire types, in
one module three different audiences imported. Usage proved the seam — the
bootloader never touched the syscall/device ABI, and user space never touched the
boot handoff — so split it by audience, one module per contract:
system/boot-handoff.zig loader <-> kernel: BootInformation, Framebuffer,
MemoryMap, the VM layout + physicalToVirtual, kernel_abi
system/abi.zig kernel <-> user, core: SystemCall, mmap prot flags,
page_size, notify_badge_bit, ServiceId
system/devices/device-abi.zig kernel <-> user, devices: DeviceDescriptor,
DeviceClass, ResourceDescriptor, ResourceKind, ...
device-abi is the devices sub-project's public interface, exposed as its own module
the way vfs exposes vfs-protocol — importable by user space, unlike the
kernel-internal device model it also feeds. That collapses a real duplication:
DeviceClass and ResourceKind were defined twice (device-model.zig and the contract,
kept "in sync by hand"); device-model now re-exports them from device-abi, so the
enum a driver matches on and the one the kernel classifies with are one type.
Each import now declares which contract it speaks: the bootloader imports only
boot-handoff; a driver only abi + device-abi (via the runtime); the kernel all
three. This also retires the `system` / `runtime.system` name overlap. page_size
lands in abi (it's part of the mmap contract user space aligns to); the bootloader
keeps its own local 4 KiB constant so it depends on nothing but the handoff.
All 21 importers rewired, docs updated to keep /system mapping to source. Build,
host tests, and the QEMU suite (36/36) all green.
The shared kernel<->user ABI contract (BootInformation, the SystemCall numbers,
DeviceDescriptor, page_size, ...) is now the `system` module at
system/system.zig, following the convention that a directory's root file takes
the directory's name.
One overlap to note: the runtime's syscall wrappers are already `runtime.system`,
so the single file that uses both the contract and those wrappers
(library/runtime/heap.zig) aliases the wrappers locally as `system_calls`. The
two are distinct (top-level `system` vs `runtime.system`); everywhere else the
contract is just `system`.
Also: the QEMU run's serial capture now lands in the FHS log location,
zig-out/var/log/system/serial0-<timestamp>.log — a stand-in for the kernel's own
logging system, which will eventually write there itself.
Suite 35/35 plus host tests green.
The source layout now mirrors the runtime filesystem hierarchy
(docs/danos-file-system-hierarchy-FSH.md): what lives under system/ in the
source is what a running danos represents under /system. Each service and
driver is a sub-project directory that is its own Zig module — cross-project
references go by module name, never by a path into another project's files.
Moves (all git mv, history preserved):
- src/ -> system/ (danos internals; the self-representation)
root.zig -> danos.zig (the kernel<->user contract module)
kernel/arch/ -> kernel/architecture/ (arch -> architecture)
device/ -> devices/ (what /system/devices reflects)
boot/ -> /boot (the loaders, top level)
- sbin/ -> split by role:
init, vfs -> system/services/<name>/<name>.zig
hpetd, busd -> system/drivers/<name>/<name>.zig
vfs-test -> system/services/vfs/vfs-test.zig (inside the vfs project)
- lib/ -> library/runtime/ (room for other libraries beside runtime)
The VFS wire protocol becomes its own module, system/services/vfs/protocol.zig
("vfs-protocol"): the vfs sub-project exposes its interface, and the runtime's
file layer imports it by name. First instance of the "protocol module" pattern
(docs/driver-model.md); usb/block will expose theirs the same way.
Also: fix a naming-standard violation in the protocol — Op -> Operation (and
req -> request, _pad -> _padding). Docs updated: /system/services added to the
FHS doc, a repository-layout section added to the docs index, and stale source
paths swept across comments and docs.
Runtime boot paths are unchanged (the bootloader still loads /sbin/init);
aligning the runtime filesystem to the FHS is a separate follow-up. Suite 35/35
plus host tests green.