Skip to contentSkip to conversation

x86: more debugging adjustments

RFC In review Jan Beulich 2 participants 4 messages Nov 22, 2007

lore
Share & tools
lore
#0 lore raw

RFC 1/1 x86: more debugging adjustments

Apart from the cleanup patches just sent I also have two bigger ones in the queue. I'm not certain they qualify for 3.2 at this point, though: While most of them can be considered enhancements, the io-brkp one (which depends on the inject-sstep one) also fixes the issue of guests on x86-64 not being able to utilize 8-byte breakpoints, and it reduces the likelihood of needing to restore the debug registers by only looking at the enable bits in the respective conditions (as HVM was already doing).

Therefore I'd like to understand whether these patches can go in in their current shape, or whether I should split out at least the 8-byte bug fix and submit this separately.

Thanks, Jan

xen/arch/x86/domain.c +3 -2
Index: 2007-11-13/xen/arch/x86/domain.c
===================================================================
@@ -42,6 +42,7 @@
 #include <asm/hypercall.h>
 #include <asm/hvm/hvm.h>
 #include <asm/hvm/support.h>
+#include <asm/debugreg.h>
 #include <asm/msr.h>
 #include <asm/nmi.h>
 #include <asm/iommu.h>
@@ -1219,7 +1220,7 @@ static void paravirt_ctxt_switch_from(st
      * inside Xen, before we get a chance to reload DR7, and this cannot always
      * safely be handled.
      */
-    if ( unlikely(v->arch.guest_context.debugreg[7]) )
+    if ( unlikely(v->arch.guest_context.debugreg[7] & DR7_ACTIVE_MASK) )
         write_debugreg(7, 0);
 }
 
