1use 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#[derive(Debug, thiserror::Error, displaydoc::Display)]
64pub enum DevicePersistError {
65 Balloon(#[from] BalloonError),
67 Block(#[from] BlockError),
69 DeviceManager(#[from] super::mmio::MmioError),
71 MmioTransport,
73 Bus(#[from] BusError),
75 #[cfg(target_arch = "aarch64")]
76 Legacy(#[from] std::io::Error),
78 Net(#[from] NetError),
80 Vsock(#[from] VsockError),
82 VsockUnixBackend(#[from] VsockUnixBackendError),
84 MmdsConfig(#[from] MmdsConfigError),
86 Entropy(#[from] EntropyError),
88 Pmem(#[from] PmemError),
90 VirtioMem(#[from] VirtioMemPersistError),
92 DeviceActivation(#[from] ActivateError),
94}
95
96#[derive(Debug, Clone, Serialize, Deserialize)]
98pub struct VirtioDeviceState<T> {
99 pub device_id: String,
101 pub device_state: T,
103 pub transport_state: MmioTransportState,
105 pub device_info: MMIODeviceInfo,
107}
108
109#[cfg(target_arch = "aarch64")]
111#[derive(Debug, Clone, Serialize, Deserialize)]
112pub struct ConnectedLegacyState {
113 pub type_: DeviceType,
115 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#[derive(Debug, Default, Clone, Serialize, Deserialize)]
127pub struct DeviceStates {
128 #[cfg(target_arch = "aarch64")]
129 pub legacy_devices: Vec<ConnectedLegacyState>,
131 pub block_devices: Vec<VirtioDeviceState<BlockState>>,
133 pub net_devices: Vec<VirtioDeviceState<NetState>>,
135 pub vsock_device: Option<VirtioDeviceState<VsockState>>,
137 pub balloon_device: Option<VirtioDeviceState<BalloonState>>,
139 pub mmds: Option<MmdsState>,
141 pub entropy_device: Option<VirtioDeviceState<EntropyState>>,
143 pub pmem_devices: Vec<VirtioDeviceState<PmemState>>,
145 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 vmgenid: VmGenId::restore((), &state.vmgenid).unwrap(),
193 #[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 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 .downcast_mut::<Vsock<VsockUnixBackend>>()
292 .unwrap();
293
294 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 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 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 .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 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 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 let _block_files;
687 let _pmem_files;
688 let mut tmp_sock_file = TempFile::new().unwrap();
689 tmp_sock_file.remove().unwrap();
690 {
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 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 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 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 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 let entropy_config = EntropyDeviceConfig::default();
741 insert_entropy_device(&mut vmm, &mut cmdline, &mut event_manager, entropy_config);
742 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}