Program the VMCS PERFMON_MASK field when PerfMon masking is enabled. The field uses the same bit definitions as IA32_PERF_GLOBAL_STATUS and can therefore be derived from ~pmu->global_status_rsvd. Expose IA32_PERF_CAPABILITIES.PERF_METRICS_AVAILABLE[15] only when PERFMON_MASK[48] is available to the guest. When PerfMon masking is enabled, guest RDMSR/WRMSR accesses to PMU global MSRs are filtered by the mask, writes outside the mask cause an #GP(0), and RDPMC returns only guest-owned counter values. Intentionally defer enabling PerfMon masking in VM-execution control until the remaining PerfMon masking support is in place, so that intermediate commits remain functional during bisection. Signed-off-by: Zide Chen --- arch/x86/include/asm/kvm_host.h | 1 + arch/x86/include/asm/vmx.h | 2 ++ arch/x86/kvm/pmu.c | 4 ++-- arch/x86/kvm/pmu.h | 6 ++++++ arch/x86/kvm/vmx/pmu_intel.c | 26 ++++++++++++++++++++++++++ arch/x86/kvm/vmx/vmx.c | 6 +++++- 6 files changed, 42 insertions(+), 3 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 262553f95793..467090bf0ab2 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -582,6 +582,7 @@ struct kvm_pmu { u64 eventsel_rsvd; u64 raw_event_mask; u64 perf_metrics; + u64 perfmon_mask; struct kvm_pmc gp_counters[KVM_MAX_NR_GP_COUNTERS]; struct kvm_pmc fixed_counters[KVM_MAX_NR_FIXED_COUNTERS]; diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h index 3f1b3096ff04..1cb092d86955 100644 --- a/arch/x86/include/asm/vmx.h +++ b/arch/x86/include/asm/vmx.h @@ -275,6 +275,8 @@ enum vmcs_field { SHARED_EPT_POINTER = 0x0000203C, PID_POINTER_TABLE = 0x00002042, PID_POINTER_TABLE_HIGH = 0x00002043, + PERFMON_MASK = 0x00002054, + PERFMON_MASK_HIGH = 0x00002055, GUEST_PHYSICAL_ADDRESS = 0x00002400, GUEST_PHYSICAL_ADDRESS_HIGH = 0x00002401, VMCS_LINK_POINTER = 0x00002800, diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c index 92ff685d11b3..f944a15160cb 100644 --- a/arch/x86/kvm/pmu.c +++ b/arch/x86/kvm/pmu.c @@ -1393,7 +1393,7 @@ void kvm_mediated_pmu_load(struct kvm_vcpu *vcpu) perf_pmu_partition_preload(); - perf_load_guest_context(false); + perf_load_guest_context(kvm_vcpu_has_perfmon_mask(vcpu)); /* * Explicitly clear PERF_GLOBAL_CTRL, as "loading" the guest's context @@ -1466,5 +1466,5 @@ void kvm_mediated_pmu_put(struct kvm_vcpu *vcpu) perf_put_guest_lvtpc(); - perf_put_guest_context(false); + perf_put_guest_context(kvm_vcpu_has_perfmon_mask(vcpu)); } diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h index 2dc12e3f3af0..057e3258e473 100644 --- a/arch/x86/kvm/pmu.h +++ b/arch/x86/kvm/pmu.h @@ -89,6 +89,12 @@ static inline bool kvm_vcpu_has_mediated_pmu(struct kvm_vcpu *vcpu) return enable_mediated_pmu && vcpu_to_pmu(vcpu)->version; } +static inline bool kvm_vcpu_has_perfmon_mask(struct kvm_vcpu *vcpu) +{ + return kvm_vcpu_has_mediated_pmu(vcpu) && + vcpu_to_pmu(vcpu)->perfmon_mask; +} + static inline unsigned long kvm_gp_pmc_mask(struct kvm_pmu *pmu) { return pmu->pmc_exists64 & diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c index 62e542eac05e..19ccc7cd319c 100644 --- a/arch/x86/kvm/vmx/pmu_intel.c +++ b/arch/x86/kvm/vmx/pmu_intel.c @@ -703,6 +703,21 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu) pmu->pebs_enable_rsvd = ~kvm_gp_pmc_mask(pmu); } } + + if (kvm_vcpu_has_mediated_pmu(vcpu) && perfmon_mask) { + pmu->perfmon_mask = ~pmu->global_status_rsvd; + + /* + * The PerfMon mask for a particular guest must be a subset + * of the module-wide mask. This masks out the global bits + * (e.g. GLOBAL_STATUS_COND_CHG) that must be handled by the + * host and were removed from global_status_rsvd without + * checking perfmon_mask, and defends in depth against any + * other bits inadvertently granted to the guest. + */ + pmu->perfmon_mask &= perfmon_mask; + vmcs_write64(PERFMON_MASK, pmu->perfmon_mask); + } } static void intel_pmu_init(struct kvm_vcpu *vcpu) @@ -739,6 +754,7 @@ static void intel_pmu_reset(struct kvm_vcpu *vcpu) struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); pmu->perf_metrics = 0; + pmu->perfmon_mask = 0; intel_pmu_release_guest_lbr_event(vcpu); } @@ -1021,6 +1037,16 @@ void intel_pmu_perfmon_mask_setup(void) perfmon_mask); perfmon_mask = 0; } + + /* + * perfmon_mask represents the maximum resources that any guest may + * have. KVM chooses to expose fewer hardware resources to guests. + */ + if (perfmon_mask) { + kvm_pmu_cap.cntr_mask64 &= perfmon_mask; + kvm_pmu_cap.fixed_cntr_mask64 &= + (perfmon_mask >> INTEL_PMC_IDX_FIXED); + } } struct kvm_pmu_ops intel_pmu_ops __initdata = { diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index cdd141d22efa..1b42c9d6f168 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -4294,6 +4294,9 @@ static void vmx_recalc_pmu_msr_intercepts(struct kvm_vcpu *vcpu) if (!cpu_has_save_perf_global_ctrl()) { vm_exit_controls_bits &= ~VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL; + /* Module parameter validation should already prevent this. */ + WARN_ON_ONCE(kvm_vcpu_has_perfmon_mask(vcpu)); + if (has_mediated_pmu) vmx_add_autostore_msr(vmx, MSR_CORE_PERF_GLOBAL_CTRL); else @@ -8133,7 +8136,8 @@ static __init u64 vmx_get_perf_capabilities(void) perf_cap &= ~PERF_CAP_PEBS_BASELINE; } - if (enable_mediated_pmu) + if (enable_mediated_pmu && + (!perfmon_mask || (perfmon_mask & GLOBAL_STATUS_PERF_METRICS_OVF))) perf_cap |= kvm_host.perf_capabilities & PERF_CAP_PERF_METRICS; return perf_cap; -- 2.55.0