]> xenbits.xensource.com Git - xen.git/commitdiff
x86/emul: Implement singlestep as a retire flag
authorAndrew Cooper <andrew.cooper3@citrix.com>
Tue, 29 Nov 2016 17:56:17 +0000 (17:56 +0000)
committerAndrew Cooper <andrew.cooper3@citrix.com>
Fri, 2 Dec 2016 17:23:01 +0000 (17:23 +0000)
The behaviour of singlestep is to raise #DB after the instruction has been
completed, but implementing it with inject_hw_exception() causes x86_emulate()
to return X86EMUL_EXCEPTION, despite succesfully completing execution of the
instruction, including register writeback.

Instead, use a retire flag to indicate singlestep, which causes x86_emulate()
to return X86EMUL_OKAY.

Update all callers of x86_emulate() to use the new retire flag.  This fixes
the behaviour of singlestep for shadow pagetable updates and mmcfg/mmio_ro
intercepts, which previously discarded the exception.

With this change, all uses of X86EMUL_EXCEPTION from x86_emulate() are
believed to have strictly fault semantics.

Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Paul Durrant <paul.durrant@citrix.com>
Acked-by: Tim Deegan <tim@xen.org>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
xen/arch/x86/hvm/emulate.c
xen/arch/x86/mm.c
xen/arch/x86/mm/shadow/multi.c
xen/arch/x86/x86_emulate/x86_emulate.c
xen/arch/x86/x86_emulate/x86_emulate.h

