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