vmotherboard/chipset/backing/arc_mutex/
device.rs1use super::services::ArcMutexChipsetServices;
8use crate::BusIdPci;
9use crate::BusIdPcieDownstreamPort;
10use crate::BusIdPcieEnumerator;
11use crate::VmmChipsetDevice;
12use arc_cyclic_builder::ArcCyclicBuilder;
13use arc_cyclic_builder::ArcCyclicBuilderExt;
14use chipset_device::mmio::RegisterMmioIntercept;
15use chipset_device::pio::RegisterPortIoIntercept;
16use closeable_mutex::CloseableMutex;
17use std::sync::Arc;
18use std::sync::Weak;
19use thiserror::Error;
20use tracing::instrument;
21
22#[derive(Debug, Error)]
23pub(crate) enum AddDeviceErrorKind {
24 #[error("could not construct device")]
25 DeviceError(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
26
27 #[error("no pci bus address provided")]
28 NoPciBusAddress,
29 #[error("no pci bus specified; call on_pci_bus(...) when adding this PCI device")]
30 NoPciBusSpecified,
31 #[error("error finalizing device")]
32 Finalize(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
33}
34
35impl AddDeviceErrorKind {
36 pub(crate) fn with_dev_name(self, dev_name: Arc<str>) -> AddDeviceError {
37 AddDeviceError {
38 dev_name,
39 inner: self,
40 }
41 }
42}
43
44#[derive(Debug, Error)]
46#[error("could not initialize {dev_name}")]
47pub struct AddDeviceError {
48 dev_name: Arc<str>,
49 #[source]
50 inner: AddDeviceErrorKind,
51}
52
53pub trait ArcMutexChipsetServicesFinalize<T> {
60 type Error;
62
63 fn finalize(self, dev: &Arc<CloseableMutex<T>>, name: Arc<str>) -> Result<(), Self::Error>;
66}
67
68pub struct ArcMutexChipsetDeviceBuilder<'a, 'b, T> {
71 services: ArcMutexChipsetServices<'a, 'b>,
72 arc_builder: ArcCyclicBuilder<CloseableMutex<T>>,
73
74 dev_name: Arc<str>,
75
76 pci_addr: Option<(u8, u8, u8)>,
77 pci_bus_id: Option<BusIdPci>,
78 pcie_port: Option<BusIdPcieDownstreamPort>,
79 pcie_rciep: Option<(BusIdPcieEnumerator, u8)>,
80 external_pci: bool,
81}
82
83impl<'a, 'b, T> ArcMutexChipsetDeviceBuilder<'a, 'b, T>
84where
85 T: VmmChipsetDevice,
86{
87 pub fn new(
89 name: Arc<str>,
90 new_device_services: impl FnOnce(
91 Weak<CloseableMutex<T>>,
92 Arc<str>,
93 ) -> ArcMutexChipsetServices<'a, 'b>,
94 ) -> Self {
95 let arc_builder: ArcCyclicBuilder<CloseableMutex<T>> = Arc::new_cyclic_builder();
96 let services = (new_device_services)(arc_builder.weak(), name.clone());
97
98 ArcMutexChipsetDeviceBuilder {
99 services,
100 arc_builder,
101
102 dev_name: name,
103
104 pci_addr: None,
105 pci_bus_id: None,
106 pcie_port: None,
107 pcie_rciep: None,
108 external_pci: false,
109 }
110 }
111
112 pub fn omit_saved_state(mut self) -> Self {
115 self.services.omit_saved_state();
116 self
117 }
118
119 pub fn with_pci_addr(mut self, bus: u8, device: u8, function: u8) -> Self {
121 self.pci_addr = Some((bus, device, function));
122 self
123 }
124
125 pub fn on_pci_bus(mut self, id: BusIdPci) -> Self {
127 self.pci_bus_id = Some(id);
128 self
129 }
130
131 pub fn on_pcie_port(mut self, id: BusIdPcieDownstreamPort) -> Self {
133 self.pcie_port = Some(id);
134 self
135 }
136
137 pub fn on_pcie_root_complex(mut self, enumerator_id: BusIdPcieEnumerator, devfn: u8) -> Self {
144 self.pcie_rciep = Some((enumerator_id, devfn));
145 self
146 }
147
148 pub fn with_external_pci(mut self) -> Self {
152 self.external_pci = true;
153 self
154 }
155
156 fn inner_add(
157 mut self,
158 typed_dev: Result<T, AddDeviceError>,
159 ) -> Result<Arc<CloseableMutex<T>>, AddDeviceError> {
160 let mut typed_dev = typed_dev?;
161
162 if let Some(dev) = typed_dev.supports_mmio() {
163 for (label, range) in dev.get_static_regions() {
165 self.services
166 .register_mmio()
167 .new_io_region(label, range.end() - range.start() + 1)
168 .map(*range.start());
169 }
170 }
171
172 if let Some(dev) = typed_dev.supports_pio() {
173 for (label, range) in dev.get_static_regions() {
175 self.services
176 .register_pio()
177 .new_io_region(label, range.end() - range.start() + 1)
178 .map(*range.start());
179 }
180 }
181
182 if !self.external_pci {
183 if let Some(dev) = typed_dev.supports_pci() {
184 let bus_options = [
185 self.pci_bus_id.is_some(),
186 self.pcie_port.is_some(),
187 self.pcie_rciep.is_some(),
188 ];
189 if bus_options.iter().filter(|&&v| v).count() > 1 {
190 panic!(
191 "wiring error: invoked multiple bus placement methods for `{}`",
192 self.dev_name
193 );
194 }
195
196 if let Some(bus_id_port) = self.pcie_port {
197 self.services.register_static_pcie(bus_id_port);
198 } else if let Some((enumerator_id, devfn)) = self.pcie_rciep {
199 self.services
200 .register_static_pcie_rciep(enumerator_id, devfn);
201 } else {
202 let bdf = match (self.pci_addr, dev.suggested_bdf()) {
204 (Some(override_bdf), Some(suggested_bdf)) => {
205 let (ob, od, of) = override_bdf;
206 let (sb, sd, sf) = suggested_bdf;
207 tracing::info!(
208 "overriding suggested bdf: using {:02x}:{:02x}:{} instead of {:02x}:{:02x}:{}",
209 ob,
210 od,
211 of,
212 sb,
213 sd,
214 sf
215 );
216 override_bdf
217 }
218 (Some(override_bdf), None) => override_bdf,
219 (None, Some(suggested_bdf)) => suggested_bdf,
220 (None, None) => {
221 return Err(
222 AddDeviceErrorKind::NoPciBusAddress.with_dev_name(self.dev_name)
223 );
224 }
225 };
226
227 let bus_id = if let Some(bus_id) = self.pci_bus_id.take() {
228 bus_id
229 } else {
230 return Err(
231 AddDeviceErrorKind::NoPciBusSpecified.with_dev_name(self.dev_name)
232 );
233 };
234
235 self.services.register_static_pci(bus_id, bdf);
236 }
237 }
238 }
239
240 let dev = self.arc_builder.build(CloseableMutex::new(typed_dev));
241
242 self.services
244 .finalize(&dev, self.dev_name.clone())
245 .map_err(|err| AddDeviceErrorKind::Finalize(err.into()).with_dev_name(self.dev_name))?;
246
247 Ok(dev)
248 }
249
250 #[instrument(name = "add_device", skip_all, fields(device = self.dev_name.as_ref()))]
259 #[expect(clippy::should_implement_trait)]
260 pub fn add<F>(mut self, f: F) -> Result<Arc<CloseableMutex<T>>, AddDeviceError>
261 where
262 F: FnOnce(&mut ArcMutexChipsetServices<'a, 'b>) -> T,
263 {
264 let dev = (f)(&mut self.services);
265 self.inner_add(Ok(dev))
266 }
267
268 #[instrument(name = "add_device", skip_all, fields(device = self.dev_name.as_ref()))]
270 pub async fn add_async<F>(mut self, f: F) -> Result<Arc<CloseableMutex<T>>, AddDeviceError>
271 where
272 F: AsyncFnOnce(&mut ArcMutexChipsetServices<'a, 'b>) -> T,
273 {
274 let dev = (f)(&mut self.services).await;
275 self.inner_add(Ok(dev))
276 }
277
278 #[instrument(name = "add_device", skip_all, fields(device = self.dev_name.as_ref()))]
280 pub fn try_add<F, E>(mut self, f: F) -> Result<Arc<CloseableMutex<T>>, AddDeviceError>
281 where
282 F: FnOnce(&mut ArcMutexChipsetServices<'a, 'b>) -> Result<T, E>,
283 E: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
284 {
285 let dev = match (f)(&mut self.services) {
286 Ok(dev) => dev,
287 Err(e) => {
288 return Err(AddDeviceErrorKind::DeviceError(e.into()).with_dev_name(self.dev_name));
289 }
290 };
291 self.inner_add(Ok(dev))
292 }
293
294 #[instrument(name = "add_device", skip_all, fields(device = self.dev_name.as_ref()))]
296 pub async fn try_add_async<F, E>(
297 mut self,
298 f: F,
299 ) -> Result<Arc<CloseableMutex<T>>, AddDeviceError>
300 where
301 F: AsyncFnOnce(&mut ArcMutexChipsetServices<'a, 'b>) -> Result<T, E>,
302 E: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
303 {
304 let dev = match (f)(&mut self.services).await {
305 Ok(dev) => dev,
306 Err(e) => {
307 return Err(AddDeviceErrorKind::DeviceError(e.into()).with_dev_name(self.dev_name));
308 }
309 };
310 self.inner_add(Ok(dev))
311 }
312}