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