vmotherboard/chipset/backing/arc_mutex/
device.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Infrastructure to wire up [`ChipsetDevice`] instances to the
5//! [`Chipset`](crate::Chipset).
6
7use super::services::ArcMutexChipsetServices;
8use crate::BusIdPci;
9use crate::VmmChipsetDevice;
10use arc_cyclic_builder::ArcCyclicBuilder;
11use arc_cyclic_builder::ArcCyclicBuilderExt;
12use chipset_device::mmio::RegisterMmioIntercept;
13use chipset_device::pio::RegisterPortIoIntercept;
14use closeable_mutex::CloseableMutex;
15use std::sync::Arc;
16use std::sync::Weak;
17use thiserror::Error;
18use tracing::instrument;
19
20#[derive(Debug, Error)]
21pub(crate) enum AddDeviceErrorKind {
22    #[error("could not construct device")]
23    DeviceError(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
24
25    #[error("no pci bus address provided")]
26    NoPciBusAddress,
27    #[error("error finalizing device")]
28    Finalize(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
29}
30
31impl AddDeviceErrorKind {
32    pub(crate) fn with_dev_name(self, dev_name: Arc<str>) -> AddDeviceError {
33        AddDeviceError {
34            dev_name,
35            inner: self,
36        }
37    }
38}
39
40/// Errors that may occur while adding a device to the chipset.
41#[derive(Debug, Error)]
42#[error("could not initialize {dev_name}")]
43pub struct AddDeviceError {
44    dev_name: Arc<str>,
45    #[source]
46    inner: AddDeviceErrorKind,
47}
48
49/// Additional trait implemented by Arc + CloseableMutex [`ChipsetServices`] that gives
50/// the services an opportunity to perform any chipset-specific wiring of the
51/// constructed `Arc<CloseableMutex<T: ChipsetDevice>>`.
52///
53/// This is a separate trait from [`ChipsetServices`] because it is specific to
54/// the ArcMutex infrastructure.
55pub trait ArcMutexChipsetServicesFinalize<T> {
56    /// The error type returned by the `finalize` method.
57    type Error;
58
59    /// Called to finish wiring up the device after it has been completely
60    /// constructed.
61    fn finalize(self, dev: &Arc<CloseableMutex<T>>, name: Arc<str>) -> Result<(), Self::Error>;
62}
63
64/// A builder to streamline the construction of `Arc + CloseableMutex` wrapped
65/// `ChipsetDevice` instances.
66pub struct ArcMutexChipsetDeviceBuilder<'a, 'b, T> {
67    services: ArcMutexChipsetServices<'a, 'b>,
68    arc_builder: ArcCyclicBuilder<CloseableMutex<T>>,
69
70    dev_name: Arc<str>,
71
72    pci_addr: Option<(u8, u8, u8)>,
73    pci_bus_id: Option<BusIdPci>,
74    external_pci: bool,
75}
76
77impl<'a, 'b, T> ArcMutexChipsetDeviceBuilder<'a, 'b, T>
78where
79    T: VmmChipsetDevice,
80{
81    /// Create a new [`ArcMutexChipsetDeviceBuilder`]
82    pub fn new(
83        name: Arc<str>,
84        new_device_services: impl FnOnce(
85            Weak<CloseableMutex<T>>,
86            Arc<str>,
87        ) -> ArcMutexChipsetServices<'a, 'b>,
88    ) -> Self {
89        let arc_builder: ArcCyclicBuilder<CloseableMutex<T>> = Arc::new_cyclic_builder();
90        let services = (new_device_services)(arc_builder.weak(), name.clone());
91
92        ArcMutexChipsetDeviceBuilder {
93            services,
94            arc_builder,
95
96            dev_name: name,
97
98            pci_addr: None,
99            pci_bus_id: None,
100            external_pci: false,
101        }
102    }
103
104    /// Omit device saved state. Be careful when using this! Currently only used
105    /// for `MissingDev`!
106    pub fn omit_saved_state(mut self) -> Self {
107        self.services.omit_saved_state();
108        self
109    }
110
111    /// For PCI devices: place the device at the following PCI address
112    pub fn with_pci_addr(mut self, bus: u8, device: u8, function: u8) -> Self {
113        self.pci_addr = Some((bus, device, function));
114        self
115    }
116
117    /// For PCI devices: place the device on the specific bus
118    pub fn on_pci_bus(mut self, id: BusIdPci) -> Self {
119        self.pci_bus_id = Some(id);
120        self
121    }
122
123    /// For PCI devices: do not register the device with any PCI bus. This is
124    /// used when the device is hooked up to a bus (such as a VPCI bus) outside
125    /// of the vmotherboard infrastructure.
126    pub fn with_external_pci(mut self) -> Self {
127        self.external_pci = true;
128        self
129    }
130
131    fn inner_add(
132        mut self,
133        typed_dev: Result<T, AddDeviceError>,
134    ) -> Result<Arc<CloseableMutex<T>>, AddDeviceError> {
135        let mut typed_dev = typed_dev?;
136
137        if let Some(dev) = typed_dev.supports_mmio() {
138            // static mmio registration
139            for (label, range) in dev.get_static_regions() {
140                self.services
141                    .register_mmio()
142                    .new_io_region(label, range.end() - range.start() + 1)
143                    .map(*range.start());
144            }
145        }
146
147        if let Some(dev) = typed_dev.supports_pio() {
148            // static pio registration
149            for (label, range) in dev.get_static_regions() {
150                self.services
151                    .register_pio()
152                    .new_io_region(label, range.end() - range.start() + 1)
153                    .map(*range.start());
154            }
155        }
156
157        if !self.external_pci {
158            if let Some(dev) = typed_dev.supports_pci() {
159                // static pci registration
160                let bdf = match (self.pci_addr, dev.suggested_bdf()) {
161                    (Some(override_bdf), Some(suggested_bdf)) => {
162                        let (ob, od, of) = override_bdf;
163                        let (sb, sd, sf) = suggested_bdf;
164                        tracing::info!(
165                            "overriding suggested bdf: using {:02x}:{:02x}:{} instead of {:02x}:{:02x}:{}",
166                            ob,
167                            od,
168                            of,
169                            sb,
170                            sd,
171                            sf
172                        );
173                        override_bdf
174                    }
175                    (None, Some(bdf)) | (Some(bdf), None) => bdf,
176                    (None, None) => {
177                        return Err(
178                            AddDeviceErrorKind::NoPciBusAddress.with_dev_name(self.dev_name)
179                        );
180                    }
181                };
182
183                let bus_id = match self.pci_bus_id.take() {
184                    Some(bus_id) => bus_id,
185                    None => panic!(
186                        "wiring error: did not invoke `on_pci_bus` for `{}`",
187                        self.dev_name
188                    ),
189                };
190
191                self.services.register_static_pci(bus_id, bdf);
192            }
193        }
194
195        let dev = self.arc_builder.build(CloseableMutex::new(typed_dev));
196
197        // Now ask the services to finish wiring up the device.
198        self.services
199            .finalize(&dev, self.dev_name.clone())
200            .map_err(|err| AddDeviceErrorKind::Finalize(err.into()).with_dev_name(self.dev_name))?;
201
202        Ok(dev)
203    }
204
205    /// Construct a new device.
206    ///
207    /// If the device can fail during initialization, use
208    /// [`try_add`](Self::try_add) instead.
209    ///
210    /// Includes some basic validation that returns an error if a device
211    /// attempts to use a service without also implementing the service's
212    /// corresponding `ChipsetDevice::supports_` method.
213    #[instrument(name = "add_device", skip_all, fields(device = self.dev_name.as_ref()))]
214    pub fn add<F>(mut self, f: F) -> Result<Arc<CloseableMutex<T>>, AddDeviceError>
215    where
216        F: FnOnce(&mut ArcMutexChipsetServices<'a, 'b>) -> T,
217    {
218        let dev = (f)(&mut self.services);
219        self.inner_add(Ok(dev))
220    }
221
222    /// Just like [`add`](Self::add), except async.
223    #[instrument(name = "add_device", skip_all, fields(device = self.dev_name.as_ref()))]
224    pub async fn add_async<F>(mut self, f: F) -> Result<Arc<CloseableMutex<T>>, AddDeviceError>
225    where
226        F: AsyncFnOnce(&mut ArcMutexChipsetServices<'a, 'b>) -> T,
227    {
228        let dev = (f)(&mut self.services).await;
229        self.inner_add(Ok(dev))
230    }
231
232    /// Just like [`add`](Self::add), except fallible.
233    #[instrument(name = "add_device", skip_all, fields(device = self.dev_name.as_ref()))]
234    pub fn try_add<F, E>(mut self, f: F) -> Result<Arc<CloseableMutex<T>>, AddDeviceError>
235    where
236        F: FnOnce(&mut ArcMutexChipsetServices<'a, 'b>) -> Result<T, E>,
237        E: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
238    {
239        let dev = match (f)(&mut self.services) {
240            Ok(dev) => dev,
241            Err(e) => {
242                return Err(AddDeviceErrorKind::DeviceError(e.into()).with_dev_name(self.dev_name));
243            }
244        };
245        self.inner_add(Ok(dev))
246    }
247
248    /// Just like [`try_add`](Self::try_add), except async.
249    #[instrument(name = "add_device", skip_all, fields(device = self.dev_name.as_ref()))]
250    pub async fn try_add_async<F, E>(
251        mut self,
252        f: F,
253    ) -> Result<Arc<CloseableMutex<T>>, AddDeviceError>
254    where
255        F: AsyncFnOnce(&mut ArcMutexChipsetServices<'a, 'b>) -> Result<T, E>,
256        E: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
257    {
258        let dev = match (f)(&mut self.services).await {
259            Ok(dev) => dev,
260            Err(e) => {
261                return Err(AddDeviceErrorKind::DeviceError(e.into()).with_dev_name(self.dev_name));
262            }
263        };
264        self.inner_add(Ok(dev))
265    }
266}