KVM: x86: Add a switch_db_regs flag to handle TDX's auto-switched behavior
authorIsaku Yamahata <isaku.yamahata@intel.com>
Wed, 29 Jan 2025 09:59:01 +0000 (11:59 +0200)
committerPaolo Bonzini <pbonzini@redhat.com>
Fri, 14 Mar 2025 18:20:54 +0000 (14:20 -0400)
Add a flag KVM_DEBUGREG_AUTO_SWITCH to skip saving/restoring guest
DRs.

TDX-SEAM unconditionally saves/restores guest DRs on TD exit/enter,
and resets DRs to architectural INIT state on TD exit.  Use the new
flag KVM_DEBUGREG_AUTO_SWITCH to indicate that KVM doesn't need to
save/restore guest DRs.  KVM still needs to restore host DRs after TD
exit if there are active breakpoints in the host, which is covered by
the existing code.

MOV-DR exiting is always cleared for TDX guests, so the handler for DR
access is never called, and KVM_DEBUGREG_WONT_EXIT is never set.  Add
a warning if both KVM_DEBUGREG_WONT_EXIT and KVM_DEBUGREG_AUTO_SWITCH
are set.

Opportunistically convert the KVM_DEBUGREG_* definitions to use BIT().

Reported-by: Xiaoyao Li <xiaoyao.li@intel.com>
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
Co-developed-by: Chao Gao <chao.gao@intel.com>
Signed-off-by: Chao Gao <chao.gao@intel.com>
Signed-off-by: Isaku Yamahata <isaku.yamahata@intel.com>
[binbin: rework changelog]
Signed-off-by: Binbin Wu <binbin.wu@linux.intel.com>
Message-ID: <20241210004946.3718496-2-binbin.wu@linux.intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Message-ID: <20250129095902.16391-13-adrian.hunter@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
arch/x86/include/asm/kvm_host.h
arch/x86/kvm/vmx/tdx.c
arch/x86/kvm/x86.c

index 0c8453fa9f9e1d6864c097aaad26ef0f90665795..3a6373fc58a169c8e64ff1cae012bfddc7aafc34 100644 (file)
@@ -606,8 +606,15 @@ struct kvm_pmu {
 struct kvm_pmu_ops;
 
 enum {
-       KVM_DEBUGREG_BP_ENABLED = 1,
-       KVM_DEBUGREG_WONT_EXIT = 2,
+       KVM_DEBUGREG_BP_ENABLED         = BIT(0),
+       KVM_DEBUGREG_WONT_EXIT          = BIT(1),
+       /*
+        * Guest debug registers (DR0-3, DR6 and DR7) are saved/restored by
+        * hardware on exit from or enter to guest. KVM needn't switch them.
+        * DR0-3, DR6 and DR7 are set to their architectural INIT value on VM
+        * exit, host values need to be restored.
+        */
+       KVM_DEBUGREG_AUTO_SWITCH        = BIT(2),
 };
 
 struct kvm_mtrr {
index 7ad680b5f771aff237ee8cd67ed6900f365594ec..a6388eb95988b3cc313136bb02bf6b9c9bdeb2d2 100644 (file)
@@ -630,6 +630,7 @@ int tdx_vcpu_create(struct kvm_vcpu *vcpu)
 
        vcpu->arch.efer = EFER_SCE | EFER_LME | EFER_LMA | EFER_NX;
 
+       vcpu->arch.switch_db_regs = KVM_DEBUGREG_AUTO_SWITCH;
        vcpu->arch.cr0_guest_owned_bits = -1ul;
        vcpu->arch.cr4_guest_owned_bits = -1ul;
 
index 1133bec79a826f9053c10f79a748cf225d014bcd..1ef83f24449d60848d27b58382320f31e95a0ed5 100644 (file)
@@ -10985,7 +10985,8 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
        if (vcpu->arch.guest_fpu.xfd_err)
                wrmsrl(MSR_IA32_XFD_ERR, vcpu->arch.guest_fpu.xfd_err);
 
-       if (unlikely(vcpu->arch.switch_db_regs)) {
+       if (unlikely(vcpu->arch.switch_db_regs &&
+                    !(vcpu->arch.switch_db_regs & KVM_DEBUGREG_AUTO_SWITCH))) {
                set_debugreg(0, 7);
                set_debugreg(vcpu->arch.eff_db[0], 0);
                set_debugreg(vcpu->arch.eff_db[1], 1);
@@ -11037,6 +11038,7 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
         */
        if (unlikely(vcpu->arch.switch_db_regs & KVM_DEBUGREG_WONT_EXIT)) {
                WARN_ON(vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP);
+               WARN_ON(vcpu->arch.switch_db_regs & KVM_DEBUGREG_AUTO_SWITCH);
                kvm_x86_call(sync_dirty_debug_regs)(vcpu);
                kvm_update_dr0123(vcpu);
                kvm_update_dr7(vcpu);