Skip to content

driver-primitives: a composable driver kit + Redox virtio drivers - #1

Merged
Rekonquest merged 26 commits into
masterfrom
claude/wonderful-gates-r4rf6o
Jun 21, 2026
Merged

Rekonquest merged 26 commits into
masterfrom
claude/wonderful-gates-r4rf6o

Conversation

@Rekonquest

Copy link
Copy Markdown
Owner

Summary

Builds a small, composable driver primitive kit and, on top of it, a full set of Redox userspace virtio drivers — applying a "dumb primitives + thin orchestrators" doctrine so that wildly different driver classes share the same handful of building blocks.

Starts by refactoring the kernel's EEVDF scheduler into a pure, testable policy module, then lifts that idea outward into a standalone kit and two driver crates. Nothing in the kernel's runtime behaviour changes beyond the scheduler refactor (which is behaviour-preserving); the rest is new, self-contained crates.

What's here

driver-primitives/ — portable, no_std, 83 tests, builds for bare metal (x86_64-unknown-none):

  • Primitives: Ring, Fence/SeqCounter, HandleTable, Features, EventQueue, Transaction
  • Cross-domain: FairQueue (the scheduler's weighted virtual-time fairness, reusable for I/O / power), CapabilityRing (zero-copy handle hand-off)
  • Assembled: Reactor (io_uring-shaped submit/dispatch/complete/reap)
  • Hardware seam: mmio (volatile registers), dma (DMA regions + virtio-compatible descriptors)
  • virtio: faithful split VirtQueue, mmio transport, and orchestrators for net, gpu (multi-screen), blk, rng, input, snd
  • display (atomic multi-output modeset = Transaction; vblank = Fence), power (budget = FairQueue; suspend/resume = Transaction; sensors = EventQueue; governor)
  • runtime Platform seam + interrupt service loop

redox-virtio-drivers/ — the Redox edge: RedoxPlatform binding + six daemons (virtio-netd/gpud/blkd/rngd/inputd/sndd) + scheme front-ends + BOOTING.md runbook.

.github/workflows/ci.yml — CI that builds/tests everything on GitHub's runners.

The doctrine, paid off

The same primitive keeps reappearing across unrelated subsystems:

Primitive Reused by
VirtQueue net · gpu · blk · rng · input · snd
RxPool net and input (verbatim)
Transaction multi-screen modeset · power suspend/resume
FairQueue CPU scheduling · I/O arbitration · power budget
EventQueue device completions · sensors
Fence command completion · vblank

Verification

  • driver-primitives: 83 tests green, clippy -D warnings clean, builds for bare metal.
  • redox-virtio-drivers: host check + clippy clean; daemon logic type-checks; scheme data-path host-tested.
  • Kernel: make check green in CI.
  • The experimental redox-cross-build (redoxer) job cross-compiles the cfg(redox) binding against the real redox_syscall and is being iterated.

Not in this PR (needs a live Redox boot)

The live QEMU boot, the redox-scheme/event: scheme server, and validating the memory-scheme DMA mapping on hardware. BOOTING.md documents the turnkey steps.

🤖 Generated with Claude Code


Generated by Claude Code

claude added 24 commits June 20, 2026 15:40
The scheduler's select_next_context() had grown into a single ~240-line
function that fused two unrelated concerns: the EEVDF *policy* (pure
virtual-time arithmetic deciding what runs next) and the *mechanism*
(run-queue walks, weak-ref upgrades, per-context locking, address-space
acquisition). The policy was smeared inline and duplicated across several
branches, which meant it could not be reasoned about or tested without
booting the kernel.

Extract the policy into a new self-contained `context::eevdf` module:
plain `Entity` snapshots plus pure functions (weight_of, elapsed_ticks,
is_yield, eligible, prefer, charge, reset_slice, activate, v_advance).
It holds no locks and references no kernel types, so it carries its own
unit tests and can be checked off the kernel build entirely.

switch.rs keeps the mechanism (queue/lock/upgrade) and now drives the
policy through the explicit Entity seam: copy the relevant Context fields
in, run the math, copy the results back. Behaviour is preserved exactly;
the magic constants now live in one place.

Verified: standalone policy tests pass (12/12), `make check` is clean
with no new warnings, and the release kernel still links.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PiDS8Ac9HyB9qNvZtYiMGa
Add a portable, no_std primitive kit that device drivers compose, derived
from the published device ABIs (interface contracts, not implementations).
The premise: GPU, USB, WiFi, ethernet, audio and storage drivers are not
distinct problems but distinct *orchestrations* of one small set of dumb
mechanisms. This commit seeds the two most universal:

- ring::Ring<T, N> — a fixed-capacity FIFO descriptor ring, the substrate
  under virtio avail/used, USB URB submit/reap, NVMe SQ/CQ, GPU command IB,
  and V4L2/ALSA buffer queues. No policy: full submits hand the item back.

- fence::{SeqCounter, Fence} — the submit/complete handshake. SeqCounter
  dispenses strictly increasing tags; Fence is a monotonic completion
  high-water mark (is_passed). The substrate under GPU timeline syncobjs
  and command-completion sequence numbers.

Each primitive is dumb on purpose: no locks, no allocation (const-generic
capacity), no OS or hardware dependency. The driver (the orchestrator) owns
the state machine, placement, and the OS-facing glue (MMIO/DMA/IRQ).

The crate is its own workspace, isolated from the kernel build. It is a
portable staging home: it can be lifted into the Redox userspace `drivers`
tree (or any driver project) unchanged, since it depends on nothing here.

Verified: cargo test green (10/10), and it builds clean as no_std.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PiDS8Ac9HyB9qNvZtYiMGa
Extend the dumb-mechanism kit with three more universal primitives, each
fully unit-tested and no_std-clean:

- handle::{Handle, HandleTable} — opaque handles over a fixed slab with a
  per-slot generation counter, so a freed-then-reused slot reliably rejects
  stale handles instead of aliasing the new occupant. The safety property
  behind fd tables and GPU GEM handle tables.

- feature::{Features, negotiate, negotiate_checked} — capability discovery
  as a bitset intersection (device offers AND driver wants), with a check
  that mandatory features survived. The shape under virtio feature bits,
  DRM caps, ethtool features, WiFi cipher/AKM suites, ALSA masks.

- event::EventQueue — a pollable, non-blocking event stream that drops and
  counts on overflow (inotify IN_Q_OVERFLOW style) rather than blocking or
  corrupting. Built by composing Ring — a primitive made of a primitive.

A new integration test exercises all three together: negotiate features,
register devices by handle, emit completion events tagged by handle, drain
and resolve them, then tear a device down and watch its handle go stale.

Verified: cargo test green (24/24), builds clean as no_std.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PiDS8Ac9HyB9qNvZtYiMGa
Finish the portable kit and reach past drivers entirely.

- txn::Transaction — validate-then-commit, all-or-nothing. The DRM atomic
  modeset shape, generic: stage a batch, validate it side-effect-free
  (TEST_ONLY), then commit as a unit or abort. Nothing applies until commit.

- fairqueue::FairQueue — the cross-domain primitive. Weighted virtual-time
  fairness (the kernel's EEVDF / CFS "least-serviced-relative-to-weight goes
  next") lifted out of the scheduler into a domain-agnostic arbiter. The same
  mechanism underlies CPU scheduling, GPU context priority, NIC flow fairness
  (WFQ/fq_codel), block-device weighted queueing, audio mixing, and TDMA slot
  assignment. A "client" is any contender; a "cost" is any scalar unit. It
  decides who goes next and nothing else; it cannot create capacity (the real
  wall, work-conservation), only allocate it in proportion to weight.

The fairness property is unit-tested directly: with equal-cost turns a
weight-3 client receives ~3x the service of a weight-1 client; a late joiner
starts at the floor (no windfall, no starvation); and uneven per-turn costs
equalize work rather than turn counts.

Verified: cargo test green (34/34), builds clean as no_std.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PiDS8Ac9HyB9qNvZtYiMGa
The microkernel superpower as a primitive: hand a resource from one
protection domain to another without copying its bytes -- pass the
capability, not the payload. The same move underlies dma-buf/PRIME, an
io_uring SQE, a Wayland buffer, and a Redox scheme fd passed between
processes.

capability::{CapabilityRing, Grant} composes Ring (the transfer channel)
and SeqCounter (provenance). Two invariants fall out of the type system,
themselves a real wall we operate inside rather than fight:

- Revocation is move: send() takes the resource by value, so the sender
  cannot touch it afterward -- a compile error, not a runtime check.
- A received capability cannot be lost silently: Grant is #[must_use], so
  dropping it without install() warns.

Per-domain naming stays with HandleTable: the sender removes the resource
from its own table (marking its handle stale) and sends it; the receiver
installs the Grant into its table and gets a fresh local handle. The
integration test walks a resource A -> channel -> B and confirms A's old
handle is stale, B's new handle works, and delivery is exactly-once.

Verified: cargo test green (37/37), builds clean as no_std.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PiDS8Ac9HyB9qNvZtYiMGa
The capstone: an io_uring-shaped engine for any contended async resource,
built entirely by composing the kit. The primitives were kept dumb so that
the interesting behaviour lives in how they are wired:

- FairQueue picks which client is served next (weighted fairness).
- a pending area + HandleTable track each op across its lifecycle; the
  handle's generation counter makes a stale or duplicate completion a
  rejected no-op rather than a corruption or double-count.
- EventQueue carries completions out (overflow counted, not dropped silent).
- SeqCounter/Fence tag submissions and mark completion progress.

The cycle is submit -> dispatch -> complete -> reap. The reactor moves no
data and performs no I/O; the executor and the meaning of an "op" are the
orchestrator's. Back-pressure is explicit at both ends (pending area full
hands the op back; in-flight full stalls dispatch).

Tests assert the real properties: a full roundtrip; duplicate/stale
completions rejected via handle generation; back-pressure; and that
dispatch is fair across clients (a weight-3 client is served ~3x a
weight-1 client) -- the kernel's scheduling fairness now arbitrating device
ops instead of CPU time.

Verified: cargo test green (41/41), builds clean as no_std.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PiDS8Ac9HyB9qNvZtYiMGa
…r-shape test

Bring the kit to the edge of real hardware while keeping it portable.

- mmio::{Reg, Bank} — typed volatile register access, the kit's only unsafe
  surface, kept small and explicit. Reg wraps a raw pointer with volatile
  read/write/modify; Bank mints registers at byte offsets within a mapped
  MMIO window. The orchestrator maps the window (on Redox via the memory
  scheme) and hands in the base.

- dma::{DmaRegion, Descriptor} — the device-memory seam, deliberately a trait
  so the kit depends on no platform: the OS supplies the region (on Redox,
  redox_syscall::Dma satisfies the contract). Descriptor's flag values match
  virtio's VIRTQ_DESC_F_*, so the same descriptors drive a real virtqueue.

- tests/driver_shape.rs — a mock NIC TX path built entirely from the public
  API: submit DMA descriptors, ring an MMIO doorbell, dispatch/complete/reap
  through the Reactor. The "ready for hardware" proof; swapping the mock for a
  real virtio transport is the only remaining step to drive metal.

Verified: cargo test green (45 unit + 1 integration), builds clean as no_std,
and compiles for bare metal (x86_64-unknown-none) with no host dependencies.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PiDS8Ac9HyB9qNvZtYiMGa
The hard, real part: lay out virtio's exact descriptor-table / available-ring
/ used-ring structures in DMA memory, with little-endian fields and the
memory barriers a real device requires.

virtio::queue::VirtQueue<Q> is a compile-time-sized split virtqueue over a
caller-supplied DmaRegion. It maintains a private free-descriptor list, chains
a buffer's segments through the descriptor table, publishes the head into the
available ring (Release fence, then idx bump), and reaps completions from the
used ring (Acquire fence), freeing the chain. The add_buf/poll_used cycle is
the kit's submit/reap pattern, so the Reactor sits on top with this queue as
its executor. Layout offsets and required_bytes() are exposed so the
orchestrator can program the device and size the region.

A tests/virtio_loopback.rs integration test drives the real protocol against
a MockDevice that plays the hardware side: it reads the available ring, walks
the descriptor chain through (identity-mapped) addresses summing the bytes,
and posts to the used ring. Covered: single buffer round-trip, multi-
descriptor chains (virtio-net header + data shape), and 1000 submit/service/
reap cycles with no descriptor leaks across index wraparound.

Verified: cargo test green (45 unit + 4 integration), builds clean as no_std
and for bare metal (x86_64-unknown-none).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PiDS8Ac9HyB9qNvZtYiMGa
The policy layer that turns the generic split virtqueue into a network
device, owning three things and no mechanism of its own:

- feature negotiation (negotiate(): requires VERSION_1, accepts MAC/STATUS),
  via the kit's Features intersection;
- the 12-byte virtio-net header (VirtioNetHdr), LE-serialized;
- the RX/TX queue roles -- TX carries readable [header, frame] chains the
  device sends, RX carries device-writable buffers the device fills.

VirtioNet exposes transmit / poll_tx / post_rx / poll_rx. Buffer memory
stays with the caller (DMA regions), as the queue does; a real driver layers
a fixed buffer pool above this.

tests/virtio_net.rs transmits a frame end to end: the orchestrator writes the
header, enqueues [header, frame] on the real TX virtqueue, a mock device reads
the chain and sums the bytes, and the driver reaps the completion with the
descriptors freed. Plus unit tests for header size and feature negotiation.

Only the real transport (PCI/virtio-mmio discovery, IRQ binding via Redox's
irq scheme) remains between this and driving actual hardware.

Verified: cargo test green (48 unit + 5 integration), no_std + bare metal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PiDS8Ac9HyB9qNvZtYiMGa
…egisters)

