petri/vm/openvmm/
modify.rs1use 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 memory_range::MemoryRange;
22use net_backend_resources::mac_address::MacAddress;
23use nvme_resources::NamespaceDefinition;
24use nvme_resources::NvmeControllerHandle;
25use openvmm_defs::config::Config;
26use openvmm_defs::config::DeviceVtl;
27use openvmm_defs::config::LoadMode;
28use openvmm_defs::config::PcieDeviceConfig;
29use openvmm_defs::config::PcieRootComplexConfig;
30use openvmm_defs::config::PcieRootPortConfig;
31use openvmm_defs::config::PcieSwitchConfig;
32use openvmm_defs::config::VpciDeviceConfig;
33use openvmm_defs::config::Vtl2BaseAddressType;
34use vm_resource::IntoResource;
35use vmotherboard::ChipsetDeviceHandle;
36
37impl PetriVmConfigOpenVmm {
38 pub fn with_vtl0_alias_map(mut self) -> Self {
41 self.config
42 .hypervisor
43 .with_vtl2
44 .as_mut()
45 .expect("Not an openhcl config.")
46 .vtl0_alias_map = true;
47 self
48 }
49
50 pub fn with_battery(mut self) -> Self {
52 if self.resources.properties.is_openhcl {
53 self.ged.as_mut().unwrap().enable_battery = true;
54 } else {
55 self.config.chipset_devices.push(ChipsetDeviceHandle {
56 name: "battery".to_string(),
57 resource: BatteryDeviceHandleX64 {
58 battery_status_recv: {
59 let (tx, rx) = mesh::channel();
60 tx.send(HostBatteryUpdate::default_present());
61 rx
62 },
63 }
64 .into_resource(),
65 });
66 if let LoadMode::Uefi { enable_battery, .. } = &mut self.config.load_mode {
67 *enable_battery = true;
68 }
69 }
70 self
71 }
72
73 pub fn with_igvm_attest_test_config(mut self, config: IgvmAttestTestConfig) -> Self {
75 if !self.resources.properties.is_openhcl {
76 panic!("IGVM Attest test config is only supported for OpenHCL.")
77 };
78
79 let ged = self.ged.as_mut().expect("No GED to configure TPM");
80
81 ged.igvm_attest_test_config = Some(config);
82
83 self
84 }
85
86 pub fn with_nic(mut self) -> Self {
90 let endpoint =
91 net_backend_resources::consomme::ConsommeHandle { cidr: None }.into_resource();
92 if let Some(vtl2_settings) = self.runtime_config.vtl2_settings.as_mut() {
93 self.config.vpci_devices.push(VpciDeviceConfig {
94 vtl: DeviceVtl::Vtl2,
95 instance_id: MANA_INSTANCE,
96 resource: GdmaDeviceHandle {
97 vports: vec![VportDefinition {
98 mac_address: NIC_MAC_ADDRESS,
99 endpoint,
100 }],
101 }
102 .into_resource(),
103 });
104
105 vtl2_settings.dynamic.as_mut().unwrap().nic_devices.push(
106 vtl2_settings_proto::NicDeviceLegacy {
107 instance_id: MANA_INSTANCE.to_string(),
108 subordinate_instance_id: None,
109 max_sub_channels: None,
110 },
111 );
112 } else {
113 const NETVSP_INSTANCE: Guid = guid::guid!("c6c46cc3-9302-4344-b206-aef65e5bd0a2");
114 self.config.vmbus_devices.push((
115 DeviceVtl::Vtl0,
116 netvsp_resources::NetvspHandle {
117 instance_id: NETVSP_INSTANCE,
118 mac_address: NIC_MAC_ADDRESS,
119 endpoint,
120 max_queues: None,
121 }
122 .into_resource(),
123 ));
124 }
125
126 self
127 }
128
129 pub fn with_pcie_nic(mut self, port_name: &str, mac_address: MacAddress) -> Self {
131 let endpoint =
132 net_backend_resources::consomme::ConsommeHandle { cidr: None }.into_resource();
133 self.config.pcie_devices.push(PcieDeviceConfig {
134 port_name: port_name.to_string(),
135 resource: GdmaDeviceHandle {
136 vports: vec![VportDefinition {
137 mac_address,
138 endpoint,
139 }],
140 }
141 .into_resource(),
142 });
143
144 self
145 }
146
147 pub fn with_pcie_nvme(mut self, port_name: &str, subsystem_id: Guid) -> Self {
149 self.config.pcie_devices.push(PcieDeviceConfig {
150 port_name: port_name.to_string(),
151 resource: NvmeControllerHandle {
152 subsystem_id,
153 max_io_queues: 64,
154 msix_count: 64,
155 namespaces: vec![NamespaceDefinition {
156 nsid: 1,
157 disk: LayeredDiskHandle::single_layer(RamDiskLayerHandle {
158 len: Some(1024 * 1024),
159 sector_size: None,
160 })
161 .into_resource(),
162 read_only: false,
163 }],
164 requests: None,
165 }
166 .into_resource(),
167 });
168
169 self
170 }
171
172 pub fn with_virtio_nic(mut self, port_name: &str) -> Self {
177 let endpoint =
178 net_backend_resources::consomme::ConsommeHandle { cidr: None }.into_resource();
179
180 self.config.pcie_devices.push(PcieDeviceConfig {
181 port_name: port_name.to_string(),
182 resource: virtio_resources::VirtioPciDeviceHandle(
183 virtio_resources::net::VirtioNetHandle {
184 max_queues: None,
185 mac_address: NIC_MAC_ADDRESS,
186 endpoint,
187 }
188 .into_resource(),
189 )
190 .into_resource(),
191 });
192
193 self
194 }
195
196 pub fn with_vtl2_relocation_mode(mut self, mode: Vtl2BaseAddressType) -> Self {
198 let LoadMode::Igvm {
199 vtl2_base_address, ..
200 } = &mut self.config.load_mode
201 else {
202 panic!("vtl2 relocation mode is only supported for OpenHCL firmware")
203 };
204 *vtl2_base_address = mode;
205 self
206 }
207
208 pub fn with_memory_backing_file(mut self, path: impl Into<std::path::PathBuf>) -> Self {
214 self.memory_backing_file = Some(path.into());
215 self
216 }
217
218 pub fn with_pcie_root_topology(
224 mut self,
225 segment_count: u64,
226 root_complex_per_segment: u64,
227 root_ports_per_root_complex: u64,
228 ) -> Self {
229 const SINGLE_BUS_NUMBER_ECAM_SIZE: u64 = 1024 * 1024; const FULL_SEGMENT_ECAM_SIZE: u64 = 256 * SINGLE_BUS_NUMBER_ECAM_SIZE; const LOW_MMIO_SIZE: u64 = 64 * 1024 * 1024; const HIGH_MMIO_SIZE: u64 = 1024 * 1024 * 1024; let ecam_size = segment_count * FULL_SEGMENT_ECAM_SIZE;
236 let low_mmio_size = segment_count * root_complex_per_segment * LOW_MMIO_SIZE;
237 let high_mmio_size = segment_count * root_complex_per_segment * HIGH_MMIO_SIZE;
238
239 let low_mmio_start = self.config.memory.mmio_gaps[0].start();
240 let high_mmio_end = self.config.memory.mmio_gaps[1].end();
241
242 let ecam_gap = MemoryRange::new(low_mmio_start - ecam_size..low_mmio_start);
243 let low_gap = MemoryRange::new(ecam_gap.start() - low_mmio_size..ecam_gap.start());
244 let high_gap = MemoryRange::new(high_mmio_end..high_mmio_end + high_mmio_size);
245
246 self.config.memory.pci_ecam_gaps.push(ecam_gap);
247 self.config.memory.pci_mmio_gaps.push(low_gap);
248 self.config.memory.pci_mmio_gaps.push(high_gap);
249
250 for segment in 0..segment_count {
252 let bus_count_per_rc = 256 / root_complex_per_segment;
253 for rc_index_in_segment in 0..root_complex_per_segment {
254 let index = segment * root_complex_per_segment + rc_index_in_segment;
255 let name = format!("s{}rc{}", segment, rc_index_in_segment);
256
257 let start_bus = rc_index_in_segment * bus_count_per_rc;
258 let end_bus = start_bus + bus_count_per_rc - 1;
259
260 let ecam_range_start = ecam_gap.start()
261 + segment * FULL_SEGMENT_ECAM_SIZE
262 + start_bus * SINGLE_BUS_NUMBER_ECAM_SIZE;
263 let ecam_range_end =
264 ecam_range_start + bus_count_per_rc * SINGLE_BUS_NUMBER_ECAM_SIZE;
265
266 let low_mmio_start = low_gap.start() + index * LOW_MMIO_SIZE;
267 let low_mmio_end = low_gap.start() + (index + 1) * LOW_MMIO_SIZE;
268 let high_mmio_start = high_gap.start() + index * HIGH_MMIO_SIZE;
269 let high_mmio_end = high_gap.start() + (index + 1) * HIGH_MMIO_SIZE;
270
271 let ports = (0..root_ports_per_root_complex)
272 .map(|i| PcieRootPortConfig {
273 name: format!("s{}rc{}rp{}", segment, rc_index_in_segment, i),
274 hotplug: true,
275 })
276 .collect();
277
278 self.config.pcie_root_complexes.push(PcieRootComplexConfig {
279 index: index.try_into().unwrap(),
280 name,
281 segment: segment.try_into().unwrap(),
282 start_bus: start_bus.try_into().unwrap(),
283 end_bus: end_bus.try_into().unwrap(),
284 ecam_range: MemoryRange::new(ecam_range_start..ecam_range_end),
285 low_mmio: MemoryRange::new(low_mmio_start..low_mmio_end),
286 high_mmio: MemoryRange::new(high_mmio_start..high_mmio_end),
287 ports,
288 });
289 }
290 }
291
292 self
293 }
294
295 pub fn with_pcie_switch(
297 mut self,
298 port_name: &str,
299 switch_name: &str,
300 port_count: u8,
301 hotplug: bool,
302 ) -> Self {
303 self.config.pcie_switches.push(PcieSwitchConfig {
304 name: switch_name.to_string(),
305 num_downstream_ports: port_count,
306 parent_port: port_name.to_string(),
307 hotplug,
308 });
309 self
310 }
311
312 pub fn with_custom_config(mut self, f: impl FnOnce(&mut Config)) -> Self {
316 f(&mut self.config);
317 self
318 }
319
320 pub fn with_allow_early_vtl0_access(mut self, allow: bool) -> Self {
326 self.config
327 .hypervisor
328 .with_vtl2
329 .as_mut()
330 .unwrap()
331 .late_map_vtl0_memory =
332 (!allow).then_some(openvmm_defs::config::LateMapVtl0MemoryPolicy::InjectException);
333
334 self
335 }
336}