petri/vm/openvmm/
start.rs1use super::PetriVmConfigOpenVmm;
7use super::PetriVmOpenVmm;
8use super::PetriVmResourcesOpenVmm;
9use crate::OpenvmmLogConfig;
10use crate::PetriLogFile;
11use crate::PetriVmRuntimeConfig;
12use crate::worker::Worker;
13use anyhow::Context;
14use mesh_process::Mesh;
15use mesh_process::ProcessConfig;
16use mesh_worker::WorkerHost;
17use openvmm_defs::config::DeviceVtl;
18use pal_async::pipe::PolledPipe;
19use pal_async::task::Spawn;
20use petri_artifacts_common::tags::MachineArch;
21use petri_artifacts_common::tags::OsFlavor;
22use std::collections::BTreeMap;
23use std::ffi::OsString;
24use std::io::Write;
25use std::sync::Arc;
26use vm_resource::IntoResource;
27
28impl PetriVmConfigOpenVmm {
29 async fn run_core(self) -> anyhow::Result<(PetriVmOpenVmm, PetriVmRuntimeConfig)> {
30 let Self {
31 runtime_config,
32 arch,
33 host_log_levels,
34 mut config,
35
36 mesh,
37
38 mut resources,
39
40 openvmm_log_file,
41
42 ged,
43 framebuffer_view,
44 } = self;
45
46 let has_pcie = !config.pcie_root_complexes.is_empty();
47
48 let supports_save_restore = !resources.properties.is_openhcl
54 && !resources.properties.is_pcat
55 && !matches!(arch, MachineArch::Aarch64)
56 && !resources.properties.using_vpci
57 && !has_pcie;
58
59 if let Some(mut ged) = ged {
61 ged.vtl2_settings = Some(prost::Message::encode_to_vec(
62 runtime_config.vtl2_settings.as_ref().unwrap(),
63 ));
64 config
65 .vmbus_devices
66 .push((DeviceVtl::Vtl2, ged.into_resource()));
67 }
68
69 tracing::debug!(?config, "OpenVMM config");
70
71 let log_env = match host_log_levels {
72 None | Some(OpenvmmLogConfig::TestDefault) => BTreeMap::<OsString, OsString>::from([
73 ("OPENVMM_LOG".into(), "debug".into()),
74 ("OPENVMM_SHOW_SPANS".into(), "true".into()),
75 ]),
76 Some(OpenvmmLogConfig::BuiltInDefault) => BTreeMap::new(),
77 Some(OpenvmmLogConfig::Custom(levels)) => levels
78 .iter()
79 .map(|(k, v)| (OsString::from(k), OsString::from(v)))
80 .collect::<BTreeMap<OsString, OsString>>(),
81 };
82
83 let host = Self::openvmm_host(&mut resources, &mesh, openvmm_log_file, log_env)
84 .await
85 .context("failed to create host process")?;
86 let (worker, halt_notif) = Worker::launch(&host, config)
87 .await
88 .context("failed to launch vm worker")?;
89
90 let worker = Arc::new(worker);
91
92 let mut vm = PetriVmOpenVmm::new(
93 super::runtime::PetriVmInner {
94 resources,
95 mesh,
96 worker,
97 framebuffer_view,
98 },
99 halt_notif,
100 );
101
102 tracing::info!("Resuming VM");
103 vm.resume().await?;
104
105 if supports_save_restore {
107 tracing::info!("Testing save/restore");
108 vm.verify_save_restore().await?;
109 }
110
111 tracing::info!("VM ready");
112 Ok((vm, runtime_config))
113 }
114
115 pub async fn run(mut self) -> anyhow::Result<(PetriVmOpenVmm, PetriVmRuntimeConfig)> {
118 let launch_linux_direct_pipette = if self.resources.properties.using_vtl0_pipette {
119 if matches!(self.resources.properties.os_flavor, OsFlavor::Windows)
120 && !self.resources.properties.is_isolated
121 {
122 let mut imc_hive_file =
125 tempfile::tempfile().context("failed to create temp file")?;
126 imc_hive_file
127 .write_all(include_bytes!("../../../guest-bootstrap/imc.hiv"))
128 .context("failed to write imc hive")?;
129
130 self.config.vmbus_devices.push((
132 DeviceVtl::Vtl0,
133 vmbfs_resources::VmbfsImcDeviceHandle {
134 file: imc_hive_file,
135 }
136 .into_resource(),
137 ));
138 }
139
140 self.resources.properties.is_linux_direct
141 } else {
142 false
143 };
144
145 let (mut vm, config) = self.run_core().await?;
147
148 if launch_linux_direct_pipette {
149 vm.launch_linux_direct_pipette().await?;
150 }
151
152 Ok((vm, config))
153 }
154
155 async fn openvmm_host(
156 resources: &mut PetriVmResourcesOpenVmm,
157 mesh: &Mesh,
158 log_file: PetriLogFile,
159 vmm_env: BTreeMap<OsString, OsString>,
160 ) -> anyhow::Result<WorkerHost> {
161 let (stderr_read, stderr_write) = pal::pipe_pair()?;
164 let task = resources.driver.spawn(
165 "serial log",
166 crate::log_task(
167 log_file,
168 PolledPipe::new(&resources.driver, stderr_read)
169 .context("failed to create polled pipe")?,
170 "openvmm stderr",
171 ),
172 );
173 resources.log_stream_tasks.push(task);
174
175 let (host, runner) = mesh_worker::worker_host();
176 mesh.launch_host(
177 ProcessConfig::new("vmm")
178 .process_name(&resources.openvmm_path)
179 .stderr(Some(stderr_write))
180 .env(vmm_env.into_iter()),
181 openvmm_defs::entrypoint::MeshHostParams { runner },
182 )
183 .await?;
184 Ok(host)
185 }
186}