1use 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 pub pci_segment: Option<PciSegment>,
50 pub virtio_devices: HashMap<(u32, String), Arc<Mutex<VirtioPciDevice>>>,
52}
53
54#[derive(Debug, thiserror::Error, displaydoc::Display)]
55pub enum PciManagerError {
56 ResourceAllocation(#[from] vm_allocator::Error),
58 Bus(#[from] BusError),
60 PciRoot(#[from] PciRootError),
62 Msi(#[from] InterruptError),
64 VirtioPciDevice(#[from] VirtioPciDeviceError),
66 Kvm(#[from] vmm_sys_util::errno::Error),
68 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 assert!(self.pci_segment.is_none());
81
82 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 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 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 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 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 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 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 pub device_id: String,
222 pub pci_device_bdf: u32,
224 pub device_state: T,
226 pub transport_state: VirtioPciDeviceState,
228}
229
230#[derive(Default, Debug, Clone, Serialize, Deserialize)]
231pub struct PciDevicesState {
232 pub pci_enabled: bool,
234 pub block_devices: Vec<VirtioDeviceState<BlockState>>,
236 pub net_devices: Vec<VirtioDeviceState<NetState>>,
238 pub vsock_device: Option<VirtioDeviceState<VsockState>>,
240 pub balloon_device: Option<VirtioDeviceState<BalloonState>>,
242 pub mmds: Option<MmdsState>,
244 pub entropy_device: Option<VirtioDeviceState<EntropyState>>,
246 pub pmem_devices: Vec<VirtioDeviceState<PmemState>>,
248 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 .downcast_mut::<Vsock<VsockUnixBackend>>()
356 .unwrap();
357
358 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 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 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 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 .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 let _block_files;
675 let _pmem_files;
676 let mut tmp_sock_file = TempFile::new().unwrap();
677 tmp_sock_file.remove().unwrap();
678 {
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 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 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 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 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 let entropy_config = EntropyDeviceConfig::default();
730 insert_entropy_device(&mut vmm, &mut cmdline, &mut event_manager, entropy_config);
731 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 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}