1#![expect(missing_docs)]
7#![forbid(unsafe_code)]
8
9use chipset_arc_mutex_device::device::ArcMutexChipsetDeviceBuilder;
10use chipset_arc_mutex_device::device::ArcMutexChipsetServicesFinalize;
11use chipset_arc_mutex_device::services::ChipsetServices;
12use chipset_arc_mutex_device::services::ChipsetServicesMeta;
13use chipset_arc_mutex_device::services::MmioInterceptServices;
14use chipset_arc_mutex_device::services::PciConfigSpaceServices;
15use chipset_arc_mutex_device::services::PollDeviceServices;
16use chipset_arc_mutex_device::services::PortIoInterceptServices;
17use chipset_device::ChipsetDevice;
18use chipset_device::io::IoResult;
19use chipset_device::io::deferred::DeferredToken;
20use chipset_device::mmio::ControlMmioIntercept;
21use chipset_device::mmio::RegisterMmioIntercept;
22use chipset_device::pio::ControlPortIoIntercept;
23use chipset_device::pio::RegisterPortIoIntercept;
24use closeable_mutex::CloseableMutex;
25use parking_lot::RwLock;
26use range_map_vec::RangeMap;
27use std::cell::Cell;
28use std::collections::BTreeMap;
29use std::sync::Arc;
30use std::sync::Weak;
31use std::task::Context;
32use std::task::Poll;
33use std::task::Waker;
34use zerocopy::FromBytes;
35
36type InterceptRanges<U> =
37 Arc<RwLock<RangeMap<U, (Box<str>, Weak<CloseableMutex<dyn ChipsetDevice>>)>>>;
38
39#[derive(Default)]
46pub struct FuzzChipset {
47 devices: Vec<Arc<CloseableMutex<dyn ChipsetDevice>>>,
48 mmio_ranges: InterceptRanges<u64>,
49 pio_ranges: InterceptRanges<u16>,
50 pci_devices: BTreeMap<(u8, u8, u8), Weak<CloseableMutex<dyn ChipsetDevice>>>,
51 poll_devices: Vec<Weak<CloseableMutex<dyn ChipsetDevice>>>,
52 max_defer_poll_count: usize,
53}
54
55impl FuzzChipset {
56 pub fn new(max_poll_count: usize) -> Self {
59 Self {
60 devices: Default::default(),
61 mmio_ranges: Default::default(),
62 pio_ranges: Default::default(),
63 pci_devices: Default::default(),
64 poll_devices: Default::default(),
65 max_defer_poll_count: max_poll_count,
66 }
67 }
68
69 pub fn device_builder<T: ChipsetDevice>(
71 &mut self,
72 name: &'static str,
73 ) -> ArcMutexChipsetDeviceBuilder<FuzzChipsetServicesImpl<'_>, T> {
74 ArcMutexChipsetDeviceBuilder::new(name.into(), |dev, _name| {
75 FuzzChipsetServicesImpl::new(self, dev)
76 })
77 }
78
79 fn mmio_read(&self, addr: u64, data: &mut [u8]) -> Option<()> {
81 let dev = self.mmio_ranges.read().get(&addr)?.1.upgrade().unwrap();
84 let mut locked_dev = dev.lock();
85 let result = locked_dev
86 .supports_mmio()
87 .expect("objects on the mmio bus support mmio")
88 .mmio_read(addr, data);
89 match result {
90 IoResult::Ok => {}
91 IoResult::Err(_) => {
92 data.fill(!0);
93 }
94 IoResult::Defer(t) => self.defer_read_now_or_never(&mut *locked_dev, t, data),
95 }
96 Some(())
97 }
98
99 fn mmio_write(&self, addr: u64, data: &[u8]) -> Option<()> {
101 let dev = self.mmio_ranges.read().get(&addr)?.1.upgrade().unwrap();
104 let mut locked_dev = dev.lock();
105 let result = locked_dev
106 .supports_mmio()
107 .expect("objects on the mmio bus support mmio")
108 .mmio_write(addr, data);
109 match result {
110 IoResult::Ok => {}
111 IoResult::Err(_) => {}
112 IoResult::Defer(t) => self.defer_write_now_or_never(&mut *locked_dev, t),
113 }
114 Some(())
115 }
116
117 fn pio_read(&self, addr: u16, data: &mut [u8]) -> Option<()> {
119 let dev = self.pio_ranges.read().get(&addr)?.1.upgrade().unwrap();
122 let mut locked_dev = dev.lock();
123 let result = locked_dev
124 .supports_pio()
125 .expect("objects on the pio bus support pio")
126 .io_read(addr, data);
127 match result {
128 IoResult::Ok => {}
129 IoResult::Err(_) => {
130 data.fill(!0);
131 }
132 IoResult::Defer(t) => self.defer_read_now_or_never(&mut *locked_dev, t, data),
133 }
134 Some(())
135 }
136
137 fn pio_write(&self, addr: u16, data: &[u8]) -> Option<()> {
139 let dev = self.pio_ranges.read().get(&addr)?.1.upgrade().unwrap();
142 let mut locked_dev = dev.lock();
143 let result = locked_dev
144 .supports_pio()
145 .expect("objects on the pio bus support pio")
146 .io_write(addr, data);
147 match result {
148 IoResult::Ok => {}
149 IoResult::Err(_) => {}
150 IoResult::Defer(t) => self.defer_write_now_or_never(&mut *locked_dev, t),
151 }
152 Some(())
153 }
154
155 fn pci_read(&self, bdf: (u8, u8, u8), offset: u16, data: &mut [u8]) -> Option<()> {
157 let dev = self.pci_devices.get(&bdf)?.upgrade().unwrap();
158 let mut locked_dev = dev.lock();
159 let result = locked_dev
160 .supports_pci()
161 .expect("objects on the pci bus support pci")
162 .pci_cfg_read(offset, u32::mut_from_bytes(data).unwrap());
163 match result {
164 IoResult::Ok => {}
165 IoResult::Err(_) => {
166 data.fill(0);
167 }
168 IoResult::Defer(t) => self.defer_read_now_or_never(&mut *locked_dev, t, data),
169 }
170 Some(())
171 }
172
173 fn pci_write(&self, bdf: (u8, u8, u8), offset: u16, value: u32) -> Option<()> {
175 let dev = self.pci_devices.get(&bdf)?.upgrade().unwrap();
176 let mut locked_dev = dev.lock();
177 let result = locked_dev
178 .supports_pci()
179 .expect("objects on the pci bus support pci")
180 .pci_cfg_write(offset, value);
181 match result {
182 IoResult::Ok => {}
183 IoResult::Err(_) => {}
184 IoResult::Defer(t) => self.defer_write_now_or_never(&mut *locked_dev, t),
185 }
186 Some(())
187 }
188
189 fn poll_device(&self, index: usize) -> Option<()> {
191 self.poll_devices[index]
192 .upgrade()
193 .unwrap()
194 .lock()
195 .supports_poll_device()
196 .expect("objects supporting polling support polling")
197 .poll_device(&mut Context::from_waker(Waker::noop()));
198 Some(())
199 }
200
201 fn defer_read_now_or_never(
203 &self,
204 dev: &mut dyn ChipsetDevice,
205 mut t: DeferredToken,
206 data: &mut [u8],
207 ) {
208 let mut cx = Context::from_waker(Waker::noop());
209 let dev = dev
210 .supports_poll_device()
211 .expect("objects returning a DeferredToken support polling");
212 for _ in 0..self.max_defer_poll_count {
217 dev.poll_device(&mut cx);
218 match t.poll_read(&mut cx, data) {
219 Poll::Ready(Ok(())) => return,
220 Poll::Ready(Err(e)) => panic!("deferred read failed: {:?}", e),
221 Poll::Pending => {}
222 }
223 }
224 if self.max_defer_poll_count == 0 {
225 panic!(
226 "Device operation returned a deferred read. Call FuzzChipset::new and set a non-zero max_poll_count to poll async operations."
227 );
228 } else {
229 panic!(
230 "Device operation returned a deferred read that didn't complete after {} polls",
231 self.max_defer_poll_count
232 )
233 }
234 }
235
236 fn defer_write_now_or_never(&self, dev: &mut dyn ChipsetDevice, mut t: DeferredToken) {
238 let mut cx = Context::from_waker(Waker::noop());
239 let dev = dev
240 .supports_poll_device()
241 .expect("objects returning a DeferredToken support polling");
242 for _ in 0..self.max_defer_poll_count {
247 dev.poll_device(&mut cx);
248 match t.poll_write(&mut cx) {
249 Poll::Ready(Ok(())) => return,
250 Poll::Ready(Err(e)) => panic!("deferred write failed: {:?}", e),
251 Poll::Pending => {}
252 }
253 }
254 if self.max_defer_poll_count == 0 {
255 panic!(
256 "Device operation returned a deferred write. Call FuzzChipset::new and set a non-zero max_poll_count to poll async operations."
257 );
258 } else {
259 panic!(
260 "Device operation returned a deferred write that didn't complete after {} polls",
261 self.max_defer_poll_count
262 )
263 }
264 }
265
266 pub fn get_arbitrary_action(
269 &self,
270 u: &mut arbitrary::Unstructured<'_>,
271 ) -> arbitrary::Result<ChipsetAction> {
272 #[derive(arbitrary::Arbitrary)]
273 enum ChipsetActionKind {
274 MmioRead,
275 MmioWrite,
276 PortIoRead,
277 PortIoWrite,
278 PciRead,
279 PciWrite,
280 Poll,
281 }
282
283 let action_kind: ChipsetActionKind = u.arbitrary()?;
284 let action = match action_kind {
285 ChipsetActionKind::MmioRead | ChipsetActionKind::MmioWrite => {
286 let active_ranges = self
287 .mmio_ranges
288 .read()
289 .iter()
290 .map(|(r, _)| r)
291 .collect::<Vec<_>>();
292 let range = u.choose(&active_ranges)?;
293
294 let addr = u.int_in_range(range.clone())?;
295 let len = *u.choose(&[1, 2, 4, 8])?;
296
297 if matches!(action_kind, ChipsetActionKind::MmioRead) {
298 ChipsetAction::MmioRead { addr, len }
299 } else {
300 let val = u.bytes(len)?.to_vec();
301 ChipsetAction::MmioWrite { addr, val }
302 }
303 }
304 ChipsetActionKind::PortIoRead | ChipsetActionKind::PortIoWrite => {
305 let active_ranges = self
306 .pio_ranges
307 .read()
308 .iter()
309 .map(|(r, _)| r)
310 .collect::<Vec<_>>();
311 let range = u.choose(&active_ranges)?;
312
313 let addr = u.int_in_range(range.clone())?;
314 let len = *u.choose(&[1, 2, 4])?;
315
316 if matches!(action_kind, ChipsetActionKind::PortIoRead) {
317 ChipsetAction::PortIoRead { addr, len }
318 } else {
319 let val = u.bytes(len)?.to_vec();
320 ChipsetAction::PortIoWrite { addr, val }
321 }
322 }
323 ChipsetActionKind::PciRead | ChipsetActionKind::PciWrite => {
324 let attached_bdfs = self.pci_devices.keys().collect::<Vec<_>>();
325 let bdf = **u.choose(&attached_bdfs)?;
326
327 let offset = u.int_in_range(0..=4096)?; if matches!(action_kind, ChipsetActionKind::PciRead) {
330 ChipsetAction::PciRead { bdf, offset }
331 } else {
332 ChipsetAction::PciWrite {
333 bdf,
334 offset,
335 val: u.arbitrary()?,
336 }
337 }
338 }
339 ChipsetActionKind::Poll => {
340 let index = u.choose_index(self.poll_devices.len())?;
341 ChipsetAction::Poll { index }
342 }
343 };
344
345 Ok(action)
346 }
347
348 pub fn exec_action(&self, action: ChipsetAction) -> Option<()> {
350 let mut buf = [0; 8];
351 match action {
352 ChipsetAction::MmioRead { addr, len } => self.mmio_read(addr, &mut buf[..len]),
353 ChipsetAction::MmioWrite { addr, val } => self.mmio_write(addr, &val),
354 ChipsetAction::PortIoRead { addr, len } => self.pio_read(addr, &mut buf[..len]),
355 ChipsetAction::PortIoWrite { addr, val } => self.pio_write(addr, &val),
356 ChipsetAction::PciRead { bdf, offset } => self.pci_read(bdf, offset, &mut buf[..4]),
357 ChipsetAction::PciWrite { bdf, offset, val } => self.pci_write(bdf, offset, val),
358 ChipsetAction::Poll { index } => self.poll_device(index),
359 }
360 }
361}
362
363#[derive(Debug)]
364pub enum ChipsetAction {
365 MmioRead {
366 addr: u64,
367 len: usize,
368 },
369 MmioWrite {
370 addr: u64,
371 val: Vec<u8>,
372 },
373 PortIoRead {
374 addr: u16,
375 len: usize,
376 },
377 PortIoWrite {
378 addr: u16,
379 val: Vec<u8>,
380 },
381 PciRead {
382 bdf: (u8, u8, u8),
383 offset: u16,
384 },
385 PciWrite {
386 bdf: (u8, u8, u8),
387 offset: u16,
388 val: u32,
389 },
390 Poll {
391 index: usize,
392 },
393}
394
395pub struct FuzzRegisterIntercept<U> {
397 dev: Weak<CloseableMutex<dyn ChipsetDevice>>,
398 map: InterceptRanges<U>,
399}
400
401struct FuzzControlIntercept<U> {
404 map: InterceptRanges<U>,
405 region_name: Box<str>,
406 len: U,
407 addr: Option<U>,
408 io: Weak<CloseableMutex<dyn ChipsetDevice>>,
409}
410
411macro_rules! impl_intercept {
412 ($register_trait:ident, $control_trait:ident, $register:ident, $control:ident, $usize:ty) => {
413 pub type $register = FuzzRegisterIntercept<$usize>;
414 type $control = FuzzControlIntercept<$usize>;
415
416 impl $register_trait for $register {
417 fn new_io_region(&mut self, region_name: &str, len: $usize) -> Box<dyn $control_trait> {
418 Box::new($control {
419 map: self.map.clone(),
420 region_name: region_name.into(),
421 len,
422 addr: None,
423 io: self.dev.clone(),
424 })
425 }
426 }
427
428 impl $control_trait for $control {
429 fn region_name(&self) -> &str {
430 &self.region_name
431 }
432
433 fn map(&mut self, addr: $usize) {
434 self.unmap();
435 if self.map.write().insert(
436 addr..=addr
437 .checked_add(self.len - 1)
438 .expect("overflow during addition, not possible in real hardware"),
439 (self.region_name.clone(), self.io.clone()),
440 ) {
441 self.addr = Some(addr);
442 } else {
443 tracing::trace!("{}::map failed", stringify!($control));
444 }
445 }
446
447 fn unmap(&mut self) {
448 if let Some(addr) = self.addr.take() {
449 let _entry = self.map.write().remove(&addr).unwrap();
450 }
451 }
452
453 fn addr(&self) -> Option<$usize> {
454 self.addr
455 }
456
457 fn len(&self) -> $usize {
458 self.len
459 }
460
461 fn offset_of(&self, addr: $usize) -> Option<$usize> {
462 let base = self.addr?;
463
464 (base..(base + self.len))
465 .contains(&addr)
466 .then(|| addr - base)
467 }
468 }
469 };
470}
471
472impl_intercept!(
473 RegisterMmioIntercept,
474 ControlMmioIntercept,
475 FuzzRegisterMmioIntercept,
476 FuzzControlMmioIntercept,
477 u64
478);
479impl_intercept!(
480 RegisterPortIoIntercept,
481 ControlPortIoIntercept,
482 FuzzRegisterPortIoIntercept,
483 FuzzControlPortIoIntercept,
484 u16
485);
486
487pub struct FuzzChipsetServicesImpl<'a> {
489 vm_chipset: &'a mut FuzzChipset,
490 dev: Weak<CloseableMutex<dyn ChipsetDevice>>,
491 took_mmio: Cell<bool>,
492 took_pio: Cell<bool>,
493 took_pci: Cell<bool>,
494 took_poll: Cell<bool>,
495}
496
497impl<'a> FuzzChipsetServicesImpl<'a> {
498 pub fn new(
499 vm_chipset: &'a mut FuzzChipset,
500 dev: Weak<CloseableMutex<dyn ChipsetDevice>>,
501 ) -> Self {
502 Self {
503 vm_chipset,
504 dev,
505 took_mmio: false.into(),
506 took_pio: false.into(),
507 took_pci: false.into(),
508 took_poll: false.into(),
509 }
510 }
511}
512
513pub enum FuzzChipsetServicesMeta {}
516impl ChipsetServicesMeta for FuzzChipsetServicesMeta {
517 type RegisterMmioIntercept = FuzzRegisterMmioIntercept;
518 type RegisterPortIoIntercept = FuzzRegisterPortIoIntercept;
519}
520
521impl ChipsetServices for FuzzChipsetServicesImpl<'_> {
522 type M = FuzzChipsetServicesMeta;
523
524 #[inline(always)]
525 fn supports_mmio(&mut self) -> Option<&mut dyn MmioInterceptServices<M = Self::M>> {
526 Some(self)
527 }
528
529 #[inline(always)]
530 fn supports_pio(&mut self) -> Option<&mut dyn PortIoInterceptServices<M = Self::M>> {
531 Some(self)
532 }
533
534 #[inline(always)]
535 fn supports_pci(&mut self) -> Option<&mut dyn PciConfigSpaceServices<M = Self::M>> {
536 Some(self)
537 }
538
539 #[inline(always)]
540 fn supports_poll_device(&mut self) -> Option<&mut dyn PollDeviceServices<M = Self::M>> {
541 Some(self)
542 }
543}
544
545impl<T: ChipsetDevice> ArcMutexChipsetServicesFinalize<T> for FuzzChipsetServicesImpl<'_> {
546 fn finalize(self, dev: &Arc<CloseableMutex<T>>, _name: Arc<str>) {
547 self.vm_chipset.devices.push(dev.clone());
548 }
549}
550
551impl MmioInterceptServices for FuzzChipsetServicesImpl<'_> {
552 fn register_mmio(&self) -> FuzzRegisterMmioIntercept {
553 self.took_mmio.set(true);
554 FuzzRegisterMmioIntercept {
555 dev: self.dev.clone(),
556 map: self.vm_chipset.mmio_ranges.clone(),
557 }
558 }
559
560 fn is_being_used(&self) -> bool {
561 self.took_mmio.get()
562 }
563}
564
565impl PortIoInterceptServices for FuzzChipsetServicesImpl<'_> {
566 fn register_pio(&self) -> FuzzRegisterPortIoIntercept {
567 self.took_pio.set(true);
568 FuzzRegisterPortIoIntercept {
569 dev: self.dev.clone(),
570 map: self.vm_chipset.pio_ranges.clone(),
571 }
572 }
573
574 fn is_being_used(&self) -> bool {
575 self.took_pio.get()
576 }
577}
578
579impl PciConfigSpaceServices for FuzzChipsetServicesImpl<'_> {
580 fn register_static_pci(&mut self, bus: u8, device: u8, function: u8) {
581 self.took_pci.set(true);
582 self.vm_chipset
583 .pci_devices
584 .insert((bus, device, function), self.dev.clone());
585 }
586
587 fn is_being_used(&self) -> bool {
588 self.took_pci.get()
589 }
590}
591
592impl PollDeviceServices for FuzzChipsetServicesImpl<'_> {
593 fn register_poll(&mut self) {
594 self.took_poll.set(true);
595 self.vm_chipset.poll_devices.push(self.dev.clone());
596 }
597
598 fn is_being_used(&self) -> bool {
599 self.took_poll.get()
600 }
601}