The last piece between the kit and a live virtio device. VirtioMmio talks to
the device through mmio::Bank/Reg (real volatile register access), so the same
code drives QEMU's virtio-mmio device once the window is mapped (on Redox via
the memory scheme).

Implements the virtio 1.0 MMIO bring-up: verify MagicValue/Version/DeviceID;
reset then ACKNOWLEDGE|DRIVER; read 64-bit DeviceFeatures (sel low/high),
write DriverFeatures and confirm FEATURES_OK; program each queue's
desc/avail/used addresses (from VirtQueue) and mark it ready; DRIVER_OK.
Plus notify, interrupt status/ack, and device config-space reads. Register
offsets and status bits are exposed as documented constants.

tests/virtio_mmio.rs drives the full handshake against a flat mock register
window: discovery, bad-magic rejection, feature negotiation, queue address
programming (verified against VirtQueue::{desc,avail,used}_addr), DRIVER_OK,
notify, interrupt ack, and queue-too-large rejection.

Verified: cargo test green (48 unit + 8 integration), no_std + bare metal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PiDS8Ac9HyB9qNvZtYiMGa
Multi-monitor support is the kit again in a graphics costume:

- Atomic multi-output modeset IS the Transaction primitive. Configuring
  several screens at once is all-or-nothing: Display::commit validates the
  whole batch (DRM TEST_ONLY) and only then applies it, so a bad mode on one
  output never leaves the others half-reconfigured. On failure nothing changes.

