driver-lab proxy driver specification

This is the normative specification for the engineering-only driver-lab proxy driver (lab_proxy) at //src/devices/driver-lab. The companion host tooling specification lives at //tools/driver-lab/SPEC.md.

The specification is delivered in three phases. Phase 1 -- unclaimed-node binding through existing Driver Framework mechanisms, with no Driver Manager changes -- is delivered first. Phase 2 -- in-situ cooperative debugging via an embedded driver-lab library -- extends the architecture to existing, active drivers without managed takeover or Driver Manager modifications, allowing live register inspection while preserving device state. Phase 3 -- software state banks, runtime knobs, and diagnostic triggers -- extends in-situ debugging beyond physical hardware registers so host plans can observe internal driver state, inject bounded race-window delays, toggle candidate fixes at runtime, and trigger diagnostic actions in a single build.

Phase 1: unclaimed-node proxy

Implementation status

Changeset numbers (CS) are global across this specification and //tools/driver-lab/SPEC.md, whose changesets interleave with these; gaps in the numbering below are driver-lab changesets. Every changeset marks itself complete in this list when it lands, so at any commit this list shows which changes precede and which follow that commit.

  • [x] CS1 [driver-lab] Proxy driver specification -- this document.

  • [x] CS3 [driver-lab] Phase 1 proxy driver core -- the takeover-ready fuchsia.driver.lab wire contract (sections 7.4, 11) and the host-testable policy core: ceiling and exact allowlist enforcement (9, 10), per-boot identity and description digests (8.3, 10.2), bounded reads and snapshots (12.1, 12.5), session lifecycle and leases (16), and the audit ring (18). Bind rules are false and no resources are offered.

  • [x] CS9 [driver-lab] Platform resource provider, bind rules, realm tests -- resource acquisition and local MMIO mapping (8.4, 15), the PROXY_TARGET bind library and property-gated binding (7.2, 7.3), driver realm tests (23.3), and the lab_root test fixture serving a fake platform device (23.3, and the live-activation path documented by CS10).

  • [x] CS10 [driver-lab] Add live-target conformance runbook (in //tools/driver-lab/SPEC.md).

  • [x] CS11 [driver-lab] Host node discovery via fuchsia.driver.development (in //tools/driver-lab/SPEC.md).

  • [x] CS12 [driver-lab] Direct mode client and published-protocol workflow (in //tools/driver-lab/SPEC.md).

  • [x] CS13 [driver-lab] Target ceiling policy manifests and verification (9.2, 9.6, 22, 23.1; milestone P1 remainder).

Remaining phase 1 work:

  • [x] CS14 [driver-lab] Bounded sequences and mutation in proxy driver (11.4, 11.5, 12.2-12.4, 13; milestone P3).
  • [x] CS15 [driver-lab] Host tooling support for mutation and sequences (in //tools/driver-lab/SPEC.md).
  • [x] CS16 [driver-lab] Driver-shaped public Python API (in //tools/driver-lab/SPEC.md).
  • [x] CS17 [driver-lab] Stop-path hardening and cancellation (19; milestone P2).
  • [x] CS18 [driver-lab] Host CLI subtool expansion and verified teardown (in //tools/driver-lab/SPEC.md).
  • [x] CS19 [driver-lab] Protocol resources, typed endpoints, and heterogeneous sequence (8.1, 8.4, 9.5, 11.5, 11.6, 12, 14.2; milestone P4).
  • [ ] Engineering assembly inclusion and production-absence verification (7.1, 25).
  • [x] CS20 [driver-lab] Interrupt observation and local acknowledgement (11.7, 17; milestone P5).
  • [x] CS21 [driver-lab] Structured configuration and per-class rate limits (12.1, 22).
  • [x] CS22 [driver-lab] Serial capture and independent recovery integration (in //tools/driver-lab/SPEC.md; milestone H5).

Phase: 1 -- new-driver development. The proxy binds to unclaimed nodes through existing Driver Framework mechanisms. Existing-driver debugging via an embedded library is specified in the Phase 2 section of this document.

Scope: standalone proxy driver, target policy, resource execution, audit, and lifecycle.

Companion specification: host tooling at //tools/driver-lab/SPEC.md

1. Purpose

The proxy driver provides exclusive, resource-scoped hardware access on a Fuchsia target for interactive and agent-assisted driver development. It runs locally on the target, maps only resources offered to its device node, executes bounded operations, enforces a target ceiling and session allowlist, and records a target-side audit trail.

The driver-lab capability supports two deployment situations:

  1. it is selected as the standalone proxy driver for an unclaimed device node during new-driver exploration (phase 1, specified here); or
  2. its core functionality is embedded as a library in an existing, active driver for in-situ live debugging (phase 2).

The core policy, execution, audit, and wire contracts serve both situations. Phase 1 specifies the standalone driver; Phase 2 specifies the embedded library.

2. Normative language

The terms must, must not, should, and may describe requirements, recommendations, and optional behavior respectively.

The following terms have specific meanings:

  • Proxy: The standalone DFv2 driver defined here.
  • Target ceiling: Immutable target policy describing the greatest access the proxy may provide for a node/resource class.
  • Session allowlist: Exact plan-derived permissions supplied when a session opens. It may only narrow the target ceiling.
  • Managed takeover: Driver Manager stops a node's current driver, binds the proxy, and later restores the original driver. Defined in the Phase 2 section of this document.
  • Resource identity: A stable logical description and digest for an offered resource. It never includes a host-provided physical address.
  • Partial completion: An execution in which at least one operation began but the complete plan did not finish successfully.

3. Goals

The proxy must:

  1. Run in a dedicated driver host.
  2. Obtain resources only from the node to which it is bound.
  3. Support platform-, PCI-, and other parent-native resource providers through narrow backend adapters.
  4. Map MMIO locally with device-appropriate cache policy.
  5. Retain all objects required for each mapping's lifetime.
  6. Represent resources by stable logical IDs and names.
  7. Describe resource size, kind, supported operations, target ceiling, and identity digest.
  8. Enforce both the target ceiling and the exact session allowlist immediately before every access.
  9. Execute bounded MMIO reads, snapshots, masked writes, polls, delays, and barriers.
  10. Support resource-specific GPIO, SPI, I2C, serial, clock, reset, and interrupt operations without erasing their production semantics.
  11. Execute timing-sensitive sequences locally when explicitly requested.
  12. Serialize mutation and reject ambiguous concurrent execution.
  13. Report explicit per-operation status and partial completion.
  14. Record attempted, rejected, successful, failed, and cleanup operations in a bounded audit ring.
  15. Close or cancel every outstanding request during stop.
  16. Contain an experimental driver fault away from Driver Manager and unrelated production drivers.
  17. Be included only in authorized engineering configurations and remain inactive until explicitly bound.

4. Non-goals

The proxy must not:

  • accept an arbitrary physical address;
  • request root resource;
  • export MMIO VMOs, interrupt handles, BTIs, IOMMUs, or root-like capabilities to the host;
  • map resources belonging to another node;
  • share ownership with the normal driver for the same node;
  • inspect the memory or private state of an active production driver;
  • run inside Driver Manager;
  • execute arbitrary uploaded code or target-side scripts;
  • load a host-provided policy that widens the target ceiling;
  • claim transactional rollback for MMIO;
  • automatically restore every register from captured “before” values;
  • permit unbounded polling, sleeping, snapshots, vectors, or audit output;
  • expose DMA programming, arbitrary PCI configuration writes, or SMC calls in the initial protocol;
  • bind broadly to normal nodes without explicit Driver Manager selection or engineering-only bind metadata;
  • coordinate operations across multiple device nodes in one session;
  • guarantee sub-millisecond timing precision for delays, polls, or inter-operation gaps in V1; or
  • appear in a production image.

5. Architecture

Host tooling
    |
    | ffx target transport
    v
Driver Manager control plane
  discovery: fuchsia.driver.development
    |
    | bind proxy (phase 1) / connect embedded endpoint (phase 2)
    v
Target Component (standalone lab_proxy in phase 1, active driver in phase 2)
    |
    +-- policy and session server
    +-- MMIO executor
    +-- protocol-resource adapters
    +-- interrupt tracker / event tap
    +-- audit ring
    |
    v
Hardware resources mapped to the target driver

Driver Manager owns discovery (fuchsia.driver.development). The session server owns the experimental session and bounded hardware execution after connection.

6. Design invariants

6.1 Capability confinement

The proxy can access only resources offered to its bound node. It must use the parent's normal platform, PCI, or bus protocol. It must not obtain a global resource and reconstruct mappings from host input.

6.2 Hardware safety and concurrency

In phase 1 the standalone proxy binds only to nodes with no bound driver. In phase 2 the embedded library runs inside the active driver and enforces safety through cooperative locking, quiesce hooks for mutating sequences, and ceiling denials on destructive reads.

6.3 Target ceiling is authoritative

The host may provide an exact read consent list and an approved plan, but it cannot:

  • add a resource;
  • increase a resource size;
  • add an operation width;
  • remove a hard deny;
  • increase a target deadline;
  • enable a disabled resource class; or
  • widen a write mask.

6.4 Local execution

MMIO mappings and interrupt objects remain on the target. FIDL carries scalar operations, protocol transactions, results, descriptions, and audit entries.

6.5 Programming-model fidelity

The target wire contract should preserve the meaning of normal driver operations:

  • protocol-backed resources retain production FIDL request and response semantics where possible;
  • MMIO remains explicitly width- and offset-based;
  • interrupt waiting remains asynchronous;
  • reset and clock state changes remain explicit;
  • SPI/I2C/serial exchanges do not become generic byte writes; and
  • errors are not collapsed into prose.

A generic sequence protocol may coordinate heterogeneous operations locally, but it must identify the original resource operation precisely. It is an execution mechanism, not a replacement programming model.

6.6 Bounded execution

Every request has target-enforced bounds. No host field can select an infinite timeout, unconstrained vector, or blocking dispatcher sleep.

6.7 Explicit partial completion

Writes generally cannot be undone. Execution reports every operation that began, stops according to declared error rules, and never labels the sequence atomic.

6.8 Audit is diagnostic, not durable authorization

The proxy audit establishes what it attempted under a run/session context. It does not prove that the host approval was issued by a particular human.

7. Deployment and activation

7.1 Engineering-only packaging

The proxy package should be present by default in the engineering configurations that support driver-lab workflows. Presence alone grants no hardware access.

Production images must not offer any of the following, and the build must verify this:

  • the proxy driver package;
  • the Driver Manager takeover protocol (phase 2);
  • special debug bind metadata;
  • target debug policy artifacts; or
  • debug capability routes.

The verification mechanism (package absence, a Driver Manager binary variant, or config gating with capability-route verification) is owned by platform assembly. This specification requires only that the chosen mechanism be verifiable at build time.

7.2 Inactive by default

The proxy must not opportunistically claim normal device nodes. In phase 1 it becomes eligible only through explicit engineering-only node metadata or a developer-created test node; it may be loaded through engineering assembly inclusion or ephemeral registration (fuchsia.driver.registrar). Plans cannot supply a driver URL.

In phase 2 a Driver Manager operation may additionally select the fixed proxy identity for an exclusive managed takeover. That node-scoped force-bind is new Driver Manager work (the existing DisableDriver is global by URL and unsuitable) and is specified in the Phase 2 section of this document.

7.3 Direct selection during development

For hardware without a normal driver, an engineering product or explicit developer action selects the proxy at initial bind. This is the phase 1 activation path. It uses the same policy, FIDL, evidence, and session implementation that the embedded library (phase 2) reuses.

7.4 Embedded library in-situ debugging (phase 2)

In-situ debugging -- embedding the driver-lab server into an active driver, sharing mapped MMIO via VMO duplication, coordinating concurrency via quiesce hooks, and tapping interrupts -- is specified in the Phase 2 section of this document. The phase 1 wire contract serves both standalone and embedded deployments with no major version change.

8. Resource model

8.1 Resource kinds

The extensible resource model begins with:

  • MMIO region;
  • interrupt;
  • GPIO protocol;
  • SPI protocol;
  • I2C protocol;
  • serial protocol;
  • clock protocol; and
  • reset protocol.

Additional kinds require explicit capability descriptions, bounds, tests, and threat-model review.

8.2 Resource description

Each resource description contains:

  • numeric ID scoped to the proxy instance;
  • stable logical name when available;
  • resource kind;
  • provider identity/type;
  • logical size where applicable;
  • supported access widths or protocol methods;
  • read/write classification;
  • target limits;
  • stable metadata used to compute a resource-description digest; and
  • flags identifying missing or provisional metadata.

Descriptions never expose physical addresses or raw target handles.

8.3 Resource digest

The digest is computed from canonical descriptions including:

  • node identity criteria;
  • provider kind;
  • resource ordinal/name;
  • logical size;
  • operation widths;
  • the immutable target-ceiling entries that apply to this resource, not the whole policy manifest; and
  • relevant stable parent metadata.

Dynamic handle values, mapping addresses, boot IDs, and service-instance names are excluded.

The digest allows a persistent host read grant to stop matching when resource layout or policy changes. Scoping the ceiling input per resource ensures that a policy change on one resource does not invalidate grants on unrelated resources.

8.4 Parent adapters

Use an injected ResourceProvider interface with implementations appropriate to the target node:

ResourceProvider
    DescribeResources()
    OpenMmio(id)
    OpenInterrupt(id)
    ConnectProtocol(id, kind)

PlatformResourceProvider
PciResourceProvider
Other reviewed bus-specific providers
FakeResourceProvider

Do not place bus-specific address translation in the FIDL server or policy engine.

If a parent exposes a larger region than the proxy's logical resource, checked translation must verify both the logical resource and actual backing mapping:

backing_offset = logical_base + request_offset

All arithmetic is checked before access.

Resource providers communicate with the parent over zircon channel-transport FIDL. The isolated driver-host requirement (goal 1) already forces this: driver-transport (fdf) protocols require colocation with the parent. Parents that offer resources only over driver transport are ineligible for proxy access in V1; whether a colocated mode is ever introduced is a phase 2 open decision.

9. Target policy

9.1 Policy layers

The proxy evaluates:

actual resource bounds
    ∩ immutable target ceiling
    ∩ session allowlist
    ∩ session mode
    ∩ per-operation structural validation

Every layer must permit the operation.

9.2 Immutable target ceiling

The ceiling comes from engineering assembly, board/bus metadata, or immutable package content. Runtime configuration may narrow it but cannot widen it.

It specifies:

  • permitted resource providers and resource kinds;
  • maximum logical region sizes;
  • access widths;
  • hard-denied read offsets or ranges;
  • whether unknown reads may be enabled by a session grant;
  • exact writable offsets and masks;
  • precondition/readback requirements;
  • permitted protocol methods and maximum transaction sizes;
  • interrupt observation permission;
  • maximum operations, snapshot items, poll time, delay, and sequence duration;
  • audit capacity; and
  • cleanup rules.

9.3 Unknown reads

For a resource whose ceiling permits operator-authorized exploration, an otherwise unclassified aligned read may be allowed only when:

  • the offset fits the logical and actual resource;
  • it is not hard-denied;
  • the exact resource, offset, width, and access class appear in the session allowlist; and
  • all rate, count, and duration limits are satisfied.

This is an engineering risk decision. The proxy does not claim that an allowed unknown read is side-effect free.

A session request to sweep a range is expanded into exact offsets before the session opens. The proxy does not accept a wildcard “all offsets in this region” allowlist rule.

9.4 Writes

Writes are denied unless the immutable target ceiling contains an exact writable-register entry. A host/session grant cannot invent a write entry.

Each entry specifies:

  • offset and width;
  • allowed mask;
  • whether read-modify-write is valid;
  • allowed precondition mask;
  • whether a precondition is required;
  • readback mask and semantics;
  • barrier requirements;
  • failure classification; and
  • optional explicitly reviewed cleanup.

Write-one-to-clear, self-clearing, FIFO-like, or destructive-read registers require a distinct operation semantic. Do not overload ordinary masked writes.

9.5 Protocol-backed resources

For GPIO, SPI, I2C, serial, clock, reset, and future protocols, policy uses method-specific bounds rather than pretending they are MMIO:

  • method allowlist;
  • transfer length;
  • bus address or chip-select restrictions where applicable;
  • configuration ranges;
  • timeout/deadline;
  • permitted state changes; and
  • reset/cleanup behavior.

9.6 Policy generation

When policy is expressed in JSON5 or another checked-in format, generate:

  1. a target-native immutable table; and
  2. a canonical manifest returned in Describe.

Generation rejects duplicate IDs/names, overflow, ambiguous overlap, invalid masks, unreachable cleanup, nondeterministic ordering, and limits above FIDL maxima.

Hash the canonical manifest bytes. Evidence records the digest.

10. Session allowlist

10.1 Purpose

The session allowlist is derived from the canonical host plan after permission resolution. It prevents a stale or buggy client from accessing anything beyond the frozen run, even though it is not cryptographic proof of approval.

10.2 Required identity

Opening a session includes:

  • run and case IDs;
  • plan digest;
  • host tool version;
  • expected boot ID;
  • expected proxy generation;
  • expected resource-description digest;
  • expected target-policy digest;
  • read-only or mutating mode;
  • exact allowed operations or access rules; and
  • bounded plan limits.

The proxy rejects a stale identity before access.

10.3 Narrowing validation

Before opening the session, the proxy validates the entire allowlist against the target ceiling. A rejected allowlist creates no session and performs no hardware operation.

A read-only session can never issue a write even if a write appears in target policy. A mutating session additionally requires an exclusive mutation lease.

11. Wire contract

The final FIDL syntax must match the selected Fuchsia revision. The semantic contract below is normative. The V1 contract is implemented as the fuchsia.driver.lab library with the Proxy (Describe, OpenSession) and Session (Read32, Snapshot, ReadAudit) protocols.

11.1 Limits

The protocol declares compile-time bounds for:

  • resource names and device IDs;
  • resource count;
  • operation count;
  • snapshot item count;
  • protocol transaction size;
  • audit page size;
  • detail strings;
  • session allowlist rules; and
  • total encoded response size.

Runtime target limits may be smaller.

11.2 Proxy description

Describe returns:

  • protocol major/minor;
  • proxy implementation version;
  • proxy instance/generation;
  • boot ID;
  • node identity and topology generation;
  • takeover identity when applicable (reserved; populated only in phase 2);
  • target policy name/digest;
  • canonical resource descriptions and digest;
  • supported resource and operation kinds;
  • runtime limits;
  • audit capacity; and
  • fault-isolation/session capabilities.

11.3 Session creation

OpenSession accepts run context, session mode, identity expectations, and the exact allowlist. It returns a session channel and session ID.

Read-only sessions may coexist when safe. Exactly one mutating session exists per proxy instance. Target-local sequences are serialized initially, including read-only sequences whose interleaving would make evidence ambiguous.

The allowlist is fixed for the session's lifetime in V1. Expanding consent requires closing the session and reopening it with the expanded allowlist; a narrowing-only ExtendAllowlist operation is a deferred open decision (section 26).

11.4 MMIO operations

The initial scalar MMIO methods are:

  • Read32(resource_id, offset);
  • Write32(resource_id, offset, value, write_mask, precondition, readback);
  • Snapshot(items); and
  • Poll32(resource_id, offset, expected, mask, interval, timeout).

Additional widths require protocol and policy revision. Offsets are bytes from the start of a logical resource.

11.5 Ordered sequence

ExecuteSequence accepts a bounded ordered list containing:

  • resource-specific scalar operations;
  • delay;
  • memory barrier; and
  • only those protocol transactions whose exact semantics are represented.

Every entry identifies resource kind, resource ID, and operation-specific fields. Unknown operations produce ZX_ERR_NOT_SUPPORTED.

The sequence is not atomic. Whole-sequence structural/policy validation occurs before the first hardware operation. Hardware preconditions are evaluated at execution time.

11.6 Resource-specific endpoints

Where practical, a session should open a typed endpoint for a standard protocol-backed resource. The endpoint:

  • preserves generated FIDL request/response types;
  • is bound to the session identity;
  • enforces the same target policy and allowlist;
  • writes the same audit records; and
  • observes the same deadlines and cancellation.

If an exact production protocol cannot represent necessary debug policy or audit behavior, define a narrowly shaped adapter and document the difference.

11.7 Interrupt observation

WaitForInterrupt(resource_id, after_sequence, timeout) is a bounded hanging request. The proxy retains and acknowledges the interrupt locally.

The report includes:

  • interrupt resource ID;
  • event sequence;
  • total count;
  • target timestamp; and
  • coalesced/overflow information.

The initial protocol observes interrupts only. It does not let the host change polarity, routing, or masking.

11.8 Audit

ReadAudit(cursor, limit) returns a bounded page and reports the oldest available sequence so the host can detect gaps.

11.9 Versioning

  • Increment minor for optional compatible fields and safely rejectable flexible operations.
  • Increment major for changed operation meaning, policy guarantees, session semantics, or evidence interpretation.
  • Never reuse an ordinal with different meaning.
  • The host requires an exact major version.
  • Golden descriptions and compatibility matrices cover every supported major.

12. MMIO semantics

12.1 Read validation

Immediately before every read:

  1. verify session and proxy generation;
  2. find the resource;
  3. require supported width and alignment;
  4. check offset + width for overflow;
  5. require the access within logical size;
  6. require the access within actual mapped size;
  7. enforce target read ceiling/hard denies;
  8. match the exact session allowlist;
  9. enforce access-class count/rate/deadline limits; and
  10. perform one volatile read.

Rejected accesses are audited without touching hardware.

12.2 Masked writes

For an ordinary 32-bit masked write:

policy_mask = immutable target mask
requested   = request.write_mask

require requested != 0
require (requested & ~policy_mask) == 0

if requested covers the full register width
   and no precondition is configured or requested:
    volatile_write32(request.value)
else:
    require read-modify-write valid for this policy entry
    before = volatile_read32()
    require precondition if configured
    after = (before & ~requested) | (request.value & requested)
    volatile_write32(after)

apply required barrier
read back configured stable bits if required

The before-read in the read-modify-write path is itself a hardware read with potential side effects; it is audited, and it is permitted only where the policy entry declares read-modify-write valid. Write-only registers must be declared full-mask, non-read-modify-write, and without preconditions.

Reject rather than silently narrow a requested mask.

12.3 Polls and delays

Polls use asynchronous timers on the driver dispatcher. They never busy-wait and never block the dispatcher with sleep.

Dispatcher timers provide millisecond-class scheduling precision. V1 makes no sub-millisecond guarantee for delays, polls, or inter-operation gaps; whether ceiling-bounded short spin-delays are permitted for microsecond-class sequencing is an open decision (section 26). Declared timing precision is part of capability reporting.

The proxy enforces:

  • maximum interval frequency;
  • maximum timeout;
  • maximum iteration count;
  • sequence wall-clock deadline; and
  • cancellation on session close or stop.

12.4 Barriers

Use the architecture/platform MMIO primitives from the selected Fuchsia tree. A barrier orders accesses; it does not prove device-internal completion.

12.5 Snapshot

Validate every item before the first read. The snapshot is an ordered series of reads, not an atomic hardware snapshot. Each value includes an individual timestamp, status, and audit sequence.

13. Execution and dispatcher model

Start with one synchronized dispatcher for:

  • FIDL requests;
  • session state;
  • execution state machines;
  • timers;
  • interrupt callbacks;
  • audit ordering; and
  • driver stop.

Use an asynchronous sequence state machine:

ValidateWholeSequence
    |
    v
BeginOperation
    +-- immediate operation -> Record -> next
    +-- delay -> arm timer -> resume -> Record -> next
    +-- poll -> read -> matched/timeout/arm interval
    +-- protocol wait -> async completion -> Record -> next

A second target-local sequence receives ZX_ERR_SHOULD_WAIT until safe concurrency semantics are designed.

On failure:

  • stop when stop_on_error is true;
  • continue only for explicitly recoverable errors when it is false;
  • never continue after stop, peer closure, invalid backend state, or a policy-classified fatal error; and
  • initially require stop_on_error = true for every mutating sequence.

14. Core implementation boundaries

Use narrow testable modules:

ProxyDriver
    lifecycle, outgoing service, instance identity

ResourceProvider
    node/parent-specific acquisition

HardwareBackend
    MMIO, barriers, clock, and injected protocol operations

AccessPolicy
    actual bounds + target ceiling + session allowlist

Executor
    scalar operations and asynchronous sequences

SessionServer
    run context, allowlist, leases, cancellation

ProtocolResourceAdapter
    GPIO/SPI/I2C/serial/clock/reset endpoints

InterruptTracker
    async waits, counters, acknowledgment

AuditRing
    bounded ordered target evidence

FIDL conversion stays at service boundaries. Policy and executor tests run without a component realm: the pure-logic modules form the lab_proxy_core crate, which builds for both the target and the build host so their tests run without an emulator.

15. Driver start

Start must:

  1. read immutable engineering configuration;
  2. load/generate the target ceiling;
  3. connect to the node's parent-native resource provider;
  4. enumerate offered resources;
  5. validate policy against actual resources;
  6. map permitted MMIO locally;
  7. obtain and register permitted interrupts;
  8. connect permitted protocol-backed resources;
  9. derive boot/proxy/resource identities and digests;
  10. create FIDL binding groups;
  11. publish the proxy service through the Driver Framework route expected by the Driver Manager control plane; and
  12. emit one structured ready event.

Start performs no experimental write. Avoid reads with possible side effects. An identity read during start is allowed only when explicitly declared safe and audited.

Start fails if a required resource is absent, undersized, ambiguous, or incompatible with policy.

16. Sessions and leases

A session owns:

  • run context;
  • exact allowlist;
  • read-only/mutating mode;
  • session ID;
  • pending timers/waits;
  • sequence state; and
  • audit cursor metadata.

On session channel close:

  • cancel timers and waits;
  • stop new operations;
  • release the mutation lease;
  • audit session closure;
  • run only explicitly configured fail-safe cleanup; and
  • finish within a bounded deadline.

Cleanup is fallible and audited. A resource with unknown safe cleanup has no automatic cleanup and relies on normal driver rebind, target reset, or out-of-band recovery.

17. Interrupt handling

For every interrupt callback:

  1. capture target monotonic time;
  2. increment count and sequence;
  3. calculate coalescing information where available;
  4. satisfy eligible waiters;
  5. append an event/audit entry; and
  6. acknowledge the interrupt on every path.

Use virtual interrupts in tests. An interrupt storm must not allocate unbounded memory or starve stop/restoration.

18. Audit ring

The fixed-capacity ring assigns monotonically increasing 64-bit sequence numbers. Wrap overwrites old entries but does not reset sequence.

The ring is in-memory: a driver-host crash loses undrained entries. Host workflows must therefore drain audit incrementally during mutating plans rather than only at run end.

Each entry contains:

  • target boot and proxy generation;
  • session and run context;
  • resource identity;
  • operation index and normalized operation;
  • allowlist match/grant reference when provided;
  • target policy decision;
  • status;
  • applicable before/after/value data;
  • start/completion or event timestamp; and
  • cleanup/failure classification.

Never audit pointer values, physical addresses, channel handles, or unbounded caller strings.

The driver layer mirrors each appended entry into system tracing through an append observer, keeping the core crate free of Fuchsia dependencies. Entries are written to the driver_lab trace category, or driver_lab:irq for interrupt taps, and successful writes also sample a per-resource counter. Mirroring must be a no-op unless a trace session enables the category, must not block, and must be disabled for embedded servers in builds without enable_driver_lab. Trace events are best-effort; the audit ring remains the authoritative record.

19. Stop

PrepareStop must:

  1. reject new sessions;
  2. stop accepting operations;
  3. cancel sequence timers and protocol waits;
  4. cancel interrupt waits;
  5. finish in-flight callbacks;
  6. close FIDL bindings with an epitaph;
  7. run only bounded configured cleanup;
  8. stop interrupt handlers;
  9. release mappings/resources; and
  10. complete stop within the Driver Manager stop deadline.

Every pending call terminates with a result, epitaph, cancellation, or bounded host timeout.

20. Error model

Use typed application errors and Zircon status consistently:

  • invalid/misaligned input;
  • out of resource bounds;
  • target ceiling denial;
  • session allowlist denial;
  • stale boot/proxy/resource identity;
  • failed precondition;
  • unsupported resource/method;
  • timeout;
  • cancellation;
  • mutation lease contention;
  • partial execution; and
  • fatal backend state.

Machine-parsed information must not exist only in a human detail string.

Execution reports contain:

  • sequence/batch ID;
  • first and last audit sequence;
  • result for every operation that began;
  • stopped-on-error flag;
  • partial-completion classification;
  • fatal/nonfatal classification; and
  • proxy health after execution.

21. Source layout

Exact paths may follow repository conventions:

src/devices/driver-lab/
    BUILD.bazel
    BUILD.gn
    meta/
    fidl/
        BUILD.bazel
        BUILD.gn
        driver_lab.fidl          fuchsia.driver.lab wire contract; moves to
                                 an internal-category SDK library once the
                                 contract stabilizes
    src/
        lib.rs                   driver lifecycle and service publication
        driver_lab_lib.rs        embedded Rust library crate root
        embedded.rs              EmbeddedLabServer and DriverLabBuilder
        c_api.rs                 C FFI staticlib shim (driver_lab_c)
        server.rs                FIDL session server
        trace.rs                 audit-to-trace mirroring (driver_lab categories)
        core.rs                  host-testable core crate root
        access_policy.rs
        audit_ring.rs
        digest.rs
        executor.rs
        hardware_backend.rs
        session.rs
        resource_provider.rs           planned: parent adapters (P0/P1)
        protocol_resource_adapter.rs   planned (P4)
        interrupt_tracker.rs           planned (P5)
    cpp/
        include/lib/driver_lab/
            driver_lab.h         idiomatic C++20 wrapper and StateVmoBank
            driver_lab_c.h       pure C ABI declarations and global state helpers
        driver_lab.cc            C++20 wrapper and StateVmoBank implementation
        tests/
            driver_lab_cpp_test.cc   C++ embedded server and StateVmoBank unit tests
    policy/                            planned: schema and generation
    bind/

src/devices/bin/driver_manager/
    ... engineering takeover integration (phase 2) ...

The proxy is implemented in Rust on the DFv2 Rust driver bindings. Where the Rust driver-framework, MMIO, or interrupt bindings lag their C++ equivalents in the selected revision, the gap is a feasibility-spike finding to resolve upstream, not a reason to switch languages. The wire contract and host programming model remain language-neutral.

22. Build and configuration

Create separate targets for:

  • FIDL libraries and generated bindings;
  • policy schema/generator;
  • pure policy/executor library;
  • proxy driver/component/package;
  • unit and integration tests;
  • Driver Manager takeover integration (phase 2);
  • host bindings and compatibility goldens;
  • engineering assembly inclusion; and
  • production-absence verification.

Structured configuration may reduce:

  • enabled state;
  • audit capacity;
  • log verbosity;
  • maximum operation count;
  • maximum deadlines; and
  • automatic restoration timeout (phase 2).

It may not widen resource or write policy.

23. Testing

23.1 Policy tests

Cover:

  • known/unknown resources;
  • logical versus actual sizes;
  • digest determinism;
  • hard-denied reads;
  • unknown read with and without exact allowlist;
  • width/alignment;
  • overflow;
  • one-shot versus poll permission;
  • target limit narrowing;
  • read-only versus mutating session;
  • exact write offset/mask;
  • precondition/readback requirements;
  • protocol method and transaction limits; and
  • cleanup validation.

23.2 Executor tests

Use fake MMIO, fake clocks, fake timers, and fake protocol resources. Cover:

  • scalar read;
  • snapshot prevalidation;
  • masked write calculation;
  • rejected mask performs no write;
  • failed precondition;
  • readback success/mismatch;
  • immediate and delayed poll success;
  • poll timeout;
  • barriers and ordering;
  • protocol transaction;
  • stop-on-error partial result;
  • cancellation;
  • audit/result correspondence; and
  • concurrent sequence rejection.

23.3 Driver realm tests

Provide fake parent resources and test:

  • platform-like and PCI-like providers;
  • resource enumeration;
  • missing/undersized resources;
  • mapping and device cache policy;
  • service publication;
  • session opening;
  • virtual interrupts;
  • driver stop;
  • pending request cancellation; and
  • no resource handles returned to the host.

23.4 Activation integration tests

With a synthetic unclaimed node:

  1. register and bind the proxy through existing mechanisms;
  2. verify identity, generation, and digest reporting;
  3. execute a read-only plan;
  4. stop the proxy; and
  5. verify the node is again unclaimed.

Inject failure at every transition. Managed-takeover integration tests are specified in phase 2.

23.5 Interrupt tests

Verify count/sequence, retained events, future waits, timeout, coalescing, multiple observers, close cancellation, stop cancellation, acknowledgment, storm bounding, and stop/teardown responsiveness.

23.6 Security-negative tests

Verify rejection of:

  • physical address input;
  • unknown resource;
  • resource size boundary/overflow;
  • unsupported width;
  • hard-denied read;
  • read absent from session allowlist;
  • one-shot grant used for poll;
  • write absent from immutable target policy;
  • excessive write mask;
  • missing precondition;
  • oversized operation vectors or transactions;
  • excessive timeout/delay;
  • mutation from read-only session;
  • second mutating session;
  • stale identity/digest;
  • arbitrary driver URL or protocol path;
  • concurrent ownership with normal driver; and
  • proxy/control artifacts in a production image.

23.7 Fault injection

Cover:

FaultRequired result
Peer closes before operationno access; transport failure
Peer closes after writepartial/unknown result remains visible
Stop during delay/pollbounded cancellation
Driver host crashesproxy generation changes; session becomes stale
Target rebootboot identity changes; no replay
Audit wrapexplicit gap reported
Interrupt stormbounded memory and responsive stop
Protocol backend hangstarget/host deadline and recovery path
Cleanup failsaudited failure; no success claim

24. Milestones

Milestone P0: feasibility

  • validate proxy bind to an unclaimed node via existing registration and bind mechanisms;
  • validate Rust DFv2 driver support for MMIO mapping, interrupt handling, synchronized-dispatcher timers, and service publication;
  • classify candidate parent resource providers as channel-transport or driver-transport-only in the selected revision;
  • validate the first parent resource provider;
  • validate isolated host placement;
  • define the engineering/production assembly boundary with platform assembly owners; and
  • (phase-2 gate) review node-scoped force-bind, node persistence across normal-driver unbind, and restoration with the driver-framework team.

Milestone P1: read-only proxy

  • Describe;
  • immutable ceiling;
  • session allowlist;
  • named MMIO mapping;
  • Read32 and snapshot;
  • audit; and
  • driver realm tests.

Exit: only an exact allowed read inside an offered resource reaches fake MMIO.

Milestone P2: activation integration

  • proxy registration, bind, and service routing on an unclaimed synthetic node;
  • generation identity;
  • bounded stop; and
  • verified teardown back to an unclaimed node.

Exit: synthetic activation and teardown succeed and every injected transition failure is visible. (Managed-takeover integration is the corresponding phase 2 milestone.)

Milestone P3: bounded sequence and mutation

  • delay, poll, and barrier;
  • exact masked writes;
  • precondition/readback;
  • mutation lease;
  • cleanup semantics;
  • partial completion; and
  • expanded fault tests.

Exit: no out-of-policy bit can be written and no failed sequence is called atomic.

Milestone P4: protocol resources

  • representative GPIO and SPI or I2C adapters;
  • production-shaped typed endpoints;
  • method-specific policy and audit;
  • heterogeneous target-local sequence; and
  • translation-fidelity documentation.

Exit: host Python can perform equivalent resource operations through direct and proxy modes without flattening their semantics.

Milestone P5: interrupts and physical validation

  • interrupt observation;
  • virtual tests;
  • selected hardware-safe read validation;
  • selected reviewed mutation;
  • driver-host failure drill; and
  • out-of-band recovery drill.

Exit: hardware runs preserve evidence and return the target to a known operational state.

25. Definition of done

The phase 1 proxy is ready when:

  • production images verifiably do not offer it;
  • it is present but inactive in selected engineering images;
  • it binds without an arbitrary host-supplied driver URL;
  • it binds only to nodes with no bound driver;
  • it receives only resources offered to its node;
  • no host API accepts a physical address or raw target resource handle;
  • actual bounds, target ceiling, and session allowlist are all enforced;
  • unknown reads require an exact session grant;
  • writes require immutable exact target policy;
  • operations and waits are bounded;
  • target-local execution never blocks the dispatcher;
  • partial completion is represented honestly;
  • audit covers attempted and rejected operations;
  • driver stop cancels all pending work;
  • teardown verifiably returns the node to an unclaimed state;
  • synthetic, security-negative, fault-injection, and selected physical tests pass;
  • the wire contract remains usable from the companion host tooling (//tools/driver-lab/SPEC.md); and
  • the wire contract carries the reserved takeover fields so phase 2 requires no major version change.

26. Open decisions

Before implementation, resolve:

  1. The first parent resource-provider adapter.
  2. How generic engineering read ceilings are supplied without unsafe global defaults.
  3. Which offsets/ranges must be hard-denied even with operator consent.
  4. The initial write-policy authoring and review process.
  5. Whether standard FIDL protocols can be served unchanged while preserving session policy/audit.
  6. The Rust driver-framework, MMIO, interrupt, and timer APIs available in the selected Fuchsia revision, and any binding gaps requiring upstream work.
  7. Whether a narrowing-only ExtendAllowlist session operation replaces V1's reopen-per-grant behavior.
  8. Whether ceiling-bounded short spin-delays are permitted for microsecond-class sequencing.

Takeover-related decisions (force-bind and restore APIs, node-class persistence, restoration-state persistence, driver-host restart during takeover, subtree teardown) live in the Phase 2 section of this document.

27. Primary references

Phase 2: embedded driver library & in-situ debugging

Implementation status

Changesets continue the global numbering from Phase 1:

  • [x] CS24 [driver-lab] Core library extraction & pre-mapped MMIO adapter
  • [x] CS25 [driver-lab] Embedded Rust driver library with ServiceFs integration
  • [x] CS26 [driver-lab] Cooperative locking, quiesce hooks, and interrupt tap support
  • [x] CS27 [driver-lab] Assembly gating, CML debug shard, and production-absence verification
  • [x] CS28 [driver-lab] Reference integration: synthetic platform driver in testing/
  • [x] CS29 [driver-lab] Host tooling discovery of embedded debug endpoints and in-situ session workflow (in //tools/driver-lab/SPEC.md).
  • [x] CS30 [driver-lab] In-situ live-target conformance test suite (in //tools/driver-lab/SPEC.md).
  • [x] CS34 [driver-lab] Expose FIDL and debug shard to Bazel
  • [x] CS35 [driver-lab] Native Bazel build for driver_lab_rust
  • [x] CS36 [driver-lab] Add C FFI staticlib shim (driver_lab_c)
  • [x] CS37 [driver-lab] Add C/C++ wrapper and StateVmoBank
  • [x] CS38 [driver-lab] Add C++ embedded server unit tests

Phase: 2 -- existing-driver in-situ inspection and live debugging via embedded library. Extends: Phase 1 of this document. The wire contract (fuchsia.driver.lab), policy models, audit ring, and bounded executor apply unchanged.

Companion specification: host tooling at //tools/driver-lab/SPEC.md.

1. Purpose

Phase 2 enables empirical hardware exploration and live debugging of existing, bound drivers without stopping them or tearing down their child subtrees.

A driver incorporates the driver_lab library during compilation (in engineering and debug builds), shares its mapped hardware resources with the embedded server, and serves fuchsia.driver.lab.Service from its outgoing directory. Host tooling connects directly to the driver's moniker, enabling register inspection, bounded probing, and timing-sensitive sequences while the driver remains alive and active.

2. Architecture & library model

2.1 Capability sharing via VMO duplication

The embedded library does not request root capabilities or acquire new resources from the parent node:

  • The driver duplicates its existing MMIO VMO handle (zx::Rights::SAME_RIGHTS).
  • The library creates an independent local mapping (MappedMmio) within the same driver host process address space.
  • The driver's internal data structures, register types, and private memory remain unperturbed.

2.2 Concurrency and cooperative interlocking

Because both the driver‘s normal dispatch loops and the host’s probe sequences have access to hardware registers, unsynchronized mutations must be prevented:

  • Read operations: Scalar reads and snapshots to known read-safe registers are permitted concurrently. Destructive registers (clear-on-read interrupt status, FIFO data registers) must be marked hard_denied in the target ceiling manifest unless explicitly authorized by an exact allowlist.
  • Mutating sequences and quiescing: Opening a mutating session acquires an exclusive mutation lease. The library invokes an optional driver-registered synchronous quiesce_hook(bool paused) callback, allowing the driver's state machine or worker threads to pause background register polling while host mutations and sequences execute. If complex driver dispatchers require asynchronous coordination in the future, this interface may be extended to support an async stream/channel notification.

2.3 Interrupt observation via event tapping

In Zircon, a zx::interrupt object can only be bound to a single dispatcher or thread. The library therefore does not attempt to steal or bind the raw interrupt handle.

  • The driver instruments its existing interrupt service routine to ping the library: lab.notify_interrupt(resource_id).
  • The library increments the sequence counter, appends to the audit ring, and satisfies pending host WaitForInterrupt hanging gets.

3. Language roadmap

3.1 Phase 2a: Rust driver library (driver_lab_rust)

Prioritized for immediate support across Fuchsia's DFv2 Rust driver stack in both GN and Bazel builds.

  • Links directly against lab_proxy_core.
  • Native integration with fdf_component::DriverContext, ServiceFs, and fuchsia_async::Scope (with GN-only driver crates feature-gated under fdf_driver and a pure-zx root VMAR mapping fallback for native Bazel compilation).
  • First reference integration: synthetic platform driver in testing/ (CS28).

3.2 Phase 2b: C/C++ embedded driver library (driver_lab_c and driver_lab_cpp)

Supports DFv2 C++ and hybrid C/C++ drivers across both GN and Bazel builds by wrapping driver_lab_rust in a thin extern "C" FFI static library (driver_lab_c, declared in cpp/include/lib/driver_lab/driver_lab_c.h) so all SHA-256 resource/policy digest calculation, session allowlist enforcement, audit ring buffering, and sequence execution remain single-sourced in Rust:

  • driver_lab_c exposes opaque DriverLabBuilderHandle and DriverLabServerHandle pointers over a pure C ABI, runs a dedicated background fuchsia_async::LocalExecutor thread to serve incoming fuchsia.driver.lab.Proxy channels without blocking the C++ fdf::Dispatcher, and provides process-wide global state/knob helpers (driver_lab_global_set_state_u32 and driver_lab_global_get_knob_u32) for legacy .c files.
  • driver_lab_cpp (cpp/include/lib/driver_lab/driver_lab.h and cpp/driver_lab.cc) provides RAII C++20 wrappers in namespace driver_lab: StateVmoBank (shared-memory VMO bank for telemetry/state and host-tunable fault-injection knobs), Builder, and EmbeddedServer (integrating with fdf::OutgoingDirectory and component::OutgoingDirectory).
  • Unit tests (driver_lab_cpp_tests in cpp/tests/driver_lab_cpp_test.cc) verify Describe, mutating OpenSession, Read32, Write32 on StateVmoBank knobs, global C state/knob visibility, interrupt tapping, quiesce hooks, ReadAudit, and hard-denied range rejection over FIDL.

4. Production absence and security gating

The embedded debug library must never be active in production (user) builds:

  1. Compilation gating: Gated by build argument (enable_driver_lab = is_debug). In release builds, library types compile to zero-sized no-ops and outgoing service handlers are omitted.
  2. Component manifest sharding: Driver manifests import a common shard //src/devices/driver-lab/meta/debug.shard.cml (exported to both GN and Bazel targets as //src/devices/driver-lab:meta/debug.shard.cml) that exposes fuchsia.driver.lab.Service.
  3. Product assembly verification: Platform assembly golden checks verify that no production package or bootfs driver exposes fuchsia.driver.lab.Service.

5. Open design decisions resolved

  1. Takeover unbind/force-bind APIs: Obsoleted. No Driver Manager changes required.
  2. Subtree teardown and power framework: Obsoleted. Drivers remain bound; subtrees and power topologies remain undisturbed.
  3. ARM64 clock/power gating panics: Resolved. The running driver maintains its votes for power domains and clocks throughout the debug session.

Phase 3: software state banks, runtime knobs, and diagnostic triggers

Implementation status

Changesets continue the global numbering from Phase 2:

  • [x] CS31 [driver-lab] Software state banks, runtime knobs, and diagnostic triggers
  • [x] CS32 [driver-lab] Host plan aliases and StateHandle API for state and knob experiments (in //tools/driver-lab/SPEC.md).
  • [x] CS33 [driver-lab] Update driver-lab and autoda-fix skills for Mode A/B/C verification (in //tools/driver-lab/SPEC.md).

Phase: 3 -- single-build in-situ software state observation, runtime knobs, and diagnostic triggers. Extends: Phases 1 and 2 of this document. StateBank implements MmioBackend and ResourceBackend, requiring no breaking changes to fuchsia.driver.lab.

Companion specification: host tooling at //tools/driver-lab/SPEC.md.

1. Purpose

Phase 3 extends embedded in-situ debugging (driver_lab_rust::embedded) beyond physical MMIO registers so developers and agents can reproduce, diagnose, prototype fixes for, and verify software state-machine and concurrency bugs without repeated edit -> build -> flash/OTA -> reboot cycles.

2. StateBank architecture and primitives

lab_proxy_core::state_bank::StateBank exposes a 32-bit word-addressed virtual resource backed by 4-byte aligned offsets within a declared logical_size:

  1. Observable state slots (define_state_slot(offset, initial) -> StateSlotHandle):
    • Backed by Arc<AtomicU32> (StateEntry::AtomicSlot).
    • Read-only to the host by default (offset is omitted from writable_offsets).
    • The driver updates and reads the slot with zero lock contention via StateSlotHandle (load, store, fetch_add, fetch_or, fetch_and).
  2. Runtime knobs (define_knob(offset, initial) -> StateSlotHandle):
    • Backed by Arc<AtomicU32> (StateEntry::AtomicSlot) with offset added to writable_offsets.
    • Enables fault/race-window injection knobs (e.g., bounded microsecond delay at a suspect critical section) and candidate fix-toggle knobs (e.g., enabling a lock or teardown guard at runtime to prove reproduction with knob = 0 and resolution with knob = 1 in the same boot).
  3. On-demand read probes (define_read_probe(offset, probe)):
    • Backed by Arc<dyn Fn() -> Result<u32, BackendError> + Send + Sync> (StateEntry::ReadProbe), evaluated synchronously when read32 is called.
    • Read-only; host writes are rejected.
  4. Diagnostic triggers (define_trigger(offset, trigger)):
    • Backed by StateEntry::Trigger { last_result: Arc<AtomicU32>, callback } with offset added to writable_offsets.
    • Invokes callback(value) on write32(offset, value) under a mutating session lease, stores the returned u32 status into last_result for immediate readback or subsequent read32/poll32, and returns Err(BackendError::Fault) if the callback fails.

3. Policy enforcement and wire contract stability

  • StateBank implements MmioBackend (read32, write32, and barrier via std::sync::atomic::fence(Ordering::SeqCst)) and ResourceBackend.
  • LiveBackend::State(StateBank) integrates with DriverLabBuilder::with_state_bank(name, bank), which registers the bank in ProvidedResources with a ResourceCeiling populated from bank.writable_offsets().
  • All existing target policy invariants (writable_registers, hard_denied, session allowlist narrowing, mutating session lease, quiesce_hook, and target-audit.jsonl recording) apply automatically without changing driver_lab.fidl.

4. Reference sample driver integration

lab_sample_driver (//src/devices/driver-lab/testing/src/sample_driver.rs) registers a "state0" StateBank alongside "mmio0" and "irq0", exposing STATE_BUSY_FLAG (0x00), STATE_INVARIANT_VIOLATIONS (0x04), KNOB_RACE_DELAY_US (0x08), KNOB_FIX_ENABLED (0x0C), and TRIGGER_CONCURRENT_OP (0x10), verified in sample_driver_realm_test.