vmotherboard/chipset/backing/arc_mutex/
device.rs1use super::services::ArcMutexChipsetServices;
8use crate::BusIdPci;
9use crate::BusIdPcieDownstreamPort;
10use crate::VmmChipsetDevice;
11use arc_cyclic_builder::ArcCyclicBuilder;
12use arc_cyclic_builder::ArcCyclicBuilderExt;
13use chipset_device::mmio::RegisterMmioIntercept;
14use chipset_device::pio::RegisterPortIoIntercept;
15use closeable_mutex::CloseableMutex;
16use std::sync::Arc;
17use std::sync::Weak;
18use thiserror::Error;
19use tracing::instrument;
20
21#[derive(Debug, Error)]
22pub(crate) enum AddDeviceErrorKind {
23 #[error("could not construct device")]
24 DeviceError(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
25
26 #[error("no pci bus address provided")]
27 NoPciBusAddress,
28 #[error("error finalizing device")]
29 Finalize(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
30}
31
32impl AddDeviceErrorKind {
33 pub(crate) fn with_dev_name(self, dev_name: Arc<str>) -> AddDeviceError {
34 AddDeviceError {
35 dev_name,
36 inner: self,
37 }
38 }
39}
40
41#[derive(Debug, Error)]
43#[error("could not initialize {dev_name}")]
44pub struct AddDeviceError {
45 dev_name: Arc<str>,
46 #[source]
47 inner: AddDeviceErrorKind,
48}
49
50pub trait ArcMutexChipsetServicesFinalize<T> {
57 type Error;
59
60 fn finalize(self, dev: &Arc<CloseableMutex<T>>, name: Arc<str>) -> Result<(), Self::Error>;
63}
64
65pub struct ArcMutexChipsetDeviceBuilder<'a, 'b, T> {
68 services: ArcMutexChipsetServices<'a, 'b>,
69 arc_builder: ArcCyclicBuilder<CloseableMutex<T>>,
70
71 dev_name: Arc<str>,
72
73 pci_addr: Option<(u8, u8, u8)>,
74 pci_bus_id: Option<BusIdPci>,
75 pcie_port: Option<BusIdPcieDownstreamPort>,
76 external_pci: bool,
77}
78
79impl<'a, 'b, T> ArcMutexChipsetDeviceBuilder<'a, 'b, T>
80where
81 T: VmmChipsetDevice,
82{
83 pub fn new(
85 name: Arc<str>,
86 new_device_services: impl FnOnce(
87 Weak<CloseableMutex<T>>,
88 Arc<str>,
89 ) -> ArcMutexChipsetServices<'a, 'b>,
90 ) -> Self {
91 let arc_builder: ArcCyclicBuilder<CloseableMutex<T>> = Arc::new_cyclic_builder();
92 let services = (new_device_services)(arc_builder.weak(), name.clone());
93
94 ArcMutexChipsetDeviceBuilder {
95 services,
96 arc_builder,
97
98 dev_name: name,
99
100 pci_addr: None,
101 pci_bus_id: None,
102 pcie_port: None,
103 external_pci: false,
104 }
105 }
106
107 pub fn omit_saved_state(mut self) -> Self {
110 self.services.omit_saved_state();
111 self
112 }
113
114 pub fn with_pci_addr(mut self, bus: u8, device: u8, function: u8) -> Self {
116 self.pci_addr = Some((bus, device, function));
117 self
118 }
119
120 pub fn on_pci_bus(mut self, id: BusIdPci) -> Self {
122 self.pci_bus_id = Some(id);
123 self
124 }
125
126 pub fn on_pcie_port(mut self, id: BusIdPcieDownstreamPort) -> Self {
128 self.pcie_port = Some(id);
129 self
130 }
131
132 pub fn with_external_pci(mut self) -> Self {
136 self.external_pci = true;
137 self
138 }
139
140 fn inner_add(
141 mut self,
142 typed_dev: Result<T, AddDeviceError>,
143 ) -> Result<Arc<CloseableMutex<T>>, AddDeviceError> {
144 let mut typed_dev = typed_dev?;
145
146 if let Some(dev) = typed_dev.supports_mmio() {
147 for (label, range) in dev.get_static_regions() {
149 self.services
150 .register_mmio()
151 .new_io_region(label, range.end() - range.start() + 1)
152 .map(*range.start());
153 }
154 }
155
156 if let Some(dev) = typed_dev.supports_pio() {
157 for (label, range) in dev.get_static_regions() {
159 self.services
160 .register_pio()
161 .new_io_region(label, range.end() - range.start() + 1)
162 .map(*range.start());
163 }
164 }
165
166 if !self.external_pci {
167 if let Some(dev) = typed_dev.supports_pci() {
168 if self.pci_bus_id.is_some() && self.pcie_port.is_some() {
169 panic!(
170 "wiring error: invoked both `on_pci_bus` and `on_pcie_port` for `{}`",
171 self.dev_name
172 );
173 }
174
175 if let Some(bus_id_port) = self.pcie_port {
176 self.services.register_static_pcie(bus_id_port);
177 } else {
178 let bdf = match (self.pci_addr, dev.suggested_bdf()) {
180 (Some(override_bdf), Some(suggested_bdf)) => {
181 let (ob, od, of) = override_bdf;
182 let (sb, sd, sf) = suggested_bdf;
183 tracing::info!(
184 "overriding suggested bdf: using {:02x}:{:02x}:{} instead of {:02x}:{:02x}:{}",
185 ob,
186 od,
187 of,
188 sb,
189 sd,
190 sf
191 );
192 override_bdf
193 }
194 (None, Some(bdf)) | (Some(bdf), None) => bdf,
195 (None, None) => {
196 return Err(
197 AddDeviceErrorKind::NoPciBusAddress.with_dev_name(self.dev_name)
198 );
199 }
200 };
201
202 let bus_id = match self.pci_bus_id.take() {
203 Some(bus_id) => bus_id,
204 None => panic!(
205 "wiring error: did not invoke `on_pci_bus` for `{}`",
206 self.dev_name
207 ),
208 };
209
210 self.services.register_static_pci(bus_id, bdf);
211 }
212 }
213 }
214
215 let dev = self.arc_builder.build(CloseableMutex::new(typed_dev));
216
217 self.services
219 .finalize(&dev, self.dev_name.clone())
220 .map_err(|err| AddDeviceErrorKind::Finalize(err.into()).with_dev_name(self.dev_name))?;
221
222 Ok(dev)
223 }
224
225 #[instrument(name = "add_device", skip_all, fields(device = self.dev_name.as_ref()))]
234 #[expect(clippy::should_implement_trait)]
235 pub fn add<F>(mut self, f: F) -> Result<Arc<CloseableMutex<T>>, AddDeviceError>
236 where
237 F: FnOnce(&mut ArcMutexChipsetServices<'a, 'b>) -> T,
238 {
239 let dev = (f)(&mut self.services);
240 self.inner_add(Ok(dev))
241 }
242
243 #[instrument(name = "add_device", skip_all, fields(device = self.dev_name.as_ref()))]
245 pub async fn add_async<F>(mut self, f: F) -> Result<Arc<CloseableMutex<T>>, AddDeviceError>
246 where
247 F: AsyncFnOnce(&mut ArcMutexChipsetServices<'a, 'b>) -> T,
248 {
249 let dev = (f)(&mut self.services).await;
250 self.inner_add(Ok(dev))
251 }
252
253 #[instrument(name = "add_device", skip_all, fields(device = self.dev_name.as_ref()))]
255 pub fn try_add<F, E>(mut self, f: F) -> Result<Arc<CloseableMutex<T>>, AddDeviceError>
256 where
257 F: FnOnce(&mut ArcMutexChipsetServices<'a, 'b>) -> Result<T, E>,
258 E: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
259 {
260 let dev = match (f)(&mut self.services) {
261 Ok(dev) => dev,
262 Err(e) => {
263 return Err(AddDeviceErrorKind::DeviceError(e.into()).with_dev_name(self.dev_name));
264 }
265 };
266 self.inner_add(Ok(dev))
267 }
268
269 #[instrument(name = "add_device", skip_all, fields(device = self.dev_name.as_ref()))]
271 pub async fn try_add_async<F, E>(
272 mut self,
273 f: F,
274 ) -> Result<Arc<CloseableMutex<T>>, AddDeviceError>
275 where
276 F: AsyncFnOnce(&mut ArcMutexChipsetServices<'a, 'b>) -> Result<T, E>,
277 E: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
278 {
279 let dev = match (f)(&mut self.services).await {
280 Ok(dev) => dev,
281 Err(e) => {
282 return Err(AddDeviceErrorKind::DeviceError(e.into()).with_dev_name(self.dev_name));
283 }
284 };
285 self.inner_add(Ok(dev))
286 }
287}