diff --git a/flowey/flowey_lib_hvlite/src/init_vmm_tests_env.rs b/flowey/flowey_lib_hvlite/src/init_vmm_tests_env.rs index bc12fdea4e..505de6934b 100644 --- a/flowey/flowey_lib_hvlite/src/init_vmm_tests_env.rs +++ b/flowey/flowey_lib_hvlite/src/init_vmm_tests_env.rs @@ -140,6 +140,36 @@ impl SimpleFlowNode for Node { }) }); + // Request all supported kernel versions for version-specific paths + let versioned_kernels: Vec<_> = + crate::resolve_openvmm_test_linux_kernel::LinuxTestKernelVersion::ALL + .iter() + .map(|&kver| { + let kernel = ctx.reqv(|v| { + crate::resolve_openvmm_test_linux_kernel::Request::Get( + crate::resolve_openvmm_test_linux_kernel::OpenvmmTestKernelFile::Kernel, + arch, + kver, + v, + ) + }); + let bzimage = + crate::resolve_openvmm_test_linux_kernel::OpenvmmTestKernelFile::BzImage + .is_available_for(arch) + .then(|| { + ctx.reqv(|v| { + crate::resolve_openvmm_test_linux_kernel::Request::Get( + crate::resolve_openvmm_test_linux_kernel::OpenvmmTestKernelFile::BzImage, + arch, + kver, + v, + ) + }) + }); + (kver, kernel, bzimage) + }) + .collect(); + let uefi = ctx.reqv(|v| crate::download_uefi_mu_msvm::Request::GetMsvmFd { arch, msvm_fd: v }); @@ -164,6 +194,10 @@ impl SimpleFlowNode for Node { let test_linux_initrd = test_linux_initrd.claim(ctx); let test_linux_kernel = test_linux_kernel.claim(ctx); let test_linux_bzimage = test_linux_bzimage.claim(ctx); + let versioned_kernels: Vec<_> = versioned_kernels + .into_iter() + .map(|(kver, kernel, bzimage)| (kver, kernel.claim(ctx), bzimage.claim(ctx))) + .collect(); let uefi = uefi.claim(ctx); let release_igvm_files_dir = release_igvm_files.claim(ctx); move |rt| { @@ -455,6 +489,16 @@ impl SimpleFlowNode for Node { )?; } + // Place version-specific kernels at versioned paths + for (kver, kernel_var, bzimage_var) in versioned_kernels { + let version_dir = test_content_dir.join(arch_dir).join(kver.artifact_tag()); + fs_err::create_dir_all(&version_dir)?; + fs_err::copy(rt.read(kernel_var), version_dir.join(kernel_file_name))?; + if let Some(bz_var) = bzimage_var { + fs_err::copy(rt.read(bz_var), version_dir.join("bzImage"))?; + } + } + let uefi_dir = test_content_dir.join(match arch { CommonArch::Aarch64 => { "hyperv.uefi.mscoreuefi.AARCH64.RELEASE/MsvmAARCH64/RELEASE_CLANGPDB/FV" diff --git a/flowey/flowey_lib_hvlite/src/resolve_openvmm_deps.rs b/flowey/flowey_lib_hvlite/src/resolve_openvmm_deps.rs index bbafb2f36c..f1feae3d43 100644 --- a/flowey/flowey_lib_hvlite/src/resolve_openvmm_deps.rs +++ b/flowey/flowey_lib_hvlite/src/resolve_openvmm_deps.rs @@ -2,10 +2,16 @@ // Licensed under the MIT License. //! Download various pre-built `openvmm-deps` dependencies, or use a local path if specified. +//! +//! The openvmm-deps release publishes separate archives: +//! - `openvmm-deps.{arch}.{ver}.tar.gz` — SDK tools (dbgrd, shell, sysroot, petritools) +//! - `openvmm-test-initrd.{arch}.{ver}.tar.gz` — shared test initrd +//! - `openvmm-test-linux-{kernel_ver}.{arch}.{ver}.tar.gz` — test kernel use crate::common::CommonArch; use flowey::node::prelude::*; use std::collections::BTreeMap; +use std::collections::BTreeSet; /// Which file to extract from the openvmm-deps archive. #[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] @@ -87,9 +93,6 @@ impl FlowNodeWithConfig for Node { return Ok(()); } - // Which architectures have at least one dep requested? - let needs_arch = |arch: CommonArch| deps.keys().any(|(_, a)| *a == arch); - if !local_paths.is_empty() { ctx.emit_rust_step("use local openvmm-deps", |ctx| { let deps = deps.claim(ctx); @@ -118,67 +121,65 @@ impl FlowNodeWithConfig for Node { return Ok(()); } - let extract_tar_gz_persistent_dir = ctx.persistent_dir(); - - let download_archive = |arch: CommonArch, ctx: &mut NodeCtx<'_>| { - let version = version.clone().expect("local requests handled above"); - let arch_str = match arch { - CommonArch::X86_64 => "x86_64", - CommonArch::Aarch64 => "aarch64", - }; - ctx.reqv(|v| flowey_lib_common::download_gh_release::Request { - repo_owner: "microsoft".into(), - repo_name: "openvmm-deps".into(), - needs_auth: false, - tag: version.clone(), - file_name: format!("openvmm-deps.{arch_str}.{version}.tar.gz"), - path: v, - }) - }; + let version = version.expect("local requests handled above"); + + // Determine which architectures we need to download. + let needed_archs: BTreeSet = deps.keys().map(|(_, arch)| *arch).collect(); + + let persistent_dir = ctx.persistent_dir(); - let openvmm_deps_tar_gz_x64 = - needs_arch(CommonArch::X86_64).then(|| download_archive(CommonArch::X86_64, ctx)); - let openvmm_deps_tar_gz_aarch64 = - needs_arch(CommonArch::Aarch64).then(|| download_archive(CommonArch::Aarch64, ctx)); + // Download each unique architecture. + let downloads: BTreeMap> = needed_archs + .into_iter() + .map(|arch| { + let arch_str = match arch { + CommonArch::X86_64 => "x86_64", + CommonArch::Aarch64 => "aarch64", + }; + let file_name = format!("openvmm-deps.{arch_str}.{version}.tar.gz"); + let path = ctx.reqv(|v| flowey_lib_common::download_gh_release::Request { + repo_owner: "microsoft".into(), + repo_name: "openvmm-deps".into(), + needs_auth: false, + tag: version.clone(), + file_name, + path: v, + }); + (arch, path) + }) + .collect(); ctx.emit_rust_step("unpack openvmm-deps archive", |ctx| { - let extract_tar_gz_persistent_dir = extract_tar_gz_persistent_dir.claim(ctx); - let openvmm_deps_tar_gz_x64 = openvmm_deps_tar_gz_x64.claim(ctx); - let openvmm_deps_tar_gz_aarch64 = openvmm_deps_tar_gz_aarch64.claim(ctx); + let persistent_dir = persistent_dir.claim(ctx); + let downloads: BTreeMap<_, _> = downloads + .into_iter() + .map(|(key, var)| (key, var.claim(ctx))) + .collect(); let deps = deps.claim(ctx); - let version = version.clone().expect("local requests handled above"); + let version = version.clone(); move |rt| { - let persistent_dir = extract_tar_gz_persistent_dir.map(|d| rt.read(d)); - let extract_dir_x64 = openvmm_deps_tar_gz_x64 - .map(|file| { - let file = rt.read(file); - flowey_lib_common::_util::extract::extract_tar_gz_if_new( - rt, - persistent_dir.as_deref(), - &file, - &version, - ) - }) - .transpose()?; - let extract_dir_aarch64 = openvmm_deps_tar_gz_aarch64 - .map(|file| { - let file = rt.read(file); - flowey_lib_common::_util::extract::extract_tar_gz_if_new( + let persistent_dir = persistent_dir.map(|d| rt.read(d)); + + // Extract each downloaded archive, keyed by architecture. + let extract_dirs: BTreeMap = downloads + .into_iter() + .map(|(arch, var)| { + let file = rt.read(var); + let dir = flowey_lib_common::_util::extract::extract_tar_gz_if_new( rt, persistent_dir.as_deref(), &file, &version, - ) + )?; + Ok((arch, dir)) }) - .transpose()?; - - let base_dir = |arch| match arch { - CommonArch::X86_64 => extract_dir_x64.clone().unwrap(), - CommonArch::Aarch64 => extract_dir_aarch64.clone().unwrap(), - }; + .collect::>()?; for ((dep, arch), vars) in deps { - let path = base_dir(arch).join(dep.filename()); + let extract_dir = extract_dirs + .get(&arch) + .expect("archive was downloaded for this arch"); + let path = extract_dir.join(dep.filename()); rt.write_all(vars, &path) } diff --git a/flowey/flowey_lib_hvlite/src/resolve_openvmm_test_linux_kernel.rs b/flowey/flowey_lib_hvlite/src/resolve_openvmm_test_linux_kernel.rs index 501ba742df..b98741d2e2 100644 --- a/flowey/flowey_lib_hvlite/src/resolve_openvmm_test_linux_kernel.rs +++ b/flowey/flowey_lib_hvlite/src/resolve_openvmm_test_linux_kernel.rs @@ -21,13 +21,10 @@ use std::collections::BTreeSet; /// Which Linux test kernel version to fetch from the openvmm-deps GitHub /// release. -/// -/// The `openvmm-deps` release currently only ships the 6.1 kernel; additional -/// kernel lines (e.g. 6.6, 6.12) are intended to be added as purely additive -/// follow-ups, both upstream and as new variants of this enum. #[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum LinuxTestKernelVersion { Linux6_1, + Linux6_18, } impl LinuxTestKernelVersion { @@ -36,8 +33,12 @@ impl LinuxTestKernelVersion { pub fn artifact_tag(self) -> &'static str { match self { Self::Linux6_1 => "6.1", + Self::Linux6_18 => "6.18", } } + + /// All supported kernel versions. + pub const ALL: &'static [LinuxTestKernelVersion] = &[Self::Linux6_1, Self::Linux6_18]; } /// Which file to extract from a per-(arch, kver) `openvmm-test-linux` archive. @@ -73,7 +74,7 @@ impl OpenvmmTestKernelFile { /// The default Linux test kernel version. Call sites that don't otherwise care /// which kernel they're using should pass this. pub const DEFAULT_LINUX_TEST_KERNEL_VERSION: LinuxTestKernelVersion = - LinuxTestKernelVersion::Linux6_1; + LinuxTestKernelVersion::Linux6_18; flowey_config! { /// Config for the resolve_openvmm_test_linux_kernel node. diff --git a/petri/src/vm/mod.rs b/petri/src/vm/mod.rs index 11cbb8797f..16e663ef50 100644 --- a/petri/src/vm/mod.rs +++ b/petri/src/vm/mod.rs @@ -2484,6 +2484,36 @@ impl Firmware { } } + /// Constructs a [`Firmware::LinuxDirect`] configuration with a specific + /// kernel version. + pub fn linux_direct_with_version( + resolver: &ArtifactResolver<'_>, + arch: MachineArch, + version: petri_artifacts_vmm_test::LinuxDirectKernelVersion, + ) -> Self { + use petri_artifacts_vmm_test::LinuxDirectKernelVersion; + use petri_artifacts_vmm_test::artifacts::loadable::*; + let kernel = match (arch, version) { + (MachineArch::X86_64, LinuxDirectKernelVersion::V6_1) => { + resolver.require(LINUX_DIRECT_TEST_KERNEL_X64_6_1).erase() + } + (MachineArch::X86_64, LinuxDirectKernelVersion::V6_18) => { + resolver.require(LINUX_DIRECT_TEST_KERNEL_X64_6_18).erase() + } + (MachineArch::Aarch64, LinuxDirectKernelVersion::V6_1) => resolver + .require(LINUX_DIRECT_TEST_KERNEL_AARCH64_6_1) + .erase(), + (MachineArch::Aarch64, LinuxDirectKernelVersion::V6_18) => resolver + .require(LINUX_DIRECT_TEST_KERNEL_AARCH64_6_18) + .erase(), + }; + let initrd = match arch { + MachineArch::X86_64 => resolver.require(LINUX_DIRECT_TEST_INITRD_X64).erase(), + MachineArch::Aarch64 => resolver.require(LINUX_DIRECT_TEST_INITRD_AARCH64).erase(), + }; + Firmware::LinuxDirect { kernel, initrd } + } + /// Constructs a [`Firmware::LinuxDirect`] configuration that uses a /// compressed bzImage kernel instead of an uncompressed ELF. /// @@ -2496,6 +2526,28 @@ impl Firmware { } } + /// Constructs a [`Firmware::LinuxDirect`] bzImage configuration with a + /// specific kernel version. + pub fn linux_direct_bzimage_with_version( + resolver: &ArtifactResolver<'_>, + version: petri_artifacts_vmm_test::LinuxDirectKernelVersion, + ) -> Self { + use petri_artifacts_vmm_test::LinuxDirectKernelVersion; + use petri_artifacts_vmm_test::artifacts::loadable::*; + let kernel = match version { + LinuxDirectKernelVersion::V6_1 => { + resolver.require(LINUX_DIRECT_TEST_BZIMAGE_X64_6_1).erase() + } + LinuxDirectKernelVersion::V6_18 => { + resolver.require(LINUX_DIRECT_TEST_BZIMAGE_X64_6_18).erase() + } + }; + Firmware::LinuxDirect { + kernel, + initrd: resolver.require(LINUX_DIRECT_TEST_INITRD_X64).erase(), + } + } + /// Constructs a standard [`Firmware::OpenhclLinuxDirect`] configuration. pub fn openhcl_linux_direct(resolver: &ArtifactResolver<'_>, arch: MachineArch) -> Self { use petri_artifacts_vmm_test::artifacts::openhcl_igvm::*; diff --git a/vmm_tests/petri_artifact_resolver_openvmm_known_paths/src/lib.rs b/vmm_tests/petri_artifact_resolver_openvmm_known_paths/src/lib.rs index 3b392cc672..f77c983516 100644 --- a/vmm_tests/petri_artifact_resolver_openvmm_known_paths/src/lib.rs +++ b/vmm_tests/petri_artifact_resolver_openvmm_known_paths/src/lib.rs @@ -71,6 +71,14 @@ impl petri_artifacts_core::ResolveTestArtifact for OpenvmmKnownPathsTestArtifact _ if id == loadable::LINUX_DIRECT_TEST_INITRD_X64 => linux_direct_test_initrd_path(MachineArch::X86_64), _ if id == loadable::LINUX_DIRECT_TEST_INITRD_AARCH64 => linux_direct_test_initrd_path(MachineArch::Aarch64), + // Version-specific kernel artifacts + _ if id == loadable::LINUX_DIRECT_TEST_KERNEL_X64_6_1 => linux_direct_versioned_kernel_path("x64", "6.1", "vmlinux"), + _ if id == loadable::LINUX_DIRECT_TEST_KERNEL_X64_6_18 => linux_direct_versioned_kernel_path("x64", "6.18", "vmlinux"), + _ if id == loadable::LINUX_DIRECT_TEST_KERNEL_AARCH64_6_1 => linux_direct_versioned_kernel_path("aarch64", "6.1", "Image"), + _ if id == loadable::LINUX_DIRECT_TEST_KERNEL_AARCH64_6_18 => linux_direct_versioned_kernel_path("aarch64", "6.18", "Image"), + _ if id == loadable::LINUX_DIRECT_TEST_BZIMAGE_X64_6_1 => linux_direct_versioned_kernel_path("x64", "6.1", "bzImage"), + _ if id == loadable::LINUX_DIRECT_TEST_BZIMAGE_X64_6_18 => linux_direct_versioned_kernel_path("x64", "6.18", "bzImage"), + _ if id == petritools::PETRITOOLS_EROFS_X64 => petritools_erofs_path(MachineArch::X86_64), _ if id == petritools::PETRITOOLS_EROFS_AARCH64 => petritools_erofs_path(MachineArch::Aarch64), @@ -192,6 +200,13 @@ pub fn resolve_bundle_name(id: ErasedArtifactHandle) -> Option<&'static str> { _ if id == loadable::LINUX_DIRECT_TEST_KERNEL_AARCH64 => Some("aarch64/Image"), _ if id == loadable::LINUX_DIRECT_TEST_INITRD_X64 => Some("x64/initrd"), _ if id == loadable::LINUX_DIRECT_TEST_INITRD_AARCH64 => Some("aarch64/initrd"), + // Version-specific kernel bundle names + _ if id == loadable::LINUX_DIRECT_TEST_KERNEL_X64_6_1 => Some("x64/6.1/vmlinux"), + _ if id == loadable::LINUX_DIRECT_TEST_KERNEL_X64_6_18 => Some("x64/6.18/vmlinux"), + _ if id == loadable::LINUX_DIRECT_TEST_KERNEL_AARCH64_6_1 => Some("aarch64/6.1/Image"), + _ if id == loadable::LINUX_DIRECT_TEST_KERNEL_AARCH64_6_18 => Some("aarch64/6.18/Image"), + _ if id == loadable::LINUX_DIRECT_TEST_BZIMAGE_X64_6_1 => Some("x64/6.1/bzImage"), + _ if id == loadable::LINUX_DIRECT_TEST_BZIMAGE_X64_6_18 => Some("x64/6.18/bzImage"), _ if id == petritools::PETRITOOLS_EROFS_X64 => Some("x64/petritools.erofs"), _ if id == petritools::PETRITOOLS_EROFS_AARCH64 => Some("aarch64/petritools.erofs"), _ if id == loadable::UEFI_FIRMWARE_X64 => { @@ -495,6 +510,22 @@ fn linux_direct_arm_image_path() -> anyhow::Result { ) } +/// Path to a version-specific linux direct kernel or bzImage file. +fn linux_direct_versioned_kernel_path( + arch_dir: &str, + version: &str, + filename: &str, +) -> anyhow::Result { + let bundle_name = format!("{arch_dir}/{version}/{filename}"); + get_path( + ".packages/underhill-deps-private", + bundle_name, + MissingCommand::Restore { + description: "version-specific linux direct test kernel", + }, + ) +} + /// Path to our packaged PCAT firmware. fn pcat_firmware_path() -> anyhow::Result { get_path( diff --git a/vmm_tests/petri_artifacts_vmm_test/src/lib.rs b/vmm_tests/petri_artifacts_vmm_test/src/lib.rs index 5b9d1b1382..6717519ab6 100644 --- a/vmm_tests/petri_artifacts_vmm_test/src/lib.rs +++ b/vmm_tests/petri_artifacts_vmm_test/src/lib.rs @@ -5,6 +5,18 @@ #![forbid(unsafe_code)] +/// Which Linux kernel version to use for linux-direct tests. +/// +/// Used by test macros and petri firmware selection to pick +/// version-specific kernel artifacts. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum LinuxDirectKernelVersion { + /// Linux 6.1 + V6_1, + /// Linux 6.18 + V6_18, +} + /// Artifact declarations pub mod artifacts { use petri_artifacts_common::tags::IsVmgsTool; @@ -123,16 +135,31 @@ pub mod artifacts { > = petri_artifacts_core::ArtifactHandle::new(); declare_artifacts! { - /// Test linux direct kernel (from OpenVMM deps) + /// Test linux direct kernel (from OpenVMM deps) — default version LINUX_DIRECT_TEST_KERNEL_X64, /// Test linux direct initrd (from OpenVMM deps) LINUX_DIRECT_TEST_INITRD_X64, - /// Test linux direct kernel (from OpenVMM deps) + /// Test linux direct kernel (from OpenVMM deps) — default version LINUX_DIRECT_TEST_KERNEL_AARCH64, /// Test linux direct initrd (from OpenVMM deps) LINUX_DIRECT_TEST_INITRD_AARCH64, - /// Test linux direct bzImage kernel (from OpenVMM deps) + /// Test linux direct bzImage kernel (from OpenVMM deps) — default version LINUX_DIRECT_TEST_BZIMAGE_X64, + + // Version-specific kernel artifacts + /// Test linux direct kernel 6.1 (from OpenVMM deps) + LINUX_DIRECT_TEST_KERNEL_X64_6_1, + /// Test linux direct kernel 6.18 (from OpenVMM deps) + LINUX_DIRECT_TEST_KERNEL_X64_6_18, + /// Test linux direct kernel 6.1 (from OpenVMM deps) + LINUX_DIRECT_TEST_KERNEL_AARCH64_6_1, + /// Test linux direct kernel 6.18 (from OpenVMM deps) + LINUX_DIRECT_TEST_KERNEL_AARCH64_6_18, + /// Test linux direct bzImage kernel 6.1 (from OpenVMM deps) + LINUX_DIRECT_TEST_BZIMAGE_X64_6_1, + /// Test linux direct bzImage kernel 6.18 (from OpenVMM deps) + LINUX_DIRECT_TEST_BZIMAGE_X64_6_18, + /// PCAT firmware DLL PCAT_FIRMWARE_X64, /// SVGA firmware DLL @@ -163,6 +190,31 @@ pub mod artifacts { const ARCH: MachineArch = MachineArch::X86_64; } + // Version-specific IsLoadable impls + impl IsLoadable for LINUX_DIRECT_TEST_KERNEL_X64_6_1 { + const ARCH: MachineArch = MachineArch::X86_64; + } + + impl IsLoadable for LINUX_DIRECT_TEST_KERNEL_X64_6_18 { + const ARCH: MachineArch = MachineArch::X86_64; + } + + impl IsLoadable for LINUX_DIRECT_TEST_KERNEL_AARCH64_6_1 { + const ARCH: MachineArch = MachineArch::Aarch64; + } + + impl IsLoadable for LINUX_DIRECT_TEST_KERNEL_AARCH64_6_18 { + const ARCH: MachineArch = MachineArch::Aarch64; + } + + impl IsLoadable for LINUX_DIRECT_TEST_BZIMAGE_X64_6_1 { + const ARCH: MachineArch = MachineArch::X86_64; + } + + impl IsLoadable for LINUX_DIRECT_TEST_BZIMAGE_X64_6_18 { + const ARCH: MachineArch = MachineArch::X86_64; + } + impl IsLoadable for PCAT_FIRMWARE_X64 { const ARCH: MachineArch = MachineArch::X86_64; } diff --git a/vmm_tests/vmm_test_macros/src/lib.rs b/vmm_tests/vmm_test_macros/src/lib.rs index 552bb9ea5f..c41ba87843 100644 --- a/vmm_tests/vmm_test_macros/src/lib.rs +++ b/vmm_tests/vmm_test_macros/src/lib.rs @@ -46,15 +46,31 @@ enum Vmm { } enum Firmware { - LinuxDirect, - LinuxDirectBzImage, + LinuxDirect(Option), + LinuxDirectBzImage(Option), Pcat(PcatGuest), Uefi(UefiGuest), - OpenhclLinuxDirect, + OpenhclLinuxDirect(Option), OpenhclPcat(PcatGuest), OpenhclUefi(OpenhclUefiOptions, UefiGuest), } +/// Kernel version selection for linux-direct tests (macro-internal). +#[derive(Clone, Copy)] +enum LinuxKernelVersion { + V6_1, + V6_18, +} + +impl LinuxKernelVersion { + fn name_prefix(self) -> String { + match self { + LinuxKernelVersion::V6_1 => "kernel_6_1".to_string(), + LinuxKernelVersion::V6_18 => "kernel_6_18".to_string(), + } + } +} + #[derive(Default)] struct OpenhclUefiOptions { isolation: Option, @@ -114,6 +130,17 @@ fn arch_to_tokens(arch: MachineArch) -> TokenStream { } } +fn kernel_version_to_tokens(version: LinuxKernelVersion) -> TokenStream { + match version { + LinuxKernelVersion::V6_1 => { + quote!(::petri_artifacts_vmm_test::LinuxDirectKernelVersion::V6_1) + } + LinuxKernelVersion::V6_18 => { + quote!(::petri_artifacts_vmm_test::LinuxDirectKernelVersion::V6_18) + } + } +} + impl ResolvedConfig { fn name_prefix(&self) -> String { let arch_prefix = arch_to_str(self.arch); @@ -124,30 +151,33 @@ impl ResolvedConfig { }; let firmware_prefix = match &self.firmware { - Firmware::LinuxDirect => "linux", - Firmware::LinuxDirectBzImage => "linux_bzimage", + Firmware::LinuxDirect(_) => "linux", + Firmware::LinuxDirectBzImage(_) => "linux_bzimage", Firmware::Pcat(_) => "pcat", Firmware::Uefi(_) => "uefi", - Firmware::OpenhclLinuxDirect => "openhcl_linux", + Firmware::OpenhclLinuxDirect(_) => "openhcl_linux", Firmware::OpenhclPcat(..) => "openhcl_pcat", Firmware::OpenhclUefi(..) => "openhcl_uefi", }; let guest_prefix = match &self.firmware { - Firmware::LinuxDirect | Firmware::LinuxDirectBzImage | Firmware::OpenhclLinuxDirect => { - None - } + Firmware::LinuxDirect(_) + | Firmware::LinuxDirectBzImage(_) + | Firmware::OpenhclLinuxDirect(_) => None, Firmware::Pcat(guest) | Firmware::OpenhclPcat(guest) => Some(guest.name_prefix()), Firmware::Uefi(guest) | Firmware::OpenhclUefi(_, guest) => guest.name_prefix(), }; let options_prefix = match &self.firmware { - Firmware::LinuxDirect - | Firmware::LinuxDirectBzImage + Firmware::LinuxDirect(None) + | Firmware::LinuxDirectBzImage(None) | Firmware::Pcat(_) | Firmware::Uefi(_) - | Firmware::OpenhclLinuxDirect + | Firmware::OpenhclLinuxDirect(None) | Firmware::OpenhclPcat(_) => None, + Firmware::LinuxDirect(Some(v)) + | Firmware::LinuxDirectBzImage(Some(v)) + | Firmware::OpenhclLinuxDirect(Some(v)) => Some(v.name_prefix()), Firmware::OpenhclUefi(opt, _) => opt.name_prefix(), }; @@ -224,19 +254,34 @@ impl ToTokens for FirmwareAndArch { fn to_tokens(&self, tokens: &mut TokenStream) { let arch = arch_to_tokens(self.arch); tokens.extend(match &self.firmware { - Firmware::LinuxDirect => { + Firmware::LinuxDirect(None) => { quote!(::petri::Firmware::linux_direct(resolver, #arch)) } - Firmware::LinuxDirectBzImage => { + Firmware::LinuxDirect(Some(v)) => { + let version = kernel_version_to_tokens(*v); + quote!(::petri::Firmware::linux_direct_with_version(resolver, #arch, #version)) + } + Firmware::LinuxDirectBzImage(None) => { quote!(::petri::Firmware::linux_direct_bzimage(resolver)) } + Firmware::LinuxDirectBzImage(Some(v)) => { + let version = kernel_version_to_tokens(*v); + quote!(::petri::Firmware::linux_direct_bzimage_with_version(resolver, #version)) + } Firmware::Pcat(guest) => { quote!(::petri::Firmware::pcat(resolver, #guest)) } Firmware::Uefi(guest) => { quote!(::petri::Firmware::uefi(resolver, #arch, #guest)) } - Firmware::OpenhclLinuxDirect => { + Firmware::OpenhclLinuxDirect(None) => { + quote!(::petri::Firmware::openhcl_linux_direct(resolver, #arch)) + } + Firmware::OpenhclLinuxDirect(Some(v)) => { + // OpenhclLinuxDirect uses IGVM files and doesn't directly select kernel version. + // For now, kernel version selection for openhcl_linux_direct is a no-op — the + // IGVM file embeds the kernel. Pass through as the default. + let _ = v; quote!(::petri::Firmware::openhcl_linux_direct(resolver, #arch)) } Firmware::OpenhclPcat(guest) => { @@ -405,10 +450,22 @@ impl Parse for Config { }; let (arch, firmware) = match remainder { - "linux_direct_x64" => (MachineArch::X86_64, Firmware::LinuxDirect), - "linux_direct_bzimage_x64" => (MachineArch::X86_64, Firmware::LinuxDirectBzImage), - "linux_direct_aarch64" => (MachineArch::Aarch64, Firmware::LinuxDirect), - "openhcl_linux_direct_x64" => (MachineArch::X86_64, Firmware::OpenhclLinuxDirect), + "linux_direct_x64" => { + let kver = parse_linux_kernel_version(input)?; + (MachineArch::X86_64, Firmware::LinuxDirect(kver)) + } + "linux_direct_bzimage_x64" => { + let kver = parse_linux_kernel_version(input)?; + (MachineArch::X86_64, Firmware::LinuxDirectBzImage(kver)) + } + "linux_direct_aarch64" => { + let kver = parse_linux_kernel_version(input)?; + (MachineArch::Aarch64, Firmware::LinuxDirect(kver)) + } + "openhcl_linux_direct_x64" => { + let kver = parse_linux_kernel_version(input)?; + (MachineArch::X86_64, Firmware::OpenhclLinuxDirect(kver)) + } "pcat_x64" => ( MachineArch::X86_64, Firmware::Pcat(parse_pcat_guest(input)?), @@ -467,6 +524,25 @@ fn parse_uefi_guest(input: ParseStream<'_>) -> syn::Result { parens.parse::() } +/// Parse an optional parenthesized kernel version, e.g. `(kernel_6_1)`. +/// +/// Returns `None` if no parenthesized group follows, allowing the default +/// kernel to be used. +fn parse_linux_kernel_version(input: ParseStream<'_>) -> syn::Result> { + if input.peek(syn::token::Paren) { + let content; + syn::parenthesized!(content in input); + let word = content.parse::()?; + match &*word.to_string() { + "kernel_6_1" => Ok(Some(LinuxKernelVersion::V6_1)), + "kernel_6_18" => Ok(Some(LinuxKernelVersion::V6_18)), + _ => Err(Error::new(word.span(), "unrecognized kernel version")), + } + } else { + Ok(None) + } +} + impl Parse for PcatGuest { fn parse(input: ParseStream<'_>) -> syn::Result { let word = input.parse::()?; @@ -691,8 +767,11 @@ fn parse_extra_deps(input: ParseStream<'_>) -> syn::Result> { /// /// Valid configuration options are: /// - `{vmm}_linux_direct_{arch}`: Our provided Linux direct image +/// - `{vmm}_linux_direct_{arch}()`: Linux direct with a specific kernel version /// - `{vmm}_linux_direct_bzimage_x64`: Our provided Linux direct bzImage (compressed kernel, x86_64 only) +/// - `{vmm}_linux_direct_bzimage_x64()`: bzImage with a specific kernel version /// - `{vmm}_openhcl_linux_direct_{arch}`: Our provided Linux direct image with OpenHCL +/// - `{vmm}_openhcl_linux_direct_{arch}()`: OpenHCL Linux direct with a specific kernel version /// - `{vmm}_pcat_{arch}()`: A Gen 1 configuration /// - `{vmm}_uefi_{arch}()`: A Gen 2 configuration /// - `{vmm}_openhcl_pcat_{arch}()`: A Gen 1 configuration with OpenHCL @@ -739,6 +818,10 @@ fn parse_extra_deps(input: ParseStream<'_>) -> syn::Result> { /// - `snp`: Use SNP isolation. /// - `tdx`: Use TDX isolation. /// +/// Valid kernel version options are: +/// - `kernel_6_1`: Linux 6.1 kernel +/// - `kernel_6_18`: Linux 6.18 kernel +/// /// Each configuration can be optionally followed by a square-bracketed, comma-separated /// list of additional artifacts required for that particular configuration. #[proc_macro_attribute] diff --git a/vmm_tests/vmm_tests/tests/tests/multiarch/pcie.rs b/vmm_tests/vmm_tests/tests/tests/multiarch/pcie.rs index fa90a937ca..634a2249b6 100644 --- a/vmm_tests/vmm_tests/tests/tests/multiarch/pcie.rs +++ b/vmm_tests/vmm_tests/tests/tests/multiarch/pcie.rs @@ -275,7 +275,7 @@ async fn pcie_switches(config: PetriVmBuilder) -> anyhow::R /// /// NOTE: This test relies on device specific software (drivers, /// tooling) within the guest OS to perform the validation. -#[openvmm_test(unstable_linux_direct_x64)] +#[openvmm_test(unstable_linux_direct_x64(kernel_6_1))] async fn pcie_devices(config: PetriVmBuilder) -> anyhow::Result<()> { let os_flavor = config.os_flavor(); let (vm, agent) = config