1use std::convert::Infallible;
9use std::fmt::Debug;
10use std::os::unix::io::FromRawFd;
11use std::path::PathBuf;
12use std::sync::{Arc, Mutex};
13
14use acpi::ACPIDeviceManager;
15use event_manager::{MutEventSubscriber, SubscriberOps};
16#[cfg(target_arch = "x86_64")]
17use legacy::{LegacyDeviceError, PortIODeviceManager};
18use linux_loader::loader::Cmdline;
19use log::{info, warn};
20use mmio::{MMIODeviceManager, MmioError};
21use pci_mngr::{PciDevices, PciDevicesConstructorArgs, PciManagerError};
22use persist::MMIODevManagerConstructorArgs;
23use serde::{Deserialize, Serialize};
24use utils::time::TimestampUs;
25use vmm_sys_util::eventfd::EventFd;
26
27use crate::device_manager::acpi::ACPIDeviceError;
28#[cfg(target_arch = "x86_64")]
29use crate::devices::legacy::I8042Device;
30#[cfg(target_arch = "aarch64")]
31use crate::devices::legacy::RTCDevice;
32use crate::devices::legacy::serial::SerialOut;
33use crate::devices::legacy::{IER_RDA_BIT, IER_RDA_OFFSET, SerialDevice};
34use crate::devices::pseudo::BootTimer;
35use crate::devices::virtio::device::VirtioDevice;
36use crate::devices::virtio::transport::mmio::{IrqTrigger, MmioTransport};
37use crate::resources::VmResources;
38use crate::snapshot::Persist;
39use crate::utils::open_file_write_nonblock;
40use crate::vstate::bus::BusError;
41use crate::vstate::memory::GuestMemoryMmap;
42use crate::{EmulateSerialInitError, EventManager, Vm};
43
44pub mod acpi;
46pub mod legacy;
48pub mod mmio;
50pub mod pci_mngr;
52pub mod persist;
54
55#[derive(Debug, thiserror::Error, displaydoc::Display)]
56pub enum DeviceManagerCreateError {
58 EventFd(#[from] std::io::Error),
60 #[cfg(target_arch = "x86_64")]
61 PortIOError(#[from] LegacyDeviceError),
63 ResourceAllocator(#[from] vm_allocator::Error),
65}
66
67#[derive(Debug, thiserror::Error, displaydoc::Display)]
68pub enum AttachDeviceError {
70 MmioTransport(#[from] MmioError),
72 Bus(#[from] BusError),
74 AttachAcpiDevice(#[from] ACPIDeviceError),
76 #[cfg(target_arch = "aarch64")]
77 Cmdline,
79 #[cfg(target_arch = "aarch64")]
80 CreateSerial(#[from] std::io::Error),
82 PciTransport(#[from] PciManagerError),
84}
85
86#[derive(Debug, thiserror::Error, displaydoc::Display)]
87pub enum FindDeviceError {
89 DeviceNotFound,
91}
92
93#[derive(Debug)]
94pub struct DeviceManager {
96 pub mmio_devices: MMIODeviceManager,
98 #[cfg(target_arch = "x86_64")]
99 pub legacy_devices: PortIODeviceManager,
101 pub acpi_devices: ACPIDeviceManager,
103 pub pci_devices: PciDevices,
105}
106
107impl DeviceManager {
108 fn open_stdout_nonblocking() -> Result<std::fs::File, std::io::Error> {
110 let fd = unsafe { libc::dup(libc::STDOUT_FILENO) };
112 if fd < 0 {
113 return Err(std::io::Error::last_os_error());
114 }
115 let flags = unsafe { libc::fcntl(fd, libc::F_GETFL, 0) };
117 if flags < 0 {
118 unsafe { libc::close(fd) };
120 return Err(std::io::Error::last_os_error());
121 }
122 let rc = unsafe { libc::fcntl(fd, libc::F_SETFL, flags | libc::O_NONBLOCK) };
124 if rc < 0 {
125 unsafe { libc::close(fd) };
127 return Err(std::io::Error::last_os_error());
128 }
129 Ok(unsafe { std::fs::File::from_raw_fd(fd) })
131 }
132
133 fn setup_serial_device(
135 event_manager: &mut EventManager,
136 output: Option<&PathBuf>,
137 ) -> Result<Arc<Mutex<SerialDevice>>, std::io::Error> {
138 let (serial_in, serial_out) = match output {
139 Some(path) => (None, open_file_write_nonblock(path).map(SerialOut::File)?),
140 None => {
141 let stdout_file = match Self::open_stdout_nonblocking() {
142 Ok(file) => SerialOut::File(file),
143 Err(err) => {
144 warn!("Falling back to blocking stdout for serial output: {}", err);
145 SerialOut::Stdout(std::io::stdout())
146 }
147 };
148 (Some(std::io::stdin()), stdout_file)
149 }
150 };
151
152 let serial = Arc::new(Mutex::new(SerialDevice::new(serial_in, serial_out)?));
153 event_manager.add_subscriber(serial.clone());
154 Ok(serial)
155 }
156
157 #[cfg(target_arch = "x86_64")]
158 fn create_legacy_devices(
159 event_manager: &mut EventManager,
160 vcpus_exit_evt: &EventFd,
161 vm: &Vm,
162 serial_output: Option<&PathBuf>,
163 ) -> Result<PortIODeviceManager, DeviceManagerCreateError> {
164 let serial = Self::setup_serial_device(event_manager, serial_output)?;
166 let reset_evt = vcpus_exit_evt
167 .try_clone()
168 .map_err(DeviceManagerCreateError::EventFd)?;
169 let i8042 = Arc::new(Mutex::new(I8042Device::new(reset_evt)?));
171
172 let mut legacy_devices = PortIODeviceManager::new(serial, i8042)?;
174 legacy_devices.register_devices(vm)?;
175 Ok(legacy_devices)
176 }
177
178 #[cfg_attr(target_arch = "aarch64", allow(unused))]
179 pub fn new(
180 event_manager: &mut EventManager,
181 vcpus_exit_evt: &EventFd,
182 vm: &Vm,
183 serial_output: Option<&PathBuf>,
184 ) -> Result<Self, DeviceManagerCreateError> {
185 #[cfg(target_arch = "x86_64")]
186 let legacy_devices =
187 Self::create_legacy_devices(event_manager, vcpus_exit_evt, vm, serial_output)?;
188
189 Ok(DeviceManager {
190 mmio_devices: MMIODeviceManager::new(),
191 #[cfg(target_arch = "x86_64")]
192 legacy_devices,
193 acpi_devices: ACPIDeviceManager::new(&mut vm.resource_allocator()),
194 pci_devices: PciDevices::new(),
195 })
196 }
197
198 pub(crate) fn attach_mmio_virtio_device<
200 T: 'static + VirtioDevice + MutEventSubscriber + Debug,
201 >(
202 &mut self,
203 vm: &Vm,
204 id: String,
205 device: Arc<Mutex<T>>,
206 cmdline: &mut Cmdline,
207 is_vhost_user: bool,
208 ) -> Result<(), AttachDeviceError> {
209 let interrupt = Arc::new(IrqTrigger::new());
210 let device =
212 MmioTransport::new(vm.guest_memory().clone(), interrupt, device, is_vhost_user);
213 self.mmio_devices
214 .register_mmio_virtio_for_boot(vm, id, device, cmdline)?;
215
216 Ok(())
217 }
218
219 pub(crate) fn attach_virtio_device<T: 'static + VirtioDevice + MutEventSubscriber + Debug>(
221 &mut self,
222 vm: &Arc<Vm>,
223 id: String,
224 device: Arc<Mutex<T>>,
225 cmdline: &mut Cmdline,
226 is_vhost_user: bool,
227 ) -> Result<(), AttachDeviceError> {
228 if self.pci_devices.pci_segment.is_some() {
229 self.pci_devices.attach_pci_virtio_device(vm, id, device)?;
230 } else {
231 self.attach_mmio_virtio_device(vm, id, device, cmdline, is_vhost_user)?;
232 }
233
234 Ok(())
235 }
236
237 pub(crate) fn attach_boot_timer_device(
239 &mut self,
240 vm: &Vm,
241 request_ts: TimestampUs,
242 ) -> Result<(), AttachDeviceError> {
243 let boot_timer = Arc::new(Mutex::new(BootTimer::new(request_ts)));
244
245 self.mmio_devices
246 .register_mmio_boot_timer(&vm.common.mmio_bus, boot_timer)?;
247
248 Ok(())
249 }
250
251 pub(crate) fn attach_vmgenid_device(&mut self, vm: &Vm) -> Result<(), AttachDeviceError> {
252 self.acpi_devices.attach_vmgenid(vm)?;
253 Ok(())
254 }
255
256 #[cfg(target_arch = "x86_64")]
257 pub(crate) fn attach_vmclock_device(&mut self, vm: &Vm) -> Result<(), AttachDeviceError> {
258 self.acpi_devices.attach_vmclock(vm)?;
259 Ok(())
260 }
261
262 #[cfg(target_arch = "aarch64")]
263 pub(crate) fn attach_legacy_devices_aarch64(
264 &mut self,
265 vm: &Vm,
266 event_manager: &mut EventManager,
267 cmdline: &mut Cmdline,
268 serial_out_path: Option<&PathBuf>,
269 ) -> Result<(), AttachDeviceError> {
270 let cmdline_contains_console = cmdline
272 .as_cstring()
273 .map_err(|_| AttachDeviceError::Cmdline)?
274 .into_string()
275 .map_err(|_| AttachDeviceError::Cmdline)?
276 .contains("console=");
277
278 if cmdline_contains_console {
279 let serial = Self::setup_serial_device(event_manager, serial_out_path)?;
280 self.mmio_devices.register_mmio_serial(vm, serial, None)?;
281 self.mmio_devices.add_mmio_serial_to_cmdline(cmdline)?;
282 }
283
284 let rtc = Arc::new(Mutex::new(RTCDevice::new()));
285 self.mmio_devices.register_mmio_rtc(vm, rtc, None)?;
286 Ok(())
287 }
288
289 pub fn enable_pci(&mut self, vm: &Arc<Vm>) -> Result<(), PciManagerError> {
291 self.pci_devices.attach_pci_segment(vm)
292 }
293
294 pub fn kick_virtio_devices(&self) {
296 info!("Artificially kick devices");
297 let _: Result<(), MmioError> = self.mmio_devices.for_each_virtio_device(|_, _, device| {
299 let mmio_transport_locked = device.inner.lock().expect("Poisoned lock");
300 mmio_transport_locked
301 .device()
302 .lock()
303 .expect("Poisoned lock")
304 .kick();
305 Ok(())
306 });
307 for virtio_pci_device in self.pci_devices.virtio_devices.values() {
309 virtio_pci_device
310 .lock()
311 .expect("Poisoned lock")
312 .virtio_device()
313 .lock()
314 .expect("Poisoned lock")
315 .kick();
316 }
317 }
318
319 fn do_mark_virtio_queue_memory_dirty(
320 device: Arc<Mutex<dyn VirtioDevice>>,
321 mem: &GuestMemoryMmap,
322 ) {
323 let mut locked_device = device.lock().expect("Poisoned lock");
327 if locked_device.is_activated() {
328 locked_device.mark_queue_memory_dirty(mem).unwrap()
329 }
330 }
331
332 pub fn mark_virtio_queue_memory_dirty(&self, mem: &GuestMemoryMmap) {
334 let _: Result<(), Infallible> = self.mmio_devices.for_each_virtio_device(|_, _, device| {
336 let mmio_transport_locked = device.inner.lock().expect("Poisoned locked");
337 Self::do_mark_virtio_queue_memory_dirty(mmio_transport_locked.device(), mem);
338 Ok(())
339 });
340
341 for device in self.pci_devices.virtio_devices.values() {
343 let virtio_device = device.lock().expect("Poisoned lock").virtio_device();
344 Self::do_mark_virtio_queue_memory_dirty(virtio_device, mem);
345 }
346 }
347
348 pub fn get_virtio_device(
350 &self,
351 virtio_type: u32,
352 device_id: &str,
353 ) -> Option<Arc<Mutex<dyn VirtioDevice>>> {
354 if self.pci_devices.pci_segment.is_some() {
355 let pci_device = self.pci_devices.get_virtio_device(virtio_type, device_id)?;
356 Some(
357 pci_device
358 .lock()
359 .expect("Poisoned lock")
360 .virtio_device()
361 .clone(),
362 )
363 } else {
364 let mmio_device = self
365 .mmio_devices
366 .get_virtio_device(virtio_type, device_id)?;
367 Some(
368 mmio_device
369 .inner
370 .lock()
371 .expect("Poisoned lock")
372 .device()
373 .clone(),
374 )
375 }
376 }
377
378 pub fn with_virtio_device<T, F, R>(&self, id: &str, f: F) -> Result<R, FindDeviceError>
380 where
381 T: VirtioDevice + 'static + Debug,
382 F: FnOnce(&mut T) -> R,
383 {
384 if let Some(device) = self.get_virtio_device(T::const_device_type(), id) {
385 let mut dev = device.lock().expect("Poisoned lock");
386 Ok(f(dev
387 .as_mut_any()
388 .downcast_mut::<T>()
389 .expect("Invalid device for a given device type")))
390 } else {
391 Err(FindDeviceError::DeviceNotFound)
392 }
393 }
394}
395
396#[derive(Debug, Default, Clone, Serialize, Deserialize)]
397pub struct DevicesState {
399 pub mmio_state: persist::DeviceStates,
401 pub acpi_state: persist::ACPIDeviceManagerState,
403 pub pci_state: pci_mngr::PciDevicesState,
405}
406
407#[derive(Debug, thiserror::Error, displaydoc::Display)]
408pub enum DevicePersistError {
409 MmioRestore(#[from] persist::DevicePersistError),
411 AcpiRestore(#[from] ACPIDeviceError),
413 PciRestore(#[from] PciManagerError),
415 VmGenidUpdate(#[from] std::io::Error),
417 SerialRestore(#[from] EmulateSerialInitError),
419 Bus(#[from] BusError),
421 DeviceManager(#[from] DeviceManagerCreateError),
423}
424
425pub struct DeviceRestoreArgs<'a> {
426 pub mem: &'a GuestMemoryMmap,
427 pub vm: &'a Arc<Vm>,
428 pub event_manager: &'a mut EventManager,
429 pub vcpus_exit_evt: &'a EventFd,
430 pub vm_resources: &'a mut VmResources,
431 pub instance_id: &'a str,
432}
433
434impl std::fmt::Debug for DeviceRestoreArgs<'_> {
435 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
436 f.debug_struct("DeviceRestoreArgs")
437 .field("mem", &self.mem)
438 .field("vm", &self.vm)
439 .field("vm_resources", &self.vm_resources)
440 .field("instance_id", &self.instance_id)
441 .finish()
442 }
443}
444
445impl<'a> Persist<'a> for DeviceManager {
446 type State = DevicesState;
447 type ConstructorArgs = DeviceRestoreArgs<'a>;
448 type Error = DevicePersistError;
449
450 fn save(&self) -> Self::State {
451 DevicesState {
452 mmio_state: self.mmio_devices.save(),
453 acpi_state: self.acpi_devices.save(),
454 pci_state: self.pci_devices.save(),
455 }
456 }
457
458 fn restore(
459 constructor_args: Self::ConstructorArgs,
460 state: &Self::State,
461 ) -> Result<Self, Self::Error> {
462 #[cfg(target_arch = "x86_64")]
464 let legacy_devices = Self::create_legacy_devices(
465 constructor_args.event_manager,
466 constructor_args.vcpus_exit_evt,
467 constructor_args.vm,
468 constructor_args.vm_resources.serial_out_path.as_ref(),
469 )?;
470
471 let mmio_ctor_args = MMIODevManagerConstructorArgs {
473 mem: constructor_args.mem,
474 vm: constructor_args.vm,
475 event_manager: constructor_args.event_manager,
476 vm_resources: constructor_args.vm_resources,
477 instance_id: constructor_args.instance_id,
478 };
479 let mmio_devices = MMIODeviceManager::restore(mmio_ctor_args, &state.mmio_state)?;
480
481 let mut acpi_devices = ACPIDeviceManager::restore(constructor_args.vm, &state.acpi_state)?;
483 acpi_devices.vmgenid.notify_guest()?;
484
485 let pci_ctor_args = PciDevicesConstructorArgs {
487 vm: constructor_args.vm,
488 mem: constructor_args.mem,
489 vm_resources: constructor_args.vm_resources,
490 instance_id: constructor_args.instance_id,
491 event_manager: constructor_args.event_manager,
492 };
493 let pci_devices = PciDevices::restore(pci_ctor_args, &state.pci_state)?;
494
495 let device_manager = DeviceManager {
496 mmio_devices,
497 #[cfg(target_arch = "x86_64")]
498 legacy_devices,
499 acpi_devices,
500 pci_devices,
501 };
502
503 device_manager.emulate_serial_init()?;
506
507 Ok(device_manager)
508 }
509}
510
511impl DeviceManager {
512 pub fn emulate_serial_init(&self) -> Result<(), EmulateSerialInitError> {
514 #[cfg(target_arch = "aarch64")]
522 {
523 if let Some(device) = &self.mmio_devices.serial {
524 let mut device_locked = device.inner.lock().expect("Poisoned lock");
525
526 device_locked
527 .serial
528 .write(IER_RDA_OFFSET, IER_RDA_BIT)
529 .map_err(|_| EmulateSerialInitError(std::io::Error::last_os_error()))?;
530 }
531 Ok(())
532 }
533
534 #[cfg(target_arch = "x86_64")]
535 {
536 let mut serial = self
537 .legacy_devices
538 .stdio_serial
539 .lock()
540 .expect("Poisoned lock");
541
542 serial
543 .serial
544 .write(IER_RDA_OFFSET, IER_RDA_BIT)
545 .map_err(|_| EmulateSerialInitError(std::io::Error::last_os_error()))?;
546 Ok(())
547 }
548 }
549}
550
551#[cfg(test)]
552pub(crate) mod tests {
553 use super::*;
554 #[cfg(target_arch = "aarch64")]
555 use crate::builder::tests::default_vmm;
556 use crate::vstate::resources::ResourceAllocator;
557
558 pub(crate) fn default_device_manager() -> DeviceManager {
559 let mut resource_allocator = ResourceAllocator::new();
560 let mmio_devices = MMIODeviceManager::new();
561 let acpi_devices = ACPIDeviceManager::new(&mut resource_allocator);
562 let pci_devices = PciDevices::new();
563
564 #[cfg(target_arch = "x86_64")]
565 let legacy_devices = PortIODeviceManager::new(
566 Arc::new(Mutex::new(
567 SerialDevice::new(None, SerialOut::Sink).unwrap(),
568 )),
569 Arc::new(Mutex::new(
570 I8042Device::new(EventFd::new(libc::EFD_NONBLOCK).unwrap()).unwrap(),
571 )),
572 )
573 .unwrap();
574
575 DeviceManager {
576 mmio_devices,
577 #[cfg(target_arch = "x86_64")]
578 legacy_devices,
579 acpi_devices,
580 pci_devices,
581 }
582 }
583
584 #[cfg(target_arch = "aarch64")]
585 #[test]
586 fn test_attach_legacy_serial() {
587 let mut vmm = default_vmm();
588 assert!(vmm.device_manager.mmio_devices.rtc.is_none());
589 assert!(vmm.device_manager.mmio_devices.serial.is_none());
590
591 let mut cmdline = Cmdline::new(4096).unwrap();
592 let mut event_manager = EventManager::new().unwrap();
593 vmm.device_manager
594 .attach_legacy_devices_aarch64(&vmm.vm, &mut event_manager, &mut cmdline, None)
595 .unwrap();
596 assert!(vmm.device_manager.mmio_devices.rtc.is_some());
597 assert!(vmm.device_manager.mmio_devices.serial.is_none());
598
599 let mut vmm = default_vmm();
600 cmdline.insert("console", "/dev/blah").unwrap();
601 vmm.device_manager
602 .attach_legacy_devices_aarch64(&vmm.vm, &mut event_manager, &mut cmdline, None)
603 .unwrap();
604 assert!(vmm.device_manager.mmio_devices.rtc.is_some());
605 assert!(vmm.device_manager.mmio_devices.serial.is_some());
606
607 assert!(
608 cmdline
609 .as_cstring()
610 .unwrap()
611 .into_string()
612 .unwrap()
613 .contains(&format!(
614 "earlycon=uart,mmio,0x{:08x}",
615 vmm.device_manager
616 .mmio_devices
617 .serial
618 .as_ref()
619 .unwrap()
620 .resources
621 .addr
622 ))
623 );
624 }
625}