Skip to main content

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 advertises that the
66    /// ChipsetDevice can respond to tdisp requests as a physical device (not
67    /// relayed) in the host (openvmm).
68    #[inline(always)]
69    fn supports_tdisp_host(&mut self) -> Option<&mut dyn tdisp::TdispHostDeviceTarget> {
70        None
71    }
72
73    /// Optionally returns a trait object which advertises that the
74    /// ChipsetDevice is paravirtualizing a device's TDISP interface in the
75    /// guest. This should only be implemented within a guest as part of a
76    /// virtual bus (VPCI, EPCI).
77    #[inline(always)]
78    fn supports_tdisp_relay(&mut self) -> Option<&mut dyn tdisp::TdispRelayedDeviceTarget> {
79        None
80    }
81}
82
83/// Shared by `mmio` and `pio`
84macro_rules! io_region {
85    ($register:ident, $control:ident, $addr:ty) => {
86        /// A trait to register device-specific IO intercept regions.
87        pub trait $register {
88            /// Registers a new IO region of the given length.
89            fn new_io_region(&mut self, region_name: &str, len: $addr) -> Box<dyn $control>;
90        }
91
92        /// A trait to map/unmap a device-specific IO memory region.
93        pub trait $control: Send + Sync {
94            /// Return the region's name.
95            fn region_name(&self) -> &str;
96
97            /// Enables the IO region at the given address.
98            ///
99            /// This method will never fail, as devices are not expected to gracefully
100            /// handle the case where an IO region overlaps with an existing region.
101            fn map(&mut self, addr: $addr);
102
103            /// Disables the IO region.
104            fn unmap(&mut self);
105
106            /// Return the currently mapped address.
107            ///
108            /// Returns `None` if the region is currently unmapped.
109            fn addr(&self) -> Option<$addr>;
110
111            /// Return the length of the region.
112            fn len(&self) -> $addr;
113
114            /// Return the offset of `addr` from the region's base address.
115            ///
116            /// Returns `None` if the provided `addr` is outside of the memory
117            /// region, or the region is currently unmapped.
118            ///
119            /// # Example
120            ///
121            /// ```ignore
122            /// let foo_region = register.new_io_region("foo", 0x10);
123            /// foo_region.map(0x1000);
124            /// assert_eq!(foo_region.offset_of(0x1003), Some(3));
125            /// assert_eq!(foo_region.offset_of(0x900), None);
126            /// assert_eq!(foo_region.offset_of(0x1020), None);
127            /// foo_region.unmap();
128            /// assert_eq!(foo_region.offset_of(0x1003), None);
129            /// ```
130            fn offset_of(&self, addr: $addr) -> Option<$addr>;
131        }
132
133        // DEVNOTE: we explicitly want to implement Inspect using trait method
134        // (as opposed to adding a `: Inspect` bound) so we can ensure a
135        // consistent inspect tree regardless of backing implementation.
136        impl inspect::Inspect for dyn $control {
137            fn inspect(&self, req: inspect::Request<'_>) {
138                req.respond()
139                    .field("name", self.region_name())
140                    .hex("len", self.len())
141                    .field("addr", self.addr().map(inspect::AsHex));
142            }
143        }
144    };
145}
146
147pub mod interrupt;
148pub mod io;
149pub mod isa_dma;
150pub mod mmio;
151pub mod pci;
152pub mod pio;
153pub mod poll_device;