1use crate::build_openhcl_igvm_from_recipe::OpenhclIgvmRecipe;
8use crate::download_openvmm_deps::OpenvmmDepsArch;
9use crate::download_release_igvm_files_from_gh::OpenhclReleaseVersion;
10use crate::download_uefi_mu_msvm::MuMsvmArch;
11use flowey::node::prelude::*;
12use std::collections::BTreeMap;
13
14flowey_request! {
15 pub struct Request {
16 pub test_content_dir: ReadVar<PathBuf>,
19 pub disk_images_dir: Option<ReadVar<PathBuf>>,
21 pub vmm_tests_target: target_lexicon::Triple,
26
27 pub register_openvmm: Option<ReadVar<crate::build_openvmm::OpenvmmOutput>>,
29 pub register_pipette_windows: Option<ReadVar<crate::build_pipette::PipetteOutput>>,
31 pub register_pipette_linux_musl: Option<ReadVar<crate::build_pipette::PipetteOutput>>,
33 pub register_guest_test_uefi:
35 Option<ReadVar<crate::build_guest_test_uefi::GuestTestUefiOutput>>,
36 pub register_openhcl_igvm_files: Option<
38 ReadVar<
39 Vec<(
40 OpenhclIgvmRecipe,
41 crate::run_igvmfilegen::IgvmOutput,
42 )>,
43 >,
44 >,
45 pub register_tmks: Option<ReadVar<crate::build_tmks::TmksOutput>>,
47 pub register_tmk_vmm: Option<ReadVar<crate::build_tmk_vmm::TmkVmmOutput>>,
49 pub register_tmk_vmm_linux_musl: Option<ReadVar<crate::build_tmk_vmm::TmkVmmOutput>>,
51 pub register_vmgstool: Option<ReadVar<crate::build_vmgstool::VmgstoolOutput>>,
53
54 pub get_test_log_path: Option<WriteVar<PathBuf>>,
56 pub get_env: WriteVar<BTreeMap<String, String>>,
58 pub release_igvm_files: Option<ReadVar<crate::download_release_igvm_files_from_gh::ReleaseOutput>>,
59 pub use_relative_paths: bool,
61 }
62}
63
64new_simple_flow_node!(struct Node);
65
66impl SimpleFlowNode for Node {
67 type Request = Request;
68
69 fn imports(ctx: &mut ImportCtx<'_>) {
70 ctx.import::<crate::download_openvmm_deps::Node>();
71 ctx.import::<crate::git_checkout_openvmm_repo::Node>();
72 ctx.import::<crate::download_uefi_mu_msvm::Node>();
73 }
74
75 fn process_request(request: Self::Request, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
76 let Request {
77 test_content_dir,
78 vmm_tests_target,
79 register_openvmm,
80 register_pipette_windows,
81 register_pipette_linux_musl,
82 register_guest_test_uefi,
83 register_tmks,
84 register_tmk_vmm,
85 register_tmk_vmm_linux_musl,
86 register_vmgstool,
87 disk_images_dir,
88 register_openhcl_igvm_files,
89 get_test_log_path,
90 get_env,
91 release_igvm_files,
92 use_relative_paths,
93 } = request;
94
95 let openvmm_deps_arch = match vmm_tests_target.architecture {
96 target_lexicon::Architecture::X86_64 => OpenvmmDepsArch::X86_64,
97 target_lexicon::Architecture::Aarch64(_) => OpenvmmDepsArch::Aarch64,
98 arch => anyhow::bail!("unsupported arch {arch}"),
99 };
100
101 let test_linux_initrd = ctx.reqv(|v| {
102 crate::download_openvmm_deps::Request::GetLinuxTestInitrd(openvmm_deps_arch, v)
103 });
104 let test_linux_kernel = ctx.reqv(|v| {
105 crate::download_openvmm_deps::Request::GetLinuxTestKernel(openvmm_deps_arch, v)
106 });
107
108 let mu_msvm_arch = match vmm_tests_target.architecture {
109 target_lexicon::Architecture::X86_64 => MuMsvmArch::X86_64,
110 target_lexicon::Architecture::Aarch64(_) => MuMsvmArch::Aarch64,
111 arch => anyhow::bail!("unsupported arch {arch}"),
112 };
113 let uefi = ctx.reqv(|v| crate::download_uefi_mu_msvm::Request::GetMsvmFd {
114 arch: mu_msvm_arch,
115 msvm_fd: v,
116 });
117
118 ctx.emit_rust_step("setting up vmm_tests env", |ctx| {
119 let test_content_dir = test_content_dir.claim(ctx);
120 let get_env = get_env.claim(ctx);
121 let get_test_log_path = get_test_log_path.claim(ctx);
122 let openvmm = register_openvmm.claim(ctx);
123 let pipette_win = register_pipette_windows.claim(ctx);
124 let pipette_linux = register_pipette_linux_musl.claim(ctx);
125 let guest_test_uefi = register_guest_test_uefi.claim(ctx);
126 let tmks = register_tmks.claim(ctx);
127 let tmk_vmm = register_tmk_vmm.claim(ctx);
128 let tmk_vmm_linux_musl = register_tmk_vmm_linux_musl.claim(ctx);
129 let vmgstool = register_vmgstool.claim(ctx);
130 let disk_image_dir = disk_images_dir.claim(ctx);
131 let openhcl_igvm_files = register_openhcl_igvm_files.claim(ctx);
132 let test_linux_initrd = test_linux_initrd.claim(ctx);
133 let test_linux_kernel = test_linux_kernel.claim(ctx);
134 let uefi = uefi.claim(ctx);
135 let release_igvm_files_dir = release_igvm_files.claim(ctx);
136 move |rt| {
137 let test_linux_initrd = rt.read(test_linux_initrd);
138 let test_linux_kernel = rt.read(test_linux_kernel);
139 let uefi = rt.read(uefi);
140 let release_igvm_files_dir = rt.read(release_igvm_files_dir);
141 let test_content_dir = rt.read(test_content_dir);
142
143 let mut env = BTreeMap::new();
144
145 let windows_via_wsl2 = flowey_lib_common::_util::running_in_wsl(rt)
146 && matches!(
147 vmm_tests_target.operating_system,
148 target_lexicon::OperatingSystem::Windows
149 );
150
151 let working_dir_ref = test_content_dir.as_path();
152 let working_dir_win = windows_via_wsl2.then(|| {
153 flowey_lib_common::_util::wslpath::linux_to_win(working_dir_ref)
154 .display()
155 .to_string()
156 });
157 let maybe_convert_path = |path: &Path| -> anyhow::Result<String> {
158 let path = if windows_via_wsl2 {
159 flowey_lib_common::_util::wslpath::linux_to_win(path)
160 } else {
161 path.absolute()
162 .with_context(|| format!("invalid path {}", path.display()))?
163 };
164 let path = if use_relative_paths {
165 if windows_via_wsl2 {
166 let working_dir_trimmed =
167 working_dir_win.as_ref().unwrap().trim_end_matches('\\');
168 let path_win = path.display().to_string();
169 let path_trimmed = path_win.trim_end_matches('\\');
170 PathBuf::from(format!(
171 "$PSScriptRoot{}",
172 path_trimmed
173 .strip_prefix(working_dir_trimmed)
174 .with_context(|| format!(
175 "{} not in {}",
176 path_win, working_dir_trimmed
177 ),)?
178 ))
179 } else {
180 path.strip_prefix(working_dir_ref)
181 .with_context(|| {
182 format!(
183 "{} not in {}",
184 path.display(),
185 working_dir_ref.display()
186 )
187 })?
188 .to_path_buf()
189 }
190 } else {
191 path
192 };
193 Ok(path.display().to_string())
194 };
195
196 env.insert(
197 "VMM_TESTS_CONTENT_DIR".into(),
198 maybe_convert_path(&test_content_dir)?,
199 );
200
201 let test_log_dir = test_content_dir.join("test_results");
203 if !test_log_dir.exists() {
204 fs_err::create_dir(&test_log_dir)?
205 };
206 env.insert(
207 "TEST_OUTPUT_PATH".into(),
208 maybe_convert_path(&test_log_dir)?,
209 );
210
211 if let Some(disk_image_dir) = disk_image_dir {
212 env.insert(
213 "VMM_TEST_IMAGES".into(),
214 maybe_convert_path(&rt.read(disk_image_dir))?,
215 );
216 }
217
218 if let Some(openvmm) = openvmm {
219 match rt.read(openvmm) {
221 crate::build_openvmm::OpenvmmOutput::WindowsBin { exe, pdb: _ } => {
222 fs_err::copy(exe, test_content_dir.join("openvmm.exe"))?;
223 }
224 crate::build_openvmm::OpenvmmOutput::LinuxBin { bin, dbg: _ } => {
225 let dst = test_content_dir.join("openvmm");
226 fs_err::copy(bin, dst.clone())?;
227 dst.make_executable()?;
228 }
229 }
230 }
231
232 if let Some(pipette_win) = pipette_win {
233 match rt.read(pipette_win) {
234 crate::build_pipette::PipetteOutput::WindowsBin { exe, pdb: _ } => {
235 fs_err::copy(exe, test_content_dir.join("pipette.exe"))?;
236 }
237 _ => anyhow::bail!("did not find `pipette.exe` in RegisterPipetteWindows"),
238 }
239 }
240
241 if let Some(pipette_linux) = pipette_linux {
242 match rt.read(pipette_linux) {
243 crate::build_pipette::PipetteOutput::LinuxBin { bin, dbg: _ } => {
244 fs_err::copy(bin, test_content_dir.join("pipette"))?;
245 }
246 _ => {
247 anyhow::bail!("did not find `pipette.exe` in RegisterPipetteLinuxMusl")
248 }
249 }
250 }
251
252 if let Some(guest_test_uefi) = guest_test_uefi {
253 let crate::build_guest_test_uefi::GuestTestUefiOutput {
254 efi: _,
255 pdb: _,
256 img,
257 } = rt.read(guest_test_uefi);
258 fs_err::copy(img, test_content_dir.join("guest_test_uefi.img"))?;
259 }
260
261 if let Some(tmks) = tmks {
262 let crate::build_tmks::TmksOutput { bin, dbg: _ } = rt.read(tmks);
263 fs_err::copy(bin, test_content_dir.join("simple_tmk"))?;
264 }
265
266 if let Some(tmk_vmm) = tmk_vmm {
267 match rt.read(tmk_vmm) {
268 crate::build_tmk_vmm::TmkVmmOutput::WindowsBin { exe, .. } => {
269 fs_err::copy(exe, test_content_dir.join("tmk_vmm.exe"))?;
270 }
271 crate::build_tmk_vmm::TmkVmmOutput::LinuxBin { bin, .. } => {
272 let dst = test_content_dir.join("tmk_vmm");
273 fs_err::copy(bin, &dst)?;
274 dst.make_executable()?;
275 }
276 }
277 }
278
279 if let Some(tmk_vmm_linux_musl) = tmk_vmm_linux_musl {
280 let crate::build_tmk_vmm::TmkVmmOutput::LinuxBin { bin, dbg: _ } =
281 rt.read(tmk_vmm_linux_musl)
282 else {
283 anyhow::bail!("invalid tmk_vmm output")
284 };
285 fs_err::copy(bin, test_content_dir.join("tmk_vmm"))?;
289 }
290
291 if let Some(vmgstool) = vmgstool {
292 match rt.read(vmgstool) {
293 crate::build_vmgstool::VmgstoolOutput::WindowsBin { exe, .. } => {
294 fs_err::copy(exe, test_content_dir.join("vmgstool.exe"))?;
295 }
296 crate::build_vmgstool::VmgstoolOutput::LinuxBin { bin, .. } => {
297 let dst = test_content_dir.join("vmgstool");
298 fs_err::copy(bin, &dst)?;
299 dst.make_executable()?;
300 }
301 }
302 }
303
304 if let Some(openhcl_igvm_files) = openhcl_igvm_files {
305 for (recipe, openhcl_igvm) in rt.read(openhcl_igvm_files) {
306 let crate::run_igvmfilegen::IgvmOutput { igvm_bin, .. } = openhcl_igvm;
307
308 let filename = match recipe {
309 OpenhclIgvmRecipe::X64 => "openhcl-x64.bin",
310 OpenhclIgvmRecipe::X64Devkern => "openhcl-x64-devkern.bin",
311 OpenhclIgvmRecipe::X64Cvm => "openhcl-x64-cvm.bin",
312 OpenhclIgvmRecipe::X64TestLinuxDirect => {
313 "openhcl-x64-test-linux-direct.bin"
314 }
315 OpenhclIgvmRecipe::Aarch64 => "openhcl-aarch64.bin",
316 OpenhclIgvmRecipe::Aarch64Devkern => "openhcl-aarch64-devkern.bin",
317 _ => {
318 log::info!("petri doesn't support this OpenHCL recipe: {recipe:?}");
319 continue;
320 }
321 };
322
323 fs_err::copy(igvm_bin, test_content_dir.join(filename))?;
324 }
325 }
326
327 if let Some(release_igvm_files) = release_igvm_files_dir {
328 let latest_release_version = OpenhclReleaseVersion::latest();
329
330 if let Some(src) = &release_igvm_files.openhcl {
331 let new_name = format!("{latest_release_version}-x64-openhcl.bin");
332 fs_err::copy(src, test_content_dir.join(new_name))?;
333 }
334
335 if let Some(src) = &release_igvm_files.openhcl_aarch64 {
336 let new_name = format!("{latest_release_version}-aarch64-openhcl.bin");
337 fs_err::copy(src, test_content_dir.join(new_name))?;
338 }
339
340 if let Some(src) = &release_igvm_files.openhcl_direct {
341 let new_name = format!("{latest_release_version}-x64-direct-openhcl.bin");
342 fs_err::copy(src, test_content_dir.join(new_name))?;
343 }
344 }
345
346 let (arch_dir, kernel_file_name) = match openvmm_deps_arch {
347 OpenvmmDepsArch::X86_64 => ("x64", "vmlinux"),
348 OpenvmmDepsArch::Aarch64 => ("aarch64", "Image"),
349 };
350 fs_err::create_dir_all(test_content_dir.join(arch_dir))?;
351 fs_err::copy(
352 test_linux_initrd,
353 test_content_dir.join(arch_dir).join("initrd"),
354 )?;
355 fs_err::copy(
356 test_linux_kernel,
357 test_content_dir.join(arch_dir).join(kernel_file_name),
358 )?;
359
360 let uefi_dir = test_content_dir
361 .join(format!(
362 "hyperv.uefi.mscoreuefi.{}.RELEASE",
363 match mu_msvm_arch {
364 MuMsvmArch::Aarch64 => "AARCH64",
365 MuMsvmArch::X86_64 => "x64",
366 }
367 ))
368 .join(format!(
369 "Msvm{}",
370 match mu_msvm_arch {
371 MuMsvmArch::Aarch64 => "AARCH64",
372 MuMsvmArch::X86_64 => "X64",
373 }
374 ))
375 .join("RELEASE_VS2022")
376 .join("FV");
377 fs_err::create_dir_all(&uefi_dir)?;
378 fs_err::copy(uefi, uefi_dir.join("MSVM.fd"))?;
379
380 log::debug!("final folder content: {}", test_content_dir.display());
382 for entry in test_content_dir.read_dir()? {
383 let entry = entry?;
384 log::debug!("contains: {:?}", entry.file_name());
385 }
386
387 rt.write(get_env, &env);
388
389 if let Some(var) = get_test_log_path {
390 rt.write(var, &test_log_dir)
391 }
392
393 Ok(())
394 }
395 });
396
397 Ok(())
398 }
399}