| <!-- Copyright 2026 The Fuchsia Authors. All rights reserved. | |
| Use of this source code is governed by a BSD-style license that can be | |
| found in the LICENSE file. --> | |
| # 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 | |
| ```text | |
| 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: | |
| ```text | |
| 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: | |
| ```text | |
| 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: | |
| ```text | |
| 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: | |
| ```text | |
| 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: | |
| ```text | |
| 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: | |
| ```text | |
| 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: | |
| ```text | |
| 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: | |
| | Fault | Required result | | |
| |---|---| | |
| | Peer closes before operation | no access; transport failure | | |
| | Peer closes after write | partial/unknown result remains visible | | |
| | Stop during delay/poll | bounded cancellation | | |
| | Driver host crashes | proxy generation changes; session becomes stale | | |
| | Target reboot | boot identity changes; no replay | | |
| | Audit wrap | explicit gap reported | | |
| | Interrupt storm | bounded memory and responsive stop | | |
| | Protocol backend hangs | target/host deadline and recovery path | | |
| | Cleanup fails | audited 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 | |
| - [Driver communication and services](https://fuchsia.dev/fuchsia-src/concepts/drivers/driver_communication) | |
| - [Mapping device memory in a driver](https://fuchsia.dev/fuchsia-src/concepts/drivers/mapping-a-devices-memory-in-a-driver) | |
| - [Using hardware resources in a driver](https://fuchsia.dev/fuchsia-src/development/drivers/tutorials/sdk_build_driver/hardware-resources) | |
| - [Handling interrupts in a driver](https://fuchsia.dev/fuchsia-src/development/drivers/developer_guide/handle-interrupts-in-a-driver) | |
| - [Driver runner and driver hosts](https://fuchsia.dev/fuchsia-src/concepts/components/v2/driver_runner) | |
| - [Fuchsia registers driver](https://fuchsia.dev/fuchsia-src/development/drivers/driver_guides/registers/overview) | |
| - [Structured configuration](https://fuchsia.dev/fuchsia-src/development/components/configuration/structured_config) | |
| - [Build configuration and product assembly](https://fuchsia.dev/fuchsia-src/development/build/software_assembly/build_configuration) | |
| ## 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`. | |