- Page-flip / vblank IS the Fence primitive. flip() dispenses a per-output
  ticket (SeqCounter) and returns it; the next vblank() signals completion
  (Fence::signal), and flip_completed() checks it (Fence::is_passed).

Display<N> models N outputs (connector + scanout), each with a connection
state, an EDID-style max mode, current mode/framebuffer, and an independent
vblank timeline. connect/disconnect, check (validate-only), commit (atomic),
flip, vblank, and flip_completed make up the API.

Unit tests cover: a single commit reconfiguring three monitors; a bad change
aborting the whole commit with the other outputs untouched; a mode exceeding
the panel rejected; and a page flip completing on vblank. Transaction now
derives Debug.

Verified: cargo test green (53 unit + 8 integration), no_std + bare metal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PiDS8Ac9HyB9qNvZtYiMGa
…en path

The device that actually drives monitors in QEMU, and the piece that wires the
display orchestrator to real (virtual) hardware. virtio-gpu reuses the kit's
VirtQueue (control queue) and the mmio transport, and adds the 2D command
vocabulary serialized to the exact little-endian wire structures:
get_display_info, create_resource_2d, attach_backing, set_scanout,
transfer_to_host_2d, resource_flush. Plus response parsing
(parse_display_info -> per-scanout rects/enabled).

