1#![forbid(unsafe_code)]
7
8use petri_artifacts_common::tags::MachineArch;
9use petri_artifacts_core::ErasedArtifactHandle;
10use std::env::consts::EXE_EXTENSION;
11use std::path::Path;
12use std::path::PathBuf;
13use vmm_test_images::KnownTestArtifacts;
14
15pub struct OpenvmmKnownPathsTestArtifactResolver<'a>(&'a str);
19
20impl<'a> OpenvmmKnownPathsTestArtifactResolver<'a> {
21 pub fn new(test_name: &'a str) -> Self {
23 Self(test_name)
24 }
25}
26
27impl petri_artifacts_core::ResolveTestArtifact for OpenvmmKnownPathsTestArtifactResolver<'_> {
28 #[rustfmt::skip]
29 fn resolve(&self, id: ErasedArtifactHandle) -> anyhow::Result<PathBuf> {
30 use petri_artifacts_common::artifacts as common;
31 use petri_artifacts_vmm_test::artifacts::*;
32
33 match id {
34 _ if id == common::PIPETTE_WINDOWS_X64 => pipette_path(MachineArch::X86_64, PipetteFlavor::Windows),
35 _ if id == common::PIPETTE_LINUX_X64 => pipette_path(MachineArch::X86_64, PipetteFlavor::Linux),
36 _ if id == common::PIPETTE_WINDOWS_AARCH64 => pipette_path(MachineArch::Aarch64, PipetteFlavor::Windows),
37 _ if id == common::PIPETTE_LINUX_AARCH64 => pipette_path(MachineArch::Aarch64, PipetteFlavor::Linux),
38
39 _ if id == common::TEST_LOG_DIRECTORY => test_log_directory_path(self.0),
40
41 _ if id == OPENVMM_NATIVE => openvmm_native_executable_path(),
42
43 _ if id == loadable::LINUX_DIRECT_TEST_KERNEL_X64 => linux_direct_x64_test_kernel_path(),
44 _ if id == loadable::LINUX_DIRECT_TEST_KERNEL_AARCH64 => linux_direct_arm_image_path(),
45 _ if id == loadable::LINUX_DIRECT_TEST_INITRD_X64 => linux_direct_test_initrd_path(MachineArch::X86_64),
46 _ if id == loadable::LINUX_DIRECT_TEST_INITRD_AARCH64 => linux_direct_test_initrd_path(MachineArch::Aarch64),
47
48 _ if id == loadable::PCAT_FIRMWARE_X64 => pcat_firmware_path(),
49 _ if id == loadable::SVGA_FIRMWARE_X64 => svga_firmware_path(),
50 _ if id == loadable::UEFI_FIRMWARE_X64 => uefi_firmware_path(MachineArch::X86_64),
51 _ if id == loadable::UEFI_FIRMWARE_AARCH64 => uefi_firmware_path(MachineArch::Aarch64),
52
53 _ if id == openhcl_igvm::LATEST_STANDARD_X64 => openhcl_bin_path(MachineArch::X86_64, OpenhclVersion::Latest, OpenhclFlavor::Standard),
54 _ if id == openhcl_igvm::LATEST_STANDARD_DEV_KERNEL_X64 => openhcl_bin_path(MachineArch::X86_64, OpenhclVersion::Latest, OpenhclFlavor::StandardDevKernel),
55 _ if id == openhcl_igvm::LATEST_CVM_X64 => openhcl_bin_path(MachineArch::X86_64, OpenhclVersion::Latest, OpenhclFlavor::Cvm),
56 _ if id == openhcl_igvm::LATEST_LINUX_DIRECT_TEST_X64 => openhcl_bin_path(MachineArch::X86_64, OpenhclVersion::Latest, OpenhclFlavor::LinuxDirect),
57 _ if id == openhcl_igvm::LATEST_STANDARD_AARCH64 => openhcl_bin_path(MachineArch::Aarch64, OpenhclVersion::Latest, OpenhclFlavor::Standard),
58 _ if id == openhcl_igvm::LATEST_STANDARD_DEV_KERNEL_AARCH64 => openhcl_bin_path(MachineArch::Aarch64, OpenhclVersion::Latest, OpenhclFlavor::StandardDevKernel),
59
60 _ if id == openhcl_igvm::RELEASE_25_05_STANDARD_X64 => openhcl_bin_path(MachineArch::X86_64, OpenhclVersion::Release2505, OpenhclFlavor::Standard),
61 _ if id == openhcl_igvm::RELEASE_25_05_LINUX_DIRECT_X64 => openhcl_bin_path(MachineArch::X86_64, OpenhclVersion::Release2505, OpenhclFlavor::LinuxDirect),
62 _ if id == openhcl_igvm::RELEASE_25_05_STANDARD_AARCH64 => openhcl_bin_path(MachineArch::Aarch64, OpenhclVersion::Release2505, OpenhclFlavor::Standard),
63
64 _ if id == openhcl_igvm::um_bin::LATEST_LINUX_DIRECT_TEST_X64 => openhcl_extras_path(OpenhclVersion::Latest,OpenhclFlavor::LinuxDirect,OpenhclExtras::UmBin),
65 _ if id == openhcl_igvm::um_dbg::LATEST_LINUX_DIRECT_TEST_X64 => openhcl_extras_path(OpenhclVersion::Latest,OpenhclFlavor::LinuxDirect,OpenhclExtras::UmDbg),
66
67 _ if id == test_vhd::GUEST_TEST_UEFI_X64 => guest_test_uefi_disk_path(MachineArch::X86_64),
68 _ if id == test_vhd::GUEST_TEST_UEFI_AARCH64 => guest_test_uefi_disk_path(MachineArch::Aarch64),
69 _ if id == test_vhd::GEN1_WINDOWS_DATA_CENTER_CORE2022_X64 => get_test_artifact_path(KnownTestArtifacts::Gen1WindowsDataCenterCore2022X64Vhd),
70 _ if id == test_vhd::GEN2_WINDOWS_DATA_CENTER_CORE2022_X64 => get_test_artifact_path(KnownTestArtifacts::Gen2WindowsDataCenterCore2022X64Vhd),
71 _ if id == test_vhd::GEN2_WINDOWS_DATA_CENTER_CORE2025_X64 => get_test_artifact_path(KnownTestArtifacts::Gen2WindowsDataCenterCore2025X64Vhd),
72 _ if id == test_vhd::FREE_BSD_13_2_X64 => get_test_artifact_path(KnownTestArtifacts::FreeBsd13_2X64Vhd),
73 _ if id == test_vhd::UBUNTU_2204_SERVER_X64 => get_test_artifact_path(KnownTestArtifacts::Ubuntu2204ServerX64Vhd),
74 _ if id == test_vhd::UBUNTU_2404_SERVER_X64 => get_test_artifact_path(KnownTestArtifacts::Ubuntu2404ServerX64Vhd),
75 _ if id == test_vhd::UBUNTU_2404_SERVER_AARCH64 => get_test_artifact_path(KnownTestArtifacts::Ubuntu2404ServerAarch64Vhd),
76 _ if id == test_vhd::WINDOWS_11_ENTERPRISE_AARCH64 => get_test_artifact_path(KnownTestArtifacts::Windows11EnterpriseAarch64Vhdx),
77
78 _ if id == test_iso::FREE_BSD_13_2_X64 => get_test_artifact_path(KnownTestArtifacts::FreeBsd13_2X64Iso),
79
80 _ if id == test_vmgs::VMGS_WITH_BOOT_ENTRY => get_test_artifact_path(KnownTestArtifacts::VmgsWithBootEntry),
81
82 _ if id == tmks::TMK_VMM_NATIVE => tmk_vmm_native_executable_path(),
83 _ if id == tmks::TMK_VMM_LINUX_X64_MUSL => tmk_vmm_paravisor_path(MachineArch::X86_64),
84 _ if id == tmks::TMK_VMM_LINUX_AARCH64_MUSL => tmk_vmm_paravisor_path(MachineArch::Aarch64),
85 _ if id == tmks::SIMPLE_TMK_X64 => simple_tmk_path(MachineArch::X86_64),
86 _ if id == tmks::SIMPLE_TMK_AARCH64 => simple_tmk_path(MachineArch::Aarch64),
87
88 _ => anyhow::bail!("no support for given artifact type"),
89 }
90 }
91}
92
93#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
94enum PipetteFlavor {
95 Windows,
96 Linux,
97}
98
99#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
100enum OpenhclVersion {
101 Latest,
102 Release2505,
103}
104
105#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
106enum OpenhclFlavor {
107 Standard,
108 StandardDevKernel,
109 Cvm,
110 LinuxDirect,
111}
112
113#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
114enum OpenhclExtras {
115 UmBin,
116 UmDbg,
117}
118
119fn target_arch_path(arch: MachineArch) -> &'static str {
121 match arch {
122 MachineArch::X86_64 => "x86_64",
123 MachineArch::Aarch64 => "aarch64",
124 }
125}
126
127fn get_test_artifact_path(artifact: KnownTestArtifacts) -> Result<PathBuf, anyhow::Error> {
128 let images_dir = std::env::var("VMM_TEST_IMAGES");
129 let full_path = Path::new(images_dir.as_deref().unwrap_or("images"));
130
131 get_path(
132 full_path,
133 artifact.filename(),
134 MissingCommand::Xtask {
135 xtask_args: &[
136 "guest-test",
137 "download-image",
138 "--artifacts",
139 &artifact.name(),
140 ],
141 description: "test artifact",
142 },
143 )
144}
145
146fn guest_test_uefi_disk_path(arch: MachineArch) -> anyhow::Result<PathBuf> {
148 get_path(
150 format!("target/{}-unknown-uefi/debug", target_arch_path(arch)),
151 "guest_test_uefi.img",
152 MissingCommand::Xtask {
153 xtask_args: &[
154 "guest-test",
155 "uefi",
156 &format!(
157 "--boot{}",
158 match arch {
159 MachineArch::X86_64 => "x64",
160 MachineArch::Aarch64 => "aa64",
161 }
162 ),
163 ],
164 description: "guest_test_uefi image",
165 },
166 )
167}
168
169fn pipette_path(arch: MachineArch, os_flavor: PipetteFlavor) -> anyhow::Result<PathBuf> {
171 let (target_suffixes, binary) = match os_flavor {
174 PipetteFlavor::Windows => (vec!["pc-windows-msvc", "pc-windows-gnu"], "pipette.exe"),
175 PipetteFlavor::Linux => (vec!["unknown-linux-musl"], "pipette"),
176 };
177 for (index, target_suffix) in target_suffixes.iter().enumerate() {
178 let target = format!("{}-{}", target_arch_path(arch), target_suffix);
179 match get_path(
180 format!("target/{target}/debug"),
181 binary,
182 MissingCommand::Build {
183 package: "pipette",
184 target: Some(&target),
185 },
186 ) {
187 Ok(path) => return Ok(path),
188 Err(err) => {
189 if index < target_suffixes.len() - 1 {
190 continue;
191 } else {
192 anyhow::bail!(
193 "None of the suffixes {:?} had `pipette` built, {err:?}",
194 target_suffixes
195 );
196 }
197 }
198 }
199 }
200
201 unreachable!()
202}
203
204fn openvmm_native_executable_path() -> anyhow::Result<PathBuf> {
206 get_output_executable_path("openvmm")
207}
208
209fn tmk_vmm_native_executable_path() -> anyhow::Result<PathBuf> {
211 get_output_executable_path("tmk_vmm")
212}
213
214fn tmk_vmm_paravisor_path(arch: MachineArch) -> anyhow::Result<PathBuf> {
215 let target = match arch {
216 MachineArch::X86_64 => "x86_64-unknown-linux-musl",
217 MachineArch::Aarch64 => "aarch64-unknown-linux-musl",
218 };
219 get_path(
220 format!("target/{target}/debug"),
221 "tmk_vmm",
222 MissingCommand::Build {
223 package: "tmk_vmm",
224 target: Some(target),
225 },
226 )
227}
228
229fn simple_tmk_path(arch: MachineArch) -> anyhow::Result<PathBuf> {
231 let arch_str = match arch {
232 MachineArch::X86_64 => "x86_64",
233 MachineArch::Aarch64 => "aarch64",
234 };
235 let target = match arch {
236 MachineArch::X86_64 => "x86_64-unknown-none",
237 MachineArch::Aarch64 => "aarch64-minimal_rt-none",
238 };
239 get_path(
240 format!("target/{target}/debug"),
241 "simple_tmk",
242 MissingCommand::Custom {
243 description: "simple_tmk",
244 cmd: &format!(
245 "RUSTC_BOOTSTRAP=1 cargo build -p simple_tmk --config openhcl/minimal_rt/{arch_str}-config.toml"
246 ),
247 },
248 )
249}
250
251fn linux_direct_x64_test_kernel_path() -> anyhow::Result<PathBuf> {
253 get_path(
254 ".packages/underhill-deps-private",
255 "x64/vmlinux",
256 MissingCommand::Restore {
257 description: "linux direct test kernel",
258 },
259 )
260}
261
262fn linux_direct_test_initrd_path(arch: MachineArch) -> anyhow::Result<PathBuf> {
264 get_path(
265 ".packages/underhill-deps-private",
266 format!(
267 "{}/initrd",
268 match arch {
269 MachineArch::X86_64 => "x64",
270 MachineArch::Aarch64 => "aarch64",
271 }
272 ),
273 MissingCommand::Restore {
274 description: "linux direct test initrd",
275 },
276 )
277}
278
279fn linux_direct_arm_image_path() -> anyhow::Result<PathBuf> {
281 get_path(
282 ".packages/underhill-deps-private",
283 "aarch64/Image",
284 MissingCommand::Restore {
285 description: "linux direct test kernel",
286 },
287 )
288}
289
290fn pcat_firmware_path() -> anyhow::Result<PathBuf> {
292 get_path(
293 ".packages",
294 "Microsoft.Windows.VmFirmware.Pcat.amd64fre/content/vmfirmwarepcat.dll",
295 MissingCommand::Restore {
296 description: "PCAT firmware binary",
297 },
298 )
299}
300
301fn svga_firmware_path() -> anyhow::Result<PathBuf> {
303 get_path(
304 ".packages",
305 "Microsoft.Windows.VmEmulatedDevices.amd64fre/content/VmEmulatedDevices.dll",
306 MissingCommand::Restore {
307 description: "SVGA firmware binary",
308 },
309 )
310}
311
312fn uefi_firmware_path(arch: MachineArch) -> anyhow::Result<PathBuf> {
314 get_path(
315 ".packages",
316 match arch {
317 MachineArch::X86_64 => {
318 "hyperv.uefi.mscoreuefi.x64.RELEASE/MsvmX64/RELEASE_VS2022/FV/MSVM.fd"
319 }
320 MachineArch::Aarch64 => {
321 "hyperv.uefi.mscoreuefi.AARCH64.RELEASE/MsvmAARCH64/RELEASE_VS2022/FV/MSVM.fd"
322 }
323 },
324 MissingCommand::Restore {
325 description: "UEFI firmware binary",
326 },
327 )
328}
329
330fn openhcl_bin_path(
332 arch: MachineArch,
333 version: OpenhclVersion,
334 flavor: OpenhclFlavor,
335) -> anyhow::Result<PathBuf> {
336 let (path, name, cmd) = match (arch, version, flavor) {
337 (MachineArch::X86_64, OpenhclVersion::Latest, OpenhclFlavor::Standard) => (
338 "flowey-out/artifacts/build-igvm/debug/x64",
339 "openhcl-x64.bin",
340 MissingCommand::XFlowey {
341 description: "OpenHCL IGVM file",
342 xflowey_args: &["build-igvm", "x64"],
343 },
344 ),
345 (MachineArch::X86_64, OpenhclVersion::Latest, OpenhclFlavor::StandardDevKernel) => (
346 "flowey-out/artifacts/build-igvm/debug/x64-devkern",
347 "openhcl-x64-devkern.bin",
348 MissingCommand::XFlowey {
349 description: "OpenHCL IGVM file",
350 xflowey_args: &["build-igvm", "x64-devkern"],
351 },
352 ),
353 (MachineArch::X86_64, OpenhclVersion::Latest, OpenhclFlavor::Cvm) => (
354 "flowey-out/artifacts/build-igvm/debug/x64-cvm",
355 "openhcl-x64-cvm.bin",
356 MissingCommand::XFlowey {
357 description: "OpenHCL IGVM file",
358 xflowey_args: &["build-igvm", "x64-cvm"],
359 },
360 ),
361 (MachineArch::X86_64, OpenhclVersion::Latest, OpenhclFlavor::LinuxDirect) => (
362 "flowey-out/artifacts/build-igvm/debug/x64-test-linux-direct",
363 "openhcl-x64-test-linux-direct.bin",
364 MissingCommand::XFlowey {
365 description: "OpenHCL IGVM file",
366 xflowey_args: &["build-igvm", "x64-test-linux-direct"],
367 },
368 ),
369 (MachineArch::Aarch64, OpenhclVersion::Latest, OpenhclFlavor::Standard) => (
370 "flowey-out/artifacts/build-igvm/debug/aarch64",
371 "openhcl-aarch64.bin",
372 MissingCommand::XFlowey {
373 description: "OpenHCL IGVM file",
374 xflowey_args: &["build-igvm", "aarch64"],
375 },
376 ),
377 (MachineArch::Aarch64, OpenhclVersion::Latest, OpenhclFlavor::StandardDevKernel) => (
378 "flowey-out/artifacts/build-igvm/debug/aarch64-devkern",
379 "openhcl-aarch64-devkern.bin",
380 MissingCommand::XFlowey {
381 description: "OpenHCL IGVM file",
382 xflowey_args: &["build-igvm", "aarch64-devkern"],
383 },
384 ),
385 (MachineArch::X86_64, OpenhclVersion::Release2505, OpenhclFlavor::LinuxDirect) => (
386 "flowey-out/artifacts/latest-release-igvm-files",
387 "release-2505-x64-direct-openhcl.bin",
388 MissingCommand::XFlowey {
389 description: "Previous OpenHCL release IGVM file",
390 xflowey_args: &["restore-packages"],
391 },
392 ),
393 (MachineArch::X86_64, OpenhclVersion::Release2505, OpenhclFlavor::Standard) => (
394 "flowey-out/artifacts/latest-release-igvm-files",
395 "release-2505-x64-openhcl.bin",
396 MissingCommand::XFlowey {
397 description: "Previous OpenHCL release IGVM file",
398 xflowey_args: &["restore-packages"],
399 },
400 ),
401 (MachineArch::Aarch64, OpenhclVersion::Release2505, OpenhclFlavor::Standard) => (
402 "flowey-out/artifacts/latest-release-igvm-files",
403 "release-2505-aarch64-openhcl.bin",
404 MissingCommand::XFlowey {
405 description: "Previous OpenHCL release IGVM file",
406 xflowey_args: &["restore-packages"],
407 },
408 ),
409 _ => anyhow::bail!("no openhcl bin with given arch, version, and flavor"),
410 };
411
412 get_path(path, name, cmd)
413}
414
415fn openhcl_extras_path(
417 version: OpenhclVersion,
418 flavor: OpenhclFlavor,
419 item: OpenhclExtras,
420) -> anyhow::Result<PathBuf> {
421 if !matches!(version, OpenhclVersion::Latest) || !matches!(flavor, OpenhclFlavor::LinuxDirect) {
422 anyhow::bail!("Debug symbol path currently only available for LATEST_LINUX_DIRECT_TEST")
423 }
424
425 let (path, name) = match item {
426 OpenhclExtras::UmBin => (
427 "flowey-out/artifacts/build-igvm/debug/x64-test-linux-direct",
428 "openvmm_hcl_msft",
429 ),
430 OpenhclExtras::UmDbg => (
431 "flowey-out/artifacts/build-igvm/debug/x64-test-linux-direct",
432 "openvmm_hcl_msft.dbg",
433 ),
434 };
435
436 get_path(
437 path,
438 name,
439 MissingCommand::XFlowey {
440 description: "OpenHCL IGVM file",
441 xflowey_args: &["build-igvm", "x64-test-linux-direct"],
442 },
443 )
444}
445
446fn test_log_directory_path(test_name: &str) -> anyhow::Result<PathBuf> {
448 let root = if let Some(path) = std::env::var_os("TEST_OUTPUT_PATH") {
449 PathBuf::from(path)
450 } else {
451 get_repo_root()?.join("vmm_test_results")
452 };
453 let path = root.join(test_name.replace("::", "__"));
456 fs_err::create_dir_all(&path)?;
457 Ok(path)
458}
459
460const VMM_TESTS_DIR_ENV_VAR: &str = "VMM_TESTS_CONTENT_DIR";
461
462pub fn get_repo_root() -> anyhow::Result<PathBuf> {
464 Ok(Path::new(env!("CARGO_MANIFEST_DIR")).join("../.."))
465}
466
467pub fn get_path(
481 search_path: impl AsRef<Path>,
482 file_name: impl AsRef<Path>,
483 missing_cmd: MissingCommand<'_>,
484) -> anyhow::Result<PathBuf> {
485 let search_path = search_path.as_ref();
486 let file_name = file_name.as_ref();
487 if file_name.is_absolute() {
488 anyhow::bail!("{} should be a relative path", file_name.display());
489 }
490
491 if let Ok(env_dir) = std::env::var(VMM_TESTS_DIR_ENV_VAR) {
492 let full_path = Path::new(&env_dir).join(file_name);
493 if full_path.try_exists()? {
494 return Ok(full_path);
495 }
496 }
497
498 let file_path = if search_path.is_absolute() {
499 search_path.to_owned()
500 } else {
501 get_repo_root()?.join(search_path)
502 };
503
504 let full_path = file_path.join(file_name);
505 if !full_path.exists() {
506 eprintln!("Failed to find {:?}.", full_path);
507 missing_cmd.to_error()?;
508 }
509
510 Ok(full_path)
511}
512
513pub fn get_output_executable_path(name: &str) -> anyhow::Result<PathBuf> {
517 let mut path: PathBuf = std::env::current_exe()?;
518 if path.parent().and_then(|x| x.file_name()).unwrap() == "deps" {
521 path.pop();
522 }
523
524 get_path(
525 path.parent().unwrap(),
526 Path::new(name).with_extension(EXE_EXTENSION),
527 MissingCommand::Build {
528 package: name,
529 target: None,
530 },
531 )
532}
533
534#[derive(Copy, Clone)]
537#[expect(missing_docs)] pub enum MissingCommand<'a> {
539 Build {
541 package: &'a str,
542 target: Option<&'a str>,
543 },
544 Xtask {
546 description: &'a str,
547 xtask_args: &'a [&'a str],
548 },
549 XFlowey {
551 description: &'a str,
552 xflowey_args: &'a [&'a str],
553 },
554 Restore { description: &'a str },
556 Custom { description: &'a str, cmd: &'a str },
558}
559
560impl MissingCommand<'_> {
561 fn to_error(self) -> anyhow::Result<()> {
562 match self {
563 MissingCommand::Build { package, target } => anyhow::bail!(
564 "Failed to find {package} binary. Run `cargo build {target_args}-p {package}` to build it.",
565 target_args =
566 target.map_or(String::new(), |target| format!("--target {} ", target)),
567 ),
568 MissingCommand::Xtask {
569 description,
570 xtask_args: args,
571 } => {
572 anyhow::bail!(
573 "Failed to find {}. Run `cargo xtask {}` to create it.",
574 description,
575 args.join(" ")
576 )
577 }
578 MissingCommand::XFlowey {
579 description,
580 xflowey_args: args,
581 } => anyhow::bail!(
582 "Failed to find {}. Run `cargo xflowey {}` to create it.",
583 description,
584 args.join(" ")
585 ),
586 MissingCommand::Restore { description } => {
587 anyhow::bail!(
588 "Failed to find {}. Run `cargo xflowey restore-packages`.",
589 description
590 )
591 }
592 MissingCommand::Custom { description, cmd } => {
593 anyhow::bail!(
594 "Failed to find {}. Run `{}` to create it.",
595 description,
596 cmd
597 )
598 }
599 }
600 }
601}