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