chipset_device/lib.rs
1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Types and traits to model chipset devices, and associated chipset services.
5
6#![forbid(unsafe_code)]
7
8/// Implemented by any device that is considered part of the guest's "chipset"
9/// (insofar as it exists on one or more system busses).
10//
11// DEVNOTE: In the past, `ChipsetDevice` included explicit bounds for things
12// like `Inspect` and `SaveRestore`, traits that all OpenVMM devices are
13// expected to implement. While this works, it comes with a few drawbacks:
14// - It adds some heavy dependencies to an otherwise lightweight crate
15// - It adds substantial boilerplate when implementing "test" devices, as those
16// traits need to be implemented + stubbed-out with `todo!()`s
17pub trait ChipsetDevice: 'static + Send /* see DEVNOTE before adding bounds */ {
18 /// Optionally returns a trait object to send IO port intercepts to.
19 #[inline(always)]
20 fn supports_pio(&mut self) -> Option<&mut dyn pio::PortIoIntercept> {
21 None
22 }
23
24 /// Optionally returns a trait object to send MMIO port intercepts to.
25 #[inline(always)]
26 fn supports_mmio(&mut self) -> Option<&mut dyn mmio::MmioIntercept> {
27 None
28 }
29
30 /// Optionally returns a trait object to send PCI config space accesses to.
31 #[inline(always)]
32 fn supports_pci(&mut self) -> Option<&mut dyn pci::PciConfigSpace> {
33 None
34 }
35
36 /// Optionally returns a trait object to send poll requests to.
37 #[inline(always)]
38 fn supports_poll_device(&mut self) -> Option<&mut dyn poll_device::PollDevice> {
39 None
40 }
41
42 /// Optionally returns a trait object to send interrupt line changes to.
43 #[inline(always)]
44 fn supports_line_interrupt_target(
45 &mut self,
46 ) -> Option<&mut dyn interrupt::LineInterruptTarget> {
47 None
48 }
49
50 /// Optionally returns a trait object to send EOI requests to.
51 #[inline(always)]
52 fn supports_handle_eoi(&mut self) -> Option<&mut dyn interrupt::HandleEoi> {
53 None
54 }
55
56 /// Optionally returns a trait object with which to acknowledge PIC
57 /// interrupts.
58 #[inline(always)]
59 fn supports_acknowledge_pic_interrupt(
60 &mut self,
61 ) -> Option<&mut dyn interrupt::AcknowledgePicInterrupt> {
62 None
63 }
64
65 /// Optionally returns a trait object which implements TDISP host
66 /// communication.
67 #[inline(always)]
68 fn supports_tdisp(&mut self) -> Option<&mut dyn tdisp::TdispHostDeviceTarget> {
69 None
70 }
71}
72
73/// Shared by `mmio` and `pio`
74macro_rules! io_region {
75 ($register:ident, $control:ident, $addr:ty) => {
76 /// A trait to register device-specific IO intercept regions.
77 pub trait $register {
78 /// Registers a new IO region of the given length.
79 fn new_io_region(&mut self, region_name: &str, len: $addr) -> Box<dyn $control>;
80 }
81
82 /// A trait to map/unmap a device-specific IO memory region.
83 pub trait $control: Send + Sync {
84 /// Return the region's name.
85 fn region_name(&self) -> &str;
86
87 /// Enables the IO region at the given address.
88 ///
89 /// This method will never fail, as devices are not expected to gracefully
90 /// handle the case where an IO region overlaps with an existing region.
91 fn map(&mut self, addr: $addr);
92
93 /// Disables the IO region.
94 fn unmap(&mut self);
95
96 /// Return the currently mapped address.
97 ///
98 /// Returns `None` if the region is currently unmapped.
99 fn addr(&self) -> Option<$addr>;
100
101 /// Return the length of the region.
102 fn len(&self) -> $addr;
103
104 /// Return the offset of `addr` from the region's base address.
105 ///
106 /// Returns `None` if the provided `addr` is outside of the memory
107 /// region, or the region is currently unmapped.
108 ///
109 /// # Example
110 ///
111 /// ```ignore
112 /// let foo_region = register.new_io_region("foo", 0x10);
113 /// foo_region.map(0x1000);
114 /// assert_eq!(foo_region.offset_of(0x1003), Some(3));
115 /// assert_eq!(foo_region.offset_of(0x900), None);
116 /// assert_eq!(foo_region.offset_of(0x1020), None);
117 /// foo_region.unmap();
118 /// assert_eq!(foo_region.offset_of(0x1003), None);
119 /// ```
120 fn offset_of(&self, addr: $addr) -> Option<$addr>;
121 }
122
123 // DEVNOTE: we explicitly want to implement Inspect using trait method
124 // (as opposed to adding a `: Inspect` bound) so we can ensure a
125 // consistent inspect tree regardless of backing implementation.
126 impl inspect::Inspect for dyn $control {
127 fn inspect(&self, req: inspect::Request<'_>) {
128 req.respond()
129 .field("name", self.region_name())
130 .hex("len", self.len())
131 .field("addr", self.addr().map(inspect::AsHex));
132 }
133 }
134 };
135}
136
137pub mod interrupt;
138pub mod io;
139pub mod mmio;
140pub mod pci;
141pub mod pio;
142pub mod poll_device;