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;