lab_proxy_core/hardware_backend.rs
1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Hardware access abstraction for the executor.
6//!
7//! The real driver implements [`MmioBackend`] over a locally mapped MMIO
8//! region; tests and driver realm fakes use [`FakeMmio`]. Keeping the trait
9//! in the core crate lets the entire read path run on the build host.
10
11use std::collections::{BTreeMap, BTreeSet};
12
13/// Errors surfaced by a hardware backend.
14#[derive(Clone, Copy, Debug, PartialEq, Eq)]
15pub enum BackendError {
16 /// The backend could not complete the access; the proxy treats this as
17 /// fatal backend state for the current operation.
18 Fault,
19}
20
21/// A source of target monotonic time, injected so core logic is
22/// deterministic under test. The driver supplies zx monotonic time.
23pub trait Clock {
24 /// Current timestamp in nanoseconds.
25 fn now_ns(&mut self) -> i64;
26}
27
28/// An asynchronous timer for delays and polling.
29pub trait Timer: Clock + Send {
30 /// Asynchronously sleeps for `duration_ns` nanoseconds.
31 fn sleep(&mut self, duration_ns: i64) -> impl std::future::Future<Output = ()> + Send;
32}
33
34/// Deterministic test clock advancing by a fixed step per reading.
35#[derive(Clone, Debug, Default)]
36pub struct FakeClock {
37 /// Timestamp returned by the next call.
38 pub now: i64,
39 /// Amount added after each call.
40 pub step: i64,
41 /// Record of sleeps requested.
42 pub sleeps: Vec<i64>,
43}
44
45impl FakeClock {
46 /// Creates a fake clock.
47 pub fn new(now: i64, step: i64) -> Self {
48 Self { now, step, sleeps: Vec::new() }
49 }
50}
51
52impl Clock for FakeClock {
53 fn now_ns(&mut self) -> i64 {
54 let now = self.now;
55 self.now += self.step;
56 now
57 }
58}
59
60impl Timer for FakeClock {
61 async fn sleep(&mut self, duration_ns: i64) {
62 if duration_ns > 0 {
63 self.now += duration_ns;
64 self.sleeps.push(duration_ns);
65 }
66 }
67}
68
69/// Volatile 32-bit MMIO access to one resource.
70pub trait MmioBackend {
71 /// Performs one volatile 32-bit read at `offset` bytes from the start
72 /// of the logical resource.
73 fn read32(&mut self, offset: u64) -> Result<u32, BackendError>;
74
75 /// Performs one volatile 32-bit write at `offset` bytes from the start
76 /// of the logical resource.
77 fn write32(&mut self, offset: u64, value: u32) -> Result<(), BackendError>;
78
79 /// Applies a platform MMIO memory barrier.
80 fn barrier(&mut self);
81}
82
83/// In-memory fake MMIO region that records every access, for unit tests
84/// and driver realm fakes. Reads of unset offsets return zero.
85#[derive(Clone, Debug, Default)]
86pub struct FakeMmio {
87 values: BTreeMap<u64, u32>,
88 faults: BTreeSet<u64>,
89 /// Offsets read, in order.
90 pub accesses: Vec<u64>,
91 /// Writes performed, in order: (offset, value).
92 pub write_accesses: Vec<(u64, u32)>,
93 /// Number of memory barriers performed.
94 pub barriers: usize,
95}
96
97impl FakeMmio {
98 /// Creates an empty fake region.
99 pub fn new() -> Self {
100 Self::default()
101 }
102
103 /// Sets the value returned for reads at `offset`.
104 pub fn set(&mut self, offset: u64, value: u32) {
105 self.values.insert(offset, value);
106 }
107
108 /// Makes reads or writes at `offset` fail with [`BackendError::Fault`].
109 pub fn fail_at(&mut self, offset: u64) {
110 self.faults.insert(offset);
111 }
112}
113
114impl MmioBackend for FakeMmio {
115 fn read32(&mut self, offset: u64) -> Result<u32, BackendError> {
116 self.accesses.push(offset);
117 if self.faults.contains(&offset) {
118 return Err(BackendError::Fault);
119 }
120 Ok(self.values.get(&offset).copied().unwrap_or(0))
121 }
122
123 fn write32(&mut self, offset: u64, value: u32) -> Result<(), BackendError> {
124 self.write_accesses.push((offset, value));
125 if self.faults.contains(&offset) {
126 return Err(BackendError::Fault);
127 }
128 self.values.insert(offset, value);
129 Ok(())
130 }
131
132 fn barrier(&mut self) {
133 self.barriers += 1;
134 }
135}