Skip to main content

fake_mmio/inject/
after_store.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::{
8    GenericVmoMemoryHandler, MaybeVmoMemoryHandler, VmoMemoryHandler, VmoOpHelper,
9};
10use crate::operand::MmioOperand;
11
12/// A handler wrapper that executes a callback after every store operation.
13pub struct AfterStore<H, F> {
14    inner: H,
15    callback: F,
16}
17
18impl<H, F> AfterStore<H, F> {
19    /// Creates a new [`AfterStore`] wrapping `inner` with `callback`.
20    pub fn new(inner: H, callback: F) -> Self {
21        Self { inner, callback }
22    }
23}
24
25impl<H: VmoMemoryHandler, F: Fn(Range<usize>)> GenericVmoMemoryHandler for AfterStore<H, F> {
26    fn load<T: MmioOperand>(&self, op: VmoOpHelper<'_, T>) -> T {
27        T::load(&self.inner, op)
28    }
29
30    fn store<T: MmioOperand>(&self, op: VmoOpHelper<'_, T>, value: T) {
31        let range = op.range();
32        T::store(&self.inner, op, value);
33        (self.callback)(range)
34    }
35}
36
37impl<H: MaybeVmoMemoryHandler, F> MaybeVmoMemoryHandler for AfterStore<H, F> {
38    fn get_handler(&self, range: Range<usize>) -> Option<&(impl VmoMemoryHandler + ?Sized)> {
39        self.inner.get_handler(range)
40    }
41}
42
43#[cfg(test)]
44mod tests {
45    use super::*;
46    use crate::CachedVmoMemory;
47    use crate::inject::passthrough::Passthrough;
48    use crate::inject::{DispatchNode, DispatchNodeHandler, VmoMemory};
49    use crate::operand::MmioOperand;
50    use crate::operand::tests::OperandCallable;
51    use mmio::MmioExt as _;
52    use mmio::region::MmioRegion;
53    use std::sync::{Arc, Mutex};
54
55    #[test]
56    fn test_after_store() {
57        let stores = Arc::new(Mutex::new(Vec::<Range<usize>>::new()));
58        let stores_clone = stores.clone();
59        let handler: DispatchNodeHandler =
60            DispatchNode::from(AfterStore::new(Passthrough, move |range| {
61                stores_clone.lock().unwrap().push(range);
62            }))
63            .into_handler();
64
65        let mut mmio = CachedVmoMemory::new_mapped(4096)
66            .expect("Failed to map VMO.")
67            .map(|vmo| VmoMemory::new(handler, vmo));
68
69        // Load should not trigger the store callback.
70        let _ = mmio.load::<u32>(0);
71        assert!(stores.lock().unwrap().is_empty());
72
73        struct StoreTester<'a>(&'a mut MmioRegion<VmoMemory<DispatchNodeHandler>>);
74
75        impl OperandCallable for StoreTester<'_> {
76            fn call<T: MmioOperand>(&mut self) {
77                let val = T::try_from(0x12).unwrap();
78                self.0.store::<T>(0, val);
79                assert_eq!(self.0.load::<T>(0), val);
80            }
81        }
82
83        StoreTester(&mut mmio).call_all();
84
85        let recorded = stores.lock().unwrap().clone();
86        assert_eq!(recorded, vec![0..1, 0..2, 0..4, 0..8]);
87    }
88}