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