get_display_info is the bridge to the Display orchestrator: each enabled
scanout becomes an output configured in one atomic modeset.

tests/virtio_gpu_multiscreen.rs runs the whole path against a mock virtio-gpu
device that answers like QEMU: discover two monitors (1920x1080 + 1280x720),
bring them both up in a single atomic Display::commit, then present a frame on
scanout 0 (create -> attach -> set_scanout -> transfer -> flush), verifying the
device received each command type and the control queue ends balanced. Unit
tests verify command serialization byte-for-byte.

Verified: cargo test green (55 unit + 9 integration), no_std + bare metal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PiDS8Ac9HyB9qNvZtYiMGa
A virtio driver needs exactly four things from the OS: DMA-coherent memory, a
mapped MMIO window, and a way to wait for and acknowledge the device interrupt.
runtime::Platform is that seam; runtime::run is the interrupt-driven service
loop written once against it -- block on the IRQ, snapshot+clear the device
interrupt status, service the queues, ack at the device and OS level, repeat
until the interrupt source closes.

This keeps driver-primitives portable: the real Redox binding (physalloc/
physmap + the irq scheme) is a separate crate implementing this trait, while
the loop logic stays here and is host-tested with a mock platform that delivers
N interrupts then signals shutdown.

Verified: cargo test green (57 unit + 9 integration), no_std + bare metal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PiDS8Ac9HyB9qNvZtYiMGa
The OS-specific edge for the kit. Implements driver-primitives' Platform seam
for Redox and ships the daemon binaries; everything above the seam stays in the
portable, tested kit.

