From 5a61f444373e489a25ef064416220a8078f5add5 Mon Sep 17 00:00:00 2001 From: Ming-Wei Shih Date: Tue, 25 Nov 2025 00:43:09 +0000 Subject: [PATCH 01/32] support test igvmagent for vmm tests Signed-off-by: Ming-Wei Shih --- Cargo.lock | 35 +- Cargo.toml | 3 + openhcl/underhill_attestation/Cargo.toml | 2 +- openvmm/openvmm_resources/Cargo.toml | 2 +- .../get/guest_emulation_device/Cargo.toml | 5 +- .../get/guest_emulation_device/src/lib.rs | 81 +---- .../src/lib.rs} | 106 +++++-- .../src/test_crypto.rs | 3 +- .../get/test_igvm_agent_rpc_server/Cargo.toml | 30 ++ .../get/test_igvm_agent_rpc_server/build.rs | 298 ++++++++++++++++++ .../idl/IGVmAgentRpcApi.idl | 86 +++++ .../test_igvm_agent_rpc_server/src/main.rs | 45 +++ .../src/rpc/handlers.rs | 263 ++++++++++++++++ .../src/rpc/igvm_agent.rs | 57 ++++ .../test_igvm_agent_rpc_server/src/rpc/mod.rs | 8 + .../src/rpc/server.rs | 153 +++++++++ .../src/lib.rs | 17 + vmm_tests/petri_artifacts_vmm_test/src/lib.rs | 10 + vmm_tests/vmm_tests/tests/tests/x86_64/tpm.rs | 76 +++-- 19 files changed, 1154 insertions(+), 126 deletions(-) rename vm/devices/get/{guest_emulation_device/src/test_igvm_agent.rs => test_igvm_agent_lib/src/lib.rs} (90%) rename vm/devices/get/{guest_emulation_device => test_igvm_agent_lib}/src/test_crypto.rs (99%) create mode 100644 vm/devices/get/test_igvm_agent_rpc_server/Cargo.toml create mode 100644 vm/devices/get/test_igvm_agent_rpc_server/build.rs create mode 100644 vm/devices/get/test_igvm_agent_rpc_server/idl/IGVmAgentRpcApi.idl create mode 100644 vm/devices/get/test_igvm_agent_rpc_server/src/main.rs create mode 100644 vm/devices/get/test_igvm_agent_rpc_server/src/rpc/handlers.rs create mode 100644 vm/devices/get/test_igvm_agent_rpc_server/src/rpc/igvm_agent.rs create mode 100644 vm/devices/get/test_igvm_agent_rpc_server/src/rpc/mod.rs create mode 100644 vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs diff --git a/Cargo.lock b/Cargo.lock index 59688b712b..6cdc8bb9b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2619,7 +2619,6 @@ name = "guest_emulation_device" version = "0.0.0" dependencies = [ "async-trait", - "base64 0.22.1", "disk_backend", "disklayer_ram", "futures", @@ -2634,11 +2633,10 @@ dependencies = [ "pal_async", "parking_lot", "power_resources", - "rsa", "scsi_buffers", "serde_json", - "sha2", "task_control", + "test_igvm_agent_lib", "thiserror 2.0.16", "tracelimit", "tracing", @@ -7218,6 +7216,37 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "test_igvm_agent_lib" +version = "0.0.0" +dependencies = [ + "base64 0.22.1", + "get_resources", + "inspect", + "openhcl_attestation_protocol", + "rsa", + "serde_json", + "sha2", + "thiserror 2.0.16", + "tracing", + "zerocopy 0.8.25", +] + +[[package]] +name = "test_igvm_agent_rpc_server" +version = "0.0.0" +dependencies = [ + "cc", + "cfg-if", + "guid", + "parking_lot", + "serde_json", + "test_igvm_agent_lib", + "tracing", + "tracing-subscriber", + "windows-sys 0.59.0", +] + [[package]] name = "test_with_tracing" version = "0.0.0" diff --git a/Cargo.toml b/Cargo.toml index f627aacb47..e2dfd7dedc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,6 +44,7 @@ members = [ # tools "petri/make_imc_hive", "petri/petri-tool", + "vm/devices/get/test_igvm_agent_rpc_server", "vm/devices/tpm/tpm_guest_tests", "vm/loader/igvmfilegen", "vm/vmgs/vmgs_lib", @@ -222,6 +223,8 @@ get_protocol = { path = "vm/devices/get/get_protocol" } get_resources = { path = "vm/devices/get/get_resources" } guest_crash_device = { path = "vm/devices/get/guest_crash_device" } guest_emulation_device = { path = "vm/devices/get/guest_emulation_device" } +test_igvm_agent_lib = { path = "vm/devices/get/test_igvm_agent_lib" } +test_igvm_agent_rpc_server = { path = "vm/devices/get/test_igvm_agent_rpc_server" } guest_emulation_log = { path = "vm/devices/get/guest_emulation_log" } guest_emulation_transport = { path = "vm/devices/get/guest_emulation_transport" } vtl2_settings_proto = { path = "vm/devices/get/vtl2_settings_proto" } diff --git a/openhcl/underhill_attestation/Cargo.toml b/openhcl/underhill_attestation/Cargo.toml index 63aeae3537..6f2085924c 100644 --- a/openhcl/underhill_attestation/Cargo.toml +++ b/openhcl/underhill_attestation/Cargo.toml @@ -41,7 +41,7 @@ disk_backend.workspace = true vmgs = { workspace = true, features = ["encryption_ossl", "test_helpers"] } vmgs_format.workspace = true guest_emulation_transport = { workspace = true, features = ["test_utilities"] } -guest_emulation_device = { workspace = true, features = ["test_igvm_agent"] } +guest_emulation_device.workspace = true test_with_tracing.workspace = true user_driver_emulated_mock.workspace = true diff --git a/openvmm/openvmm_resources/Cargo.toml b/openvmm/openvmm_resources/Cargo.toml index 3f42329970..bc6ab1dc2e 100644 --- a/openvmm/openvmm_resources/Cargo.toml +++ b/openvmm/openvmm_resources/Cargo.toml @@ -78,7 +78,7 @@ virtio_pmem.workspace = true # Vmbus devices guest_crash_device.workspace = true -guest_emulation_device = { workspace = true, features = ["test_igvm_agent"] } +guest_emulation_device.workspace = true guest_emulation_log.workspace = true hyperv_ic.workspace = true netvsp.workspace = true diff --git a/vm/devices/get/guest_emulation_device/Cargo.toml b/vm/devices/get/guest_emulation_device/Cargo.toml index 05ccf15623..105f40446f 100644 --- a/vm/devices/get/guest_emulation_device/Cargo.toml +++ b/vm/devices/get/guest_emulation_device/Cargo.toml @@ -8,11 +8,11 @@ rust-version.workspace = true [features] test_utilities = ["dep:disklayer_ram"] -test_igvm_agent = ["dep:base64", "dep:sha2", "dep:rsa"] [dependencies] get_protocol.workspace = true get_resources.workspace = true +test_igvm_agent_lib.workspace = true disk_backend.workspace = true disklayer_ram = { workspace = true, optional = true } @@ -35,14 +35,11 @@ pal_async.workspace = true task_control.workspace = true tracelimit.workspace = true -base64 = { workspace = true, optional = true } futures.workspace = true parking_lot.workspace = true serde_json = { workspace = true, features = ["std"] } -sha2 = { workspace = true, optional = true } thiserror.workspace = true jiff.workspace = true -rsa = { workspace = true, optional = true } tracing.workspace = true zerocopy.workspace = true guid.workspace = true diff --git a/vm/devices/get/guest_emulation_device/src/lib.rs b/vm/devices/get/guest_emulation_device/src/lib.rs index ec1e3c98a3..d1adff826e 100644 --- a/vm/devices/get/guest_emulation_device/src/lib.rs +++ b/vm/devices/get/guest_emulation_device/src/lib.rs @@ -16,15 +16,10 @@ pub mod resolver; #[cfg(feature = "test_utilities")] pub mod test_utilities; -#[cfg(feature = "test_igvm_agent")] -mod test_igvm_agent; - -#[cfg(feature = "test_igvm_agent")] -mod test_crypto; - -#[cfg(feature = "test_igvm_agent")] -use crate::test_igvm_agent::TestIgvmAgent; - +pub use test_igvm_agent_lib::IgvmAgentAction; +pub use test_igvm_agent_lib::IgvmAgentTestPlan; +pub use test_igvm_agent_lib::IgvmAgentTestSetting; +use test_igvm_agent_lib::TestIgvmAgent; use async_trait::async_trait; use core::mem::size_of; use disk_backend::Disk; @@ -52,7 +47,6 @@ use get_protocol::dps_json::PcatBootDevice; use get_resources::ged::FirmwareEvent; use get_resources::ged::GuestEmulationRequest; use get_resources::ged::GuestServicingFlags; -use get_resources::ged::IgvmAttestTestConfig; use get_resources::ged::ModifyVtl2SettingsError; use get_resources::ged::SaveRestoreError; use get_resources::ged::Vtl0StartError; @@ -66,12 +60,9 @@ use jiff::civil::date; use jiff::tz::TimeZone; use mesh::error::RemoteError; use mesh::rpc::Rpc; -use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestType; use power_resources::PowerRequest; use power_resources::PowerRequestClient; use scsi_buffers::OwnedRequestBuffers; -use std::collections::HashMap; -use std::collections::VecDeque; use std::io::IoSlice; use task_control::StopTask; use thiserror::Error; @@ -117,9 +108,8 @@ enum Error { LargeDpsV2Unimplemented, #[error("invalid IGVM_ATTEST request")] InvalidIgvmAttestRequest, - #[cfg(feature = "test_igvm_agent")] #[error("test IGVM agent error")] - TestIgvmAgent(#[source] test_igvm_agent::Error), + TestIgvmAgent(#[source] test_igvm_agent_lib::Error), #[error("failed to write to shared memory")] SharedMemoryWriteFailed(#[source] guestmem::GuestMemoryError), } @@ -207,43 +197,6 @@ pub enum GuestEvent { BootAttempt, } -/// Possible actions for the IGVM agent to take in response to a request. -#[derive(Debug, Clone)] -pub enum IgvmAgentAction { - RespondSuccess, - RespondFailure, - NoResponse, -} - -/// IGVM Agent test plan that specifies the list of action for a given request type. -pub type IgvmAgentTestPlan = HashMap>; - -/// IGVM Agent test setting. Custom Inspect impl avoids requiring HashMap to implement Inspect. -#[derive(Debug)] -pub enum IgvmAgentTestSetting { - /// Use test config that will be mapped to a plan. Used when creating GED via `GuestEmulationDeviceHandle` - /// (VMM tests). - TestConfig(IgvmAttestTestConfig), - /// Use test plan. Used when creating GED via test_utilities (unit tests). - TestPlan(IgvmAgentTestPlan), -} - -impl Inspect for IgvmAgentTestSetting { - fn inspect(&self, req: inspect::Request<'_>) { - let mut resp = req.respond(); - match self { - Self::TestConfig(cfg) => { - resp.field("TestConfig", cfg); - } - Self::TestPlan(plan) => { - // Only expose summary to avoid needing Inspect on HashMap. - let len = plan.len(); - resp.field("TestPlan len", len); - } - } - } -} - /// VMBUS device that implements the host side of the Guest Emulation Transport protocol. #[derive(InspectMut)] pub struct GuestEmulationDevice { @@ -268,7 +221,6 @@ pub struct GuestEmulationDevice { igvm_agent_setting: Option, - #[cfg(feature = "test_igvm_agent")] /// Test agent implementation for `handle_igvm_attest` #[inspect(skip)] igvm_agent: TestIgvmAgent, @@ -310,7 +262,6 @@ impl GuestEmulationDevice { waiting_for_vtl0_start: Vec::new(), last_save_restore_buf_len: 0, igvm_agent_setting, - #[cfg(feature = "test_igvm_agent")] igvm_agent: TestIgvmAgent::new(), test_gsp_by_id, } @@ -934,22 +885,14 @@ impl GedChannel { } let (response_payload, length) = { - #[cfg(feature = "test_igvm_agent")] - { - if let Some(setting) = &state.igvm_agent_setting { - state.igvm_agent.install_plan_from_setting(setting); - } - - state - .igvm_agent - .handle_request(&request.report[..request.report_length as usize]) - .map_err(Error::TestIgvmAgent)? - } - #[cfg(not(feature = "test_igvm_agent"))] - { - tracing::warn!("Test IGVM agent feature not enabled, returning empty response"); - (&[][..], 0) + if let Some(setting) = &state.igvm_agent_setting { + state.igvm_agent.install_plan_from_setting(setting); } + + state + .igvm_agent + .handle_request(&request.report[..request.report_length as usize]) + .map_err(Error::TestIgvmAgent)? }; // Write the response payload to the guest's shared memory diff --git a/vm/devices/get/guest_emulation_device/src/test_igvm_agent.rs b/vm/devices/get/test_igvm_agent_lib/src/lib.rs similarity index 90% rename from vm/devices/get/guest_emulation_device/src/test_igvm_agent.rs rename to vm/devices/get/test_igvm_agent_lib/src/lib.rs index 946b39dc75..61ef98651b 100644 --- a/vm/devices/get/guest_emulation_device/src/test_igvm_agent.rs +++ b/vm/devices/get/test_igvm_agent_lib/src/lib.rs @@ -8,9 +8,8 @@ //! NOTE: This is a test implementation and should not be used in production. -use crate::IgvmAgentAction; -use crate::IgvmAgentTestPlan; -use crate::IgvmAgentTestSetting; +mod test_crypto; + use crate::test_crypto::DummyRng; use crate::test_crypto::TestSha1; use crate::test_crypto::aes_key_wrap_with_padding; @@ -36,16 +35,15 @@ use rsa::rand_core::RngCore; use rsa::rand_core::SeedableRng; use sha2::Sha256; use std::collections::VecDeque; -use std::sync::Once; +use std::collections::HashMap; use thiserror::Error; +use inspect::Inspect; use zerocopy::FromBytes; use zerocopy::IntoBytes; -// Support one-time initialization for `install_plan_from_setting`. -static INIT: Once = Once::new(); - +#[expect(missing_docs)] // self-explanatory fields #[derive(Debug, Error)] -pub(crate) enum Error { +pub enum Error { #[error("unsupported igvm attest request type: {0:?}")] UnsupportedIgvmAttestRequestType(u32), #[error("failed to initialize keys for attestation")] @@ -65,8 +63,9 @@ pub(crate) enum Error { KeyReleaseError(#[source] KeyReleaseError), } +#[expect(missing_docs)] // self-explanatory fields #[derive(Debug, Error)] -pub(crate) enum WrappedKeyError { +pub enum WrappedKeyError { #[error("RSA encryption error")] RsaEncryptionError(#[source] rsa::Error), #[error("JSON serialization error")] @@ -77,8 +76,9 @@ pub(crate) enum WrappedKeyError { SecretKeyNotInitialized, } +#[expect(missing_docs)] // self-explanatory fields #[derive(Debug, Error)] -pub(crate) enum KeyReleaseError { +pub enum KeyReleaseError { #[error("invalid runtime claims")] InvalidRuntimeClaims, #[error("missing transfer key in runtime claims")] @@ -97,13 +97,53 @@ pub(crate) enum KeyReleaseError { /// Test IGVM agent includes states that need to be persisted. #[derive(Debug, Clone, Default)] -pub(crate) struct TestIgvmAgent { +pub struct TestIgvmAgent { /// Optional RSA private key used for attestation. secret_key: Option, /// Optional DES key des_key: Option<[u8; 32]>, /// Optional scripted actions per request type for tests. plan: Option, + /// Track whether the plan has been installed to prevent multiple installations. + plan_installed: bool, +} + +/// Possible actions for the IGVM agent to take in response to a request. +#[derive(Debug, Clone)] +pub enum IgvmAgentAction { + /// Emit a successful response payload. + RespondSuccess, + /// Emit a response that indicates a protocol error. + RespondFailure, + /// Skip responding to simulate a timeout. + NoResponse, +} + +/// IGVM Agent test plan specifying scripted actions for a request type. +pub type IgvmAgentTestPlan = HashMap>; + +/// Settings used to configure the IGVM agent for tests. +#[derive(Debug, Clone)] +pub enum IgvmAgentTestSetting { + /// Use a pre-defined test configuration that maps to a plan. + TestConfig(IgvmAttestTestConfig), + /// Use a manually provided plan. + TestPlan(IgvmAgentTestPlan), +} + +impl Inspect for IgvmAgentTestSetting { + fn inspect(&self, req: inspect::Request<'_>) { + let mut resp = req.respond(); + match self { + Self::TestConfig(cfg) => { + resp.field("TestConfig", cfg); + } + Self::TestPlan(plan) => { + let len = plan.len(); + resp.field("TestPlan len", len); + } + } + } } fn test_config_to_plan(test_config: &IgvmAttestTestConfig) -> IgvmAgentTestPlan { @@ -133,32 +173,39 @@ fn test_config_to_plan(test_config: &IgvmAttestTestConfig) -> IgvmAgentTestPlan impl TestIgvmAgent { /// Create an instance. - pub(crate) fn new() -> Self { + pub fn new() -> Self { Self { secret_key: None, des_key: None, plan: None, + plan_installed: false, } } - /// Install a scripted plan used by tests based on the setting (one-time only). Allow to be called multiple times. + /// Install a scripted plan used by tests based on the setting. + /// Can be called multiple times but will only install the plan once per instance. pub fn install_plan_from_setting(&mut self, setting: &IgvmAgentTestSetting) { - INIT.call_once(|| { - tracing::info!("install the scripted plan for test IGVM Agent"); + // Only install the plan once per agent instance + if self.plan_installed { + return; + } - match setting { - IgvmAgentTestSetting::TestPlan(plan) => { - self.plan = Some(plan.clone()); - } - IgvmAgentTestSetting::TestConfig(config) => { - self.plan = Some(test_config_to_plan(config)); - } + tracing::info!("install the scripted plan for test IGVM Agent"); + + match setting { + IgvmAgentTestSetting::TestPlan(plan) => { + self.plan = Some(plan.clone()); } - }); + IgvmAgentTestSetting::TestConfig(config) => { + self.plan = Some(test_config_to_plan(config)); + } + } + + self.plan_installed = true; } /// Take the next scripted action for the given request type, if any. - pub(crate) fn take_next_action( + pub fn take_next_action( &mut self, request_type: IgvmAttestRequestType, ) -> Option { @@ -167,7 +214,8 @@ impl TestIgvmAgent { plan.get_mut(&request_type)?.pop_front() } - pub(crate) fn handle_request(&mut self, request_bytes: &[u8]) -> Result<(Vec, u32), Error> { + /// Request handler. + pub fn handle_request(&mut self, request_bytes: &[u8]) -> Result<(Vec, u32), Error> { let request = IgvmAttestRequestBase::read_from_prefix(request_bytes) .map_err(|_| Error::InvalidIgvmAttestRequest)? .0; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759) @@ -400,7 +448,7 @@ impl TestIgvmAgent { Ok((response, length)) } - pub(crate) fn initialize_keys(&mut self) -> Result<(), Error> { + fn initialize_keys(&mut self) -> Result<(), Error> { if self.secret_key.is_some() && self.des_key.is_some() { // Keys are already initialized, nothing to do. return Ok(()); @@ -425,7 +473,7 @@ impl TestIgvmAgent { Ok(()) } - pub(crate) fn generate_mock_wrapped_key_response(&self) -> Result, WrappedKeyError> { + fn generate_mock_wrapped_key_response(&self) -> Result, WrappedKeyError> { use openhcl_attestation_protocol::igvm_attest::cps; // Ensure DES key is available @@ -483,7 +531,7 @@ impl TestIgvmAgent { } /// Generate a mock JWT response for testing KEY_RELEASE_REQUEST - pub(crate) fn generate_mock_key_release_response( + fn generate_mock_key_release_response( &self, runtime_claims_bytes: &[u8], ) -> Result { @@ -527,7 +575,7 @@ impl TestIgvmAgent { } /// Generate a mock JWT response for testing KEY_RELEASE_REQUEST - pub(crate) fn generate_jwt_with_rsa_key( + fn generate_jwt_with_rsa_key( &self, public_key: RsaPublicKey, ) -> Result { diff --git a/vm/devices/get/guest_emulation_device/src/test_crypto.rs b/vm/devices/get/test_igvm_agent_lib/src/test_crypto.rs similarity index 99% rename from vm/devices/get/guest_emulation_device/src/test_crypto.rs rename to vm/devices/get/test_igvm_agent_lib/src/test_crypto.rs index ad01fdf777..8530bb5401 100644 --- a/vm/devices/get/guest_emulation_device/src/test_crypto.rs +++ b/vm/devices/get/test_igvm_agent_lib/src/test_crypto.rs @@ -12,7 +12,8 @@ use rsa::rand_core::CryptoRng; use rsa::rand_core::RngCore; use rsa::rand_core::SeedableRng; use sha2::digest; -use sha2::digest::consts::{U20, U64}; +use sha2::digest::consts::U20; +use sha2::digest::consts::U64; use sha2::digest::core_api::BlockSizeUser; /// Minimal, non-constant-time SHA-1 implementation sufficient to satisfy the diff --git a/vm/devices/get/test_igvm_agent_rpc_server/Cargo.toml b/vm/devices/get/test_igvm_agent_rpc_server/Cargo.toml new file mode 100644 index 0000000000..9cacb89d69 --- /dev/null +++ b/vm/devices/get/test_igvm_agent_rpc_server/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "test_igvm_agent_rpc_server" +edition.workspace = true +rust-version.workspace = true + +[dependencies] +test_igvm_agent_lib.workspace = true + +guid.workspace = true + +cfg-if.workspace = true +parking_lot.workspace = true +tracing.workspace = true +tracing-subscriber = { workspace = true, features = ["env-filter"] } + +[target.'cfg(windows)'.dependencies] +windows-sys = { version = "0.59", features = [ + "Win32_Foundation", + "Win32_System_Memory", + "Win32_System_Rpc", + "Win32_System_Console", + "Win32_System_Com", +] } + +[build-dependencies] +cc = "1" +serde_json = "1" + +[lints] +workspace = true diff --git a/vm/devices/get/test_igvm_agent_rpc_server/build.rs b/vm/devices/get/test_igvm_agent_rpc_server/build.rs new file mode 100644 index 0000000000..846f913066 --- /dev/null +++ b/vm/devices/get/test_igvm_agent_rpc_server/build.rs @@ -0,0 +1,298 @@ +//! Build script that compiles Windows RPC stubs for the IGVM agent façade. + +use serde_json::Value; +use std::env; +use std::ffi::OsString; +use std::path::{Component, Path, PathBuf}; +use std::process::Command; + +struct CrossConfig { + include: Vec, + lib: Vec, + bin_dirs: Vec, +} + +fn main() { + println!("cargo:rerun-if-changed=idl/IGVmAgentRpcApi.idl"); + println!("cargo:rerun-if-env-changed=MIDL"); + + let target = env::var("TARGET").unwrap_or_default(); + let target_env = target.replace('-', "_"); + println!("cargo:rerun-if-env-changed=MIDLRT_{}", target_env); + + let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap_or_default(); + if target_os != "windows" { + // Stub interface is only needed when targeting Windows. + return; + } + + let host = env::var("HOST").unwrap_or_default(); + let host_is_windows = host.contains("windows"); + let midl_info = locate_midl(&target_env); + + if !host_is_windows && midl_info.is_none() { + panic!( + "MIDL compiler is required to build for Windows targets. Install the Windows SDK with MIDL compiler, or set MIDL environment variable to point to a cross-compilation MIDL tool." + ); + } + + let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR not set")); + let idl_path = Path::new("idl/IGVmAgentRpcApi.idl"); + + let (midl, uses_cross_tool) = midl_info.unwrap_or_else(|| ("midl".to_owned(), false)); + let mut cmd = Command::new(&midl); + cmd.arg("/nologo"); + + let cross_cfg = if !host_is_windows { + match load_cross_config(&target) { + Ok(cfg) => Some(cfg), + Err(err) => { + panic!("Failed to load cross-compilation configuration: {err}"); + } + } + } else { + None + }; + + match (uses_cross_tool, cross_cfg.as_ref()) { + (true, Some(cfg)) => { + if let Some(cl_path) = locate_cl_exe(cfg) { + if let Some(bin_dir) = cl_path.parent() { + let mut path_value = env::var_os("PATH").unwrap_or_else(OsString::new); + if !path_value.is_empty() { + path_value.push(":"); + } + path_value.push(bin_dir.as_os_str()); + cmd.env("PATH", &path_value); + + let mut wslenv = env::var("WSLENV").unwrap_or_default(); + if !wslenv.split(':').any(|entry| entry == "PATH/wl") { + if !wslenv.is_empty() { + wslenv.push(':'); + } + wslenv.push_str("PATH/wl"); + } + cmd.env("WSLENV", wslenv); + } else { + panic!( + "Missing parent directory for cl.exe in cross-compilation configuration" + ); + } + } else { + panic!( + "Unable to locate cl.exe in cross-compilation configuration" + ); + } + } + (false, Some(cfg)) => { + if let Err(err) = configure_cross_env(&mut cmd, cfg) { + panic!("Failed to configure cross-compilation environment: {err}"); + } + } + _ => {} + } + + let pointer_width = + env::var("CARGO_CFG_TARGET_POINTER_WIDTH").unwrap_or_else(|_| "64".to_owned()); + if pointer_width == "32" { + cmd.args(["/env", "win32"]); + } else { + cmd.args(["/env", "x64"]); + } + + let out_dir_arg = path_for_midl(&out_dir, host_is_windows); + cmd.arg("/out"); + cmd.arg(&out_dir_arg); + + let idl_canon = idl_path + .canonicalize() + .expect("failed to canonicalize IDL path"); + let idl_arg = path_for_midl(&idl_canon, host_is_windows); + cmd.arg(&idl_arg); + + let status = cmd.status().unwrap_or_else(|err| { + panic!( + "Failed to execute MIDL `{midl}`: {err}. Install the Windows MIDL compiler." + ) + }); + if !status.success() { + panic!("midl failed: status {status}"); + } + + let stub_c = out_dir.join("IGVmAgentRpcApi_s.c"); + + if !stub_c.exists() { + panic!("MIDL did not produce expected stub: {}", stub_c.display()); + } + + cc::Build::new() + .file(&stub_c) + .include(&out_dir) + .compile("igvm_agent_rpc_stub"); + + println!("cargo:rustc-link-lib=Rpcrt4"); +} + +fn locate_midl(target_env: &str) -> Option<(String, bool)> { + let key = format!("MIDLRT_{}", target_env); + if let Ok(path) = env::var(&key) { + if !path.is_empty() { + return Some((path, true)); + } + } + + if let Ok(path) = env::var("MIDL") { + if !path.is_empty() { + return Some((path, false)); + } + } + + None +} + +fn configure_cross_env(cmd: &mut Command, cfg: &CrossConfig) -> Result<(), String> { + let include = join_windows_paths(&cfg.include)?; + let lib = join_windows_paths(&cfg.lib)?; + let mut path_entries = join_windows_paths(&cfg.bin_dirs)?; + + if let Some(existing) = env::var_os("PATH") { + if !existing.is_empty() { + if !path_entries.is_empty() { + path_entries.push(";"); + } + path_entries.push(existing); + } + } + + cmd.env("INCLUDE", &include); + cmd.env("LIB", &lib); + cmd.env("PATH", &path_entries); + + Ok(()) +} + +fn load_cross_config(target: &str) -> Result { + let arch = target + .split('-') + .next() + .ok_or_else(|| "could not determine target arch".to_string())?; + let tool = env::var_os("OPENVMM_WINDOWS_CROSS_TOOL") + .ok_or_else(|| "OPENVMM_WINDOWS_CROSS_TOOL not set".to_string())?; + + let output = Command::new(&tool) + .arg("--arch") + .arg(arch) + .arg("--dump") + .output() + .map_err(|err| format!("failed to run cross tool `{tool:?}`: {err}"))?; + + if !output.status.success() { + return Err("cross tool did not return success".to_string()); + } + + let value: Value = serde_json::from_slice(&output.stdout) + .map_err(|err| format!("failed to parse cross tool output: {err}"))?; + + let include = extract_paths(&value, "include")?; + let lib = extract_paths(&value, "lib")?; + let mut bin_dirs = extract_paths(&value, "sdk")?; + + if let Some(msvc_bin) = msvc_bin_dir(&include, arch) { + bin_dirs.push(msvc_bin); + } + + Ok(CrossConfig { + include, + lib, + bin_dirs, + }) +} + +fn extract_paths(value: &Value, key: &str) -> Result, String> { + let array = value + .get(key) + .and_then(|v| v.as_array()) + .ok_or_else(|| format!("missing `{key}` array in cross tool output"))?; + + let mut paths = Vec::new(); + for entry in array { + let path = entry + .as_str() + .ok_or_else(|| format!("invalid `{key}` entry in cross tool output"))?; + paths.push(PathBuf::from(path)); + } + + if paths.is_empty() { + return Err(format!("no entries found for `{key}`")); + } + + Ok(paths) +} + +fn msvc_bin_dir(include_paths: &[PathBuf], target_arch: &str) -> Option { + for include in include_paths { + let mut base = include.parent()?.to_path_buf(); + base.push("bin"); + base.push("Hostx64"); + base.push(match target_arch { + "aarch64" | "arm64" => "arm64", + _ => "x64", + }); + + if base.join("clang-cl.exe").exists() || base.join("cl.exe").exists() { + return Some(base); + } + } + + None +} + +fn locate_cl_exe(cfg: &CrossConfig) -> Option { + for dir in &cfg.bin_dirs { + let candidate = dir.join("cl.exe"); + if candidate.exists() { + return Some(candidate); + } + } + + None +} + +fn join_windows_paths(paths: &[PathBuf]) -> Result { + let mut parts = Vec::with_capacity(paths.len()); + for path in paths { + let converted = path_for_midl(path, false); + let piece = converted.to_string_lossy().into_owned(); + if piece.is_empty() { + return Err(format!( + "unable to convert path `{}` to Windows format", + path.display() + )); + } + parts.push(piece); + } + + Ok(OsString::from(parts.join(";"))) +} + +fn path_for_midl(path: &Path, host_is_windows: bool) -> OsString { + if host_is_windows { + return path.as_os_str().to_os_string(); + } + + if matches!(path.components().next(), Some(Component::Prefix(_))) { + return path.as_os_str().to_os_string(); + } + + match Command::new("wslpath").arg("-w").arg(path).output() { + Ok(output) if output.status.success() => { + let converted = String::from_utf8_lossy(&output.stdout).trim().to_owned(); + if converted.is_empty() { + path.as_os_str().to_os_string() + } else { + OsString::from(converted) + } + } + _ => path.as_os_str().to_os_string(), + } +} diff --git a/vm/devices/get/test_igvm_agent_rpc_server/idl/IGVmAgentRpcApi.idl b/vm/devices/get/test_igvm_agent_rpc_server/idl/IGVmAgentRpcApi.idl new file mode 100644 index 0000000000..e81f0cb3f1 --- /dev/null +++ b/vm/devices/get/test_igvm_agent_rpc_server/idl/IGVmAgentRpcApi.idl @@ -0,0 +1,86 @@ +/*++ + +Copyright (c) Microsoft Corporation + +Abstract: + + IGVM Agent RPC Interface Definition File. + +--*/ + +cpp_quote("static WCHAR IGVM_AGENT_RPC_ENDPOINT[] = L\"IGVM_AGENT_RPC_SERVER\";") + +import "oaidl.idl"; +import "ocidl.idl"; + +[ + uuid(4f92949d-e1b6-468d-b6ba-731b591edccf), + version(1.0), + pointer_default(unique) +] +interface IGVmAgentRpcApi +{ + const UINT32 GspMaxClearSize = 64; + const UINT32 GspMaxCipherSize = 512; + const UINT32 GspMaxCount = 2; + const UINT32 StatusFlagNoError = 0x0; + const UINT32 StatusFlagStateRefreshRequest = 0x1; + const UINT32 StatusFlagSupportsAgentRequired = 0x2; + + typedef struct + { + UINT32 Length; + BYTE Buffer[GspMaxClearSize]; + } GspClear; + + typedef struct + { + UINT32 Length; + BYTE Buffer[GspMaxCipherSize]; + } GspCipher; + + typedef struct + { + GspClear NewGsp; + GspCipher EncryptedGsp[GspMaxCount]; + UINT32 SupportedStatusFlags; + } GspRequestInfo; + + typedef struct + { + GspCipher EncryptedGsp; + GspClear DecryptedGsp[GspMaxCount]; + UINT32 ResponseStatusFlags; + } GspResponseInfo; + + HRESULT + RpcIGVmAttest( + [in] handle_t BindingHandle, + [in] GUID VmId, + [in] GUID RequestId, + [in, ref, string] + LPCWSTR VmName, + [in, range(0, 2048)] + UINT32 AgentDataSize, + [in, ref, size_is(AgentDataSize)] + BYTE* AgentData, + UINT32 ReportSize, + [in, ref, size_is(ReportSize)] + BYTE* Report, + [in, range(0, 65536)] + UINT32 ResponseBufferSize, + [out] UINT32* ResponseWrittenSize, + [out, ref, size_is(ResponseBufferSize), length_is(*ResponseWrittenSize)] + BYTE* Response + ); + + HRESULT + RpcVmGspRequest( + [in] handle_t BindingHandle, + [in] GUID VmId, + [in, ref, string] + LPCWSTR VmName, + [in] GspRequestInfo* RequestData, + [out] GspResponseInfo* ResponseData + ); +} diff --git a/vm/devices/get/test_igvm_agent_rpc_server/src/main.rs b/vm/devices/get/test_igvm_agent_rpc_server/src/main.rs new file mode 100644 index 0000000000..510e01c719 --- /dev/null +++ b/vm/devices/get/test_igvm_agent_rpc_server/src/main.rs @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! Standalone executable that hosts the IGVM agent Windows RPC façade. + +// UNSAFETY: Windows FFI +#![cfg_attr(windows, expect(unsafe_code))] + +#[cfg(target_os = "windows")] +mod rpc; + +use cfg_if::cfg_if; +use std::process::ExitCode; + +fn main() -> ExitCode { + cfg_if! { + if #[cfg(target_os = "windows")] { + use tracing_subscriber::fmt; + use tracing_subscriber::EnvFilter; + + let filter = EnvFilter::try_from_default_env() + .unwrap_or_else(|_| EnvFilter::new("test_igvm_agent_rpc_server=info")); + + let _ = fmt() + .with_env_filter(filter) + .with_writer(std::io::stdout) + .try_init(); + + tracing::info!("launching IGVM agent RPC server binary"); + + if let Err(err) = rpc::run_server() { + tracing::error!(%err, "failed to run IGVM agent RPC server"); + return ExitCode::FAILURE; + } + + tracing::info!("IGVM agent RPC server exited successfully"); + + ExitCode::SUCCESS + } + else { + eprintln!("IGVM agent RPC server is only supported on Windows hosts."); + ExitCode::FAILURE + } + } +} diff --git a/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/handlers.rs b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/handlers.rs new file mode 100644 index 0000000000..25adbb1256 --- /dev/null +++ b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/handlers.rs @@ -0,0 +1,263 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use std::ffi::c_void; +use std::ptr; +use std::slice; +use crate::rpc::igvm_agent; +use guid::Guid; + +type HRESULT = i32; + +const S_OK: HRESULT = 0; +const E_FAIL: HRESULT = 0x8000_4005u32 as HRESULT; +const E_INVALIDARG: HRESULT = 0x8007_0057u32 as HRESULT; +const E_POINTER: HRESULT = 0x8000_4003u32 as HRESULT; +const HRESULT_INSUFFICIENT_BUFFER: HRESULT = 0x8007_007Au32 as HRESULT; + +#[unsafe(no_mangle)] +/// Allocator shim invoked by the generated MIDL stubs. +pub unsafe extern "C" fn MIDL_user_allocate(size: usize) -> *mut c_void { + use windows_sys::Win32::System::Com::CoTaskMemAlloc; + unsafe { CoTaskMemAlloc(size) } +} + +#[unsafe(no_mangle)] +/// Deallocator shim invoked by the generated MIDL stubs. +pub unsafe extern "C" fn MIDL_user_free(ptr: *mut c_void) { + use windows_sys::Win32::System::Com::CoTaskMemFree; + if !ptr.is_null() { + unsafe { + CoTaskMemFree(ptr); + } + } +} + +/// VM GSP request payload descriptor provided by the RPC caller. +#[repr(C)] +pub struct GspRequestInfo { + /// Pointer to the request payload buffer. + pub request_buffer: *const u8, + /// Size of the request payload in bytes. + pub request_size: u32, +} + +/// VM GSP response buffer descriptor owned by the RPC caller. +#[repr(C)] +pub struct GspResponseInfo { + /// Pointer to the writable response buffer. + pub response_buffer: *mut u8, + /// Capacity of the response buffer in bytes. + pub response_buffer_size: u32, + /// Actual number of bytes written to the response buffer. + pub response_size: u32, +} + +fn write_response_size(ptr: *mut u32, value: u32) -> Result<(), HRESULT> { + if ptr.is_null() { + Err(E_POINTER) + } else { + unsafe { + *ptr = value; + } + Ok(()) + } +} + +fn copy_to_buffer(buffer: &[u8], dest: *mut u8) { + if !buffer.is_empty() { + unsafe { + ptr::copy_nonoverlapping(buffer.as_ptr(), dest, buffer.len()); + } + } +} + +fn format_hresult(hr: HRESULT) -> String { + format!("{:#010x}", hr as u32) +} + +fn read_guid(ptr: *const Guid) -> Option { + if ptr.is_null() { + None + } else { + Some(unsafe { *ptr }) + } +} + +fn read_utf16(ptr: *const u16) -> Option { + const MAX_LEN: usize = 1024; + + if ptr.is_null() { + return None; + } + + unsafe { + let mut len = 0usize; + while len < MAX_LEN { + if *ptr.add(len) == 0 { + break; + } + len += 1; + } + + if len == MAX_LEN { + return None; + } + + let slice = slice::from_raw_parts(ptr, len); + String::from_utf16(slice).ok() + } +} + +/// Entry point that services `RpcIGVmAttest` requests for the test agent. +#[unsafe(export_name = "RpcIGVmAttest")] +pub extern "system" fn rpc_igvm_attest( + _binding_handle: *mut c_void, + _vm_id: *const Guid, + _request_id: *const Guid, + _vm_name: *const u16, + _agent_data_size: u32, + _agent_data: *const u8, + report_size: u32, + report: *const u8, + response_buffer_size: u32, + response_written_size: *mut u32, + response: *mut u8, +) -> HRESULT { + let vm_id_str = read_guid(_vm_id).map(|g| g.to_string()); + let request_id_str = read_guid(_request_id).map(|g| g.to_string()); + let vm_name_str = read_utf16(_vm_name); + + tracing::info!( + vm_id = vm_id_str.as_deref().unwrap_or(""), + request_id = request_id_str.as_deref().unwrap_or(""), + vm_name = vm_name_str.as_deref().unwrap_or(""), + report_size, + response_buffer_size, + "RpcIGVmAttest request received" + ); + + if let Err(err) = write_response_size(response_written_size, 0) { + tracing::error!(hresult = format_hresult(err), "failed to clear response size"); + return err; + } + + let report_slice = unsafe { + if report_size == 0 { + &[][..] + } else if report.is_null() { + tracing::error!("report pointer is null while report_size > 0"); + return E_INVALIDARG; + } else { + slice::from_raw_parts(report, report_size as usize) + } + }; + + tracing::debug!(payload_bytes = report_slice.len(), "invoking attest igvm_agent"); + + let payload = match igvm_agent::process_igvm_attest(report_slice) { + Ok(payload) => payload, + Err(err) => { + tracing::error!(?err, "igvm_agent::process_igvm_attest failed"); + return E_FAIL; + } + }; + + let payload_len = payload.len() as u32; + + if payload_len > response_buffer_size { + tracing::warn!( + required = payload_len, + available = response_buffer_size, + "response buffer too small for attest payload" + ); + let _ = write_response_size(response_written_size, payload_len); + return HRESULT_INSUFFICIENT_BUFFER; + } + + if payload_len > 0 { + if response.is_null() { + tracing::error!("response buffer pointer is null while payload_len > 0"); + return E_INVALIDARG; + } + copy_to_buffer(&payload, response); + } + + if let Err(err) = write_response_size(response_written_size, payload_len) { + tracing::error!(hresult = format_hresult(err), "failed to set response size"); + return err; + } + + tracing::info!(response_size = payload_len, "RpcIGVmAttest completed successfully"); + + S_OK +} + +/// Entry point that services `RpcVmGspRequest` calls for the test agent. +#[unsafe(export_name = "RpcVmGspRequest")] +pub extern "system" fn rpc_vm_gsp_request( + _binding_handle: *mut c_void, + _vm_id: *const Guid, + _vm_name: *const u16, + request_data: *const GspRequestInfo, + response_data: *mut GspResponseInfo, +) -> HRESULT { + if request_data.is_null() || response_data.is_null() { + tracing::error!("RpcVmGspRequest received null descriptor pointer"); + return E_INVALIDARG; + } + + let request = unsafe { &*request_data }; + let response = unsafe { &mut *response_data }; + + let vm_id_str = read_guid(_vm_id).map(|g| g.to_string()); + let vm_name_str = read_utf16(_vm_name); + + tracing::info!( + vm_id = vm_id_str.as_deref().unwrap_or(""), + vm_name = vm_name_str.as_deref().unwrap_or(""), + request_size = request.request_size, + response_capacity = response.response_buffer_size, + "RpcVmGspRequest received" + ); + + if request.request_size > 0 && request.request_buffer.is_null() { + tracing::error!("request buffer pointer is null while request_size > 0"); + return E_INVALIDARG; + } + + if response.response_buffer_size > 0 && response.response_buffer.is_null() { + tracing::error!("response buffer pointer is null while response_buffer_size > 0"); + return E_POINTER; + } + + let request_bytes = if request.request_size == 0 { + &[][..] + } else { + unsafe { slice::from_raw_parts(request.request_buffer, request.request_size as usize) } + }; + + tracing::debug!(payload_bytes = request_bytes.len(), "invoking VM GSP igvm_agent"); + + let payload = igvm_agent::process_vm_gsp_request(request_bytes); + let payload_len = payload.len() as u32; + + if payload_len > response.response_buffer_size { + tracing::warn!( + required = payload_len, + available = response.response_buffer_size, + "response buffer too small for VM GSP payload" + ); + response.response_size = payload_len; + return HRESULT_INSUFFICIENT_BUFFER; + } + + if payload_len > 0 { + copy_to_buffer(&payload, response.response_buffer); + } + + response.response_size = payload_len; + tracing::info!(response_size = payload_len, "RpcVmGspRequest completed successfully"); + + S_OK +} diff --git a/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/igvm_agent.rs b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/igvm_agent.rs new file mode 100644 index 0000000000..65d0f8b0b4 --- /dev/null +++ b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/igvm_agent.rs @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! Shared façade that exposes the test IGVM agent through a singleton instance. + +use test_igvm_agent_lib::Error; +use test_igvm_agent_lib::IgvmAgentTestSetting; +use test_igvm_agent_lib::TestIgvmAgent; +use parking_lot::Mutex; +use std::sync::OnceLock; + +/// Errors surfaced by the test IGVM agent façade. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum TestAgentFacadeError { + /// The request payload could not be processed by the agent. + InvalidRequest, + /// The underlying agent reported an unexpected failure. + AgentFailure, +} + +/// Convenience result type for façade invocations. +pub type TestAgentResult = Result; + +static TEST_AGENT: OnceLock> = OnceLock::new(); + +fn global_agent() -> &'static Mutex { + TEST_AGENT.get_or_init(|| Mutex::new(TestIgvmAgent::new())) +} + +fn guard_agent() -> parking_lot::MutexGuard<'static, TestIgvmAgent> { + global_agent() + .lock() +} + +/// Install a scripted test plan for the shared test agent instance. +pub fn install_plan(setting: &IgvmAgentTestSetting) { + let mut agent = guard_agent(); + agent.install_plan_from_setting(setting); +} + +/// Process an attestation request payload using the shared test agent. +pub fn process_igvm_attest(report: &[u8]) -> TestAgentResult> { + let mut agent = guard_agent(); + let (payload, expected_len) = agent.handle_request(report).map_err(|err| match err { + Error::InvalidIgvmAttestRequest => TestAgentFacadeError::InvalidRequest, + _ => TestAgentFacadeError::AgentFailure, + })?; + if payload.len() != expected_len as usize { + return Err(TestAgentFacadeError::InvalidRequest); + } + Ok(payload) +} + +/// Process a VM GSP request payload by echoing it back to the caller. +pub fn process_vm_gsp_request(request: &[u8]) -> Vec { + request.to_vec() +} diff --git a/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/mod.rs b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/mod.rs new file mode 100644 index 0000000000..3c279a364b --- /dev/null +++ b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/mod.rs @@ -0,0 +1,8 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +pub use server::run_server; + +mod handlers; +mod igvm_agent; +mod server; diff --git a/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs new file mode 100644 index 0000000000..9e9f0eb426 --- /dev/null +++ b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs @@ -0,0 +1,153 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use core::ffi::c_void; +use std::ffi::OsStr; +use std::os::windows::ffi::OsStrExt; +use std::ptr; +use std::sync::atomic::AtomicBool; +use std::sync::atomic::Ordering; +use windows_sys::Win32::Foundation::BOOL; +use windows_sys::Win32::Foundation::FALSE; +use windows_sys::Win32::Foundation::TRUE; +use windows_sys::Win32::System::Console::CTRL_BREAK_EVENT; +use windows_sys::Win32::System::Console::CTRL_C_EVENT; +use windows_sys::Win32::System::Console::CTRL_CLOSE_EVENT; +use windows_sys::Win32::System::Console::CTRL_SHUTDOWN_EVENT; +use windows_sys::Win32::System::Console::SetConsoleCtrlHandler; +use windows_sys::Win32::System::Rpc::RpcMgmtStopServerListening; +use windows_sys::Win32::System::Rpc::RpcServerListen; +use windows_sys::Win32::System::Rpc::RpcServerRegisterIf3; +use windows_sys::Win32::System::Rpc::RpcServerUnregisterIf; +use windows_sys::Win32::System::Rpc::RpcServerUseProtseqEpW; +use windows_sys::Win32::System::Rpc::RPC_C_LISTEN_MAX_CALLS_DEFAULT; +use windows_sys::Win32::System::Rpc::RPC_C_PROTSEQ_MAX_REQS_DEFAULT; +use windows_sys::Win32::System::Rpc::RPC_STATUS; +use windows_sys::Win32::System::Rpc::RPC_S_NOT_LISTENING; +use windows_sys::Win32::System::Rpc::RPC_S_OK; + +pub const PROTOCOL_SEQUENCE: &str = "ncalrpc"; +pub const ENDPOINT: &str = "IGVM_AGENT_RPC_SERVER"; + +pub type RpcInterfaceHandle = *mut c_void; + +unsafe extern "C" { + pub static IGVmAgentRpcApi_v1_0_s_ifspec: RpcInterfaceHandle; +} +fn to_wide(s: &str) -> Vec { + OsStr::new(s).encode_wide().chain([0]).collect() +} + +unsafe extern "system" fn rpc_bind_callback( + _context: *const c_void, + _uuid: *const c_void, +) -> RPC_STATUS { + tracing::debug!("rpc_bind_callback invoked"); + RPC_S_OK +} + +static STOP_REQUESTED: AtomicBool = AtomicBool::new(false); + +unsafe extern "system" fn console_ctrl_handler(ctrl_type: u32) -> BOOL { + match ctrl_type { + CTRL_C_EVENT | CTRL_BREAK_EVENT | CTRL_CLOSE_EVENT | CTRL_SHUTDOWN_EVENT => { + if !STOP_REQUESTED.swap(true, Ordering::SeqCst) { + tracing::info!("console control signal {ctrl_type} received; requesting shutdown"); + let status = unsafe { RpcMgmtStopServerListening(ptr::null_mut()) }; + if status != RPC_S_OK && status != RPC_S_NOT_LISTENING { + tracing::error!("RpcMgmtStopServerListening failed: {status}"); + } + } + TRUE + } + _ => 0, + } +} + +struct ConsoleHandlerGuard; + +impl ConsoleHandlerGuard { + fn register() -> Result { + unsafe { + if SetConsoleCtrlHandler(Some(console_ctrl_handler), TRUE) == 0 { + return Err("SetConsoleCtrlHandler failed".to_owned()); + } + } + tracing::info!("console control handler registered"); + + Ok(Self) + } +} + +impl Drop for ConsoleHandlerGuard { + fn drop(&mut self) { + unsafe { + SetConsoleCtrlHandler(Some(console_ctrl_handler), FALSE); + } + } +} + +fn register_protocol_and_interface() -> Result<(), String> { + unsafe { + tracing::info!(protocol = %PROTOCOL_SEQUENCE, endpoint = %ENDPOINT, "registering RPC protocol"); + let mut protocol_seq = to_wide(PROTOCOL_SEQUENCE); + let mut endpoint = to_wide(ENDPOINT); + + let status = RpcServerUseProtseqEpW( + protocol_seq.as_mut_ptr(), + RPC_C_PROTSEQ_MAX_REQS_DEFAULT, + endpoint.as_mut_ptr(), + ptr::null_mut(), + ); + if status != RPC_S_OK { + tracing::error!(status = status, "RpcServerUseProtseqEpW failed"); + return Err(format!("RpcServerUseProtseqEpW failed: {status}")); + } + tracing::info!("RPC protocol bound successfully"); + + let status = RpcServerRegisterIf3( + IGVmAgentRpcApi_v1_0_s_ifspec, + ptr::null_mut(), + ptr::null_mut(), + 0, + RPC_C_LISTEN_MAX_CALLS_DEFAULT, + 0, + Some(rpc_bind_callback), + ptr::null_mut(), + ); + if status != RPC_S_OK { + tracing::error!(status = status, "RpcServerRegisterIf3 failed"); + return Err(format!("RpcServerRegisterIf3 failed: {status}")); + } + } + + tracing::info!("RPC interface registered"); + Ok(()) +} + +fn unregister_interface() { + unsafe { + RpcServerUnregisterIf(IGVmAgentRpcApi_v1_0_s_ifspec, ptr::null_mut(), 0); + } + tracing::info!("RPC interface unregistered"); +} + +/// Run the IGVM agent RPC server until it is interrupted. +pub fn run_server() -> Result<(), String> { + tracing::info!("starting IGVM agent RPC server"); + register_protocol_and_interface()?; + let _handler = ConsoleHandlerGuard::register()?; + + let listen_status = unsafe { RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, 0) }; + + unregister_interface(); + + if listen_status != RPC_S_OK { + tracing::error!(status = listen_status, "RpcServerListen failed"); + return Err(format!("RpcServerListen failed: {listen_status}")); + } + + tracing::info!("IGVM agent RPC server stopped cleanly"); + + Ok(()) +} 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 79583e783e..16ff3e09e4 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 @@ -95,6 +95,10 @@ impl petri_artifacts_core::ResolveTestArtifact for OpenvmmKnownPathsTestArtifact tpm_guest_tests_linux_path(MachineArch::X86_64) } + _ if id == host_tools::TEST_IGVM_AGENT_RPC_SERVER_WINDOWS_X64 => { + test_igvm_agent_rpc_server_windows_path(MachineArch::X86_64) + } + _ => anyhow::bail!("no support for given artifact type"), } } @@ -277,6 +281,19 @@ fn tpm_guest_tests_linux_path(arch: MachineArch) -> anyhow::Result { ) } +/// Path to the output location of the test_igvm_agent_rpc_server executable. +fn test_igvm_agent_rpc_server_windows_path(arch: MachineArch) -> anyhow::Result { + let target = windows_msvc_target(arch); + get_path( + format!("target/{target}/debug"), + "test_igvm_agent_rpc_server.exe", + MissingCommand::Build { + package: "test_igvm_agent_rpc_server", + target: Some(target), + }, + ) +} + fn tmk_vmm_paravisor_path(arch: MachineArch) -> anyhow::Result { let target = match arch { MachineArch::X86_64 => "x86_64-unknown-linux-musl", diff --git a/vmm_tests/petri_artifacts_vmm_test/src/lib.rs b/vmm_tests/petri_artifacts_vmm_test/src/lib.rs index cbf5747f2a..33a129b257 100644 --- a/vmm_tests/petri_artifacts_vmm_test/src/lib.rs +++ b/vmm_tests/petri_artifacts_vmm_test/src/lib.rs @@ -54,6 +54,16 @@ pub mod artifacts { } } + /// Host-side tools used by the VMM tests. + pub mod host_tools { + use petri_artifacts_core::declare_artifacts; + + declare_artifacts! { + /// Windows x86_64 build of the `test_igvm_agent_rpc_server` executable. + TEST_IGVM_AGENT_RPC_SERVER_WINDOWS_X64, + } + } + /// Loadable artifacts pub mod loadable { use petri_artifacts_common::tags::IsLoadable; diff --git a/vmm_tests/vmm_tests/tests/tests/x86_64/tpm.rs b/vmm_tests/vmm_tests/tests/tests/x86_64/tpm.rs index d750ed667f..c98d9aa6f4 100644 --- a/vmm_tests/vmm_tests/tests/tests/x86_64/tpm.rs +++ b/vmm_tests/vmm_tests/tests/tests/x86_64/tpm.rs @@ -14,8 +14,11 @@ use petri::pipette::cmd; use petri_artifacts_common::tags::OsFlavor; use petri_artifacts_vmm_test::artifacts::guest_tools::TPM_GUEST_TESTS_LINUX_X64; use petri_artifacts_vmm_test::artifacts::guest_tools::TPM_GUEST_TESTS_WINDOWS_X64; +use petri_artifacts_vmm_test::artifacts::host_tools::TEST_IGVM_AGENT_RPC_SERVER_WINDOWS_X64; use pipette_client::PipetteClient; use std::path::Path; +#[cfg(windows)] +use pal; use vmm_test_macros::openvmm_test; use vmm_test_macros::openvmm_test_no_agent; #[cfg(windows)] @@ -464,19 +467,55 @@ async fn tpm_test_platform_hierarchy_disabled( /// CVM with guest tpm tests on Hyper-V. #[cfg(windows)] #[vmm_test( - hyperv_openhcl_uefi_x64[vbs](vhd(ubuntu_2504_server_x64))[TPM_GUEST_TESTS_LINUX_X64], - hyperv_openhcl_uefi_x64[vbs](vhd(windows_datacenter_core_2025_x64_prepped))[TPM_GUEST_TESTS_WINDOWS_X64], - hyperv_openhcl_uefi_x64[tdx](vhd(ubuntu_2504_server_x64))[TPM_GUEST_TESTS_LINUX_X64], - hyperv_openhcl_uefi_x64[tdx](vhd(windows_datacenter_core_2025_x64_prepped))[TPM_GUEST_TESTS_WINDOWS_X64], - hyperv_openhcl_uefi_x64[snp](vhd(ubuntu_2504_server_x64))[TPM_GUEST_TESTS_LINUX_X64], - hyperv_openhcl_uefi_x64[snp](vhd(windows_datacenter_core_2025_x64_prepped))[TPM_GUEST_TESTS_WINDOWS_X64], + hyperv_openhcl_uefi_x64[vbs](vhd(ubuntu_2504_server_x64))[TPM_GUEST_TESTS_LINUX_X64, TEST_IGVM_AGENT_RPC_SERVER_WINDOWS_X64], + hyperv_openhcl_uefi_x64[vbs](vhd(windows_datacenter_core_2025_x64_prepped))[TPM_GUEST_TESTS_WINDOWS_X64, TEST_IGVM_AGENT_RPC_SERVER_WINDOWS_X64], + hyperv_openhcl_uefi_x64[tdx](vhd(ubuntu_2504_server_x64))[TPM_GUEST_TESTS_LINUX_X64, TEST_IGVM_AGENT_RPC_SERVER_WINDOWS_X64], + hyperv_openhcl_uefi_x64[tdx](vhd(windows_datacenter_core_2025_x64_prepped))[TPM_GUEST_TESTS_WINDOWS_X64, TEST_IGVM_AGENT_RPC_SERVER_WINDOWS_X64], + hyperv_openhcl_uefi_x64[snp](vhd(ubuntu_2504_server_x64))[TPM_GUEST_TESTS_LINUX_X64, TEST_IGVM_AGENT_RPC_SERVER_WINDOWS_X64], + hyperv_openhcl_uefi_x64[snp](vhd(windows_datacenter_core_2025_x64_prepped))[TPM_GUEST_TESTS_WINDOWS_X64, TEST_IGVM_AGENT_RPC_SERVER_WINDOWS_X64], )] -async fn cvm_tpm_guest_tests( +async fn cvm_tpm_guest_tests( config: PetriVmBuilder, - extra_deps: (ResolvedArtifact,), + extra_deps: (ResolvedArtifact, ResolvedArtifact), ) -> anyhow::Result<()> { + use std::process::Stdio; + use std::io::Read; + let os_flavor = config.os_flavor(); - // TODO: Add test IGVMAgent RPC server to support the boot-time attestation. + let (tpm_guest_tests_artifact, rpc_server_artifact) = extra_deps; + + // Spawn the test IGVM agent RPC server on the host before creating the VM + tracing::info!("launching test_igvm_agent_rpc_server"); + let rpc_server_path = rpc_server_artifact.get(); + let (stderr_read, stderr_write) = pal::pipe_pair()?; + let mut rpc_server_child = std::process::Command::new(rpc_server_path) + .stdin(Stdio::null()) + .stdout(Stdio::piped()) + .stderr(stderr_write) + .spawn() + .context("failed to spawn test_igvm_agent_rpc_server")?; + + // Wait for stdout to close to ensure the server is ready + let mut stdout = rpc_server_child + .stdout + .take() + .context("failed to take stdout from RPC server")?; + let mut b = [0]; + stdout + .read(&mut b) + .context("failed to read from RPC server stdout")?; + + // Create a guard to ensure the RPC server is killed when this function exits + struct RpcServerGuard(std::process::Child); + impl Drop for RpcServerGuard { + fn drop(&mut self) { + tracing::info!("terminating test_igvm_agent_rpc_server"); + let _ = self.0.kill(); + let _ = self.0.wait(); + } + } + let _rpc_server_guard = RpcServerGuard(rpc_server_child); + let config = config .with_tpm_state_persistence(false) .with_guest_state_lifetime(PetriGuestStateLifetime::Disk); @@ -488,20 +527,21 @@ async fn cvm_tpm_guest_tests( OsFlavor::Windows => TPM_GUEST_TESTS_WINDOWS_GUEST_PATH, _ => unreachable!(), }; - let (artifact,) = extra_deps; - let host_binary_path = artifact.get(); + let host_binary_path = tpm_guest_tests_artifact.get(); let tpm_guest_tests = TpmGuestTests::send_tpm_guest_tests(&agent, host_binary_path, guest_binary_path, os_flavor) .await?; - // TODO: Add test IGVMAgent RPC server to support AK Cert - // let expected_hex = expected_ak_cert_hex(); - // let ak_cert_output = tpm_guest_tests.read_ak_cert_with_expected_hex(expected_hex.as_str()).await?; + // Verify AK cert with the test IGVM agent RPC server + let expected_hex = expected_ak_cert_hex(); + let ak_cert_output = tpm_guest_tests + .read_ak_cert_with_expected_hex(expected_hex.as_str()) + .await?; - // ensure!( - // ak_cert_output.contains("AK certificate matches expected value"), - // format!("{ak_cert_output}") - // ); + ensure!( + ak_cert_output.contains("AK certificate matches expected value"), + format!("{ak_cert_output}") + ); let report_output = tpm_guest_tests .read_report() From 7e7e0d61867f41529e817a538e00a200945b6999 Mon Sep 17 00:00:00 2001 From: Ming-Wei Shih Date: Tue, 2 Dec 2025 20:39:55 +0000 Subject: [PATCH 02/32] wip - support in-house test igvm agent Signed-off-by: Ming-Wei Shih --- Cargo.lock | 5 +- .../src/pipelines/checkin_gates.rs | 21 ++ ...sume_and_test_nextest_vmm_tests_archive.rs | 4 + .../local_build_and_run_nextest_vmm_tests.rs | 25 +++ .../src/build_test_igvm_agent_rpc_server.rs | 100 +++++++++ .../src/init_vmm_tests_env.rs | 10 + flowey/flowey_lib_hvlite/src/lib.rs | 1 + .../get/test_igvm_agent_rpc_server/Cargo.toml | 3 + .../get/test_igvm_agent_rpc_server/build.rs | 15 +- .../test_igvm_agent_rpc_server/src/main.rs | 47 ++++ .../src/rpc/handlers.rs | 201 +++++++++++------- .../src/rpc/igvm_agent.rs | 12 +- .../test_igvm_agent_rpc_server/src/rpc/mod.rs | 2 +- .../src/rpc/server.rs | 10 +- 14 files changed, 347 insertions(+), 109 deletions(-) create mode 100644 flowey/flowey_lib_hvlite/src/build_test_igvm_agent_rpc_server.rs diff --git a/Cargo.lock b/Cargo.lock index 7b30a34d5c..a2e566e0a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7227,7 +7227,7 @@ dependencies = [ "sha2", "thiserror 2.0.16", "tracing", - "zerocopy 0.8.25", + "zerocopy 0.8.27", ] [[package]] @@ -7236,12 +7236,15 @@ version = "0.0.0" dependencies = [ "cc", "cfg-if", + "clap", + "get_resources", "guid", "parking_lot", "serde_json", "test_igvm_agent_lib", "tracing", "tracing-subscriber", + "winapi", "windows-sys 0.59.0", ] diff --git a/flowey/flowey_hvlite/src/pipelines/checkin_gates.rs b/flowey/flowey_hvlite/src/pipelines/checkin_gates.rs index 7c9796c045..2b2542d85f 100644 --- a/flowey/flowey_hvlite/src/pipelines/checkin_gates.rs +++ b/flowey/flowey_hvlite/src/pipelines/checkin_gates.rs @@ -255,6 +255,9 @@ impl IntoPipeline for CheckinGatesCli { let (pub_tpm_guest_tests, use_tpm_guest_tests_windows) = pipeline.new_typed_artifact(format!("{arch_tag}-windows-tpm_guest_tests")); + let (pub_test_igvm_agent_rpc_server, use_test_igvm_agent_rpc_server) = + pipeline.new_typed_artifact(format!("{arch_tag}-windows-test_igvm_agent_rpc_server")); + // filter off interesting artifacts required by the VMM tests job match arch { CommonArch::X86_64 => { @@ -268,6 +271,8 @@ impl IntoPipeline for CheckinGatesCli { vmm_tests_artifacts_windows_x86.use_vmgstool = Some(use_vmgstool.clone()); vmm_tests_artifacts_windows_x86.use_tpm_guest_tests_windows = Some(use_tpm_guest_tests_windows.clone()); + vmm_tests_artifacts_windows_x86.use_test_igvm_agent_rpc_server = + Some(use_test_igvm_agent_rpc_server.clone()); } CommonArch::Aarch64 => { vmm_tests_artifacts_windows_aarch64.use_openvmm = Some(use_openvmm.clone()); @@ -403,6 +408,14 @@ impl IntoPipeline for CheckinGatesCli { }, profile: CommonProfile::from_release(release), tpm_guest_tests: ctx.publish_typed_artifact(pub_tpm_guest_tests), + }) + .dep_on(|ctx| flowey_lib_hvlite::build_test_igvm_agent_rpc_server::Request { + target: CommonTriple::Common { + arch, + platform: CommonPlatform::WindowsMsvc, + }, + profile: CommonProfile::from_release(release), + test_igvm_agent_rpc_server: ctx.publish_typed_artifact(pub_test_igvm_agent_rpc_server), }); // Hang building the windows VMM tests off this big windows job. @@ -1187,6 +1200,7 @@ mod vmm_tests_artifact_builders { use flowey_lib_hvlite::build_openvmm::OpenvmmOutput; use flowey_lib_hvlite::build_pipette::PipetteOutput; use flowey_lib_hvlite::build_prep_steps::PrepStepsOutput; + use flowey_lib_hvlite::build_test_igvm_agent_rpc_server::TestIgvmAgentRpcServerOutput; use flowey_lib_hvlite::build_tmk_vmm::TmkVmmOutput; use flowey_lib_hvlite::build_tmks::TmksOutput; use flowey_lib_hvlite::build_tpm_guest_tests::TpmGuestTestsOutput; @@ -1240,6 +1254,7 @@ mod vmm_tests_artifact_builders { vmgstool: None, tpm_guest_tests_windows: None, tpm_guest_tests_linux: None, + test_igvm_agent_rpc_server: None, })) } } @@ -1254,6 +1269,7 @@ mod vmm_tests_artifact_builders { pub use_vmgstool: Option>, pub use_tpm_guest_tests_windows: Option>, pub use_tpm_guest_tests_linux: Option>, + pub use_test_igvm_agent_rpc_server: Option>, // linux build machine pub use_openhcl_igvm_files: Option, pub use_pipette_linux_musl: Option>, @@ -1278,6 +1294,7 @@ mod vmm_tests_artifact_builders { use_vmgstool, use_tpm_guest_tests_windows, use_tpm_guest_tests_linux, + use_test_igvm_agent_rpc_server, } = self; let use_openvmm = use_openvmm.ok_or("openvmm")?; @@ -1294,6 +1311,8 @@ mod vmm_tests_artifact_builders { use_tpm_guest_tests_windows.ok_or("tpm_guest_tests_windows")?; let use_tpm_guest_tests_linux = use_tpm_guest_tests_linux.ok_or("tpm_guest_tests_linux")?; + let use_test_igvm_agent_rpc_server = + use_test_igvm_agent_rpc_server.ok_or("test_igvm_agent_rpc_server")?; Ok(Box::new(move |ctx| VmmTestsDepArtifacts { openvmm: Some(ctx.use_typed_artifact(&use_openvmm)), @@ -1308,6 +1327,7 @@ mod vmm_tests_artifact_builders { vmgstool: Some(ctx.use_typed_artifact(&use_vmgstool)), tpm_guest_tests_windows: Some(ctx.use_typed_artifact(&use_tpm_guest_tests_windows)), tpm_guest_tests_linux: Some(ctx.use_typed_artifact(&use_tpm_guest_tests_linux)), + test_igvm_agent_rpc_server: Some(ctx.use_typed_artifact(&use_test_igvm_agent_rpc_server)), })) } } @@ -1365,6 +1385,7 @@ mod vmm_tests_artifact_builders { vmgstool: Some(ctx.use_typed_artifact(&use_vmgstool)), tpm_guest_tests_windows: None, tpm_guest_tests_linux: None, + test_igvm_agent_rpc_server: None, })) } } diff --git a/flowey/flowey_lib_hvlite/src/_jobs/consume_and_test_nextest_vmm_tests_archive.rs b/flowey/flowey_lib_hvlite/src/_jobs/consume_and_test_nextest_vmm_tests_archive.rs index 7e805f7447..2bd4bc41a4 100644 --- a/flowey/flowey_lib_hvlite/src/_jobs/consume_and_test_nextest_vmm_tests_archive.rs +++ b/flowey/flowey_lib_hvlite/src/_jobs/consume_and_test_nextest_vmm_tests_archive.rs @@ -8,6 +8,7 @@ use crate::build_nextest_vmm_tests::NextestVmmTestsArchive; use crate::build_openvmm::OpenvmmOutput; use crate::build_pipette::PipetteOutput; use crate::build_prep_steps::PrepStepsOutput; +use crate::build_test_igvm_agent_rpc_server::TestIgvmAgentRpcServerOutput; use crate::build_tmk_vmm::TmkVmmOutput; use crate::build_tmks::TmksOutput; use crate::build_tpm_guest_tests::TpmGuestTestsOutput; @@ -32,6 +33,7 @@ pub struct VmmTestsDepArtifacts { pub vmgstool: Option>, pub tpm_guest_tests_windows: Option>, pub tpm_guest_tests_linux: Option>, + pub test_igvm_agent_rpc_server: Option>, } flowey_request! { @@ -112,6 +114,7 @@ impl SimpleFlowNode for Node { vmgstool: register_vmgstool, tpm_guest_tests_windows: register_tpm_guest_tests_windows, tpm_guest_tests_linux: register_tpm_guest_tests_linux, + test_igvm_agent_rpc_server: register_test_igvm_agent_rpc_server, } = dep_artifact_dirs; let register_openhcl_igvm_files = artifact_dir_openhcl_igvm_files.map(|artifact_dir| { @@ -176,6 +179,7 @@ impl SimpleFlowNode for Node { register_vmgstool, register_tpm_guest_tests_windows, register_tpm_guest_tests_linux, + register_test_igvm_agent_rpc_server, disk_images_dir: Some(disk_images_dir), register_openhcl_igvm_files, get_test_log_path: Some(get_test_log_path), diff --git a/flowey/flowey_lib_hvlite/src/_jobs/local_build_and_run_nextest_vmm_tests.rs b/flowey/flowey_lib_hvlite/src/_jobs/local_build_and_run_nextest_vmm_tests.rs index a31d85c8d8..e50ef76f8d 100644 --- a/flowey/flowey_lib_hvlite/src/_jobs/local_build_and_run_nextest_vmm_tests.rs +++ b/flowey/flowey_lib_hvlite/src/_jobs/local_build_and_run_nextest_vmm_tests.rs @@ -118,6 +118,7 @@ pub struct BuildSelections { pub vmgstool: bool, pub tpm_guest_tests_windows: bool, pub tpm_guest_tests_linux: bool, + pub test_igvm_agent_rpc_server: bool, } // Build everything we can by default @@ -136,6 +137,7 @@ impl Default for BuildSelections { vmgstool: true, tpm_guest_tests_windows: true, tpm_guest_tests_linux: true, + test_igvm_agent_rpc_server: true, } } } @@ -182,6 +184,7 @@ impl SimpleFlowNode for Node { ctx.import::(); ctx.import::(); ctx.import::(); + ctx.import::(); ctx.import::(); ctx.import::(); ctx.import::(); @@ -290,6 +293,7 @@ impl SimpleFlowNode for Node { filter.push_str(" & !test(windows)"); build.pipette_windows = false; build.tpm_guest_tests_windows = false; + build.test_igvm_agent_rpc_server = false; } if !freebsd { filter.push_str(" & !test(freebsd)"); @@ -402,6 +406,7 @@ impl SimpleFlowNode for Node { build.pipette_linux = false; build.tmk_vmm_linux = false; build.tpm_guest_tests_linux = false; + build.test_igvm_agent_rpc_server = false; } let register_openhcl_igvm_files = build.openhcl.then(|| { @@ -664,6 +669,25 @@ impl SimpleFlowNode for Node { output }); + let register_test_igvm_agent_rpc_server = build.test_igvm_agent_rpc_server.then(|| { + let output = ctx.reqv(|v| crate::build_test_igvm_agent_rpc_server::Request { + target: CommonTriple::Common { + arch, + platform: CommonPlatform::WindowsMsvc, + }, + profile: CommonProfile::from_release(release), + test_igvm_agent_rpc_server: v, + }); + + if copy_extras { + copy_to_dir.push(( + extras_dir.to_owned(), + output.map(ctx, |x| Some(x.pdb.clone())), + )); + } + output + }); + let register_tmk_vmm = build.tmk_vmm_windows.then(|| { let output = ctx.reqv(|v| crate::build_tmk_vmm::Request { target: CommonTriple::Common { @@ -882,6 +906,7 @@ impl SimpleFlowNode for Node { register_vmgstool, register_tpm_guest_tests_windows, register_tpm_guest_tests_linux, + register_test_igvm_agent_rpc_server, disk_images_dir: Some(test_artifacts_dir), register_openhcl_igvm_files, get_test_log_path: None, diff --git a/flowey/flowey_lib_hvlite/src/build_test_igvm_agent_rpc_server.rs b/flowey/flowey_lib_hvlite/src/build_test_igvm_agent_rpc_server.rs new file mode 100644 index 0000000000..27b0412cee --- /dev/null +++ b/flowey/flowey_lib_hvlite/src/build_test_igvm_agent_rpc_server.rs @@ -0,0 +1,100 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! Build `test_igvm_agent_rpc_server` binaries + +use crate::run_cargo_build::common::CommonProfile; +use crate::run_cargo_build::common::CommonTriple; +use flowey::node::prelude::*; +use flowey_lib_common::run_cargo_build::CargoCrateType; +use std::collections::BTreeMap; + +#[derive(Serialize, Deserialize)] +pub struct TestIgvmAgentRpcServerOutput { + #[serde(rename = "test_igvm_agent_rpc_server.exe")] + pub exe: PathBuf, + #[serde(rename = "test_igvm_agent_rpc_server.pdb")] + pub pdb: PathBuf, +} + +impl Artifact for TestIgvmAgentRpcServerOutput {} + +flowey_request! { + pub struct Request { + pub target: CommonTriple, + pub profile: CommonProfile, + pub test_igvm_agent_rpc_server: WriteVar, + } +} + +new_simple_flow_node!(struct Node); + +impl SimpleFlowNode for Node { + type Request = Request; + + fn imports(ctx: &mut ImportCtx<'_>) { + ctx.import::(); + } + + fn process_request(request: Self::Request, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> { + let Request { + target, + profile, + test_igvm_agent_rpc_server, + } = request; + + let target_triple = target.as_triple(); + + // Only Windows MSVC is supported for test_igvm_agent_rpc_server + if target_triple.operating_system != target_lexicon::OperatingSystem::Windows + || target_triple.environment != target_lexicon::Environment::Msvc + { + anyhow::bail!( + "test_igvm_agent_rpc_server only supports Windows MSVC targets, got: {}", + target_triple + ); + } + + let env_key = format!( + "CARGO_TARGET_{}_RUSTFLAGS", + target_triple.to_string().replace('-', "_").to_uppercase() + ); + + let mut env: BTreeMap = BTreeMap::new(); + + // Enable CRT static linking + env.insert(env_key, "-Ctarget-feature=+crt-static".to_string()); + + let extra_env = Some(ReadVar::from_static(env)); + + let output = ctx.reqv(|v| crate::run_cargo_build::Request { + crate_name: "test_igvm_agent_rpc_server".into(), + out_name: "test_igvm_agent_rpc_server".into(), + crate_type: CargoCrateType::Bin, + profile: profile.into(), + features: Default::default(), + target: target_triple, + no_split_dbg_info: false, + extra_env, + pre_build_deps: Vec::new(), + output: v, + }); + + ctx.emit_minor_rust_step("report built test_igvm_agent_rpc_server", |ctx| { + let test_igvm_agent_rpc_server = test_igvm_agent_rpc_server.claim(ctx); + let output = output.claim(ctx); + move |rt| { + let output = match rt.read(output) { + crate::run_cargo_build::CargoBuildOutput::WindowsBin { exe, pdb } => { + TestIgvmAgentRpcServerOutput { exe, pdb } + } + _ => unreachable!("unsupported build output variant for test_igvm_agent_rpc_server"), + }; + + rt.write(test_igvm_agent_rpc_server, &output); + } + }); + + Ok(()) + } +} 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 97d0cee558..9827e0d2df 100644 --- a/flowey/flowey_lib_hvlite/src/init_vmm_tests_env.rs +++ b/flowey/flowey_lib_hvlite/src/init_vmm_tests_env.rs @@ -6,6 +6,7 @@ use crate::build_openhcl_igvm_from_recipe::OpenhclIgvmRecipe; use crate::build_tpm_guest_tests::TpmGuestTestsOutput; +use crate::build_test_igvm_agent_rpc_server::TestIgvmAgentRpcServerOutput; use crate::download_openvmm_deps::OpenvmmDepsArch; use crate::download_release_igvm_files_from_gh::OpenhclReleaseVersion; use crate::download_uefi_mu_msvm::MuMsvmArch; @@ -55,6 +56,8 @@ flowey_request! { pub register_tpm_guest_tests_windows: Option>, /// Register a Linux tpm_guest_tests binary pub register_tpm_guest_tests_linux: Option>, + /// Register a Windows test_igvm_agent_rpc_server binary + pub register_test_igvm_agent_rpc_server: Option>, /// Get the path to the folder containing various logs emitted VMM tests. pub get_test_log_path: Option>, @@ -91,6 +94,7 @@ impl SimpleFlowNode for Node { register_vmgstool, register_tpm_guest_tests_windows, register_tpm_guest_tests_linux, + register_test_igvm_agent_rpc_server, disk_images_dir, register_openhcl_igvm_files, get_test_log_path, @@ -134,6 +138,7 @@ impl SimpleFlowNode for Node { let tmk_vmm = register_tmk_vmm.claim(ctx); let tmk_vmm_linux_musl = register_tmk_vmm_linux_musl.claim(ctx); let vmgstool = register_vmgstool.claim(ctx); + let test_igvm_agent_rpc_server = register_test_igvm_agent_rpc_server.claim(ctx); let tpm_guest_tests_windows = register_tpm_guest_tests_windows.claim(ctx); let tpm_guest_tests_linux = register_tpm_guest_tests_linux.claim(ctx); let disk_image_dir = disk_images_dir.claim(ctx); @@ -329,6 +334,11 @@ impl SimpleFlowNode for Node { dst.make_executable()?; } + if let Some(test_igvm_agent_rpc_server) = test_igvm_agent_rpc_server { + let TestIgvmAgentRpcServerOutput { exe, .. } = rt.read(test_igvm_agent_rpc_server); + fs_err::copy(exe, test_content_dir.join("test_igvm_agent_rpc_server.exe"))?; + } + if let Some(openhcl_igvm_files) = openhcl_igvm_files { for (recipe, openhcl_igvm) in rt.read(openhcl_igvm_files) { let crate::run_igvmfilegen::IgvmOutput { igvm_bin, .. } = openhcl_igvm; diff --git a/flowey/flowey_lib_hvlite/src/lib.rs b/flowey/flowey_lib_hvlite/src/lib.rs index b1ddecbe91..6e1b51ea17 100644 --- a/flowey/flowey_lib_hvlite/src/lib.rs +++ b/flowey/flowey_lib_hvlite/src/lib.rs @@ -29,6 +29,7 @@ pub mod build_rustdoc; pub mod build_sidecar; pub mod build_tmk_vmm; pub mod build_tmks; +pub mod build_test_igvm_agent_rpc_server; pub mod build_tpm_guest_tests; pub mod build_vmfirmwareigvm_dll; pub mod build_vmgstool; diff --git a/vm/devices/get/test_igvm_agent_rpc_server/Cargo.toml b/vm/devices/get/test_igvm_agent_rpc_server/Cargo.toml index 9cacb89d69..877fc2794b 100644 --- a/vm/devices/get/test_igvm_agent_rpc_server/Cargo.toml +++ b/vm/devices/get/test_igvm_agent_rpc_server/Cargo.toml @@ -5,15 +5,18 @@ rust-version.workspace = true [dependencies] test_igvm_agent_lib.workspace = true +get_resources.workspace = true guid.workspace = true cfg-if.workspace = true +clap = { workspace = true, features = ["derive"] } parking_lot.workspace = true tracing.workspace = true tracing-subscriber = { workspace = true, features = ["env-filter"] } [target.'cfg(windows)'.dependencies] +winapi.workspace = true windows-sys = { version = "0.59", features = [ "Win32_Foundation", "Win32_System_Memory", diff --git a/vm/devices/get/test_igvm_agent_rpc_server/build.rs b/vm/devices/get/test_igvm_agent_rpc_server/build.rs index 846f913066..e51906197d 100644 --- a/vm/devices/get/test_igvm_agent_rpc_server/build.rs +++ b/vm/devices/get/test_igvm_agent_rpc_server/build.rs @@ -58,7 +58,7 @@ fn main() { (true, Some(cfg)) => { if let Some(cl_path) = locate_cl_exe(cfg) { if let Some(bin_dir) = cl_path.parent() { - let mut path_value = env::var_os("PATH").unwrap_or_else(OsString::new); + let mut path_value = env::var_os("PATH").unwrap_or_default(); if !path_value.is_empty() { path_value.push(":"); } @@ -79,9 +79,7 @@ fn main() { ); } } else { - panic!( - "Unable to locate cl.exe in cross-compilation configuration" - ); + panic!("Unable to locate cl.exe in cross-compilation configuration"); } } (false, Some(cfg)) => { @@ -104,16 +102,11 @@ fn main() { cmd.arg("/out"); cmd.arg(&out_dir_arg); - let idl_canon = idl_path - .canonicalize() - .expect("failed to canonicalize IDL path"); - let idl_arg = path_for_midl(&idl_canon, host_is_windows); + let idl_arg = path_for_midl(idl_path, host_is_windows); cmd.arg(&idl_arg); let status = cmd.status().unwrap_or_else(|err| { - panic!( - "Failed to execute MIDL `{midl}`: {err}. Install the Windows MIDL compiler." - ) + panic!("Failed to execute MIDL `{midl}`: {err}. Install the Windows MIDL compiler.") }); if !status.success() { panic!("midl failed: status {status}"); diff --git a/vm/devices/get/test_igvm_agent_rpc_server/src/main.rs b/vm/devices/get/test_igvm_agent_rpc_server/src/main.rs index 510e01c719..e3eeeca8da 100644 --- a/vm/devices/get/test_igvm_agent_rpc_server/src/main.rs +++ b/vm/devices/get/test_igvm_agent_rpc_server/src/main.rs @@ -10,13 +10,50 @@ mod rpc; use cfg_if::cfg_if; +use clap::Parser; +use get_resources::ged::IgvmAttestTestConfig; use std::process::ExitCode; +/// IGVM Agent RPC Server +#[derive(Parser, Debug)] +#[clap(name = "test_igvm_agent_rpc_server")] +#[clap(about = "Test IGVM Agent RPC Server for attestation testing")] +struct Args { + /// Test configuration to use for the IGVM agent + #[clap(long, value_enum)] + test_config: Option, +} + +/// Test configuration options +#[derive(Debug, Clone, Copy, clap::ValueEnum)] +enum TestConfig { + /// Test AK cert retry after failure + AkCertRequestFailureAndRetry, + /// Test AK cert persistency across boots + AkCertPersistentAcrossBoot, +} + +impl From for IgvmAttestTestConfig { + fn from(config: TestConfig) -> Self { + match config { + TestConfig::AkCertRequestFailureAndRetry => { + IgvmAttestTestConfig::AkCertRequestFailureAndRetry + } + TestConfig::AkCertPersistentAcrossBoot => { + IgvmAttestTestConfig::AkCertPersistentAcrossBoot + } + } + } +} + fn main() -> ExitCode { cfg_if! { if #[cfg(target_os = "windows")] { use tracing_subscriber::fmt; use tracing_subscriber::EnvFilter; + use test_igvm_agent_lib::IgvmAgentTestSetting; + + let args = Args::parse(); let filter = EnvFilter::try_from_default_env() .unwrap_or_else(|_| EnvFilter::new("test_igvm_agent_rpc_server=info")); @@ -28,6 +65,16 @@ fn main() -> ExitCode { tracing::info!("launching IGVM agent RPC server binary"); + // Install test plan if a configuration was provided + if let Some(test_config) = args.test_config { + let igvm_config: IgvmAttestTestConfig = test_config.into(); + let setting = IgvmAgentTestSetting::TestConfig(igvm_config); + rpc::igvm_agent::install_plan(&setting); + tracing::info!(?test_config, "installed test configuration"); + } else { + tracing::info!("no test configuration provided, using default behavior"); + } + if let Err(err) = rpc::run_server() { tracing::error!(%err, "failed to run IGVM agent RPC server"); return ExitCode::FAILURE; diff --git a/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/handlers.rs b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/handlers.rs index 25adbb1256..c3aef00b03 100644 --- a/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/handlers.rs +++ b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/handlers.rs @@ -1,19 +1,20 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +use crate::rpc::igvm_agent; +use guid::Guid; use std::ffi::c_void; +use std::mem::size_of; use std::ptr; use std::slice; -use crate::rpc::igvm_agent; -use guid::Guid; - -type HRESULT = i32; - -const S_OK: HRESULT = 0; -const E_FAIL: HRESULT = 0x8000_4005u32 as HRESULT; -const E_INVALIDARG: HRESULT = 0x8007_0057u32 as HRESULT; -const E_POINTER: HRESULT = 0x8000_4003u32 as HRESULT; -const HRESULT_INSUFFICIENT_BUFFER: HRESULT = 0x8007_007Au32 as HRESULT; +use winapi::shared::winerror::E_FAIL; +use winapi::shared::winerror::E_INVALIDARG; +use winapi::shared::winerror::E_OUTOFMEMORY; +use winapi::shared::winerror::E_POINTER; +use winapi::shared::winerror::HRESULT; +use winapi::shared::winerror::S_OK; +use windows_sys::Win32::System::Rpc::RPC_S_SERVER_UNAVAILABLE; +use windows_sys::Win32::System::Rpc::RpcRaiseException; #[unsafe(no_mangle)] /// Allocator shim invoked by the generated MIDL stubs. @@ -33,26 +34,51 @@ pub unsafe extern "C" fn MIDL_user_free(ptr: *mut c_void) { } } -/// VM GSP request payload descriptor provided by the RPC caller. +// Constants from IDL +const GSP_MAX_CLEAR_SIZE: usize = 64; +const GSP_MAX_CIPHER_SIZE: usize = 512; +const GSP_MAX_COUNT: usize = 2; + +/// GSP clear text buffer (matches IDL GspClear) +#[repr(C)] +pub struct GspClear { + pub length: u32, + pub buffer: [u8; GSP_MAX_CLEAR_SIZE], +} + +/// GSP encrypted buffer (matches IDL GspCipher) +#[repr(C)] +pub struct GspCipher { + pub length: u32, + pub buffer: [u8; GSP_MAX_CIPHER_SIZE], +} + +/// VM GSP request payload descriptor provided by the RPC caller (matches IDL GspRequestInfo). #[repr(C)] pub struct GspRequestInfo { - /// Pointer to the request payload buffer. - pub request_buffer: *const u8, - /// Size of the request payload in bytes. - pub request_size: u32, + pub new_gsp: GspClear, + pub encrypted_gsp: [GspCipher; GSP_MAX_COUNT], + pub supported_status_flags: u32, } -/// VM GSP response buffer descriptor owned by the RPC caller. +/// VM GSP response buffer descriptor owned by the RPC caller (matches IDL GspResponseInfo). #[repr(C)] pub struct GspResponseInfo { - /// Pointer to the writable response buffer. - pub response_buffer: *mut u8, - /// Capacity of the response buffer in bytes. - pub response_buffer_size: u32, - /// Actual number of bytes written to the response buffer. - pub response_size: u32, + pub encrypted_gsp: GspCipher, + pub decrypted_gsp: [GspClear; GSP_MAX_COUNT], + pub response_status_flags: u32, } +// Compile-time size checks to ensure structures match IDL definitions +// GspClear: 4 (length) + 64 (buffer) = 68 bytes +const _: () = assert!(size_of::() == 68); +// GspCipher: 4 (length) + 512 (buffer) = 516 bytes +const _: () = assert!(size_of::() == 516); +// GspRequestInfo: 68 (GspClear) + 1032 (2 x GspCipher) + 4 (flags) = 1104 bytes +const _: () = assert!(size_of::() == 1104); +// GspResponseInfo: 516 (GspCipher) + 136 (2 x GspClear) + 4 (flags) = 656 bytes +const _: () = assert!(size_of::() == 656); + fn write_response_size(ptr: *mut u32, value: u32) -> Result<(), HRESULT> { if ptr.is_null() { Err(E_POINTER) @@ -113,10 +139,10 @@ fn read_utf16(ptr: *const u16) -> Option { #[unsafe(export_name = "RpcIGVmAttest")] pub extern "system" fn rpc_igvm_attest( _binding_handle: *mut c_void, - _vm_id: *const Guid, - _request_id: *const Guid, - _vm_name: *const u16, - _agent_data_size: u32, + vm_id: *const Guid, + request_id: *const Guid, + vm_name: *const u16, + agent_data_size: u32, _agent_data: *const u8, report_size: u32, report: *const u8, @@ -124,21 +150,25 @@ pub extern "system" fn rpc_igvm_attest( response_written_size: *mut u32, response: *mut u8, ) -> HRESULT { - let vm_id_str = read_guid(_vm_id).map(|g| g.to_string()); - let request_id_str = read_guid(_request_id).map(|g| g.to_string()); - let vm_name_str = read_utf16(_vm_name); + let vm_id_str = read_guid(vm_id).map(|g| g.to_string()); + let request_id_str = read_guid(request_id).map(|g| g.to_string()); + let vm_name_str = read_utf16(vm_name); tracing::info!( vm_id = vm_id_str.as_deref().unwrap_or(""), request_id = request_id_str.as_deref().unwrap_or(""), vm_name = vm_name_str.as_deref().unwrap_or(""), + agent_data_size, report_size, response_buffer_size, "RpcIGVmAttest request received" ); if let Err(err) = write_response_size(response_written_size, 0) { - tracing::error!(hresult = format_hresult(err), "failed to clear response size"); + tracing::error!( + hresult = format_hresult(err), + "failed to clear response size" + ); return err; } @@ -153,7 +183,10 @@ pub extern "system" fn rpc_igvm_attest( } }; - tracing::debug!(payload_bytes = report_slice.len(), "invoking attest igvm_agent"); + tracing::debug!( + payload_bytes = report_slice.len(), + "invoking attest igvm_agent" + ); let payload = match igvm_agent::process_igvm_attest(report_slice) { Ok(payload) => payload, @@ -171,8 +204,7 @@ pub extern "system" fn rpc_igvm_attest( available = response_buffer_size, "response buffer too small for attest payload" ); - let _ = write_response_size(response_written_size, payload_len); - return HRESULT_INSUFFICIENT_BUFFER; + return E_OUTOFMEMORY; } if payload_len > 0 { @@ -188,12 +220,17 @@ pub extern "system" fn rpc_igvm_attest( return err; } - tracing::info!(response_size = payload_len, "RpcIGVmAttest completed successfully"); + tracing::info!( + response_size = payload_len, + "RpcIGVmAttest completed successfully" + ); S_OK } /// Entry point that services `RpcVmGspRequest` calls for the test agent. +/// +/// This function is currently disabled and will raise RPC_S_SERVER_UNAVAILABLE. #[unsafe(export_name = "RpcVmGspRequest")] pub extern "system" fn rpc_vm_gsp_request( _binding_handle: *mut c_void, @@ -202,62 +239,62 @@ pub extern "system" fn rpc_vm_gsp_request( request_data: *const GspRequestInfo, response_data: *mut GspResponseInfo, ) -> HRESULT { - if request_data.is_null() || response_data.is_null() { - tracing::error!("RpcVmGspRequest received null descriptor pointer"); - return E_INVALIDARG; - } - - let request = unsafe { &*request_data }; - let response = unsafe { &mut *response_data }; - + // Log the request parameters before raising the exception let vm_id_str = read_guid(_vm_id).map(|g| g.to_string()); let vm_name_str = read_utf16(_vm_name); - tracing::info!( - vm_id = vm_id_str.as_deref().unwrap_or(""), - vm_name = vm_name_str.as_deref().unwrap_or(""), - request_size = request.request_size, - response_capacity = response.response_buffer_size, - "RpcVmGspRequest received" - ); - - if request.request_size > 0 && request.request_buffer.is_null() { - tracing::error!("request buffer pointer is null while request_size > 0"); - return E_INVALIDARG; - } - - if response.response_buffer_size > 0 && response.response_buffer.is_null() { - tracing::error!("response buffer pointer is null while response_buffer_size > 0"); - return E_POINTER; - } - - let request_bytes = if request.request_size == 0 { - &[][..] + // Now we can safely dereference the structures since they match the IDL definitions + let (new_gsp_len, encrypted_gsp_count, supported_flags) = if !request_data.is_null() { + let request = unsafe { &*request_data }; + let encrypted_count = request + .encrypted_gsp + .iter() + .filter(|gsp| gsp.length > 0) + .count(); + ( + request.new_gsp.length, + encrypted_count, + request.supported_status_flags, + ) } else { - unsafe { slice::from_raw_parts(request.request_buffer, request.request_size as usize) } + (0, 0, 0) }; - tracing::debug!(payload_bytes = request_bytes.len(), "invoking VM GSP igvm_agent"); - - let payload = igvm_agent::process_vm_gsp_request(request_bytes); - let payload_len = payload.len() as u32; + let (response_encrypted_len, response_decrypted_count, response_flags) = + if !response_data.is_null() { + let response = unsafe { &*response_data }; + let decrypted_count = response + .decrypted_gsp + .iter() + .filter(|gsp| gsp.length > 0) + .count(); + ( + response.encrypted_gsp.length, + decrypted_count, + response.response_status_flags, + ) + } else { + (0, 0, 0) + }; - if payload_len > response.response_buffer_size { - tracing::warn!( - required = payload_len, - available = response.response_buffer_size, - "response buffer too small for VM GSP payload" - ); - response.response_size = payload_len; - return HRESULT_INSUFFICIENT_BUFFER; - } + tracing::warn!( + vm_id = vm_id_str.as_deref().unwrap_or(""), + vm_name = vm_name_str.as_deref().unwrap_or(""), + new_gsp_length = new_gsp_len, + encrypted_gsp_count = encrypted_gsp_count, + supported_status_flags = supported_flags, + response_encrypted_length = response_encrypted_len, + response_decrypted_count = response_decrypted_count, + response_status_flags = response_flags, + "RpcVmGspRequest called but support is disabled - raising RPC_S_SERVER_UNAVAILABLE" + ); - if payload_len > 0 { - copy_to_buffer(&payload, response.response_buffer); + // Raise RPC_S_SERVER_UNAVAILABLE exception + unsafe { + RpcRaiseException(RPC_S_SERVER_UNAVAILABLE); } - response.response_size = payload_len; - tracing::info!(response_size = payload_len, "RpcVmGspRequest completed successfully"); - - S_OK + // This line is never reached due to RpcRaiseException, but kept for clarity + #[allow(unreachable_code)] + E_FAIL } diff --git a/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/igvm_agent.rs b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/igvm_agent.rs index 65d0f8b0b4..0c7ad429c5 100644 --- a/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/igvm_agent.rs +++ b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/igvm_agent.rs @@ -3,11 +3,11 @@ //! Shared façade that exposes the test IGVM agent through a singleton instance. +use parking_lot::Mutex; +use std::sync::OnceLock; use test_igvm_agent_lib::Error; use test_igvm_agent_lib::IgvmAgentTestSetting; use test_igvm_agent_lib::TestIgvmAgent; -use parking_lot::Mutex; -use std::sync::OnceLock; /// Errors surfaced by the test IGVM agent façade. #[derive(Debug, Clone, PartialEq, Eq)] @@ -28,8 +28,7 @@ fn global_agent() -> &'static Mutex { } fn guard_agent() -> parking_lot::MutexGuard<'static, TestIgvmAgent> { - global_agent() - .lock() + global_agent().lock() } /// Install a scripted test plan for the shared test agent instance. @@ -50,8 +49,3 @@ pub fn process_igvm_attest(report: &[u8]) -> TestAgentResult> { } Ok(payload) } - -/// Process a VM GSP request payload by echoing it back to the caller. -pub fn process_vm_gsp_request(request: &[u8]) -> Vec { - request.to_vec() -} diff --git a/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/mod.rs b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/mod.rs index 3c279a364b..661b8a00ea 100644 --- a/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/mod.rs +++ b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/mod.rs @@ -4,5 +4,5 @@ pub use server::run_server; mod handlers; -mod igvm_agent; +pub mod igvm_agent; mod server; diff --git a/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs index 9e9f0eb426..531edf7dd6 100644 --- a/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs +++ b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs @@ -15,16 +15,16 @@ use windows_sys::Win32::System::Console::CTRL_C_EVENT; use windows_sys::Win32::System::Console::CTRL_CLOSE_EVENT; use windows_sys::Win32::System::Console::CTRL_SHUTDOWN_EVENT; use windows_sys::Win32::System::Console::SetConsoleCtrlHandler; +use windows_sys::Win32::System::Rpc::RPC_C_LISTEN_MAX_CALLS_DEFAULT; +use windows_sys::Win32::System::Rpc::RPC_C_PROTSEQ_MAX_REQS_DEFAULT; +use windows_sys::Win32::System::Rpc::RPC_S_NOT_LISTENING; +use windows_sys::Win32::System::Rpc::RPC_S_OK; +use windows_sys::Win32::System::Rpc::RPC_STATUS; use windows_sys::Win32::System::Rpc::RpcMgmtStopServerListening; use windows_sys::Win32::System::Rpc::RpcServerListen; use windows_sys::Win32::System::Rpc::RpcServerRegisterIf3; use windows_sys::Win32::System::Rpc::RpcServerUnregisterIf; use windows_sys::Win32::System::Rpc::RpcServerUseProtseqEpW; -use windows_sys::Win32::System::Rpc::RPC_C_LISTEN_MAX_CALLS_DEFAULT; -use windows_sys::Win32::System::Rpc::RPC_C_PROTSEQ_MAX_REQS_DEFAULT; -use windows_sys::Win32::System::Rpc::RPC_STATUS; -use windows_sys::Win32::System::Rpc::RPC_S_NOT_LISTENING; -use windows_sys::Win32::System::Rpc::RPC_S_OK; pub const PROTOCOL_SEQUENCE: &str = "ncalrpc"; pub const ENDPOINT: &str = "IGVM_AGENT_RPC_SERVER"; From 24b2d9baf0d409fe2cf844c0d1425c5b6aada774 Mon Sep 17 00:00:00 2001 From: Ming-Wei Shih Date: Tue, 2 Dec 2025 23:39:18 +0000 Subject: [PATCH 03/32] update Signed-off-by: Ming-Wei Shih --- Cargo.toml | 1 - .../src/pipelines/checkin_gates.rs | 25 +++++++----- .../src/build_test_igvm_agent_rpc_server.rs | 6 ++- .../src/init_vmm_tests_env.rs | 5 ++- flowey/flowey_lib_hvlite/src/lib.rs | 2 +- .../get/guest_emulation_device/src/lib.rs | 8 ++-- vm/devices/get/test_igvm_agent_lib/src/lib.rs | 4 +- .../src/rpc/handlers.rs | 32 ++++++++++++--- .../src/rpc/server.rs | 15 +++++-- .../vmm_tests/tests/tests/multiarch/tpm.rs | 40 +++++++++++++++---- 10 files changed, 98 insertions(+), 40 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 72b0aa1f38..f1a55c2261 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -226,7 +226,6 @@ get_resources = { path = "vm/devices/get/get_resources" } guest_crash_device = { path = "vm/devices/get/guest_crash_device" } guest_emulation_device = { path = "vm/devices/get/guest_emulation_device" } test_igvm_agent_lib = { path = "vm/devices/get/test_igvm_agent_lib" } -test_igvm_agent_rpc_server = { path = "vm/devices/get/test_igvm_agent_rpc_server" } guest_emulation_log = { path = "vm/devices/get/guest_emulation_log" } guest_emulation_transport = { path = "vm/devices/get/guest_emulation_transport" } vtl2_settings_proto = { path = "vm/devices/get/vtl2_settings_proto" } diff --git a/flowey/flowey_hvlite/src/pipelines/checkin_gates.rs b/flowey/flowey_hvlite/src/pipelines/checkin_gates.rs index 2b2542d85f..5a1f59ec40 100644 --- a/flowey/flowey_hvlite/src/pipelines/checkin_gates.rs +++ b/flowey/flowey_hvlite/src/pipelines/checkin_gates.rs @@ -255,8 +255,8 @@ impl IntoPipeline for CheckinGatesCli { let (pub_tpm_guest_tests, use_tpm_guest_tests_windows) = pipeline.new_typed_artifact(format!("{arch_tag}-windows-tpm_guest_tests")); - let (pub_test_igvm_agent_rpc_server, use_test_igvm_agent_rpc_server) = - pipeline.new_typed_artifact(format!("{arch_tag}-windows-test_igvm_agent_rpc_server")); + let (pub_test_igvm_agent_rpc_server, use_test_igvm_agent_rpc_server) = pipeline + .new_typed_artifact(format!("{arch_tag}-windows-test_igvm_agent_rpc_server")); // filter off interesting artifacts required by the VMM tests job match arch { @@ -409,14 +409,17 @@ impl IntoPipeline for CheckinGatesCli { profile: CommonProfile::from_release(release), tpm_guest_tests: ctx.publish_typed_artifact(pub_tpm_guest_tests), }) - .dep_on(|ctx| flowey_lib_hvlite::build_test_igvm_agent_rpc_server::Request { - target: CommonTriple::Common { - arch, - platform: CommonPlatform::WindowsMsvc, + .dep_on( + |ctx| flowey_lib_hvlite::build_test_igvm_agent_rpc_server::Request { + target: CommonTriple::Common { + arch, + platform: CommonPlatform::WindowsMsvc, + }, + profile: CommonProfile::from_release(release), + test_igvm_agent_rpc_server: ctx + .publish_typed_artifact(pub_test_igvm_agent_rpc_server), }, - profile: CommonProfile::from_release(release), - test_igvm_agent_rpc_server: ctx.publish_typed_artifact(pub_test_igvm_agent_rpc_server), - }); + ); // Hang building the windows VMM tests off this big windows job. match arch { @@ -1327,7 +1330,9 @@ mod vmm_tests_artifact_builders { vmgstool: Some(ctx.use_typed_artifact(&use_vmgstool)), tpm_guest_tests_windows: Some(ctx.use_typed_artifact(&use_tpm_guest_tests_windows)), tpm_guest_tests_linux: Some(ctx.use_typed_artifact(&use_tpm_guest_tests_linux)), - test_igvm_agent_rpc_server: Some(ctx.use_typed_artifact(&use_test_igvm_agent_rpc_server)), + test_igvm_agent_rpc_server: Some( + ctx.use_typed_artifact(&use_test_igvm_agent_rpc_server), + ), })) } } diff --git a/flowey/flowey_lib_hvlite/src/build_test_igvm_agent_rpc_server.rs b/flowey/flowey_lib_hvlite/src/build_test_igvm_agent_rpc_server.rs index 27b0412cee..f414470ee4 100644 --- a/flowey/flowey_lib_hvlite/src/build_test_igvm_agent_rpc_server.rs +++ b/flowey/flowey_lib_hvlite/src/build_test_igvm_agent_rpc_server.rs @@ -44,7 +44,7 @@ impl SimpleFlowNode for Node { } = request; let target_triple = target.as_triple(); - + // Only Windows MSVC is supported for test_igvm_agent_rpc_server if target_triple.operating_system != target_lexicon::OperatingSystem::Windows || target_triple.environment != target_lexicon::Environment::Msvc @@ -88,7 +88,9 @@ impl SimpleFlowNode for Node { crate::run_cargo_build::CargoBuildOutput::WindowsBin { exe, pdb } => { TestIgvmAgentRpcServerOutput { exe, pdb } } - _ => unreachable!("unsupported build output variant for test_igvm_agent_rpc_server"), + _ => unreachable!( + "unsupported build output variant for test_igvm_agent_rpc_server" + ), }; rt.write(test_igvm_agent_rpc_server, &output); 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 9827e0d2df..6c3da29b33 100644 --- a/flowey/flowey_lib_hvlite/src/init_vmm_tests_env.rs +++ b/flowey/flowey_lib_hvlite/src/init_vmm_tests_env.rs @@ -5,8 +5,8 @@ //! require to run. use crate::build_openhcl_igvm_from_recipe::OpenhclIgvmRecipe; -use crate::build_tpm_guest_tests::TpmGuestTestsOutput; use crate::build_test_igvm_agent_rpc_server::TestIgvmAgentRpcServerOutput; +use crate::build_tpm_guest_tests::TpmGuestTestsOutput; use crate::download_openvmm_deps::OpenvmmDepsArch; use crate::download_release_igvm_files_from_gh::OpenhclReleaseVersion; use crate::download_uefi_mu_msvm::MuMsvmArch; @@ -335,7 +335,8 @@ impl SimpleFlowNode for Node { } if let Some(test_igvm_agent_rpc_server) = test_igvm_agent_rpc_server { - let TestIgvmAgentRpcServerOutput { exe, .. } = rt.read(test_igvm_agent_rpc_server); + let TestIgvmAgentRpcServerOutput { exe, .. } = + rt.read(test_igvm_agent_rpc_server); fs_err::copy(exe, test_content_dir.join("test_igvm_agent_rpc_server.exe"))?; } diff --git a/flowey/flowey_lib_hvlite/src/lib.rs b/flowey/flowey_lib_hvlite/src/lib.rs index 6e1b51ea17..f08ab9cabe 100644 --- a/flowey/flowey_lib_hvlite/src/lib.rs +++ b/flowey/flowey_lib_hvlite/src/lib.rs @@ -27,9 +27,9 @@ pub mod build_pipette; pub mod build_prep_steps; pub mod build_rustdoc; pub mod build_sidecar; +pub mod build_test_igvm_agent_rpc_server; pub mod build_tmk_vmm; pub mod build_tmks; -pub mod build_test_igvm_agent_rpc_server; pub mod build_tpm_guest_tests; pub mod build_vmfirmwareigvm_dll; pub mod build_vmgstool; diff --git a/vm/devices/get/guest_emulation_device/src/lib.rs b/vm/devices/get/guest_emulation_device/src/lib.rs index 1a5d02db58..720a7c4276 100644 --- a/vm/devices/get/guest_emulation_device/src/lib.rs +++ b/vm/devices/get/guest_emulation_device/src/lib.rs @@ -16,10 +16,6 @@ pub mod resolver; #[cfg(feature = "test_utilities")] pub mod test_utilities; -pub use test_igvm_agent_lib::IgvmAgentAction; -pub use test_igvm_agent_lib::IgvmAgentTestPlan; -pub use test_igvm_agent_lib::IgvmAgentTestSetting; -use test_igvm_agent_lib::TestIgvmAgent; use async_trait::async_trait; use core::mem::size_of; use disk_backend::Disk; @@ -66,6 +62,10 @@ use power_resources::PowerRequestClient; use scsi_buffers::OwnedRequestBuffers; use std::io::IoSlice; use task_control::StopTask; +pub use test_igvm_agent_lib::IgvmAgentAction; +pub use test_igvm_agent_lib::IgvmAgentTestPlan; +pub use test_igvm_agent_lib::IgvmAgentTestSetting; +use test_igvm_agent_lib::TestIgvmAgent; use thiserror::Error; use video_core::FramebufferControl; use vmbus_async::async_dgram::AsyncRecvExt; diff --git a/vm/devices/get/test_igvm_agent_lib/src/lib.rs b/vm/devices/get/test_igvm_agent_lib/src/lib.rs index fc1a96a8d5..4b45e6d546 100644 --- a/vm/devices/get/test_igvm_agent_lib/src/lib.rs +++ b/vm/devices/get/test_igvm_agent_lib/src/lib.rs @@ -15,6 +15,7 @@ use crate::test_crypto::TestSha1; use crate::test_crypto::aes_key_wrap_with_padding; use base64::Engine; use get_resources::ged::IgvmAttestTestConfig; +use inspect::Inspect; use openhcl_attestation_protocol::igvm_attest::get::IGVM_ATTEST_REQUEST_CURRENT_VERSION; use openhcl_attestation_protocol::igvm_attest::get::IGVM_ATTEST_RESPONSE_CURRENT_VERSION; use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestAkCertResponseHeader; @@ -34,10 +35,9 @@ use rsa::rand_core::OsRng; use rsa::rand_core::RngCore; use rsa::rand_core::SeedableRng; use sha2::Sha256; -use std::collections::VecDeque; use std::collections::HashMap; +use std::collections::VecDeque; use thiserror::Error; -use inspect::Inspect; use zerocopy::FromBytes; use zerocopy::IntoBytes; diff --git a/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/handlers.rs b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/handlers.rs index c3aef00b03..00bf1ecffc 100644 --- a/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/handlers.rs +++ b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/handlers.rs @@ -18,6 +18,7 @@ use windows_sys::Win32::System::Rpc::RpcRaiseException; #[unsafe(no_mangle)] /// Allocator shim invoked by the generated MIDL stubs. +// SAFETY: FFI pub unsafe extern "C" fn MIDL_user_allocate(size: usize) -> *mut c_void { use windows_sys::Win32::System::Com::CoTaskMemAlloc; unsafe { CoTaskMemAlloc(size) } @@ -25,6 +26,7 @@ pub unsafe extern "C" fn MIDL_user_allocate(size: usize) -> *mut c_void { #[unsafe(no_mangle)] /// Deallocator shim invoked by the generated MIDL stubs. +// SAFETY: FFI pub unsafe extern "C" fn MIDL_user_free(ptr: *mut c_void) { use windows_sys::Win32::System::Com::CoTaskMemFree; if !ptr.is_null() { @@ -83,6 +85,7 @@ fn write_response_size(ptr: *mut u32, value: u32) -> Result<(), HRESULT> { if ptr.is_null() { Err(E_POINTER) } else { + // SAFETY: memory access unsafe { *ptr = value; } @@ -117,25 +120,38 @@ fn read_utf16(ptr: *const u16) -> Option { return None; } - unsafe { + // SAFETY: memory access + std::panic::catch_unwind(|| unsafe { let mut len = 0usize; + + // Scan for null terminator with bounds checking while len < MAX_LEN { - if *ptr.add(len) == 0 { + // Use read_volatile to prevent compiler optimizations that might + // assume the pointer is valid, and to ensure we actually read + // the memory (which will fault if invalid) + let char_val = ptr::read_volatile(ptr.add(len)); + if char_val == 0 { break; } len += 1; } + // If we hit MAX_LEN without finding a null terminator, + // the string is too long or the pointer is invalid if len == MAX_LEN { return None; } + // Now that we know the length, create a slice and convert to String let slice = slice::from_raw_parts(ptr, len); String::from_utf16(slice).ok() - } + }) + .ok() + .flatten() } /// Entry point that services `RpcIGVmAttest` requests for the test agent. +// SAFETY: FFI #[unsafe(export_name = "RpcIGVmAttest")] pub extern "system" fn rpc_igvm_attest( _binding_handle: *mut c_void, @@ -172,6 +188,7 @@ pub extern "system" fn rpc_igvm_attest( return err; } + // SAFETY: memory access let report_slice = unsafe { if report_size == 0 { &[][..] @@ -231,6 +248,7 @@ pub extern "system" fn rpc_igvm_attest( /// Entry point that services `RpcVmGspRequest` calls for the test agent. /// /// This function is currently disabled and will raise RPC_S_SERVER_UNAVAILABLE. +// SAFETY: FFI #[unsafe(export_name = "RpcVmGspRequest")] pub extern "system" fn rpc_vm_gsp_request( _binding_handle: *mut c_void, @@ -245,6 +263,7 @@ pub extern "system" fn rpc_vm_gsp_request( // Now we can safely dereference the structures since they match the IDL definitions let (new_gsp_len, encrypted_gsp_count, supported_flags) = if !request_data.is_null() { + // SAFETY: memory access let request = unsafe { &*request_data }; let encrypted_count = request .encrypted_gsp @@ -262,6 +281,7 @@ pub extern "system" fn rpc_vm_gsp_request( let (response_encrypted_len, response_decrypted_count, response_flags) = if !response_data.is_null() { + // SAFETY: memory access let response = unsafe { &*response_data }; let decrypted_count = response .decrypted_gsp @@ -290,11 +310,11 @@ pub extern "system" fn rpc_vm_gsp_request( ); // Raise RPC_S_SERVER_UNAVAILABLE exception + // SAFETY: Make an FFI call unsafe { RpcRaiseException(RPC_S_SERVER_UNAVAILABLE); } - // This line is never reached due to RpcRaiseException, but kept for clarity - #[allow(unreachable_code)] - E_FAIL + // This line is never reached due to RpcRaiseException + unreachable!(); } diff --git a/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs index 531edf7dd6..abed32c085 100644 --- a/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs +++ b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs @@ -31,13 +31,16 @@ pub const ENDPOINT: &str = "IGVM_AGENT_RPC_SERVER"; pub type RpcInterfaceHandle = *mut c_void; +// SAFETY: FFI handle unsafe extern "C" { - pub static IGVmAgentRpcApi_v1_0_s_ifspec: RpcInterfaceHandle; + pub static IgvmAgentRpcApi: RpcInterfaceHandle; } + fn to_wide(s: &str) -> Vec { OsStr::new(s).encode_wide().chain([0]).collect() } +// SAFETY: FFI unsafe extern "system" fn rpc_bind_callback( _context: *const c_void, _uuid: *const c_void, @@ -48,6 +51,7 @@ unsafe extern "system" fn rpc_bind_callback( static STOP_REQUESTED: AtomicBool = AtomicBool::new(false); +// SAFETY: FFI unsafe extern "system" fn console_ctrl_handler(ctrl_type: u32) -> BOOL { match ctrl_type { CTRL_C_EVENT | CTRL_BREAK_EVENT | CTRL_CLOSE_EVENT | CTRL_SHUTDOWN_EVENT => { @@ -60,7 +64,7 @@ unsafe extern "system" fn console_ctrl_handler(ctrl_type: u32) -> BOOL { } TRUE } - _ => 0, + _ => FALSE, } } @@ -68,6 +72,7 @@ struct ConsoleHandlerGuard; impl ConsoleHandlerGuard { fn register() -> Result { + // SAFETY: Make an FFI call. unsafe { if SetConsoleCtrlHandler(Some(console_ctrl_handler), TRUE) == 0 { return Err("SetConsoleCtrlHandler failed".to_owned()); @@ -81,6 +86,7 @@ impl ConsoleHandlerGuard { impl Drop for ConsoleHandlerGuard { fn drop(&mut self) { + // SAFETY: Make an FFI call. unsafe { SetConsoleCtrlHandler(Some(console_ctrl_handler), FALSE); } @@ -106,7 +112,7 @@ fn register_protocol_and_interface() -> Result<(), String> { tracing::info!("RPC protocol bound successfully"); let status = RpcServerRegisterIf3( - IGVmAgentRpcApi_v1_0_s_ifspec, + IgvmAgentRpcApi, ptr::null_mut(), ptr::null_mut(), 0, @@ -126,8 +132,9 @@ fn register_protocol_and_interface() -> Result<(), String> { } fn unregister_interface() { + // SAFETY: Make an FFI call. unsafe { - RpcServerUnregisterIf(IGVmAgentRpcApi_v1_0_s_ifspec, ptr::null_mut(), 0); + RpcServerUnregisterIf(IgvmAgentRpcApi, ptr::null_mut(), 0); } tracing::info!("RPC interface unregistered"); } diff --git a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs index 08cdb9d789..4084e737aa 100644 --- a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs +++ b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs @@ -3,6 +3,8 @@ use anyhow::Context; use anyhow::ensure; +#[cfg(windows)] +use pal; use petri::PetriGuestStateLifetime; use petri::PetriVmBuilder; use petri::PetriVmmBackend; @@ -16,8 +18,6 @@ use petri_artifacts_vmm_test::artifacts::guest_tools::TPM_GUEST_TESTS_WINDOWS_X6 use petri_artifacts_vmm_test::artifacts::host_tools::TEST_IGVM_AGENT_RPC_SERVER_WINDOWS_X64; use pipette_client::PipetteClient; use std::path::Path; -#[cfg(windows)] -use pal; use vmm_test_macros::openvmm_test; use vmm_test_macros::vmm_test; @@ -456,8 +456,10 @@ async fn cvm_tpm_guest_tests( config: PetriVmBuilder, extra_deps: (ResolvedArtifact, ResolvedArtifact), ) -> anyhow::Result<()> { - use std::process::Stdio; + use std::io::BufRead; + use std::io::BufReader; use std::io::Read; + use std::process::Stdio; let os_flavor = config.os_flavor(); let (tpm_guest_tests_artifact, rpc_server_artifact) = extra_deps; @@ -483,20 +485,42 @@ async fn cvm_tpm_guest_tests( .read(&mut b) .context("failed to read from RPC server stdout")?; + // Spawn a task to read and log stderr from the RPC server + let stderr_task = std::thread::spawn(move || { + let reader = BufReader::new(stderr_read); + for line in reader.lines() { + match line { + Ok(line) => tracing::info!(target: "test_igvm_agent_rpc_server", "{}", line), + Err(e) => { + tracing::warn!("failed to read RPC server stderr: {}", e); + break; + } + } + } + }); + // Create a guard to ensure the RPC server is killed when this function exits - struct RpcServerGuard(std::process::Child); + struct RpcServerGuard { + child: std::process::Child, + stderr_task: std::thread::JoinHandle<()>, + } impl Drop for RpcServerGuard { fn drop(&mut self) { tracing::info!("terminating test_igvm_agent_rpc_server"); - let _ = self.0.kill(); - let _ = self.0.wait(); + let _ = self.child.kill(); + let _ = self.child.wait(); + // Give the stderr logging thread a moment to finish + let _ = self.stderr_task.join(); } } - let _rpc_server_guard = RpcServerGuard(rpc_server_child); + let _rpc_server_guard = RpcServerGuard { + child: rpc_server_child, + stderr_task, + }; let config = config .with_tpm(true) - .with_tpm_state_persistence(false) + .with_tpm_state_persistence(true) .with_guest_state_lifetime(PetriGuestStateLifetime::Disk); let (vm, agent) = config.run().await?; From b00172dcf92c6535f28a9df63471e8c6e37307c8 Mon Sep 17 00:00:00 2001 From: Ming-Wei Shih Date: Wed, 3 Dec 2025 00:36:34 +0000 Subject: [PATCH 04/32] safety comment Signed-off-by: Ming-Wei Shih --- .../test_igvm_agent_rpc_server/src/main.rs | 3 -- .../src/rpc/handlers.rs | 10 +++- .../test_igvm_agent_rpc_server/src/rpc/mod.rs | 3 ++ .../src/rpc/server.rs | 52 +++++++++++-------- 4 files changed, 41 insertions(+), 27 deletions(-) diff --git a/vm/devices/get/test_igvm_agent_rpc_server/src/main.rs b/vm/devices/get/test_igvm_agent_rpc_server/src/main.rs index e3eeeca8da..c84e5189a4 100644 --- a/vm/devices/get/test_igvm_agent_rpc_server/src/main.rs +++ b/vm/devices/get/test_igvm_agent_rpc_server/src/main.rs @@ -3,9 +3,6 @@ //! Standalone executable that hosts the IGVM agent Windows RPC façade. -// UNSAFETY: Windows FFI -#![cfg_attr(windows, expect(unsafe_code))] - #[cfg(target_os = "windows")] mod rpc; diff --git a/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/handlers.rs b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/handlers.rs index 00bf1ecffc..ac30a877a2 100644 --- a/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/handlers.rs +++ b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/handlers.rs @@ -18,18 +18,22 @@ use windows_sys::Win32::System::Rpc::RpcRaiseException; #[unsafe(no_mangle)] /// Allocator shim invoked by the generated MIDL stubs. -// SAFETY: FFI +/// # SAFETY +/// Define FFI to fullfil the linker requirement pub unsafe extern "C" fn MIDL_user_allocate(size: usize) -> *mut c_void { use windows_sys::Win32::System::Com::CoTaskMemAlloc; + // SAFETY: make an FFI call unsafe { CoTaskMemAlloc(size) } } #[unsafe(no_mangle)] /// Deallocator shim invoked by the generated MIDL stubs. -// SAFETY: FFI +/// # SAFETY +/// Define FFI to fullfil the linker requirement pub unsafe extern "C" fn MIDL_user_free(ptr: *mut c_void) { use windows_sys::Win32::System::Com::CoTaskMemFree; if !ptr.is_null() { + // SAFETY: make an FFI call unsafe { CoTaskMemFree(ptr); } @@ -95,6 +99,7 @@ fn write_response_size(ptr: *mut u32, value: u32) -> Result<(), HRESULT> { fn copy_to_buffer(buffer: &[u8], dest: *mut u8) { if !buffer.is_empty() { + // SAFETY: memory access unsafe { ptr::copy_nonoverlapping(buffer.as_ptr(), dest, buffer.len()); } @@ -109,6 +114,7 @@ fn read_guid(ptr: *const Guid) -> Option { if ptr.is_null() { None } else { + // SAFETY: memory access Some(unsafe { *ptr }) } } diff --git a/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/mod.rs b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/mod.rs index 661b8a00ea..ca906dbcbc 100644 --- a/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/mod.rs +++ b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/mod.rs @@ -1,6 +1,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +// UNSAFETY: Windows FFI +#![expect(unsafe_code)] + pub use server::run_server; mod handlers; diff --git a/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs index abed32c085..c2b013e60f 100644 --- a/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs +++ b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs @@ -33,14 +33,15 @@ pub type RpcInterfaceHandle = *mut c_void; // SAFETY: FFI handle unsafe extern "C" { - pub static IgvmAgentRpcApi: RpcInterfaceHandle; + pub static IGVmAgentRpcApi_v1_0_s_ifspec: RpcInterfaceHandle; } fn to_wide(s: &str) -> Vec { OsStr::new(s).encode_wide().chain([0]).collect() } -// SAFETY: FFI +/// # SAFETY +/// The callback for the passing through FFI unsafe extern "system" fn rpc_bind_callback( _context: *const c_void, _uuid: *const c_void, @@ -51,12 +52,14 @@ unsafe extern "system" fn rpc_bind_callback( static STOP_REQUESTED: AtomicBool = AtomicBool::new(false); -// SAFETY: FFI +/// # SAFETY +/// Used by RPC handler registration. unsafe extern "system" fn console_ctrl_handler(ctrl_type: u32) -> BOOL { match ctrl_type { CTRL_C_EVENT | CTRL_BREAK_EVENT | CTRL_CLOSE_EVENT | CTRL_SHUTDOWN_EVENT => { if !STOP_REQUESTED.swap(true, Ordering::SeqCst) { tracing::info!("console control signal {ctrl_type} received; requesting shutdown"); + // SAFETY: Make an FFI call. let status = unsafe { RpcMgmtStopServerListening(ptr::null_mut()) }; if status != RPC_S_OK && status != RPC_S_NOT_LISTENING { tracing::error!("RpcMgmtStopServerListening failed: {status}"); @@ -94,25 +97,29 @@ impl Drop for ConsoleHandlerGuard { } fn register_protocol_and_interface() -> Result<(), String> { - unsafe { - tracing::info!(protocol = %PROTOCOL_SEQUENCE, endpoint = %ENDPOINT, "registering RPC protocol"); - let mut protocol_seq = to_wide(PROTOCOL_SEQUENCE); - let mut endpoint = to_wide(ENDPOINT); + tracing::info!(protocol = %PROTOCOL_SEQUENCE, endpoint = %ENDPOINT, "registering RPC protocol"); + let mut protocol_seq = to_wide(PROTOCOL_SEQUENCE); + let mut endpoint = to_wide(ENDPOINT); - let status = RpcServerUseProtseqEpW( + // SAFETY: Make an FFI call. + let status = unsafe { + RpcServerUseProtseqEpW( protocol_seq.as_mut_ptr(), RPC_C_PROTSEQ_MAX_REQS_DEFAULT, endpoint.as_mut_ptr(), ptr::null_mut(), - ); - if status != RPC_S_OK { - tracing::error!(status = status, "RpcServerUseProtseqEpW failed"); - return Err(format!("RpcServerUseProtseqEpW failed: {status}")); - } - tracing::info!("RPC protocol bound successfully"); + ) + }; + if status != RPC_S_OK { + tracing::error!(status = status, "RpcServerUseProtseqEpW failed"); + return Err(format!("RpcServerUseProtseqEpW failed: {status}")); + } + tracing::info!("RPC protocol bound successfully"); - let status = RpcServerRegisterIf3( - IgvmAgentRpcApi, + // SAFETY: Make an FFI call. + let status = unsafe { + RpcServerRegisterIf3( + IGVmAgentRpcApi_v1_0_s_ifspec, ptr::null_mut(), ptr::null_mut(), 0, @@ -120,11 +127,11 @@ fn register_protocol_and_interface() -> Result<(), String> { 0, Some(rpc_bind_callback), ptr::null_mut(), - ); - if status != RPC_S_OK { - tracing::error!(status = status, "RpcServerRegisterIf3 failed"); - return Err(format!("RpcServerRegisterIf3 failed: {status}")); - } + ) + }; + if status != RPC_S_OK { + tracing::error!(status = status, "RpcServerRegisterIf3 failed"); + return Err(format!("RpcServerRegisterIf3 failed: {status}")); } tracing::info!("RPC interface registered"); @@ -134,7 +141,7 @@ fn register_protocol_and_interface() -> Result<(), String> { fn unregister_interface() { // SAFETY: Make an FFI call. unsafe { - RpcServerUnregisterIf(IgvmAgentRpcApi, ptr::null_mut(), 0); + RpcServerUnregisterIf(IGVmAgentRpcApi_v1_0_s_ifspec, ptr::null_mut(), 0); } tracing::info!("RPC interface unregistered"); } @@ -145,6 +152,7 @@ pub fn run_server() -> Result<(), String> { register_protocol_and_interface()?; let _handler = ConsoleHandlerGuard::register()?; + // SAFETY: Make an FFI call. let listen_status = unsafe { RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, 0) }; unregister_interface(); From 0aadbcc1a0b65972d3b6578ac1fd0e95c8d7ef92 Mon Sep 17 00:00:00 2001 From: Ming-Wei Shih Date: Wed, 3 Dec 2025 00:47:02 +0000 Subject: [PATCH 05/32] x Signed-off-by: Ming-Wei Shih --- .github/workflows/openvmm-ci.yaml | 154 +++++++++++------- .github/workflows/openvmm-pr-release.yaml | 154 +++++++++++------- .github/workflows/openvmm-pr.yaml | 154 +++++++++++------- Cargo.lock | 3 +- Cargo.toml | 1 + .../get/guest_emulation_device/Cargo.toml | 1 - .../get/test_igvm_agent_rpc_server/Cargo.toml | 9 +- .../get/test_igvm_agent_rpc_server/build.rs | 3 + .../src/rpc/server.rs | 2 +- .../vmm_tests/tests/tests/multiarch/tpm.rs | 12 +- 10 files changed, 318 insertions(+), 175 deletions(-) diff --git a/.github/workflows/openvmm-ci.yaml b/.github/workflows/openvmm-ci.yaml index 8e2c335fc1..b50906c49e 100644 --- a/.github/workflows/openvmm-ci.yaml +++ b/.github/workflows/openvmm-ci.yaml @@ -1905,7 +1905,7 @@ jobs: - name: 🌼📦 Download artifacts uses: actions/download-artifact@v4 with: - pattern: '{_internal-flowey-bootstrap-x86_64-windows-uid-12,x64-guest_test_uefi,x64-linux-musl-pipette,x64-linux-musl-tmk_vmm,x64-linux-tpm_guest_tests,x64-openhcl-igvm,x64-tmks,x64-windows-openvmm,x64-windows-pipette,x64-windows-prep_steps,x64-windows-tmk_vmm,x64-windows-tpm_guest_tests,x64-windows-vmgstool,x64-windows-vmm-tests-archive}' + pattern: '{_internal-flowey-bootstrap-x86_64-windows-uid-12,x64-guest_test_uefi,x64-linux-musl-pipette,x64-linux-musl-tmk_vmm,x64-linux-tpm_guest_tests,x64-openhcl-igvm,x64-tmks,x64-windows-openvmm,x64-windows-pipette,x64-windows-prep_steps,x64-windows-test_igvm_agent_rpc_server,x64-windows-tmk_vmm,x64-windows-tpm_guest_tests,x64-windows-vmgstool,x64-windows-vmm-tests-archive}' path: ${{ runner.temp }}/used_artifacts/ - run: echo "${{ runner.temp }}/used_artifacts/_internal-flowey-bootstrap-x86_64-windows-uid-12" >> $GITHUB_PATH shell: bash @@ -1933,6 +1933,7 @@ jobs: echo "${{ runner.temp }}\\used_artifacts\\x64-windows-openvmm" | flowey.exe v 16 'artifact_use_from_x64-windows-openvmm' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-pipette" | flowey.exe v 16 'artifact_use_from_x64-windows-pipette' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-prep_steps" | flowey.exe v 16 'artifact_use_from_x64-windows-prep_steps' --is-raw-string update + echo "${{ runner.temp }}\\used_artifacts\\x64-windows-test_igvm_agent_rpc_server" | flowey.exe v 16 'artifact_use_from_x64-windows-test_igvm_agent_rpc_server' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-tmk_vmm" | flowey.exe v 16 'artifact_use_from_x64-windows-tmk_vmm' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-tpm_guest_tests" | flowey.exe v 16 'artifact_use_from_x64-windows-tpm_guest_tests' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-vmgstool" | flowey.exe v 16 'artifact_use_from_x64-windows-vmgstool' --is-raw-string update @@ -1944,12 +1945,13 @@ jobs: flowey.exe e 16 flowey_core::pipeline::artifact::resolve 6 flowey.exe e 16 flowey_core::pipeline::artifact::resolve 1 flowey.exe e 16 flowey_core::pipeline::artifact::resolve 0 - flowey.exe e 16 flowey_core::pipeline::artifact::resolve 8 + flowey.exe e 16 flowey_core::pipeline::artifact::resolve 9 flowey.exe e 16 flowey_core::pipeline::artifact::resolve 2 flowey.exe e 16 flowey_core::pipeline::artifact::resolve 4 + flowey.exe e 16 flowey_core::pipeline::artifact::resolve 11 flowey.exe e 16 flowey_core::pipeline::artifact::resolve 10 - flowey.exe e 16 flowey_core::pipeline::artifact::resolve 9 flowey.exe e 16 flowey_core::pipeline::artifact::resolve 3 + flowey.exe e 16 flowey_core::pipeline::artifact::resolve 8 flowey.exe e 16 flowey_lib_hvlite::_jobs::consume_and_test_nextest_vmm_tests_archive 0 shell: bash - name: resolve OpenHCL igvm artifact @@ -2106,7 +2108,7 @@ jobs: flowey.exe e 16 flowey_lib_hvlite::git_checkout_openvmm_repo 0 flowey.exe e 16 flowey_lib_hvlite::run_cargo_nextest_run 0 flowey.exe e 16 flowey_lib_hvlite::run_cargo_nextest_run 1 - flowey.exe e 16 flowey_core::pipeline::artifact::resolve 11 + flowey.exe e 16 flowey_core::pipeline::artifact::resolve 12 flowey.exe e 16 flowey_lib_hvlite::test_nextest_vmm_tests_archive 0 shell: bash - name: generate nextest command @@ -2183,7 +2185,7 @@ jobs: - name: 🌼📦 Download artifacts uses: actions/download-artifact@v4 with: - pattern: '{_internal-flowey-bootstrap-x86_64-windows-uid-12,x64-guest_test_uefi,x64-linux-musl-pipette,x64-linux-musl-tmk_vmm,x64-linux-tpm_guest_tests,x64-openhcl-igvm,x64-tmks,x64-windows-openvmm,x64-windows-pipette,x64-windows-prep_steps,x64-windows-tmk_vmm,x64-windows-tpm_guest_tests,x64-windows-vmgstool,x64-windows-vmm-tests-archive}' + pattern: '{_internal-flowey-bootstrap-x86_64-windows-uid-12,x64-guest_test_uefi,x64-linux-musl-pipette,x64-linux-musl-tmk_vmm,x64-linux-tpm_guest_tests,x64-openhcl-igvm,x64-tmks,x64-windows-openvmm,x64-windows-pipette,x64-windows-prep_steps,x64-windows-test_igvm_agent_rpc_server,x64-windows-tmk_vmm,x64-windows-tpm_guest_tests,x64-windows-vmgstool,x64-windows-vmm-tests-archive}' path: ${{ runner.temp }}/used_artifacts/ - run: echo "${{ runner.temp }}/used_artifacts/_internal-flowey-bootstrap-x86_64-windows-uid-12" >> $GITHUB_PATH shell: bash @@ -2211,6 +2213,7 @@ jobs: echo "${{ runner.temp }}\\used_artifacts\\x64-windows-openvmm" | flowey.exe v 17 'artifact_use_from_x64-windows-openvmm' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-pipette" | flowey.exe v 17 'artifact_use_from_x64-windows-pipette' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-prep_steps" | flowey.exe v 17 'artifact_use_from_x64-windows-prep_steps' --is-raw-string update + echo "${{ runner.temp }}\\used_artifacts\\x64-windows-test_igvm_agent_rpc_server" | flowey.exe v 17 'artifact_use_from_x64-windows-test_igvm_agent_rpc_server' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-tmk_vmm" | flowey.exe v 17 'artifact_use_from_x64-windows-tmk_vmm' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-tpm_guest_tests" | flowey.exe v 17 'artifact_use_from_x64-windows-tpm_guest_tests' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-vmgstool" | flowey.exe v 17 'artifact_use_from_x64-windows-vmgstool' --is-raw-string update @@ -2222,12 +2225,13 @@ jobs: flowey.exe e 17 flowey_core::pipeline::artifact::resolve 6 flowey.exe e 17 flowey_core::pipeline::artifact::resolve 1 flowey.exe e 17 flowey_core::pipeline::artifact::resolve 0 - flowey.exe e 17 flowey_core::pipeline::artifact::resolve 8 + flowey.exe e 17 flowey_core::pipeline::artifact::resolve 9 flowey.exe e 17 flowey_core::pipeline::artifact::resolve 2 flowey.exe e 17 flowey_core::pipeline::artifact::resolve 4 + flowey.exe e 17 flowey_core::pipeline::artifact::resolve 11 flowey.exe e 17 flowey_core::pipeline::artifact::resolve 10 - flowey.exe e 17 flowey_core::pipeline::artifact::resolve 9 flowey.exe e 17 flowey_core::pipeline::artifact::resolve 3 + flowey.exe e 17 flowey_core::pipeline::artifact::resolve 8 flowey.exe e 17 flowey_lib_hvlite::_jobs::consume_and_test_nextest_vmm_tests_archive 0 shell: bash - name: resolve OpenHCL igvm artifact @@ -2384,7 +2388,7 @@ jobs: flowey.exe e 17 flowey_lib_hvlite::git_checkout_openvmm_repo 0 flowey.exe e 17 flowey_lib_hvlite::run_cargo_nextest_run 0 flowey.exe e 17 flowey_lib_hvlite::run_cargo_nextest_run 1 - flowey.exe e 17 flowey_core::pipeline::artifact::resolve 11 + flowey.exe e 17 flowey_core::pipeline::artifact::resolve 12 flowey.exe e 17 flowey_lib_hvlite::test_nextest_vmm_tests_archive 0 shell: bash - name: generate nextest command @@ -2464,7 +2468,7 @@ jobs: - name: 🌼📦 Download artifacts uses: actions/download-artifact@v4 with: - pattern: '{_internal-flowey-bootstrap-x86_64-windows-uid-12,x64-guest_test_uefi,x64-linux-musl-pipette,x64-linux-musl-tmk_vmm,x64-linux-tpm_guest_tests,x64-openhcl-igvm,x64-tmks,x64-windows-openvmm,x64-windows-pipette,x64-windows-prep_steps,x64-windows-tmk_vmm,x64-windows-tpm_guest_tests,x64-windows-vmgstool,x64-windows-vmm-tests-archive}' + pattern: '{_internal-flowey-bootstrap-x86_64-windows-uid-12,x64-guest_test_uefi,x64-linux-musl-pipette,x64-linux-musl-tmk_vmm,x64-linux-tpm_guest_tests,x64-openhcl-igvm,x64-tmks,x64-windows-openvmm,x64-windows-pipette,x64-windows-prep_steps,x64-windows-test_igvm_agent_rpc_server,x64-windows-tmk_vmm,x64-windows-tpm_guest_tests,x64-windows-vmgstool,x64-windows-vmm-tests-archive}' path: ${{ runner.temp }}/used_artifacts/ - run: echo "${{ runner.temp }}/used_artifacts/_internal-flowey-bootstrap-x86_64-windows-uid-12" >> $GITHUB_PATH shell: bash @@ -2492,6 +2496,7 @@ jobs: echo "${{ runner.temp }}\\used_artifacts\\x64-windows-openvmm" | flowey.exe v 18 'artifact_use_from_x64-windows-openvmm' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-pipette" | flowey.exe v 18 'artifact_use_from_x64-windows-pipette' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-prep_steps" | flowey.exe v 18 'artifact_use_from_x64-windows-prep_steps' --is-raw-string update + echo "${{ runner.temp }}\\used_artifacts\\x64-windows-test_igvm_agent_rpc_server" | flowey.exe v 18 'artifact_use_from_x64-windows-test_igvm_agent_rpc_server' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-tmk_vmm" | flowey.exe v 18 'artifact_use_from_x64-windows-tmk_vmm' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-tpm_guest_tests" | flowey.exe v 18 'artifact_use_from_x64-windows-tpm_guest_tests' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-vmgstool" | flowey.exe v 18 'artifact_use_from_x64-windows-vmgstool' --is-raw-string update @@ -2503,12 +2508,13 @@ jobs: flowey.exe e 18 flowey_core::pipeline::artifact::resolve 6 flowey.exe e 18 flowey_core::pipeline::artifact::resolve 1 flowey.exe e 18 flowey_core::pipeline::artifact::resolve 0 - flowey.exe e 18 flowey_core::pipeline::artifact::resolve 8 + flowey.exe e 18 flowey_core::pipeline::artifact::resolve 9 flowey.exe e 18 flowey_core::pipeline::artifact::resolve 2 flowey.exe e 18 flowey_core::pipeline::artifact::resolve 4 + flowey.exe e 18 flowey_core::pipeline::artifact::resolve 11 flowey.exe e 18 flowey_core::pipeline::artifact::resolve 10 - flowey.exe e 18 flowey_core::pipeline::artifact::resolve 9 flowey.exe e 18 flowey_core::pipeline::artifact::resolve 3 + flowey.exe e 18 flowey_core::pipeline::artifact::resolve 8 flowey.exe e 18 flowey_lib_hvlite::_jobs::consume_and_test_nextest_vmm_tests_archive 0 shell: bash - name: resolve OpenHCL igvm artifact @@ -2665,7 +2671,7 @@ jobs: flowey.exe e 18 flowey_lib_hvlite::git_checkout_openvmm_repo 0 flowey.exe e 18 flowey_lib_hvlite::run_cargo_nextest_run 0 flowey.exe e 18 flowey_lib_hvlite::run_cargo_nextest_run 1 - flowey.exe e 18 flowey_core::pipeline::artifact::resolve 11 + flowey.exe e 18 flowey_core::pipeline::artifact::resolve 12 flowey.exe e 18 flowey_lib_hvlite::test_nextest_vmm_tests_archive 0 shell: bash - name: generate nextest command @@ -2742,7 +2748,7 @@ jobs: - name: 🌼📦 Download artifacts uses: actions/download-artifact@v4 with: - pattern: '{_internal-flowey-bootstrap-x86_64-windows-uid-12,x64-guest_test_uefi,x64-linux-musl-pipette,x64-linux-musl-tmk_vmm,x64-linux-tpm_guest_tests,x64-openhcl-igvm,x64-tmks,x64-windows-openvmm,x64-windows-pipette,x64-windows-prep_steps,x64-windows-tmk_vmm,x64-windows-tpm_guest_tests,x64-windows-vmgstool,x64-windows-vmm-tests-archive}' + pattern: '{_internal-flowey-bootstrap-x86_64-windows-uid-12,x64-guest_test_uefi,x64-linux-musl-pipette,x64-linux-musl-tmk_vmm,x64-linux-tpm_guest_tests,x64-openhcl-igvm,x64-tmks,x64-windows-openvmm,x64-windows-pipette,x64-windows-prep_steps,x64-windows-test_igvm_agent_rpc_server,x64-windows-tmk_vmm,x64-windows-tpm_guest_tests,x64-windows-vmgstool,x64-windows-vmm-tests-archive}' path: ${{ runner.temp }}/used_artifacts/ - run: echo "${{ runner.temp }}/used_artifacts/_internal-flowey-bootstrap-x86_64-windows-uid-12" >> $GITHUB_PATH shell: bash @@ -2770,6 +2776,7 @@ jobs: echo "${{ runner.temp }}\\used_artifacts\\x64-windows-openvmm" | flowey.exe v 19 'artifact_use_from_x64-windows-openvmm' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-pipette" | flowey.exe v 19 'artifact_use_from_x64-windows-pipette' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-prep_steps" | flowey.exe v 19 'artifact_use_from_x64-windows-prep_steps' --is-raw-string update + echo "${{ runner.temp }}\\used_artifacts\\x64-windows-test_igvm_agent_rpc_server" | flowey.exe v 19 'artifact_use_from_x64-windows-test_igvm_agent_rpc_server' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-tmk_vmm" | flowey.exe v 19 'artifact_use_from_x64-windows-tmk_vmm' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-tpm_guest_tests" | flowey.exe v 19 'artifact_use_from_x64-windows-tpm_guest_tests' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-vmgstool" | flowey.exe v 19 'artifact_use_from_x64-windows-vmgstool' --is-raw-string update @@ -2781,12 +2788,13 @@ jobs: flowey.exe e 19 flowey_core::pipeline::artifact::resolve 6 flowey.exe e 19 flowey_core::pipeline::artifact::resolve 1 flowey.exe e 19 flowey_core::pipeline::artifact::resolve 0 - flowey.exe e 19 flowey_core::pipeline::artifact::resolve 8 + flowey.exe e 19 flowey_core::pipeline::artifact::resolve 9 flowey.exe e 19 flowey_core::pipeline::artifact::resolve 2 flowey.exe e 19 flowey_core::pipeline::artifact::resolve 4 + flowey.exe e 19 flowey_core::pipeline::artifact::resolve 11 flowey.exe e 19 flowey_core::pipeline::artifact::resolve 10 - flowey.exe e 19 flowey_core::pipeline::artifact::resolve 9 flowey.exe e 19 flowey_core::pipeline::artifact::resolve 3 + flowey.exe e 19 flowey_core::pipeline::artifact::resolve 8 flowey.exe e 19 flowey_lib_hvlite::_jobs::consume_and_test_nextest_vmm_tests_archive 0 shell: bash - name: resolve OpenHCL igvm artifact @@ -2943,7 +2951,7 @@ jobs: flowey.exe e 19 flowey_lib_hvlite::git_checkout_openvmm_repo 0 flowey.exe e 19 flowey_lib_hvlite::run_cargo_nextest_run 0 flowey.exe e 19 flowey_lib_hvlite::run_cargo_nextest_run 1 - flowey.exe e 19 flowey_core::pipeline::artifact::resolve 11 + flowey.exe e 19 flowey_core::pipeline::artifact::resolve 12 flowey.exe e 19 flowey_lib_hvlite::test_nextest_vmm_tests_archive 0 shell: bash - name: generate nextest command @@ -3266,12 +3274,12 @@ jobs: shell: bash - name: creating new test content dir run: |- - flowey e 20 flowey_core::pipeline::artifact::resolve 3 - flowey e 20 flowey_core::pipeline::artifact::resolve 6 flowey e 20 flowey_core::pipeline::artifact::resolve 1 flowey e 20 flowey_core::pipeline::artifact::resolve 0 flowey e 20 flowey_core::pipeline::artifact::resolve 2 flowey e 20 flowey_core::pipeline::artifact::resolve 5 + flowey e 20 flowey_core::pipeline::artifact::resolve 3 + flowey e 20 flowey_core::pipeline::artifact::resolve 6 flowey e 20 flowey_lib_hvlite::_jobs::consume_and_test_nextest_vmm_tests_archive 0 shell: bash - name: create azcopy cache dir @@ -3591,6 +3599,7 @@ jobs: shell: bash - name: creating new test content dir run: |- + flowey.exe e 21 flowey_core::pipeline::artifact::resolve 4 flowey.exe e 21 flowey_core::pipeline::artifact::resolve 5 flowey.exe e 21 flowey_core::pipeline::artifact::resolve 1 flowey.exe e 21 flowey_core::pipeline::artifact::resolve 0 @@ -3598,7 +3607,6 @@ jobs: flowey.exe e 21 flowey_core::pipeline::artifact::resolve 2 flowey.exe e 21 flowey_core::pipeline::artifact::resolve 3 flowey.exe e 21 flowey_core::pipeline::artifact::resolve 7 - flowey.exe e 21 flowey_core::pipeline::artifact::resolve 4 flowey.exe e 21 flowey_lib_hvlite::_jobs::consume_and_test_nextest_vmm_tests_archive 0 shell: bash - name: resolve OpenHCL igvm artifact @@ -4005,6 +4013,8 @@ jobs: echo "${{ runner.temp }}\\publish_artifacts\\aarch64-windows-pipette" | flowey.exe v 3 'artifact_publish_from_aarch64-windows-pipette' --is-raw-string update mkdir -p "$AgentTempDirNormal/publish_artifacts/aarch64-windows-prep_steps" echo "${{ runner.temp }}\\publish_artifacts\\aarch64-windows-prep_steps" | flowey.exe v 3 'artifact_publish_from_aarch64-windows-prep_steps' --is-raw-string update + mkdir -p "$AgentTempDirNormal/publish_artifacts/aarch64-windows-test_igvm_agent_rpc_server" + echo "${{ runner.temp }}\\publish_artifacts\\aarch64-windows-test_igvm_agent_rpc_server" | flowey.exe v 3 'artifact_publish_from_aarch64-windows-test_igvm_agent_rpc_server' --is-raw-string update mkdir -p "$AgentTempDirNormal/publish_artifacts/aarch64-windows-tmk_vmm" echo "${{ runner.temp }}\\publish_artifacts\\aarch64-windows-tmk_vmm" | flowey.exe v 3 'artifact_publish_from_aarch64-windows-tmk_vmm' --is-raw-string update mkdir -p "$AgentTempDirNormal/publish_artifacts/aarch64-windows-tpm_guest_tests" @@ -4081,32 +4091,42 @@ jobs: - name: symlink protoc run: |- flowey.exe e 3 flowey_lib_hvlite::init_openvmm_magicpath_protoc 0 - flowey.exe e 3 flowey_lib_hvlite::init_cross_build 6 + flowey.exe e 3 flowey_lib_hvlite::init_cross_build 7 shell: bash - name: cargo build prep_steps run: |- flowey.exe e 3 flowey_lib_common::run_cargo_build 2 flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 2 flowey.exe e 3 flowey_lib_hvlite::build_prep_steps 0 - flowey.exe e 3 flowey_core::pipeline::artifact::publish 4 - flowey.exe e 3 flowey_lib_hvlite::init_cross_build 3 + flowey.exe e 3 flowey_core::pipeline::artifact::publish 5 + flowey.exe e 3 flowey_lib_hvlite::init_cross_build 4 shell: bash - name: cargo build vmgstool run: |- - flowey.exe e 3 flowey_lib_common::run_cargo_build 5 - flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 7 + flowey.exe e 3 flowey_lib_common::run_cargo_build 6 + flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 10 flowey.exe e 3 flowey_lib_hvlite::build_vmgstool 0 - flowey.exe e 3 flowey_core::pipeline::artifact::publish 5 - flowey.exe e 3 flowey_lib_hvlite::init_cross_build 2 - flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 4 - flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 5 + flowey.exe e 3 flowey_core::pipeline::artifact::publish 6 + flowey.exe e 3 flowey_lib_hvlite::init_cross_build 3 + flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 7 + flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 8 shell: bash - name: cargo build tpm_guest_tests run: |- - flowey.exe e 3 flowey_lib_common::run_cargo_build 4 - flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 6 + flowey.exe e 3 flowey_lib_common::run_cargo_build 5 + flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 9 flowey.exe e 3 flowey_lib_hvlite::build_tpm_guest_tests 0 - flowey.exe e 3 flowey_core::pipeline::artifact::publish 6 + flowey.exe e 3 flowey_core::pipeline::artifact::publish 7 + flowey.exe e 3 flowey_lib_hvlite::init_cross_build 1 + flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 3 + flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 4 + shell: bash + - name: cargo build test_igvm_agent_rpc_server + run: |- + flowey.exe e 3 flowey_lib_common::run_cargo_build 3 + flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 5 + flowey.exe e 3 flowey_lib_hvlite::build_test_igvm_agent_rpc_server 0 + flowey.exe e 3 flowey_core::pipeline::artifact::publish 0 shell: bash - name: create cargo-nextest cache dir run: |- @@ -4147,31 +4167,31 @@ jobs: run: |- flowey.exe e 3 flowey_lib_common::run_cargo_nextest_archive 0 flowey.exe e 3 flowey_lib_hvlite::build_nextest_vmm_tests 0 - flowey.exe e 3 flowey_core::pipeline::artifact::publish 0 - flowey.exe e 3 flowey_lib_hvlite::init_cross_build 4 + flowey.exe e 3 flowey_core::pipeline::artifact::publish 1 + flowey.exe e 3 flowey_lib_hvlite::init_cross_build 5 shell: bash - name: cargo build openvmm run: |- flowey.exe e 3 flowey_lib_common::run_cargo_build 0 flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 0 flowey.exe e 3 flowey_lib_hvlite::build_openvmm 0 - flowey.exe e 3 flowey_core::pipeline::artifact::publish 1 - flowey.exe e 3 flowey_lib_hvlite::init_cross_build 5 + flowey.exe e 3 flowey_core::pipeline::artifact::publish 2 + flowey.exe e 3 flowey_lib_hvlite::init_cross_build 6 shell: bash - name: cargo build pipette run: |- flowey.exe e 3 flowey_lib_common::run_cargo_build 1 flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 1 flowey.exe e 3 flowey_lib_hvlite::build_pipette 0 - flowey.exe e 3 flowey_core::pipeline::artifact::publish 2 - flowey.exe e 3 flowey_lib_hvlite::init_cross_build 1 + flowey.exe e 3 flowey_core::pipeline::artifact::publish 3 + flowey.exe e 3 flowey_lib_hvlite::init_cross_build 2 shell: bash - name: cargo build tmk_vmm run: |- - flowey.exe e 3 flowey_lib_common::run_cargo_build 3 - flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 3 + flowey.exe e 3 flowey_lib_common::run_cargo_build 4 + flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 6 flowey.exe e 3 flowey_lib_hvlite::build_tmk_vmm 0 - flowey.exe e 3 flowey_core::pipeline::artifact::publish 3 + flowey.exe e 3 flowey_core::pipeline::artifact::publish 4 shell: bash - name: 'validate cache entry: cargo-nextest' run: flowey.exe e 3 flowey_lib_common::cache 3 @@ -4197,6 +4217,12 @@ jobs: name: aarch64-windows-prep_steps path: ${{ runner.temp }}/publish_artifacts/aarch64-windows-prep_steps/ include-hidden-files: true + - name: 🌼📦 Publish aarch64-windows-test_igvm_agent_rpc_server + uses: actions/upload-artifact@v4 + with: + name: aarch64-windows-test_igvm_agent_rpc_server + path: ${{ runner.temp }}/publish_artifacts/aarch64-windows-test_igvm_agent_rpc_server/ + include-hidden-files: true - name: 🌼📦 Publish aarch64-windows-tmk_vmm uses: actions/upload-artifact@v4 with: @@ -4522,6 +4548,8 @@ jobs: echo "${{ runner.temp }}\\publish_artifacts\\x64-windows-pipette" | flowey.exe v 5 'artifact_publish_from_x64-windows-pipette' --is-raw-string update mkdir -p "$AgentTempDirNormal/publish_artifacts/x64-windows-prep_steps" echo "${{ runner.temp }}\\publish_artifacts\\x64-windows-prep_steps" | flowey.exe v 5 'artifact_publish_from_x64-windows-prep_steps' --is-raw-string update + mkdir -p "$AgentTempDirNormal/publish_artifacts/x64-windows-test_igvm_agent_rpc_server" + echo "${{ runner.temp }}\\publish_artifacts\\x64-windows-test_igvm_agent_rpc_server" | flowey.exe v 5 'artifact_publish_from_x64-windows-test_igvm_agent_rpc_server' --is-raw-string update mkdir -p "$AgentTempDirNormal/publish_artifacts/x64-windows-tmk_vmm" echo "${{ runner.temp }}\\publish_artifacts\\x64-windows-tmk_vmm" | flowey.exe v 5 'artifact_publish_from_x64-windows-tmk_vmm' --is-raw-string update mkdir -p "$AgentTempDirNormal/publish_artifacts/x64-windows-tpm_guest_tests" @@ -4598,7 +4626,7 @@ jobs: - name: symlink protoc run: |- flowey.exe e 5 flowey_lib_hvlite::init_openvmm_magicpath_protoc 0 - flowey.exe e 5 flowey_lib_hvlite::init_cross_build 4 + flowey.exe e 5 flowey_lib_hvlite::init_cross_build 5 shell: bash - name: cargo build openvmm run: |- @@ -4606,7 +4634,7 @@ jobs: flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 0 flowey.exe e 5 flowey_lib_hvlite::build_openvmm 0 flowey.exe e 5 flowey_core::pipeline::artifact::publish 0 - flowey.exe e 5 flowey_lib_hvlite::init_cross_build 5 + flowey.exe e 5 flowey_lib_hvlite::init_cross_build 6 shell: bash - name: cargo build pipette run: |- @@ -4614,15 +4642,15 @@ jobs: flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 1 flowey.exe e 5 flowey_lib_hvlite::build_pipette 0 flowey.exe e 5 flowey_core::pipeline::artifact::publish 1 - flowey.exe e 5 flowey_lib_hvlite::init_cross_build 1 + flowey.exe e 5 flowey_lib_hvlite::init_cross_build 2 shell: bash - name: cargo build tmk_vmm run: |- - flowey.exe e 5 flowey_lib_common::run_cargo_build 3 - flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 3 + flowey.exe e 5 flowey_lib_common::run_cargo_build 4 + flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 6 flowey.exe e 5 flowey_lib_hvlite::build_tmk_vmm 0 flowey.exe e 5 flowey_core::pipeline::artifact::publish 2 - flowey.exe e 5 flowey_lib_hvlite::init_cross_build 6 + flowey.exe e 5 flowey_lib_hvlite::init_cross_build 7 shell: bash - name: cargo build prep_steps run: |- @@ -4630,24 +4658,34 @@ jobs: flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 2 flowey.exe e 5 flowey_lib_hvlite::build_prep_steps 0 flowey.exe e 5 flowey_core::pipeline::artifact::publish 3 - flowey.exe e 5 flowey_lib_hvlite::init_cross_build 3 + flowey.exe e 5 flowey_lib_hvlite::init_cross_build 4 shell: bash - name: cargo build vmgstool run: |- - flowey.exe e 5 flowey_lib_common::run_cargo_build 5 - flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 7 + flowey.exe e 5 flowey_lib_common::run_cargo_build 6 + flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 10 flowey.exe e 5 flowey_lib_hvlite::build_vmgstool 0 flowey.exe e 5 flowey_core::pipeline::artifact::publish 4 - flowey.exe e 5 flowey_lib_hvlite::init_cross_build 2 - flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 4 - flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 5 + flowey.exe e 5 flowey_lib_hvlite::init_cross_build 3 + flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 7 + flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 8 shell: bash - name: cargo build tpm_guest_tests run: |- - flowey.exe e 5 flowey_lib_common::run_cargo_build 4 - flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 6 + flowey.exe e 5 flowey_lib_common::run_cargo_build 5 + flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 9 flowey.exe e 5 flowey_lib_hvlite::build_tpm_guest_tests 0 flowey.exe e 5 flowey_core::pipeline::artifact::publish 5 + flowey.exe e 5 flowey_lib_hvlite::init_cross_build 1 + flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 3 + flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 4 + shell: bash + - name: cargo build test_igvm_agent_rpc_server + run: |- + flowey.exe e 5 flowey_lib_common::run_cargo_build 3 + flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 5 + flowey.exe e 5 flowey_lib_hvlite::build_test_igvm_agent_rpc_server 0 + flowey.exe e 5 flowey_core::pipeline::artifact::publish 6 shell: bash - name: create cargo-nextest cache dir run: |- @@ -4688,7 +4726,7 @@ jobs: run: |- flowey.exe e 5 flowey_lib_common::run_cargo_nextest_archive 0 flowey.exe e 5 flowey_lib_hvlite::build_nextest_vmm_tests 0 - flowey.exe e 5 flowey_core::pipeline::artifact::publish 6 + flowey.exe e 5 flowey_core::pipeline::artifact::publish 7 shell: bash - name: 'validate cache entry: cargo-nextest' run: flowey.exe e 5 flowey_lib_common::cache 3 @@ -4714,6 +4752,12 @@ jobs: name: x64-windows-prep_steps path: ${{ runner.temp }}/publish_artifacts/x64-windows-prep_steps/ include-hidden-files: true + - name: 🌼📦 Publish x64-windows-test_igvm_agent_rpc_server + uses: actions/upload-artifact@v4 + with: + name: x64-windows-test_igvm_agent_rpc_server + path: ${{ runner.temp }}/publish_artifacts/x64-windows-test_igvm_agent_rpc_server/ + include-hidden-files: true - name: 🌼📦 Publish x64-windows-tmk_vmm uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/openvmm-pr-release.yaml b/.github/workflows/openvmm-pr-release.yaml index 950d7877b1..9f51ecf4cd 100644 --- a/.github/workflows/openvmm-pr-release.yaml +++ b/.github/workflows/openvmm-pr-release.yaml @@ -1914,7 +1914,7 @@ jobs: - name: 🌼📦 Download artifacts uses: actions/download-artifact@v4 with: - pattern: '{_internal-flowey-bootstrap-x86_64-windows-uid-12,x64-guest_test_uefi,x64-linux-musl-pipette,x64-linux-musl-tmk_vmm,x64-linux-tpm_guest_tests,x64-openhcl-igvm,x64-tmks,x64-windows-openvmm,x64-windows-pipette,x64-windows-prep_steps,x64-windows-tmk_vmm,x64-windows-tpm_guest_tests,x64-windows-vmgstool,x64-windows-vmm-tests-archive}' + pattern: '{_internal-flowey-bootstrap-x86_64-windows-uid-12,x64-guest_test_uefi,x64-linux-musl-pipette,x64-linux-musl-tmk_vmm,x64-linux-tpm_guest_tests,x64-openhcl-igvm,x64-tmks,x64-windows-openvmm,x64-windows-pipette,x64-windows-prep_steps,x64-windows-test_igvm_agent_rpc_server,x64-windows-tmk_vmm,x64-windows-tpm_guest_tests,x64-windows-vmgstool,x64-windows-vmm-tests-archive}' path: ${{ runner.temp }}/used_artifacts/ - run: echo "${{ runner.temp }}/used_artifacts/_internal-flowey-bootstrap-x86_64-windows-uid-12" >> $GITHUB_PATH shell: bash @@ -1942,6 +1942,7 @@ jobs: echo "${{ runner.temp }}\\used_artifacts\\x64-windows-openvmm" | flowey.exe v 16 'artifact_use_from_x64-windows-openvmm' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-pipette" | flowey.exe v 16 'artifact_use_from_x64-windows-pipette' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-prep_steps" | flowey.exe v 16 'artifact_use_from_x64-windows-prep_steps' --is-raw-string update + echo "${{ runner.temp }}\\used_artifacts\\x64-windows-test_igvm_agent_rpc_server" | flowey.exe v 16 'artifact_use_from_x64-windows-test_igvm_agent_rpc_server' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-tmk_vmm" | flowey.exe v 16 'artifact_use_from_x64-windows-tmk_vmm' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-tpm_guest_tests" | flowey.exe v 16 'artifact_use_from_x64-windows-tpm_guest_tests' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-vmgstool" | flowey.exe v 16 'artifact_use_from_x64-windows-vmgstool' --is-raw-string update @@ -1953,12 +1954,13 @@ jobs: flowey.exe e 16 flowey_core::pipeline::artifact::resolve 6 flowey.exe e 16 flowey_core::pipeline::artifact::resolve 1 flowey.exe e 16 flowey_core::pipeline::artifact::resolve 0 - flowey.exe e 16 flowey_core::pipeline::artifact::resolve 8 + flowey.exe e 16 flowey_core::pipeline::artifact::resolve 9 flowey.exe e 16 flowey_core::pipeline::artifact::resolve 2 flowey.exe e 16 flowey_core::pipeline::artifact::resolve 4 + flowey.exe e 16 flowey_core::pipeline::artifact::resolve 11 flowey.exe e 16 flowey_core::pipeline::artifact::resolve 10 - flowey.exe e 16 flowey_core::pipeline::artifact::resolve 9 flowey.exe e 16 flowey_core::pipeline::artifact::resolve 3 + flowey.exe e 16 flowey_core::pipeline::artifact::resolve 8 flowey.exe e 16 flowey_lib_hvlite::_jobs::consume_and_test_nextest_vmm_tests_archive 0 shell: bash - name: resolve OpenHCL igvm artifact @@ -2115,7 +2117,7 @@ jobs: flowey.exe e 16 flowey_lib_hvlite::git_checkout_openvmm_repo 0 flowey.exe e 16 flowey_lib_hvlite::run_cargo_nextest_run 0 flowey.exe e 16 flowey_lib_hvlite::run_cargo_nextest_run 1 - flowey.exe e 16 flowey_core::pipeline::artifact::resolve 11 + flowey.exe e 16 flowey_core::pipeline::artifact::resolve 12 flowey.exe e 16 flowey_lib_hvlite::test_nextest_vmm_tests_archive 0 shell: bash - name: generate nextest command @@ -2192,7 +2194,7 @@ jobs: - name: 🌼📦 Download artifacts uses: actions/download-artifact@v4 with: - pattern: '{_internal-flowey-bootstrap-x86_64-windows-uid-12,x64-guest_test_uefi,x64-linux-musl-pipette,x64-linux-musl-tmk_vmm,x64-linux-tpm_guest_tests,x64-openhcl-igvm,x64-tmks,x64-windows-openvmm,x64-windows-pipette,x64-windows-prep_steps,x64-windows-tmk_vmm,x64-windows-tpm_guest_tests,x64-windows-vmgstool,x64-windows-vmm-tests-archive}' + pattern: '{_internal-flowey-bootstrap-x86_64-windows-uid-12,x64-guest_test_uefi,x64-linux-musl-pipette,x64-linux-musl-tmk_vmm,x64-linux-tpm_guest_tests,x64-openhcl-igvm,x64-tmks,x64-windows-openvmm,x64-windows-pipette,x64-windows-prep_steps,x64-windows-test_igvm_agent_rpc_server,x64-windows-tmk_vmm,x64-windows-tpm_guest_tests,x64-windows-vmgstool,x64-windows-vmm-tests-archive}' path: ${{ runner.temp }}/used_artifacts/ - run: echo "${{ runner.temp }}/used_artifacts/_internal-flowey-bootstrap-x86_64-windows-uid-12" >> $GITHUB_PATH shell: bash @@ -2220,6 +2222,7 @@ jobs: echo "${{ runner.temp }}\\used_artifacts\\x64-windows-openvmm" | flowey.exe v 17 'artifact_use_from_x64-windows-openvmm' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-pipette" | flowey.exe v 17 'artifact_use_from_x64-windows-pipette' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-prep_steps" | flowey.exe v 17 'artifact_use_from_x64-windows-prep_steps' --is-raw-string update + echo "${{ runner.temp }}\\used_artifacts\\x64-windows-test_igvm_agent_rpc_server" | flowey.exe v 17 'artifact_use_from_x64-windows-test_igvm_agent_rpc_server' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-tmk_vmm" | flowey.exe v 17 'artifact_use_from_x64-windows-tmk_vmm' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-tpm_guest_tests" | flowey.exe v 17 'artifact_use_from_x64-windows-tpm_guest_tests' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-vmgstool" | flowey.exe v 17 'artifact_use_from_x64-windows-vmgstool' --is-raw-string update @@ -2231,12 +2234,13 @@ jobs: flowey.exe e 17 flowey_core::pipeline::artifact::resolve 6 flowey.exe e 17 flowey_core::pipeline::artifact::resolve 1 flowey.exe e 17 flowey_core::pipeline::artifact::resolve 0 - flowey.exe e 17 flowey_core::pipeline::artifact::resolve 8 + flowey.exe e 17 flowey_core::pipeline::artifact::resolve 9 flowey.exe e 17 flowey_core::pipeline::artifact::resolve 2 flowey.exe e 17 flowey_core::pipeline::artifact::resolve 4 + flowey.exe e 17 flowey_core::pipeline::artifact::resolve 11 flowey.exe e 17 flowey_core::pipeline::artifact::resolve 10 - flowey.exe e 17 flowey_core::pipeline::artifact::resolve 9 flowey.exe e 17 flowey_core::pipeline::artifact::resolve 3 + flowey.exe e 17 flowey_core::pipeline::artifact::resolve 8 flowey.exe e 17 flowey_lib_hvlite::_jobs::consume_and_test_nextest_vmm_tests_archive 0 shell: bash - name: resolve OpenHCL igvm artifact @@ -2393,7 +2397,7 @@ jobs: flowey.exe e 17 flowey_lib_hvlite::git_checkout_openvmm_repo 0 flowey.exe e 17 flowey_lib_hvlite::run_cargo_nextest_run 0 flowey.exe e 17 flowey_lib_hvlite::run_cargo_nextest_run 1 - flowey.exe e 17 flowey_core::pipeline::artifact::resolve 11 + flowey.exe e 17 flowey_core::pipeline::artifact::resolve 12 flowey.exe e 17 flowey_lib_hvlite::test_nextest_vmm_tests_archive 0 shell: bash - name: generate nextest command @@ -2473,7 +2477,7 @@ jobs: - name: 🌼📦 Download artifacts uses: actions/download-artifact@v4 with: - pattern: '{_internal-flowey-bootstrap-x86_64-windows-uid-12,x64-guest_test_uefi,x64-linux-musl-pipette,x64-linux-musl-tmk_vmm,x64-linux-tpm_guest_tests,x64-openhcl-igvm,x64-tmks,x64-windows-openvmm,x64-windows-pipette,x64-windows-prep_steps,x64-windows-tmk_vmm,x64-windows-tpm_guest_tests,x64-windows-vmgstool,x64-windows-vmm-tests-archive}' + pattern: '{_internal-flowey-bootstrap-x86_64-windows-uid-12,x64-guest_test_uefi,x64-linux-musl-pipette,x64-linux-musl-tmk_vmm,x64-linux-tpm_guest_tests,x64-openhcl-igvm,x64-tmks,x64-windows-openvmm,x64-windows-pipette,x64-windows-prep_steps,x64-windows-test_igvm_agent_rpc_server,x64-windows-tmk_vmm,x64-windows-tpm_guest_tests,x64-windows-vmgstool,x64-windows-vmm-tests-archive}' path: ${{ runner.temp }}/used_artifacts/ - run: echo "${{ runner.temp }}/used_artifacts/_internal-flowey-bootstrap-x86_64-windows-uid-12" >> $GITHUB_PATH shell: bash @@ -2501,6 +2505,7 @@ jobs: echo "${{ runner.temp }}\\used_artifacts\\x64-windows-openvmm" | flowey.exe v 18 'artifact_use_from_x64-windows-openvmm' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-pipette" | flowey.exe v 18 'artifact_use_from_x64-windows-pipette' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-prep_steps" | flowey.exe v 18 'artifact_use_from_x64-windows-prep_steps' --is-raw-string update + echo "${{ runner.temp }}\\used_artifacts\\x64-windows-test_igvm_agent_rpc_server" | flowey.exe v 18 'artifact_use_from_x64-windows-test_igvm_agent_rpc_server' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-tmk_vmm" | flowey.exe v 18 'artifact_use_from_x64-windows-tmk_vmm' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-tpm_guest_tests" | flowey.exe v 18 'artifact_use_from_x64-windows-tpm_guest_tests' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-vmgstool" | flowey.exe v 18 'artifact_use_from_x64-windows-vmgstool' --is-raw-string update @@ -2512,12 +2517,13 @@ jobs: flowey.exe e 18 flowey_core::pipeline::artifact::resolve 6 flowey.exe e 18 flowey_core::pipeline::artifact::resolve 1 flowey.exe e 18 flowey_core::pipeline::artifact::resolve 0 - flowey.exe e 18 flowey_core::pipeline::artifact::resolve 8 + flowey.exe e 18 flowey_core::pipeline::artifact::resolve 9 flowey.exe e 18 flowey_core::pipeline::artifact::resolve 2 flowey.exe e 18 flowey_core::pipeline::artifact::resolve 4 + flowey.exe e 18 flowey_core::pipeline::artifact::resolve 11 flowey.exe e 18 flowey_core::pipeline::artifact::resolve 10 - flowey.exe e 18 flowey_core::pipeline::artifact::resolve 9 flowey.exe e 18 flowey_core::pipeline::artifact::resolve 3 + flowey.exe e 18 flowey_core::pipeline::artifact::resolve 8 flowey.exe e 18 flowey_lib_hvlite::_jobs::consume_and_test_nextest_vmm_tests_archive 0 shell: bash - name: resolve OpenHCL igvm artifact @@ -2674,7 +2680,7 @@ jobs: flowey.exe e 18 flowey_lib_hvlite::git_checkout_openvmm_repo 0 flowey.exe e 18 flowey_lib_hvlite::run_cargo_nextest_run 0 flowey.exe e 18 flowey_lib_hvlite::run_cargo_nextest_run 1 - flowey.exe e 18 flowey_core::pipeline::artifact::resolve 11 + flowey.exe e 18 flowey_core::pipeline::artifact::resolve 12 flowey.exe e 18 flowey_lib_hvlite::test_nextest_vmm_tests_archive 0 shell: bash - name: generate nextest command @@ -2751,7 +2757,7 @@ jobs: - name: 🌼📦 Download artifacts uses: actions/download-artifact@v4 with: - pattern: '{_internal-flowey-bootstrap-x86_64-windows-uid-12,x64-guest_test_uefi,x64-linux-musl-pipette,x64-linux-musl-tmk_vmm,x64-linux-tpm_guest_tests,x64-openhcl-igvm,x64-tmks,x64-windows-openvmm,x64-windows-pipette,x64-windows-prep_steps,x64-windows-tmk_vmm,x64-windows-tpm_guest_tests,x64-windows-vmgstool,x64-windows-vmm-tests-archive}' + pattern: '{_internal-flowey-bootstrap-x86_64-windows-uid-12,x64-guest_test_uefi,x64-linux-musl-pipette,x64-linux-musl-tmk_vmm,x64-linux-tpm_guest_tests,x64-openhcl-igvm,x64-tmks,x64-windows-openvmm,x64-windows-pipette,x64-windows-prep_steps,x64-windows-test_igvm_agent_rpc_server,x64-windows-tmk_vmm,x64-windows-tpm_guest_tests,x64-windows-vmgstool,x64-windows-vmm-tests-archive}' path: ${{ runner.temp }}/used_artifacts/ - run: echo "${{ runner.temp }}/used_artifacts/_internal-flowey-bootstrap-x86_64-windows-uid-12" >> $GITHUB_PATH shell: bash @@ -2779,6 +2785,7 @@ jobs: echo "${{ runner.temp }}\\used_artifacts\\x64-windows-openvmm" | flowey.exe v 19 'artifact_use_from_x64-windows-openvmm' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-pipette" | flowey.exe v 19 'artifact_use_from_x64-windows-pipette' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-prep_steps" | flowey.exe v 19 'artifact_use_from_x64-windows-prep_steps' --is-raw-string update + echo "${{ runner.temp }}\\used_artifacts\\x64-windows-test_igvm_agent_rpc_server" | flowey.exe v 19 'artifact_use_from_x64-windows-test_igvm_agent_rpc_server' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-tmk_vmm" | flowey.exe v 19 'artifact_use_from_x64-windows-tmk_vmm' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-tpm_guest_tests" | flowey.exe v 19 'artifact_use_from_x64-windows-tpm_guest_tests' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-vmgstool" | flowey.exe v 19 'artifact_use_from_x64-windows-vmgstool' --is-raw-string update @@ -2790,12 +2797,13 @@ jobs: flowey.exe e 19 flowey_core::pipeline::artifact::resolve 6 flowey.exe e 19 flowey_core::pipeline::artifact::resolve 1 flowey.exe e 19 flowey_core::pipeline::artifact::resolve 0 - flowey.exe e 19 flowey_core::pipeline::artifact::resolve 8 + flowey.exe e 19 flowey_core::pipeline::artifact::resolve 9 flowey.exe e 19 flowey_core::pipeline::artifact::resolve 2 flowey.exe e 19 flowey_core::pipeline::artifact::resolve 4 + flowey.exe e 19 flowey_core::pipeline::artifact::resolve 11 flowey.exe e 19 flowey_core::pipeline::artifact::resolve 10 - flowey.exe e 19 flowey_core::pipeline::artifact::resolve 9 flowey.exe e 19 flowey_core::pipeline::artifact::resolve 3 + flowey.exe e 19 flowey_core::pipeline::artifact::resolve 8 flowey.exe e 19 flowey_lib_hvlite::_jobs::consume_and_test_nextest_vmm_tests_archive 0 shell: bash - name: resolve OpenHCL igvm artifact @@ -2952,7 +2960,7 @@ jobs: flowey.exe e 19 flowey_lib_hvlite::git_checkout_openvmm_repo 0 flowey.exe e 19 flowey_lib_hvlite::run_cargo_nextest_run 0 flowey.exe e 19 flowey_lib_hvlite::run_cargo_nextest_run 1 - flowey.exe e 19 flowey_core::pipeline::artifact::resolve 11 + flowey.exe e 19 flowey_core::pipeline::artifact::resolve 12 flowey.exe e 19 flowey_lib_hvlite::test_nextest_vmm_tests_archive 0 shell: bash - name: generate nextest command @@ -3275,12 +3283,12 @@ jobs: shell: bash - name: creating new test content dir run: |- - flowey e 20 flowey_core::pipeline::artifact::resolve 3 - flowey e 20 flowey_core::pipeline::artifact::resolve 6 flowey e 20 flowey_core::pipeline::artifact::resolve 1 flowey e 20 flowey_core::pipeline::artifact::resolve 0 flowey e 20 flowey_core::pipeline::artifact::resolve 2 flowey e 20 flowey_core::pipeline::artifact::resolve 5 + flowey e 20 flowey_core::pipeline::artifact::resolve 3 + flowey e 20 flowey_core::pipeline::artifact::resolve 6 flowey e 20 flowey_lib_hvlite::_jobs::consume_and_test_nextest_vmm_tests_archive 0 shell: bash - name: create azcopy cache dir @@ -3600,6 +3608,7 @@ jobs: shell: bash - name: creating new test content dir run: |- + flowey.exe e 21 flowey_core::pipeline::artifact::resolve 4 flowey.exe e 21 flowey_core::pipeline::artifact::resolve 5 flowey.exe e 21 flowey_core::pipeline::artifact::resolve 1 flowey.exe e 21 flowey_core::pipeline::artifact::resolve 0 @@ -3607,7 +3616,6 @@ jobs: flowey.exe e 21 flowey_core::pipeline::artifact::resolve 2 flowey.exe e 21 flowey_core::pipeline::artifact::resolve 3 flowey.exe e 21 flowey_core::pipeline::artifact::resolve 7 - flowey.exe e 21 flowey_core::pipeline::artifact::resolve 4 flowey.exe e 21 flowey_lib_hvlite::_jobs::consume_and_test_nextest_vmm_tests_archive 0 shell: bash - name: resolve OpenHCL igvm artifact @@ -4014,6 +4022,8 @@ jobs: echo "${{ runner.temp }}\\publish_artifacts\\aarch64-windows-pipette" | flowey.exe v 3 'artifact_publish_from_aarch64-windows-pipette' --is-raw-string update mkdir -p "$AgentTempDirNormal/publish_artifacts/aarch64-windows-prep_steps" echo "${{ runner.temp }}\\publish_artifacts\\aarch64-windows-prep_steps" | flowey.exe v 3 'artifact_publish_from_aarch64-windows-prep_steps' --is-raw-string update + mkdir -p "$AgentTempDirNormal/publish_artifacts/aarch64-windows-test_igvm_agent_rpc_server" + echo "${{ runner.temp }}\\publish_artifacts\\aarch64-windows-test_igvm_agent_rpc_server" | flowey.exe v 3 'artifact_publish_from_aarch64-windows-test_igvm_agent_rpc_server' --is-raw-string update mkdir -p "$AgentTempDirNormal/publish_artifacts/aarch64-windows-tmk_vmm" echo "${{ runner.temp }}\\publish_artifacts\\aarch64-windows-tmk_vmm" | flowey.exe v 3 'artifact_publish_from_aarch64-windows-tmk_vmm' --is-raw-string update mkdir -p "$AgentTempDirNormal/publish_artifacts/aarch64-windows-tpm_guest_tests" @@ -4090,32 +4100,42 @@ jobs: - name: symlink protoc run: |- flowey.exe e 3 flowey_lib_hvlite::init_openvmm_magicpath_protoc 0 - flowey.exe e 3 flowey_lib_hvlite::init_cross_build 6 + flowey.exe e 3 flowey_lib_hvlite::init_cross_build 7 shell: bash - name: cargo build prep_steps run: |- flowey.exe e 3 flowey_lib_common::run_cargo_build 2 flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 2 flowey.exe e 3 flowey_lib_hvlite::build_prep_steps 0 - flowey.exe e 3 flowey_core::pipeline::artifact::publish 4 - flowey.exe e 3 flowey_lib_hvlite::init_cross_build 3 + flowey.exe e 3 flowey_core::pipeline::artifact::publish 5 + flowey.exe e 3 flowey_lib_hvlite::init_cross_build 4 shell: bash - name: cargo build vmgstool run: |- - flowey.exe e 3 flowey_lib_common::run_cargo_build 5 - flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 7 + flowey.exe e 3 flowey_lib_common::run_cargo_build 6 + flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 10 flowey.exe e 3 flowey_lib_hvlite::build_vmgstool 0 - flowey.exe e 3 flowey_core::pipeline::artifact::publish 5 - flowey.exe e 3 flowey_lib_hvlite::init_cross_build 2 - flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 4 - flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 5 + flowey.exe e 3 flowey_core::pipeline::artifact::publish 6 + flowey.exe e 3 flowey_lib_hvlite::init_cross_build 3 + flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 7 + flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 8 shell: bash - name: cargo build tpm_guest_tests run: |- - flowey.exe e 3 flowey_lib_common::run_cargo_build 4 - flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 6 + flowey.exe e 3 flowey_lib_common::run_cargo_build 5 + flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 9 flowey.exe e 3 flowey_lib_hvlite::build_tpm_guest_tests 0 - flowey.exe e 3 flowey_core::pipeline::artifact::publish 6 + flowey.exe e 3 flowey_core::pipeline::artifact::publish 7 + flowey.exe e 3 flowey_lib_hvlite::init_cross_build 1 + flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 3 + flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 4 + shell: bash + - name: cargo build test_igvm_agent_rpc_server + run: |- + flowey.exe e 3 flowey_lib_common::run_cargo_build 3 + flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 5 + flowey.exe e 3 flowey_lib_hvlite::build_test_igvm_agent_rpc_server 0 + flowey.exe e 3 flowey_core::pipeline::artifact::publish 0 shell: bash - name: create cargo-nextest cache dir run: |- @@ -4156,31 +4176,31 @@ jobs: run: |- flowey.exe e 3 flowey_lib_common::run_cargo_nextest_archive 0 flowey.exe e 3 flowey_lib_hvlite::build_nextest_vmm_tests 0 - flowey.exe e 3 flowey_core::pipeline::artifact::publish 0 - flowey.exe e 3 flowey_lib_hvlite::init_cross_build 4 + flowey.exe e 3 flowey_core::pipeline::artifact::publish 1 + flowey.exe e 3 flowey_lib_hvlite::init_cross_build 5 shell: bash - name: cargo build openvmm run: |- flowey.exe e 3 flowey_lib_common::run_cargo_build 0 flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 0 flowey.exe e 3 flowey_lib_hvlite::build_openvmm 0 - flowey.exe e 3 flowey_core::pipeline::artifact::publish 1 - flowey.exe e 3 flowey_lib_hvlite::init_cross_build 5 + flowey.exe e 3 flowey_core::pipeline::artifact::publish 2 + flowey.exe e 3 flowey_lib_hvlite::init_cross_build 6 shell: bash - name: cargo build pipette run: |- flowey.exe e 3 flowey_lib_common::run_cargo_build 1 flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 1 flowey.exe e 3 flowey_lib_hvlite::build_pipette 0 - flowey.exe e 3 flowey_core::pipeline::artifact::publish 2 - flowey.exe e 3 flowey_lib_hvlite::init_cross_build 1 + flowey.exe e 3 flowey_core::pipeline::artifact::publish 3 + flowey.exe e 3 flowey_lib_hvlite::init_cross_build 2 shell: bash - name: cargo build tmk_vmm run: |- - flowey.exe e 3 flowey_lib_common::run_cargo_build 3 - flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 3 + flowey.exe e 3 flowey_lib_common::run_cargo_build 4 + flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 6 flowey.exe e 3 flowey_lib_hvlite::build_tmk_vmm 0 - flowey.exe e 3 flowey_core::pipeline::artifact::publish 3 + flowey.exe e 3 flowey_core::pipeline::artifact::publish 4 shell: bash - name: 'validate cache entry: cargo-nextest' run: flowey.exe e 3 flowey_lib_common::cache 3 @@ -4206,6 +4226,12 @@ jobs: name: aarch64-windows-prep_steps path: ${{ runner.temp }}/publish_artifacts/aarch64-windows-prep_steps/ include-hidden-files: true + - name: 🌼📦 Publish aarch64-windows-test_igvm_agent_rpc_server + uses: actions/upload-artifact@v4 + with: + name: aarch64-windows-test_igvm_agent_rpc_server + path: ${{ runner.temp }}/publish_artifacts/aarch64-windows-test_igvm_agent_rpc_server/ + include-hidden-files: true - name: 🌼📦 Publish aarch64-windows-tmk_vmm uses: actions/upload-artifact@v4 with: @@ -4531,6 +4557,8 @@ jobs: echo "${{ runner.temp }}\\publish_artifacts\\x64-windows-pipette" | flowey.exe v 5 'artifact_publish_from_x64-windows-pipette' --is-raw-string update mkdir -p "$AgentTempDirNormal/publish_artifacts/x64-windows-prep_steps" echo "${{ runner.temp }}\\publish_artifacts\\x64-windows-prep_steps" | flowey.exe v 5 'artifact_publish_from_x64-windows-prep_steps' --is-raw-string update + mkdir -p "$AgentTempDirNormal/publish_artifacts/x64-windows-test_igvm_agent_rpc_server" + echo "${{ runner.temp }}\\publish_artifacts\\x64-windows-test_igvm_agent_rpc_server" | flowey.exe v 5 'artifact_publish_from_x64-windows-test_igvm_agent_rpc_server' --is-raw-string update mkdir -p "$AgentTempDirNormal/publish_artifacts/x64-windows-tmk_vmm" echo "${{ runner.temp }}\\publish_artifacts\\x64-windows-tmk_vmm" | flowey.exe v 5 'artifact_publish_from_x64-windows-tmk_vmm' --is-raw-string update mkdir -p "$AgentTempDirNormal/publish_artifacts/x64-windows-tpm_guest_tests" @@ -4607,7 +4635,7 @@ jobs: - name: symlink protoc run: |- flowey.exe e 5 flowey_lib_hvlite::init_openvmm_magicpath_protoc 0 - flowey.exe e 5 flowey_lib_hvlite::init_cross_build 4 + flowey.exe e 5 flowey_lib_hvlite::init_cross_build 5 shell: bash - name: cargo build openvmm run: |- @@ -4615,7 +4643,7 @@ jobs: flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 0 flowey.exe e 5 flowey_lib_hvlite::build_openvmm 0 flowey.exe e 5 flowey_core::pipeline::artifact::publish 0 - flowey.exe e 5 flowey_lib_hvlite::init_cross_build 5 + flowey.exe e 5 flowey_lib_hvlite::init_cross_build 6 shell: bash - name: cargo build pipette run: |- @@ -4623,15 +4651,15 @@ jobs: flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 1 flowey.exe e 5 flowey_lib_hvlite::build_pipette 0 flowey.exe e 5 flowey_core::pipeline::artifact::publish 1 - flowey.exe e 5 flowey_lib_hvlite::init_cross_build 1 + flowey.exe e 5 flowey_lib_hvlite::init_cross_build 2 shell: bash - name: cargo build tmk_vmm run: |- - flowey.exe e 5 flowey_lib_common::run_cargo_build 3 - flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 3 + flowey.exe e 5 flowey_lib_common::run_cargo_build 4 + flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 6 flowey.exe e 5 flowey_lib_hvlite::build_tmk_vmm 0 flowey.exe e 5 flowey_core::pipeline::artifact::publish 2 - flowey.exe e 5 flowey_lib_hvlite::init_cross_build 6 + flowey.exe e 5 flowey_lib_hvlite::init_cross_build 7 shell: bash - name: cargo build prep_steps run: |- @@ -4639,24 +4667,34 @@ jobs: flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 2 flowey.exe e 5 flowey_lib_hvlite::build_prep_steps 0 flowey.exe e 5 flowey_core::pipeline::artifact::publish 3 - flowey.exe e 5 flowey_lib_hvlite::init_cross_build 3 + flowey.exe e 5 flowey_lib_hvlite::init_cross_build 4 shell: bash - name: cargo build vmgstool run: |- - flowey.exe e 5 flowey_lib_common::run_cargo_build 5 - flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 7 + flowey.exe e 5 flowey_lib_common::run_cargo_build 6 + flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 10 flowey.exe e 5 flowey_lib_hvlite::build_vmgstool 0 flowey.exe e 5 flowey_core::pipeline::artifact::publish 4 - flowey.exe e 5 flowey_lib_hvlite::init_cross_build 2 - flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 4 - flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 5 + flowey.exe e 5 flowey_lib_hvlite::init_cross_build 3 + flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 7 + flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 8 shell: bash - name: cargo build tpm_guest_tests run: |- - flowey.exe e 5 flowey_lib_common::run_cargo_build 4 - flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 6 + flowey.exe e 5 flowey_lib_common::run_cargo_build 5 + flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 9 flowey.exe e 5 flowey_lib_hvlite::build_tpm_guest_tests 0 flowey.exe e 5 flowey_core::pipeline::artifact::publish 5 + flowey.exe e 5 flowey_lib_hvlite::init_cross_build 1 + flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 3 + flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 4 + shell: bash + - name: cargo build test_igvm_agent_rpc_server + run: |- + flowey.exe e 5 flowey_lib_common::run_cargo_build 3 + flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 5 + flowey.exe e 5 flowey_lib_hvlite::build_test_igvm_agent_rpc_server 0 + flowey.exe e 5 flowey_core::pipeline::artifact::publish 6 shell: bash - name: create cargo-nextest cache dir run: |- @@ -4697,7 +4735,7 @@ jobs: run: |- flowey.exe e 5 flowey_lib_common::run_cargo_nextest_archive 0 flowey.exe e 5 flowey_lib_hvlite::build_nextest_vmm_tests 0 - flowey.exe e 5 flowey_core::pipeline::artifact::publish 6 + flowey.exe e 5 flowey_core::pipeline::artifact::publish 7 shell: bash - name: 'validate cache entry: cargo-nextest' run: flowey.exe e 5 flowey_lib_common::cache 3 @@ -4723,6 +4761,12 @@ jobs: name: x64-windows-prep_steps path: ${{ runner.temp }}/publish_artifacts/x64-windows-prep_steps/ include-hidden-files: true + - name: 🌼📦 Publish x64-windows-test_igvm_agent_rpc_server + uses: actions/upload-artifact@v4 + with: + name: x64-windows-test_igvm_agent_rpc_server + path: ${{ runner.temp }}/publish_artifacts/x64-windows-test_igvm_agent_rpc_server/ + include-hidden-files: true - name: 🌼📦 Publish x64-windows-tmk_vmm uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/openvmm-pr.yaml b/.github/workflows/openvmm-pr.yaml index 5c7fec2e06..b45b60aced 100644 --- a/.github/workflows/openvmm-pr.yaml +++ b/.github/workflows/openvmm-pr.yaml @@ -2568,7 +2568,7 @@ jobs: - name: 🌼📦 Download artifacts uses: actions/download-artifact@v4 with: - pattern: '{_internal-flowey-bootstrap-x86_64-windows-uid-14,x64-guest_test_uefi,x64-linux-musl-pipette,x64-linux-musl-tmk_vmm,x64-linux-tpm_guest_tests,x64-openhcl-igvm,x64-tmks,x64-windows-openvmm,x64-windows-pipette,x64-windows-prep_steps,x64-windows-tmk_vmm,x64-windows-tpm_guest_tests,x64-windows-vmgstool,x64-windows-vmm-tests-archive}' + pattern: '{_internal-flowey-bootstrap-x86_64-windows-uid-14,x64-guest_test_uefi,x64-linux-musl-pipette,x64-linux-musl-tmk_vmm,x64-linux-tpm_guest_tests,x64-openhcl-igvm,x64-tmks,x64-windows-openvmm,x64-windows-pipette,x64-windows-prep_steps,x64-windows-test_igvm_agent_rpc_server,x64-windows-tmk_vmm,x64-windows-tpm_guest_tests,x64-windows-vmgstool,x64-windows-vmm-tests-archive}' path: ${{ runner.temp }}/used_artifacts/ - run: echo "${{ runner.temp }}/used_artifacts/_internal-flowey-bootstrap-x86_64-windows-uid-14" >> $GITHUB_PATH shell: bash @@ -2596,6 +2596,7 @@ jobs: echo "${{ runner.temp }}\\used_artifacts\\x64-windows-openvmm" | flowey.exe v 18 'artifact_use_from_x64-windows-openvmm' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-pipette" | flowey.exe v 18 'artifact_use_from_x64-windows-pipette' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-prep_steps" | flowey.exe v 18 'artifact_use_from_x64-windows-prep_steps' --is-raw-string update + echo "${{ runner.temp }}\\used_artifacts\\x64-windows-test_igvm_agent_rpc_server" | flowey.exe v 18 'artifact_use_from_x64-windows-test_igvm_agent_rpc_server' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-tmk_vmm" | flowey.exe v 18 'artifact_use_from_x64-windows-tmk_vmm' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-tpm_guest_tests" | flowey.exe v 18 'artifact_use_from_x64-windows-tpm_guest_tests' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-vmgstool" | flowey.exe v 18 'artifact_use_from_x64-windows-vmgstool' --is-raw-string update @@ -2607,12 +2608,13 @@ jobs: flowey.exe e 18 flowey_core::pipeline::artifact::resolve 6 flowey.exe e 18 flowey_core::pipeline::artifact::resolve 1 flowey.exe e 18 flowey_core::pipeline::artifact::resolve 0 - flowey.exe e 18 flowey_core::pipeline::artifact::resolve 8 + flowey.exe e 18 flowey_core::pipeline::artifact::resolve 9 flowey.exe e 18 flowey_core::pipeline::artifact::resolve 2 flowey.exe e 18 flowey_core::pipeline::artifact::resolve 4 + flowey.exe e 18 flowey_core::pipeline::artifact::resolve 11 flowey.exe e 18 flowey_core::pipeline::artifact::resolve 10 - flowey.exe e 18 flowey_core::pipeline::artifact::resolve 9 flowey.exe e 18 flowey_core::pipeline::artifact::resolve 3 + flowey.exe e 18 flowey_core::pipeline::artifact::resolve 8 flowey.exe e 18 flowey_lib_hvlite::_jobs::consume_and_test_nextest_vmm_tests_archive 0 shell: bash - name: resolve OpenHCL igvm artifact @@ -2769,7 +2771,7 @@ jobs: flowey.exe e 18 flowey_lib_hvlite::git_checkout_openvmm_repo 0 flowey.exe e 18 flowey_lib_hvlite::run_cargo_nextest_run 0 flowey.exe e 18 flowey_lib_hvlite::run_cargo_nextest_run 1 - flowey.exe e 18 flowey_core::pipeline::artifact::resolve 11 + flowey.exe e 18 flowey_core::pipeline::artifact::resolve 12 flowey.exe e 18 flowey_lib_hvlite::test_nextest_vmm_tests_archive 0 shell: bash - name: generate nextest command @@ -2846,7 +2848,7 @@ jobs: - name: 🌼📦 Download artifacts uses: actions/download-artifact@v4 with: - pattern: '{_internal-flowey-bootstrap-x86_64-windows-uid-14,x64-guest_test_uefi,x64-linux-musl-pipette,x64-linux-musl-tmk_vmm,x64-linux-tpm_guest_tests,x64-openhcl-igvm,x64-tmks,x64-windows-openvmm,x64-windows-pipette,x64-windows-prep_steps,x64-windows-tmk_vmm,x64-windows-tpm_guest_tests,x64-windows-vmgstool,x64-windows-vmm-tests-archive}' + pattern: '{_internal-flowey-bootstrap-x86_64-windows-uid-14,x64-guest_test_uefi,x64-linux-musl-pipette,x64-linux-musl-tmk_vmm,x64-linux-tpm_guest_tests,x64-openhcl-igvm,x64-tmks,x64-windows-openvmm,x64-windows-pipette,x64-windows-prep_steps,x64-windows-test_igvm_agent_rpc_server,x64-windows-tmk_vmm,x64-windows-tpm_guest_tests,x64-windows-vmgstool,x64-windows-vmm-tests-archive}' path: ${{ runner.temp }}/used_artifacts/ - run: echo "${{ runner.temp }}/used_artifacts/_internal-flowey-bootstrap-x86_64-windows-uid-14" >> $GITHUB_PATH shell: bash @@ -2874,6 +2876,7 @@ jobs: echo "${{ runner.temp }}\\used_artifacts\\x64-windows-openvmm" | flowey.exe v 19 'artifact_use_from_x64-windows-openvmm' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-pipette" | flowey.exe v 19 'artifact_use_from_x64-windows-pipette' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-prep_steps" | flowey.exe v 19 'artifact_use_from_x64-windows-prep_steps' --is-raw-string update + echo "${{ runner.temp }}\\used_artifacts\\x64-windows-test_igvm_agent_rpc_server" | flowey.exe v 19 'artifact_use_from_x64-windows-test_igvm_agent_rpc_server' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-tmk_vmm" | flowey.exe v 19 'artifact_use_from_x64-windows-tmk_vmm' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-tpm_guest_tests" | flowey.exe v 19 'artifact_use_from_x64-windows-tpm_guest_tests' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-vmgstool" | flowey.exe v 19 'artifact_use_from_x64-windows-vmgstool' --is-raw-string update @@ -2885,12 +2888,13 @@ jobs: flowey.exe e 19 flowey_core::pipeline::artifact::resolve 6 flowey.exe e 19 flowey_core::pipeline::artifact::resolve 1 flowey.exe e 19 flowey_core::pipeline::artifact::resolve 0 - flowey.exe e 19 flowey_core::pipeline::artifact::resolve 8 + flowey.exe e 19 flowey_core::pipeline::artifact::resolve 9 flowey.exe e 19 flowey_core::pipeline::artifact::resolve 2 flowey.exe e 19 flowey_core::pipeline::artifact::resolve 4 + flowey.exe e 19 flowey_core::pipeline::artifact::resolve 11 flowey.exe e 19 flowey_core::pipeline::artifact::resolve 10 - flowey.exe e 19 flowey_core::pipeline::artifact::resolve 9 flowey.exe e 19 flowey_core::pipeline::artifact::resolve 3 + flowey.exe e 19 flowey_core::pipeline::artifact::resolve 8 flowey.exe e 19 flowey_lib_hvlite::_jobs::consume_and_test_nextest_vmm_tests_archive 0 shell: bash - name: resolve OpenHCL igvm artifact @@ -3047,7 +3051,7 @@ jobs: flowey.exe e 19 flowey_lib_hvlite::git_checkout_openvmm_repo 0 flowey.exe e 19 flowey_lib_hvlite::run_cargo_nextest_run 0 flowey.exe e 19 flowey_lib_hvlite::run_cargo_nextest_run 1 - flowey.exe e 19 flowey_core::pipeline::artifact::resolve 11 + flowey.exe e 19 flowey_core::pipeline::artifact::resolve 12 flowey.exe e 19 flowey_lib_hvlite::test_nextest_vmm_tests_archive 0 shell: bash - name: generate nextest command @@ -3341,7 +3345,7 @@ jobs: - name: 🌼📦 Download artifacts uses: actions/download-artifact@v4 with: - pattern: '{_internal-flowey-bootstrap-x86_64-windows-uid-14,x64-guest_test_uefi,x64-linux-musl-pipette,x64-linux-musl-tmk_vmm,x64-linux-tpm_guest_tests,x64-openhcl-igvm,x64-tmks,x64-windows-openvmm,x64-windows-pipette,x64-windows-prep_steps,x64-windows-tmk_vmm,x64-windows-tpm_guest_tests,x64-windows-vmgstool,x64-windows-vmm-tests-archive}' + pattern: '{_internal-flowey-bootstrap-x86_64-windows-uid-14,x64-guest_test_uefi,x64-linux-musl-pipette,x64-linux-musl-tmk_vmm,x64-linux-tpm_guest_tests,x64-openhcl-igvm,x64-tmks,x64-windows-openvmm,x64-windows-pipette,x64-windows-prep_steps,x64-windows-test_igvm_agent_rpc_server,x64-windows-tmk_vmm,x64-windows-tpm_guest_tests,x64-windows-vmgstool,x64-windows-vmm-tests-archive}' path: ${{ runner.temp }}/used_artifacts/ - run: echo "${{ runner.temp }}/used_artifacts/_internal-flowey-bootstrap-x86_64-windows-uid-14" >> $GITHUB_PATH shell: bash @@ -3369,6 +3373,7 @@ jobs: echo "${{ runner.temp }}\\used_artifacts\\x64-windows-openvmm" | flowey.exe v 20 'artifact_use_from_x64-windows-openvmm' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-pipette" | flowey.exe v 20 'artifact_use_from_x64-windows-pipette' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-prep_steps" | flowey.exe v 20 'artifact_use_from_x64-windows-prep_steps' --is-raw-string update + echo "${{ runner.temp }}\\used_artifacts\\x64-windows-test_igvm_agent_rpc_server" | flowey.exe v 20 'artifact_use_from_x64-windows-test_igvm_agent_rpc_server' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-tmk_vmm" | flowey.exe v 20 'artifact_use_from_x64-windows-tmk_vmm' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-tpm_guest_tests" | flowey.exe v 20 'artifact_use_from_x64-windows-tpm_guest_tests' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-vmgstool" | flowey.exe v 20 'artifact_use_from_x64-windows-vmgstool' --is-raw-string update @@ -3380,12 +3385,13 @@ jobs: flowey.exe e 20 flowey_core::pipeline::artifact::resolve 6 flowey.exe e 20 flowey_core::pipeline::artifact::resolve 1 flowey.exe e 20 flowey_core::pipeline::artifact::resolve 0 - flowey.exe e 20 flowey_core::pipeline::artifact::resolve 8 + flowey.exe e 20 flowey_core::pipeline::artifact::resolve 9 flowey.exe e 20 flowey_core::pipeline::artifact::resolve 2 flowey.exe e 20 flowey_core::pipeline::artifact::resolve 4 + flowey.exe e 20 flowey_core::pipeline::artifact::resolve 11 flowey.exe e 20 flowey_core::pipeline::artifact::resolve 10 - flowey.exe e 20 flowey_core::pipeline::artifact::resolve 9 flowey.exe e 20 flowey_core::pipeline::artifact::resolve 3 + flowey.exe e 20 flowey_core::pipeline::artifact::resolve 8 flowey.exe e 20 flowey_lib_hvlite::_jobs::consume_and_test_nextest_vmm_tests_archive 0 shell: bash - name: resolve OpenHCL igvm artifact @@ -3542,7 +3548,7 @@ jobs: flowey.exe e 20 flowey_lib_hvlite::git_checkout_openvmm_repo 0 flowey.exe e 20 flowey_lib_hvlite::run_cargo_nextest_run 0 flowey.exe e 20 flowey_lib_hvlite::run_cargo_nextest_run 1 - flowey.exe e 20 flowey_core::pipeline::artifact::resolve 11 + flowey.exe e 20 flowey_core::pipeline::artifact::resolve 12 flowey.exe e 20 flowey_lib_hvlite::test_nextest_vmm_tests_archive 0 shell: bash - name: generate nextest command @@ -3619,7 +3625,7 @@ jobs: - name: 🌼📦 Download artifacts uses: actions/download-artifact@v4 with: - pattern: '{_internal-flowey-bootstrap-x86_64-windows-uid-14,x64-guest_test_uefi,x64-linux-musl-pipette,x64-linux-musl-tmk_vmm,x64-linux-tpm_guest_tests,x64-openhcl-igvm,x64-tmks,x64-windows-openvmm,x64-windows-pipette,x64-windows-prep_steps,x64-windows-tmk_vmm,x64-windows-tpm_guest_tests,x64-windows-vmgstool,x64-windows-vmm-tests-archive}' + pattern: '{_internal-flowey-bootstrap-x86_64-windows-uid-14,x64-guest_test_uefi,x64-linux-musl-pipette,x64-linux-musl-tmk_vmm,x64-linux-tpm_guest_tests,x64-openhcl-igvm,x64-tmks,x64-windows-openvmm,x64-windows-pipette,x64-windows-prep_steps,x64-windows-test_igvm_agent_rpc_server,x64-windows-tmk_vmm,x64-windows-tpm_guest_tests,x64-windows-vmgstool,x64-windows-vmm-tests-archive}' path: ${{ runner.temp }}/used_artifacts/ - run: echo "${{ runner.temp }}/used_artifacts/_internal-flowey-bootstrap-x86_64-windows-uid-14" >> $GITHUB_PATH shell: bash @@ -3647,6 +3653,7 @@ jobs: echo "${{ runner.temp }}\\used_artifacts\\x64-windows-openvmm" | flowey.exe v 21 'artifact_use_from_x64-windows-openvmm' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-pipette" | flowey.exe v 21 'artifact_use_from_x64-windows-pipette' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-prep_steps" | flowey.exe v 21 'artifact_use_from_x64-windows-prep_steps' --is-raw-string update + echo "${{ runner.temp }}\\used_artifacts\\x64-windows-test_igvm_agent_rpc_server" | flowey.exe v 21 'artifact_use_from_x64-windows-test_igvm_agent_rpc_server' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-tmk_vmm" | flowey.exe v 21 'artifact_use_from_x64-windows-tmk_vmm' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-tpm_guest_tests" | flowey.exe v 21 'artifact_use_from_x64-windows-tpm_guest_tests' --is-raw-string update echo "${{ runner.temp }}\\used_artifacts\\x64-windows-vmgstool" | flowey.exe v 21 'artifact_use_from_x64-windows-vmgstool' --is-raw-string update @@ -3658,12 +3665,13 @@ jobs: flowey.exe e 21 flowey_core::pipeline::artifact::resolve 6 flowey.exe e 21 flowey_core::pipeline::artifact::resolve 1 flowey.exe e 21 flowey_core::pipeline::artifact::resolve 0 - flowey.exe e 21 flowey_core::pipeline::artifact::resolve 8 + flowey.exe e 21 flowey_core::pipeline::artifact::resolve 9 flowey.exe e 21 flowey_core::pipeline::artifact::resolve 2 flowey.exe e 21 flowey_core::pipeline::artifact::resolve 4 + flowey.exe e 21 flowey_core::pipeline::artifact::resolve 11 flowey.exe e 21 flowey_core::pipeline::artifact::resolve 10 - flowey.exe e 21 flowey_core::pipeline::artifact::resolve 9 flowey.exe e 21 flowey_core::pipeline::artifact::resolve 3 + flowey.exe e 21 flowey_core::pipeline::artifact::resolve 8 flowey.exe e 21 flowey_lib_hvlite::_jobs::consume_and_test_nextest_vmm_tests_archive 0 shell: bash - name: resolve OpenHCL igvm artifact @@ -3820,7 +3828,7 @@ jobs: flowey.exe e 21 flowey_lib_hvlite::git_checkout_openvmm_repo 0 flowey.exe e 21 flowey_lib_hvlite::run_cargo_nextest_run 0 flowey.exe e 21 flowey_lib_hvlite::run_cargo_nextest_run 1 - flowey.exe e 21 flowey_core::pipeline::artifact::resolve 11 + flowey.exe e 21 flowey_core::pipeline::artifact::resolve 12 flowey.exe e 21 flowey_lib_hvlite::test_nextest_vmm_tests_archive 0 shell: bash - name: generate nextest command @@ -3929,12 +3937,12 @@ jobs: shell: bash - name: creating new test content dir run: |- - flowey e 22 flowey_core::pipeline::artifact::resolve 3 - flowey e 22 flowey_core::pipeline::artifact::resolve 6 flowey e 22 flowey_core::pipeline::artifact::resolve 1 flowey e 22 flowey_core::pipeline::artifact::resolve 0 flowey e 22 flowey_core::pipeline::artifact::resolve 2 flowey e 22 flowey_core::pipeline::artifact::resolve 5 + flowey e 22 flowey_core::pipeline::artifact::resolve 3 + flowey e 22 flowey_core::pipeline::artifact::resolve 6 flowey e 22 flowey_lib_hvlite::_jobs::consume_and_test_nextest_vmm_tests_archive 0 shell: bash - name: create azcopy cache dir @@ -4254,6 +4262,7 @@ jobs: shell: bash - name: creating new test content dir run: |- + flowey.exe e 23 flowey_core::pipeline::artifact::resolve 4 flowey.exe e 23 flowey_core::pipeline::artifact::resolve 5 flowey.exe e 23 flowey_core::pipeline::artifact::resolve 1 flowey.exe e 23 flowey_core::pipeline::artifact::resolve 0 @@ -4261,7 +4270,6 @@ jobs: flowey.exe e 23 flowey_core::pipeline::artifact::resolve 2 flowey.exe e 23 flowey_core::pipeline::artifact::resolve 3 flowey.exe e 23 flowey_core::pipeline::artifact::resolve 7 - flowey.exe e 23 flowey_core::pipeline::artifact::resolve 4 flowey.exe e 23 flowey_lib_hvlite::_jobs::consume_and_test_nextest_vmm_tests_archive 0 shell: bash - name: resolve OpenHCL igvm artifact @@ -4730,6 +4738,8 @@ jobs: echo "${{ runner.temp }}\\publish_artifacts\\aarch64-windows-pipette" | flowey.exe v 3 'artifact_publish_from_aarch64-windows-pipette' --is-raw-string update mkdir -p "$AgentTempDirNormal/publish_artifacts/aarch64-windows-prep_steps" echo "${{ runner.temp }}\\publish_artifacts\\aarch64-windows-prep_steps" | flowey.exe v 3 'artifact_publish_from_aarch64-windows-prep_steps' --is-raw-string update + mkdir -p "$AgentTempDirNormal/publish_artifacts/aarch64-windows-test_igvm_agent_rpc_server" + echo "${{ runner.temp }}\\publish_artifacts\\aarch64-windows-test_igvm_agent_rpc_server" | flowey.exe v 3 'artifact_publish_from_aarch64-windows-test_igvm_agent_rpc_server' --is-raw-string update mkdir -p "$AgentTempDirNormal/publish_artifacts/aarch64-windows-tmk_vmm" echo "${{ runner.temp }}\\publish_artifacts\\aarch64-windows-tmk_vmm" | flowey.exe v 3 'artifact_publish_from_aarch64-windows-tmk_vmm' --is-raw-string update mkdir -p "$AgentTempDirNormal/publish_artifacts/aarch64-windows-tpm_guest_tests" @@ -4806,32 +4816,42 @@ jobs: - name: symlink protoc run: |- flowey.exe e 3 flowey_lib_hvlite::init_openvmm_magicpath_protoc 0 - flowey.exe e 3 flowey_lib_hvlite::init_cross_build 6 + flowey.exe e 3 flowey_lib_hvlite::init_cross_build 7 shell: bash - name: cargo build prep_steps run: |- flowey.exe e 3 flowey_lib_common::run_cargo_build 2 flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 2 flowey.exe e 3 flowey_lib_hvlite::build_prep_steps 0 - flowey.exe e 3 flowey_core::pipeline::artifact::publish 4 - flowey.exe e 3 flowey_lib_hvlite::init_cross_build 3 + flowey.exe e 3 flowey_core::pipeline::artifact::publish 5 + flowey.exe e 3 flowey_lib_hvlite::init_cross_build 4 shell: bash - name: cargo build vmgstool run: |- - flowey.exe e 3 flowey_lib_common::run_cargo_build 5 - flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 7 + flowey.exe e 3 flowey_lib_common::run_cargo_build 6 + flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 10 flowey.exe e 3 flowey_lib_hvlite::build_vmgstool 0 - flowey.exe e 3 flowey_core::pipeline::artifact::publish 5 - flowey.exe e 3 flowey_lib_hvlite::init_cross_build 2 - flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 4 - flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 5 + flowey.exe e 3 flowey_core::pipeline::artifact::publish 6 + flowey.exe e 3 flowey_lib_hvlite::init_cross_build 3 + flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 7 + flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 8 shell: bash - name: cargo build tpm_guest_tests run: |- - flowey.exe e 3 flowey_lib_common::run_cargo_build 4 - flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 6 + flowey.exe e 3 flowey_lib_common::run_cargo_build 5 + flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 9 flowey.exe e 3 flowey_lib_hvlite::build_tpm_guest_tests 0 - flowey.exe e 3 flowey_core::pipeline::artifact::publish 6 + flowey.exe e 3 flowey_core::pipeline::artifact::publish 7 + flowey.exe e 3 flowey_lib_hvlite::init_cross_build 1 + flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 3 + flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 4 + shell: bash + - name: cargo build test_igvm_agent_rpc_server + run: |- + flowey.exe e 3 flowey_lib_common::run_cargo_build 3 + flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 5 + flowey.exe e 3 flowey_lib_hvlite::build_test_igvm_agent_rpc_server 0 + flowey.exe e 3 flowey_core::pipeline::artifact::publish 0 shell: bash - name: create cargo-nextest cache dir run: |- @@ -4872,31 +4892,31 @@ jobs: run: |- flowey.exe e 3 flowey_lib_common::run_cargo_nextest_archive 0 flowey.exe e 3 flowey_lib_hvlite::build_nextest_vmm_tests 0 - flowey.exe e 3 flowey_core::pipeline::artifact::publish 0 - flowey.exe e 3 flowey_lib_hvlite::init_cross_build 4 + flowey.exe e 3 flowey_core::pipeline::artifact::publish 1 + flowey.exe e 3 flowey_lib_hvlite::init_cross_build 5 shell: bash - name: cargo build openvmm run: |- flowey.exe e 3 flowey_lib_common::run_cargo_build 0 flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 0 flowey.exe e 3 flowey_lib_hvlite::build_openvmm 0 - flowey.exe e 3 flowey_core::pipeline::artifact::publish 1 - flowey.exe e 3 flowey_lib_hvlite::init_cross_build 5 + flowey.exe e 3 flowey_core::pipeline::artifact::publish 2 + flowey.exe e 3 flowey_lib_hvlite::init_cross_build 6 shell: bash - name: cargo build pipette run: |- flowey.exe e 3 flowey_lib_common::run_cargo_build 1 flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 1 flowey.exe e 3 flowey_lib_hvlite::build_pipette 0 - flowey.exe e 3 flowey_core::pipeline::artifact::publish 2 - flowey.exe e 3 flowey_lib_hvlite::init_cross_build 1 + flowey.exe e 3 flowey_core::pipeline::artifact::publish 3 + flowey.exe e 3 flowey_lib_hvlite::init_cross_build 2 shell: bash - name: cargo build tmk_vmm run: |- - flowey.exe e 3 flowey_lib_common::run_cargo_build 3 - flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 3 + flowey.exe e 3 flowey_lib_common::run_cargo_build 4 + flowey.exe e 3 flowey_lib_hvlite::run_cargo_build 6 flowey.exe e 3 flowey_lib_hvlite::build_tmk_vmm 0 - flowey.exe e 3 flowey_core::pipeline::artifact::publish 3 + flowey.exe e 3 flowey_core::pipeline::artifact::publish 4 shell: bash - name: 'validate cache entry: cargo-nextest' run: flowey.exe e 3 flowey_lib_common::cache 3 @@ -4922,6 +4942,12 @@ jobs: name: aarch64-windows-prep_steps path: ${{ runner.temp }}/publish_artifacts/aarch64-windows-prep_steps/ include-hidden-files: true + - name: 🌼📦 Publish aarch64-windows-test_igvm_agent_rpc_server + uses: actions/upload-artifact@v4 + with: + name: aarch64-windows-test_igvm_agent_rpc_server + path: ${{ runner.temp }}/publish_artifacts/aarch64-windows-test_igvm_agent_rpc_server/ + include-hidden-files: true - name: 🌼📦 Publish aarch64-windows-tmk_vmm uses: actions/upload-artifact@v4 with: @@ -5247,6 +5273,8 @@ jobs: echo "${{ runner.temp }}\\publish_artifacts\\x64-windows-pipette" | flowey.exe v 5 'artifact_publish_from_x64-windows-pipette' --is-raw-string update mkdir -p "$AgentTempDirNormal/publish_artifacts/x64-windows-prep_steps" echo "${{ runner.temp }}\\publish_artifacts\\x64-windows-prep_steps" | flowey.exe v 5 'artifact_publish_from_x64-windows-prep_steps' --is-raw-string update + mkdir -p "$AgentTempDirNormal/publish_artifacts/x64-windows-test_igvm_agent_rpc_server" + echo "${{ runner.temp }}\\publish_artifacts\\x64-windows-test_igvm_agent_rpc_server" | flowey.exe v 5 'artifact_publish_from_x64-windows-test_igvm_agent_rpc_server' --is-raw-string update mkdir -p "$AgentTempDirNormal/publish_artifacts/x64-windows-tmk_vmm" echo "${{ runner.temp }}\\publish_artifacts\\x64-windows-tmk_vmm" | flowey.exe v 5 'artifact_publish_from_x64-windows-tmk_vmm' --is-raw-string update mkdir -p "$AgentTempDirNormal/publish_artifacts/x64-windows-tpm_guest_tests" @@ -5323,7 +5351,7 @@ jobs: - name: symlink protoc run: |- flowey.exe e 5 flowey_lib_hvlite::init_openvmm_magicpath_protoc 0 - flowey.exe e 5 flowey_lib_hvlite::init_cross_build 4 + flowey.exe e 5 flowey_lib_hvlite::init_cross_build 5 shell: bash - name: cargo build openvmm run: |- @@ -5331,7 +5359,7 @@ jobs: flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 0 flowey.exe e 5 flowey_lib_hvlite::build_openvmm 0 flowey.exe e 5 flowey_core::pipeline::artifact::publish 0 - flowey.exe e 5 flowey_lib_hvlite::init_cross_build 5 + flowey.exe e 5 flowey_lib_hvlite::init_cross_build 6 shell: bash - name: cargo build pipette run: |- @@ -5339,15 +5367,15 @@ jobs: flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 1 flowey.exe e 5 flowey_lib_hvlite::build_pipette 0 flowey.exe e 5 flowey_core::pipeline::artifact::publish 1 - flowey.exe e 5 flowey_lib_hvlite::init_cross_build 1 + flowey.exe e 5 flowey_lib_hvlite::init_cross_build 2 shell: bash - name: cargo build tmk_vmm run: |- - flowey.exe e 5 flowey_lib_common::run_cargo_build 3 - flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 3 + flowey.exe e 5 flowey_lib_common::run_cargo_build 4 + flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 6 flowey.exe e 5 flowey_lib_hvlite::build_tmk_vmm 0 flowey.exe e 5 flowey_core::pipeline::artifact::publish 2 - flowey.exe e 5 flowey_lib_hvlite::init_cross_build 6 + flowey.exe e 5 flowey_lib_hvlite::init_cross_build 7 shell: bash - name: cargo build prep_steps run: |- @@ -5355,24 +5383,34 @@ jobs: flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 2 flowey.exe e 5 flowey_lib_hvlite::build_prep_steps 0 flowey.exe e 5 flowey_core::pipeline::artifact::publish 3 - flowey.exe e 5 flowey_lib_hvlite::init_cross_build 3 + flowey.exe e 5 flowey_lib_hvlite::init_cross_build 4 shell: bash - name: cargo build vmgstool run: |- - flowey.exe e 5 flowey_lib_common::run_cargo_build 5 - flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 7 + flowey.exe e 5 flowey_lib_common::run_cargo_build 6 + flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 10 flowey.exe e 5 flowey_lib_hvlite::build_vmgstool 0 flowey.exe e 5 flowey_core::pipeline::artifact::publish 4 - flowey.exe e 5 flowey_lib_hvlite::init_cross_build 2 - flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 4 - flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 5 + flowey.exe e 5 flowey_lib_hvlite::init_cross_build 3 + flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 7 + flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 8 shell: bash - name: cargo build tpm_guest_tests run: |- - flowey.exe e 5 flowey_lib_common::run_cargo_build 4 - flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 6 + flowey.exe e 5 flowey_lib_common::run_cargo_build 5 + flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 9 flowey.exe e 5 flowey_lib_hvlite::build_tpm_guest_tests 0 flowey.exe e 5 flowey_core::pipeline::artifact::publish 5 + flowey.exe e 5 flowey_lib_hvlite::init_cross_build 1 + flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 3 + flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 4 + shell: bash + - name: cargo build test_igvm_agent_rpc_server + run: |- + flowey.exe e 5 flowey_lib_common::run_cargo_build 3 + flowey.exe e 5 flowey_lib_hvlite::run_cargo_build 5 + flowey.exe e 5 flowey_lib_hvlite::build_test_igvm_agent_rpc_server 0 + flowey.exe e 5 flowey_core::pipeline::artifact::publish 6 shell: bash - name: create cargo-nextest cache dir run: |- @@ -5413,7 +5451,7 @@ jobs: run: |- flowey.exe e 5 flowey_lib_common::run_cargo_nextest_archive 0 flowey.exe e 5 flowey_lib_hvlite::build_nextest_vmm_tests 0 - flowey.exe e 5 flowey_core::pipeline::artifact::publish 6 + flowey.exe e 5 flowey_core::pipeline::artifact::publish 7 shell: bash - name: 'validate cache entry: cargo-nextest' run: flowey.exe e 5 flowey_lib_common::cache 3 @@ -5439,6 +5477,12 @@ jobs: name: x64-windows-prep_steps path: ${{ runner.temp }}/publish_artifacts/x64-windows-prep_steps/ include-hidden-files: true + - name: 🌼📦 Publish x64-windows-test_igvm_agent_rpc_server + uses: actions/upload-artifact@v4 + with: + name: x64-windows-test_igvm_agent_rpc_server + path: ${{ runner.temp }}/publish_artifacts/x64-windows-test_igvm_agent_rpc_server/ + include-hidden-files: true - name: 🌼📦 Publish x64-windows-tmk_vmm uses: actions/upload-artifact@v4 with: diff --git a/Cargo.lock b/Cargo.lock index a2e566e0a3..ee6bb2baf7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2636,7 +2636,6 @@ dependencies = [ "inspect", "jiff", "mesh", - "openhcl_attestation_protocol", "pal_async", "parking_lot", "power_resources", @@ -7245,7 +7244,7 @@ dependencies = [ "tracing", "tracing-subscriber", "winapi", - "windows-sys 0.59.0", + "windows-sys 0.61.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index f1a55c2261..1311cfd7fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -422,6 +422,7 @@ bitvec = { version = "1.1", default-features = false } blocking = "1.2" caps = "0.5" cargo_toml = "0.22" +cc = "1.2.34" cfg-if = "1" clap = "4.2" crc32fast = { version = "1.3.2", default-features = false } diff --git a/vm/devices/get/guest_emulation_device/Cargo.toml b/vm/devices/get/guest_emulation_device/Cargo.toml index 105f40446f..fa33f441d7 100644 --- a/vm/devices/get/guest_emulation_device/Cargo.toml +++ b/vm/devices/get/guest_emulation_device/Cargo.toml @@ -17,7 +17,6 @@ test_igvm_agent_lib.workspace = true disk_backend.workspace = true disklayer_ram = { workspace = true, optional = true } guestmem.workspace = true -openhcl_attestation_protocol.workspace = true power_resources.workspace = true scsi_buffers.workspace = true video_core.workspace = true diff --git a/vm/devices/get/test_igvm_agent_rpc_server/Cargo.toml b/vm/devices/get/test_igvm_agent_rpc_server/Cargo.toml index 877fc2794b..f5654535b4 100644 --- a/vm/devices/get/test_igvm_agent_rpc_server/Cargo.toml +++ b/vm/devices/get/test_igvm_agent_rpc_server/Cargo.toml @@ -1,3 +1,6 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + [package] name = "test_igvm_agent_rpc_server" edition.workspace = true @@ -17,7 +20,7 @@ tracing-subscriber = { workspace = true, features = ["env-filter"] } [target.'cfg(windows)'.dependencies] winapi.workspace = true -windows-sys = { version = "0.59", features = [ +windows-sys = { workspace = true, features = [ "Win32_Foundation", "Win32_System_Memory", "Win32_System_Rpc", @@ -26,8 +29,8 @@ windows-sys = { version = "0.59", features = [ ] } [build-dependencies] -cc = "1" -serde_json = "1" +cc.workspace = true +serde_json = { workspace = true, features = ["std"] } [lints] workspace = true diff --git a/vm/devices/get/test_igvm_agent_rpc_server/build.rs b/vm/devices/get/test_igvm_agent_rpc_server/build.rs index e51906197d..bb386b8ddb 100644 --- a/vm/devices/get/test_igvm_agent_rpc_server/build.rs +++ b/vm/devices/get/test_igvm_agent_rpc_server/build.rs @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + //! Build script that compiles Windows RPC stubs for the IGVM agent façade. use serde_json::Value; diff --git a/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs index c2b013e60f..b33b4bb2f1 100644 --- a/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs +++ b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs @@ -7,7 +7,7 @@ use std::os::windows::ffi::OsStrExt; use std::ptr; use std::sync::atomic::AtomicBool; use std::sync::atomic::Ordering; -use windows_sys::Win32::Foundation::BOOL; +use winapi::shared::minwindef::BOOL; use windows_sys::Win32::Foundation::FALSE; use windows_sys::Win32::Foundation::TRUE; use windows_sys::Win32::System::Console::CTRL_BREAK_EVENT; diff --git a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs index 4084e737aa..c25252e6a4 100644 --- a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs +++ b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs @@ -480,10 +480,16 @@ async fn cvm_tpm_guest_tests( .stdout .take() .context("failed to take stdout from RPC server")?; - let mut b = [0]; - stdout - .read(&mut b) + let mut byte = [0u8]; + let n = stdout + .read(&mut byte) .context("failed to read from RPC server stdout")?; + if n != 0 { + anyhow::bail!( + "expected RPC server stdout to close (EOF), but read {} bytes", + n + ); + } // Spawn a task to read and log stderr from the RPC server let stderr_task = std::thread::spawn(move || { From b0f3dea1975a341d10810112d88369eef952c6a2 Mon Sep 17 00:00:00 2001 From: Ming-Wei Shih Date: Wed, 3 Dec 2025 00:53:33 +0000 Subject: [PATCH 06/32] missing file Signed-off-by: Ming-Wei Shih --- vm/devices/get/test_igvm_agent_lib/Cargo.toml | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 vm/devices/get/test_igvm_agent_lib/Cargo.toml diff --git a/vm/devices/get/test_igvm_agent_lib/Cargo.toml b/vm/devices/get/test_igvm_agent_lib/Cargo.toml new file mode 100644 index 0000000000..8b151f9c1b --- /dev/null +++ b/vm/devices/get/test_igvm_agent_lib/Cargo.toml @@ -0,0 +1,22 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +[package] +name = "test_igvm_agent_lib" +edition.workspace = true +rust-version.workspace = true + +[dependencies] +base64.workspace = true +get_resources.workspace = true +inspect = { workspace = true, features = ["derive"] } +openhcl_attestation_protocol.workspace = true +rsa.workspace = true +serde_json.workspace = true +sha2.workspace = true +thiserror.workspace = true +tracing.workspace = true +zerocopy.workspace = true + +[lints] +workspace = true From 275748330837a44a783bf00cd18cd8cf16f7ef7b Mon Sep 17 00:00:00 2001 From: Ming-Wei Shih Date: Wed, 3 Dec 2025 01:13:32 +0000 Subject: [PATCH 07/32] fix Signed-off-by: Ming-Wei Shih --- .../get/test_igvm_agent_rpc_server/build.rs | 80 ++++++++++++++++++- .../vmm_tests/tests/tests/multiarch/tpm.rs | 1 + 2 files changed, 77 insertions(+), 4 deletions(-) diff --git a/vm/devices/get/test_igvm_agent_rpc_server/build.rs b/vm/devices/get/test_igvm_agent_rpc_server/build.rs index bb386b8ddb..3cd653da32 100644 --- a/vm/devices/get/test_igvm_agent_rpc_server/build.rs +++ b/vm/devices/get/test_igvm_agent_rpc_server/build.rs @@ -33,10 +33,21 @@ fn main() { let host_is_windows = host.contains("windows"); let midl_info = locate_midl(&target_env); - if !host_is_windows && midl_info.is_none() { - panic!( - "MIDL compiler is required to build for Windows targets. Install the Windows SDK with MIDL compiler, or set MIDL environment variable to point to a cross-compilation MIDL tool." - ); + if midl_info.is_none() { + if host_is_windows { + panic!( + "MIDL compiler not found. Please install the Windows SDK which includes MIDL, \ + or set the MIDL environment variable to point to midl.exe. \ + You can download the Windows SDK from: \ + https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/" + ); + } else { + panic!( + "MIDL compiler is required to build for Windows targets from non-Windows hosts. \ + Set MIDL or MIDLRT_{} environment variable to point to a cross-compilation MIDL tool.", + target_env + ); + } } let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR not set")); @@ -130,6 +141,7 @@ fn main() { } fn locate_midl(target_env: &str) -> Option<(String, bool)> { + // Check for cross-compilation MIDL tool first let key = format!("MIDLRT_{}", target_env); if let Ok(path) = env::var(&key) { if !path.is_empty() { @@ -143,6 +155,66 @@ fn locate_midl(target_env: &str) -> Option<(String, bool)> { } } + // On Windows, try to find MIDL from the Windows SDK + #[cfg(windows)] + { + if let Some(midl_path) = find_windows_sdk_midl() { + return Some((midl_path, false)); + } + } + + None +} + +#[cfg(windows)] +fn find_windows_sdk_midl() -> Option { + use std::process::Command; + + // Try common Windows SDK paths directly + // The SDK can be installed standalone without Visual Studio + let sdk_dirs = [ + "C:\\Program Files (x86)\\Windows Kits\\10\\bin", + "C:\\Program Files\\Windows Kits\\10\\bin", + ]; + + for sdk_dir in &sdk_dirs { + // Try to find the latest SDK version + if let Ok(entries) = std::fs::read_dir(sdk_dir) { + let mut versions: Vec<_> = entries + .filter_map(|e| e.ok()) + .filter(|e| e.file_type().map(|t| t.is_dir()).unwrap_or(false)) + .filter_map(|e| { + let name = e.file_name(); + let name_str = name.to_string_lossy(); + // Check if it looks like a version number (starts with digit) + if name_str.chars().next()?.is_ascii_digit() { + Some((name_str.to_string(), e.path())) + } else { + None + } + }) + .collect(); + + // Sort versions in reverse order to get the latest + versions.sort_by(|a, b| b.0.cmp(&a.0)); + + for (_, version_path) in versions { + // Check both x64 and x86 subdirectories + for arch_subdir in &["x64", "x86"] { + let midl_path = version_path.join(arch_subdir).join("midl.exe"); + if midl_path.exists() { + return Some(midl_path.to_string_lossy().to_string()); + } + } + } + } + } + + // Fallback: check if "midl" is in PATH + if Command::new("midl").arg("/?").output().is_ok() { + return Some("midl".to_string()); + } + None } diff --git a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs index c25252e6a4..1e90997112 100644 --- a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs +++ b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs @@ -15,6 +15,7 @@ use petri::pipette::cmd; use petri_artifacts_common::tags::OsFlavor; use petri_artifacts_vmm_test::artifacts::guest_tools::TPM_GUEST_TESTS_LINUX_X64; use petri_artifacts_vmm_test::artifacts::guest_tools::TPM_GUEST_TESTS_WINDOWS_X64; +#[cfg(windows)] use petri_artifacts_vmm_test::artifacts::host_tools::TEST_IGVM_AGENT_RPC_SERVER_WINDOWS_X64; use pipette_client::PipetteClient; use std::path::Path; From 859ef12abc2fea3a1a19a39776f97e262b17c8f9 Mon Sep 17 00:00:00 2001 From: Ming-Wei Shih Date: Wed, 3 Dec 2025 01:40:37 +0000 Subject: [PATCH 08/32] x Signed-off-by: Ming-Wei Shih --- .../get/test_igvm_agent_rpc_server/build.rs | 42 ++++++++++++++++++- .../vmm_tests/tests/tests/multiarch/tpm.rs | 10 ++--- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/vm/devices/get/test_igvm_agent_rpc_server/build.rs b/vm/devices/get/test_igvm_agent_rpc_server/build.rs index 3cd653da32..76acdf2c1f 100644 --- a/vm/devices/get/test_igvm_agent_rpc_server/build.rs +++ b/vm/devices/get/test_igvm_agent_rpc_server/build.rs @@ -101,7 +101,14 @@ fn main() { panic!("Failed to configure cross-compilation environment: {err}"); } } - _ => {} + _ => { + // When building on native Windows, set up the MSVC environment for MIDL + if host_is_windows { + if let Err(err) = setup_msvc_env_for_midl(&mut cmd, &target) { + panic!("Failed to set up MSVC environment for MIDL: {err}"); + } + } + } } let pointer_width = @@ -218,6 +225,39 @@ fn find_windows_sdk_midl() -> Option { None } +#[cfg(windows)] +fn setup_msvc_env_for_midl(cmd: &mut Command, target: &str) -> Result<(), String> { + // Use the cc crate to get the MSVC compiler tool, which will give us + // access to the properly configured environment including cl.exe path + let tool = cc::Build::new().target(target).host(target).get_compiler(); + + // Get the path to cl.exe + let cl_path = tool.path(); + if let Some(bin_dir) = cl_path.parent() { + // Add the MSVC bin directory to PATH so MIDL can find cl.exe + let mut path_value = env::var_os("PATH").unwrap_or_default(); + if !path_value.is_empty() { + path_value.push(";"); + } + path_value.push(bin_dir.as_os_str()); + cmd.env("PATH", &path_value); + + // Also set up INCLUDE and LIB environment variables that MIDL/cl.exe need + for (key, value) in tool.env() { + cmd.env(key, value); + } + } else { + return Err("Failed to find parent directory of cl.exe".to_string()); + } + + Ok(()) +} + +#[cfg(not(windows))] +fn setup_msvc_env_for_midl(_cmd: &mut Command, _target: &str) -> Result<(), String> { + Ok(()) +} + fn configure_cross_env(cmd: &mut Command, cfg: &CrossConfig) -> Result<(), String> { let include = join_windows_paths(&cfg.include)?; let lib = join_windows_paths(&cfg.lib)?; diff --git a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs index 1e90997112..9ef773d75b 100644 --- a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs +++ b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs @@ -3,8 +3,6 @@ use anyhow::Context; use anyhow::ensure; -#[cfg(windows)] -use pal; use petri::PetriGuestStateLifetime; use petri::PetriVmBuilder; use petri::PetriVmmBackend; @@ -509,7 +507,7 @@ async fn cvm_tpm_guest_tests( // Create a guard to ensure the RPC server is killed when this function exits struct RpcServerGuard { child: std::process::Child, - stderr_task: std::thread::JoinHandle<()>, + stderr_task: Option>, } impl Drop for RpcServerGuard { fn drop(&mut self) { @@ -517,12 +515,14 @@ async fn cvm_tpm_guest_tests( let _ = self.child.kill(); let _ = self.child.wait(); // Give the stderr logging thread a moment to finish - let _ = self.stderr_task.join(); + if let Some(task) = self.stderr_task.take() { + let _ = task.join(); + } } } let _rpc_server_guard = RpcServerGuard { child: rpc_server_child, - stderr_task, + stderr_task: Some(stderr_task), }; let config = config From c78b43c0a8e4f885a38d1b49a2b99ff7095908d9 Mon Sep 17 00:00:00 2001 From: Ming-Wei Shih Date: Wed, 3 Dec 2025 18:40:04 +0000 Subject: [PATCH 09/32] x Signed-off-by: Ming-Wei Shih --- vm/devices/get/test_igvm_agent_rpc_server/src/main.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/vm/devices/get/test_igvm_agent_rpc_server/src/main.rs b/vm/devices/get/test_igvm_agent_rpc_server/src/main.rs index c84e5189a4..c8d3c1e4e6 100644 --- a/vm/devices/get/test_igvm_agent_rpc_server/src/main.rs +++ b/vm/devices/get/test_igvm_agent_rpc_server/src/main.rs @@ -57,11 +57,15 @@ fn main() -> ExitCode { let _ = fmt() .with_env_filter(filter) - .with_writer(std::io::stdout) + .with_writer(std::io::stderr) .try_init(); tracing::info!("launching IGVM agent RPC server binary"); + // Close stdout to signal that the server is ready to accept connections. + // The test harness waits for stdout EOF before proceeding. + drop(std::io::stdout()); + // Install test plan if a configuration was provided if let Some(test_config) = args.test_config { let igvm_config: IgvmAttestTestConfig = test_config.into(); From 9362454b48c699eb464ad523fd9b018860a14a46 Mon Sep 17 00:00:00 2001 From: Ming-Wei Shih Date: Wed, 3 Dec 2025 20:54:42 +0000 Subject: [PATCH 10/32] try Signed-off-by: Ming-Wei Shih --- Cargo.lock | 1 + .../get/test_igvm_agent_rpc_server/build.rs | 16 +++++++++------- vmm_tests/vmm_tests/Cargo.toml | 1 + vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs | 11 +++++++++++ 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ee6bb2baf7..0cfd4dbc5d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9459,6 +9459,7 @@ dependencies = [ "nvme_test", "pal", "pal_async", + "parking_lot", "petri", "petri_artifact_resolver_openvmm_known_paths", "petri_artifacts_common", diff --git a/vm/devices/get/test_igvm_agent_rpc_server/build.rs b/vm/devices/get/test_igvm_agent_rpc_server/build.rs index 76acdf2c1f..fc3240b76b 100644 --- a/vm/devices/get/test_igvm_agent_rpc_server/build.rs +++ b/vm/devices/get/test_igvm_agent_rpc_server/build.rs @@ -111,13 +111,15 @@ fn main() { } } - let pointer_width = - env::var("CARGO_CFG_TARGET_POINTER_WIDTH").unwrap_or_else(|_| "64".to_owned()); - if pointer_width == "32" { - cmd.args(["/env", "win32"]); - } else { - cmd.args(["/env", "x64"]); - } + // Determine the MIDL target environment based on the Cargo target. + // xtask-fmt allow-target-arch dependency + let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_else(|_| "x86_64".to_owned()); + let midl_env = match arch.as_str() { + "x86_64" => "x64", + "aarch64" => "arm64", + unsupported => panic!("Unsupported architecture for MIDL: {}", unsupported), + }; + cmd.args(["/env", midl_env]); let out_dir_arg = path_for_midl(&out_dir, host_is_windows); cmd.arg("/out"); diff --git a/vmm_tests/vmm_tests/Cargo.toml b/vmm_tests/vmm_tests/Cargo.toml index d1198b6244..e6088f7c3f 100644 --- a/vmm_tests/vmm_tests/Cargo.toml +++ b/vmm_tests/vmm_tests/Cargo.toml @@ -48,6 +48,7 @@ vmgs_resources.workspace = true anyhow.workspace = true futures.workspace = true jiff.workspace = true +parking_lot.workspace = true tracing.workspace = true zerocopy.workspace = true static_assertions.workspace = true diff --git a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs index 9ef773d75b..c67e25b935 100644 --- a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs +++ b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs @@ -3,6 +3,7 @@ use anyhow::Context; use anyhow::ensure; +use parking_lot::Mutex; use petri::PetriGuestStateLifetime; use petri::PetriVmBuilder; use petri::PetriVmmBackend; @@ -26,6 +27,11 @@ const AK_CERT_TOTAL_BYTES: usize = 4096; const TPM_GUEST_TESTS_LINUX_GUEST_PATH: &str = "/tmp/tpm_guest_tests"; const TPM_GUEST_TESTS_WINDOWS_GUEST_PATH: &str = "C:\\tpm_guest_tests.exe"; +// Global mutex to serialize access to the RPC server endpoint. +// Only one test can run the RPC server at a time since it binds to a fixed endpoint. +#[cfg(windows)] +static RPC_SERVER_LOCK: Mutex<()> = Mutex::new(()); + fn expected_ak_cert_hex() -> String { use std::fmt::Write as _; @@ -463,6 +469,11 @@ async fn cvm_tpm_guest_tests( let os_flavor = config.os_flavor(); let (tpm_guest_tests_artifact, rpc_server_artifact) = extra_deps; + // Acquire the RPC server lock to ensure only one test runs the server at a time. + // The RPC server binds to a fixed endpoint, so parallel tests would conflict. + let _rpc_lock = RPC_SERVER_LOCK.lock(); + tracing::info!("acquired RPC server lock"); + // Spawn the test IGVM agent RPC server on the host before creating the VM tracing::info!("launching test_igvm_agent_rpc_server"); let rpc_server_path = rpc_server_artifact.get(); From be86612a45cd450483e06a207935f5dfebcffbfc Mon Sep 17 00:00:00 2001 From: Ming-Wei Shih Date: Wed, 3 Dec 2025 22:27:35 +0000 Subject: [PATCH 11/32] x Signed-off-by: Ming-Wei Shih --- vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs index c67e25b935..2e45bb3d79 100644 --- a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs +++ b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs @@ -3,6 +3,7 @@ use anyhow::Context; use anyhow::ensure; +#[cfg(windows)] use parking_lot::Mutex; use petri::PetriGuestStateLifetime; use petri::PetriVmBuilder; From cbc4e0eb8edd54c3a22f581a83adf3d55139ef61 Mon Sep 17 00:00:00 2001 From: Ming-Wei Shih Date: Wed, 3 Dec 2025 23:56:18 +0000 Subject: [PATCH 12/32] x Signed-off-by: Ming-Wei Shih --- Cargo.lock | 1 - vmm_tests/vmm_tests/Cargo.toml | 1 - vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs | 4 ++-- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0cfd4dbc5d..ee6bb2baf7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9459,7 +9459,6 @@ dependencies = [ "nvme_test", "pal", "pal_async", - "parking_lot", "petri", "petri_artifact_resolver_openvmm_known_paths", "petri_artifacts_common", diff --git a/vmm_tests/vmm_tests/Cargo.toml b/vmm_tests/vmm_tests/Cargo.toml index e6088f7c3f..d1198b6244 100644 --- a/vmm_tests/vmm_tests/Cargo.toml +++ b/vmm_tests/vmm_tests/Cargo.toml @@ -48,7 +48,6 @@ vmgs_resources.workspace = true anyhow.workspace = true futures.workspace = true jiff.workspace = true -parking_lot.workspace = true tracing.workspace = true zerocopy.workspace = true static_assertions.workspace = true diff --git a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs index 2e45bb3d79..789f4c3000 100644 --- a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs +++ b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs @@ -4,7 +4,7 @@ use anyhow::Context; use anyhow::ensure; #[cfg(windows)] -use parking_lot::Mutex; +use futures::lock::Mutex; use petri::PetriGuestStateLifetime; use petri::PetriVmBuilder; use petri::PetriVmmBackend; @@ -472,7 +472,7 @@ async fn cvm_tpm_guest_tests( // Acquire the RPC server lock to ensure only one test runs the server at a time. // The RPC server binds to a fixed endpoint, so parallel tests would conflict. - let _rpc_lock = RPC_SERVER_LOCK.lock(); + let _rpc_lock = RPC_SERVER_LOCK.lock().await; tracing::info!("acquired RPC server lock"); // Spawn the test IGVM agent RPC server on the host before creating the VM From 8901d7b6e76ed09891902dfac69f033fd94592aa Mon Sep 17 00:00:00 2001 From: Ming-Wei Shih Date: Thu, 4 Dec 2025 00:39:44 +0000 Subject: [PATCH 13/32] x Signed-off-by: Ming-Wei Shih --- vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs index 789f4c3000..cac0016736 100644 --- a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs +++ b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs @@ -19,6 +19,8 @@ use petri_artifacts_vmm_test::artifacts::guest_tools::TPM_GUEST_TESTS_WINDOWS_X6 use petri_artifacts_vmm_test::artifacts::host_tools::TEST_IGVM_AGENT_RPC_SERVER_WINDOWS_X64; use pipette_client::PipetteClient; use std::path::Path; +#[cfg(windows)] +use std::sync::LazyLock; use vmm_test_macros::openvmm_test; use vmm_test_macros::vmm_test; @@ -31,7 +33,7 @@ const TPM_GUEST_TESTS_WINDOWS_GUEST_PATH: &str = "C:\\tpm_guest_tests.exe"; // Global mutex to serialize access to the RPC server endpoint. // Only one test can run the RPC server at a time since it binds to a fixed endpoint. #[cfg(windows)] -static RPC_SERVER_LOCK: Mutex<()> = Mutex::new(()); +static RPC_SERVER_LOCK: LazyLock> = LazyLock::new(|| Mutex::new(())); fn expected_ak_cert_hex() -> String { use std::fmt::Write as _; From 3af3f2b8fd29a0ae6a7b2ddcf10aa12fed47ce89 Mon Sep 17 00:00:00 2001 From: Ming-Wei Shih Date: Thu, 4 Dec 2025 02:42:43 +0000 Subject: [PATCH 14/32] try Signed-off-by: Ming-Wei Shih --- .../src/rpc/server.rs | 64 +++++++++++++++---- .../vmm_tests/tests/tests/multiarch/tpm.rs | 3 + 2 files changed, 55 insertions(+), 12 deletions(-) diff --git a/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs index b33b4bb2f1..88d985736f 100644 --- a/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs +++ b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs @@ -101,20 +101,60 @@ fn register_protocol_and_interface() -> Result<(), String> { let mut protocol_seq = to_wide(PROTOCOL_SEQUENCE); let mut endpoint = to_wide(ENDPOINT); - // SAFETY: Make an FFI call. - let status = unsafe { - RpcServerUseProtseqEpW( - protocol_seq.as_mut_ptr(), - RPC_C_PROTSEQ_MAX_REQS_DEFAULT, - endpoint.as_mut_ptr(), - ptr::null_mut(), - ) - }; - if status != RPC_S_OK { - tracing::error!(status = status, "RpcServerUseProtseqEpW failed"); + // Retry binding to the endpoint, as it may take time for Windows to release + // it after a previous server process exits. + const MAX_RETRIES: u32 = 10; + const RETRY_DELAY_MS: u64 = 100; + let mut last_status = RPC_S_OK; + + for attempt in 1..=MAX_RETRIES { + // SAFETY: Make an FFI call. + let status = unsafe { + RpcServerUseProtseqEpW( + protocol_seq.as_mut_ptr(), + RPC_C_PROTSEQ_MAX_REQS_DEFAULT, + endpoint.as_mut_ptr(), + ptr::null_mut(), + ) + }; + + if status == RPC_S_OK { + tracing::info!("RPC protocol bound successfully"); + break; + } + + last_status = status; + + // Error 1740 (EPT_S_CANT_PERFORM_OP or RPC_S_DUPLICATE_ENDPOINT) means + // the endpoint is already in use or still being released. + if status == 1740 && attempt < MAX_RETRIES { + tracing::warn!( + attempt = attempt, + max_retries = MAX_RETRIES, + "endpoint in use, retrying after delay" + ); + std::thread::sleep(std::time::Duration::from_millis(RETRY_DELAY_MS)); + continue; + } + + tracing::error!( + status = status, + attempt = attempt, + "RpcServerUseProtseqEpW failed" + ); return Err(format!("RpcServerUseProtseqEpW failed: {status}")); } - tracing::info!("RPC protocol bound successfully"); + + if last_status != RPC_S_OK { + tracing::error!( + status = last_status, + "failed to bind endpoint after retries" + ); + return Err(format!( + "RpcServerUseProtseqEpW failed after {} retries: {}", + MAX_RETRIES, last_status + )); + } // SAFETY: Make an FFI call. let status = unsafe { diff --git a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs index cac0016736..a8ad734131 100644 --- a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs +++ b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs @@ -532,6 +532,9 @@ async fn cvm_tpm_guest_tests( if let Some(task) = self.stderr_task.take() { let _ = task.join(); } + // Give Windows a moment to fully release the RPC endpoint before + // the next test tries to bind to it. + std::thread::sleep(std::time::Duration::from_millis(50)); } } let _rpc_server_guard = RpcServerGuard { From 889cb08c4378dd0ad270e324f32e2dd3f280c5bb Mon Sep 17 00:00:00 2001 From: Ming-Wei Shih Date: Thu, 4 Dec 2025 19:28:30 +0000 Subject: [PATCH 15/32] x Signed-off-by: Ming-Wei Shih --- .../get/test_igvm_agent_rpc_server/src/main.rs | 4 ---- .../test_igvm_agent_rpc_server/src/rpc/server.rs | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/vm/devices/get/test_igvm_agent_rpc_server/src/main.rs b/vm/devices/get/test_igvm_agent_rpc_server/src/main.rs index c8d3c1e4e6..f4f98cf153 100644 --- a/vm/devices/get/test_igvm_agent_rpc_server/src/main.rs +++ b/vm/devices/get/test_igvm_agent_rpc_server/src/main.rs @@ -62,10 +62,6 @@ fn main() -> ExitCode { tracing::info!("launching IGVM agent RPC server binary"); - // Close stdout to signal that the server is ready to accept connections. - // The test harness waits for stdout EOF before proceeding. - drop(std::io::stdout()); - // Install test plan if a configuration was provided if let Some(test_config) = args.test_config { let igvm_config: IgvmAttestTestConfig = test_config.into(); diff --git a/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs index 88d985736f..faf5366742 100644 --- a/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs +++ b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs @@ -192,6 +192,21 @@ pub fn run_server() -> Result<(), String> { register_protocol_and_interface()?; let _handler = ConsoleHandlerGuard::register()?; + // Close stdout to signal that the server is ready to accept connections. + // The test harness waits for stdout EOF before proceeding. + // We must close the actual file descriptor, not just drop the Rust handle. + // SAFETY: Make FFI calls. + unsafe { + use windows_sys::Win32::Foundation::CloseHandle; + use windows_sys::Win32::System::Console::GetStdHandle; + use windows_sys::Win32::System::Console::STD_OUTPUT_HANDLE; + let stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); + if stdout_handle != 0 && stdout_handle != !0 { + CloseHandle(stdout_handle); + tracing::info!("closed stdout to signal readiness"); + } + } + // SAFETY: Make an FFI call. let listen_status = unsafe { RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, 0) }; From 5e2f250b7589adc96f6975bf1fbceb1aa946abb2 Mon Sep 17 00:00:00 2001 From: Ming-Wei Shih Date: Thu, 4 Dec 2025 19:48:12 +0000 Subject: [PATCH 16/32] x Signed-off-by: Ming-Wei Shih --- vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs index faf5366742..eec37a5cd9 100644 --- a/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs +++ b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs @@ -201,7 +201,7 @@ pub fn run_server() -> Result<(), String> { use windows_sys::Win32::System::Console::GetStdHandle; use windows_sys::Win32::System::Console::STD_OUTPUT_HANDLE; let stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); - if stdout_handle != 0 && stdout_handle != !0 { + if stdout_handle != std::ptr::null_mut() && stdout_handle != std::ptr::null_mut() { CloseHandle(stdout_handle); tracing::info!("closed stdout to signal readiness"); } From e867ad9865a6f19206b268bf020ecf3fad051a38 Mon Sep 17 00:00:00 2001 From: Ming-Wei Shih Date: Thu, 4 Dec 2025 20:25:21 +0000 Subject: [PATCH 17/32] x Signed-off-by: Ming-Wei Shih --- vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs index eec37a5cd9..10187fa80a 100644 --- a/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs +++ b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs @@ -201,7 +201,7 @@ pub fn run_server() -> Result<(), String> { use windows_sys::Win32::System::Console::GetStdHandle; use windows_sys::Win32::System::Console::STD_OUTPUT_HANDLE; let stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); - if stdout_handle != std::ptr::null_mut() && stdout_handle != std::ptr::null_mut() { + if !stdout_handle.is_null() { CloseHandle(stdout_handle); tracing::info!("closed stdout to signal readiness"); } From 6e0bb85360ef2a2a24b511b74dabf1e2afa7caf3 Mon Sep 17 00:00:00 2001 From: Ming-Wei Shih Date: Fri, 5 Dec 2025 02:11:10 +0000 Subject: [PATCH 18/32] global initializer Signed-off-by: Ming-Wei Shih --- Cargo.lock | 1 + vmm_tests/vmm_tests/Cargo.toml | 1 + .../vmm_tests/tests/tests/multiarch/tpm.rs | 192 ++++++++++-------- 3 files changed, 114 insertions(+), 80 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ee6bb2baf7..0cfd4dbc5d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9459,6 +9459,7 @@ dependencies = [ "nvme_test", "pal", "pal_async", + "parking_lot", "petri", "petri_artifact_resolver_openvmm_known_paths", "petri_artifacts_common", diff --git a/vmm_tests/vmm_tests/Cargo.toml b/vmm_tests/vmm_tests/Cargo.toml index d1198b6244..e6088f7c3f 100644 --- a/vmm_tests/vmm_tests/Cargo.toml +++ b/vmm_tests/vmm_tests/Cargo.toml @@ -48,6 +48,7 @@ vmgs_resources.workspace = true anyhow.workspace = true futures.workspace = true jiff.workspace = true +parking_lot.workspace = true tracing.workspace = true zerocopy.workspace = true static_assertions.workspace = true diff --git a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs index a8ad734131..7db69b0d7d 100644 --- a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs +++ b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs @@ -3,8 +3,6 @@ use anyhow::Context; use anyhow::ensure; -#[cfg(windows)] -use futures::lock::Mutex; use petri::PetriGuestStateLifetime; use petri::PetriVmBuilder; use petri::PetriVmmBackend; @@ -19,8 +17,6 @@ use petri_artifacts_vmm_test::artifacts::guest_tools::TPM_GUEST_TESTS_WINDOWS_X6 use petri_artifacts_vmm_test::artifacts::host_tools::TEST_IGVM_AGENT_RPC_SERVER_WINDOWS_X64; use pipette_client::PipetteClient; use std::path::Path; -#[cfg(windows)] -use std::sync::LazyLock; use vmm_test_macros::openvmm_test; use vmm_test_macros::vmm_test; @@ -30,10 +26,117 @@ const AK_CERT_TOTAL_BYTES: usize = 4096; const TPM_GUEST_TESTS_LINUX_GUEST_PATH: &str = "/tmp/tpm_guest_tests"; const TPM_GUEST_TESTS_WINDOWS_GUEST_PATH: &str = "C:\\tpm_guest_tests.exe"; -// Global mutex to serialize access to the RPC server endpoint. -// Only one test can run the RPC server at a time since it binds to a fixed endpoint. +// Global RPC server that runs once for all tests. +// The RPC server binds to a fixed endpoint, so we share one server across all tests. #[cfg(windows)] -static RPC_SERVER_LOCK: LazyLock> = LazyLock::new(|| Mutex::new(())); +pub mod windows { + use anyhow::Context; + use parking_lot::Mutex; + use std::path::Path; + use std::sync::LazyLock; + + pub static GLOBAL_RPC_SERVER: LazyLock = LazyLock::new(GlobalRpcServer::new); + + #[cfg(windows)] + pub struct GlobalRpcServer { + _child: Mutex>, + _stderr_task: Mutex>>, + } + + #[cfg(windows)] + impl GlobalRpcServer { + fn new() -> Self { + tracing::info!("initializing global RPC server"); + // We'll start the server lazily in ensure_started + Self { + _child: Mutex::new(None), + _stderr_task: Mutex::new(None), + } + } + + pub fn ensure_started(&self, rpc_server_path: &Path) -> anyhow::Result<()> { + use std::io::BufRead; + use std::io::BufReader; + use std::io::Read; + use std::process::Stdio; + + let mut child_lock = self._child.lock(); + + // Check if already started + if let Some(ref mut child) = *child_lock { + // Check if still running + if child.try_wait()?.is_none() { + tracing::debug!("global RPC server already running"); + return Ok(()); + } + tracing::warn!("global RPC server died, restarting"); + } + + tracing::info!("starting global RPC server"); + let (stderr_read, stderr_write) = pal::pipe_pair()?; + let mut rpc_server_child = std::process::Command::new(rpc_server_path) + .stdin(Stdio::null()) + .stdout(Stdio::piped()) + .stderr(stderr_write) + .spawn() + .context("failed to spawn test_igvm_agent_rpc_server")?; + + // Wait for stdout to close to ensure the server is ready + let mut stdout = rpc_server_child + .stdout + .take() + .context("failed to take stdout from RPC server")?; + let mut byte = [0u8]; + let n = stdout + .read(&mut byte) + .context("failed to read from RPC server stdout")?; + if n != 0 { + anyhow::bail!( + "expected RPC server stdout to close (EOF), but read {} bytes", + n + ); + } + drop(stdout); + + tracing::info!("global RPC server is ready"); + + // Spawn a task to read and log stderr from the RPC server + let stderr_task = std::thread::spawn(move || { + let reader = BufReader::new(stderr_read); + for line in reader.lines() { + match line { + Ok(line) => { + tracing::info!(target: "test_igvm_agent_rpc_server", "{}", line) + } + Err(e) => { + tracing::warn!("failed to read RPC server stderr: {}", e); + break; + } + } + } + }); + + *child_lock = Some(rpc_server_child); + *self._stderr_task.lock() = Some(stderr_task); + + Ok(()) + } + } + + #[cfg(windows)] + impl Drop for GlobalRpcServer { + fn drop(&mut self) { + tracing::info!("shutting down global RPC server"); + if let Some(mut child) = self._child.lock().take() { + let _ = child.kill(); + let _ = child.wait(); + } + if let Some(task) = self._stderr_task.lock().take() { + let _ = task.join(); + } + } + } +} fn expected_ak_cert_hex() -> String { use std::fmt::Write as _; @@ -464,83 +567,12 @@ async fn cvm_tpm_guest_tests( config: PetriVmBuilder, extra_deps: (ResolvedArtifact, ResolvedArtifact), ) -> anyhow::Result<()> { - use std::io::BufRead; - use std::io::BufReader; - use std::io::Read; - use std::process::Stdio; - let os_flavor = config.os_flavor(); let (tpm_guest_tests_artifact, rpc_server_artifact) = extra_deps; - // Acquire the RPC server lock to ensure only one test runs the server at a time. - // The RPC server binds to a fixed endpoint, so parallel tests would conflict. - let _rpc_lock = RPC_SERVER_LOCK.lock().await; - tracing::info!("acquired RPC server lock"); - - // Spawn the test IGVM agent RPC server on the host before creating the VM - tracing::info!("launching test_igvm_agent_rpc_server"); + // Ensure the global RPC server is running let rpc_server_path = rpc_server_artifact.get(); - let (stderr_read, stderr_write) = pal::pipe_pair()?; - let mut rpc_server_child = std::process::Command::new(rpc_server_path) - .stdin(Stdio::null()) - .stdout(Stdio::piped()) - .stderr(stderr_write) - .spawn() - .context("failed to spawn test_igvm_agent_rpc_server")?; - - // Wait for stdout to close to ensure the server is ready - let mut stdout = rpc_server_child - .stdout - .take() - .context("failed to take stdout from RPC server")?; - let mut byte = [0u8]; - let n = stdout - .read(&mut byte) - .context("failed to read from RPC server stdout")?; - if n != 0 { - anyhow::bail!( - "expected RPC server stdout to close (EOF), but read {} bytes", - n - ); - } - - // Spawn a task to read and log stderr from the RPC server - let stderr_task = std::thread::spawn(move || { - let reader = BufReader::new(stderr_read); - for line in reader.lines() { - match line { - Ok(line) => tracing::info!(target: "test_igvm_agent_rpc_server", "{}", line), - Err(e) => { - tracing::warn!("failed to read RPC server stderr: {}", e); - break; - } - } - } - }); - - // Create a guard to ensure the RPC server is killed when this function exits - struct RpcServerGuard { - child: std::process::Child, - stderr_task: Option>, - } - impl Drop for RpcServerGuard { - fn drop(&mut self) { - tracing::info!("terminating test_igvm_agent_rpc_server"); - let _ = self.child.kill(); - let _ = self.child.wait(); - // Give the stderr logging thread a moment to finish - if let Some(task) = self.stderr_task.take() { - let _ = task.join(); - } - // Give Windows a moment to fully release the RPC endpoint before - // the next test tries to bind to it. - std::thread::sleep(std::time::Duration::from_millis(50)); - } - } - let _rpc_server_guard = RpcServerGuard { - child: rpc_server_child, - stderr_task: Some(stderr_task), - }; + windows::GLOBAL_RPC_SERVER.ensure_started(rpc_server_path)?; let config = config .with_tpm(true) From 7ff1a6282679a38eb8de6bf21ad485e5387c2444 Mon Sep 17 00:00:00 2001 From: Ming-Wei Shih Date: Fri, 5 Dec 2025 06:39:53 +0000 Subject: [PATCH 19/32] x Signed-off-by: Ming-Wei Shih --- .../src/rpc/server.rs | 62 ++++--------------- .../vmm_tests/tests/tests/multiarch/tpm.rs | 27 ++------ 2 files changed, 17 insertions(+), 72 deletions(-) diff --git a/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs index 10187fa80a..3933257900 100644 --- a/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs +++ b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/server.rs @@ -101,60 +101,22 @@ fn register_protocol_and_interface() -> Result<(), String> { let mut protocol_seq = to_wide(PROTOCOL_SEQUENCE); let mut endpoint = to_wide(ENDPOINT); - // Retry binding to the endpoint, as it may take time for Windows to release - // it after a previous server process exits. - const MAX_RETRIES: u32 = 10; - const RETRY_DELAY_MS: u64 = 100; - let mut last_status = RPC_S_OK; - - for attempt in 1..=MAX_RETRIES { - // SAFETY: Make an FFI call. - let status = unsafe { - RpcServerUseProtseqEpW( - protocol_seq.as_mut_ptr(), - RPC_C_PROTSEQ_MAX_REQS_DEFAULT, - endpoint.as_mut_ptr(), - ptr::null_mut(), - ) - }; - - if status == RPC_S_OK { - tracing::info!("RPC protocol bound successfully"); - break; - } - - last_status = status; - - // Error 1740 (EPT_S_CANT_PERFORM_OP or RPC_S_DUPLICATE_ENDPOINT) means - // the endpoint is already in use or still being released. - if status == 1740 && attempt < MAX_RETRIES { - tracing::warn!( - attempt = attempt, - max_retries = MAX_RETRIES, - "endpoint in use, retrying after delay" - ); - std::thread::sleep(std::time::Duration::from_millis(RETRY_DELAY_MS)); - continue; - } + // SAFETY: Make an FFI call. + let status = unsafe { + RpcServerUseProtseqEpW( + protocol_seq.as_mut_ptr(), + RPC_C_PROTSEQ_MAX_REQS_DEFAULT, + endpoint.as_mut_ptr(), + ptr::null_mut(), + ) + }; - tracing::error!( - status = status, - attempt = attempt, - "RpcServerUseProtseqEpW failed" - ); + if status != RPC_S_OK { + tracing::error!(status = status, "RpcServerUseProtseqEpW failed"); return Err(format!("RpcServerUseProtseqEpW failed: {status}")); } - if last_status != RPC_S_OK { - tracing::error!( - status = last_status, - "failed to bind endpoint after retries" - ); - return Err(format!( - "RpcServerUseProtseqEpW failed after {} retries: {}", - MAX_RETRIES, last_status - )); - } + tracing::info!("RPC protocol bound successfully"); // SAFETY: Make an FFI call. let status = unsafe { diff --git a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs index 7db69b0d7d..1900187cdc 100644 --- a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs +++ b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs @@ -37,20 +37,17 @@ pub mod windows { pub static GLOBAL_RPC_SERVER: LazyLock = LazyLock::new(GlobalRpcServer::new); - #[cfg(windows)] pub struct GlobalRpcServer { + // Keep the Child handle so we can check if it's still running. + // We intentionally don't kill it - it will be cleaned up when the test process exits. _child: Mutex>, - _stderr_task: Mutex>>, } - #[cfg(windows)] impl GlobalRpcServer { fn new() -> Self { tracing::info!("initializing global RPC server"); - // We'll start the server lazily in ensure_started Self { _child: Mutex::new(None), - _stderr_task: Mutex::new(None), } } @@ -100,8 +97,9 @@ pub mod windows { tracing::info!("global RPC server is ready"); - // Spawn a task to read and log stderr from the RPC server - let stderr_task = std::thread::spawn(move || { + // Spawn a detached task to read and log stderr from the RPC server. + // This task will continue running for the lifetime of the test suite. + std::thread::spawn(move || { let reader = BufReader::new(stderr_read); for line in reader.lines() { match line { @@ -117,25 +115,10 @@ pub mod windows { }); *child_lock = Some(rpc_server_child); - *self._stderr_task.lock() = Some(stderr_task); Ok(()) } } - - #[cfg(windows)] - impl Drop for GlobalRpcServer { - fn drop(&mut self) { - tracing::info!("shutting down global RPC server"); - if let Some(mut child) = self._child.lock().take() { - let _ = child.kill(); - let _ = child.wait(); - } - if let Some(task) = self._stderr_task.lock().take() { - let _ = task.join(); - } - } - } } fn expected_ak_cert_hex() -> String { From c055cdde1976a94b2a34106a2b0244064bdf0e58 Mon Sep 17 00:00:00 2001 From: Ming-Wei Shih Date: Fri, 5 Dec 2025 19:42:59 +0000 Subject: [PATCH 20/32] try resolving leak Signed-off-by: Ming-Wei Shih --- .../vmm_tests/tests/tests/multiarch/tpm.rs | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs index 1900187cdc..a3a3000929 100644 --- a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs +++ b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs @@ -38,16 +38,16 @@ pub mod windows { pub static GLOBAL_RPC_SERVER: LazyLock = LazyLock::new(GlobalRpcServer::new); pub struct GlobalRpcServer { - // Keep the Child handle so we can check if it's still running. - // We intentionally don't kill it - it will be cleaned up when the test process exits. - _child: Mutex>, + // We track whether the server has been started to avoid starting it multiple times. + // We intentionally don't keep a handle - the process will be cleaned up when the test exits. + _started: Mutex, } impl GlobalRpcServer { fn new() -> Self { tracing::info!("initializing global RPC server"); Self { - _child: Mutex::new(None), + _started: Mutex::new(false), } } @@ -57,16 +57,12 @@ pub mod windows { use std::io::Read; use std::process::Stdio; - let mut child_lock = self._child.lock(); + let mut started = self._started.lock(); - // Check if already started - if let Some(ref mut child) = *child_lock { - // Check if still running - if child.try_wait()?.is_none() { - tracing::debug!("global RPC server already running"); - return Ok(()); - } - tracing::warn!("global RPC server died, restarting"); + // Only start once per test run + if *started { + tracing::debug!("global RPC server already started"); + return Ok(()); } tracing::info!("starting global RPC server"); @@ -114,7 +110,11 @@ pub mod windows { } }); - *child_lock = Some(rpc_server_child); + // Drop the Child handle so nextest doesn't track it as a leaked process. + // The OS will clean up the process when the test runner exits. + drop(rpc_server_child); + + *started = true; Ok(()) } From 2044482091dff37403dec2f4bae37fdae89996d2 Mon Sep 17 00:00:00 2001 From: Ming-Wei Shih Date: Fri, 5 Dec 2025 23:44:26 +0000 Subject: [PATCH 21/32] test Signed-off-by: Ming-Wei Shih --- support/pal/src/windows/job.rs | 30 +++++++++++++++++ .../vmm_tests/tests/tests/multiarch/tpm.rs | 33 +++++++++++++++++-- 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/support/pal/src/windows/job.rs b/support/pal/src/windows/job.rs index d0f41fa832..42fa85f822 100644 --- a/support/pal/src/windows/job.rs +++ b/support/pal/src/windows/job.rs @@ -56,6 +56,36 @@ impl Job { } Ok(()) } + + /// Assigns a process to this job object by process ID. + pub fn assign_process(&self, process_id: u32) -> io::Result<()> { + // SAFETY: `OpenProcess` returns an owned handle or null. + let process_handle = unsafe { + winapi::um::processthreadsapi::OpenProcess( + winapi::um::winnt::PROCESS_SET_QUOTA | winapi::um::winnt::PROCESS_TERMINATE, + 0, + process_id, + ) + }; + if process_handle.is_null() { + return Err(io::Error::last_os_error()); + } + + // SAFETY: We just created this handle successfully. + let process_handle = unsafe { OwnedHandle::from_raw_handle(process_handle) }; + + // SAFETY: `AssignProcessToJobObject` is safe to call with valid handles. + let r = unsafe { + winapi::um::jobapi2::AssignProcessToJobObject( + self.0.as_raw_handle(), + process_handle.as_raw_handle(), + ) + }; + if r == 0 { + return Err(io::Error::last_os_error()); + } + Ok(()) + } } impl AsHandle for Job { diff --git a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs index a3a3000929..b951cadd46 100644 --- a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs +++ b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs @@ -31,6 +31,7 @@ const TPM_GUEST_TESTS_WINDOWS_GUEST_PATH: &str = "C:\\tpm_guest_tests.exe"; #[cfg(windows)] pub mod windows { use anyhow::Context; + use pal::windows::job::Job; use parking_lot::Mutex; use std::path::Path; use std::sync::LazyLock; @@ -39,8 +40,9 @@ pub mod windows { pub struct GlobalRpcServer { // We track whether the server has been started to avoid starting it multiple times. - // We intentionally don't keep a handle - the process will be cleaned up when the test exits. _started: Mutex, + // Keep a handle to the job object to ensure cleanup when tests exit. + _job: Mutex>, } impl GlobalRpcServer { @@ -48,6 +50,7 @@ pub mod windows { tracing::info!("initializing global RPC server"); Self { _started: Mutex::new(false), + _job: Mutex::new(None), } } @@ -55,6 +58,7 @@ pub mod windows { use std::io::BufRead; use std::io::BufReader; use std::io::Read; + use std::os::windows::process::CommandExt; use std::process::Stdio; let mut started = self._started.lock(); @@ -66,14 +70,33 @@ pub mod windows { } tracing::info!("starting global RPC server"); + + // Create a job object that will kill all assigned processes when closed. + // This ensures the RPC server is cleaned up when the test runner exits. + let job = Job::new().context("failed to create job object")?; + job.set_terminate_on_close() + .context("failed to configure job object")?; + let (stderr_read, stderr_write) = pal::pipe_pair()?; + + // Spawn the RPC server as a detached process using CREATE_NEW_PROCESS_GROUP + // and CREATE_BREAKAWAY_FROM_JOB flags. This prevents nextest from tracking + // it as a child process. + const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200; + const CREATE_BREAKAWAY_FROM_JOB: u32 = 0x01000000; + let mut rpc_server_child = std::process::Command::new(rpc_server_path) .stdin(Stdio::null()) .stdout(Stdio::piped()) .stderr(stderr_write) + .creation_flags(CREATE_NEW_PROCESS_GROUP | CREATE_BREAKAWAY_FROM_JOB) .spawn() .context("failed to spawn test_igvm_agent_rpc_server")?; + // Assign the RPC server process to our job object so it gets cleaned up. + job.assign_process(rpc_server_child.id()) + .context("failed to assign RPC server to job object")?; + // Wait for stdout to close to ensure the server is ready let mut stdout = rpc_server_child .stdout @@ -110,10 +133,14 @@ pub mod windows { } }); - // Drop the Child handle so nextest doesn't track it as a leaked process. - // The OS will clean up the process when the test runner exits. + // Drop the Child handle - the process was spawned with breakaway flags + // so it won't be tracked by nextest's job object. drop(rpc_server_child); + // Store the job handle so it stays alive until GlobalRpcServer is dropped. + // When the job is dropped, all processes in the job will be terminated. + *self._job.lock() = Some(job); + *started = true; Ok(()) From a8ba3d3f139b541c0a63932a7cc0fea709741c75 Mon Sep 17 00:00:00 2001 From: Ming-Wei Shih Date: Sat, 6 Dec 2025 07:41:22 +0000 Subject: [PATCH 22/32] try Signed-off-by: Ming-Wei Shih --- vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs index b951cadd46..2413978af6 100644 --- a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs +++ b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs @@ -79,17 +79,13 @@ pub mod windows { let (stderr_read, stderr_write) = pal::pipe_pair()?; - // Spawn the RPC server as a detached process using CREATE_NEW_PROCESS_GROUP - // and CREATE_BREAKAWAY_FROM_JOB flags. This prevents nextest from tracking - // it as a child process. const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200; - const CREATE_BREAKAWAY_FROM_JOB: u32 = 0x01000000; let mut rpc_server_child = std::process::Command::new(rpc_server_path) .stdin(Stdio::null()) .stdout(Stdio::piped()) .stderr(stderr_write) - .creation_flags(CREATE_NEW_PROCESS_GROUP | CREATE_BREAKAWAY_FROM_JOB) + .creation_flags(CREATE_NEW_PROCESS_GROUP) .spawn() .context("failed to spawn test_igvm_agent_rpc_server")?; From 18ab177ce61097bb08271ea03e50c3015c7cbe0a Mon Sep 17 00:00:00 2001 From: Ming-Wei Shih Date: Sat, 6 Dec 2025 08:41:49 +0000 Subject: [PATCH 23/32] x Signed-off-by: Ming-Wei Shih --- vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs index 2413978af6..809b3826f5 100644 --- a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs +++ b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs @@ -129,8 +129,8 @@ pub mod windows { } }); - // Drop the Child handle - the process was spawned with breakaway flags - // so it won't be tracked by nextest's job object. + // Drop the Child handle. The process is managed by our job object and will + // be automatically terminated when the job is closed (test runner exits). drop(rpc_server_child); // Store the job handle so it stays alive until GlobalRpcServer is dropped. From 374ecc93eb04b8903d4209dde75fb3c9a0ad7a9b Mon Sep 17 00:00:00 2001 From: Ming-Wei Shih Date: Mon, 8 Dec 2025 19:23:13 +0000 Subject: [PATCH 24/32] comments Signed-off-by: Ming-Wei Shih --- .../src/rpc/handlers.rs | 41 +++++++++++-------- .../vmm_tests/tests/tests/multiarch/tpm.rs | 34 ++++++++------- 2 files changed, 44 insertions(+), 31 deletions(-) diff --git a/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/handlers.rs b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/handlers.rs index ac30a877a2..5390199ecc 100644 --- a/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/handlers.rs +++ b/vm/devices/get/test_igvm_agent_rpc_server/src/rpc/handlers.rs @@ -97,9 +97,23 @@ fn write_response_size(ptr: *mut u32, value: u32) -> Result<(), HRESULT> { } } -fn copy_to_buffer(buffer: &[u8], dest: *mut u8) { +/// Copies `buffer` to the destination pointer `dest`. +/// +/// # Safety Requirements +/// The caller must ensure: +/// - `dest` is valid for writes of `buffer.len()` bytes +/// - `dest` does not overlap with `buffer` +fn copy_to_buffer(buffer: &[u8], dest: *mut u8, dest_size: usize) { + debug_assert!( + buffer.len() <= dest_size, + "buffer length {} exceeds destination size {}", + buffer.len(), + dest_size + ); + debug_assert!(!dest.is_null() || buffer.is_empty()); + if !buffer.is_empty() { - // SAFETY: memory access + // SAFETY: Caller guarantees dest has sufficient space (verified by debug_assert above). unsafe { ptr::copy_nonoverlapping(buffer.as_ptr(), dest, buffer.len()); } @@ -126,34 +140,27 @@ fn read_utf16(ptr: *const u16) -> Option { return None; } - // SAFETY: memory access - std::panic::catch_unwind(|| unsafe { + // SAFETY: The caller (RPC runtime) is responsible for providing valid pointers. + // This is a test server, so we trust the RPC infrastructure to provide valid data. + unsafe { let mut len = 0usize; // Scan for null terminator with bounds checking while len < MAX_LEN { - // Use read_volatile to prevent compiler optimizations that might - // assume the pointer is valid, and to ensure we actually read - // the memory (which will fault if invalid) - let char_val = ptr::read_volatile(ptr.add(len)); - if char_val == 0 { + if *ptr.add(len) == 0 { break; } len += 1; } - // If we hit MAX_LEN without finding a null terminator, - // the string is too long or the pointer is invalid + // If we hit MAX_LEN without finding a null terminator, truncate if len == MAX_LEN { - return None; + len = MAX_LEN - 1; } - // Now that we know the length, create a slice and convert to String let slice = slice::from_raw_parts(ptr, len); String::from_utf16(slice).ok() - }) - .ok() - .flatten() + } } /// Entry point that services `RpcIGVmAttest` requests for the test agent. @@ -235,7 +242,7 @@ pub extern "system" fn rpc_igvm_attest( tracing::error!("response buffer pointer is null while payload_len > 0"); return E_INVALIDARG; } - copy_to_buffer(&payload, response); + copy_to_buffer(&payload, response, response_buffer_size as usize); } if let Err(err) = write_response_size(response_written_size, payload_len) { diff --git a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs index 809b3826f5..f0ff13e509 100644 --- a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs +++ b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs @@ -112,22 +112,28 @@ pub mod windows { tracing::info!("global RPC server is ready"); - // Spawn a detached task to read and log stderr from the RPC server. - // This task will continue running for the lifetime of the test suite. - std::thread::spawn(move || { - let reader = BufReader::new(stderr_read); - for line in reader.lines() { - match line { - Ok(line) => { - tracing::info!(target: "test_igvm_agent_rpc_server", "{}", line) - } - Err(e) => { - tracing::warn!("failed to read RPC server stderr: {}", e); - break; + // Spawn a named thread to read and log stderr from the RPC server. + // When the RPC server exits (either normally or when killed by the job object), + // the pipe will be closed and reader.lines() will return None, causing the + // thread to exit naturally. No explicit cleanup is needed. + std::thread::Builder::new() + .name("rpc-server-stderr".to_string()) + .spawn(move || { + let reader = BufReader::new(stderr_read); + for line in reader.lines() { + match line { + Ok(line) => { + tracing::info!(target: "test_igvm_agent_rpc_server", "{}", line) + } + Err(e) => { + tracing::debug!("RPC server stderr pipe closed: {}", e); + break; + } } } - } - }); + tracing::debug!("RPC server stderr reader thread exiting"); + }) + .expect("failed to spawn stderr reader thread"); // Drop the Child handle. The process is managed by our job object and will // be automatically terminated when the job is closed (test runner exits). From 486dd514f5015eda67d9cef3429ac88c5a0c1f19 Mon Sep 17 00:00:00 2001 From: Ming-Wei Shih Date: Wed, 10 Dec 2025 00:36:41 +0000 Subject: [PATCH 25/32] job does not really work Signed-off-by: Ming-Wei Shih --- support/pal/src/windows/job.rs | 30 -------- .../get/test_igvm_agent_rpc_server/build.rs | 2 +- .../vmm_tests/tests/tests/multiarch/tpm.rs | 74 +++++++++++-------- 3 files changed, 46 insertions(+), 60 deletions(-) diff --git a/support/pal/src/windows/job.rs b/support/pal/src/windows/job.rs index 42fa85f822..d0f41fa832 100644 --- a/support/pal/src/windows/job.rs +++ b/support/pal/src/windows/job.rs @@ -56,36 +56,6 @@ impl Job { } Ok(()) } - - /// Assigns a process to this job object by process ID. - pub fn assign_process(&self, process_id: u32) -> io::Result<()> { - // SAFETY: `OpenProcess` returns an owned handle or null. - let process_handle = unsafe { - winapi::um::processthreadsapi::OpenProcess( - winapi::um::winnt::PROCESS_SET_QUOTA | winapi::um::winnt::PROCESS_TERMINATE, - 0, - process_id, - ) - }; - if process_handle.is_null() { - return Err(io::Error::last_os_error()); - } - - // SAFETY: We just created this handle successfully. - let process_handle = unsafe { OwnedHandle::from_raw_handle(process_handle) }; - - // SAFETY: `AssignProcessToJobObject` is safe to call with valid handles. - let r = unsafe { - winapi::um::jobapi2::AssignProcessToJobObject( - self.0.as_raw_handle(), - process_handle.as_raw_handle(), - ) - }; - if r == 0 { - return Err(io::Error::last_os_error()); - } - Ok(()) - } } impl AsHandle for Job { diff --git a/vm/devices/get/test_igvm_agent_rpc_server/build.rs b/vm/devices/get/test_igvm_agent_rpc_server/build.rs index fc3240b76b..43b42ac992 100644 --- a/vm/devices/get/test_igvm_agent_rpc_server/build.rs +++ b/vm/devices/get/test_igvm_agent_rpc_server/build.rs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -//! Build script that compiles Windows RPC stubs for the IGVM agent façade. +//! Build script that compiles the Windows RPC server for test IGVM agent. use serde_json::Value; use std::env; diff --git a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs index f0ff13e509..f25255f13b 100644 --- a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs +++ b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs @@ -31,7 +31,6 @@ const TPM_GUEST_TESTS_WINDOWS_GUEST_PATH: &str = "C:\\tpm_guest_tests.exe"; #[cfg(windows)] pub mod windows { use anyhow::Context; - use pal::windows::job::Job; use parking_lot::Mutex; use std::path::Path; use std::sync::LazyLock; @@ -39,10 +38,8 @@ pub mod windows { pub static GLOBAL_RPC_SERVER: LazyLock = LazyLock::new(GlobalRpcServer::new); pub struct GlobalRpcServer { - // We track whether the server has been started to avoid starting it multiple times. + // We track whether we've attempted to start the server in this process. _started: Mutex, - // Keep a handle to the job object to ensure cleanup when tests exit. - _job: Mutex>, } impl GlobalRpcServer { @@ -50,7 +47,6 @@ pub mod windows { tracing::info!("initializing global RPC server"); Self { _started: Mutex::new(false), - _job: Mutex::new(None), } } @@ -61,24 +57,24 @@ pub mod windows { use std::os::windows::process::CommandExt; use std::process::Stdio; + // Note: nextest runs each test in a separate process, so this lock only + // protects against concurrent calls within the same test process. + // Multiple test processes may race to start the server, but only one + // will succeed - the others will see the server exit with error 1740 + // (endpoint already in use) and will use the existing server. let mut started = self._started.lock(); - // Only start once per test run if *started { - tracing::debug!("global RPC server already started"); + tracing::debug!("global RPC server already started in this process"); return Ok(()); } - tracing::info!("starting global RPC server"); - - // Create a job object that will kill all assigned processes when closed. - // This ensures the RPC server is cleaned up when the test runner exits. - let job = Job::new().context("failed to create job object")?; - job.set_terminate_on_close() - .context("failed to configure job object")?; + tracing::info!("attempting to start global RPC server"); let (stderr_read, stderr_write) = pal::pipe_pair()?; + // Spawn the RPC server in a new process group so it doesn't receive + // console signals from the test process. const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200; let mut rpc_server_child = std::process::Command::new(rpc_server_path) @@ -89,11 +85,9 @@ pub mod windows { .spawn() .context("failed to spawn test_igvm_agent_rpc_server")?; - // Assign the RPC server process to our job object so it gets cleaned up. - job.assign_process(rpc_server_child.id()) - .context("failed to assign RPC server to job object")?; - - // Wait for stdout to close to ensure the server is ready + // Wait for stdout to close - this signals either: + // 1. The server is ready and accepting connections + // 2. The server failed to start (e.g., endpoint already in use) let mut stdout = rpc_server_child .stdout .take() @@ -110,12 +104,38 @@ pub mod windows { } drop(stdout); - tracing::info!("global RPC server is ready"); + // Check if the server started successfully or failed. + // Give it a moment to exit if it's going to fail. + std::thread::sleep(std::time::Duration::from_millis(50)); + + match rpc_server_child.try_wait()? { + Some(status) => { + // Server exited - likely because another process already started it. + // This is expected in nextest where tests run in parallel processes. + tracing::info!( + exit_code = ?status.code(), + "RPC server exited (another test process likely started it first)" + ); + *started = true; + return Ok(()); + } + None => { + // Server is still running - we successfully started it. + // Note: We intentionally don't use a job object here because nextest + // runs tests in separate processes. If we used a job object, the server + // would be killed when THIS test process exits, even if other test + // processes are still using it. Instead, we let the server run as an + // orphan - it will be cleaned up by: + // - nextest's own job object (if it uses one) + // - The CI runner's cleanup + // - Manual cleanup if running locally + tracing::info!("global RPC server started successfully"); + } + } // Spawn a named thread to read and log stderr from the RPC server. - // When the RPC server exits (either normally or when killed by the job object), - // the pipe will be closed and reader.lines() will return None, causing the - // thread to exit naturally. No explicit cleanup is needed. + // When the RPC server exits, the pipe will be closed and reader.lines() + // will return None, causing the thread to exit naturally. std::thread::Builder::new() .name("rpc-server-stderr".to_string()) .spawn(move || { @@ -135,14 +155,10 @@ pub mod windows { }) .expect("failed to spawn stderr reader thread"); - // Drop the Child handle. The process is managed by our job object and will - // be automatically terminated when the job is closed (test runner exits). + // Drop the Child handle. We intentionally don't keep it or use a job object + // to avoid killing the server when this test process exits. drop(rpc_server_child); - // Store the job handle so it stays alive until GlobalRpcServer is dropped. - // When the job is dropped, all processes in the job will be terminated. - *self._job.lock() = Some(job); - *started = true; Ok(()) From 571fa53ef7350108edcc3b583323090acee1e428 Mon Sep 17 00:00:00 2001 From: Ming-Wei Shih Date: Wed, 10 Dec 2025 07:43:10 +0000 Subject: [PATCH 26/32] debug Signed-off-by: Ming-Wei Shih --- .../vmm_tests/tests/tests/multiarch/tpm.rs | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs index f25255f13b..53abb4bdba 100644 --- a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs +++ b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs @@ -164,6 +164,46 @@ pub mod windows { Ok(()) } } + + /// Checks if any process with the given executable name is running. + /// Used for debugging to verify the RPC server is still alive. + pub fn is_process_running(exe_name: &str) -> bool { + use std::process::Command; + + // Use tasklist to check if the process is running + let output = Command::new("tasklist") + .args(["/FI", &format!("IMAGENAME eq {}", exe_name), "/NH"]) + .output(); + + match output { + Ok(output) => { + let stdout = String::from_utf8_lossy(&output.stdout); + // tasklist returns the process name if found, or "INFO: No tasks" if not + stdout.contains(exe_name) + } + Err(e) => { + tracing::warn!("failed to run tasklist: {}", e); + false + } + } + } + + /// Logs diagnostic information about the RPC server process status. + pub fn check_rpc_server_status() { + const RPC_SERVER_EXE: &str = "test_igvm_agent_rpc_server.exe"; + + if is_process_running(RPC_SERVER_EXE) { + tracing::info!( + exe = RPC_SERVER_EXE, + "RPC server health check: process is still running" + ); + } else { + tracing::warn!( + exe = RPC_SERVER_EXE, + "RPC server health check: process is NOT running - this may cause test failures!" + ); + } + } } fn expected_ak_cert_hex() -> String { @@ -647,5 +687,9 @@ async fn cvm_tpm_guest_tests( agent.power_off().await?; vm.wait_for_clean_teardown().await?; + // Debug: Check if the RPC server is still running at the end of the test. + // This helps diagnose flaky tests that may be caused by the server dying unexpectedly. + windows::check_rpc_server_status(); + Ok(()) } From c39c0b7f4268f8cd6dfb7d2bf0e1c68f55dee039 Mon Sep 17 00:00:00 2001 From: Ming-Wei Shih Date: Tue, 16 Dec 2025 00:52:52 +0000 Subject: [PATCH 27/32] debug Signed-off-by: Ming-Wei Shih --- vm/devices/get/guest_emulation_device/src/lib.rs | 7 ++++--- vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs | 9 +++++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/vm/devices/get/guest_emulation_device/src/lib.rs b/vm/devices/get/guest_emulation_device/src/lib.rs index 720a7c4276..be1cb09c0c 100644 --- a/vm/devices/get/guest_emulation_device/src/lib.rs +++ b/vm/devices/get/guest_emulation_device/src/lib.rs @@ -16,6 +16,10 @@ pub mod resolver; #[cfg(feature = "test_utilities")] pub mod test_utilities; +pub use test_igvm_agent_lib::IgvmAgentAction; +pub use test_igvm_agent_lib::IgvmAgentTestPlan; +pub use test_igvm_agent_lib::IgvmAgentTestSetting; + use async_trait::async_trait; use core::mem::size_of; use disk_backend::Disk; @@ -62,9 +66,6 @@ use power_resources::PowerRequestClient; use scsi_buffers::OwnedRequestBuffers; use std::io::IoSlice; use task_control::StopTask; -pub use test_igvm_agent_lib::IgvmAgentAction; -pub use test_igvm_agent_lib::IgvmAgentTestPlan; -pub use test_igvm_agent_lib::IgvmAgentTestSetting; use test_igvm_agent_lib::TestIgvmAgent; use thiserror::Error; use video_core::FramebufferControl; diff --git a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs index 53abb4bdba..b3ddbac86d 100644 --- a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs +++ b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs @@ -647,6 +647,10 @@ async fn cvm_tpm_guest_tests( .with_tpm_state_persistence(true) .with_guest_state_lifetime(PetriGuestStateLifetime::Disk); + // Debug: Check if the RPC server is still running at the end of the test. + // This helps diagnose flaky tests that may be caused by the server dying unexpectedly. + windows::check_rpc_server_status(); + let (vm, agent) = config.run().await?; let guest_binary_path = match os_flavor { @@ -684,12 +688,13 @@ async fn cvm_tpm_guest_tests( format!("{report_output}") ); - agent.power_off().await?; - vm.wait_for_clean_teardown().await?; // Debug: Check if the RPC server is still running at the end of the test. // This helps diagnose flaky tests that may be caused by the server dying unexpectedly. windows::check_rpc_server_status(); + agent.power_off().await?; + vm.wait_for_clean_teardown().await?; + Ok(()) } From 5d1815f15e276da230e4218e82e2f9da34daa1e3 Mon Sep 17 00:00:00 2001 From: Ming-Wei Shih Date: Tue, 16 Dec 2025 02:07:17 +0000 Subject: [PATCH 28/32] test Signed-off-by: Ming-Wei Shih --- vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs index b3ddbac86d..a7cb889d9c 100644 --- a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs +++ b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs @@ -178,8 +178,11 @@ pub mod windows { match output { Ok(output) => { let stdout = String::from_utf8_lossy(&output.stdout); - // tasklist returns the process name if found, or "INFO: No tasks" if not - stdout.contains(exe_name) + // tasklist returns "INFO: No tasks are running..." if no match found. + // If a process is found, it shows the process info without "INFO: No tasks". + // Note: tasklist truncates long process names in output, so we can't + // reliably check for the exact name in the output. + !stdout.contains("INFO: No tasks") } Err(e) => { tracing::warn!("failed to run tasklist: {}", e); @@ -688,7 +691,6 @@ async fn cvm_tpm_guest_tests( format!("{report_output}") ); - // Debug: Check if the RPC server is still running at the end of the test. // This helps diagnose flaky tests that may be caused by the server dying unexpectedly. windows::check_rpc_server_status(); From b751b5fa4ebd33d8b2f80713d8e6900a37c93fc9 Mon Sep 17 00:00:00 2001 From: Ming-Wei Shih Date: Tue, 16 Dec 2025 18:52:35 +0000 Subject: [PATCH 29/32] order Signed-off-by: Ming-Wei Shih --- vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs index a7cb889d9c..9f191741c8 100644 --- a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs +++ b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs @@ -666,6 +666,10 @@ async fn cvm_tpm_guest_tests( TpmGuestTests::send_tpm_guest_tests(&agent, host_binary_path, guest_binary_path, os_flavor) .await?; + // Debug: Check if the RPC server is still running at the end of the test. + // This helps diagnose flaky tests that may be caused by the server dying unexpectedly. + windows::check_rpc_server_status(); + // Verify AK cert with the test IGVM agent RPC server let expected_hex = expected_ak_cert_hex(); let ak_cert_output = tpm_guest_tests @@ -691,10 +695,6 @@ async fn cvm_tpm_guest_tests( format!("{report_output}") ); - // Debug: Check if the RPC server is still running at the end of the test. - // This helps diagnose flaky tests that may be caused by the server dying unexpectedly. - windows::check_rpc_server_status(); - agent.power_off().await?; vm.wait_for_clean_teardown().await?; From a5cc8b09a8ce643453ea212bf75629cc1b64ce95 Mon Sep 17 00:00:00 2001 From: Ming-Wei Shih Date: Wed, 17 Dec 2025 00:33:30 +0000 Subject: [PATCH 30/32] test flowey change Signed-off-by: Ming-Wei Shih --- Cargo.lock | 1 - ...sume_and_test_nextest_vmm_tests_archive.rs | 12 ++ flowey/flowey_lib_hvlite/src/lib.rs | 1 + .../src/run_test_igvm_agent_rpc_server.rs | 137 ++++++++++++ vmm_tests/vmm_tests/Cargo.toml | 1 - .../vmm_tests/tests/tests/multiarch/tpm.rs | 201 +++--------------- 6 files changed, 179 insertions(+), 174 deletions(-) create mode 100644 flowey/flowey_lib_hvlite/src/run_test_igvm_agent_rpc_server.rs diff --git a/Cargo.lock b/Cargo.lock index 7ef89e0ee1..7edcab89c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9461,7 +9461,6 @@ dependencies = [ "nvme_test", "pal", "pal_async", - "parking_lot", "petri", "petri_artifact_resolver_openvmm_known_paths", "petri_artifacts_common", diff --git a/flowey/flowey_lib_hvlite/src/_jobs/consume_and_test_nextest_vmm_tests_archive.rs b/flowey/flowey_lib_hvlite/src/_jobs/consume_and_test_nextest_vmm_tests_archive.rs index 2bd4bc41a4..ae95512e42 100644 --- a/flowey/flowey_lib_hvlite/src/_jobs/consume_and_test_nextest_vmm_tests_archive.rs +++ b/flowey/flowey_lib_hvlite/src/_jobs/consume_and_test_nextest_vmm_tests_archive.rs @@ -77,6 +77,7 @@ impl SimpleFlowNode for Node { ctx.import::(); ctx.import::(); ctx.import::(); + ctx.import::(); ctx.import::(); ctx.import::(); } @@ -188,6 +189,17 @@ impl SimpleFlowNode for Node { use_relative_paths: false, }); + // Start the test_igvm_agent_rpc_server before running tests. + // This must happen after init_vmm_tests_env which copies the binary. + // The server runs in the background for the duration of the test run. + #[cfg(windows)] + pre_run_deps.push( + ctx.reqv(|done| crate::run_test_igvm_agent_rpc_server::Request { + env: extra_env.clone(), + done, + }), + ); + if needs_prep_run { pre_run_deps.push(ctx.reqv(|done| crate::run_prep_steps::Request { prep_steps: register_prep_steps.expect("Test run indicated prep_steps was needed but built prep_steps binary was not given"), diff --git a/flowey/flowey_lib_hvlite/src/lib.rs b/flowey/flowey_lib_hvlite/src/lib.rs index f08ab9cabe..e82237840b 100644 --- a/flowey/flowey_lib_hvlite/src/lib.rs +++ b/flowey/flowey_lib_hvlite/src/lib.rs @@ -57,5 +57,6 @@ pub mod run_cargo_nextest_run; pub mod run_igvmfilegen; pub mod run_prep_steps; pub mod run_split_debug_info; +pub mod run_test_igvm_agent_rpc_server; pub mod test_nextest_unit_tests_archive; pub mod test_nextest_vmm_tests_archive; diff --git a/flowey/flowey_lib_hvlite/src/run_test_igvm_agent_rpc_server.rs b/flowey/flowey_lib_hvlite/src/run_test_igvm_agent_rpc_server.rs new file mode 100644 index 0000000000..06d88ef796 --- /dev/null +++ b/flowey/flowey_lib_hvlite/src/run_test_igvm_agent_rpc_server.rs @@ -0,0 +1,137 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! Start the test_igvm_agent_rpc_server before running VMM tests. +//! +//! The RPC server provides a fake IGVM agent attestation endpoint for +//! CVM TPM guest tests. It must be running before the tests start and +//! stay alive for the duration of the test run. +//! +//! This node starts the server from the test content directory (where +//! init_vmm_tests_env copies the binary) and redirects output to a log file. + +use flowey::node::prelude::*; +use std::collections::BTreeMap; + +flowey_request! { + pub struct Request { + /// Environment variables from init_vmm_tests_env (contains VMM_TESTS_CONTENT_DIR and TEST_OUTPUT_PATH) + pub env: ReadVar>, + /// Completion indicator - signals that the server is ready + pub done: WriteVar, + } +} + +new_simple_flow_node!(struct Node); + +impl SimpleFlowNode for Node { + type Request = Request; + + fn imports(_ctx: &mut ImportCtx<'_>) {} + + fn process_request(request: Self::Request, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> { + let Request { env, done } = request; + + ctx.emit_rust_step("starting test_igvm_agent_rpc_server", |ctx| { + let env = env.claim(ctx); + done.claim(ctx); + move |rt| { + let env = rt.read(env); + + // Only run on Windows - the RPC server is Windows-only + #[cfg(not(windows))] + { + let _ = env; + log::info!("test_igvm_agent_rpc_server is Windows-only, skipping"); + return Ok(()); + } + + #[cfg(windows)] + { + use std::os::windows::process::CommandExt; + + // Get paths from environment + let test_content_dir = env + .get("VMM_TESTS_CONTENT_DIR") + .context("VMM_TESTS_CONTENT_DIR not set")?; + let test_output_path = env + .get("TEST_OUTPUT_PATH") + .context("TEST_OUTPUT_PATH not set")?; + + let exe = Path::new(test_content_dir) + .join("test_igvm_agent_rpc_server.exe"); + + if !exe.exists() { + log::info!( + "test_igvm_agent_rpc_server.exe not found at {}, skipping", + exe.display() + ); + return Ok(()); + } + + // Create log file for server output + let log_file_path = Path::new(test_output_path) + .join("test_igvm_agent_rpc_server.log"); + let log_file = std::fs::File::create(&log_file_path)?; + let log_file_stderr = log_file.try_clone()?; + + log::info!( + "starting test_igvm_agent_rpc_server from {}, logs at: {}", + exe.display(), + log_file_path.display() + ); + + // Spawn the RPC server as a background process. + // Use CREATE_NEW_PROCESS_GROUP so it doesn't receive console signals. + const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200; + + let mut child = std::process::Command::new(&exe) + .stdin(std::process::Stdio::null()) + .stdout(log_file) + .stderr(log_file_stderr) + .creation_flags(CREATE_NEW_PROCESS_GROUP) + .spawn() + .with_context(|| { + format!( + "failed to spawn test_igvm_agent_rpc_server: {}", + exe.display() + ) + })?; + + // Give the server a moment to start up and bind to the RPC endpoint. + // The server closes stdout when it's ready, but since we redirected + // stdout to a file, we can't detect that. Instead, we poll briefly. + std::thread::sleep(std::time::Duration::from_millis(500)); + + // Check if the server is still running + match child.try_wait()? { + Some(status) => { + // Server exited - this is an error unless endpoint was already in use + anyhow::bail!( + "test_igvm_agent_rpc_server exited unexpectedly with status: {:?}. \ + Check logs at: {}", + status.code(), + log_file_path.display() + ); + } + None => { + log::info!( + "test_igvm_agent_rpc_server started successfully (pid: {})", + child.id() + ); + } + } + + // Don't wait on the child - let it run in the background. + // The process will be cleaned up when the CI job ends. + // We intentionally drop the Child handle without waiting. + drop(child); + + Ok(()) + } + } + }); + + Ok(()) + } +} diff --git a/vmm_tests/vmm_tests/Cargo.toml b/vmm_tests/vmm_tests/Cargo.toml index e6088f7c3f..d1198b6244 100644 --- a/vmm_tests/vmm_tests/Cargo.toml +++ b/vmm_tests/vmm_tests/Cargo.toml @@ -48,7 +48,6 @@ vmgs_resources.workspace = true anyhow.workspace = true futures.workspace = true jiff.workspace = true -parking_lot.workspace = true tracing.workspace = true zerocopy.workspace = true static_assertions.workspace = true diff --git a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs index 9f191741c8..96724d8b47 100644 --- a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs +++ b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs @@ -13,8 +13,6 @@ use petri::pipette::cmd; use petri_artifacts_common::tags::OsFlavor; use petri_artifacts_vmm_test::artifacts::guest_tools::TPM_GUEST_TESTS_LINUX_X64; use petri_artifacts_vmm_test::artifacts::guest_tools::TPM_GUEST_TESTS_WINDOWS_X64; -#[cfg(windows)] -use petri_artifacts_vmm_test::artifacts::host_tools::TEST_IGVM_AGENT_RPC_SERVER_WINDOWS_X64; use pipette_client::PipetteClient; use std::path::Path; use vmm_test_macros::openvmm_test; @@ -26,147 +24,12 @@ const AK_CERT_TOTAL_BYTES: usize = 4096; const TPM_GUEST_TESTS_LINUX_GUEST_PATH: &str = "/tmp/tpm_guest_tests"; const TPM_GUEST_TESTS_WINDOWS_GUEST_PATH: &str = "C:\\tpm_guest_tests.exe"; -// Global RPC server that runs once for all tests. -// The RPC server binds to a fixed endpoint, so we share one server across all tests. +// Utilities for checking if the RPC server is running. +// In CI, the RPC server is started by flowey before tests run. +// For local development, the server can be started manually. #[cfg(windows)] pub mod windows { - use anyhow::Context; - use parking_lot::Mutex; - use std::path::Path; - use std::sync::LazyLock; - - pub static GLOBAL_RPC_SERVER: LazyLock = LazyLock::new(GlobalRpcServer::new); - - pub struct GlobalRpcServer { - // We track whether we've attempted to start the server in this process. - _started: Mutex, - } - - impl GlobalRpcServer { - fn new() -> Self { - tracing::info!("initializing global RPC server"); - Self { - _started: Mutex::new(false), - } - } - - pub fn ensure_started(&self, rpc_server_path: &Path) -> anyhow::Result<()> { - use std::io::BufRead; - use std::io::BufReader; - use std::io::Read; - use std::os::windows::process::CommandExt; - use std::process::Stdio; - - // Note: nextest runs each test in a separate process, so this lock only - // protects against concurrent calls within the same test process. - // Multiple test processes may race to start the server, but only one - // will succeed - the others will see the server exit with error 1740 - // (endpoint already in use) and will use the existing server. - let mut started = self._started.lock(); - - if *started { - tracing::debug!("global RPC server already started in this process"); - return Ok(()); - } - - tracing::info!("attempting to start global RPC server"); - - let (stderr_read, stderr_write) = pal::pipe_pair()?; - - // Spawn the RPC server in a new process group so it doesn't receive - // console signals from the test process. - const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200; - - let mut rpc_server_child = std::process::Command::new(rpc_server_path) - .stdin(Stdio::null()) - .stdout(Stdio::piped()) - .stderr(stderr_write) - .creation_flags(CREATE_NEW_PROCESS_GROUP) - .spawn() - .context("failed to spawn test_igvm_agent_rpc_server")?; - - // Wait for stdout to close - this signals either: - // 1. The server is ready and accepting connections - // 2. The server failed to start (e.g., endpoint already in use) - let mut stdout = rpc_server_child - .stdout - .take() - .context("failed to take stdout from RPC server")?; - let mut byte = [0u8]; - let n = stdout - .read(&mut byte) - .context("failed to read from RPC server stdout")?; - if n != 0 { - anyhow::bail!( - "expected RPC server stdout to close (EOF), but read {} bytes", - n - ); - } - drop(stdout); - - // Check if the server started successfully or failed. - // Give it a moment to exit if it's going to fail. - std::thread::sleep(std::time::Duration::from_millis(50)); - - match rpc_server_child.try_wait()? { - Some(status) => { - // Server exited - likely because another process already started it. - // This is expected in nextest where tests run in parallel processes. - tracing::info!( - exit_code = ?status.code(), - "RPC server exited (another test process likely started it first)" - ); - *started = true; - return Ok(()); - } - None => { - // Server is still running - we successfully started it. - // Note: We intentionally don't use a job object here because nextest - // runs tests in separate processes. If we used a job object, the server - // would be killed when THIS test process exits, even if other test - // processes are still using it. Instead, we let the server run as an - // orphan - it will be cleaned up by: - // - nextest's own job object (if it uses one) - // - The CI runner's cleanup - // - Manual cleanup if running locally - tracing::info!("global RPC server started successfully"); - } - } - - // Spawn a named thread to read and log stderr from the RPC server. - // When the RPC server exits, the pipe will be closed and reader.lines() - // will return None, causing the thread to exit naturally. - std::thread::Builder::new() - .name("rpc-server-stderr".to_string()) - .spawn(move || { - let reader = BufReader::new(stderr_read); - for line in reader.lines() { - match line { - Ok(line) => { - tracing::info!(target: "test_igvm_agent_rpc_server", "{}", line) - } - Err(e) => { - tracing::debug!("RPC server stderr pipe closed: {}", e); - break; - } - } - } - tracing::debug!("RPC server stderr reader thread exiting"); - }) - .expect("failed to spawn stderr reader thread"); - - // Drop the Child handle. We intentionally don't keep it or use a job object - // to avoid killing the server when this test process exits. - drop(rpc_server_child); - - *started = true; - - Ok(()) - } - } - /// Checks if any process with the given executable name is running. - /// Used for debugging to verify the RPC server is still alive. pub fn is_process_running(exe_name: &str) -> bool { use std::process::Command; @@ -180,8 +43,6 @@ pub mod windows { let stdout = String::from_utf8_lossy(&output.stdout); // tasklist returns "INFO: No tasks are running..." if no match found. // If a process is found, it shows the process info without "INFO: No tasks". - // Note: tasklist truncates long process names in output, so we can't - // reliably check for the exact name in the output. !stdout.contains("INFO: No tasks") } Err(e) => { @@ -191,20 +52,22 @@ pub mod windows { } } - /// Logs diagnostic information about the RPC server process status. - pub fn check_rpc_server_status() { + /// Verifies that the RPC server is running. + /// In CI, the server is started by flowey before tests run. + /// Returns an error if the server is not running. + pub fn ensure_rpc_server_running() -> anyhow::Result<()> { const RPC_SERVER_EXE: &str = "test_igvm_agent_rpc_server.exe"; if is_process_running(RPC_SERVER_EXE) { - tracing::info!( - exe = RPC_SERVER_EXE, - "RPC server health check: process is still running" - ); + tracing::info!(exe = RPC_SERVER_EXE, "RPC server is running"); + Ok(()) } else { - tracing::warn!( - exe = RPC_SERVER_EXE, - "RPC server health check: process is NOT running - this may cause test failures!" - ); + anyhow::bail!( + "RPC server ({}) is not running. \ + In CI, the server should be started by flowey. \ + For local development, start the server manually before running tests.", + RPC_SERVER_EXE + ) } } } @@ -625,35 +488,33 @@ async fn tpm_test_platform_hierarchy_disabled( // } /// CVM with guest tpm tests on Hyper-V. +/// +/// The test requires the test_igvm_agent_rpc_server to be running. +/// In CI, the server is started by flowey before tests run. +/// For local development, start the server manually before running tests. #[cfg(windows)] #[vmm_test( - hyperv_openhcl_uefi_x64[vbs](vhd(ubuntu_2504_server_x64))[TPM_GUEST_TESTS_LINUX_X64, TEST_IGVM_AGENT_RPC_SERVER_WINDOWS_X64], - hyperv_openhcl_uefi_x64[vbs](vhd(windows_datacenter_core_2025_x64_prepped))[TPM_GUEST_TESTS_WINDOWS_X64, TEST_IGVM_AGENT_RPC_SERVER_WINDOWS_X64], - hyperv_openhcl_uefi_x64[tdx](vhd(ubuntu_2504_server_x64))[TPM_GUEST_TESTS_LINUX_X64, TEST_IGVM_AGENT_RPC_SERVER_WINDOWS_X64], - hyperv_openhcl_uefi_x64[tdx](vhd(windows_datacenter_core_2025_x64_prepped))[TPM_GUEST_TESTS_WINDOWS_X64, TEST_IGVM_AGENT_RPC_SERVER_WINDOWS_X64], - hyperv_openhcl_uefi_x64[snp](vhd(ubuntu_2504_server_x64))[TPM_GUEST_TESTS_LINUX_X64, TEST_IGVM_AGENT_RPC_SERVER_WINDOWS_X64], - hyperv_openhcl_uefi_x64[snp](vhd(windows_datacenter_core_2025_x64_prepped))[TPM_GUEST_TESTS_WINDOWS_X64, TEST_IGVM_AGENT_RPC_SERVER_WINDOWS_X64], + hyperv_openhcl_uefi_x64[vbs](vhd(ubuntu_2504_server_x64))[TPM_GUEST_TESTS_LINUX_X64], + hyperv_openhcl_uefi_x64[vbs](vhd(windows_datacenter_core_2025_x64_prepped))[TPM_GUEST_TESTS_WINDOWS_X64], + hyperv_openhcl_uefi_x64[tdx](vhd(ubuntu_2504_server_x64))[TPM_GUEST_TESTS_LINUX_X64], + hyperv_openhcl_uefi_x64[tdx](vhd(windows_datacenter_core_2025_x64_prepped))[TPM_GUEST_TESTS_WINDOWS_X64], + hyperv_openhcl_uefi_x64[snp](vhd(ubuntu_2504_server_x64))[TPM_GUEST_TESTS_LINUX_X64], + hyperv_openhcl_uefi_x64[snp](vhd(windows_datacenter_core_2025_x64_prepped))[TPM_GUEST_TESTS_WINDOWS_X64], )] -async fn cvm_tpm_guest_tests( +async fn cvm_tpm_guest_tests( config: PetriVmBuilder, - extra_deps: (ResolvedArtifact, ResolvedArtifact), + (tpm_guest_tests_artifact,): (ResolvedArtifact,), ) -> anyhow::Result<()> { let os_flavor = config.os_flavor(); - let (tpm_guest_tests_artifact, rpc_server_artifact) = extra_deps; - // Ensure the global RPC server is running - let rpc_server_path = rpc_server_artifact.get(); - windows::GLOBAL_RPC_SERVER.ensure_started(rpc_server_path)?; + // Verify the RPC server is running (started by flowey in CI) + windows::ensure_rpc_server_running()?; let config = config .with_tpm(true) .with_tpm_state_persistence(true) .with_guest_state_lifetime(PetriGuestStateLifetime::Disk); - // Debug: Check if the RPC server is still running at the end of the test. - // This helps diagnose flaky tests that may be caused by the server dying unexpectedly. - windows::check_rpc_server_status(); - let (vm, agent) = config.run().await?; let guest_binary_path = match os_flavor { @@ -666,10 +527,6 @@ async fn cvm_tpm_guest_tests( TpmGuestTests::send_tpm_guest_tests(&agent, host_binary_path, guest_binary_path, os_flavor) .await?; - // Debug: Check if the RPC server is still running at the end of the test. - // This helps diagnose flaky tests that may be caused by the server dying unexpectedly. - windows::check_rpc_server_status(); - // Verify AK cert with the test IGVM agent RPC server let expected_hex = expected_ak_cert_hex(); let ak_cert_output = tpm_guest_tests From 7c4d9f89140bc91bd84b47cae13e770723e42067 Mon Sep 17 00:00:00 2001 From: Ming-Wei Shih Date: Wed, 17 Dec 2025 00:54:01 +0000 Subject: [PATCH 31/32] update Signed-off-by: Ming-Wei Shih --- .github/workflows/openvmm-ci.yaml | 19 +++++++++++++++-- .github/workflows/openvmm-pr-release.yaml | 19 +++++++++++++++-- .github/workflows/openvmm-pr.yaml | 19 +++++++++++++++-- ...sume_and_test_nextest_vmm_tests_archive.rs | 21 ++++++++++++------- .../src/run_test_igvm_agent_rpc_server.rs | 7 +++---- 5 files changed, 67 insertions(+), 18 deletions(-) diff --git a/.github/workflows/openvmm-ci.yaml b/.github/workflows/openvmm-ci.yaml index 82f50a0807..90cc8b81ec 100644 --- a/.github/workflows/openvmm-ci.yaml +++ b/.github/workflows/openvmm-ci.yaml @@ -2117,6 +2117,9 @@ jobs: - name: install vmm tests deps (windows) run: flowey.exe e 16 flowey_lib_hvlite::install_vmm_tests_deps 0 shell: bash + - name: starting test_igvm_agent_rpc_server + run: flowey.exe e 16 flowey_lib_hvlite::run_test_igvm_agent_rpc_server 0 + shell: bash - name: run 'vmm_tests' nextest tests run: |- flowey.exe e 16 flowey_lib_common::run_cargo_nextest_run 0 @@ -2395,8 +2398,11 @@ jobs: run: flowey.exe e 17 flowey_lib_common::gen_cargo_nextest_run_cmd 0 shell: bash - name: install vmm tests deps (windows) + run: flowey.exe e 17 flowey_lib_hvlite::install_vmm_tests_deps 0 + shell: bash + - name: starting test_igvm_agent_rpc_server run: |- - flowey.exe e 17 flowey_lib_hvlite::install_vmm_tests_deps 0 + flowey.exe e 17 flowey_lib_hvlite::run_test_igvm_agent_rpc_server 0 flowey.exe e 17 flowey_core::pipeline::artifact::resolve 7 shell: bash - name: running vmm_test prep_steps @@ -2680,6 +2686,9 @@ jobs: - name: install vmm tests deps (windows) run: flowey.exe e 18 flowey_lib_hvlite::install_vmm_tests_deps 0 shell: bash + - name: starting test_igvm_agent_rpc_server + run: flowey.exe e 18 flowey_lib_hvlite::run_test_igvm_agent_rpc_server 0 + shell: bash - name: run 'vmm_tests' nextest tests run: |- flowey.exe e 18 flowey_lib_common::run_cargo_nextest_run 0 @@ -2958,8 +2967,11 @@ jobs: run: flowey.exe e 19 flowey_lib_common::gen_cargo_nextest_run_cmd 0 shell: bash - name: install vmm tests deps (windows) + run: flowey.exe e 19 flowey_lib_hvlite::install_vmm_tests_deps 0 + shell: bash + - name: starting test_igvm_agent_rpc_server run: |- - flowey.exe e 19 flowey_lib_hvlite::install_vmm_tests_deps 0 + flowey.exe e 19 flowey_lib_hvlite::run_test_igvm_agent_rpc_server 0 flowey.exe e 19 flowey_core::pipeline::artifact::resolve 7 shell: bash - name: running vmm_test prep_steps @@ -3772,6 +3784,9 @@ jobs: - name: install vmm tests deps (windows) run: flowey.exe e 21 flowey_lib_hvlite::install_vmm_tests_deps 0 shell: bash + - name: starting test_igvm_agent_rpc_server + run: flowey.exe e 21 flowey_lib_hvlite::run_test_igvm_agent_rpc_server 0 + shell: bash - name: run 'vmm_tests' nextest tests run: |- flowey.exe e 21 flowey_lib_common::run_cargo_nextest_run 0 diff --git a/.github/workflows/openvmm-pr-release.yaml b/.github/workflows/openvmm-pr-release.yaml index a6351c9ec3..8a0b99f061 100644 --- a/.github/workflows/openvmm-pr-release.yaml +++ b/.github/workflows/openvmm-pr-release.yaml @@ -2126,6 +2126,9 @@ jobs: - name: install vmm tests deps (windows) run: flowey.exe e 16 flowey_lib_hvlite::install_vmm_tests_deps 0 shell: bash + - name: starting test_igvm_agent_rpc_server + run: flowey.exe e 16 flowey_lib_hvlite::run_test_igvm_agent_rpc_server 0 + shell: bash - name: run 'vmm_tests' nextest tests run: |- flowey.exe e 16 flowey_lib_common::run_cargo_nextest_run 0 @@ -2404,8 +2407,11 @@ jobs: run: flowey.exe e 17 flowey_lib_common::gen_cargo_nextest_run_cmd 0 shell: bash - name: install vmm tests deps (windows) + run: flowey.exe e 17 flowey_lib_hvlite::install_vmm_tests_deps 0 + shell: bash + - name: starting test_igvm_agent_rpc_server run: |- - flowey.exe e 17 flowey_lib_hvlite::install_vmm_tests_deps 0 + flowey.exe e 17 flowey_lib_hvlite::run_test_igvm_agent_rpc_server 0 flowey.exe e 17 flowey_core::pipeline::artifact::resolve 7 shell: bash - name: running vmm_test prep_steps @@ -2689,6 +2695,9 @@ jobs: - name: install vmm tests deps (windows) run: flowey.exe e 18 flowey_lib_hvlite::install_vmm_tests_deps 0 shell: bash + - name: starting test_igvm_agent_rpc_server + run: flowey.exe e 18 flowey_lib_hvlite::run_test_igvm_agent_rpc_server 0 + shell: bash - name: run 'vmm_tests' nextest tests run: |- flowey.exe e 18 flowey_lib_common::run_cargo_nextest_run 0 @@ -2967,8 +2976,11 @@ jobs: run: flowey.exe e 19 flowey_lib_common::gen_cargo_nextest_run_cmd 0 shell: bash - name: install vmm tests deps (windows) + run: flowey.exe e 19 flowey_lib_hvlite::install_vmm_tests_deps 0 + shell: bash + - name: starting test_igvm_agent_rpc_server run: |- - flowey.exe e 19 flowey_lib_hvlite::install_vmm_tests_deps 0 + flowey.exe e 19 flowey_lib_hvlite::run_test_igvm_agent_rpc_server 0 flowey.exe e 19 flowey_core::pipeline::artifact::resolve 7 shell: bash - name: running vmm_test prep_steps @@ -3781,6 +3793,9 @@ jobs: - name: install vmm tests deps (windows) run: flowey.exe e 21 flowey_lib_hvlite::install_vmm_tests_deps 0 shell: bash + - name: starting test_igvm_agent_rpc_server + run: flowey.exe e 21 flowey_lib_hvlite::run_test_igvm_agent_rpc_server 0 + shell: bash - name: run 'vmm_tests' nextest tests run: |- flowey.exe e 21 flowey_lib_common::run_cargo_nextest_run 0 diff --git a/.github/workflows/openvmm-pr.yaml b/.github/workflows/openvmm-pr.yaml index a7cc7f4a66..5b8ddb6851 100644 --- a/.github/workflows/openvmm-pr.yaml +++ b/.github/workflows/openvmm-pr.yaml @@ -2780,6 +2780,9 @@ jobs: - name: install vmm tests deps (windows) run: flowey.exe e 18 flowey_lib_hvlite::install_vmm_tests_deps 0 shell: bash + - name: starting test_igvm_agent_rpc_server + run: flowey.exe e 18 flowey_lib_hvlite::run_test_igvm_agent_rpc_server 0 + shell: bash - name: run 'vmm_tests' nextest tests run: |- flowey.exe e 18 flowey_lib_common::run_cargo_nextest_run 0 @@ -3058,8 +3061,11 @@ jobs: run: flowey.exe e 19 flowey_lib_common::gen_cargo_nextest_run_cmd 0 shell: bash - name: install vmm tests deps (windows) + run: flowey.exe e 19 flowey_lib_hvlite::install_vmm_tests_deps 0 + shell: bash + - name: starting test_igvm_agent_rpc_server run: |- - flowey.exe e 19 flowey_lib_hvlite::install_vmm_tests_deps 0 + flowey.exe e 19 flowey_lib_hvlite::run_test_igvm_agent_rpc_server 0 flowey.exe e 19 flowey_core::pipeline::artifact::resolve 7 shell: bash - name: running vmm_test prep_steps @@ -3557,6 +3563,9 @@ jobs: - name: install vmm tests deps (windows) run: flowey.exe e 20 flowey_lib_hvlite::install_vmm_tests_deps 0 shell: bash + - name: starting test_igvm_agent_rpc_server + run: flowey.exe e 20 flowey_lib_hvlite::run_test_igvm_agent_rpc_server 0 + shell: bash - name: run 'vmm_tests' nextest tests run: |- flowey.exe e 20 flowey_lib_common::run_cargo_nextest_run 0 @@ -3835,8 +3844,11 @@ jobs: run: flowey.exe e 21 flowey_lib_common::gen_cargo_nextest_run_cmd 0 shell: bash - name: install vmm tests deps (windows) + run: flowey.exe e 21 flowey_lib_hvlite::install_vmm_tests_deps 0 + shell: bash + - name: starting test_igvm_agent_rpc_server run: |- - flowey.exe e 21 flowey_lib_hvlite::install_vmm_tests_deps 0 + flowey.exe e 21 flowey_lib_hvlite::run_test_igvm_agent_rpc_server 0 flowey.exe e 21 flowey_core::pipeline::artifact::resolve 7 shell: bash - name: running vmm_test prep_steps @@ -4435,6 +4447,9 @@ jobs: - name: install vmm tests deps (windows) run: flowey.exe e 23 flowey_lib_hvlite::install_vmm_tests_deps 0 shell: bash + - name: starting test_igvm_agent_rpc_server + run: flowey.exe e 23 flowey_lib_hvlite::run_test_igvm_agent_rpc_server 0 + shell: bash - name: run 'vmm_tests' nextest tests run: |- flowey.exe e 23 flowey_lib_common::run_cargo_nextest_run 0 diff --git a/flowey/flowey_lib_hvlite/src/_jobs/consume_and_test_nextest_vmm_tests_archive.rs b/flowey/flowey_lib_hvlite/src/_jobs/consume_and_test_nextest_vmm_tests_archive.rs index ae95512e42..93f2e50ed7 100644 --- a/flowey/flowey_lib_hvlite/src/_jobs/consume_and_test_nextest_vmm_tests_archive.rs +++ b/flowey/flowey_lib_hvlite/src/_jobs/consume_and_test_nextest_vmm_tests_archive.rs @@ -189,16 +189,21 @@ impl SimpleFlowNode for Node { use_relative_paths: false, }); - // Start the test_igvm_agent_rpc_server before running tests. + // Start the test_igvm_agent_rpc_server before running tests (Windows only). // This must happen after init_vmm_tests_env which copies the binary. // The server runs in the background for the duration of the test run. - #[cfg(windows)] - pre_run_deps.push( - ctx.reqv(|done| crate::run_test_igvm_agent_rpc_server::Request { - env: extra_env.clone(), - done, - }), - ); + // The node itself handles the platform check at runtime. + if matches!( + target.operating_system, + target_lexicon::OperatingSystem::Windows + ) { + pre_run_deps.push( + ctx.reqv(|done| crate::run_test_igvm_agent_rpc_server::Request { + env: extra_env.clone(), + done, + }), + ); + } if needs_prep_run { pre_run_deps.push(ctx.reqv(|done| crate::run_prep_steps::Request { diff --git a/flowey/flowey_lib_hvlite/src/run_test_igvm_agent_rpc_server.rs b/flowey/flowey_lib_hvlite/src/run_test_igvm_agent_rpc_server.rs index 06d88ef796..f3723ebc7d 100644 --- a/flowey/flowey_lib_hvlite/src/run_test_igvm_agent_rpc_server.rs +++ b/flowey/flowey_lib_hvlite/src/run_test_igvm_agent_rpc_server.rs @@ -58,8 +58,7 @@ impl SimpleFlowNode for Node { .get("TEST_OUTPUT_PATH") .context("TEST_OUTPUT_PATH not set")?; - let exe = Path::new(test_content_dir) - .join("test_igvm_agent_rpc_server.exe"); + let exe = Path::new(test_content_dir).join("test_igvm_agent_rpc_server.exe"); if !exe.exists() { log::info!( @@ -70,8 +69,8 @@ impl SimpleFlowNode for Node { } // Create log file for server output - let log_file_path = Path::new(test_output_path) - .join("test_igvm_agent_rpc_server.log"); + let log_file_path = + Path::new(test_output_path).join("test_igvm_agent_rpc_server.log"); let log_file = std::fs::File::create(&log_file_path)?; let log_file_stderr = log_file.try_clone()?; From c9b78688b21dfb55e9c295b6761c04974b92930b Mon Sep 17 00:00:00 2001 From: Ming-Wei Shih Date: Wed, 17 Dec 2025 01:58:31 +0000 Subject: [PATCH 32/32] x Signed-off-by: Ming-Wei Shih --- .../local_build_and_run_nextest_vmm_tests.rs | 15 +++++++++++++++ .../src/run_test_igvm_agent_rpc_server.rs | 2 +- vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs | 5 +++-- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/flowey/flowey_lib_hvlite/src/_jobs/local_build_and_run_nextest_vmm_tests.rs b/flowey/flowey_lib_hvlite/src/_jobs/local_build_and_run_nextest_vmm_tests.rs index e50ef76f8d..26111314a8 100644 --- a/flowey/flowey_lib_hvlite/src/_jobs/local_build_and_run_nextest_vmm_tests.rs +++ b/flowey/flowey_lib_hvlite/src/_jobs/local_build_and_run_nextest_vmm_tests.rs @@ -186,6 +186,7 @@ impl SimpleFlowNode for Node { ctx.import::(); ctx.import::(); ctx.import::(); + ctx.import::(); ctx.import::(); ctx.import::(); ctx.import::(); @@ -1019,6 +1020,20 @@ impl SimpleFlowNode for Node { } } else { side_effects.push(ctx.reqv(crate::install_vmm_tests_deps::Request::Install)); + + // Start the test_igvm_agent_rpc_server before running tests (Windows only). + if matches!( + target_triple.operating_system, + target_lexicon::OperatingSystem::Windows + ) { + side_effects.push(ctx.reqv(|done| { + crate::run_test_igvm_agent_rpc_server::Request { + env: extra_env.clone(), + done, + } + })); + } + if let Some((prep_steps, _)) = register_prep_steps { side_effects.push(ctx.reqv(|done| crate::run_prep_steps::Request { prep_steps, diff --git a/flowey/flowey_lib_hvlite/src/run_test_igvm_agent_rpc_server.rs b/flowey/flowey_lib_hvlite/src/run_test_igvm_agent_rpc_server.rs index f3723ebc7d..b0f7170011 100644 --- a/flowey/flowey_lib_hvlite/src/run_test_igvm_agent_rpc_server.rs +++ b/flowey/flowey_lib_hvlite/src/run_test_igvm_agent_rpc_server.rs @@ -43,7 +43,7 @@ impl SimpleFlowNode for Node { { let _ = env; log::info!("test_igvm_agent_rpc_server is Windows-only, skipping"); - return Ok(()); + Ok(()) } #[cfg(windows)] diff --git a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs index 96724d8b47..1c86b6dae8 100644 --- a/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs +++ b/vmm_tests/vmm_tests/tests/tests/multiarch/tpm.rs @@ -503,7 +503,7 @@ async fn tpm_test_platform_hierarchy_disabled( )] async fn cvm_tpm_guest_tests( config: PetriVmBuilder, - (tpm_guest_tests_artifact,): (ResolvedArtifact,), + extra_deps: (ResolvedArtifact,), ) -> anyhow::Result<()> { let os_flavor = config.os_flavor(); @@ -522,7 +522,8 @@ async fn cvm_tpm_guest_tests( OsFlavor::Windows => TPM_GUEST_TESTS_WINDOWS_GUEST_PATH, _ => unreachable!(), }; - let host_binary_path = tpm_guest_tests_artifact.get(); + let (artifact,) = extra_deps; + let host_binary_path = artifact.get(); let tpm_guest_tests = TpmGuestTests::send_tpm_guest_tests(&agent, host_binary_path, guest_binary_path, os_flavor) .await?;