vmm/device_manager/
persist.rs

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