Skip to main content

fake_mmio/inject/
range_override.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
5use std::ops::Range;
6
7use crate::inject::{GenericVmoMemoryHandler, VmoMemoryHandler, VmoOpHelper};
8use crate::operand::MmioOperand;
9
10/// A handler that delegates matching ranges to a [`MaybeVmoMemoryHandler`]
11/// implementation and falls back to a parent handler for unmatched ranges.
12pub struct RangeOverride<P, O> {
13    maybe_override: O,
14    parent: P,
15}
16
17impl<P, O> RangeOverride<P, O> {
18    /// Creates a new [`RangeOverride`] with the given `parent` and
19    /// `maybe_override`.
20    pub fn new(parent: P, maybe_override: O) -> Self {
21        Self { maybe_override, parent }
22    }
23}
24
25impl<P: VmoMemoryHandler, O: MaybeVmoMemoryHandler> GenericVmoMemoryHandler
26    for RangeOverride<P, O>
27{
28    fn load<T: MmioOperand>(&self, op: VmoOpHelper<'_, T>) -> T {
29        match self.maybe_override.get_handler(op.range()) {
30            Some(handler) => T::load(handler, op),
31            None => T::load(&self.parent, op),
32        }
33    }
34
35    fn store<T: MmioOperand>(&self, op: VmoOpHelper<'_, T>, value: T) {
36        match self.maybe_override.get_handler(op.range()) {
37            Some(handler) => T::store(handler, op, value),
38            None => T::store(&self.parent, op, value),
39        }
40    }
41}
42
43/// A trait abstracting _optional_ handling of a VMO range.
44pub trait MaybeVmoMemoryHandler {
45    /// Returns a [`VmoMemoryHandler`] if `range` can be operated on by the
46    /// implementer.
47    fn get_handler(&self, range: Range<usize>) -> Option<&(impl VmoMemoryHandler + ?Sized)>;
48}
49
50/// Provides a mock for `MaybeVmoMemoryHandler` via `mockall`.
51///
52/// NOTE: We can't use `automock` here because `mockall` gets confused with the
53/// lifetimes.
54#[cfg(test)]
55mod mock {
56    use super::*;
57
58    mockall::mock! {
59        pub MaybeVmoMemoryHandler<T: VmoMemoryHandler + 'static> {
60            pub fn get_handler<'a>(&'a self, range: Range<usize>) -> Option<&'a T>;
61        }
62    }
63
64    impl<T: VmoMemoryHandler + 'static> MaybeVmoMemoryHandler for MockMaybeVmoMemoryHandler<T> {
65        fn get_handler(&self, range: Range<usize>) -> Option<&(impl VmoMemoryHandler + ?Sized)> {
66            self.get_handler(range)
67        }
68    }
69}
70#[cfg(test)]
71pub use mock::*;
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76    use crate::CachedVmoMemory;
77    use crate::inject::dispatch::DispatchBuilder;
78    use crate::inject::passthrough::Passthrough;
79    use crate::inject::{MockVmoMemoryHandler, VmoMemory};
80    use mmio::MmioExt as _;
81
82    #[test]
83    fn test_range_override() {
84        let expected_val = 0xcafe_babe_u32;
85        let mut mock = MockVmoMemoryHandler::new();
86        let _ = mock
87            .expect_load32()
88            .once()
89            .withf(|op| op.offset() == 0 && op.range() == (0..4))
90            .return_const(expected_val);
91
92        let dispatch = DispatchBuilder::new().insert_node(0..4, mock).build();
93        let handler = RangeOverride::new(Passthrough, dispatch);
94
95        let mut mmio = CachedVmoMemory::new_mapped(4096)
96            .expect("Failed to map VMO.")
97            .map(|vmo| VmoMemory::new(handler, vmo));
98
99        // Access in overridden range 0..4 returns the constant from MockVmoMemoryHandler.
100        assert_eq!(mmio.load::<u32>(0), expected_val);
101
102        // Access outside overridden range (offset 4) falls back to Passthrough.
103        let val_outside = 0x1234_5678_u32;
104        mmio.store::<u32>(4, val_outside);
105        assert_eq!(mmio.load::<u32>(4), val_outside);
106    }
107}