@@ -1234,7 +1235,7 @@ static void paravirt_ctxt_switch_to(stru
     if ( unlikely(cr4 != read_cr4()) )
         write_cr4(cr4);
 
-    if ( unlikely(v->arch.guest_context.debugreg[7]) )
+    if ( unlikely(v->arch.guest_context.debugreg[7] & DR7_ACTIVE_MASK) )
     {
         write_debugreg(0, v->arch.guest_context.debugreg[0]);
         write_debugreg(1, v->arch.guest_context.debugreg[1]);
xen/arch/x86/domctl.c +6 -0
Index: 2007-11-13/xen/arch/x86/domctl.c
===================================================================
@@ -825,12 +825,18 @@ void arch_get_info_guest(struct vcpu *v,
                 c.nat->ctrlreg[1] = xen_pfn_to_cr3(
                     pagetable_get_pfn(v->arch.guest_table_user));
 #endif
+
+            c.nat->debugreg[7] |= c.nat->debugreg[5];
+            c.nat->debugreg[5] = 0;
         }
 #ifdef CONFIG_COMPAT
         else
         {
             l4_pgentry_t *l4e = __va(pagetable_get_paddr(v->arch.guest_table));
             c.cmp->ctrlreg[3] = compat_pfn_to_cr3(l4e_get_pfn(*l4e));
+
+            c.cmp->debugreg[7] |= c.cmp->debugreg[5];
+            c.cmp->debugreg[5] = 0;
         }
 #endif
 
xen/arch/x86/hvm/svm/svm.c +1 -2
Index: 2007-11-13/xen/arch/x86/hvm/svm/svm.c
===================================================================
@@ -34,6 +34,7 @@
 #include <asm/cpufeature.h>
 #include <asm/processor.h>
 #include <asm/types.h>
+#include <asm/debugreg.h>
 #include <asm/msr.h>
 #include <asm/spinlock.h>
 #include <asm/hvm/hvm.h>
@@ -189,8 +190,6 @@ static void __restore_debug_registers(st
  * if one of the breakpoints is enabled.  So mask out all bits that don't
  * enable some breakpoint functionality.
  */
-#define DR7_ACTIVE_MASK 0xff
-
 static void svm_restore_dr(struct vcpu *v)
 {
     if ( unlikely(v->arch.guest_context.debugreg[7] & DR7_ACTIVE_MASK) )
xen/arch/x86/hvm/vmx/vmx.c +1 -2
Index: 2007-11-13/xen/arch/x86/hvm/vmx/vmx.c
===================================================================
@@ -33,6 +33,7 @@
 #include <asm/cpufeature.h>
 #include <asm/processor.h>
 #include <asm/types.h>
+#include <asm/debugreg.h>
 #include <asm/msr.h>
 #include <asm/spinlock.h>
 #include <asm/paging.h>
@@ -434,8 +435,6 @@ static void __restore_debug_registers(st
  * if one of the breakpoints is enabled.  So mask out all bits that don't
  * enable some breakpoint functionality.
  */
-#define DR7_ACTIVE_MASK 0xff
-
 static void vmx_restore_dr(struct vcpu *v)
 {
     /* NB. __vmread() is not usable here, so we cannot read from the VMCS. */
xen/arch/x86/traps.c +94 -27

238 lines not on this page · raw message

xen/include/asm-x86/debugreg.h +9 -3
Index: 2007-11-13/xen/include/asm-x86/debugreg.h
===================================================================
@@ -33,11 +33,13 @@
 
 #define DR_RW_EXECUTE (0x0)   /* Settings for the access types to trap on */
 #define DR_RW_WRITE (0x1)
+#define DR_IO (0x2)
 #define DR_RW_READ (0x3)
 
 #define DR_LEN_1 (0x0) /* Settings for data length to trap on */
 #define DR_LEN_2 (0x4)
 #define DR_LEN_4 (0xC)
+#define DR_LEN_8 (0x8)
 
 /* The low byte to the control register determine which registers are
    enabled.  There are 4 fields of two bits.  One bit is "local", meaning
@@ -53,12 +55,16 @@
 #define DR_LOCAL_ENABLE_MASK (0x55)  /* Set  local bits for all 4 regs */
 #define DR_GLOBAL_ENABLE_MASK (0xAA) /* Set global bits for all 4 regs */
 
+#define DR7_ACTIVE_MASK (DR_LOCAL_ENABLE_MASK|DR_GLOBAL_ENABLE_MASK)
+
 /* The second byte to the control register has a few special things.
    We can slow the instruction pipeline for instructions coming via the
    gdt or the ldt if we want to.  I am not sure why this is an advantage */
 
-#define DR_CONTROL_RESERVED (~0xFFFF03FFUL) /* Reserved by Intel */
-#define DR_LOCAL_SLOWDOWN (0x100)   /* Local slow the pipeline */
-#define DR_GLOBAL_SLOWDOWN (0x200)  /* Global slow the pipeline */
+#define DR_CONTROL_RESERVED_ZERO (~0xFFFF23FFUL) /* Reserved, read as zero */
+#define DR_CONTROL_RESERVED_ONE  ( 0x00000400  ) /* Reserved, read as one */
+#define DR_LOCAL_EXACT_ENABLE    ( 0x00000100  ) /* Local exact enable */
+#define DR_GLOBAL_EXACT_ENABLE   ( 0x00000200  ) /* Global exact enable */
+#define DR_GENERAL_DETECT        ( 0x00002000  ) /* General detect enable */
 
 #endif /* _X86_DEBUGREG_H */
xen/arch/x86/hvm/io.c +2 -0
Index: 2007-11-13/xen/arch/x86/hvm/io.c
===================================================================
@@ -863,6 +863,8 @@ void hvm_io_assist(void)
     /* Copy register changes back into current guest state. */
     regs->eflags &= ~X86_EFLAGS_RF;
     memcpy(guest_cpu_user_regs(), regs, HVM_CONTEXT_STACK_BYTES);
+    if ( regs->eflags & X86_EFLAGS_TF )
+        hvm_inject_exception(TRAP_debug, HVM_DELIVER_NO_ERROR_CODE, 0);
 
  out:
     vcpu_end_shutdown_deferral(v);
xen/arch/x86/hvm/platform.c +0 -3
Index: 2007-11-13/xen/arch/x86/hvm/platform.c
===================================================================
@@ -1061,7 +1061,6 @@ void handle_mmio(unsigned long gpa)
     }
 
     regs->eip += inst_len; /* advance %eip */
-    regs->eflags &= ~X86_EFLAGS_RF;
 
     switch ( mmio_op->instr ) {
     case INSTR_MOV:
@@ -1121,7 +1120,6 @@ void handle_mmio(unsigned long gpa)
             /* The guest does not have the non-mmio address mapped. 
              * Need to send in a page fault */
             regs->eip -= inst_len; /* do not advance %eip */
-            regs->eflags |= X86_EFLAGS_RF; /* RF was set by original #PF */
             hvm_inject_exception(TRAP_page_fault, pfec, addr);
             return;
         }
@@ -1150,7 +1148,6 @@ void handle_mmio(unsigned long gpa)
                         /* Failed on the page-spanning copy.  Inject PF into
                          * the guest for the address where we failed */
                         regs->eip -= inst_len; /* do not advance %eip */
-                        regs->eflags |= X86_EFLAGS_RF; /* RF was set by #PF */
                         /* Must set CR2 at the failing address */ 
                         addr += size - rv;
                         gdprintk(XENLOG_DEBUG, "Pagefault on non-io side of a "
xen/arch/x86/hvm/svm/svm.c +29 -18

119 lines not on this page · raw message

xen/arch/x86/hvm/vmx/vmx.c +25 -10
Index: 2007-11-13/xen/arch/x86/hvm/vmx/vmx.c
===================================================================
@@ -1093,6 +1093,14 @@ static void vmx_inject_exception(
     vmx_inject_hw_exception(v, trapnr, errcode);
     if ( trapnr == TRAP_page_fault )
         v->arch.hvm_vcpu.guest_cr[2] = cr2;
+    if ( trapnr == TRAP_debug &&
+         (guest_cpu_user_regs()->eflags & X86_EFLAGS_TF) )
+    {
+        unsigned long dr6 = read_debugreg(6);
+
+        v->arch.guest_context.debugreg[6] = dr6 |= 0x4000;
+        write_debugreg(6, dr6);
+    }
 }
 
 static void vmx_update_vtpr(struct vcpu *v, unsigned long value)
@@ -1184,13 +1192,20 @@ static int __get_instruction_length(void
     return len;
 }
 
-static void __update_guest_eip(unsigned long inst_len)
+static void __update_guest_eip(struct vcpu *v, unsigned long inst_len)
 {
     struct cpu_user_regs *regs = guest_cpu_user_regs();
     unsigned long x;
 
     regs->eip += inst_len;
     regs->eflags &= ~X86_EFLAGS_RF;
+    if ( v && (regs->eflags & X86_EFLAGS_TF) )
+    {
+        x = read_debugreg(6);
+        v->arch.guest_context.debugreg[6] = x |= 0x4000;
+        write_debugreg(6, x);
+        vmx_inject_hw_exception(v, TRAP_debug, HVM_DELIVER_NO_ERROR_CODE);
+    }
 
     x = __vmread(GUEST_INTERRUPTIBILITY_INFO);
     if ( x & (VMX_INTR_SHADOW_STI | VMX_INTR_SHADOW_MOV_SS) )
@@ -2824,18 +2839,18 @@ asmlinkage void vmx_vmexit_handler(struc
     }
     case EXIT_REASON_CPUID:
         inst_len = __get_instruction_length(); /* Safe: CPUID */
-        __update_guest_eip(inst_len);
+        __update_guest_eip(v, inst_len);
         vmx_do_cpuid(regs);
         break;
     case EXIT_REASON_HLT:
         inst_len = __get_instruction_length(); /* Safe: HLT */
-        __update_guest_eip(inst_len);
+        __update_guest_eip(NULL, inst_len);
         vmx_do_hlt(regs);
         break;
     case EXIT_REASON_INVLPG:
     {
         inst_len = __get_instruction_length(); /* Safe: INVLPG */
-        __update_guest_eip(inst_len);
+        __update_guest_eip(v, inst_len);
         exit_qualification = __vmread(EXIT_QUALIFICATION);
         vmx_do_invlpg(exit_qualification);
         break;
@@ -2848,7 +2863,7 @@ asmlinkage void vmx_vmexit_handler(struc
         rc = hvm_do_hypercall(regs);
         if ( rc != HVM_HCALL_preempted )
         {
-            __update_guest_eip(inst_len);
+            __update_guest_eip(v, inst_len);
             if ( rc == HVM_HCALL_invalidate )
                 send_invalidate_req();
         }
@@ -2859,7 +2874,7 @@ asmlinkage void vmx_vmexit_handler(struc
         exit_qualification = __vmread(EXIT_QUALIFICATION);
         inst_len = __get_instruction_length(); /* Safe: MOV Cn, LMSW, CLTS */
         if ( vmx_cr_access(exit_qualification, regs) )
-            __update_guest_eip(inst_len);
+            __update_guest_eip(v, inst_len);
         break;
     }
     case EXIT_REASON_DR_ACCESS:
@@ -2874,12 +2889,12 @@ asmlinkage void vmx_vmexit_handler(struc
     case EXIT_REASON_MSR_READ:
         inst_len = __get_instruction_length(); /* Safe: RDMSR */
         if ( vmx_do_msr_read(regs) )
-            __update_guest_eip(inst_len);
+            __update_guest_eip(v, inst_len);
         break;
     case EXIT_REASON_MSR_WRITE:
         inst_len = __get_instruction_length(); /* Safe: WRMSR */
         if ( vmx_do_msr_write(regs) )
-            __update_guest_eip(inst_len);
+            __update_guest_eip(v, inst_len);
         break;
 
     case EXIT_REASON_MWAIT_INSTRUCTION:
@@ -2893,7 +2908,7 @@ asmlinkage void vmx_vmexit_handler(struc
     case EXIT_REASON_VMWRITE:
     case EXIT_REASON_VMXOFF:
     case EXIT_REASON_VMXON:
-        vmx_inject_hw_exception(v, TRAP_invalid_op, VMX_DELIVER_NO_ERROR_CODE);
+        vmx_inject_hw_exception(v, TRAP_invalid_op, HVM_DELIVER_NO_ERROR_CODE);
         break;
 
     case EXIT_REASON_TPR_BELOW_THRESHOLD:
@@ -2911,7 +2926,7 @@ asmlinkage void vmx_vmexit_handler(struc
     case EXIT_REASON_INVD:
     {
         inst_len = __get_instruction_length(); /* Safe: INVD */
-        __update_guest_eip(inst_len);
+        __update_guest_eip(v, inst_len);
         if ( !list_empty(&(domain_hvm_iommu(v->domain)->pdev_list)) )
             wbinvd();
         break;
xen/arch/x86/traps.c +14 -5
Index: 2007-11-13/xen/arch/x86/traps.c
===================================================================
@@ -412,6 +412,17 @@ static void do_guest_trap(
                  regs->error_code);
 }
 
+static void instruction_done(struct cpu_user_regs *regs, unsigned long eip)
+{
+    regs->eip = eip;
+    regs->eflags &= ~X86_EFLAGS_RF;
+    if ( regs->eflags & X86_EFLAGS_TF )
+    {
+        current->arch.guest_context.debugreg[6] |= 0xffff4ff0;
+        do_guest_trap(TRAP_debug, regs, 0);
+    }
+}
+
 /*
  * Called from asm to set up the NMI trapbounce info.
  * Returns 0 if no callback is set up, else 1.
@@ -657,8 +668,7 @@ static int emulate_forced_invalid_op(str
     regs->ebx = b;
     regs->ecx = c;
     regs->edx = d;
-    regs->eip = eip;
-    regs->eflags &= ~X86_EFLAGS_RF;
+    instruction_done(regs, eip);
 
     trace_trap_one_addr(TRC_PV_FORCED_INVALID_OP, regs->eip);
 
@@ -1950,8 +1960,7 @@ static int emulate_privileged_op(struct 
 #undef rd_ad
 
  done:
-    regs->eip = eip;
-    regs->eflags &= ~X86_EFLAGS_RF;
+    instruction_done(regs, eip);
  skip:
     return EXCRET_fault_fixed;
 
@@ -2320,8 +2329,8 @@ static void emulate_gate_op(struct cpu_u
     else
         sel |= (regs->cs & 3);
 
-    regs->eip = off;
     regs->cs = sel;
+    instruction_done(regs, off);
 #endif
 }
 
xen/arch/x86/x86_emulate.c +1 -0
Index: 2007-11-13/xen/arch/x86/x86_emulate.c
===================================================================
@@ -1625,6 +1625,7 @@ x86_emulate(
     /* Commit shadow register state. */
     _regs.eflags &= ~EF_RF;
     *ctxt->regs = _regs;
+    /* FIXME generate_exception_if(_regs.eflags & EF_TF, EXC_DB); */
 
  done:
     return rc;
xen/include/asm-x86/hvm/support.h +1 -1
Index: 2007-11-13/xen/include/asm-x86/hvm/support.h
===================================================================
@@ -50,7 +50,7 @@ static inline vcpu_iodata_t *get_ioreq(s
 #define TYPE_CLTS               (2 << 4)
 #define TYPE_LMSW               (3 << 4)
 
-#define VMX_DELIVER_NO_ERROR_CODE  -1
+#define HVM_DELIVER_NO_ERROR_CODE  -1
 
 #if HVM_DEBUG
 #define DBG_LEVEL_0                 (1 << 0)
xen/include/asm-x86/hvm/vmx/vmx.h +3 -3
Index: 2007-11-13/xen/include/asm-x86/hvm/vmx/vmx.h
===================================================================
@@ -272,7 +272,7 @@ static inline void __vmx_inject_exceptio
      */
 
     intr_fields = (INTR_INFO_VALID_MASK | (type<<8) | trap);
-    if ( error_code != VMX_DELIVER_NO_ERROR_CODE ) {
+    if ( error_code != HVM_DELIVER_NO_ERROR_CODE ) {
         __vmwrite(VM_ENTRY_EXCEPTION_ERROR_CODE, error_code);
         intr_fields |= INTR_INFO_DELIVER_CODE_MASK;
     }
@@ -294,13 +294,13 @@ static inline void vmx_inject_hw_excepti
 static inline void vmx_inject_extint(struct vcpu *v, int trap)
 {
     __vmx_inject_exception(v, trap, X86_EVENTTYPE_EXT_INTR,
-                           VMX_DELIVER_NO_ERROR_CODE);
+                           HVM_DELIVER_NO_ERROR_CODE);
 }
 
 static inline void vmx_inject_nmi(struct vcpu *v)
 {
     __vmx_inject_exception(v, 2, X86_EVENTTYPE_NMI,
-                           VMX_DELIVER_NO_ERROR_CODE);
+                           HVM_DELIVER_NO_ERROR_CODE);
 }
 
 #endif /* __ASM_X86_HVM_VMX_VMX_H__ */

_______________________________________________
Xen-devel mailing list
[email protected]
http://lists.xensource.com/xen-devel

#1 lore raw

Inject-sstep: Looks fine in principle. A few specific questions:
* Why is all the RFLAGS.RF logic removed from platform.c?
* Why is DR6 adjustment done in svm_hvm_inject_exception() rather than
svm_inject_exception() (to match vmx_inject_exception())?
* Why do some instruction emulations pass NULL to update_guest_eip() and
hence bypass #DB injection? That seems bogus.

Io-brkp: This one needs more explanation about exactly what things are being improved. There's more going on here than your brief explanation below. My only comment at this point is that I don't see that supporting CR4.DE==0 is very useful. I don't subscribe to the view that we should support every little detail of x86 architecture just because it's there, especially for PV guests.

All other patches are applied and I queued up a couple for 3.1.3 also.

-- Keir

Quoted 18 lines · Jan Beulich

Apart from the cleanup patches just sent I also have two bigger ones in the queue. I'm not certain they qualify for 3.2 at this point, though: While most of them can be considered enhancements, the io-brkp one (which depends on the inject-sstep one) also fixes the issue of guests on x86-64 not being able to utilize 8-byte breakpoints, and it reduces the likelihood of needing to restore the debug registers by only looking at the enable bits in the respective conditions (as HVM was already doing).

Therefore I'd like to understand whether these patches can go in in their current shape, or whether I should split out at least the 8-byte bug fix and submit this separately.

Thanks, Jan

_______________________________________________
Xen-devel mailing list
[email protected]
http://lists.xensource.com/xen-devel

#2 lore raw
Quoted 3 lines
Quoted 1 line
Quoted 1 line

Keir Fraser <[email protected]> 22.11.07 15:50 >>>

Inject-sstep: Looks fine in principle. A few specific questions:
* Why is all the RFLAGS.RF logic removed from platform.c?

As it seems to me it was misplaced there (yes, I added it, but I probably didn't understand the execution flow properly back then). The instruction completes in hvm_io_assist(), there's no register state writeback elsewhere that I could see, and hence the updating of EFLAGS.RF also belongs just there.

Quoted 2 lines

* Why is DR6 adjustment done in svm_hvm_inject_exception() rather than svm_inject_exception() (to match vmx_inject_exception())?

Because
a) svm_hvm_inject_exception() is the counterpart of vmx_inject_exception(), svm_inject_exception() is an internal helper.

Quoted 2 lines

* Why do some instruction emulations pass NULL to update_guest_eip() and hence bypass #DB injection? That seems bogus.

That's intentional (to a certain degree): Especially for the HLT case I wasn't really certain injecting an exception here would have the intended effect. I'm pretty sure you'd have to suppress the normal HLT handling in that case, and it seemed safer for a first cut to not inject an exception at all here (thus simply retaining current behavior for this special case).
For SVM's VMEXIT_EXCEPTION_BP case it seems certainly wrong to inject one.

Quoted 2 lines

Io-brkp: This one needs more explanation about exactly what things are being improved. There's more going on here than your brief explanation below. My

Oh, I see that I didn't describe the original purpose of emulating the #DB on I/O port accesses - I must have thought that this is implied by the name. This is being done by not committing the respective enable bits to %dr7, but rather tracking them in the DR5 shadow.

Beyond that, the only things I didn't describe are mere cleanup items, like replacing numeric literals with manifest constants.

Quoted 1 line

only comment at this point is that I don't see that supporting CR4.DE==0 is

You probably mean CR4.DE==1? If you doubt that, why did you apply the recent change to allow the guest to drive the bit?

Quoted 3 lines

very useful. I don't subscribe to the view that we should support every little detail of x86 architecture just because it's there, especially for PV guests.

I think that a kernel debugger in a guest should certainly have the capability of doing this, but I understand your reservation to a certain degree.

Quoted 1 line

All other patches are applied and I queued up a couple for 3.1.3 also.

Thanks. Btw., the 'official' tree appears to be stuck again...

Jan

#3 lore raw
Quoted 11 lines · Jan Beulich
Quoted 2 lines

* Why do some instruction emulations pass NULL to update_guest_eip() and hence bypass #DB injection? That seems bogus.

That's intentional (to a certain degree): Especially for the HLT case I wasn't really certain injecting an exception here would have the intended effect. I'm pretty sure you'd have to suppress the normal HLT handling in that case, and it seemed safer for a first cut to not inject an exception at all here (thus
simply retaining current behavior for this special case).
For SVM's VMEXIT_EXCEPTION_BP case it seems certainly wrong to inject one.

Ah, you could be right. I don't have that much experience with EFLAGS.TF, but from the reference manuals it looks rather as if, on return from an exception or interrupt, when EFLAGS.TF is re-set by IRET, you actually do not #DB with EIP pointing at the instruction you IRET to? And this is because single-step #DB is a trap? Also I suppose that a software exception or interrupt causes single-step #DB to be skipped when you actually execute INT n, INT3, INTO or whatever. So you effectively never see single-step #DB with EIP pointing at the instruction following one of those INT instructions?

I suppose I could validate this for myself. :-)

Anyway, apart from that the patches are fine. Re-submit with Signed-off-by lines and I'll drop them in.

-- Keir