vmotherboard/chipset/builder/
mod.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Exports [`ChipsetBuilder`].
5
6mod errors;
7
8use self::errors::ChipsetBuilderError;
9use self::errors::ErrorListExt;
10use self::errors::FinalChipsetBuilderError;
11use super::backing::arc_mutex::device::ArcMutexChipsetDeviceBuilder;
12use super::backing::arc_mutex::pci::BusResolverWeakMutexPci;
13use super::backing::arc_mutex::pci::RegisterWeakMutexPci;
14use super::backing::arc_mutex::pci::WeakMutexPciEntry;
15use super::backing::arc_mutex::services::ArcMutexChipsetServices;
16use super::backing::arc_mutex::state_unit::ArcMutexChipsetDeviceUnit;
17use crate::BusIdPci;
18use crate::DebugEventHandler;
19use crate::VmmChipsetDevice;
20use crate::chipset::Chipset;
21use crate::chipset::io_ranges::IoRanges;
22use chipset_device::ChipsetDevice;
23use chipset_device_resources::LineSetId;
24use closeable_mutex::CloseableMutex;
25use pal_async::task::Spawn;
26use pal_async::task::Task;
27use state_unit::SpawnedUnit;
28use state_unit::StateUnits;
29use state_unit::UnitHandle;
30use std::ops::RangeInclusive;
31use std::sync::Arc;
32use std::sync::Weak;
33use vmcore::line_interrupt::LineSetTarget;
34use vmcore::vm_task::VmTaskDriverSource;
35use vmcore::vmtime::VmTimeSource;
36
37/// A (type erased) bundle of state unit handles for added devices.
38pub struct ChipsetDevices {
39    chipset_unit: UnitHandle,
40    _chipset_task: Task<()>,
41    _arc_mutex_device_units: Vec<SpawnedUnit<ArcMutexChipsetDeviceUnit>>,
42    _line_set_units: Vec<SpawnedUnit<()>>,
43}
44
45impl ChipsetDevices {
46    /// The root chipset unit handle.
47    ///
48    /// All devices that have MMIO, PIO, or PCI callbacks have a "stop after"
49    /// dependency on this handle.
50    pub fn chipset_unit(&self) -> &UnitHandle {
51        &self.chipset_unit
52    }
53}
54
55#[derive(Default)]
56pub(crate) struct BusResolver {
57    pci: BusResolverWeakMutexPci,
58}
59
60/// A builder for [`Chipset`]
61pub struct ChipsetBuilder<'a> {
62    // The chipset that's getting built-up
63    pub(crate) vm_chipset: Chipset,
64
65    pub(crate) bus_resolver: BusResolver,
66
67    // External runtime dependencies
68    pub(crate) units: &'a StateUnits,
69    pub(crate) driver_source: &'a VmTaskDriverSource,
70    pub(crate) vmtime: &'a VmTimeSource,
71    pub(crate) vmtime_unit: &'a UnitHandle,
72
73    // Root chipset state-unit (which devices may need to take a dependency on,
74    // if they use any Chipset-specific services)
75    pub(crate) chipset_unit: UnitHandle,
76    chipset_recv: mesh::Receiver<state_unit::StateRequest>,
77
78    line_sets: super::line_sets::LineSets,
79
80    // Fields related to `Arc + Mutex`-backed `ChipsetDevice` construction
81    arc_mutex_device_units: Vec<SpawnedUnit<ArcMutexChipsetDeviceUnit>>,
82}
83
84impl<'a> ChipsetBuilder<'a> {
85    pub(crate) fn new(
86        driver_source: &'a VmTaskDriverSource,
87        units: &'a StateUnits,
88        debug_event_handler: Arc<dyn DebugEventHandler>,
89        vmtime: &'a VmTimeSource,
90        vmtime_unit: &'a UnitHandle,
91        trace_unknown_pio: bool,
92        trace_unknown_mmio: bool,
93        fallback_mmio_device: Option<Arc<CloseableMutex<dyn ChipsetDevice>>>,
94    ) -> Self {
95        let (send, chipset_recv) = mesh::channel();
96        let chipset_unit = units.add("chipset").build(send).unwrap();
97
98        Self {
99            vm_chipset: Chipset {
100                mmio_ranges: IoRanges::new(trace_unknown_mmio, fallback_mmio_device),
101                pio_ranges: IoRanges::new(trace_unknown_pio, None),
102
103                pic: None,
104                eoi_handler: None,
105                debug_event_handler,
106            },
107
108            bus_resolver: BusResolver::default(),
109
110            units,
111            driver_source,
112            vmtime,
113            vmtime_unit,
114
115            chipset_unit,
116            chipset_recv,
117
118            line_sets: super::line_sets::LineSets::new(),
119
120            arc_mutex_device_units: Vec::new(),
121        }
122    }
123
124    pub(crate) fn register_arc_mutex_device_unit(
125        &mut self,
126        unit: SpawnedUnit<ArcMutexChipsetDeviceUnit>,
127    ) {
128        self.arc_mutex_device_units.push(unit)
129    }
130
131    pub(crate) fn register_weak_mutex_pci_bus(
132        &mut self,
133        bus_id: BusIdPci,
134        bus: Box<dyn RegisterWeakMutexPci>,
135    ) {
136        let existing = self.bus_resolver.pci.buses.insert(bus_id.clone(), bus);
137        assert!(
138            existing.is_none(),
139            "shouldn't be possible to have duplicate bus IDs: {:?}",
140            bus_id
141        )
142    }
143
144    pub(crate) fn register_weak_mutex_pci_device(
145        &mut self,
146        bus_id: BusIdPci,
147        bdf: (u8, u8, u8),
148        name: Arc<str>,
149        dev: Weak<CloseableMutex<dyn ChipsetDevice>>,
150    ) {
151        self.bus_resolver
152            .pci
153            .devices
154            .entry(bus_id)
155            .or_default()
156            .push(WeakMutexPciEntry { bdf, name, dev });
157    }
158
159    pub(crate) fn line_set(
160        &mut self,
161        id: LineSetId,
162    ) -> (&vmcore::line_interrupt::LineSet, &UnitHandle) {
163        self.line_sets.line_set(self.driver_source, self.units, id)
164    }
165
166    #[must_use]
167    pub(crate) fn try_set_pic(
168        &mut self,
169        pic: Option<Arc<CloseableMutex<dyn ChipsetDevice>>>,
170    ) -> bool {
171        if self.vm_chipset.pic.is_some() {
172            return false;
173        }
174        self.vm_chipset.pic = pic;
175        true
176    }
177
178    #[must_use]
179    pub(crate) fn try_set_eoi_handler(
180        &mut self,
181        eoi_handler: Option<Arc<CloseableMutex<dyn ChipsetDevice>>>,
182    ) -> bool {
183        if self.vm_chipset.eoi_handler.is_some() {
184            return false;
185        }
186        self.vm_chipset.eoi_handler = eoi_handler;
187        true
188    }
189
190    /// Add a new [`ChipsetDevice`](chipset_device::ChipsetDevice) to the
191    /// chipset. **`dev_name` must be unique!**
192    pub fn arc_mutex_device<'b, T: VmmChipsetDevice>(
193        &'b mut self,
194        dev_name: impl Into<Arc<str>>,
195    ) -> ArcMutexChipsetDeviceBuilder<'b, 'a, T> {
196        ArcMutexChipsetDeviceBuilder::new(dev_name.into(), |dev, name| {
197            ArcMutexChipsetServices::new(self, dev.clone(), name)
198        })
199    }
200
201    /// Wrap up device construction, returning the completed chipset and devices
202    pub fn build(mut self) -> Result<(Arc<Chipset>, ChipsetDevices), FinalChipsetBuilderError> {
203        let mut errs = None;
204
205        for conflict in (self.vm_chipset.mmio_ranges).take_static_registration_conflicts() {
206            errs.append(ChipsetBuilderError::MmioConflict(conflict));
207        }
208
209        for conflict in (self.vm_chipset.pio_ranges).take_static_registration_conflicts() {
210            errs.append(ChipsetBuilderError::PioConflict(conflict));
211        }
212
213        {
214            let BusResolver { pci } = self.bus_resolver;
215
216            match pci.resolve() {
217                Ok(()) => {}
218                Err(conflicts) => {
219                    for conflict in conflicts {
220                        errs.append(ChipsetBuilderError::PciConflict(conflict));
221                    }
222                }
223            }
224        }
225
226        if let Some(err) = errs {
227            return Err(FinalChipsetBuilderError(err));
228        }
229
230        // Spawn a task for the chipset unit.
231        let vm_chipset = Arc::new(self.vm_chipset);
232        let chipset_task = self.driver_source.simple().spawn("chipset-unit", {
233            let vm_chipset = vm_chipset.clone();
234            let mut recv = self.chipset_recv;
235            async move {
236                while let Ok(req) = recv.recv().await {
237                    req.apply(&mut chipset_unit::ChipsetUnit(&vm_chipset)).await;
238                }
239            }
240        });
241
242        let devices = ChipsetDevices {
243            chipset_unit: self.chipset_unit,
244            _chipset_task: chipset_task,
245            _arc_mutex_device_units: self.arc_mutex_device_units,
246            _line_set_units: self.line_sets.units,
247        };
248
249        Ok((vm_chipset, devices))
250    }
251
252    /// Add a new line set target from an external source.
253    pub fn add_external_line_target(
254        &mut self,
255        id: LineSetId,
256        source_range: RangeInclusive<u32>,
257        target_start: u32,
258        debug_label: &str,
259        target: Arc<dyn LineSetTarget>,
260    ) {
261        self.line_set(id)
262            .0
263            .add_target(source_range, target_start, debug_label, target)
264    }
265}
266
267mod chipset_unit {
268    use crate::Chipset;
269    use inspect::InspectMut;
270    use state_unit::StateUnit;
271    use vmcore::save_restore::RestoreError;
272    use vmcore::save_restore::SaveError;
273    use vmcore::save_restore::SavedStateBlob;
274
275    #[derive(InspectMut)]
276    #[inspect(transparent)]
277    pub struct ChipsetUnit<'a>(pub &'a Chipset);
278
279    impl StateUnit for ChipsetUnit<'_> {
280        async fn start(&mut self) {}
281
282        async fn stop(&mut self) {}
283
284        async fn reset(&mut self) -> anyhow::Result<()> {
285            Ok(())
286        }
287
288        async fn save(&mut self) -> Result<Option<SavedStateBlob>, SaveError> {
289            Ok(None)
290        }
291
292        async fn restore(&mut self, _buffer: SavedStateBlob) -> Result<(), RestoreError> {
293            Err(RestoreError::SavedStateNotSupported)
294        }
295    }
296}