vmm/device_manager/
pci_mngr.rs

1// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::collections::HashMap;
5use std::fmt::Debug;
6use std::ops::DerefMut;
7use std::sync::{Arc, Mutex};
8
9use event_manager::{MutEventSubscriber, SubscriberOps};
10use log::{debug, error, warn};
11use serde::{Deserialize, Serialize};
12
13use super::persist::MmdsState;
14use crate::devices::pci::PciSegment;
15use crate::devices::virtio::balloon::Balloon;
16use crate::devices::virtio::balloon::persist::{BalloonConstructorArgs, BalloonState};
17use crate::devices::virtio::block::device::Block;
18use crate::devices::virtio::block::persist::{BlockConstructorArgs, BlockState};
19use crate::devices::virtio::device::VirtioDevice;
20use crate::devices::virtio::generated::virtio_ids;
21use crate::devices::virtio::mem::VirtioMem;
22use crate::devices::virtio::mem::persist::{VirtioMemConstructorArgs, VirtioMemState};
23use crate::devices::virtio::net::Net;
24use crate::devices::virtio::net::persist::{NetConstructorArgs, NetState};
25use crate::devices::virtio::pmem::device::Pmem;
26use crate::devices::virtio::pmem::persist::{PmemConstructorArgs, PmemState};
27use crate::devices::virtio::rng::Entropy;
28use crate::devices::virtio::rng::persist::{EntropyConstructorArgs, EntropyState};
29use crate::devices::virtio::transport::pci::device::{
30    CAPABILITY_BAR_SIZE, VirtioPciDevice, VirtioPciDeviceError, VirtioPciDeviceState,
31};
32use crate::devices::virtio::vsock::persist::{
33    VsockConstructorArgs, VsockState, VsockUdsConstructorArgs,
34};
35use crate::devices::virtio::vsock::{Vsock, VsockUnixBackend};
36use crate::pci::bus::PciRootError;
37use crate::resources::VmResources;
38use crate::snapshot::Persist;
39use crate::vmm_config::memory_hotplug::MemoryHotplugConfig;
40use crate::vmm_config::mmds::MmdsConfigError;
41use crate::vstate::bus::BusError;
42use crate::vstate::interrupts::InterruptError;
43use crate::vstate::memory::GuestMemoryMmap;
44use crate::{EventManager, Vm};
45
46#[derive(Debug, Default)]
47pub struct PciDevices {
48    /// PCIe segment of the VMM, if PCI is enabled. We currently support a single PCIe segment.
49    pub pci_segment: Option<PciSegment>,
50    /// All VirtIO PCI devices of the system
51    pub virtio_devices: HashMap<(u32, String), Arc<Mutex<VirtioPciDevice>>>,
52}
53
54#[derive(Debug, thiserror::Error, displaydoc::Display)]
55pub enum PciManagerError {
56    /// Resource allocation error: {0}
57    ResourceAllocation(#[from] vm_allocator::Error),
58    /// Bus error: {0}
59    Bus(#[from] BusError),
60    /// PCI root error: {0}
61    PciRoot(#[from] PciRootError),
62    /// MSI error: {0}
63    Msi(#[from] InterruptError),
64    /// VirtIO PCI device error: {0}
65    VirtioPciDevice(#[from] VirtioPciDeviceError),
66    /// KVM error: {0}
67    Kvm(#[from] vmm_sys_util::errno::Error),
68    /// MMDS error: {0}
69    Mmds(#[from] MmdsConfigError),
70}
71
72impl PciDevices {
73    pub fn new() -> Self {
74        Default::default()
75    }
76
77    pub fn attach_pci_segment(&mut self, vm: &Arc<Vm>) -> Result<(), PciManagerError> {
78        // We only support a single PCIe segment. Calling this function twice is a Firecracker
79        // internal error.
80        assert!(self.pci_segment.is_none());
81
82        // Currently we don't assign any IRQs to PCI devices. We will be using MSI-X interrupts
83        // only.
84        let pci_segment = PciSegment::new(0, vm, &[0u8; 32])?;
85        self.pci_segment = Some(pci_segment);
86
87        Ok(())
88    }
89
90    fn register_bars_with_bus(
91        vm: &Vm,
92        virtio_device: &Arc<Mutex<VirtioPciDevice>>,
93    ) -> Result<(), PciManagerError> {
94        let virtio_device_locked = virtio_device.lock().expect("Poisoned lock");
95
96        debug!(
97            "Inserting MMIO BAR region: {:#x}:{:#x}",
98            virtio_device_locked.bar_address, CAPABILITY_BAR_SIZE
99        );
100        vm.common.mmio_bus.insert(
101            virtio_device.clone(),
102            virtio_device_locked.bar_address,
103            CAPABILITY_BAR_SIZE,
104        )?;
105
106        Ok(())
107    }
108
109    pub(crate) fn attach_pci_virtio_device<
110        T: 'static + VirtioDevice + MutEventSubscriber + Debug,
111    >(
112        &mut self,
113        vm: &Arc<Vm>,
114        id: String,
115        device: Arc<Mutex<T>>,
116    ) -> Result<(), PciManagerError> {
117        // We should only be reaching this point if PCI is enabled
118        let pci_segment = self.pci_segment.as_ref().unwrap();
119        let pci_device_bdf = pci_segment.next_device_bdf()?;
120        debug!("Allocating BDF: {pci_device_bdf:?} for device");
121        let mem = vm.guest_memory().clone();
122
123        let device_type: u32 = device.lock().expect("Poisoned lock").device_type();
124
125        // Allocate one MSI vector per queue, plus one for configuration
126        let msix_num =
127            u16::try_from(device.lock().expect("Poisoned lock").queues().len() + 1).unwrap();
128
129        let msix_vectors = Vm::create_msix_group(vm.clone(), msix_num)?;
130
131        // Create the transport
132        let mut virtio_device = VirtioPciDevice::new(
133            id.clone(),
134            mem,
135            device,
136            Arc::new(msix_vectors),
137            pci_device_bdf.into(),
138        )?;
139
140        // Allocate bars
141        let mut resource_allocator_lock = vm.resource_allocator();
142        let resource_allocator = resource_allocator_lock.deref_mut();
143
144        virtio_device.allocate_bars(&mut resource_allocator.mmio64_memory);
145
146        let virtio_device = Arc::new(Mutex::new(virtio_device));
147        pci_segment
148            .pci_bus
149            .lock()
150            .expect("Poisoned lock")
151            .add_device(pci_device_bdf.device() as u32, virtio_device.clone());
152
153        self.virtio_devices
154            .insert((device_type, id.clone()), virtio_device.clone());
155
156        Self::register_bars_with_bus(vm, &virtio_device)?;
157        virtio_device
158            .lock()
159            .expect("Poisoned lock")
160            .register_notification_ioevent(vm)?;
161
162        Ok(())
163    }
164
165    fn restore_pci_device<T: 'static + VirtioDevice + MutEventSubscriber + Debug>(
166        &mut self,
167        vm: &Arc<Vm>,
168        device: Arc<Mutex<T>>,
169        device_id: &str,
170        transport_state: &VirtioPciDeviceState,
171        event_manager: &mut EventManager,
172    ) -> Result<(), PciManagerError> {
173        // We should only be reaching this point if PCI is enabled
174        let pci_segment = self.pci_segment.as_ref().unwrap();
175        let device_type: u32 = device.lock().expect("Poisoned lock").device_type();
176
177        let virtio_device = Arc::new(Mutex::new(VirtioPciDevice::new_from_state(
178            device_id.to_string(),
179            vm,
180            device.clone(),
181            transport_state.clone(),
182        )?));
183
184        pci_segment
185            .pci_bus
186            .lock()
187            .expect("Poisoned lock")
188            .add_device(
189                transport_state.pci_device_bdf.device() as u32,
190                virtio_device.clone(),
191            );
192
193        self.virtio_devices
194            .insert((device_type, device_id.to_string()), virtio_device.clone());
195
196        Self::register_bars_with_bus(vm, &virtio_device)?;
197        virtio_device
198            .lock()
199            .expect("Poisoned lock")
200            .register_notification_ioevent(vm)?;
201
202        event_manager.add_subscriber(device);
203
204        Ok(())
205    }
206
207    /// Gets the specified device.
208    pub fn get_virtio_device(
209        &self,
210        device_type: u32,
211        device_id: &str,
212    ) -> Option<&Arc<Mutex<VirtioPciDevice>>> {
213        self.virtio_devices
214            .get(&(device_type, device_id.to_string()))
215    }
216}
217
218#[derive(Debug, Clone, Serialize, Deserialize)]
219pub struct VirtioDeviceState<T> {
220    /// Device identifier
221    pub device_id: String,
222    /// Device BDF
223    pub pci_device_bdf: u32,
224    /// Device state
225    pub device_state: T,
226    /// Transport state
227    pub transport_state: VirtioPciDeviceState,
228}
229
230#[derive(Default, Debug, Clone, Serialize, Deserialize)]
231pub struct PciDevicesState {
232    /// Whether PCI is enabled
233    pub pci_enabled: bool,
234    /// Block device states.
235    pub block_devices: Vec<VirtioDeviceState<BlockState>>,
236    /// Net device states.
237    pub net_devices: Vec<VirtioDeviceState<NetState>>,
238    /// Vsock device state.
239    pub vsock_device: Option<VirtioDeviceState<VsockState>>,
240    /// Balloon device state.
241    pub balloon_device: Option<VirtioDeviceState<BalloonState>>,
242    /// Mmds state.
243    pub mmds: Option<MmdsState>,
244    /// Entropy device state.
245    pub entropy_device: Option<VirtioDeviceState<EntropyState>>,
246    /// Pmem device states.
247    pub pmem_devices: Vec<VirtioDeviceState<PmemState>>,
248    /// Memory device state.
249    pub memory_device: Option<VirtioDeviceState<VirtioMemState>>,
250}
251
252pub struct PciDevicesConstructorArgs<'a> {
253    pub vm: &'a Arc<Vm>,
254    pub mem: &'a GuestMemoryMmap,
255    pub vm_resources: &'a mut VmResources,
256    pub instance_id: &'a str,
257    pub event_manager: &'a mut EventManager,
258}
259
260impl<'a> Debug for PciDevicesConstructorArgs<'a> {
261    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
262        f.debug_struct("PciDevicesConstructorArgs")
263            .field("vm", &self.vm)
264            .field("mem", &self.mem)
265            .field("vm_resources", &self.vm_resources)
266            .field("instance_id", &self.instance_id)
267            .finish()
268    }
269}
270
271impl<'a> Persist<'a> for PciDevices {
272    type State = PciDevicesState;
273    type ConstructorArgs = PciDevicesConstructorArgs<'a>;
274    type Error = PciManagerError;
275
276    fn save(&self) -> Self::State {
277        let mut state = PciDevicesState::default();
278        if self.pci_segment.is_some() {
279            state.pci_enabled = true;
280        } else {
281            return state;
282        }
283
284        for pci_dev in self.virtio_devices.values() {
285            let locked_pci_dev = pci_dev.lock().expect("Poisoned lock");
286            let transport_state = locked_pci_dev.state();
287            let virtio_dev = locked_pci_dev.virtio_device();
288            let mut locked_virtio_dev = virtio_dev.lock().expect("Poisoned lock");
289
290            let pci_device_bdf = transport_state.pci_device_bdf.into();
291
292            match locked_virtio_dev.device_type() {
293                virtio_ids::VIRTIO_ID_BALLOON => {
294                    let balloon_device = locked_virtio_dev
295                        .as_any()
296                        .downcast_ref::<Balloon>()
297                        .unwrap();
298
299                    let device_state = balloon_device.save();
300
301                    state.balloon_device = Some(VirtioDeviceState {
302                        device_id: balloon_device.id().to_string(),
303                        pci_device_bdf,
304                        device_state,
305                        transport_state,
306                    });
307                }
308                virtio_ids::VIRTIO_ID_BLOCK => {
309                    let block_dev = locked_virtio_dev
310                        .as_mut_any()
311                        .downcast_mut::<Block>()
312                        .unwrap();
313                    if block_dev.is_vhost_user() {
314                        warn!(
315                            "Skipping vhost-user-block device. VhostUserBlock does not support \
316                             snapshotting yet"
317                        );
318                    } else {
319                        block_dev.prepare_save();
320                        let device_state = block_dev.save();
321                        state.block_devices.push(VirtioDeviceState {
322                            device_id: block_dev.id().to_string(),
323                            pci_device_bdf,
324                            device_state,
325                            transport_state,
326                        });
327                    }
328                }
329                virtio_ids::VIRTIO_ID_NET => {
330                    let net_dev = locked_virtio_dev
331                        .as_mut_any()
332                        .downcast_mut::<Net>()
333                        .unwrap();
334                    if let (Some(mmds_ns), None) = (net_dev.mmds_ns.as_ref(), state.mmds.as_ref()) {
335                        let mmds_guard = mmds_ns.mmds.lock().expect("Poisoned lock");
336                        state.mmds = Some(MmdsState {
337                            version: mmds_guard.version(),
338                            imds_compat: mmds_guard.imds_compat(),
339                        });
340                    }
341                    net_dev.prepare_save();
342                    let device_state = net_dev.save();
343
344                    state.net_devices.push(VirtioDeviceState {
345                        device_id: net_dev.id().to_string(),
346                        pci_device_bdf,
347                        device_state,
348                        transport_state,
349                    })
350                }
351                virtio_ids::VIRTIO_ID_VSOCK => {
352                    let vsock_dev = locked_virtio_dev
353                        .as_mut_any()
354                        // Currently, VsockUnixBackend is the only implementation of VsockBackend.
355                        .downcast_mut::<Vsock<VsockUnixBackend>>()
356                        .unwrap();
357
358                    // Send Transport event to reset connections if device
359                    // is activated.
360                    if vsock_dev.is_activated() {
361                        vsock_dev
362                            .send_transport_reset_event()
363                            .unwrap_or_else(|err| {
364                                error!("Failed to send reset transport event: {:?}", err);
365                            });
366                    }
367
368                    // Save state after potential notification to the guest. This
369                    // way we save changes to the queue the notification can cause.
370                    let vsock_state = VsockState {
371                        backend: vsock_dev.backend().save(),
372                        frontend: vsock_dev.save(),
373                    };
374
375                    state.vsock_device = Some(VirtioDeviceState {
376                        device_id: vsock_dev.id().to_string(),
377                        pci_device_bdf,
378                        device_state: vsock_state,
379                        transport_state,
380                    });
381                }
382                virtio_ids::VIRTIO_ID_RNG => {
383                    let rng_dev = locked_virtio_dev
384                        .as_mut_any()
385                        .downcast_mut::<Entropy>()
386                        .unwrap();
387                    let device_state = rng_dev.save();
388
389                    state.entropy_device = Some(VirtioDeviceState {
390                        device_id: rng_dev.id().to_string(),
391                        pci_device_bdf,
392                        device_state,
393                        transport_state,
394                    })
395                }
396                virtio_ids::VIRTIO_ID_PMEM => {
397                    let pmem_dev = locked_virtio_dev
398                        .as_mut_any()
399                        .downcast_mut::<Pmem>()
400                        .unwrap();
401                    let device_state = pmem_dev.save();
402                    state.pmem_devices.push(VirtioDeviceState {
403                        device_id: pmem_dev.config.id.clone(),
404                        pci_device_bdf,
405                        device_state,
406                        transport_state,
407                    });
408                }
409                virtio_ids::VIRTIO_ID_MEM => {
410                    let mem_dev = locked_virtio_dev
411                        .as_mut_any()
412                        .downcast_mut::<VirtioMem>()
413                        .unwrap();
414                    let device_state = mem_dev.save();
415
416                    state.memory_device = Some(VirtioDeviceState {
417                        device_id: mem_dev.id().to_string(),
418                        pci_device_bdf,
419                        device_state,
420                        transport_state,
421                    })
422                }
423                _ => unreachable!(),
424            }
425        }
426
427        state
428    }
429
430    fn restore(
431        constructor_args: Self::ConstructorArgs,
432        state: &Self::State,
433    ) -> Result<Self, Self::Error> {
434        let mem = constructor_args.mem;
435        let mut pci_devices = PciDevices::new();
436        if !state.pci_enabled {
437            return Ok(pci_devices);
438        }
439
440        pci_devices.attach_pci_segment(constructor_args.vm)?;
441
442        if let Some(balloon_state) = &state.balloon_device {
443            let device = Arc::new(Mutex::new(
444                Balloon::restore(
445                    BalloonConstructorArgs { mem: mem.clone() },
446                    &balloon_state.device_state,
447                )
448                .unwrap(),
449            ));
450
451            constructor_args
452                .vm_resources
453                .balloon
454                .set_device(device.clone());
455
456            pci_devices
457                .restore_pci_device(
458                    constructor_args.vm,
459                    device,
460                    &balloon_state.device_id,
461                    &balloon_state.transport_state,
462                    constructor_args.event_manager,
463                )
464                .unwrap()
465        }
466
467        for block_state in &state.block_devices {
468            let device = Arc::new(Mutex::new(
469                Block::restore(
470                    BlockConstructorArgs { mem: mem.clone() },
471                    &block_state.device_state,
472                )
473                .unwrap(),
474            ));
475
476            constructor_args
477                .vm_resources
478                .block
479                .add_virtio_device(device.clone());
480
481            pci_devices
482                .restore_pci_device(
483                    constructor_args.vm,
484                    device,
485                    &block_state.device_id,
486                    &block_state.transport_state,
487                    constructor_args.event_manager,
488                )
489                .unwrap()
490        }
491
492        // Initialize MMDS if MMDS state is included.
493        if let Some(mmds) = &state.mmds {
494            constructor_args
495                .vm_resources
496                .set_mmds_basic_config(mmds.version, mmds.imds_compat, constructor_args.instance_id)
497                .unwrap();
498        } else if state
499            .net_devices
500            .iter()
501            .any(|dev| dev.device_state.mmds_ns.is_some())
502        {
503            // If there's at least one network device having an mmds_ns, it means
504            // that we are restoring from a version that did not persist the `MmdsVersionState`.
505            // Init with the default.
506            constructor_args.vm_resources.mmds_or_default()?;
507        }
508
509        for net_state in &state.net_devices {
510            let device = Arc::new(Mutex::new(
511                Net::restore(
512                    NetConstructorArgs {
513                        mem: mem.clone(),
514                        mmds: constructor_args
515                            .vm_resources
516                            .mmds
517                            .as_ref()
518                            // Clone the Arc reference.
519                            .cloned(),
520                    },
521                    &net_state.device_state,
522                )
523                .unwrap(),
524            ));
525
526            constructor_args
527                .vm_resources
528                .net_builder
529                .add_device(device.clone());
530
531            pci_devices
532                .restore_pci_device(
533                    constructor_args.vm,
534                    device,
535                    &net_state.device_id,
536                    &net_state.transport_state,
537                    constructor_args.event_manager,
538                )
539                .unwrap()
540        }
541
542        if let Some(vsock_state) = &state.vsock_device {
543            let ctor_args = VsockUdsConstructorArgs {
544                cid: vsock_state.device_state.frontend.cid,
545            };
546            let backend =
547                VsockUnixBackend::restore(ctor_args, &vsock_state.device_state.backend).unwrap();
548            let device = Arc::new(Mutex::new(
549                Vsock::restore(
550                    VsockConstructorArgs {
551                        mem: mem.clone(),
552                        backend,
553                    },
554                    &vsock_state.device_state.frontend,
555                )
556                .unwrap(),
557            ));
558
559            constructor_args
560                .vm_resources
561                .vsock
562                .set_device(device.clone());
563
564            pci_devices
565                .restore_pci_device(
566                    constructor_args.vm,
567                    device,
568                    &vsock_state.device_id,
569                    &vsock_state.transport_state,
570                    constructor_args.event_manager,
571                )
572                .unwrap()
573        }
574
575        if let Some(entropy_state) = &state.entropy_device {
576            let ctor_args = EntropyConstructorArgs { mem: mem.clone() };
577
578            let device = Arc::new(Mutex::new(
579                Entropy::restore(ctor_args, &entropy_state.device_state).unwrap(),
580            ));
581
582            constructor_args
583                .vm_resources
584                .entropy
585                .set_device(device.clone());
586
587            pci_devices
588                .restore_pci_device(
589                    constructor_args.vm,
590                    device,
591                    &entropy_state.device_id,
592                    &entropy_state.transport_state,
593                    constructor_args.event_manager,
594                )
595                .unwrap()
596        }
597
598        for pmem_state in &state.pmem_devices {
599            let device = Arc::new(Mutex::new(
600                Pmem::restore(
601                    PmemConstructorArgs {
602                        mem,
603                        vm: constructor_args.vm.as_ref(),
604                    },
605                    &pmem_state.device_state,
606                )
607                .unwrap(),
608            ));
609
610            constructor_args
611                .vm_resources
612                .pmem
613                .add_device(device.clone());
614
615            pci_devices
616                .restore_pci_device(
617                    constructor_args.vm,
618                    device,
619                    &pmem_state.device_id,
620                    &pmem_state.transport_state,
621                    constructor_args.event_manager,
622                )
623                .unwrap()
624        }
625
626        if let Some(memory_device) = &state.memory_device {
627            let ctor_args = VirtioMemConstructorArgs::new(Arc::clone(constructor_args.vm));
628            let device = VirtioMem::restore(ctor_args, &memory_device.device_state).unwrap();
629
630            constructor_args.vm_resources.memory_hotplug = Some(MemoryHotplugConfig {
631                total_size_mib: device.total_size_mib(),
632                block_size_mib: device.block_size_mib(),
633                slot_size_mib: device.slot_size_mib(),
634            });
635
636            let arcd_device = Arc::new(Mutex::new(device));
637            pci_devices
638                .restore_pci_device(
639                    constructor_args.vm,
640                    arcd_device,
641                    &memory_device.device_id,
642                    &memory_device.transport_state,
643                    constructor_args.event_manager,
644                )
645                .unwrap()
646        }
647
648        Ok(pci_devices)
649    }
650}
651
652#[cfg(test)]
653mod tests {
654    use vmm_sys_util::tempfile::TempFile;
655
656    use super::*;
657    use crate::builder::tests::*;
658    use crate::device_manager;
659    use crate::devices::virtio::block::CacheType;
660    use crate::mmds::data_store::MmdsVersion;
661    use crate::resources::VmmConfig;
662    use crate::snapshot::Snapshot;
663    use crate::vmm_config::balloon::BalloonDeviceConfig;
664    use crate::vmm_config::entropy::EntropyDeviceConfig;
665    use crate::vmm_config::memory_hotplug::MemoryHotplugConfig;
666    use crate::vmm_config::net::NetworkInterfaceConfig;
667    use crate::vmm_config::pmem::PmemConfig;
668    use crate::vmm_config::vsock::VsockDeviceConfig;
669
670    #[test]
671    fn test_device_manager_persistence() {
672        let mut buf = vec![0; 65536];
673        // These need to survive so the restored blocks find them.
674        let _block_files;
675        let _pmem_files;
676        let mut tmp_sock_file = TempFile::new().unwrap();
677        tmp_sock_file.remove().unwrap();
678        // Set up a vmm with one of each device, and get the serialized DeviceStates.
679        {
680            let mut event_manager = EventManager::new().expect("Unable to create EventManager");
681            let mut vmm = default_vmm();
682            vmm.device_manager.enable_pci(&vmm.vm).unwrap();
683            let mut cmdline = default_kernel_cmdline();
684
685            // Add a balloon device.
686            let balloon_cfg = BalloonDeviceConfig {
687                amount_mib: 123,
688                deflate_on_oom: false,
689                stats_polling_interval_s: 1,
690                free_page_hinting: false,
691                free_page_reporting: false,
692            };
693            insert_balloon_device(&mut vmm, &mut cmdline, &mut event_manager, balloon_cfg);
694            // Add a block device.
695            let drive_id = String::from("root");
696            let block_configs = vec![CustomBlockConfig::new(
697                drive_id,
698                true,
699                None,
700                true,
701                CacheType::Unsafe,
702            )];
703            _block_files =
704                insert_block_devices(&mut vmm, &mut cmdline, &mut event_manager, block_configs);
705            // Add a net device.
706            let network_interface = NetworkInterfaceConfig {
707                iface_id: String::from("netif"),
708                host_dev_name: String::from("hostname"),
709                guest_mac: None,
710                rx_rate_limiter: None,
711                tx_rate_limiter: None,
712            };
713            insert_net_device_with_mmds(
714                &mut vmm,
715                &mut cmdline,
716                &mut event_manager,
717                network_interface,
718                MmdsVersion::V2,
719            );
720            // Add a vsock device.
721            let vsock_dev_id = "vsock";
722            let vsock_config = VsockDeviceConfig {
723                vsock_id: Some(vsock_dev_id.to_string()),
724                guest_cid: 3,
725                uds_path: tmp_sock_file.as_path().to_str().unwrap().to_string(),
726            };
727            insert_vsock_device(&mut vmm, &mut cmdline, &mut event_manager, vsock_config);
728            // Add an entropy device.
729            let entropy_config = EntropyDeviceConfig::default();
730            insert_entropy_device(&mut vmm, &mut cmdline, &mut event_manager, entropy_config);
731            // Add a pmem device.
732            let pmem_id = String::from("pmem");
733            let pmem_configs = vec![PmemConfig {
734                id: pmem_id,
735                path_on_host: "".into(),
736                root_device: true,
737                read_only: true,
738            }];
739            _pmem_files =
740                insert_pmem_devices(&mut vmm, &mut cmdline, &mut event_manager, pmem_configs);
741
742            let memory_hotplug_config = MemoryHotplugConfig {
743                total_size_mib: 1024,
744                block_size_mib: 2,
745                slot_size_mib: 128,
746            };
747            insert_virtio_mem_device(
748                &mut vmm,
749                &mut cmdline,
750                &mut event_manager,
751                memory_hotplug_config,
752            );
753
754            Snapshot::new(vmm.device_manager.save())
755                .save(&mut buf.as_mut_slice())
756                .unwrap();
757        }
758
759        tmp_sock_file.remove().unwrap();
760
761        let mut event_manager = EventManager::new().expect("Unable to create EventManager");
762        // Keep in mind we are re-creating here an empty DeviceManager. Restoring later on
763        // will create a new PciDevices manager different than vmm.pci_devices. We're doing
764        // this to avoid restoring the whole Vmm, since what we really need from Vmm is the Vm
765        // object and calling default_vmm() is the easiest way to create one.
766        let vmm = default_vmm();
767        let device_manager_state: device_manager::DevicesState =
768            Snapshot::load_without_crc_check(buf.as_slice())
769                .unwrap()
770                .data;
771        let vm_resources = &mut VmResources::default();
772        let restore_args = PciDevicesConstructorArgs {
773            vm: &vmm.vm,
774            mem: vmm.vm.guest_memory(),
775            vm_resources,
776            instance_id: "microvm-id",
777            event_manager: &mut event_manager,
778        };
779        let _restored_dev_manager =
780            PciDevices::restore(restore_args, &device_manager_state.pci_state).unwrap();
781
782        let expected_vm_resources = format!(
783            r#"{{
784  "balloon": {{
785    "amount_mib": 123,
786    "deflate_on_oom": false,
787    "stats_polling_interval_s": 1,
788    "free_page_hinting": false,
789    "free_page_reporting": false
790  }},
791  "drives": [
792    {{
793      "drive_id": "root",
794      "partuuid": null,
795      "is_root_device": true,
796      "cache_type": "Unsafe",
797      "is_read_only": true,
798      "path_on_host": "{}",
799      "rate_limiter": null,
800      "io_engine": "Sync",
801      "socket": null
802    }}
803  ],
804  "boot-source": {{
805    "kernel_image_path": "",
806    "initrd_path": null,
807    "boot_args": null
808  }},
809  "cpu-config": null,
810  "logger": null,
811  "machine-config": {{
812    "vcpu_count": 1,
813    "mem_size_mib": 128,
814    "smt": false,
815    "track_dirty_pages": false,
816    "huge_pages": "None"
817  }},
818  "metrics": null,
819  "mmds-config": {{
820    "version": "V2",
821    "network_interfaces": [
822      "netif"
823    ],
824    "ipv4_address": "169.254.169.254",
825    "imds_compat": false
826  }},
827  "network-interfaces": [
828    {{
829      "iface_id": "netif",
830      "host_dev_name": "hostname",
831      "guest_mac": null,
832      "rx_rate_limiter": null,
833      "tx_rate_limiter": null
834    }}
835  ],
836  "vsock": {{
837    "guest_cid": 3,
838    "uds_path": "{}"
839  }},
840  "entropy": {{
841    "rate_limiter": null
842  }},
843  "pmem": [
844    {{
845      "id": "pmem",
846      "path_on_host": "{}",
847      "root_device": true,
848      "read_only": true
849    }}
850  ],
851  "memory-hotplug": {{
852    "total_size_mib": 1024,
853    "block_size_mib": 2,
854    "slot_size_mib": 128
855  }}
856}}"#,
857            _block_files.last().unwrap().as_path().to_str().unwrap(),
858            tmp_sock_file.as_path().to_str().unwrap(),
859            _pmem_files.last().unwrap().as_path().to_str().unwrap(),
860        );
861
862        assert_eq!(
863            vm_resources
864                .mmds
865                .as_ref()
866                .unwrap()
867                .lock()
868                .unwrap()
869                .version(),
870            MmdsVersion::V2
871        );
872        assert_eq!(
873            device_manager_state.pci_state.mmds.unwrap().version,
874            MmdsVersion::V2
875        );
876        assert_eq!(
877            expected_vm_resources,
878            serde_json::to_string_pretty(&VmmConfig::from(&*vm_resources)).unwrap()
879        );
880    }
881}