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