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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

//! Resource definitions for chipset devices.

#![warn(missing_docs)]
#![forbid(unsafe_code)]

use async_trait::async_trait;
use chipset_device::ChipsetDevice;
use guestmem::GuestMemory;
use inspect::InspectMut;
use std::ops::RangeInclusive;
use vm_resource::kind::ChipsetDeviceHandleKind;
use vm_resource::CanResolveTo;
use vmcore::device_state::ChangeDeviceState;
use vmcore::line_interrupt::LineInterrupt;
use vmcore::save_restore::ProtobufSaveRestore;
use vmcore::vm_task::VmTaskDriverSource;
use vmcore::vmtime::VmTimeSource;

/// A unique identifier for a line set.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct LineSetId(&'static str);

impl LineSetId {
    /// Returns the name of the line set.
    pub fn name(&self) -> &str {
        self.0
    }
}

/// Line set for device interrupts connected to the platform interrupt
/// controller.
pub const IRQ_LINE_SET: LineSetId = LineSetId("irq");
/// Line set for ACPI general purpose events.
pub const GPE0_LINE_SET: LineSetId = LineSetId("gpe0");
/// Line set for the BSP's local interrupts (LINT0/1) on x86.
pub const BSP_LINT_LINE_SET: LineSetId = LineSetId("bsp_lint");

impl CanResolveTo<ResolvedChipsetDevice> for ChipsetDeviceHandleKind {
    type Input<'a> = ResolveChipsetDeviceHandleParams<'a>;
}

/// Parameters used when resolving a resource with kind
/// [`ChipsetDeviceHandleKind`].
pub struct ResolveChipsetDeviceHandleParams<'a> {
    /// The name of the device.
    pub device_name: &'a str,
    /// Guest memory for device DMA operations for untrusted devices.
    pub guest_memory: &'a GuestMemory,
    /// Guest memory for device DMA operations for trusted devices, which can
    /// access encrypted memory on CVMs.
    ///
    /// For non-CVMs, this is the same as `guest_memory`.
    pub encrypted_guest_memory: &'a GuestMemory,
    /// The VM time source.
    pub vmtime: &'a VmTimeSource,
    /// Whether the VM is restoring from a saved state.
    ///
    /// FUTURE: remove this once devices have a state transition for "first
    /// boot". Device authors: try to avoid taking a dependency on this. If
    /// possible, delay any "first boot" initialization until it's really
    /// needed.
    pub is_restoring: bool,
    /// An object to confiure the chipset device's connection to the platform.
    pub configure: &'a mut dyn ConfigureChipsetDevice,
    /// The task driver source for the VM.
    pub task_driver_source: &'a VmTaskDriverSource,
    /// Object to register for MMIO intercepts.
    pub register_mmio: &'a mut (dyn chipset_device::mmio::RegisterMmioIntercept + Send),
    /// Object to register for PIO intercepts.
    pub register_pio: &'a mut (dyn chipset_device::pio::RegisterPortIoIntercept + Send),
}

/// A trait for configuring a chipset device's connection to the platform.
pub trait ConfigureChipsetDevice: Send {
    /// Creates a new line interrupt.
    fn new_line(&mut self, id: LineSetId, name: &str, vector: u32) -> LineInterrupt;

    /// Adds this device as a target for a range of line interrupts.
    fn add_line_target(
        &mut self,
        id: LineSetId,
        source_range: RangeInclusive<u32>,
        target_start: u32,
    );

    /// Tags this device so that its save/restore routines will not be called.
    fn omit_saved_state(&mut self);
}

#[async_trait]
trait DynChipsetDevice: ChipsetDevice + ProtobufSaveRestore + InspectMut {
    fn start(&mut self);
    async fn stop(&mut self);
    async fn reset(&mut self);
}

#[async_trait]
impl<T: ChangeDeviceState + ChipsetDevice + ProtobufSaveRestore + InspectMut> DynChipsetDevice
    for T
{
    fn start(&mut self) {
        self.start()
    }
    async fn stop(&mut self) {
        self.stop().await
    }
    async fn reset(&mut self) {
        self.reset().await
    }
}

/// A resolved chipset device resource.
pub struct ResolvedChipsetDevice(pub ErasedChipsetDevice);

/// A type-erased [`ChipsetDevice`].
#[derive(InspectMut)]
#[inspect(transparent(mut))]
pub struct ErasedChipsetDevice(Box<dyn DynChipsetDevice>);

impl<T: ChangeDeviceState + ChipsetDevice + ProtobufSaveRestore + InspectMut> From<T>
    for ResolvedChipsetDevice
{
    fn from(value: T) -> Self {
        Self(ErasedChipsetDevice(Box::new(value)))
    }
}

impl ChangeDeviceState for ErasedChipsetDevice {
    fn start(&mut self) {
        self.0.start()
    }

    async fn stop(&mut self) {
        self.0.stop().await
    }

    async fn reset(&mut self) {
        self.0.reset().await
    }
}

impl ChipsetDevice for ErasedChipsetDevice {
    fn supports_pio(&mut self) -> Option<&mut dyn chipset_device::pio::PortIoIntercept> {
        self.0.supports_pio()
    }

    fn supports_mmio(&mut self) -> Option<&mut dyn chipset_device::mmio::MmioIntercept> {
        self.0.supports_mmio()
    }

    fn supports_pci(&mut self) -> Option<&mut dyn chipset_device::pci::PciConfigSpace> {
        self.0.supports_pci()
    }

    fn supports_poll_device(&mut self) -> Option<&mut dyn chipset_device::poll_device::PollDevice> {
        self.0.supports_poll_device()
    }

    fn supports_line_interrupt_target(
        &mut self,
    ) -> Option<&mut dyn chipset_device::interrupt::LineInterruptTarget> {
        self.0.supports_line_interrupt_target()
    }

    fn supports_handle_eoi(&mut self) -> Option<&mut dyn chipset_device::interrupt::HandleEoi> {
        self.0.supports_handle_eoi()
    }

    fn supports_acknowledge_pic_interrupt(
        &mut self,
    ) -> Option<&mut dyn chipset_device::interrupt::AcknowledgePicInterrupt> {
        self.0.supports_acknowledge_pic_interrupt()
    }
}

impl ProtobufSaveRestore for ErasedChipsetDevice {
    fn save(
        &mut self,
    ) -> Result<vmcore::save_restore::SavedStateBlob, vmcore::save_restore::SaveError> {
        self.0.save()
    }

    fn restore(
        &mut self,
        state: vmcore::save_restore::SavedStateBlob,
    ) -> Result<(), vmcore::save_restore::RestoreError> {
        self.0.restore(state)
    }
}