fake_mmio/inject/
offset.rs1use std::ops::Range;
6
7use crate::inject::{
8 GenericVmoMemoryHandler, MaybeVmoMemoryHandler, VmoMemoryHandler, VmoOpHelper,
9};
10use crate::operand::MmioOperand;
11
12pub struct Offset<H> {
20 inner: H,
21 offset: usize,
22}
23
24impl<H> Offset<H> {
25 pub fn new(inner: H, offset: usize) -> Self {
28 Self { inner, offset }
29 }
30}
31
32impl<H: VmoMemoryHandler> GenericVmoMemoryHandler for Offset<H> {
33 fn load<T: MmioOperand>(&self, op: VmoOpHelper<'_, T>) -> T {
34 T::load(&self.inner, op.apply_offset(self.offset))
35 }
36
37 fn store<T: MmioOperand>(&self, op: VmoOpHelper<'_, T>, value: T) {
38 T::store(&self.inner, op.apply_offset(self.offset), value)
39 }
40}
41
42impl<H: MaybeVmoMemoryHandler> MaybeVmoMemoryHandler for Offset<H> {
43 fn get_handler(&self, range: Range<usize>) -> Option<&(impl VmoMemoryHandler + ?Sized)> {
44 let Some(start) = range.start.checked_sub(self.offset) else {
45 panic!("{range:?} before offset {}", self.offset);
46 };
47 self.inner.get_handler(start..start + range.len())
48 }
49}
50
51#[cfg(test)]
52mod tests {
53 use super::*;
54 use crate::CachedVmoMemory;
55 use crate::inject::dispatch::DispatchBuilder;
56 use crate::inject::passthrough::Passthrough;
57 use crate::inject::{MockMaybeVmoMemoryHandler, MockVmoMemoryHandler, VmoMemory};
58 use crate::operand::tests::OperandCallable;
59 use mmio::MmioExt as _;
60 use mmio::region::MmioRegion;
61
62 #[test]
63 fn test_offset_passthrough() {
64 const BASE_OFFSET: usize = 0x100;
65
66 struct TestRunner<'a>(&'a mut MmioRegion<VmoMemory<Offset<Passthrough>>>);
67
68 impl OperandCallable for TestRunner<'_> {
69 fn call<T: MmioOperand>(&mut self) {
70 let val = T::try_from(0x42).unwrap();
71 self.0.store::<T>(BASE_OFFSET, val);
72 assert_eq!(self.0.load::<T>(BASE_OFFSET), val);
73 }
74 }
75
76 let handler = Offset::new(Passthrough, BASE_OFFSET);
77 let mut mmio = CachedVmoMemory::new_mapped(4096)
78 .expect("Failed to map VMO.")
79 .map(|vmo| VmoMemory::new(handler, vmo));
80
81 TestRunner(&mut mmio).call_all();
82 }
83
84 #[test]
85 fn test_offset_op_helper_attributes() {
86 const BASE_OFFSET: usize = 0x200;
87 let expected_val = 0x1234_5678_u32;
88
89 let mut mock = MockVmoMemoryHandler::new();
90 let _ = mock
91 .expect_load32()
92 .once()
93 .withf(|op| {
94 op.offset() == 0
95 && op.range() == (0..4)
96 && op.vmo_offset() == BASE_OFFSET
97 && op.vmo_range() == (BASE_OFFSET..BASE_OFFSET + 4)
98 })
99 .return_const(expected_val);
100
101 let val_to_store = 0x8765_4321_u32;
102 let _ = mock
103 .expect_store32()
104 .once()
105 .withf(move |op, val| {
106 op.offset() == 4
107 && op.range() == (4..8)
108 && op.vmo_offset() == BASE_OFFSET + 4
109 && op.vmo_range() == (BASE_OFFSET + 4..BASE_OFFSET + 8)
110 && *val == val_to_store
111 })
112 .return_const(());
113
114 let handler = Offset::new(mock, BASE_OFFSET);
115 let mut mmio = CachedVmoMemory::new_mapped(4096)
116 .expect("Failed to map VMO.")
117 .map(|vmo| VmoMemory::new(handler, vmo));
118
119 assert_eq!(mmio.load::<u32>(BASE_OFFSET), expected_val);
120 mmio.store::<u32>(BASE_OFFSET + 4, val_to_store);
121 }
122
123 #[test]
124 fn test_offset_with_dispatch() {
125 const BASE_OFFSET: usize = 0x100;
126 let dispatch = DispatchBuilder::new().insert_node(0..4, Passthrough).build();
127 let handler = Offset::new(dispatch, BASE_OFFSET);
128
129 let mut mmio = CachedVmoMemory::new_mapped(4096)
130 .expect("Failed to map VMO.")
131 .map(|vmo| VmoMemory::new(handler, vmo));
132
133 let val = 0xab_cd_ef_01_u32;
134 mmio.store::<u32>(BASE_OFFSET, val);
135 assert_eq!(mmio.load::<u32>(BASE_OFFSET), val);
136 }
137
138 #[test]
139 fn test_maybe_handler() {
140 const BASE_OFFSET: usize = 0x100;
141 let mut mock = MockMaybeVmoMemoryHandler::<Passthrough>::new();
142 let _ = mock
143 .expect_get_handler()
144 .once()
145 .withf(|range| range == &(0usize..4))
146 .return_const(None);
147
148 let handler = Offset::new(mock, BASE_OFFSET);
149 assert!(handler.get_handler(BASE_OFFSET..BASE_OFFSET + 4).is_none());
150 }
151
152 #[fuchsia::test(logging = false)]
153 #[should_panic(expected = "too big for current offset")]
154 fn test_offset_underflow_panics() {
155 const BASE_OFFSET: usize = 0x100;
156 let handler = Offset::new(Passthrough, BASE_OFFSET);
157
158 let mmio = CachedVmoMemory::new_mapped(4096)
159 .expect("Failed to map VMO.")
160 .map(|vmo| VmoMemory::new(handler, vmo));
161
162 let _ = mmio.load::<u32>(0x50);
164 }
165}