When L2 is running without LBR virtualization, we should ensure
that L1's LBR msrs continue to update as usual.

Signed-off-by: Maxim Levitsky <mlevi...@redhat.com>
---
 arch/x86/kvm/svm/nested.c | 11 +++++
 arch/x86/kvm/svm/svm.c    | 98 +++++++++++++++++++++++++++++++--------
 arch/x86/kvm/svm/svm.h    |  2 +
 3 files changed, 92 insertions(+), 19 deletions(-)

diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
index ac9159b0618c7..9f7bc7db08dd3 100644
--- a/arch/x86/kvm/svm/nested.c
+++ b/arch/x86/kvm/svm/nested.c
@@ -535,6 +535,9 @@ static void nested_vmcb02_prepare_save(struct vcpu_svm 
*svm, struct vmcb *vmcb12
                svm->vcpu.arch.dr6  = svm->nested.save.dr6 | DR6_ACTIVE_LOW;
                vmcb_mark_dirty(svm->vmcb, VMCB_DR);
        }
+
+       if (unlikely(svm->vmcb01.ptr->control.virt_ext & LBR_CTL_ENABLE_MASK))
+               svm_copy_lbrs(svm->vmcb01.ptr, svm->vmcb);
 }
 
 static void nested_vmcb02_prepare_control(struct vcpu_svm *svm)
@@ -587,6 +590,9 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm 
*svm)
        svm->vmcb->control.event_inj           = svm->nested.ctl.event_inj;
        svm->vmcb->control.event_inj_err       = svm->nested.ctl.event_inj_err;
 
+       svm->vmcb->control.virt_ext            = 
svm->vmcb01.ptr->control.virt_ext &
+                                                LBR_CTL_ENABLE_MASK;
+
        nested_svm_transition_tlb_flush(vcpu);
 
        /* Enter Guest-Mode */
@@ -852,6 +858,11 @@ int nested_svm_vmexit(struct vcpu_svm *svm)
 
        svm_switch_vmcb(svm, &svm->vmcb01);
 
+       if (unlikely(svm->vmcb->control.virt_ext & LBR_CTL_ENABLE_MASK)) {
+               svm_copy_lbrs(svm->nested.vmcb02.ptr, svm->vmcb);
+               svm_update_lbrv(vcpu);
+       }
+
        /*
         * On vmexit the  GIF is set to false and
         * no event can be injected in L1.
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index b88ca7f07a0fc..294e016f575a8 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -805,6 +805,17 @@ static void init_msrpm_offsets(void)
        }
 }
 
+void svm_copy_lbrs(struct vmcb *from_vmcb, struct vmcb *to_vmcb)
+{
+       to_vmcb->save.dbgctl            = from_vmcb->save.dbgctl;
+       to_vmcb->save.br_from           = from_vmcb->save.br_from;
+       to_vmcb->save.br_to             = from_vmcb->save.br_to;
+       to_vmcb->save.last_excp_from    = from_vmcb->save.last_excp_from;
+       to_vmcb->save.last_excp_to      = from_vmcb->save.last_excp_to;
+
+       vmcb_mark_dirty(to_vmcb, VMCB_LBR);
+}
+
 static void svm_enable_lbrv(struct kvm_vcpu *vcpu)
 {
        struct vcpu_svm *svm = to_svm(vcpu);
@@ -814,6 +825,10 @@ static void svm_enable_lbrv(struct kvm_vcpu *vcpu)
        set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTBRANCHTOIP, 1, 1);
        set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTINTFROMIP, 1, 1);
        set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTINTTOIP, 1, 1);
+
+       /* Move the LBR msrs to the vmcb02 so that the guest can see them. */
+       if (is_guest_mode(vcpu))
+               svm_copy_lbrs(svm->vmcb01.ptr, svm->vmcb);
 }
 
 static void svm_disable_lbrv(struct kvm_vcpu *vcpu)
@@ -825,6 +840,63 @@ static void svm_disable_lbrv(struct kvm_vcpu *vcpu)
        set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTBRANCHTOIP, 0, 0);
        set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTINTFROMIP, 0, 0);
        set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTINTTOIP, 0, 0);
