When emulating a MOV DR on Intel with DR7.GD=1 at CPL>0, prioritize the #DB due to DR7.GD over the #GP due to CPL>0, as empirical testing shows that Intel CPUs (Skylake, Icelake and Emerald Rapids) prioritize the DR7.GD #DB over all #GPs, whereas AMD CPUs prioritize the CPL>0 #GP (but not illegal value #GPs) over the #DB. Outside of the emulator, don't bother trying to provide the "correct" priority based on the virtual CPU model, as it's simply impossible to do so without intercepting *all* MOV DR accesses, which would result in a massive, unacceptable performance hit. Note, getting the priority right when advertising Intel on AMD would also require intercepting #GP, as SVM prioritizes all exceptions over the instruction intercept. Note, neither Intel's SDM nor AMD's APM says anything about the relative priority, hence the empirical testing. Arguably Intel's description of DR7.GD: causes a debug exception to be generated prior to any MOV instruction that accesses a debug register. implies that DR7.GD has higher priority. But that's a fairly weak argument as the statement would still hold true if the #GP due to CPL>0 had higher priority, as the #GP would prevent any access to a DR. Fixes: 3b88e41a4134 ("KVM: SVM: Add intercept check for accessing dr registers") Signed-off-by: Sean Christopherson --- arch/x86/kvm/emulate.c | 7 ++++++- arch/x86/kvm/vmx/vmx.c | 6 +++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index 127a21eeef66..b4dc57fe0bc9 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -3834,6 +3834,7 @@ static int check_cr_access(struct x86_emulate_ctxt *ctxt) static int check_dr_read(struct x86_emulate_ctxt *ctxt) { + bool is_intel = ctxt->ops->guest_cpuid_is_intel_compatible(ctxt); int dr = ctxt->modrm_reg; u64 cr4; @@ -3844,12 +3845,16 @@ static int check_dr_read(struct x86_emulate_ctxt *ctxt) if ((cr4 & X86_CR4_DE) && (dr == 4 || dr == 5)) return emulate_ud(ctxt); - if (ctxt->ops->cpl(ctxt)) + /* Intel CPUs prioritize the DR7.GD=1 #DB over the CPL>0 #GP. */ + if (!is_intel && ctxt->ops->cpl(ctxt)) return emulate_gp(ctxt, 0); if (ctxt->ops->get_effective_dr7(ctxt) & DR7_GD) return emulate_db(ctxt, DR6_BD); + if (is_intel && ctxt->ops->cpl(ctxt)) + return emulate_gp(ctxt, 0); + return X86EMUL_CONTINUE; } diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index c548f22375ad..2f13d3163367 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -5750,9 +5750,6 @@ static int handle_dr(struct kvm_vcpu *vcpu) if (!kvm_require_dr(vcpu, dr)) return 1; - if (vmx_get_cpl(vcpu) > 0) - goto out; - dr7 = vmcs_readl(GUEST_DR7); if (dr7 & DR7_GD) { /* @@ -5773,6 +5770,9 @@ static int handle_dr(struct kvm_vcpu *vcpu) } } + if (vmx_get_cpl(vcpu) > 0) + goto out; + if (vcpu->guest_debug == 0) { exec_controls_clearbit(to_vmx(vcpu), CPU_BASED_MOV_DR_EXITING); -- 2.54.0.1136.gdb2ca164c4-goog