KVM: SVM: Manually context switch DEBUGCTL if LBR virtualization is disabled

Manually load the guest's DEBUGCTL prior to VMRUN (and restore the host's
value on #VMEXIT) if it diverges from the host's value and LBR
virtualization is disabled, as hardware only context switches DEBUGCTL if
LBR virtualization is fully enabled. Running the guest with the host's
value has likely been mildly problematic for quite some time, e.g. it will
result in undesirable behavior if BTF diverges (with the caveat that KVM
now suppresses guest BTF due to lack of support).

But the bug became fatal with the introduction of Bus Lock Trap ("Detect"
in kernel paralance) support for AMD (commit 408eb7417a92
("x86/bus_lock: Add support for AMD")), as a bus lock in the guest will
trigger an unexpected #DB.

Note, suppressing the bus lock #DB, i.e. simply resuming the guest without
injecting a #DB, is not an option. It wouldn't address the general issue
with DEBUGCTL, e.g. for things like BTF, and there are other guest-visible
side effects if BusLockTrap is left enabled.

If BusLockTrap is disabled, then DR6.BLD is reserved-to-1; any attempts to
clear it by software are ignored. But if BusLockTrap is enabled, software
can clear DR6.BLD:

Software enables bus lock trap by setting DebugCtl MSR[BLCKDB] (bit 2)
to 1. When bus lock trap is enabled, ... The processor indicates that
this #DB was caused by a bus lock by clearing DR6[BLD] (bit 11). DR6[11]
previously had been defined to be always 1.

and clearing DR6.BLD is "sticky" in that it's not set (i.e. lowered) by
other #DBs:

All other #DB exceptions leave DR6[BLD] unmodified

E.g. leaving BusLockTrap enable can confuse a legacy guest that writes '0'
to reset DR6.

Reported-by: rangemachine@gmail.com
Reported-by: whanos@sergal.fun
Closes: https://bugzilla.kernel.org/show_bug.cgi?id=219787
Closes: https://lore.kernel.org/all/bug-219787-28872@https.bugzilla.kernel.org%2F
Cc: Ravi Bangoria <ravi.bangoria@amd.com>
Cc: stable@vger.kernel.org
Reviewed-and-tested-by: Ravi Bangoria <ravi.bangoria@amd.com>
Link: https://lore.kernel.org/r/20250227222411.3490595-5-seanjc@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>

+14
+14
arch/x86/kvm/svm/svm.c
··· 4288 4288 clgi(); 4289 4289 kvm_load_guest_xsave_state(vcpu); 4290 4290 4291 + /* 4292 + * Hardware only context switches DEBUGCTL if LBR virtualization is 4293 + * enabled. Manually load DEBUGCTL if necessary (and restore it after 4294 + * VM-Exit), as running with the host's DEBUGCTL can negatively affect 4295 + * guest state and can even be fatal, e.g. due to Bus Lock Detect. 4296 + */ 4297 + if (!(svm->vmcb->control.virt_ext & LBR_CTL_ENABLE_MASK) && 4298 + vcpu->arch.host_debugctl != svm->vmcb->save.dbgctl) 4299 + update_debugctlmsr(svm->vmcb->save.dbgctl); 4300 + 4291 4301 kvm_wait_lapic_expire(vcpu); 4292 4302 4293 4303 /* ··· 4324 4314 4325 4315 if (unlikely(svm->vmcb->control.exit_code == SVM_EXIT_NMI)) 4326 4316 kvm_before_interrupt(vcpu, KVM_HANDLING_NMI); 4317 + 4318 + if (!(svm->vmcb->control.virt_ext & LBR_CTL_ENABLE_MASK) && 4319 + vcpu->arch.host_debugctl != svm->vmcb->save.dbgctl) 4320 + update_debugctlmsr(vcpu->arch.host_debugctl); 4327 4321 4328 4322 kvm_load_host_xsave_state(vcpu); 4329 4323 stgi();