Commit Graph
10 Commits
Author SHA1 Message Date
Daniel Samson 1379b699f3 init: /protocol replaces the ServiceId registry
A protocol is reached by name now, not by a compile-time integer. Init is
PID 1 and already knows which binary it started, so init serves /protocol
as a vfs backend: bind claims a contract with the provider's endpoint
attached, open answers with that endpoint as the reply's capability, and
readdir lists what is bound with the task and binary behind it. The kernel
reserves the prefix — nothing may mount over it, under it, or unmount it —
and ServiceId, ipc_register and ipc_lookup are gone, their syscall numbers
left vacant.

A bind is authorized by who the caller *is*: the kernel-stamped binary
together with the supervising task's identity, matched against
/system/configuration/protocol.csv. Identity, not spelling — spawn is
ungated, so an attacker can run any bundled binary, and a name-only rule
would have let it launder grants through an init of its own making. A name
a live process holds is refused to everyone else; a dead one's is released.

Three review rounds against a hostile ring-3 process found what 108 green
tests could not, because the suite contains no attacker. Publishing init's
supervision endpoint as the registry put PID 1's mailbox in every process's
hands, where two forged bytes reached the shutdown path: privileged traffic
is now believed only from the task that holds the contract it speaks for.
A capability arriving on a request outlived every path that ignored it,
one handle per call until the table was full — in init, and in the harness
ten services share — so the arriving capability is owned by the turn and
released unless a handler says otherwise. And the kernel let anyone holding
an endpoint handle aim signals, timers, exit notices and interrupts at it:
binding now requires having created it.

Suite 108/108. The new protocol-registry case asserts eleven properties,
each one an attack that must fail.
2026-08-01 02:39:07 +01:00
Daniel Samson 8d4a7cf240 kernel: user memory is reached only through a checked copy
A new user-memory module owns every kernel touch of a user buffer:
copyFromUser, the new copyToUser, and the resolve behind both. The walk
accumulates the U/S and writable bits down all four levels with the MMU's
own AND rule — folding a 2 MiB leaf in before it resolves and refusing a
1 GiB leaf outright — so a copy honours what ring 3 itself would be
allowed, closing the presence-only trust model the IPC layer carried since
bring-up. It then confirms the frame is physmap-backed, because that is how
the copy reaches it: an mmio_map'd BAR passes the permission walk and would
otherwise fault ring 0 on an alias the physmap never mapped, on the IPC path
as much as the new one.

The nine stragglers that dereferenced user pointers raw now route through
it, so a bad pointer returns -EFAULT where it used to fault the kernel.
The write direction restructures its callees around kernel bounce buffers:
scheduler and devices-broker enumerate from a slot cursor (a task exiting
between chunks can neither duplicate nor lose an entry), klog_read drains
the ring in chunks, and fs_node stages headers and names contiguously.
fs_resolve copies out before installing the endpoint handle, so a faulting
copy cannot strand a capability; its out-capacity bound no longer adds an
unbounded ring-3 length to the base, which wrapped and trapped the kernel's
own overflow check. debug_write reads the caller's message once.

Suite 107/107 (new user-memory case: seven bad pointers refused, each
paired with a sound call that must still succeed).
2026-07-31 20:57:49 +01:00
Daniel Samson c621b649f6 build: lazy dependencies — a build loads only what it ships
The 13 /test fixtures and the acpi/fdt discovery pair are .lazy in the
root zon, resolved with b.lazyDependency only when a build actually
bundles them: a plain `zig build` neither compiles the fixtures nor
loads their build files, and only the -Ddiscovery-selected package ever
loads. Fixture packages are uniform (dependency name = artifact name =
boot-path leaf), so the bundled list shrinks to a name loop. Production
manifest byte-identical; the -Dtest-case manifest carries the same 13
entries in the same order; -Ddiscovery=fdt exercises the lazy fdt path.
Discharges the plan's deferred what-this-buys #4.
2026-07-30 06:26:22 +01:00
Daniel Samson cb98a9844e build: phase 2 wave C — test fixtures build as packages
All thirteen /test/system/services fixtures convert on the pci-bus
template (thread-test threaded; crash-test/device-list/pci-cap-test/
iommu-fault-test carrying their protocol and PCI extras). With the last
addUserBinary caller gone, the root build's wrapper functions and its
default-imports plumbing are deleted — every user binary now reaches
the shared recipe only through its own package. Production boot-image
file list unchanged; a -Dtest-case build bundles the fixtures exactly
as before.
2026-07-30 04:07:19 +01:00
Daniel Samson 4701fbd123 build: phase 2 wave B — drivers build as packages
ps2-bus (bus + keyboard + mouse artifacts from one package),
usb-xhci-bus, usb-hid (keyboard + mouse), usb-storage, and virtio-gpu
convert on the pci-bus template. Their unit tests — PS/2 decode, HID
boot reports, Bulk-Only Transport/SCSI encodings, and the virtio-gpu
size checks the plan flagged as a wave carry-along — move into their
packages; the root aggregate delegates. Boot-image file list
unchanged.
2026-07-30 04:04:50 +01:00
Daniel Samson 3b23b11b0e build: phase 2 wave A — services build as packages
init, fat, display, display-demo, device-manager, input, logger, and
the two discovery fillers (acpi, fdt — each exporting an artifact named
"discovery"; the root -Ddiscovery picks which ships) convert to binary
packages on the pci-bus template. init's serial heartbeat flag rides
the dependency options (the root forwards its -Dserial). fat's and
display's unit tests move into their packages and the root aggregate
delegates to them. Boot-image file list unchanged.
2026-07-30 04:02:49 +01:00
Daniel Samson 6f4fdc2789 build: phase 2 pilot — pci-bus builds as a package
build-support gains the domains-based userBinary: the default import set
(the library/kernel concern modules, driver/service clients, mmio,
acpi-ids, xkeyboard-config) is assembled from the domain packages, and
the root shim + user link script come from the kernel package directory.
system/drivers/pci-bus is the first binary package: a ~15-line
declarative build.zig naming only its extras (device-manager-protocol,
pci-class); the root build consumes the artifact for the boot image and
the driver also builds standalone from its own directory. Boot-image
file list unchanged.
2026-07-26 22:55:47 +01:00
Daniel Samson 721288c516 build: phase 1 — library domains become packages
Each library domain (kernel, device, client, protocol, csv,
xkeyboard-config) now owns a build.zig + build.zig.zon that wires and
exports its modules, with a standalone `zig build test` per domain.
The kernel package also exports abi (source stays in system/abi.zig) so
every consumer names one module instance. The root build swaps its
createModule calls for b.dependency(...).module(...) — no binary moves;
the root is the pilot consumer (docs/build-packages-plan.md). Path
dependencies deduplicate by resolved location, so the diamond
(root -> device -> kernel, root -> kernel) yields a single instance of
each module. Boot-image file list unchanged.
2026-07-26 22:53:53 +01:00
Daniel Samson 4194bb6e32 build: phase 0 — extract the build-support package
The shared user-binary recipe (freestanding target, root-shim wiring,
link-script and image-base settings) moves out of the root build into
build-support/, the package that is the single home for cross-cutting
build changes (docs/build-packages-plan.md). The root build's
addUserBinary/addThreadedUserBinary keep their signatures and delegate;
nothing else moves. Boot-image file list unchanged.
2026-07-26 22:49:05 +01:00
daniel 3c8656fb25 started project with basic efi setup 2026-07-01 18:32:56 +01:00