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 hvlite_defs::config::Config;
19use hvlite_defs::config::DeviceVtl;
20use hvlite_defs::config::LoadMode;
21use hvlite_defs::config::VpciDeviceConfig;
22use hvlite_defs::config::Vtl2BaseAddressType;
23use tpm_resources::TpmDeviceHandle;
24use tpm_resources::TpmRegisterLayout;
25use vm_resource::IntoResource;
26use vmcore::non_volatile_store::resources::EphemeralNonVolatileStoreHandle;
27use vmotherboard::ChipsetDeviceHandle;
28use vtl2_settings_proto::Vtl2Settings;
29
30impl PetriVmConfigOpenVmm {
31    /// Enable the VTL0 alias map.
32    // TODO: Remove once #912 is fixed.
33    pub fn with_vtl0_alias_map(mut self) -> Self {
34        self.config
35            .hypervisor
36            .with_vtl2
37            .as_mut()
38            .expect("Not an openhcl config.")
39            .vtl0_alias_map = true;
40        self
41    }
42
43    /// Enable the TPM with ephemeral storage.
44    pub fn with_tpm(mut self) -> Self {
45        if self.firmware.is_openhcl() {
46            self.ged.as_mut().unwrap().enable_tpm = true;
47        } else {
48            self.config.chipset_devices.push(ChipsetDeviceHandle {
49                name: "tpm".to_string(),
50                resource: TpmDeviceHandle {
51                    ppi_store: EphemeralNonVolatileStoreHandle.into_resource(),
52                    nvram_store: EphemeralNonVolatileStoreHandle.into_resource(),
53                    refresh_tpm_seeds: false,
54                    ak_cert_type: tpm_resources::TpmAkCertTypeResource::None,
55                    register_layout: TpmRegisterLayout::IoPort,
56                    guest_secret_key: None,
57                    logger: None,
58                    is_confidential_vm: self.firmware.isolation().is_some(),
59                    // TODO: generate an actual BIOS GUID and put it here
60                    bios_guid: guid::guid!("00000000-0000-0000-0000-000000000000"),
61                }
62                .into_resource(),
63            });
64            if let LoadMode::Uefi { enable_tpm, .. } = &mut self.config.load_mode {
65                *enable_tpm = true;
66            }
67        }
68
69        self
70    }
71
72    /// Enable the battery for the VM.
73    pub fn with_battery(mut self) -> Self {
74        if self.firmware.is_openhcl() {
75            self.ged.as_mut().unwrap().enable_battery = true;
76        } else {
77            self.config.chipset_devices.push(ChipsetDeviceHandle {
78                name: "battery".to_string(),
79                resource: BatteryDeviceHandleX64 {
80                    battery_status_recv: {
81                        let (tx, rx) = mesh::channel();
82                        tx.send(HostBatteryUpdate::default_present());
83                        rx
84                    },
85                }
86                .into_resource(),
87            });
88            if let LoadMode::Uefi { enable_battery, .. } = &mut self.config.load_mode {
89                *enable_battery = true;
90            }
91        }
92        self
93    }
94
95    /// Enable TPM state persistence
96    pub fn with_tpm_state_persistence(mut self, tpm_state_persistence: bool) -> Self {
97        if !self.firmware.is_openhcl() {
98            panic!("TPM state persistence is only supported for OpenHCL.")
99        };
100
101        let ged = self.ged.as_mut().expect("No GED to configure TPM");
102
103        // Disable no_persistent_secrets implies preserving TPM states
104        // across boots
105        ged.no_persistent_secrets = !tpm_state_persistence;
106
107        self
108    }
109
110    /// Set test config for the GED's IGVM attest request handler
111    pub fn with_igvm_attest_test_config(mut self, config: IgvmAttestTestConfig) -> Self {
112        if !self.firmware.is_openhcl() {
113            panic!("IGVM Attest test config is only supported for OpenHCL.")
114        };
115
116        let ged = self.ged.as_mut().expect("No GED to configure TPM");
117
118        ged.igvm_attest_test_config = Some(config);
119
120        self
121    }
122
123    /// Enable a synthnic for the VM.
124    ///
125    /// Uses a mana emulator and the paravisor if a paravisor is present.
126    pub fn with_nic(mut self) -> Self {
127        let endpoint =
128            net_backend_resources::consomme::ConsommeHandle { cidr: None }.into_resource();
129        if self.resources.vtl2_settings.is_some() {
130            self.config.vpci_devices.push(VpciDeviceConfig {
131                vtl: DeviceVtl::Vtl2,
132                instance_id: MANA_INSTANCE,
133                resource: GdmaDeviceHandle {
134                    vports: vec![VportDefinition {
135                        mac_address: NIC_MAC_ADDRESS,
136                        endpoint,
137                    }],
138                }
139                .into_resource(),
140            });
141
142            self.resources
143                .vtl2_settings
144                .as_mut()
145                .unwrap()
146                .dynamic
147                .as_mut()
148                .unwrap()
149                .nic_devices
150                .push(vtl2_settings_proto::NicDeviceLegacy {
151                    instance_id: MANA_INSTANCE.to_string(),
152                    subordinate_instance_id: None,
153                    max_sub_channels: None,
154                });
155        } else {
156            const NETVSP_INSTANCE: guid::Guid = guid::guid!("c6c46cc3-9302-4344-b206-aef65e5bd0a2");
157            self.config.vmbus_devices.push((
158                DeviceVtl::Vtl0,
159                netvsp_resources::NetvspHandle {
160                    instance_id: NETVSP_INSTANCE,
161                    mac_address: NIC_MAC_ADDRESS,
162                    endpoint,
163                    max_queues: None,
164                }
165                .into_resource(),
166            ));
167        }
168
169        self
170    }
171
172    /// Add custom VTL 2 settings.
173    // TODO: At some point we want to replace uses of this with nicer with_disk,
174    // with_nic, etc. methods.
175    pub fn with_custom_vtl2_settings(mut self, f: impl FnOnce(&mut Vtl2Settings)) -> Self {
176        f(self
177            .resources
178            .vtl2_settings
179            .as_mut()
180            .expect("Custom VTL 2 settings are only supported with OpenHCL."));
181        self
182    }
183
184    /// Load with the specified VTL2 relocation mode.
185    pub fn with_vtl2_relocation_mode(mut self, mode: Vtl2BaseAddressType) -> Self {
186        let LoadMode::Igvm {
187            vtl2_base_address, ..
188        } = &mut self.config.load_mode
189        else {
190            panic!("vtl2 relocation mode is only supported for OpenHCL firmware")
191        };
192        *vtl2_base_address = mode;
193        self
194    }
195
196    /// This is intended for special one-off use cases. As soon as something
197    /// is needed in multiple tests we should consider making it a supported
198    /// pattern.
199    pub fn with_custom_config(mut self, f: impl FnOnce(&mut Config)) -> Self {
200        f(&mut self.config);
201        self
202    }
203
204    /// Specifies whether VTL2 should be allowed to access VTL0 memory before it
205    /// sets any VTL protections.
206    ///
207    /// This is needed just for the TMK VMM, and only until it gains support for
208    /// setting VTL protections.
209    pub fn with_allow_early_vtl0_access(mut self, allow: bool) -> Self {
210        self.config
211            .hypervisor
212            .with_vtl2
213            .as_mut()
214            .unwrap()
215            .late_map_vtl0_memory =
216            (!allow).then_some(hvlite_defs::config::LateMapVtl0MemoryPolicy::InjectException);
217
218        self
219    }
220}