- platform.rs: RedoxPlatform implements Platform via physalloc/physmap for DMA
  and MMIO, and the irq scheme (read blocks until the interrupt, write
  acknowledges) for IRQ wait/ack. The real impl is behind cfg(target_os =
  "redox"); off-Redox it compiles to a stub so the daemon logic still
  type-checks. redox_syscall is a Redox-target-only dependency.

- bin/virtio-netd.rs: map device -> negotiate features -> set up RX/TX
  virtqueues -> DRIVER_OK -> interrupt-driven service loop (runtime::run).

- bin/virtio-gpud.rs: bring up the control queue, GET_DISPLAY_INFO, and
  configure every reported monitor in one atomic modeset via the display
  orchestrator.

- README documents the layout, host vs Redox builds, the make qemu bring-up
  steps (for later), and an honesty note that the cfg(redox) binding is not
  compiled in this sandbox and may need a syscall-signature tweak.

Verified: cargo check green on host (daemon logic type-checks against the kit
through the stub Platform). The cfg(redox) binding compiles on the Redox target.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PiDS8Ac9HyB9qNvZtYiMGa
The data-path completeness that lets the virtio-net and virtio-gpu drivers
actually move traffic and pixels, kept portable and host-tested.

- net::RxPool<POOL> manages pre-posted receive buffers: post_all fills the RX
  ring with device-writable descriptors, poll returns each completed receive as
  (pool buffer index, bytes written) by mapping the queue head back to its
  buffer, and repost returns a drained buffer to the device. A NIC keeps the RX
  ring full at all times; this is that. Tested against a mock device that writes
  a frame into a posted buffer and completes it, then re-delivers after repost.

- VirtioGpu::present issues transfer_to_host_2d then resource_flush (the
  per-frame present loop), each on its own pipelined command/response pair.
  Tested: the device sees the transfer first, then the flush.

Verified: cargo test green (57 unit + 11 integration), no_std + bare metal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PiDS8Ac9HyB9qNvZtYiMGa
… servers

Complete the daemon data paths and add the OS-facing scheme glue.

- virtio-netd: allocate and pre-post an RX buffer pool (RxPool), then on each
  interrupt drain TX completions, take received frames, and re-post their
  buffers to keep the RX ring full (notifying the device when buffers return).

- virtio-gpud: synchronous bring-up -- discover monitors, configure them all in
  one atomic modeset, create a 2D resource, attach a framebuffer, set scanout 0,
  and present an initial frame (transfer + flush) -- then run the IRQ loop. A
  small wait_completion helper drives the control queue during bring-up.

- scheme.rs (Redox-only): NetScheme (network:) and FbScheme (framebuffer)
  SchemeMut skeletons plus a serve() packet loop -- the OS-facing glue clients
  talk to. Clearly marked as the remaining, unverified-in-sandbox piece; the
  production loop multiplexes the scheme socket with the IRQ via the event:
  scheme.

README updated: data paths done and host-tested in driver-primitives; the
scheme wiring is what's left.

Verified: cargo check green on host (daemon logic type-checks through the stub
platform); the cfg(redox) binding and schemes compile on the Redox target.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PiDS8Ac9HyB9qNvZtYiMGa
… governor)

Power is the kit absorbing another driver class -- four thin orchestrators,
each reusing a primitive proven elsewhere:

