1mod construct;
12mod modify;
13mod runtime;
14mod start;
15
16pub use runtime::PetriVmOpenVmm;
17
18use super::ProcessorTopology;
19use crate::Firmware;
20use crate::PetriLogFile;
21use crate::PetriLogSource;
22use crate::PetriVm;
23use crate::PetriVmConfig;
24use crate::disk_image::AgentImage;
25use crate::linux_direct_serial_agent::LinuxDirectSerialAgent;
26use crate::openhcl_diag::OpenHclDiagHandler;
27use anyhow::Context;
28use async_trait::async_trait;
29use disk_backend_resources::LayeredDiskHandle;
30use disk_backend_resources::layer::DiskLayerHandle;
31use disk_backend_resources::layer::RamDiskLayerHandle;
32use framebuffer::FramebufferAccess;
33use get_resources::ged::FirmwareEvent;
34use guid::Guid;
35use hvlite_defs::config::Config;
36use hvlite_helpers::disk::open_disk_type;
37use hyperv_ic_resources::shutdown::ShutdownRpc;
38use mesh::Receiver;
39use mesh::Sender;
40use net_backend_resources::mac_address::MacAddress;
41use pal_async::DefaultDriver;
42use pal_async::socket::PolledSocket;
43use pal_async::task::Task;
44use petri_artifacts_common::tags::MachineArch;
45use petri_artifacts_common::tags::OsFlavor;
46use petri_artifacts_core::ArtifactResolver;
47use petri_artifacts_core::ResolvedArtifact;
48use pipette_client::PipetteClient;
49use std::path::PathBuf;
50use tempfile::TempPath;
51use unix_socket::UnixListener;
52use vm_resource::IntoResource;
53use vm_resource::Resource;
54use vm_resource::kind::DiskHandleKind;
55use vtl2_settings_proto::Vtl2Settings;
56
57pub(crate) const SCSI_INSTANCE: Guid = guid::guid!("27b553e8-8b39-411b-a55f-839971a7884f");
59
60pub(crate) const BOOT_NVME_INSTANCE: Guid = guid::guid!("92bc8346-718b-449a-8751-edbf3dcd27e4");
62
63const MANA_INSTANCE: Guid = guid::guid!("f9641cf4-d915-4743-a7d8-efa75db7b85a");
65
66pub(crate) const BOOT_NVME_NSID: u32 = 37;
68
69pub(crate) const BOOT_NVME_LUN: u32 = 1;
71
72pub const NIC_MAC_ADDRESS: MacAddress = MacAddress::new([0x00, 0x15, 0x5D, 0x12, 0x12, 0x12]);
74
75pub struct PetriVmArtifactsOpenVmm {
78 firmware: Firmware,
79 arch: MachineArch,
80 agent_image: AgentImage,
81 openhcl_agent_image: Option<AgentImage>,
82 openvmm_path: ResolvedArtifact,
83}
84
85impl PetriVmArtifactsOpenVmm {
86 pub fn new(
90 resolver: &ArtifactResolver<'_>,
91 firmware: Firmware,
92 arch: MachineArch,
93 ) -> Option<Self> {
94 if arch != MachineArch::host() {
95 return None;
96 }
97 if firmware.is_openhcl() {
98 if !cfg!(windows) || arch != MachineArch::X86_64 {
100 return None;
101 }
102 }
103 let agent_image = AgentImage::new(resolver, arch, firmware.os_flavor());
104 let openhcl_agent_image = if firmware.is_openhcl() {
105 Some(AgentImage::new(resolver, arch, OsFlavor::Linux))
106 } else {
107 None
108 };
109 Some(Self {
110 firmware,
111 arch,
112 agent_image,
113 openhcl_agent_image,
114 openvmm_path: resolver
115 .require(petri_artifacts_vmm_test::artifacts::OPENVMM_NATIVE)
116 .erase(),
117 })
118 }
119}
120
121pub struct PetriVmConfigOpenVmm {
123 firmware: Firmware,
125 arch: MachineArch,
126 config: Config,
127
128 resources: PetriVmResourcesOpenVmm,
130
131 openvmm_log_file: PetriLogFile,
133
134 ged: Option<get_resources::ged::GuestEmulationDeviceHandle>,
136 vtl2_settings: Option<Vtl2Settings>,
137 framebuffer_access: Option<FramebufferAccess>,
138}
139
140#[async_trait]
141impl PetriVmConfig for PetriVmConfigOpenVmm {
142 async fn run_without_agent(self: Box<Self>) -> anyhow::Result<Box<dyn PetriVm>> {
143 Ok(Box::new(Self::run_without_agent(*self).await?))
144 }
145
146 async fn run_with_lazy_pipette(mut self: Box<Self>) -> anyhow::Result<Box<dyn PetriVm>> {
147 Ok(Box::new(Self::run_with_lazy_pipette(*self).await?))
148 }
149
150 async fn run(self: Box<Self>) -> anyhow::Result<(Box<dyn PetriVm>, PipetteClient)> {
151 let (vm, client) = Self::run(*self).await?;
152 Ok((Box::new(vm), client))
153 }
154
155 fn with_secure_boot(self: Box<Self>) -> Box<dyn PetriVmConfig> {
156 Box::new(Self::with_secure_boot(*self))
157 }
158
159 fn with_windows_secure_boot_template(self: Box<Self>) -> Box<dyn PetriVmConfig> {
160 Box::new(Self::with_windows_secure_boot_template(*self))
161 }
162
163 fn with_uefi_ca_secure_boot_template(self: Box<Self>) -> Box<dyn PetriVmConfig> {
164 Box::new(Self::with_uefi_ca_secure_boot_template(*self))
165 }
166
167 fn with_processor_topology(
168 self: Box<Self>,
169 topology: ProcessorTopology,
170 ) -> Box<dyn PetriVmConfig> {
171 Box::new(Self::with_processor_topology(*self, topology))
172 }
173
174 fn with_custom_openhcl(self: Box<Self>, artifact: ResolvedArtifact) -> Box<dyn PetriVmConfig> {
175 Box::new(Self::with_custom_openhcl(*self, artifact))
176 }
177
178 fn with_openhcl_command_line(self: Box<Self>, command_line: &str) -> Box<dyn PetriVmConfig> {
179 Box::new(Self::with_openhcl_command_line(*self, command_line))
180 }
181
182 fn with_agent_file(
183 self: Box<Self>,
184 name: &str,
185 artifact: ResolvedArtifact,
186 ) -> Box<dyn PetriVmConfig> {
187 Box::new(Self::with_agent_file(*self, name, artifact))
188 }
189
190 fn with_openhcl_agent_file(
191 self: Box<Self>,
192 name: &str,
193 artifact: ResolvedArtifact,
194 ) -> Box<dyn PetriVmConfig> {
195 Box::new(Self::with_openhcl_agent_file(*self, name, artifact))
196 }
197
198 fn with_uefi_frontpage(self: Box<Self>, enable: bool) -> Box<dyn PetriVmConfig> {
199 Box::new(Self::with_uefi_frontpage(*self, enable))
200 }
201
202 fn with_vmbus_redirect(self: Box<Self>, _: bool) -> Box<dyn PetriVmConfig> {
203 Box::new(Self::with_vmbus_redirect(*self))
204 }
205
206 fn os_flavor(&self) -> OsFlavor {
207 self.firmware.os_flavor()
208 }
209}
210
211struct PetriVmResourcesOpenVmm {
213 log_stream_tasks: Vec<Task<anyhow::Result<()>>>,
214 firmware_event_recv: Receiver<FirmwareEvent>,
215 shutdown_ic_send: Sender<ShutdownRpc>,
216 kvp_ic_send: Sender<hyperv_ic_resources::kvp::KvpConnectRpc>,
217 expected_boot_event: Option<FirmwareEvent>,
218 ged_send: Option<Sender<get_resources::ged::GuestEmulationRequest>>,
219 pipette_listener: PolledSocket<UnixListener>,
220 vtl2_pipette_listener: Option<PolledSocket<UnixListener>>,
221 openhcl_diag_handler: Option<OpenHclDiagHandler>,
222 linux_direct_serial_agent: Option<LinuxDirectSerialAgent>,
223
224 driver: DefaultDriver,
226 agent_image: AgentImage,
227 openhcl_agent_image: Option<AgentImage>,
228 openvmm_path: ResolvedArtifact,
229 output_dir: PathBuf,
230 log_source: PetriLogSource,
231
232 vtl2_vsock_path: Option<TempPath>,
234 _vmbus_vsock_path: TempPath,
235}
236
237impl PetriVmConfigOpenVmm {
238 pub fn os_flavor(&self) -> OsFlavor {
240 self.firmware.os_flavor()
241 }
242}
243
244fn memdiff_disk_from_artifact(
245 artifact: &ResolvedArtifact,
246) -> anyhow::Result<Resource<DiskHandleKind>> {
247 let path = artifact.as_ref();
248 let disk = open_disk_type(path, true)
249 .with_context(|| format!("failed to open disk: {}", path.display()))?;
250 Ok(LayeredDiskHandle {
251 layers: vec![
252 RamDiskLayerHandle { len: None }.into_resource().into(),
253 DiskLayerHandle(disk).into_resource().into(),
254 ],
255 }
256 .into_resource())
257}