Skip to main content

fake_mmio/inject/
w1c.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::marker::PhantomData;
6
7use crate::atomic::AtomicMmio;
8use crate::inject::{GenericVmoMemoryHandler, VmoOpHelper};
9use crate::operand::MmioOperand;
10
11/// A handler implementation that fakes write-1-to-clear registers of width `T`.
12///
13/// `WriteOneToClear` replaces every `store` of `v` with a `fetch_and(!v)`, which
14/// has the final effect of faking registers that clear the set bits on write.
15///
16/// The handler panics on both loads and stores if the operand width does not
17/// match `T`.
18pub struct WriteOneToClear<T>(PhantomData<T>);
19
20impl<T> WriteOneToClear<T> {
21    /// Creates a new write-1-to-clear handler.
22    pub fn new() -> Self {
23        Self(PhantomData)
24    }
25}
26
27impl<T> Default for WriteOneToClear<T> {
28    fn default() -> Self {
29        Self::new()
30    }
31}
32
33impl<T> GenericVmoMemoryHandler for WriteOneToClear<T> {
34    fn load<X: MmioOperand>(&self, op: VmoOpHelper<'_, X>) -> X {
35        assert_eq!(std::mem::size_of::<X>(), std::mem::size_of::<T>(), "incorrect width");
36        op.load()
37    }
38
39    fn store<X: MmioOperand>(&self, op: VmoOpHelper<'_, X>, value: X) {
40        assert_eq!(std::mem::size_of::<X>(), std::mem::size_of::<T>(), "incorrect width");
41        let _: X = op.borrow_region().fetch_and(0, !value);
42    }
43}
44
45#[cfg(test)]
46mod tests {
47    use super::*;
48    use crate::CachedVmoMemory;
49    use crate::inject::VmoMemory;
50    use crate::operand::MmioOperand;
51    use crate::operand::tests::OperandCallable;
52    use mmio::MmioExt as _;
53
54    const VMO_SIZE: usize = 4096;
55
56    #[test]
57    fn test_w1c_all_operand_types() {
58        struct W1CTester;
59
60        impl OperandCallable for W1CTester {
61            fn call<T: MmioOperand>(&mut self) {
62                let handler = WriteOneToClear::<T>::default();
63                let mut mmio = CachedVmoMemory::new_mapped(VMO_SIZE).expect("Failed to map VMO.");
64
65                // Set initial bits in memory using mmio.store on the unmapped region.
66                let initial_val = T::try_from(0xff).unwrap();
67                mmio.store::<T>(0, initial_val);
68
69                let mut mmio = mmio.map(|vmo| VmoMemory::new(handler, vmo));
70                assert_eq!(mmio.load::<T>(0), initial_val);
71
72                // Write 1 to clear bit 0x0f (bits 0..4).
73                let clear_bits = T::try_from(0x0f).unwrap();
74                mmio.store::<T>(0, clear_bits);
75
76                // Remaining value should be 0xf0.
77                let expected_val = T::try_from(0xf0).unwrap();
78                assert_eq!(mmio.load::<T>(0), expected_val);
79            }
80        }
81
82        W1CTester.call_all();
83    }
84
85    #[fuchsia::test(logging = false)]
86    #[should_panic(expected = "incorrect width")]
87    fn test_w1c_load_incorrect_width_panics() {
88        let handler = WriteOneToClear::<u32>::new();
89        let mmio = CachedVmoMemory::new_mapped(VMO_SIZE)
90            .expect("Failed to map VMO.")
91            .map(|vmo| VmoMemory::new(handler, vmo));
92
93        let _ = mmio.load::<u8>(0);
94    }
95
96    #[fuchsia::test(logging = false)]
97    #[should_panic(expected = "incorrect width")]
98    fn test_w1c_store_incorrect_width_panics() {
99        let mut mmio = CachedVmoMemory::new_mapped(VMO_SIZE)
100            .expect("Failed to map VMO.")
101            .map(|vmo| VmoMemory::new(WriteOneToClear::<u32>::new(), vmo));
102
103        mmio.store::<u8>(0, 0x12);
104    }
105}