- power::budget::PowerBudget -- weighted power/thermal budget allocation
  (weighted max-min fair): share a watt/thermal cap across components by
  weight, cap each at its request, redistribute headroom, flag the throttled.
  The allocation cousin of the scheduler's FairQueue.

- power::states::PowerManager -- atomic suspend/resume via Transaction. Suspend
  stages every device, validates none vetoes (a busy device blocks suspend),
  and only then commits D3 to all -- all-or-nothing, like the multi-screen
  modeset. Resume restores D0.

- power::sensors::Sensors -- battery/thermal monitoring as an EventQueue of
  edge-triggered threshold crossings (low/critical battery, thermal
  warning/critical), re-arming only after recovery so steady alarms don't flood.

- power::governor::Governor -- CPU P-state/C-state selection with a hysteresis
  dead-band so frequency doesn't oscillate; deeper C-states with more idle.

Verified: cargo test green (68 unit + 11 integration), no_std + bare metal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PiDS8Ac9HyB9qNvZtYiMGa
Two more device classes, same kit.

- virtio::blk::VirtioBlk -- block read/write as a three-descriptor chain
  (readable header with type+sector, the data buffer device-writable for read /
  driver-readable for write, a one-byte device-writable status). read/write
  helpers serialize the header and chain the descriptors; status_of reads the
  result byte.

- virtio::rng::VirtioRng -- the smallest virtio driver: hand the device a
  writable buffer, it fills it with entropy. Pure VirtQueue reuse.

Both host-tested against mock devices: blk read fills the data buffer and
returns status OK with all three descriptors freed; rng fills the requested
buffer and reports the byte count.

Verified: cargo test green (68 unit + 13 integration), no_std + bare metal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PiDS8Ac9HyB9qNvZtYiMGa
Two more device classes, finishing the common virtio set.

- virtio::input::VirtioInput -- keyboard/mouse/tablet. The device delivers
  virtio_input_events into pre-posted buffers, which is the NIC's receive
  pattern, so it reuses RxPool verbatim; parse_event decodes the 8-byte event.
  Tested: a key-press event is delivered, parsed (EV_KEY, code 30, value 1),
  and the buffer reposted.

- virtio::snd::VirtioSnd -- PCM playback. A control queue carries
  set_params/prepare/start/stop and a transmit queue carries audio buffers
  (xfer header + data + status). Tested: set_params and start are acknowledged
  OK and a playback buffer round-trips with an OK status.

With these, the kit drives the full common virtio family -- net, gpu, blk, rng,
input, snd -- plus display and power, all from the same primitives.

Verified: cargo test green (68 unit + 15 integration), no_std + bare metal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PiDS8Ac9HyB9qNvZtYiMGa
Complete the driver set: every virtio device class now has a Redox userspace
daemon, each following the netd/gpud shape -- construct RedoxPlatform, map the
device, bring it up via the kit, drive its data path, run runtime::run on the
IRQ.

- virtio-blkd: bring up the request queue, read sector 0, service loop.
- virtio-rngd: request entropy and re-request on completion.
- virtio-inputd: pre-post event buffers, deliver and re-post input events.
- virtio-sndd: control + transmit queues, configure PCM stream 0, service loop.

Six daemons total (net, gpu, blk, rng, input, snd). README updated.

Verified: cargo check green on host for all bins (daemon logic type-checks
through the stub platform); the cfg(redox) glue compiles on the Redox target.
Live functional verification is the QEMU boot (the image-build step).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PiDS8Ac9HyB9qNvZtYiMGa
Run rustfmt and clippy across both crates and fix everything they flag:
collapsed nested ifs into let-chains, used the ? operator where clippy
preferred it, gave PowerManager::suspend a real SuspendVetoed error type
instead of Result<(), ()>, and silenced one test-only needless_range_loop
where the index is also the client id.

No behaviour change. Both crates are now rustfmt-clean and clippy-clean.

Verified: cargo test green (68 unit + 15 integration), clippy clean, fmt clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PiDS8Ac9HyB9qNvZtYiMGa
Add CI that runs on GitHub's infrastructure (full internet, unlike the dev
sandbox), so the work is verified on every push.

