diff mbox series

[6/6] x86/debug: actually plumb pending_dbg through the monitor and devicemodel interfaces

Message ID 231aa9dc-ca45-491f-26d2-72b4365219a6@gmail.com (mailing list archive)
State New, archived
Headers show
Series x86/debug: fix guest dr6 value for single stepping and HW breakpoints | expand

Commit Message

Jinoh Kang Aug. 18, 2023, 3:47 p.m. UTC
Commit 21867648033d ("x86/debug: Plumb pending_dbg through the monitor
and devicemodel interfaces") introduced pending_dbg, but did not
actually populate or use the field.

Signed-off-by: Jinoh Kang <jinoh.kang.kr@gmail.com>
---
 xen/arch/x86/hvm/svm/svm.c | 34 +++++++++++++++++++++++++++++++---
 xen/arch/x86/hvm/vmx/vmx.c | 32 ++++++++++++++++++++++++--------
 2 files changed, 55 insertions(+), 11 deletions(-)
diff mbox series

Patch

diff --git a/xen/arch/x86/hvm/svm/svm.c b/xen/arch/x86/hvm/svm/svm.c
index f25d6a53f092..139be9902dae 100644
--- a/xen/arch/x86/hvm/svm/svm.c
+++ b/xen/arch/x86/hvm/svm/svm.c
@@ -2422,6 +2422,14 @@  static bool cf_check svm_get_pending_event(
     info->type = vmcb->event_inj.type;
     info->error_code = vmcb->event_inj.ec;
 
+    if ( info->type == X86_EVENTTYPE_HW_EXCEPTION &&
+         info->vector == X86_EXC_DB )
+    {
+        unsigned long dr6 = v->arch.hvm.flag_dr_dirty ?
+                            vmcb_get_dr6(vmcb) : v->arch.dr6;
+        info->extra = dr6 & ~DR_STATUS_RESERVED_ONE;
+    }
+
     return true;
 }
 
@@ -2733,16 +2741,28 @@  void svm_vmexit_handler(void)
         if ( !v->domain->debugger_attached )
         {
             unsigned int trap_type;
+            unsigned long exit_pending_dbg;
 
             if ( likely(exit_reason != VMEXIT_ICEBP) )
             {
                 trap_type = X86_EVENTTYPE_HW_EXCEPTION;
                 insn_len = 0;
+
+                __restore_debug_registers(vmcb, v);
+
+                /*
+                 * NOTE: This is slightly wrong; old bits in dr6 are not
+                 * automatically cleared by CPU on #DB, so it's not exactly
+                 * equivalent to PENDING_DBG_EXCEPTIONS in semantics.
+                 */
+                exit_pending_dbg = vmcb_get_dr6(vmcb) & ~DR_STATUS_RESERVED_ONE;
+                vmcb_set_dr6(vmcb, DR_STATUS_RESERVED_ONE);
             }
             else
             {
                 trap_type = X86_EVENTTYPE_PRI_SW_EXCEPTION;
                 insn_len = svm_get_insn_len(v, INSTR_ICEBP);
+                exit_pending_dbg = 0;
 
                 if ( !insn_len )
                     break;
@@ -2750,12 +2770,20 @@  void svm_vmexit_handler(void)
 
             rc = hvm_monitor_debug(regs->rip,
                                    HVM_MONITOR_DEBUG_EXCEPTION,
-                                   trap_type, insn_len, 0);
+                                   trap_type, insn_len, exit_pending_dbg);
             if ( rc < 0 )
                 goto unexpected_exit_type;
             if ( !rc )
-                hvm_inject_exception(X86_EXC_DB,
-                                     trap_type, insn_len, X86_EVENT_NO_EC);
+            {
+                if (trap_type == X86_EVENTTYPE_HW_EXCEPTION)
+                {
+                    /* Updates DR6 where debugger can peek. */
+                    hvm_inject_debug_exception(exit_pending_dbg);
+                }
+                else
+                    hvm_inject_exception(X86_EXC_DB,
+                                         trap_type, insn_len, X86_EVENT_NO_EC);
+            }
         }
         else
             domain_pause_for_debugger();
diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c
index 1795b9479cf9..63411b62cb94 100644
--- a/xen/arch/x86/hvm/vmx/vmx.c
+++ b/xen/arch/x86/hvm/vmx/vmx.c
@@ -2469,6 +2469,14 @@  static bool cf_check vmx_get_pending_event(
     info->type = MASK_EXTR(intr_info, INTR_INFO_INTR_TYPE_MASK);
     info->error_code = error_code;
 
+    if ( info->type == X86_EVENTTYPE_HW_EXCEPTION &&
+         info->vector == X86_EXC_DB )
+    {
+        unsigned long dr6 = v->arch.hvm.flag_dr_dirty ?
+                            read_debugreg(6) : v->arch.dr6;
+        info->extra = dr6 & ~DR_STATUS_RESERVED_ONE;
+    }
+
     return true;
 }
 
@@ -4240,13 +4248,11 @@  void vmx_vmexit_handler(struct cpu_user_regs *regs)
         {
         case X86_EXC_DB:
             /*
-             * Updates DR6 where debugger can peek (See 3B 23.2.1,
-             * Table 23-1, "Exit Qualification for Debug Exceptions").
+             * See 3B 23.2.1, Table 23-1, "Exit Qualification for Debug
+             * Exceptions".
              */
             __vmread(EXIT_QUALIFICATION, &exit_qualification);
             HVMTRACE_1D(TRAP_DEBUG, exit_qualification);
-            __restore_debug_registers(v);
-            write_debugreg(6, exit_qualification | DR_STATUS_RESERVED_ONE);
 
             /*
              * Work around SingleStep + STI/MovSS VMEntry failures.
@@ -4285,22 +4291,32 @@  void vmx_vmexit_handler(struct cpu_user_regs *regs)
 
             if ( !v->domain->debugger_attached )
             {
-                unsigned long insn_len = 0;
+                unsigned long exit_pending_dbg = 0, insn_len = 0;
                 int rc;
                 unsigned long trap_type = MASK_EXTR(intr_info,
                                                     INTR_INFO_INTR_TYPE_MASK);
 
-                if ( trap_type >= X86_EVENTTYPE_SW_INTERRUPT )
+                if ( trap_type == X86_EVENTTYPE_HW_EXCEPTION )
+                    exit_pending_dbg = exit_qualification;
+                else if ( trap_type >= X86_EVENTTYPE_SW_INTERRUPT )
                     __vmread(VM_EXIT_INSTRUCTION_LEN, &insn_len);
 
                 rc = hvm_monitor_debug(regs->rip,
                                        HVM_MONITOR_DEBUG_EXCEPTION,
-                                       trap_type, insn_len, 0);
+                                       trap_type, insn_len, exit_pending_dbg);
 
                 if ( rc < 0 )
                     goto exit_and_crash;
                 if ( !rc )
-                    vmx_propagate_intr(intr_info);
+                {
+                    if ( trap_type == X86_EVENTTYPE_HW_EXCEPTION )
+                    {
+                        /* Updates DR6 where debugger can peek. */
+                        hvm_inject_debug_exception(exit_pending_dbg);
+                    }
+                    else
+                        vmx_propagate_intr(intr_info);
+                }
             }
             else
                 domain_pause_for_debugger();