petri/vm/openvmm/
modify.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Helpers to modify a [`PetriVmConfigOpenVmm`] from its defaults.
5
6// TODO: Delete all modification functions that are not backend-specific
7// from this file, add necessary settings to the backend-agnostic
8// `PetriVmConfig`, and add corresponding functions to `PetriVmBuilder`.
9
10use super::MANA_INSTANCE;
11use super::NIC_MAC_ADDRESS;
12use super::PetriVmConfigOpenVmm;
13use chipset_resources::battery::BatteryDeviceHandleX64;
14use chipset_resources::battery::HostBatteryUpdate;
15use gdma_resources::GdmaDeviceHandle;
16use gdma_resources::VportDefinition;
17use get_resources::ged::IgvmAttestTestConfig;
18use openvmm_defs::config::Config;
19use openvmm_defs::config::DeviceVtl;
20use openvmm_defs::config::LoadMode;
21use openvmm_defs::config::VpciDeviceConfig;
22use openvmm_defs::config::Vtl2BaseAddressType;
23use vm_resource::IntoResource;
24use vmotherboard::ChipsetDeviceHandle;
25
26impl PetriVmConfigOpenVmm {
27    /// Enable the VTL0 alias map.
28    // TODO: Remove once #912 is fixed.
29    pub fn with_vtl0_alias_map(mut self) -> Self {
30        self.config
31            .hypervisor
32            .with_vtl2
33            .as_mut()
34            .expect("Not an openhcl config.")
35            .vtl0_alias_map = true;
36        self
37    }
38
39    /// Enable the battery for the VM.
40    pub fn with_battery(mut self) -> Self {
41        if self.firmware.is_openhcl() {
42            self.ged.as_mut().unwrap().enable_battery = true;
43        } else {
44            self.config.chipset_devices.push(ChipsetDeviceHandle {
45                name: "battery".to_string(),
46                resource: BatteryDeviceHandleX64 {
47                    battery_status_recv: {
48                        let (tx, rx) = mesh::channel();
49                        tx.send(HostBatteryUpdate::default_present());
50                        rx
51                    },
52                }
53                .into_resource(),
54            });
55            if let LoadMode::Uefi { enable_battery, .. } = &mut self.config.load_mode {
56                *enable_battery = true;
57            }
58        }
59        self
60    }
61
62    /// Set test config for the GED's IGVM attest request handler
63    pub fn with_igvm_attest_test_config(mut self, config: IgvmAttestTestConfig) -> Self {
64        if !self.firmware.is_openhcl() {
65            panic!("IGVM Attest test config is only supported for OpenHCL.")
66        };
67
68        let ged = self.ged.as_mut().expect("No GED to configure TPM");
69
70        ged.igvm_attest_test_config = Some(config);
71
72        self
73    }
74
75    /// Enable a synthnic for the VM.
76    ///
77    /// Uses a mana emulator and the paravisor if a paravisor is present.
78    pub fn with_nic(mut self) -> Self {
79        let endpoint =
80            net_backend_resources::consomme::ConsommeHandle { cidr: None }.into_resource();
81        if self.vtl2_settings.is_some() {
82            self.config.vpci_devices.push(VpciDeviceConfig {
83                vtl: DeviceVtl::Vtl2,
84                instance_id: MANA_INSTANCE,
85                resource: GdmaDeviceHandle {
86                    vports: vec![VportDefinition {
87                        mac_address: NIC_MAC_ADDRESS,
88                        endpoint,
89                    }],
90                }
91                .into_resource(),
92            });
93
94            self.vtl2_settings
95                .as_mut()
96                .unwrap()
97                .dynamic
98                .as_mut()
99                .unwrap()
100                .nic_devices
101                .push(vtl2_settings_proto::NicDeviceLegacy {
102                    instance_id: MANA_INSTANCE.to_string(),
103                    subordinate_instance_id: None,
104                    max_sub_channels: None,
105                });
106        } else {
107            const NETVSP_INSTANCE: guid::Guid = guid::guid!("c6c46cc3-9302-4344-b206-aef65e5bd0a2");
108            self.config.vmbus_devices.push((
109                DeviceVtl::Vtl0,
110                netvsp_resources::NetvspHandle {
111                    instance_id: NETVSP_INSTANCE,
112                    mac_address: NIC_MAC_ADDRESS,
113                    endpoint,
114                    max_queues: None,
115                }
116                .into_resource(),
117            ));
118        }
119
120        self
121    }
122
123    /// Load with the specified VTL2 relocation mode.
124    pub fn with_vtl2_relocation_mode(mut self, mode: Vtl2BaseAddressType) -> Self {
125        let LoadMode::Igvm {
126            vtl2_base_address, ..
127        } = &mut self.config.load_mode
128        else {
129            panic!("vtl2 relocation mode is only supported for OpenHCL firmware")
130        };
131        *vtl2_base_address = mode;
132        self
133    }
134
135    /// This is intended for special one-off use cases. As soon as something
136    /// is needed in multiple tests we should consider making it a supported
137    /// pattern.
138    pub fn with_custom_config(mut self, f: impl FnOnce(&mut Config)) -> Self {
139        f(&mut self.config);
140        self
141    }
142
143    /// Specifies whether VTL2 should be allowed to access VTL0 memory before it
144    /// sets any VTL protections.
145    ///
146    /// This is needed just for the TMK VMM, and only until it gains support for
147    /// setting VTL protections.
148    pub fn with_allow_early_vtl0_access(mut self, allow: bool) -> Self {
149        self.config
150            .hypervisor
151            .with_vtl2
152            .as_mut()
153            .unwrap()
154            .late_map_vtl0_memory =
155            (!allow).then_some(openvmm_defs::config::LateMapVtl0MemoryPolicy::InjectException);
156
157        self
158    }
159}