Add nested support for PMC virtualization and advertise X86_FEATURE_PERFCTR_VIRT so that a L1 hypervisor can use it for its L2 guests. As long as the L0 hypervisor uses hardware-switched mediated PMU for L1, it is used while L1 runs L2 guests as well, irrespective of what L1 does. Since hardware runs L2 from vmcb02, not vmcb12, enable PMC virtualization in vmcb02 whenever it is enabled in vmcb01, so that hardware switches the PMU state using the vmcb02 save area. The state to switch depends on whether L1 enables PMC virtualization for L2. An L1 hypervisor enables PMC virtualization for L2 by setting bit 3 of misc_ctl2 in vmcb12. Following commit 84dc9fd0354d ("KVM: nSVM: Cache all used fields from VMCB12"), cache the PMU-related fields to avoid TOCTOU hazards. Copy the cached L2 PMU state into vmcb02 for L2 VMRUNs, and back into vmcb12 on nested VMEXITs, so that L1 observes the latest L2 PMU state. When L1 does not enable PMC virtualization for L2, the PMU state to switch is the one in the vmcb01 save area. Copy it into vmcb02 for L2 VMRUNs, and back into vmcb01 on nested VMEXITs, so that the latest PMU state is preserved for L1. The counter reprogramming done on nested transitions writes into the vmcb02 save area, so run it after copying the PMU state into vmcb02; otherwise the copy overwrites it. Signed-off-by: Sandipan Das --- arch/x86/kvm/cpuid.c | 1 + arch/x86/kvm/svm/nested.c | 45 ++++++++++++++++++++++++++++++++++++--- arch/x86/kvm/svm/svm.c | 4 ++++ arch/x86/kvm/svm/svm.h | 18 ++++++++++++++++ 4 files changed, 65 insertions(+), 3 deletions(-) diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c index ddb022cb203a..ce4f984bc6e1 100644 --- a/arch/x86/kvm/cpuid.c +++ b/arch/x86/kvm/cpuid.c @@ -1217,6 +1217,7 @@ void kvm_initialize_cpu_caps(void) VENDOR_F(VGIF), VENDOR_F(VNMI), VENDOR_F(SVME_ADDR_CHK), + VENDOR_F(PERFCTR_VIRT), ); kvm_cpu_cap_init(CPUID_8000_001F_EAX, diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c index 73f37b050d0a..a9f9caeb7840 100644 --- a/arch/x86/kvm/svm/nested.c +++ b/arch/x86/kvm/svm/nested.c @@ -605,6 +605,7 @@ static void __nested_copy_vmcb_save_to_cache(struct vmcb_save_area_cached *to, to->g_pat = from->g_pat; svm_copy_lbrs(to, from); + svm_copy_pmcs(to, from); } void nested_copy_vmcb_save_to_cache(struct vcpu_svm *svm, @@ -741,6 +742,17 @@ static bool nested_vmcb12_has_lbrv(struct kvm_vcpu *vcpu) (to_svm(vcpu)->nested.ctl.misc_ctl2 & SVM_MISC2_ENABLE_V_LBR); } +static bool nested_vmcb12_has_vpmc(struct kvm_vcpu *vcpu) +{ + /* + * Since nested AVIC is not supported, L2 PMIs can only be delivered + * via VNMI, so make it a hard requirement. + */ + return guest_cpu_cap_has(vcpu, X86_FEATURE_PERFCTR_VIRT) && + nested_vnmi_enabled(to_svm(vcpu)) && + (to_svm(vcpu)->nested.ctl.misc_ctl2 & SVM_MISC2_ENABLE_V_PMC); +} + static void nested_vmcb02_prepare_save(struct vcpu_svm *svm) { struct vmcb_ctrl_area_cached *control = &svm->nested.ctl; @@ -823,6 +835,11 @@ static void nested_vmcb02_prepare_save(struct vcpu_svm *svm) } vmcb_mark_dirty(vmcb02, VMCB_LBR); svm_update_lbrv(&svm->vcpu); + + if (nested_vmcb12_has_vpmc(vcpu)) + svm_copy_pmcs(&vmcb02->save, save); + else if (kvm_vcpu_has_mediated_pmu_caps(vcpu, KVM_MEDIATED_PMU_CAP_HW_SWITCHED)) + svm_copy_pmcs(&vmcb02->save, &vmcb01->save); } static inline bool is_evtinj_soft(u32 evtinj) @@ -863,7 +880,6 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm *svm) /* Enter Guest-Mode */ enter_guest_mode(vcpu); - svm_pmu_handle_nested_transition(svm); /* * Filled at exit: exit_code, exit_info_1, exit_info_2, exit_int_info, @@ -986,6 +1002,10 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm *svm) /* SVM_MISC2_ENABLE_V_LBR is controlled by svm_update_lbrv() */ + /* L0 uses hardware-switched mode for both vmcb01 and vmcb02 */ + if (kvm_vcpu_has_mediated_pmu_caps(vcpu, KVM_MEDIATED_PMU_CAP_HW_SWITCHED)) + vmcb02->control.misc_ctl2 |= SVM_MISC2_ENABLE_V_PMC; + if (!nested_vmcb_needs_vls_intercept(svm)) vmcb02->control.misc_ctl2 |= SVM_MISC2_ENABLE_V_VMLOAD_VMSAVE; @@ -1065,6 +1085,9 @@ int enter_svm_guest_mode(struct kvm_vcpu *vcpu, u64 vmcb12_gpa, bool from_vmrun) nested_vmcb02_prepare_control(svm); nested_vmcb02_prepare_save(svm); + if (!nested_vmcb12_has_vpmc(vcpu)) + svm_pmu_handle_nested_transition(svm); + ret = nested_svm_load_cr3(&svm->vcpu, svm->nested.save.cr3, nested_npt_enabled(svm), from_vmrun); if (ret) @@ -1228,6 +1251,9 @@ void svm_copy_vmrun_state(struct vmcb_save_area *to_save, svm_copy_lbrs(to_save, from_save); to_save->dbgctl &= ~DEBUGCTL_RESERVED_BITS; } + + if (kvm_cpu_cap_has(X86_FEATURE_PERFCTR_VIRT)) + svm_copy_pmcs(to_save, from_save); } void svm_copy_vmloadsave_state(struct vmcb *to_vmcb, struct vmcb *from_vmcb) @@ -1300,6 +1326,9 @@ static int nested_svm_vmexit_update_vmcb12(struct kvm_vcpu *vcpu) if (nested_vmcb12_has_lbrv(vcpu)) svm_copy_lbrs(&vmcb12->save, &vmcb02->save); + if (nested_vmcb12_has_vpmc(vcpu)) + svm_copy_pmcs(&vmcb12->save, &vmcb02->save); + vmcb12->control.event_inj = 0; vmcb12->control.event_inj_err = 0; vmcb12->control.int_ctl = svm->nested.ctl.int_ctl; @@ -1325,7 +1354,9 @@ void nested_svm_vmexit(struct vcpu_svm *svm) /* Exit Guest-Mode */ leave_guest_mode(vcpu); - svm_pmu_handle_nested_transition(svm); + + if (!nested_vmcb12_has_vpmc(vcpu)) + svm_pmu_handle_nested_transition(svm); svm->nested.vmcb12_gpa = 0; @@ -1381,6 +1412,10 @@ void nested_svm_vmexit(struct vcpu_svm *svm) svm_update_lbrv(vcpu); + if (!nested_vmcb12_has_vpmc(vcpu) && + kvm_vcpu_has_mediated_pmu_caps(vcpu, KVM_MEDIATED_PMU_CAP_HW_SWITCHED)) + svm_copy_pmcs(&vmcb01->save, &vmcb02->save); + if (vnmi) { if (vmcb02->control.int_ctl & V_NMI_BLOCKING_MASK) vmcb01->control.int_ctl |= V_NMI_BLOCKING_MASK; @@ -1545,7 +1580,8 @@ void svm_leave_nested(struct kvm_vcpu *vcpu) * into PMU state from arbitrary contexts (e.g. to avoid using * stale state). */ - __svm_pmu_handle_nested_transition(svm, true); + if (!nested_vmcb12_has_vpmc(vcpu)) + __svm_pmu_handle_nested_transition(svm, true); svm_switch_vmcb(svm, &svm->vmcb01); @@ -2074,6 +2110,9 @@ static int svm_set_nested_state(struct kvm_vcpu *vcpu, nested_vmcb02_prepare_control(svm); + if (!nested_vmcb12_has_vpmc(vcpu)) + svm_pmu_handle_nested_transition(svm); + /* * Any previously restored state (e.g. KVM_SET_SREGS) would mark fields * dirty in vmcb01 instead of vmcb02, so mark all of vmcb02 dirty here. diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 079b1a56e995..2023d38bba63 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -5583,6 +5583,10 @@ static __init void svm_set_cpu_caps(void) if (vnmi) kvm_cpu_cap_set(X86_FEATURE_VNMI); + /* Nested AVIC is not supported, so VNMI must be enabled */ + if (vpmc && vnmi) + kvm_cpu_cap_set(X86_FEATURE_PERFCTR_VIRT); + /* Nested VM can receive #VMEXIT instead of triggering #GP */ kvm_cpu_cap_set(X86_FEATURE_SVME_ADDR_CHK); } diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h index eee26f6ce10c..3db6b73e8163 100644 --- a/arch/x86/kvm/svm/svm.h +++ b/arch/x86/kvm/svm/svm.h @@ -175,6 +175,12 @@ struct vmcb_save_area_cached { u64 br_to; u64 last_excp_from; u64 last_excp_to; + struct { + u64 perf_ctl; + u64 perf_ctr; + } pmc[6]; + u64 perf_cntr_global_status; + u64 perf_cntr_global_control; }; struct vmcb_ctrl_area_cached { @@ -806,6 +812,18 @@ do { \ (to)->last_excp_to = (from)->last_excp_to; \ } while (0) +#define svm_copy_pmcs(to, from) \ +do { \ + int i; \ + \ + (to)->perf_cntr_global_control = (from)->perf_cntr_global_control; \ + (to)->perf_cntr_global_status = (from)->perf_cntr_global_status; \ + for (i = 0; i < kvm_pmu_cap.num_counters_gp; i++) { \ + (to)->pmc[i].perf_ctl = (from)->pmc[i].perf_ctl; \ + (to)->pmc[i].perf_ctr = (from)->pmc[i].perf_ctr; \ + } \ +} while (0) + void svm_vcpu_free_msrpm(void *msrpm); void svm_enable_lbrv(struct kvm_vcpu *vcpu); void svm_update_lbrv(struct kvm_vcpu *vcpu); -- 2.53.0