From: Vishal Annapurve Date: Fri, 22 May 2026 15:15:34 +0000 (+0000) Subject: KVM: x86: Treat KVM's virtual PMU as disabled for TDX VMs X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=986833d381d2c48bdfa52652006f445570aea0e4;p=thirdparty%2Fkernel%2Flinux.git KVM: x86: Treat KVM's virtual PMU as disabled for TDX VMs Introduce a "protected PMU" concept, and use it to disable KVM's virtual PMU for TDX VMs, as the PMU state for TDX VMs is virtualized by the TDX Module[1], i.e. _can't_ emulated/virtualized by KVM, and KVM doesn't yet support enabling/exposing PMU functionality for/to TDX VMs. For now, simply treat the PMU as disabled, as it's not clear what all needs to be changed, e.g. KVM needs to do at least: 1) Configure TD_PARAMS to allow guests to use performance monitoring. 2) Restrict the TD to a subset of the PEBS counters if supported. 3) Limit the TD to setup a certain perfmon events using basic/enhanced event filtering. Explicitly disallow enabling the PMU via KVM_CAP_PMU_CAPABILITY for VMs with a protected PMU to prevent userspace from circumventing KVM's protections. Link: https://cdrdv2.intel.com/v1/dl/getContent/733575 Section 15.2[1] Suggested-by: Sean Christopherson Signed-off-by: Vishal Annapurve Link: https://patch.msgid.link/20260522151534.652522-1-vannapurve@google.com [sean: massage shortlog and changelog] Signed-off-by: Sean Christopherson --- diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 7114707b9856..beb83eea65ef 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1442,6 +1442,7 @@ struct kvm_arch { bool has_private_mem; bool has_protected_state; bool has_protected_eoi; + bool has_protected_pmu; bool pre_fault_allowed; struct hlist_head *mmu_page_hash; struct list_head active_mmu_pages; diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index b8c3d3d8bbfe..5fb3f606501b 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -638,6 +638,12 @@ int tdx_vm_init(struct kvm *kvm) kvm->arch.has_private_mem = true; kvm->arch.disabled_quirks |= KVM_X86_QUIRK_IGNORE_GUEST_PAT; + /* + * PMU support is provided by the TDX-Module (if enabled for the VM). + * From KVM's perspective, the VM doesn't have a virtual PMU. + */ + kvm->arch.has_protected_pmu = true; + /* * Because guest TD is protected, VMM can't parse the instruction in TD. * Instead, guest uses MMIO hypercall. For unmodified device driver, diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 93958df30230..c560c52b95f0 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -6920,6 +6920,10 @@ disable_exits_unlock: if (!enable_pmu || (cap->args[0] & ~KVM_CAP_PMU_VALID_MASK)) break; + if (kvm->arch.has_protected_pmu && + cap->args[0] != KVM_PMU_CAP_DISABLE) + break; + mutex_lock(&kvm->lock); if (!kvm->created_vcpus && !kvm->arch.created_mediated_pmu) { kvm->arch.enable_pmu = !(cap->args[0] & KVM_PMU_CAP_DISABLE); @@ -13362,7 +13366,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) kvm->arch.default_tsc_khz = max_tsc_khz ? : tsc_khz; kvm->arch.apic_bus_cycle_ns = APIC_BUS_CYCLE_NS_DEFAULT; kvm->arch.guest_can_read_msr_platform_info = true; - kvm->arch.enable_pmu = enable_pmu; + kvm->arch.enable_pmu = enable_pmu && !kvm->arch.has_protected_pmu; #if IS_ENABLED(CONFIG_HYPERV) spin_lock_init(&kvm->arch.hv_root_tdp_lock);