1#![forbid(unsafe_code)]
7
8use petri_artifacts_common::tags::MachineArch;
9use petri_artifacts_core::ArtifactSource;
10use petri_artifacts_core::AsArtifactHandle;
11use petri_artifacts_core::ErasedArtifactHandle;
12use std::env::consts::EXE_EXTENSION;
13use std::path::Path;
14use std::path::PathBuf;
15use vmm_test_images::CONTAINER;
16use vmm_test_images::KnownTestArtifacts;
17use vmm_test_images::STORAGE_ACCOUNT;
18
19pub fn cargo_build_profile() -> &'static str {
26 static PROFILE: std::sync::OnceLock<String> = std::sync::OnceLock::new();
27 PROFILE.get_or_init(|| {
28 if let Ok(exe) = std::env::current_exe() {
29 if exe.components().any(|c| c.as_os_str() == "release") {
30 return "release".to_string();
31 }
32 }
33 "debug".to_string()
34 })
35}
36
37pub struct OpenvmmKnownPathsTestArtifactResolver<'a>(&'a str);
41
42impl<'a> OpenvmmKnownPathsTestArtifactResolver<'a> {
43 pub fn new(test_name: &'a str) -> Self {
45 Self(test_name)
46 }
47}
48
49impl petri_artifacts_core::ResolveTestArtifact for OpenvmmKnownPathsTestArtifactResolver<'_> {
50 #[rustfmt::skip]
51 fn resolve(&self, id: ErasedArtifactHandle) -> anyhow::Result<PathBuf> {
52 use petri_artifacts_common::artifacts as common;
53 use petri_artifacts_vmm_test::artifacts::*;
54 use petri_artifacts_vmm_test::tags::IsHostedOnHvliteAzureBlobStore;
55
56 match id {
57 _ if id == common::PIPETTE_WINDOWS_X64 => pipette_path(MachineArch::X86_64, PipetteFlavor::Windows),
58 _ if id == common::PIPETTE_LINUX_X64 => pipette_path(MachineArch::X86_64, PipetteFlavor::Linux),
59 _ if id == common::PIPETTE_WINDOWS_AARCH64 => pipette_path(MachineArch::Aarch64, PipetteFlavor::Windows),
60 _ if id == common::PIPETTE_LINUX_AARCH64 => pipette_path(MachineArch::Aarch64, PipetteFlavor::Linux),
61
62 _ if id == common::TEST_LOG_DIRECTORY => test_log_directory_path(self.0),
63
64 _ if id == OPENVMM_NATIVE => openvmm_native_executable_path(),
65 #[cfg(target_os = "linux")]
66 _ if id == OPENVMM_VHOST_NATIVE => openvmm_vhost_native_executable_path(),
67
68 _ if id == loadable::LINUX_DIRECT_TEST_KERNEL_X64 => linux_direct_x64_test_kernel_path(),
69 _ if id == loadable::LINUX_DIRECT_TEST_BZIMAGE_X64 => linux_direct_x64_test_bzimage_path(),
70 _ if id == loadable::LINUX_DIRECT_TEST_KERNEL_AARCH64 => linux_direct_arm_image_path(),
71 _ if id == loadable::LINUX_DIRECT_TEST_INITRD_X64 => linux_direct_test_initrd_path(MachineArch::X86_64),
72 _ if id == loadable::LINUX_DIRECT_TEST_INITRD_AARCH64 => linux_direct_test_initrd_path(MachineArch::Aarch64),
73
74 _ if id == petritools::PETRITOOLS_EROFS_X64 => petritools_erofs_path(MachineArch::X86_64),
75 _ if id == petritools::PETRITOOLS_EROFS_AARCH64 => petritools_erofs_path(MachineArch::Aarch64),
76
77 _ if id == loadable::PCAT_FIRMWARE_X64 => pcat_firmware_path(),
78 _ if id == loadable::SVGA_FIRMWARE_X64 => svga_firmware_path(),
79 _ if id == loadable::UEFI_FIRMWARE_X64 => uefi_firmware_path(MachineArch::X86_64),
80 _ if id == loadable::UEFI_FIRMWARE_AARCH64 => uefi_firmware_path(MachineArch::Aarch64),
81
82 _ if id == openhcl_igvm::LATEST_STANDARD_X64 => openhcl_bin_path(MachineArch::X86_64, OpenhclVersion::Latest, OpenhclFlavor::Standard),
83 _ if id == openhcl_igvm::LATEST_STANDARD_DEV_KERNEL_X64 => openhcl_bin_path(MachineArch::X86_64, OpenhclVersion::Latest, OpenhclFlavor::StandardDevKernel),
84 _ if id == openhcl_igvm::LATEST_CVM_X64 => openhcl_bin_path(MachineArch::X86_64, OpenhclVersion::Latest, OpenhclFlavor::Cvm),
85 _ if id == openhcl_igvm::LATEST_LINUX_DIRECT_TEST_X64 => openhcl_bin_path(MachineArch::X86_64, OpenhclVersion::Latest, OpenhclFlavor::LinuxDirect),
86 _ if id == openhcl_igvm::LATEST_STANDARD_AARCH64 => openhcl_bin_path(MachineArch::Aarch64, OpenhclVersion::Latest, OpenhclFlavor::Standard),
87 _ if id == openhcl_igvm::LATEST_STANDARD_DEV_KERNEL_AARCH64 => openhcl_bin_path(MachineArch::Aarch64, OpenhclVersion::Latest, OpenhclFlavor::StandardDevKernel),
88
89 _ if id == openhcl_igvm::LATEST_RELEASE_STANDARD_X64 => openhcl_bin_path(MachineArch::X86_64, OpenhclVersion::Release2511, OpenhclFlavor::Standard),
90 _ if id == openhcl_igvm::LATEST_RELEASE_LINUX_DIRECT_X64 => openhcl_bin_path(MachineArch::X86_64, OpenhclVersion::Release2511, OpenhclFlavor::LinuxDirect),
91 _ if id == openhcl_igvm::LATEST_RELEASE_STANDARD_AARCH64 => openhcl_bin_path(MachineArch::Aarch64, OpenhclVersion::Release2511, OpenhclFlavor::Standard),
92
93 _ if id == openhcl_igvm::um_bin::LATEST_LINUX_DIRECT_TEST_X64 => openhcl_extras_path(OpenhclVersion::Latest,OpenhclFlavor::LinuxDirect,OpenhclExtras::UmBin),
94 _ if id == openhcl_igvm::um_dbg::LATEST_LINUX_DIRECT_TEST_X64 => openhcl_extras_path(OpenhclVersion::Latest,OpenhclFlavor::LinuxDirect,OpenhclExtras::UmDbg),
95
96 _ if id == test_vhd::GUEST_TEST_UEFI_X64 => guest_test_uefi_disk_path(MachineArch::X86_64),
97 _ if id == test_vhd::GUEST_TEST_UEFI_AARCH64 => guest_test_uefi_disk_path(MachineArch::Aarch64),
98
99 _ if id == test_vhd::GEN2_WINDOWS_DATA_CENTER_CORE2025_X64_PREPPED => {
100 let base_filename = test_vhd::GEN2_WINDOWS_DATA_CENTER_CORE2025_X64::FILENAME;
101 let prepped_filename = base_filename.replace(".vhd", "-prepped.vhd");
102 let images_dir = std::env::var("VMM_TEST_IMAGES");
103 let full_path = Path::new(images_dir.as_deref().unwrap_or("images"));
104 get_path(
105 full_path,
106 prepped_filename,
107 MissingCommand::Run {
108 description: "prepped test image",
109 package: "prep_steps",
110 },
111 )
112 }
113
114 _ if id == tmks::TMK_VMM_NATIVE => tmk_vmm_native_executable_path(),
115 _ if id == tmks::TMK_VMM_LINUX_X64_MUSL => tmk_vmm_paravisor_path(MachineArch::X86_64),
116 _ if id == tmks::TMK_VMM_LINUX_AARCH64_MUSL => tmk_vmm_paravisor_path(MachineArch::Aarch64),
117 _ if id == tmks::SIMPLE_TMK_X64 => simple_tmk_path(MachineArch::X86_64),
118 _ if id == tmks::SIMPLE_TMK_AARCH64 => simple_tmk_path(MachineArch::Aarch64),
119
120 _ if id == VMGSTOOL_NATIVE => vmgstool_native_executable_path(),
121
122 _ if id == guest_tools::TPM_GUEST_TESTS_WINDOWS_X64 => {
123 tpm_guest_tests_windows_path(MachineArch::X86_64)
124 }
125 _ if id == guest_tools::TPM_GUEST_TESTS_LINUX_X64 => {
126 tpm_guest_tests_linux_path(MachineArch::X86_64)
127 }
128
129 _ if id == host_tools::TEST_IGVM_AGENT_RPC_SERVER_WINDOWS_X64 => {
130 test_igvm_agent_rpc_server_windows_path(MachineArch::X86_64)
131 }
132
133 _ if let Some(artifact) = KnownTestArtifacts::from_handle(id) => {
135 get_test_artifact_path(artifact)
136 }
137
138 _ => anyhow::bail!("no support for given artifact type"),
139 }
140 }
141
142 fn resolve_source(&self, id: ErasedArtifactHandle) -> anyhow::Result<ArtifactSource> {
143 let local_err = match self.resolve(id) {
145 Ok(path) => return Ok(ArtifactSource::Local(path)),
146 Err(e) => e,
147 };
148
149 if let Some(artifact) = KnownTestArtifacts::from_handle(id) {
152 if artifact.supports_blob_disk() {
153 let url = format!(
154 "https://{STORAGE_ACCOUNT}.blob.core.windows.net/{CONTAINER}/{}",
155 artifact.filename()
156 );
157 return Ok(ArtifactSource::Remote { url });
158 }
159 }
160
161 Err(local_err)
163 }
164}
165
166pub fn resolve_bundle_name(id: ErasedArtifactHandle) -> Option<&'static str> {
177 use petri_artifacts_common::artifacts as common;
178 use petri_artifacts_vmm_test::artifacts::*;
179
180 match id {
181 _ if id == common::PIPETTE_LINUX_X64 => Some("pipette"),
182 _ if id == common::PIPETTE_LINUX_AARCH64 => Some("pipette"),
183 _ if id == common::PIPETTE_WINDOWS_X64 => Some("pipette.exe"),
184 _ if id == common::PIPETTE_WINDOWS_AARCH64 => Some("pipette.exe"),
185 _ if id == OPENVMM_NATIVE => Some(if cfg!(windows) {
186 "openvmm.exe"
187 } else {
188 "openvmm"
189 }),
190 _ if id == loadable::LINUX_DIRECT_TEST_KERNEL_X64 => Some("x64/vmlinux"),
191 _ if id == loadable::LINUX_DIRECT_TEST_BZIMAGE_X64 => Some("x64/bzImage"),
192 _ if id == loadable::LINUX_DIRECT_TEST_KERNEL_AARCH64 => Some("aarch64/Image"),
193 _ if id == loadable::LINUX_DIRECT_TEST_INITRD_X64 => Some("x64/initrd"),
194 _ if id == loadable::LINUX_DIRECT_TEST_INITRD_AARCH64 => Some("aarch64/initrd"),
195 _ if id == petritools::PETRITOOLS_EROFS_X64 => Some("x64/petritools.erofs"),
196 _ if id == petritools::PETRITOOLS_EROFS_AARCH64 => Some("aarch64/petritools.erofs"),
197 _ if id == loadable::UEFI_FIRMWARE_X64 => {
198 Some("hyperv.uefi.mscoreuefi.x64.RELEASE/MsvmX64/RELEASE_VS2022/FV/MSVM.fd")
199 }
200 _ if id == loadable::UEFI_FIRMWARE_AARCH64 => {
201 Some("hyperv.uefi.mscoreuefi.AARCH64.RELEASE/MsvmAARCH64/RELEASE_CLANGPDB/FV/MSVM.fd")
202 }
203 _ => {
204 None
208 }
209 }
210}
211
212#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
213enum PipetteFlavor {
214 Windows,
215 Linux,
216}
217
218#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
219enum OpenhclVersion {
220 Latest,
221 Release2511,
222}
223
224#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
225enum OpenhclFlavor {
226 Standard,
227 StandardDevKernel,
228 Cvm,
229 LinuxDirect,
230}
231
232#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
233enum OpenhclExtras {
234 UmBin,
235 UmDbg,
236}
237
238fn target_arch_path(arch: MachineArch) -> &'static str {
240 match arch {
241 MachineArch::X86_64 => "x86_64",
242 MachineArch::Aarch64 => "aarch64",
243 }
244}
245
246fn windows_msvc_target(arch: MachineArch) -> &'static str {
247 match arch {
248 MachineArch::X86_64 => "x86_64-pc-windows-msvc",
249 MachineArch::Aarch64 => "aarch64-pc-windows-msvc",
250 }
251}
252
253fn get_test_artifact_path(artifact: KnownTestArtifacts) -> Result<PathBuf, anyhow::Error> {
254 let images_dir = std::env::var("VMM_TEST_IMAGES");
255 let full_path = Path::new(images_dir.as_deref().unwrap_or("images"));
256
257 get_path(
258 full_path,
259 artifact.filename(),
260 MissingCommand::Xtask {
261 xtask_args: &[
262 "guest-test",
263 "download-image",
264 "--artifacts",
265 artifact.name(),
266 ],
267 description: "test artifact",
268 },
269 )
270}
271
272fn guest_test_uefi_disk_path(arch: MachineArch) -> anyhow::Result<PathBuf> {
274 get_path(
276 format!("target/{}-unknown-uefi/debug", target_arch_path(arch)),
277 "guest_test_uefi.img",
278 MissingCommand::Xtask {
279 xtask_args: &[
280 "guest-test",
281 "uefi",
282 &format!(
283 "--boot{}",
284 match arch {
285 MachineArch::X86_64 => "x64",
286 MachineArch::Aarch64 => "aa64",
287 }
288 ),
289 ],
290 description: "guest_test_uefi image",
291 },
292 )
293}
294
295fn pipette_path(arch: MachineArch, os_flavor: PipetteFlavor) -> anyhow::Result<PathBuf> {
297 let (target_suffixes, binary) = match os_flavor {
300 PipetteFlavor::Windows => (vec!["pc-windows-msvc", "pc-windows-gnu"], "pipette.exe"),
301 PipetteFlavor::Linux => (vec!["unknown-linux-musl"], "pipette"),
302 };
303 for (index, target_suffix) in target_suffixes.iter().enumerate() {
304 let target = format!("{}-{}", target_arch_path(arch), target_suffix);
305 match get_path(
306 format!("target/{target}/{}", cargo_build_profile()),
307 binary,
308 MissingCommand::Build {
309 package: "pipette",
310 target: Some(&target),
311 },
312 ) {
313 Ok(path) => return Ok(path),
314 Err(err) => {
315 if index < target_suffixes.len() - 1 {
316 continue;
317 } else {
318 anyhow::bail!(
319 "None of the suffixes {:?} had `pipette` built, {err:?}",
320 target_suffixes
321 );
322 }
323 }
324 }
325 }
326
327 unreachable!()
328}
329
330fn openvmm_native_executable_path() -> anyhow::Result<PathBuf> {
332 get_output_executable_path("openvmm")
333}
334
335#[cfg(target_os = "linux")]
337fn openvmm_vhost_native_executable_path() -> anyhow::Result<PathBuf> {
338 get_output_executable_path("openvmm_vhost")
339}
340
341fn tmk_vmm_native_executable_path() -> anyhow::Result<PathBuf> {
343 get_output_executable_path("tmk_vmm")
344}
345
346fn vmgstool_native_executable_path() -> anyhow::Result<PathBuf> {
348 get_output_executable_path("vmgstool")
349}
350
351fn tpm_guest_tests_windows_path(arch: MachineArch) -> anyhow::Result<PathBuf> {
353 let target = windows_msvc_target(arch);
354 get_path(
355 format!("target/{target}/debug"),
356 "tpm_guest_tests.exe",
357 MissingCommand::Build {
358 package: "tpm_guest_tests",
359 target: Some(target),
360 },
361 )
362}
363
364fn tpm_guest_tests_linux_path(arch: MachineArch) -> anyhow::Result<PathBuf> {
365 let target = match arch {
366 MachineArch::X86_64 => "x86_64-unknown-linux-gnu",
367 MachineArch::Aarch64 => "aarch64-unknown-linux-gnu",
368 };
369
370 get_path(
371 format!("target/{target}/debug"),
372 "tpm_guest_tests",
373 MissingCommand::Build {
374 package: "tpm_guest_tests",
375 target: Some(target),
376 },
377 )
378}
379
380fn test_igvm_agent_rpc_server_windows_path(arch: MachineArch) -> anyhow::Result<PathBuf> {
382 let target = windows_msvc_target(arch);
383 get_path(
384 format!("target/{target}/debug"),
385 "test_igvm_agent_rpc_server.exe",
386 MissingCommand::Build {
387 package: "test_igvm_agent_rpc_server",
388 target: Some(target),
389 },
390 )
391}
392
393fn tmk_vmm_paravisor_path(arch: MachineArch) -> anyhow::Result<PathBuf> {
394 let target = match arch {
395 MachineArch::X86_64 => "x86_64-unknown-linux-musl",
396 MachineArch::Aarch64 => "aarch64-unknown-linux-musl",
397 };
398 get_path(
399 format!("target/{target}/debug"),
400 "tmk_vmm",
401 MissingCommand::Build {
402 package: "tmk_vmm",
403 target: Some(target),
404 },
405 )
406}
407
408fn simple_tmk_path(arch: MachineArch) -> anyhow::Result<PathBuf> {
410 let arch_str = match arch {
411 MachineArch::X86_64 => "x86_64",
412 MachineArch::Aarch64 => "aarch64",
413 };
414 let target = match arch {
415 MachineArch::X86_64 => "x86_64-unknown-none",
416 MachineArch::Aarch64 => "aarch64-minimal_rt-none",
417 };
418 get_path(
419 format!("target/{target}/debug"),
420 "simple_tmk",
421 MissingCommand::Custom {
422 description: "simple_tmk",
423 cmd: &format!(
424 "RUSTC_BOOTSTRAP=1 cargo build -p simple_tmk --config openhcl/minimal_rt/{arch_str}-config.toml"
425 ),
426 },
427 )
428}
429
430fn linux_direct_x64_test_kernel_path() -> anyhow::Result<PathBuf> {
432 use petri_artifacts_vmm_test::artifacts::loadable;
433 get_path(
434 ".packages/underhill-deps-private",
435 resolve_bundle_name(loadable::LINUX_DIRECT_TEST_KERNEL_X64.erase()).unwrap(),
436 MissingCommand::Restore {
437 description: "linux direct test kernel",
438 },
439 )
440}
441
442fn linux_direct_x64_test_bzimage_path() -> anyhow::Result<PathBuf> {
444 use petri_artifacts_vmm_test::artifacts::loadable;
445 get_path(
446 ".packages/underhill-deps-private",
447 resolve_bundle_name(loadable::LINUX_DIRECT_TEST_BZIMAGE_X64.erase()).unwrap(),
448 MissingCommand::Restore {
449 description: "linux direct test bzImage",
450 },
451 )
452}
453
454fn linux_direct_test_initrd_path(arch: MachineArch) -> anyhow::Result<PathBuf> {
456 use petri_artifacts_vmm_test::artifacts::loadable;
457 let id = match arch {
458 MachineArch::X86_64 => loadable::LINUX_DIRECT_TEST_INITRD_X64.erase(),
459 MachineArch::Aarch64 => loadable::LINUX_DIRECT_TEST_INITRD_AARCH64.erase(),
460 };
461 get_path(
462 ".packages/underhill-deps-private",
463 resolve_bundle_name(id).unwrap(),
464 MissingCommand::Restore {
465 description: "linux direct test initrd",
466 },
467 )
468}
469
470fn petritools_erofs_path(arch: MachineArch) -> anyhow::Result<PathBuf> {
472 use petri_artifacts_vmm_test::artifacts::petritools;
473 let id = match arch {
474 MachineArch::X86_64 => petritools::PETRITOOLS_EROFS_X64.erase(),
475 MachineArch::Aarch64 => petritools::PETRITOOLS_EROFS_AARCH64.erase(),
476 };
477 get_path(
478 ".packages/underhill-deps-private",
479 resolve_bundle_name(id).unwrap(),
480 MissingCommand::Restore {
481 description: "petritools erofs image",
482 },
483 )
484}
485
486fn linux_direct_arm_image_path() -> anyhow::Result<PathBuf> {
488 use petri_artifacts_vmm_test::artifacts::loadable;
489 get_path(
490 ".packages/underhill-deps-private",
491 resolve_bundle_name(loadable::LINUX_DIRECT_TEST_KERNEL_AARCH64.erase()).unwrap(),
492 MissingCommand::Restore {
493 description: "linux direct test kernel",
494 },
495 )
496}
497
498fn pcat_firmware_path() -> anyhow::Result<PathBuf> {
500 get_path(
501 ".packages",
502 "Microsoft.Windows.VmFirmware.Pcat.amd64fre/content/vmfirmwarepcat.dll",
503 MissingCommand::Restore {
504 description: "PCAT firmware binary",
505 },
506 )
507}
508
509fn svga_firmware_path() -> anyhow::Result<PathBuf> {
511 get_path(
512 ".packages",
513 "Microsoft.Windows.VmEmulatedDevices.amd64fre/content/VmEmulatedDevices.dll",
514 MissingCommand::Restore {
515 description: "SVGA firmware binary",
516 },
517 )
518}
519
520fn uefi_firmware_path(arch: MachineArch) -> anyhow::Result<PathBuf> {
522 use petri_artifacts_vmm_test::artifacts::loadable;
523 let id = match arch {
524 MachineArch::X86_64 => loadable::UEFI_FIRMWARE_X64.erase(),
525 MachineArch::Aarch64 => loadable::UEFI_FIRMWARE_AARCH64.erase(),
526 };
527 get_path(
528 ".packages",
529 resolve_bundle_name(id).unwrap(),
530 MissingCommand::Restore {
531 description: "UEFI firmware binary",
532 },
533 )
534}
535
536fn openhcl_bin_path(
538 arch: MachineArch,
539 version: OpenhclVersion,
540 flavor: OpenhclFlavor,
541) -> anyhow::Result<PathBuf> {
542 let (path, name, cmd) = match (arch, version, flavor) {
543 (MachineArch::X86_64, OpenhclVersion::Latest, OpenhclFlavor::Standard) => (
544 "flowey-out/artifacts/build-igvm/debug/x64",
545 "openhcl-x64.bin",
546 MissingCommand::XFlowey {
547 description: "OpenHCL IGVM file",
548 xflowey_args: &["build-igvm", "x64"],
549 },
550 ),
551 (MachineArch::X86_64, OpenhclVersion::Latest, OpenhclFlavor::StandardDevKernel) => (
552 "flowey-out/artifacts/build-igvm/debug/x64-devkern",
553 "openhcl-x64-devkern.bin",
554 MissingCommand::XFlowey {
555 description: "OpenHCL IGVM file",
556 xflowey_args: &["build-igvm", "x64-devkern"],
557 },
558 ),
559 (MachineArch::X86_64, OpenhclVersion::Latest, OpenhclFlavor::Cvm) => (
560 "flowey-out/artifacts/build-igvm/debug/x64-cvm",
561 "openhcl-x64-cvm.bin",
562 MissingCommand::XFlowey {
563 description: "OpenHCL IGVM file",
564 xflowey_args: &["build-igvm", "x64-cvm"],
565 },
566 ),
567 (MachineArch::X86_64, OpenhclVersion::Latest, OpenhclFlavor::LinuxDirect) => (
568 "flowey-out/artifacts/build-igvm/debug/x64-test-linux-direct",
569 "openhcl-x64-test-linux-direct.bin",
570 MissingCommand::XFlowey {
571 description: "OpenHCL IGVM file",
572 xflowey_args: &["build-igvm", "x64-test-linux-direct"],
573 },
574 ),
575 (MachineArch::Aarch64, OpenhclVersion::Latest, OpenhclFlavor::Standard) => (
576 "flowey-out/artifacts/build-igvm/debug/aarch64",
577 "openhcl-aarch64.bin",
578 MissingCommand::XFlowey {
579 description: "OpenHCL IGVM file",
580 xflowey_args: &["build-igvm", "aarch64"],
581 },
582 ),
583 (MachineArch::Aarch64, OpenhclVersion::Latest, OpenhclFlavor::StandardDevKernel) => (
584 "flowey-out/artifacts/build-igvm/debug/aarch64-devkern",
585 "openhcl-aarch64-devkern.bin",
586 MissingCommand::XFlowey {
587 description: "OpenHCL IGVM file",
588 xflowey_args: &["build-igvm", "aarch64-devkern"],
589 },
590 ),
591 (MachineArch::X86_64, OpenhclVersion::Release2511, OpenhclFlavor::LinuxDirect) => (
592 "flowey-out/artifacts/last-release-igvm-files",
593 "release-2511-x64-direct-openhcl.bin",
594 MissingCommand::XFlowey {
595 description: "Previous OpenHCL release IGVM file",
596 xflowey_args: &["restore-packages"],
597 },
598 ),
599 (MachineArch::X86_64, OpenhclVersion::Release2511, OpenhclFlavor::Standard) => (
600 "flowey-out/artifacts/last-release-igvm-files",
601 "release-2511-x64-openhcl.bin",
602 MissingCommand::XFlowey {
603 description: "Previous OpenHCL release IGVM file",
604 xflowey_args: &["restore-packages"],
605 },
606 ),
607 (MachineArch::Aarch64, OpenhclVersion::Release2511, OpenhclFlavor::Standard) => (
608 "flowey-out/artifacts/last-release-igvm-files",
609 "release-2511-aarch64-openhcl.bin",
610 MissingCommand::XFlowey {
611 description: "Previous OpenHCL release IGVM file",
612 xflowey_args: &["restore-packages"],
613 },
614 ),
615 _ => anyhow::bail!("no openhcl bin with given arch, version, and flavor"),
616 };
617
618 get_path(path, name, cmd)
619}
620
621fn openhcl_extras_path(
623 version: OpenhclVersion,
624 flavor: OpenhclFlavor,
625 item: OpenhclExtras,
626) -> anyhow::Result<PathBuf> {
627 if !matches!(version, OpenhclVersion::Latest) || !matches!(flavor, OpenhclFlavor::LinuxDirect) {
628 anyhow::bail!("Debug symbol path currently only available for LATEST_LINUX_DIRECT_TEST")
629 }
630
631 let (path, name) = match item {
632 OpenhclExtras::UmBin => (
633 "flowey-out/artifacts/build-igvm/debug/x64-test-linux-direct",
634 "openvmm_hcl_msft",
635 ),
636 OpenhclExtras::UmDbg => (
637 "flowey-out/artifacts/build-igvm/debug/x64-test-linux-direct",
638 "openvmm_hcl_msft.dbg",
639 ),
640 };
641
642 get_path(
643 path,
644 name,
645 MissingCommand::XFlowey {
646 description: "OpenHCL IGVM file",
647 xflowey_args: &["build-igvm", "x64-test-linux-direct"],
648 },
649 )
650}
651
652fn test_log_directory_path(test_name: &str) -> anyhow::Result<PathBuf> {
654 let root = if let Some(path) = std::env::var_os("TEST_OUTPUT_PATH") {
655 PathBuf::from(path)
656 } else {
657 get_repo_root()?.join("vmm_test_results")
658 };
659 let path = root.join(test_name.replace("::", "__"));
662 fs_err::create_dir_all(&path)?;
663 Ok(path)
664}
665
666const VMM_TESTS_DIR_ENV_VAR: &str = "VMM_TESTS_CONTENT_DIR";
667
668pub fn get_repo_root() -> anyhow::Result<PathBuf> {
670 Ok(Path::new(env!("CARGO_MANIFEST_DIR")).join("../.."))
671}
672
673pub fn get_path(
687 search_path: impl AsRef<Path>,
688 file_name: impl AsRef<Path>,
689 missing_cmd: MissingCommand<'_>,
690) -> anyhow::Result<PathBuf> {
691 let search_path = search_path.as_ref();
692 let file_name = file_name.as_ref();
693 if file_name.is_absolute() {
694 anyhow::bail!("{} should be a relative path", file_name.display());
695 }
696
697 if let Ok(env_dir) = std::env::var(VMM_TESTS_DIR_ENV_VAR) {
698 let full_path = Path::new(&env_dir).join(file_name);
699 if full_path.try_exists()? {
700 return Ok(full_path);
701 }
702 }
703
704 let file_path = if search_path.is_absolute() {
705 search_path.to_owned()
706 } else {
707 get_repo_root()?.join(search_path)
708 };
709
710 let full_path = file_path.join(file_name);
711 if !full_path.exists() {
712 eprintln!("Failed to find {:?}.", full_path);
713 missing_cmd.to_error()?;
714 }
715
716 Ok(full_path)
717}
718
719pub fn get_output_executable_path(name: &str) -> anyhow::Result<PathBuf> {
723 let mut path: PathBuf = std::env::current_exe()?;
724 if path.parent().and_then(|x| x.file_name()).unwrap() == "deps" {
727 path.pop();
728 }
729
730 get_path(
731 path.parent().unwrap(),
732 Path::new(name).with_extension(EXE_EXTENSION),
733 MissingCommand::Build {
734 package: name,
735 target: None,
736 },
737 )
738}
739
740#[derive(Copy, Clone)]
743#[expect(missing_docs)] pub enum MissingCommand<'a> {
745 Build {
747 package: &'a str,
748 target: Option<&'a str>,
749 },
750 Run {
752 description: &'a str,
753 package: &'a str,
754 },
755 Xtask {
757 description: &'a str,
758 xtask_args: &'a [&'a str],
759 },
760 XFlowey {
762 description: &'a str,
763 xflowey_args: &'a [&'a str],
764 },
765 Restore { description: &'a str },
767 Custom { description: &'a str, cmd: &'a str },
769}
770
771impl MissingCommand<'_> {
772 fn to_error(self) -> anyhow::Result<()> {
773 match self {
774 MissingCommand::Build { package, target } => anyhow::bail!(
775 "Failed to find {package} binary. Run `cargo build {target_args}-p {package}` to build it.",
776 target_args =
777 target.map_or(String::new(), |target| format!("--target {} ", target)),
778 ),
779 MissingCommand::Run {
780 description,
781 package,
782 } => anyhow::bail!(
783 "Failed to find {}. Run `cargo run -p {}` to create it.",
784 description,
785 package
786 ),
787 MissingCommand::Xtask {
788 description,
789 xtask_args: args,
790 } => {
791 anyhow::bail!(
792 "Failed to find {}. Run `cargo xtask {}` to create it.",
793 description,
794 args.join(" ")
795 )
796 }
797 MissingCommand::XFlowey {
798 description,
799 xflowey_args: args,
800 } => anyhow::bail!(
801 "Failed to find {}. Run `cargo xflowey {}` to create it.",
802 description,
803 args.join(" ")
804 ),
805 MissingCommand::Restore { description } => {
806 anyhow::bail!(
807 "Failed to find {}. Run `cargo xflowey restore-packages`.",
808 description
809 )
810 }
811 MissingCommand::Custom { description, cmd } => {
812 anyhow::bail!(
813 "Failed to find {}. Run `{}` to create it.",
814 description,
815 cmd
816 )
817 }
818 }
819 }
820}