+
+       /*
+        * Move the LBR msrs back to the vmcb01 to avoid copying them
+        * on nested guest entries.
+        */
+       if (is_guest_mode(vcpu))
+               svm_copy_lbrs(svm->vmcb, svm->vmcb01.ptr);
+}
+
+static int svm_get_lbr_msr(struct vcpu_svm *svm, u32 index)
+{
+       /*
+        * If the LBR virtualization is disabled, the LBR msrs are always
+        * kept in the vmcb01 to avoid copying them on nested guest entries.
+        *
+        * If nested, and the LBR virtualization is enabled/disabled, the msrs
+        * are moved between the vmcb01 and vmcb02 as needed.
+        */
+       struct vmcb *vmcb =
+               (svm->vmcb->control.virt_ext & LBR_CTL_ENABLE_MASK) ?
+                       svm->vmcb : svm->vmcb01.ptr;
+
+       switch (index) {
+       case MSR_IA32_DEBUGCTLMSR:
+               return vmcb->save.dbgctl;
+       case MSR_IA32_LASTBRANCHFROMIP:
+               return vmcb->save.br_from;
+       case MSR_IA32_LASTBRANCHTOIP:
+               return vmcb->save.br_to;
+       case MSR_IA32_LASTINTFROMIP:
+               return vmcb->save.last_excp_from;
+       case MSR_IA32_LASTINTTOIP:
+               return vmcb->save.last_excp_to;
+       default:
+               KVM_BUG(false, svm->vcpu.kvm,
+                       "%s: Unknown MSR 0x%x", __func__, index);
+               return 0;
+       }
+}
+
+void svm_update_lbrv(struct kvm_vcpu *vcpu)
+{
+       struct vcpu_svm *svm = to_svm(vcpu);
+
+       bool enable_lbrv = svm_get_lbr_msr(svm, MSR_IA32_DEBUGCTLMSR) &
+                                          DEBUGCTLMSR_LBR;
+
+       bool current_enable_lbrv = !!(svm->vmcb->control.virt_ext &
+                                     LBR_CTL_ENABLE_MASK);
+
+       if (enable_lbrv == current_enable_lbrv)
+               return;
+
+       if (enable_lbrv)
+               svm_enable_lbrv(vcpu);
+       else
+               svm_disable_lbrv(vcpu);
 }
 
 void disable_nmi_singlestep(struct vcpu_svm *svm)
@@ -2591,25 +2663,12 @@ static int svm_get_msr(struct kvm_vcpu *vcpu, struct 
msr_data *msr_info)
        case MSR_TSC_AUX:
                msr_info->data = svm->tsc_aux;
                break;
-       /*
-        * Nobody will change the following 5 values in the VMCB so we can
-        * safely return them on rdmsr. They will always be 0 until LBRV is
-        * implemented.
-        */
        case MSR_IA32_DEBUGCTLMSR:
-               msr_info->data = svm->vmcb->save.dbgctl;
-               break;
        case MSR_IA32_LASTBRANCHFROMIP:
-               msr_info->data = svm->vmcb->save.br_from;
-               break;
        case MSR_IA32_LASTBRANCHTOIP:
-               msr_info->data = svm->vmcb->save.br_to;
-               break;
        case MSR_IA32_LASTINTFROMIP:
-               msr_info->data = svm->vmcb->save.last_excp_from;
-               break;
        case MSR_IA32_LASTINTTOIP:
-               msr_info->data = svm->vmcb->save.last_excp_to;
+               msr_info->data = svm_get_lbr_msr(svm, msr_info->index);
                break;
        case MSR_VM_HSAVE_PA:
                msr_info->data = svm->nested.hsave_msr;
@@ -2840,12 +2899,13 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct 
msr_data *msr)
                if (data & DEBUGCTL_RESERVED_BITS)
                        return 1;
 
-               svm->vmcb->save.dbgctl = data;
-               vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
-               if (data & (1ULL<<0))
-                       svm_enable_lbrv(vcpu);
+               if (svm->vmcb->control.virt_ext & LBR_CTL_ENABLE_MASK)
+                       svm->vmcb->save.dbgctl = data;
                else
-                       svm_disable_lbrv(vcpu);
+                       svm->vmcb01.ptr->save.dbgctl = data;
+
+               svm_update_lbrv(vcpu);
+
                break;
        case MSR_VM_HSAVE_PA:
                /*
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index c02903641d13d..b83e06d5d942a 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -476,6 +476,8 @@ u32 svm_msrpm_offset(u32 msr);
 u32 *svm_vcpu_alloc_msrpm(void);
 void svm_vcpu_init_msrpm(struct kvm_vcpu *vcpu, u32 *msrpm);
 void svm_vcpu_free_msrpm(u32 *msrpm);
+void svm_copy_lbrs(struct vmcb *from_vmcb, struct vmcb *to_vmcb);
+void svm_update_lbrv(struct kvm_vcpu *vcpu);
 
 int svm_set_efer(struct kvm_vcpu *vcpu, u64 efer);
 void svm_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0);
-- 
2.26.3

Reply via email to