- Pin driver-primitives and redox-virtio-drivers to stable Rust via their own
  rust-toolchain.toml, decoupling them from the kernel's nightly pin. The kit
  builds, tests, lints, and compiles for bare metal on stable; this makes CI
  robust regardless of nightly availability.

- .github/workflows/ci.yml jobs:
  - driver-primitives: fmt --check, clippy -D warnings, test, bare-metal build.
  - redox-virtio-drivers: fmt --check, clippy -D warnings, host check (daemon
    logic through the stub Platform).
  - kernel-check: nasm + `make check` on the kernel's nightly.
  - redox-cross-build (experimental, continue-on-error): cross-compile the
    drivers for the real Redox target via redoxer, which compiles the
    cfg(target_os = "redox") binding and catches redox_syscall API drift.

This is the answer to "can we boot/test on GitHub": Actions can reach gitlab,
install QEMU, and run redoxer, none of which the sandbox can. The first three
jobs I'm confident in; the redox-cross-build job is best-effort and we iterate
by watching CI.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PiDS8Ac9HyB9qNvZtYiMGa
- scheme.rs: NetScheme now holds the rx/tx frame queues that bridge clients and
  the driver -- deliver() feeds received frames in (from RxPool), read() pops
  one; write() enqueues a frame and the driver drains take_tx() into
  VirtioNet::transmit. FbScheme tracks flip requests (take_flips) for
  VirtioGpu::present. The data-path wiring is now real; the remaining glue is the
  event: multiplexing of the scheme socket with the device IRQ.

- BOOTING.md: turnkey runbook -- prerequisites, cookbook recipe, handing each
  daemon its device, the event-multiplexed loop shape, QEMU device flags,
  make qemu, and the redoxer fast inner loop.

Compiled only on Redox (cfg(target_os = "redox")); the redox-cross-build CI job
type-checks it via redoxer. Host check stays green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PiDS8Ac9HyB9qNvZtYiMGa
…l 0.8.1 API

The redox-cross-build CI job (redoxer) compiled the cfg(redox) code against the
actual pinned redox_syscall and caught that it targeted an API that does not
exist in this version. Fixes from reading the pinned source:

- Cargo.toml: enable the `userspace` feature. The kernel uses
  default-features=false (it *implements* syscalls); a driver *calls* them, so
  it needs `userspace`, which gates the syscall wrappers (open/read/write/...).
  Copying the kernel's setting was the root cause of the unresolved imports.

- platform.rs: this redox_syscall has no physalloc/physmap/PhysmapFlags and no
  plain `open`. Rewrite the binding against what it actually provides: openat,
  read, write, close, and fmap/funmap with Map/MapFlags for mapping DMA and MMIO
  regions from the memory scheme. Runtime specifics (scheme path, phys-address
  reporting, the openat base fd) are boot-verified; documented inline.

- scheme.rs: the SchemeMut/Packet server API is not in redox_syscall (it lives
  in the modern redox-scheme crate). Reduce scheme.rs to the OS-agnostic
  data-path queues (NetScheme rx/tx, FbScheme flips), which are now host-tested;
  the scheme server that drives them is the redox-scheme integration noted in
  BOOTING.md.

Net effect: the binding now uses real, version-correct APIs and the scheme
logic is host-tested. Remaining boot-time work: the redox-scheme server + the
event: multiplexing, and validating the memory-scheme mapping live.

Verified: host cargo test green (incl. new scheme tests), clippy clean. The
redox-cross-build CI job re-checks the cfg(redox) compile.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PiDS8Ac9HyB9qNvZtYiMGa

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bbd5a8a66e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread redox-virtio-drivers/src/platform.rs Outdated
// The device-visible address. The memory scheme reports this back on
// map; wiring that is the boot-time step (see BOOTING.md). For an
// identity-mapped region it equals the virtual address.
self.virt as u64

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Return a real DMA physical address

On Redox, the values returned by DmaRegion::phys_addr() are programmed into virtqueue descriptors and queue registers for the device to DMA to, but this implementation reports the userspace virtual mapping instead of an allocated device-visible physical address. Unless the process mapping is identity-mapped, every real virtio device will read/write the wrong memory, so the Redox drivers cannot reliably boot; store the physical address returned by the DMA allocation/mapping path and return that here.

Useful? React with 👍 / 👎.

}
let causes = mmio.interrupt_status();
service();
mmio.ack_interrupt(causes);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Acknowledge device interrupts before servicing

