chipset_arc_mutex_device/
test_chipset.rs1use crate::device::ArcMutexChipsetDeviceBuilder;
7use crate::device::ArcMutexChipsetServicesFinalize;
8use crate::services::ChipsetServices;
9use crate::services::ChipsetServicesMeta;
10use crate::services::MmioInterceptServices;
11use crate::services::Unimplemented;
12use chipset_device::ChipsetDevice;
13use chipset_device::io::IoResult;
14use chipset_device::mmio::ControlMmioIntercept;
15use chipset_device::mmio::RegisterMmioIntercept;
16use closeable_mutex::CloseableMutex;
17use parking_lot::RwLock;
18use range_map_vec::RangeMap;
19use std::cell::Cell;
20use std::sync::Arc;
21use std::sync::Weak;
22
23type MmioRanges = Arc<RwLock<RangeMap<u64, (Box<str>, Weak<CloseableMutex<dyn ChipsetDevice>>)>>>;
24
25pub struct TestMmioRangeMapper {
27 dev: Weak<CloseableMutex<dyn ChipsetDevice>>,
28 map: MmioRanges,
29}
30
31struct TestDeviceRange {
34 map: MmioRanges,
35 region_name: Box<str>,
36 len: u64,
37 addr: Option<u64>,
38 io: Weak<CloseableMutex<dyn ChipsetDevice>>,
39}
40
41impl RegisterMmioIntercept for TestMmioRangeMapper {
42 fn new_io_region(&mut self, region_name: &str, len: u64) -> Box<dyn ControlMmioIntercept> {
43 Box::new(TestDeviceRange {
44 map: self.map.clone(),
45 region_name: region_name.into(),
46 len,
47 addr: None,
48 io: self.dev.clone(),
49 })
50 }
51}
52impl ControlMmioIntercept for TestDeviceRange {
53 fn region_name(&self) -> &str {
54 &self.region_name
55 }
56
57 fn map(&mut self, addr: u64) {
58 self.unmap();
59 if self.map.write().insert(
60 addr..=addr
61 .checked_add(self.len - 1)
62 .expect("overflow during addition, not possible in real hardware"),
63 (self.region_name.clone(), self.io.clone()),
64 ) {
65 self.addr = Some(addr);
66 } else {
67 panic!("conflict!")
68 }
69 }
70
71 fn unmap(&mut self) {
72 if let Some(addr) = self.addr.take() {
73 let _entry = self.map.write().remove(&addr).unwrap();
74 }
75 }
76
77 fn addr(&self) -> Option<u64> {
78 self.addr
79 }
80
81 fn len(&self) -> u64 {
82 self.len
83 }
84
85 fn offset_of(&self, addr: u64) -> Option<u64> {
86 let base = self.addr?;
87
88 (base..(base + self.len))
89 .contains(&addr)
90 .then(|| addr - base)
91 }
92}
93
94#[derive(Default)]
96pub struct TestChipset {
97 mmio_ranges: MmioRanges,
98}
99
100impl TestChipset {
101 pub fn device_builder<T: ChipsetDevice>(
103 &self,
104 name: &'static str,
105 ) -> ArcMutexChipsetDeviceBuilder<TestChipsetServicesImpl<'_>, T> {
106 ArcMutexChipsetDeviceBuilder::new(name.into(), |dev, _name| TestChipsetServicesImpl {
107 vm_chipset: self,
108 dev,
109 took_mmio: false.into(),
110 })
111 }
112
113 pub fn mmio_read(&self, addr: u64, data: &mut [u8]) -> Option<()> {
115 let dev = self.mmio_ranges.read().get(&addr)?.1.upgrade()?;
116 match dev
119 .lock()
120 .supports_mmio()
121 .expect("objects on the mmio bus support mmio")
122 .mmio_read(addr, data)
123 {
124 IoResult::Ok => {}
125 IoResult::Err(_) => {
126 data.fill(!0);
127 }
128 IoResult::Defer(_) => unreachable!(),
129 }
130 Some(())
131 }
132
133 pub fn mmio_write(&self, addr: u64, data: &[u8]) -> Option<()> {
135 let dev = self.mmio_ranges.read().get(&addr)?.1.upgrade()?;
136 let _ = dev
139 .lock()
140 .supports_mmio()
141 .expect("objects on the mmio bus support mmio")
142 .mmio_write(addr, data);
143 Some(())
144 }
145}
146
147pub struct TestChipsetServicesImpl<'a> {
149 vm_chipset: &'a TestChipset,
150 dev: Weak<CloseableMutex<dyn ChipsetDevice>>,
151 took_mmio: Cell<bool>,
152}
153
154pub enum TestChipsetServicesMeta {}
157impl ChipsetServicesMeta for TestChipsetServicesMeta {
158 type RegisterMmioIntercept = TestMmioRangeMapper;
159 type RegisterPortIoIntercept = Unimplemented;
160}
161
162impl ChipsetServices for TestChipsetServicesImpl<'_> {
163 type M = TestChipsetServicesMeta;
164
165 #[inline(always)]
166 fn supports_mmio(&mut self) -> Option<&mut dyn MmioInterceptServices<M = Self::M>> {
167 Some(self)
168 }
169}
170
171impl<T> ArcMutexChipsetServicesFinalize<T> for TestChipsetServicesImpl<'_> {
172 fn finalize(self, _dev: &Arc<CloseableMutex<T>>, _name: Arc<str>) {}
173}
174
175impl MmioInterceptServices for TestChipsetServicesImpl<'_> {
176 fn register_mmio(&self) -> TestMmioRangeMapper {
178 self.took_mmio.set(true);
179 TestMmioRangeMapper {
180 dev: self.dev.clone(),
181 map: self.vm_chipset.mmio_ranges.clone(),
182 }
183 }
184
185 fn is_being_used(&self) -> bool {
186 self.took_mmio.get()
187 }
188}
189
190#[cfg(test)]
191mod tests {
192 use super::*;
193
194 mod sample_dev {
195 use super::*;
196 use chipset_device::io::IoResult;
197 use chipset_device::mmio::MmioIntercept;
198 use std::ops::RangeInclusive;
199
200 pub struct SampleDevice {
201 pub mmio_control: Box<dyn ControlMmioIntercept>,
202 pub mmio_read_log: Vec<u64>,
203 }
204
205 impl SampleDevice {
206 pub fn new(
207 register_mmio: &mut dyn RegisterMmioIntercept,
208 ) -> Result<Self, std::convert::Infallible> {
209 Ok(SampleDevice {
210 mmio_control: register_mmio.new_io_region("dynamic", 1),
211 mmio_read_log: Vec::new(),
212 })
213 }
214 }
215
216 impl ChipsetDevice for SampleDevice {
217 fn supports_mmio(&mut self) -> Option<&mut dyn MmioIntercept> {
218 Some(self)
219 }
220 }
221
222 impl MmioIntercept for SampleDevice {
223 fn mmio_read(&mut self, addr: u64, _: &mut [u8]) -> IoResult {
224 self.mmio_read_log.push(addr);
225 IoResult::Ok
226 }
227
228 fn mmio_write(&mut self, _: u64, _: &[u8]) -> IoResult {
229 IoResult::Ok
230 }
231
232 fn get_static_regions(&mut self) -> &[(&str, RangeInclusive<u64>)] {
233 &[("static", 10..=10)]
234 }
235 }
236 }
237
238 #[test]
239 fn closure() -> Result<(), Box<dyn std::error::Error>> {
240 let vm_chipset = TestChipset::default();
241
242 let devices_builder = ArcMutexChipsetDeviceBuilder::new("sample".into(), |dev, _name| {
243 TestChipsetServicesImpl {
244 vm_chipset: &vm_chipset,
245 dev,
246 took_mmio: false.into(),
247 }
248 });
249
250 let sample_dev: Arc<CloseableMutex<sample_dev::SampleDevice>> = devices_builder
251 .try_add(|services| sample_dev::SampleDevice::new(&mut services.register_mmio()))?;
252
253 assert!(vm_chipset.mmio_read(10, &mut []).is_some());
255 assert!(vm_chipset.mmio_read(11, &mut []).is_none());
256 sample_dev.lock().mmio_control.map(11);
257 assert!(vm_chipset.mmio_read(11, &mut []).is_some());
258 sample_dev.lock().mmio_control.unmap();
259 assert!(vm_chipset.mmio_read(11, &mut []).is_none());
260
261 assert_eq!(sample_dev.lock().mmio_read_log, [10, 11]);
262
263 Ok(())
264 }
265}