From 7ae14e6f947cec0e67939b6c041ff3f959f99eaf Mon Sep 17 00:00:00 2001 From: liangzhou121 Date: Tue, 1 Sep 2026 22:12:04 +0530 Subject: [PATCH] tdx: fix EINVAL from KVM_TDX_INIT_VCPU by not filtering vCPU CPUID KVM_TDX_CAPABILITIES only reports the CPUID bits the TDX module considers "configurable". Leaves such as 0x8000_0008 (MAXPHYADDR) are guest-visible but not part of that configurable set, so running the shared guest CPUID through tdx_filter_cpuid() before handing it to the per-vCPU KVM_SET_CPUID2 ioctl silently dropped them. Without 0x8000_0008 in KVM's local CPUID cache, vcpu->arch.maxphyaddr stayed 0, which made the kernel's reserved-GPA-bits check reject the x2APIC MSR value tdx_vcpu_init() forces on the vCPU, so KVM_TDX_INIT_VCPU failed with EINVAL before the TDX module SEAMCALL was ever attempted. Stop filtering the CPUID used for KVM_SET_CPUID2 and instead filter only a copy of it when building the KVM_TDX_INIT_VM request. Signed-off-by: liangzhou121 --- arch/src/x86_64/mod.rs | 37 +++++++------------------------------ hypervisor/src/kvm/mod.rs | 17 ++++++++++++++--- vmm/src/cpu.rs | 2 -- vmm/src/lib.rs | 6 ------ vmm/src/vm.rs | 3 --- 5 files changed, 21 insertions(+), 44 deletions(-) diff --git a/arch/src/x86_64/mod.rs b/arch/src/x86_64/mod.rs index 20b3676f9..8c1f1417b 100644 --- a/arch/src/x86_64/mod.rs +++ b/arch/src/x86_64/mod.rs @@ -70,6 +70,10 @@ const KVM_FEATURE_ASYNC_PF_BIT: u8 = 4; const KVM_FEATURE_ASYNC_PF_VMEXIT_BIT: u8 = 10; #[cfg(feature = "tdx")] const KVM_FEATURE_STEAL_TIME_BIT: u8 = 5; +#[cfg(feature = "tdx")] +const KVM_FEATURE_PV_EOI_BIT: u8 = 6; +#[cfg(feature = "tdx")] +const KVM_FEATURE_ASYNC_PF_INT_BIT: u8 = 14; const KVM_FEATURE_MSI_EXT_DEST_ID: u8 = 15; @@ -560,7 +564,6 @@ impl CpuidFeatureEntry { pub fn generate_common_cpuid( hypervisor: &dyn hypervisor::Hypervisor, config: &CpuidConfig, - #[cfg(feature = "tdx")] vm: Option<&dyn hypervisor::Vm>, ) -> super::Result> { #[allow(unused_unsafe)] // SAFETY: cpuid called with valid leaves @@ -631,22 +634,6 @@ pub fn generate_common_cpuid( CpuidPatch::patch_cpuid(&mut cpuid, &cpuid_patches); - #[cfg(feature = "tdx")] - let tdx_capabilities = if config.tdx { - let tdx_vm = vm.ok_or_else(|| { - Error::TdxCapabilities(HypervisorVmError::InitializeTdx(std::io::Error::other( - "Missing VM instance for TDX CPUID generation", - ))) - })?; - let caps = tdx_vm - .tdx_capabilities() - .map_err(Error::TdxCapabilities)?; - info!("TDX capabilities {caps:#?}"); - Some(caps) - } else { - None - }; - // Update some existing CPUID for entry in cpuid.as_mut_slice().iter_mut() { #[allow(unused_unsafe)] @@ -730,7 +717,9 @@ pub fn generate_common_cpuid( | (1 << KVM_FEATURE_CLOCKSOURCE_STABLE_BIT) | (1 << KVM_FEATURE_ASYNC_PF_BIT) | (1 << KVM_FEATURE_ASYNC_PF_VMEXIT_BIT) - | (1 << KVM_FEATURE_STEAL_TIME_BIT)); + | (1 << KVM_FEATURE_STEAL_TIME_BIT) + | (1 << KVM_FEATURE_PV_EOI_BIT) + | (1 << KVM_FEATURE_ASYNC_PF_INT_BIT)); } } _ => {} @@ -800,18 +789,6 @@ pub fn generate_common_cpuid( } } - #[cfg(feature = "tdx")] - if let Some(caps) = &tdx_capabilities { - let tdx_vm = vm.ok_or_else(|| { - Error::TdxCapabilities(HypervisorVmError::InitializeTdx(std::io::Error::other( - "Missing VM instance for TDX CPUID filtering", - ))) - })?; - tdx_vm - .tdx_filter_cpuid(&mut cpuid, caps) - .map_err(Error::TdxCapabilities)?; - } - Ok(cpuid) } diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 2c7259e62..8bb1ead23 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -1008,8 +1008,17 @@ impl vm::Vm for KvmVm { fn tdx_init(&self, cpuid: &[CpuIdEntry], max_vcpus: u32) -> vm::Result<()> { const TDX_ATTR_SEPT_VE_DISABLE: usize = 28; + // `cpuid` is shared with per-vCPU `KVM_SET_CPUID2` programming and + // must keep leaves the TDX module does not consider "configurable" + // (for example 0x8000_0008/MAXPHYADDR). `KVM_TDX_INIT_VM` accepts + // only configurable leaves reported by `KVM_TDX_CAPABILITIES`, so + // filter a copy here just for `KVM_TDX_INIT_VM`. + let tdx_capabilities = self.tdx_capabilities()?; + let mut init_vm_cpuid = cpuid.to_vec(); + self.tdx_filter_cpuid(&mut init_vm_cpuid, &tdx_capabilities)?; + let mut new_cpuid: Vec = - cpuid.iter().map(|e| (*e).into()).collect(); + init_vm_cpuid.iter().map(|e| (*e).into()).collect(); new_cpuid.resize(TDX_MAX_NR_CPUID_CONFIGS, kvm_bindings::kvm_cpuid_entry2::default()); // XFAM (the set of XSAVE state components enabled for this TD) must @@ -1020,7 +1029,7 @@ impl vm::Vm for KvmVm { // Mask them out here so Cloud Hypervisor's TDX guest CPUID matches QEMU. const XSTATE_CET_U_BIT: u64 = 11; const XSTATE_CET_S_BIT: u64 = 12; - let xfam = self.tdx_capabilities()?.supported_xfam + let xfam = tdx_capabilities.supported_xfam & !((1 << XSTATE_CET_U_BIT) | (1 << XSTATE_CET_S_BIT)); let data = KvmTdxInitVm { @@ -1031,7 +1040,7 @@ impl vm::Vm for KvmVm { mrownerconfig: [0; 6], reserved: [0; 12], cpuid: kvm_cpuid2 { - nent: cpuid.len() as u32, + nent: init_vm_cpuid.len() as u32, padding: 0, entries: new_cpuid.as_slice().try_into().unwrap(), }, @@ -1121,6 +1130,8 @@ fn tdx_command( } continue; } + // hw_error carries the raw TDX module (SEAMCALL) status code on failure. + error!("KVM TDX command failed: {e}, hw_error: {:#x}", cmd.hw_error); return Err(e); } } diff --git a/vmm/src/cpu.rs b/vmm/src/cpu.rs index 1669ca274..187979249 100644 --- a/vmm/src/cpu.rs +++ b/vmm/src/cpu.rs @@ -1109,8 +1109,6 @@ impl CpuManager { tdx, amx: self.config.features.amx, }, - #[cfg(feature = "tdx")] - vm, ) .map_err(Error::CommonCpuId)? }; diff --git a/vmm/src/lib.rs b/vmm/src/lib.rs index c0a89dc61..a23187d2e 100644 --- a/vmm/src/lib.rs +++ b/vmm/src/lib.rs @@ -1331,9 +1331,6 @@ impl Vmm { tdx: false, amx, }, - #[cfg(feature = "tdx")] - // Live Migration is not supported when TDX is enabled - None, ) .map_err(|e| { MigratableError::MigrateSend(anyhow!("Error generating common cpuid': {e:?}")) @@ -1443,9 +1440,6 @@ impl Vmm { tdx: false, amx: vm_config.cpus.features.amx, }, - #[cfg(feature = "tdx")] - // Live Migration is not supported when TDX is enabled - None, ) .map_err(|e| { MigratableError::MigrateReceive(anyhow!("Error generating common cpuid: {e:?}")) diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index d7ead06d5..74180c406 100644 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -3395,9 +3395,6 @@ impl Snapshottable for Vm { tdx: false, amx, }, - #[cfg(feature = "tdx")] - // Snapshot not possible with TDX VM - None, ) .map_err(|e| { MigratableError::MigrateReceive(anyhow!("Error generating common cpuid: {e:?}"))