]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
KVM: SVM: Add missing save/restore handling of LBR MSRs
authorYosry Ahmed <yosry@kernel.org>
Tue, 3 Mar 2026 00:33:57 +0000 (00:33 +0000)
committerSean Christopherson <seanjc@google.com>
Thu, 5 Mar 2026 00:08:44 +0000 (16:08 -0800)
MSR_IA32_DEBUGCTLMSR and LBR MSRs are currently not enumerated by
KVM_GET_MSR_INDEX_LIST, and LBR MSRs cannot be set with KVM_SET_MSRS. So
save/restore is completely broken.

Fix it by adding the MSRs to msrs_to_save_base, and allowing writes to
LBR MSRs from userspace only (as they are read-only MSRs) if LBR
virtualization is enabled.  Additionally, to correctly restore L1's LBRs
while L2 is running, make sure the LBRs are copied from the captured
VMCB01 save area in svm_copy_vmrun_state().

Note, for VMX, this also fixes a flaw where MSR_IA32_DEBUGCTLMSR isn't
reported as an MSR to save/restore.

Note #2, over-reporting MSR_IA32_LASTxxx on Intel is ok, as KVM already
handles unsupported reads and writes thanks to commit b5e2fec0ebc3 ("KVM:
Ignore DEBUGCTL MSRs with no effect") (kvm_do_msr_access() will morph the
unsupported userspace write into a nop).

Fixes: 24e09cbf480a ("KVM: SVM: enable LBR virtualization")
Cc: stable@vger.kernel.org
Reported-by: Jim Mattson <jmattson@google.com>
Signed-off-by: Yosry Ahmed <yosry@kernel.org>
Link: https://patch.msgid.link/20260303003421.2185681-4-yosry@kernel.org
[sean: guard with lbrv checks, massage changelog]
Signed-off-by: Sean Christopherson <seanjc@google.com>
arch/x86/kvm/svm/nested.c
arch/x86/kvm/svm/svm.c
arch/x86/kvm/x86.c

index 9c64d036e30bd38193ade30c3cc3859a678f7937..2b1066ce23f5bfc1a39680901e9b24d8a5a32add 100644 (file)
@@ -1099,6 +1099,11 @@ void svm_copy_vmrun_state(struct vmcb_save_area *to_save,
                to_save->isst_addr = from_save->isst_addr;
                to_save->ssp = from_save->ssp;
        }
+
+       if (kvm_cpu_cap_has(X86_FEATURE_LBRV)) {
+               svm_copy_lbrs(to_save, from_save);
+               to_save->dbgctl &= ~DEBUGCTL_RESERVED_BITS;
+       }
 }
 
 void svm_copy_vmloadsave_state(struct vmcb *to_vmcb, struct vmcb *from_vmcb)
index 7170f2f623afe314e4d7e919cf545deabfde608f..e97c56df41f632d1bed755a944c9cda75db8fa92 100644 (file)
@@ -2787,19 +2787,19 @@ static int svm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
                msr_info->data = svm->tsc_aux;
                break;
        case MSR_IA32_DEBUGCTLMSR:
-               msr_info->data = svm->vmcb->save.dbgctl;
+               msr_info->data = lbrv ? svm->vmcb->save.dbgctl : 0;
                break;
        case MSR_IA32_LASTBRANCHFROMIP:
-               msr_info->data = svm->vmcb->save.br_from;
+               msr_info->data = lbrv ? svm->vmcb->save.br_from : 0;
                break;
        case MSR_IA32_LASTBRANCHTOIP:
-               msr_info->data = svm->vmcb->save.br_to;
+               msr_info->data = lbrv ? svm->vmcb->save.br_to : 0;
                break;
        case MSR_IA32_LASTINTFROMIP:
-               msr_info->data = svm->vmcb->save.last_excp_from;
+               msr_info->data = lbrv ? svm->vmcb->save.last_excp_from : 0;
                break;
        case MSR_IA32_LASTINTTOIP:
-               msr_info->data = svm->vmcb->save.last_excp_to;
+               msr_info->data = lbrv ? svm->vmcb->save.last_excp_to : 0;
                break;
        case MSR_VM_HSAVE_PA:
                msr_info->data = svm->nested.hsave_msr;
@@ -3074,6 +3074,38 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
                vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
                svm_update_lbrv(vcpu);
                break;
+       case MSR_IA32_LASTBRANCHFROMIP:
+               if (!lbrv)
+                       return KVM_MSR_RET_UNSUPPORTED;
+               if (!msr->host_initiated)
+                       return 1;
+               svm->vmcb->save.br_from = data;
+               vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
+               break;
+       case MSR_IA32_LASTBRANCHTOIP:
+               if (!lbrv)
+                       return KVM_MSR_RET_UNSUPPORTED;
+               if (!msr->host_initiated)
+                       return 1;
+               svm->vmcb->save.br_to = data;
+               vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
+               break;
+       case MSR_IA32_LASTINTFROMIP:
+               if (!lbrv)
+                       return KVM_MSR_RET_UNSUPPORTED;
+               if (!msr->host_initiated)
+                       return 1;
+               svm->vmcb->save.last_excp_from = data;
+               vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
+               break;
+       case MSR_IA32_LASTINTTOIP:
+               if (!lbrv)
+                       return KVM_MSR_RET_UNSUPPORTED;
+               if (!msr->host_initiated)
+                       return 1;
+               svm->vmcb->save.last_excp_to = data;
+               vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
+               break;
        case MSR_VM_HSAVE_PA:
                /*
                 * Old kernels did not validate the value written to
index 6e87ec52fa0640622638c48d4707419542338392..64da02d1ee0084c1251b2c4f554b33ea65cc80c8 100644 (file)
@@ -351,6 +351,9 @@ static const u32 msrs_to_save_base[] = {
        MSR_IA32_U_CET, MSR_IA32_S_CET,
        MSR_IA32_PL0_SSP, MSR_IA32_PL1_SSP, MSR_IA32_PL2_SSP,
        MSR_IA32_PL3_SSP, MSR_IA32_INT_SSP_TAB,
+       MSR_IA32_DEBUGCTLMSR,
+       MSR_IA32_LASTBRANCHFROMIP, MSR_IA32_LASTBRANCHTOIP,
+       MSR_IA32_LASTINTFROMIP, MSR_IA32_LASTINTTOIP,
 };
 
 static const u32 msrs_to_save_pmu[] = {