index fe62500ef2a6620da34907bf8d7f00a7d379291f..91c79fa850b1e3f8fc30dea533663afbc345c416 100644 (file)
@@ -1788,6 +1788,9 @@ static int _hvm_emulate_one(struct hvm_emulate_ctxt *hvmemul_ctxt,
     if ( rc != X86EMUL_OKAY )
         return rc;
 
+    if ( hvmemul_ctxt->ctxt.retire.singlestep )
+        hvm_inject_hw_exception(TRAP_debug, X86_EVENT_NO_EC);
+
     new_intr_shadow = hvmemul_ctxt->intr_shadow;
 
     /* MOV-SS instruction toggles MOV-SS shadow, else we just clear it. */
index b7c712265eaf1e5e3b9007f3365db3247c3f8b57..231c7bf0255e131130a816eab3889460e7f9b66d 100644 (file)
@@ -5382,6 +5382,9 @@ int ptwr_do_page_fault(struct vcpu *v, unsigned long addr,
     if ( rc == X86EMUL_UNHANDLEABLE )
         goto bail;
 
+    if ( ptwr_ctxt.ctxt.retire.singlestep )
+        pv_inject_hw_exception(TRAP_debug, X86_EVENT_NO_EC);
+
     perfc_incr(ptwr_emulations);
     return EXCRET_fault_fixed;
 
@@ -5503,7 +5506,13 @@ int mmio_ro_do_page_fault(struct vcpu *v, unsigned long addr,
     else
         rc = x86_emulate(&ctxt, &mmio_ro_emulate_ops);
 
-    return rc != X86EMUL_UNHANDLEABLE ? EXCRET_fault_fixed : 0;
+    if ( rc == X86EMUL_UNHANDLEABLE )
+        return 0;
+
+    if ( ctxt.retire.singlestep )
+        pv_inject_hw_exception(TRAP_debug, X86_EVENT_NO_EC);
+
+    return EXCRET_fault_fixed;
 }
 
 void *alloc_xen_pagetable(void)
index 9ee48a891e0eaf61daa113f66d8deb42397c385e..eac2330ae0ce36311167ecde16d378d95a2c1770 100644 (file)
@@ -3422,18 +3422,36 @@ static int sh_page_fault(struct vcpu *v,
         v->arch.paging.last_write_emul_ok = 0;
 #endif
 
+    if ( emul_ctxt.ctxt.retire.singlestep )
+    {
+        if ( has_hvm_container_domain(d) )
+            hvm_inject_hw_exception(TRAP_debug, X86_EVENT_NO_EC);
+        else
+            pv_inject_hw_exception(TRAP_debug, X86_EVENT_NO_EC);
+    }
+
 #if GUEST_PAGING_LEVELS == 3 /* PAE guest */
-    if ( r == X86EMUL_OKAY ) {
+    /*
+     * If there are no pending actions, emulate up to four extra instructions
+     * in the hope of catching the "second half" of a 64-bit pagetable write.
+     */
+    if ( r == X86EMUL_OKAY && !emul_ctxt.ctxt.retire.raw )
+    {
         int i, emulation_count=0;
         this_cpu(trace_emulate_initial_va) = va;
-        /* Emulate up to four extra instructions in the hope of catching
-         * the "second half" of a 64-bit pagetable write. */
+
         for ( i = 0 ; i < 4 ; i++ )
         {
             shadow_continue_emulation(&emul_ctxt, regs);
             v->arch.paging.last_write_was_pt = 0;
             r = x86_emulate(&emul_ctxt.ctxt, emul_ops);
-            if ( r == X86EMUL_OKAY )
+
+            /*
+             * Only continue the search for the second half if there are no
+             * exceptions or pending actions.  Otherwise, give up and re-enter
+             * the guest.
+             */
+            if ( r == X86EMUL_OKAY && !emul_ctxt.ctxt.retire.raw )
             {
                 emulation_count++;
                 if ( v->arch.paging.last_write_was_pt )
@@ -3449,6 +3467,15 @@ static int sh_page_fault(struct vcpu *v,
             {
                 perfc_incr(shadow_em_ex_fail);
                 TRACE_SHADOW_PATH_FLAG(TRCE_SFLAG_EMULATION_LAST_FAILED);
+
+                if ( emul_ctxt.ctxt.retire.singlestep )
+                {
+                    if ( has_hvm_container_domain(d) )
+                        hvm_inject_hw_exception(TRAP_debug, X86_EVENT_NO_EC);
+                    else
+                        pv_inject_hw_exception(TRAP_debug, X86_EVENT_NO_EC);
+                }
+
                 break; /* Don't emulate again if we failed! */
             }
         }
index fa8d98f7d380f6d4a32ed247e6ac690e4a54ea78..7bc1cd916bab71d81a655995ebd9ab1ba29dd079 100644 (file)
@@ -2415,7 +2415,6 @@ x86_emulate(
     struct x86_emulate_state state;
     int rc;
     uint8_t b, d;
-    bool tf = ctxt->regs->eflags & EFLG_TF;
     struct operand src = { .reg = PTR_POISON };
     struct operand dst = { .reg = PTR_POISON };
     enum x86_swint_type swint_type;
@@ -5413,11 +5412,11 @@ x86_emulate(
     if ( !mode_64bit() )
         _regs.eip = (uint32_t)_regs.eip;
 
-    *ctxt->regs = _regs;
+    /* Was singestepping active at the start of this instruction? */
+    if ( (rc == X86EMUL_OKAY) && (ctxt->regs->eflags & EFLG_TF) )
+        ctxt->retire.singlestep = true;
 
-    /* Inject #DB if single-step tracing was enabled at instruction start. */
-    if ( tf && (rc == X86EMUL_OKAY) && ops->inject_hw_exception )
-        rc = ops->inject_hw_exception(EXC_DB, -1, ctxt) ? : X86EMUL_EXCEPTION;
+    *ctxt->regs = _regs;
 
  done:
     _put_fpu();
index f4bcf36d32643ccd2da858909e34ad7e22367504..5a4f9b788542352e4e55cdb0f1700c3f3a3c8ba9 100644 (file)
@@ -483,6 +483,7 @@ struct x86_emulate_ctxt
             bool hlt:1;          /* Instruction HLTed. */
             bool mov_ss:1;       /* Instruction sets MOV-SS irq shadow. */
             bool sti:1;          /* Instruction sets STI irq shadow. */
+            bool singlestep:1;   /* Singlestepping was active. */
         };
     } retire;
 };
@@ -572,12 +573,17 @@ static inline int x86_emulate_wrapper(
     struct x86_emulate_ctxt *ctxt,
     const struct x86_emulate_ops *ops)
 {
+    unsigned long orig_eip = ctxt->regs->eip;
     int rc = x86_emulate(ctxt, ops);
 
     /* Retire flags should only be set for successful instruction emulation. */
     if ( rc != X86EMUL_OKAY )
         ASSERT(ctxt->retire.raw == 0);
 
+    /* All cases returning X86EMUL_EXCEPTION should have fault semantics. */
+    if ( rc == X86EMUL_EXCEPTION )
+        ASSERT(ctxt->regs->eip == orig_eip);
+
     return rc;
 }