Because causes is captured before service() but ack_interrupt(causes) is delayed until after it, a device completion that arrives after the service closure's final poll but before this ack can set the same interrupt-status bit and then be cleared by this stale ack. In that timing the used-ring entry remains pending but the IRQ is no longer pending/rearmed, so daemons can sleep until an unrelated interrupt; acknowledge the captured causes before draining, or re-check after ack.

Useful? React with 👍 / 👎.

Comment on lines +95 to +97
gpu_dev
.present(&cmd, &resp, &tcmd, &tresp, FB_RESOURCE, rect, 0)
.map_err(|_| PlatformError::Unsupported)?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Notify the GPU queue after presenting

This queues the transfer and flush descriptors, but the daemon enters runtime::run() without notifying queue 0. Since runtime::run() blocks waiting for an IRQ, and the device is not told about the newly available descriptors, the initial frame can remain unprocessed forever unless some unrelated interrupt happens; notify the control queue or wait for these completions after present().

Useful? React with 👍 / 👎.

claude added 2 commits June 20, 2026 20:23
…s addr)

Three fixes from the PR review:

- runtime::run: acknowledge the device interrupt *before* draining the
  queues, not after. Acking after leaves a lost-completion race: a used-ring
  entry that lands between the service closure's last poll and the ack has its
  freshly-set interrupt-status bit cleared by the stale ack, so it sits pending
  with no armed IRQ. Acking first means any post-drain completion re-raises the
  interrupt and wakes the next iteration.

- virtio-gpud: notify queue 0 after present() so the device actually processes
  the queued transfer/flush descriptors; otherwise runtime::run blocks on an
  interrupt that never arrives for that work.

- RedoxDma: allocate from the memory:physical scheme and read back the real
  device-visible physical base via fpath, and return that from phys_addr()
  instead of the userspace virtual mapping. Programming a virtual address into
  a virtqueue descriptor would have the device DMA to the wrong memory.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PiDS8Ac9HyB9qNvZtYiMGa
Applies the same primitive/orchestrator split already used for the EEVDF
scheduler (`context::eevdf`) to the ptrace subsystem's pending-event queue.

- Add `common::event_ring::EventRing<T, N>`: a bounded, allocation-free,
  lock-free FIFO with overflow accounting (drop newest + count, like inotify's
  IN_Q_OVERFLOW). It is the kernel-side instantiation of the driver kit's
  Ring/EventQueue primitive, holds no policy beyond the overflow rule, and is
  unit-tested in the clean basis (5 host tests: FIFO order, overflow counting,
  bounded drain, wrap reuse, the empty->non-empty notify edge).

- Rewire `ptrace::SessionData` to delegate to it. The orchestrator keeps the
  policy it always had — the lock, the tracer wakeups, and the empty->non-empty
  edge that triggers EVENT_READ — while the queue mechanism moves behind the
  seam. `recv_events` becomes a `drain_into`; `add_event` posts and warns on
  overflow.

Behavioural note: the event queue was previously an unbounded VecDeque. The
stop/continue handshake runs over the tracee/tracer WaitConditions and
`breakpoint.reached`, not this queue, so the queued events are purely
informational; bounding them (128 slots, ~8 KiB/session) turns a slow/absent
tracer from an unbounded kernel allocation into an observable, counted overflow.

Verified: `make check` (the CI kernel gate) green; full `cargo test` green
(18 tests incl. the new 5); changed files rustfmt-clean.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PiDS8Ac9HyB9qNvZtYiMGa
@Rekonquest
Rekonquest merged commit 5372d38 into master Jun 21, 2026
8 checks passed
Rekonquest pushed a commit that referenced this pull request Jun 23, 2026
The simulators #1 optimization core (stoer-wagner-mincut), as a dumb,
host-tested kit primitive: the cheapest split of a weighted graph into two
non-empty parts, over a fixed-capacity NxN matrix (no_std, alloc-free). It is
the cross-domain optimization->security bridge in code — composed with the
AuthorityWall delegation graph it computes graph-minimal QUARANTINE (the
fewest authority edges to cut to isolate a compromised domain). Also serves
resource-placement (cut a dataflow where least data crosses) and clustering.

Verified: 5 tests incl. the classic Stoer-Wagner (1997) example (min cut = 4)
and a partition test; clippy -D + bare-metal + kernel make check green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants