1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

//! Types and traits to model chipset devices, and associated chipset services.

#![warn(missing_docs)]

/// Implemented by any device that is considered part of the guest's "chipset"
/// (insofar as it exists on one or more system busses).
//
// DEVNOTE: In the past, `ChipsetDevice` included explicit bounds for things
// like `Inspect` and `SaveRestore`, traits that all OpenVMM devices are
// expected to implement. While this works, it comes with a few drawbacks:
// - It adds some heavy dependencies to an otherwise lightweight crate
// - It adds substantial boilerplate when implementing "test" devices, as those
//   traits need to be implemented + stubbed-out with `todo!()`s
pub trait ChipsetDevice: 'static + Send /* see DEVNOTE before adding bounds */ {
    /// Optionally returns a trait object to send IO port intercepts to.
    #[inline(always)]
    fn supports_pio(&mut self) -> Option<&mut dyn pio::PortIoIntercept> {
        None
    }

    /// Optionally returns a trait object to send MMIO port intercepts to.
    #[inline(always)]
    fn supports_mmio(&mut self) -> Option<&mut dyn mmio::MmioIntercept> {
        None
    }

    /// Optionally returns a trait object to send PCI config space accesses to.
    #[inline(always)]
    fn supports_pci(&mut self) -> Option<&mut dyn pci::PciConfigSpace> {
        None
    }

    /// Optionally returns a trait object to send poll requests to.
    #[inline(always)]
    fn supports_poll_device(&mut self) -> Option<&mut dyn poll_device::PollDevice> {
        None
    }

    /// Optionally returns a trait object to send interrupt line changes to.
    #[inline(always)]
    fn supports_line_interrupt_target(
        &mut self,
    ) -> Option<&mut dyn interrupt::LineInterruptTarget> {
        None
    }

    /// Optionally returns a trait object to send EOI requests to.
    #[inline(always)]
    fn supports_handle_eoi(&mut self) -> Option<&mut dyn interrupt::HandleEoi> {
        None
    }

    /// Optionally returns a trait object with which to acknowledge PIC
    /// interrupts.
    #[inline(always)]
    fn supports_acknowledge_pic_interrupt(
        &mut self,
    ) -> Option<&mut dyn interrupt::AcknowledgePicInterrupt> {
        None
    }
}

/// Shared by `mmio` and `pio`
macro_rules! io_region {
    ($register:ident, $control:ident, $addr:ty) => {
        /// A trait to register device-specific IO intercept regions.
        pub trait $register {
            /// Registers a new IO region of the given length.
            fn new_io_region(&mut self, region_name: &str, len: $addr) -> Box<dyn $control>;
        }

        /// A trait to map/unmap a device-specific IO memory region.
        pub trait $control: Send + Sync {
            /// Return the region's name.
            fn region_name(&self) -> &str;

            /// Enables the IO region at the given address.
            ///
            /// This method will never fail, as devices are not expected to gracefully
            /// handle the case where an IO region overlaps with an existing region.
            fn map(&mut self, addr: $addr);

            /// Disables the IO region.
            fn unmap(&mut self);

            /// Return the currently mapped address.
            ///
            /// Returns `None` if the region is currently unmapped.
            fn addr(&self) -> Option<$addr>;

            /// Return the length of the region.
            fn len(&self) -> $addr;

            /// Return the offset of `addr` from the region's base address.
            ///
            /// Returns `None` if the provided `addr` is outside of the memory
            /// region, or the region is currently unmapped.
            ///
            /// # Example
            ///
            /// ```ignore
            /// let foo_region = register.new_io_region("foo", 0x10);
            /// foo_region.map(0x1000);
            /// assert_eq!(foo_region.offset_of(0x1003), Some(3));
            /// assert_eq!(foo_region.offset_of(0x900), None);
            /// assert_eq!(foo_region.offset_of(0x1020), None);
            /// foo_region.unmap();
            /// assert_eq!(foo_region.offset_of(0x1003), None);
            /// ```
            fn offset_of(&self, addr: $addr) -> Option<$addr>;
        }

        // DEVNOTE: we explicitly want to implement Inspect using trait method
        // (as opposed to adding a `: Inspect` bound) so we can ensure a
        // consistent inspect tree regardless of backing implementation.
        impl inspect::Inspect for dyn $control {
            fn inspect(&self, req: inspect::Request<'_>) {
                req.respond()
                    .field("name", self.region_name())
                    .hex("len", self.len())
                    .field("addr", self.addr().map(inspect::AsHex));
            }
        }
    };
}

pub mod interrupt;
pub mod io;
pub mod mmio;
pub mod pci;
pub mod pio;
pub mod poll_device;