1use super::MANA_INSTANCE;
11use super::NIC_MAC_ADDRESS;
12use super::PetriVmConfigOpenVmm;
13use chipset_resources::battery::BatteryDeviceHandleX64;
14use chipset_resources::battery::HostBatteryUpdate;
15use disk_backend_resources::LayeredDiskHandle;
16use disk_backend_resources::layer::RamDiskLayerHandle;
17use gdma_resources::GdmaDeviceHandle;
18use gdma_resources::VportDefinition;
19use get_resources::ged::IgvmAttestTestConfig;
20use guid::Guid;
21use net_backend_resources::mac_address::MacAddress;
22use nvme_resources::NamespaceDefinition;
23use nvme_resources::NvmeControllerHandle;
24use openvmm_defs::config::Config;
25use openvmm_defs::config::DeviceVtl;
26use openvmm_defs::config::LoadMode;
27use openvmm_defs::config::PcieDeviceConfig;
28use openvmm_defs::config::PcieIommuConfig;
29use openvmm_defs::config::PcieMmioRangeConfig;
30use openvmm_defs::config::PciePortConfig;
31use openvmm_defs::config::PcieRootComplexConfig;
32use openvmm_defs::config::PcieSwitchConfig;
33use openvmm_defs::config::VpciDeviceConfig;
34use openvmm_defs::config::Vtl2BaseAddressType;
35use vm_resource::IntoResource;
36use vmotherboard::ChipsetDeviceHandle;
37
38impl PetriVmConfigOpenVmm {
39 pub fn with_vtl0_alias_map(mut self) -> Self {
42 self.config
43 .hypervisor
44 .with_vtl2
45 .as_mut()
46 .expect("Not an openhcl config.")
47 .vtl0_alias_map = true;
48 self
49 }
50
51 pub fn with_battery(mut self) -> Self {
53 if self.resources.properties.is_openhcl {
54 self.ged.as_mut().unwrap().enable_battery = true;
55 } else {
56 self.config.chipset_devices.push(ChipsetDeviceHandle {
57 name: "battery".to_string(),
58 resource: BatteryDeviceHandleX64 {
59 battery_status_recv: {
60 let (tx, rx) = mesh::channel();
61 tx.send(HostBatteryUpdate::default_present());
62 rx
63 },
64 }
65 .into_resource(),
66 });
67 if let LoadMode::Uefi { enable_battery, .. } = &mut self.config.load_mode {
68 *enable_battery = true;
69 }
70 }
71 self
72 }
73
74 pub fn with_igvm_attest_test_config(mut self, config: IgvmAttestTestConfig) -> Self {
76 if !self.resources.properties.is_openhcl {
77 panic!("IGVM Attest test config is only supported for OpenHCL.")
78 };
79
80 let ged = self.ged.as_mut().expect("No GED to configure TPM");
81
82 ged.igvm_attest_test_config = Some(config);
83
84 self
85 }
86
87 pub fn with_nic(mut self) -> Self {
91 let endpoint = net_backend_resources::consomme::ConsommeHandle {
92 cidr: None,
93 ports: Vec::new(),
94 recv: None,
95 }
96 .into_resource();
97 if let Some(vtl2_settings) = self.runtime_config.vtl2_settings.as_mut() {
98 self.config.vpci_devices.push(VpciDeviceConfig {
99 vtl: DeviceVtl::Vtl2,
100 instance_id: MANA_INSTANCE,
101 resource: GdmaDeviceHandle {
102 vports: vec![VportDefinition {
103 mac_address: NIC_MAC_ADDRESS,
104 endpoint,
105 }],
106 }
107 .into_resource(),
108 vnode: None,
109 });
110
111 vtl2_settings.dynamic.as_mut().unwrap().nic_devices.push(
112 vtl2_settings_proto::NicDeviceLegacy {
113 instance_id: MANA_INSTANCE.to_string(),
114 subordinate_instance_id: None,
115 max_sub_channels: None,
116 },
117 );
118 } else {
119 const NETVSP_INSTANCE: Guid = guid::guid!("c6c46cc3-9302-4344-b206-aef65e5bd0a2");
120 self.config.vmbus_devices.push((
121 DeviceVtl::Vtl0,
122 netvsp_resources::NetvspHandle {
123 instance_id: NETVSP_INSTANCE,
124 mac_address: NIC_MAC_ADDRESS,
125 endpoint,
126 max_queues: None,
127 }
128 .into_resource(),
129 ));
130 }
131
132 self
133 }
134
135 pub fn with_pcie_nic(mut self, port_name: &str, mac_address: MacAddress) -> Self {
137 let endpoint = net_backend_resources::consomme::ConsommeHandle {
138 cidr: None,
139 ports: Vec::new(),
140 recv: None,
141 }
142 .into_resource();
143 self.config.pcie_devices.push(PcieDeviceConfig {
144 port_name: port_name.to_string(),
145 resource: GdmaDeviceHandle {
146 vports: vec![VportDefinition {
147 mac_address,
148 endpoint,
149 }],
150 }
151 .into_resource(),
152 });
153
154 self
155 }
156
157 pub fn with_pcie_nvme(mut self, port_name: &str, subsystem_id: Guid) -> Self {
159 self.config.pcie_devices.push(PcieDeviceConfig {
160 port_name: port_name.to_string(),
161 resource: NvmeControllerHandle {
162 subsystem_id,
163 max_io_queues: 64,
164 msix_count: 64,
165 namespaces: vec![NamespaceDefinition {
166 nsid: 1,
167 disk: LayeredDiskHandle::single_layer(RamDiskLayerHandle {
168 len: Some(1024 * 1024),
169 sector_size: None,
170 })
171 .into_resource(),
172 read_only: false,
173 }],
174 requests: None,
175 }
176 .into_resource(),
177 });
178
179 self
180 }
181
182 pub fn with_virtio_nic(mut self, port_name: &str, mac_address: MacAddress) -> Self {
187 let endpoint = net_backend_resources::consomme::ConsommeHandle {
188 cidr: None,
189 ports: Vec::new(),
190 recv: None,
191 }
192 .into_resource();
193
194 self.config.pcie_devices.push(PcieDeviceConfig {
195 port_name: port_name.to_string(),
196 resource: virtio_resources::VirtioPciDeviceHandle(
197 virtio_resources::net::VirtioNetHandle {
198 max_queues: None,
199 mac_address,
200 endpoint,
201 }
202 .into_resource(),
203 )
204 .into_resource(),
205 });
206
207 self
208 }
209
210 pub fn with_tcp_pipette_nic(mut self, port_name: &str, mac_address: MacAddress) -> Self {
218 let (port_send, port_recv) = mesh::oneshot();
219 let endpoint = net_backend_resources::consomme::ConsommeHandle {
220 cidr: None,
221 ports: vec![net_backend_resources::consomme::HostPortConfig {
222 protocol: net_backend_resources::consomme::HostPortProtocol::Tcp,
223 host_address: Some(net_backend_resources::consomme::HostIpAddress::Ipv4(
224 std::net::Ipv4Addr::LOCALHOST,
225 )),
226 host_port: net_backend_resources::consomme::HostPort::Dynamic(port_send),
227 guest_port: pipette_client::PIPETTE_PORT as u16,
228 }],
229 recv: None,
230 }
231 .into_resource();
232 self.config.pcie_devices.push(PcieDeviceConfig {
233 port_name: port_name.to_string(),
234 resource: virtio_resources::VirtioPciDeviceHandle(
235 virtio_resources::net::VirtioNetHandle {
236 max_queues: None,
237 mac_address,
238 endpoint,
239 }
240 .into_resource(),
241 )
242 .into_resource(),
243 });
244 self.resources.tcp_pipette_port = Some(port_recv);
245 self
246 }
247
248 pub fn with_nested_virt(mut self) -> Self {
250 self.config.hypervisor.nested_virt = true;
251 self
252 }
253
254 #[cfg(windows)]
270 pub fn with_dio_nic(mut self, switch_id: Option<Guid>) -> Self {
271 let switch_port_id = vmswitch::kernel::SwitchPortId {
272 switch: switch_id.unwrap_or(vmswitch::hcn::DEFAULT_SWITCH),
273 port: Guid::new_random(),
274 };
275 let _ = vmswitch::hcn::Network::open(&switch_port_id.switch)
276 .unwrap_or_else(|e| panic!("could not find switch {}: {e}", switch_port_id.switch));
277 let switch_port = vmswitch::kernel::SwitchPort::new(&switch_port_id)
278 .expect("failed to create vmswitch DIO port");
279 self.resources._switch_ports.push(switch_port);
280
281 let endpoint = net_backend_resources::dio::WindowsDirectIoHandle {
282 switch_port_id: net_backend_resources::dio::SwitchPortId {
283 switch: switch_port_id.switch,
284 port: switch_port_id.port,
285 },
286 }
287 .into_resource();
288
289 if let Some(vtl2_settings) = self.runtime_config.vtl2_settings.as_mut() {
290 self.config.vpci_devices.push(VpciDeviceConfig {
291 vtl: DeviceVtl::Vtl2,
292 instance_id: MANA_INSTANCE,
293 resource: GdmaDeviceHandle {
294 vports: vec![VportDefinition {
295 mac_address: NIC_MAC_ADDRESS,
296 endpoint,
297 }],
298 }
299 .into_resource(),
300 vnode: None,
301 });
302
303 vtl2_settings.dynamic.as_mut().unwrap().nic_devices.push(
304 vtl2_settings_proto::NicDeviceLegacy {
305 instance_id: MANA_INSTANCE.to_string(),
306 subordinate_instance_id: None,
307 max_sub_channels: None,
308 },
309 );
310 } else {
311 const NETVSP_DIO_INSTANCE: Guid = guid::guid!("d1ff4c5a-1b3c-4f0d-8e10-1b9d8b1d1cee");
312 self.config.vmbus_devices.push((
313 DeviceVtl::Vtl0,
314 netvsp_resources::NetvspHandle {
315 instance_id: NETVSP_DIO_INSTANCE,
316 mac_address: NIC_MAC_ADDRESS,
317 endpoint,
318 max_queues: None,
319 }
320 .into_resource(),
321 ));
322 }
323
324 self
325 }
326
327 pub fn with_vtl2_relocation_mode(mut self, mode: Vtl2BaseAddressType) -> Self {
329 let LoadMode::Igvm {
330 vtl2_base_address, ..
331 } = &mut self.config.load_mode
332 else {
333 panic!("vtl2 relocation mode is only supported for OpenHCL firmware")
334 };
335 *vtl2_base_address = mode;
336 self
337 }
338
339 pub fn with_memory_backing_file(mut self, path: impl Into<std::path::PathBuf>) -> Self {
351 assert_ne!(
352 self.requested_private_memory,
353 Some(true),
354 "with_memory_backing_file forces shared memory, which conflicts with \
355 the explicitly requested private memory"
356 );
357 self.memory_backing_file = Some(path.into());
358 for node in &mut self.config.numa.nodes {
359 if let Some(mem) = &mut node.mem {
360 mem.private_memory = false;
361 }
362 }
363 self
364 }
365
366 pub fn with_hugepages(mut self, hugepage_size: Option<u64>) -> Self {
374 assert_ne!(
375 self.requested_private_memory,
376 Some(true),
377 "with_hugepages forces shared memory, which conflicts with the \
378 explicitly requested private memory"
379 );
380 for node in &mut self.config.numa.nodes {
381 if let Some(mem) = &mut node.mem {
382 mem.hugepages = true;
383 mem.hugepage_size = hugepage_size;
384 mem.private_memory = false;
385 }
386 }
387 self
388 }
389
390 pub fn with_pcie_root_topology(
402 mut self,
403 segment_count: u64,
404 root_complex_per_segment: u64,
405 root_ports_per_root_complex: u64,
406 ) -> Self {
407 const LOW_MMIO_SIZE: u64 = 64 * 1024 * 1024; const HIGH_MMIO_SIZE: u64 = 1024 * 1024 * 1024; let segment_base = self
415 .config
416 .pcie_root_complexes
417 .iter()
418 .map(|rc| u64::from(rc.segment) + 1)
419 .max()
420 .unwrap_or(0);
421 let index_base = self.config.pcie_root_complexes.len() as u64;
422
423 for segment_offset in 0..segment_count {
425 let segment = segment_base + segment_offset;
426 let bus_count_per_rc = 256 / root_complex_per_segment;
427 for rc_index_in_segment in 0..root_complex_per_segment {
428 let index =
429 index_base + segment_offset * root_complex_per_segment + rc_index_in_segment;
430 let name = format!("s{}rc{}", segment, rc_index_in_segment);
431
432 let start_bus = rc_index_in_segment * bus_count_per_rc;
433 let end_bus = start_bus + bus_count_per_rc - 1;
434
435 let ports = (0..root_ports_per_root_complex)
436 .map(|i| PciePortConfig {
437 name: format!("s{}rc{}rp{}", segment, rc_index_in_segment, i),
438 devfn: None,
439 hotplug: true,
440 acs_capabilities_supported: Some(0),
441 cxl: false,
442 pasid: false,
443 })
444 .collect();
445
446 self.config.pcie_root_complexes.push(PcieRootComplexConfig {
447 index: index.try_into().unwrap(),
448 name,
449 segment: segment.try_into().unwrap(),
450 start_bus: start_bus.try_into().unwrap(),
451 end_bus: end_bus.try_into().unwrap(),
452 low_mmio: PcieMmioRangeConfig::Dynamic {
453 size: LOW_MMIO_SIZE,
454 },
455 high_mmio: PcieMmioRangeConfig::Dynamic {
456 size: HIGH_MMIO_SIZE,
457 },
458 cxl: None,
459 ports,
460 iommu: None,
461 vnode: None,
462 preserve_bars: false,
463 });
464 }
465 }
466
467 self
468 }
469
470 pub fn with_pcie_switch(
472 mut self,
473 port_name: &str,
474 switch_name: &str,
475 port_count: u8,
476 hotplug: bool,
477 ) -> Self {
478 self.config.pcie_switches.push(PcieSwitchConfig {
479 name: switch_name.to_string(),
480 parent_port: port_name.to_string(),
481 ports: (0..port_count)
482 .map(|i| PciePortConfig {
483 name: format!("{switch_name}-downstream-{i}"),
484 devfn: None,
485 hotplug,
486 acs_capabilities_supported: Some(0),
487 cxl: false,
488 pasid: false,
489 })
490 .collect(),
491 });
492 self
493 }
494
495 pub fn with_smmu(mut self, rc_names: &[&str]) -> Self {
502 for name in rc_names {
503 self.pending_iommu.push((
504 name.to_string(),
505 PcieIommuConfig::Smmu {
506 accel: false,
507 oas: openvmm_defs::config::SmmuOas::Auto,
508 },
509 ));
510 }
511 self
512 }
513
514 pub fn with_amd_iommu(mut self, rc_names: &[&str]) -> Self {
523 for name in rc_names {
524 self.pending_iommu
525 .push((name.to_string(), PcieIommuConfig::AmdVi));
526 }
527 self
528 }
529
530 pub fn with_intel_vtd(mut self, rc_names: &[&str]) -> Self {
539 for name in rc_names {
540 self.pending_iommu
541 .push((name.to_string(), PcieIommuConfig::IntelVtd));
542 }
543 self
544 }
545
546 pub fn with_custom_config(mut self, f: impl FnOnce(&mut Config)) -> Self {
550 f(&mut self.config);
551 self
552 }
553
554 pub fn with_allow_early_vtl0_access(mut self, allow: bool) -> Self {
560 self.config
561 .hypervisor
562 .with_vtl2
563 .as_mut()
564 .unwrap()
565 .late_map_vtl0_memory =
566 (!allow).then_some(openvmm_defs::config::LateMapVtl0MemoryPolicy::InjectException);
567
568 self
569 }
570}