x86: Enable MTF for HVM guest single step in gdb
authorKeir Fraser <keir.fraser@citrix.com>
Tue, 16 Dec 2008 11:49:20 +0000 (11:49 +0000)
committerKeir Fraser <keir.fraser@citrix.com>
Tue, 16 Dec 2008 11:49:20 +0000 (11:49 +0000)
Signed-off-by: Edwin Zhai <edwin.zhai@intel.com>
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
13 files changed:
tools/libxc/xc_domain.c
tools/libxc/xc_ptrace.c
tools/libxc/xenctrl.h
xen/arch/x86/domctl.c
xen/arch/x86/hvm/hvm.c
xen/arch/x86/hvm/vmx/intr.c
xen/arch/x86/hvm/vmx/vmcs.c
xen/arch/x86/hvm/vmx/vmx.c
xen/include/asm-x86/hvm/hvm.h
xen/include/asm-x86/hvm/vcpu.h
xen/include/asm-x86/hvm/vmx/vmcs.h
xen/include/asm-x86/hvm/vmx/vmx.h
xen/include/public/domctl.h

index e1bf86ee6a443d3fe4cbdc0f66fdc4b72ec5374f..afffd68d1a1c06ec44b113d63e42dcee3df97973 100644 (file)
@@ -1061,6 +1061,20 @@ int xc_domain_suppress_spurious_page_faults(int xc, uint32_t domid)
 
 }
 
+int xc_domain_debug_control(int xc, uint32_t domid, uint32_t sop, uint32_t vcpu)
+{
+    DECLARE_DOMCTL;
+
+    memset(&domctl, 0, sizeof(domctl));
+    domctl.domain = (domid_t)domid;
+    domctl.cmd = XEN_DOMCTL_debug_op;
+    domctl.u.debug_op.op     = sop;
+    domctl.u.debug_op.vcpu   = vcpu;
+
+    return do_domctl(xc, &domctl);
+}
+
+
 /*
  * Local variables:
  * mode: C
index fa6c3a0ad1b203c0e13002203dc7346095cd3f89..094bf165dfd8f5a673d9d66120f1cf289206f7ae 100644 (file)
@@ -524,10 +524,20 @@ xc_ptrace(
         /*  XXX we can still have problems if the user switches threads
          *  during single-stepping - but that just seems retarded
          */
-        ctxt[cpu].c.user_regs.eflags |= PSL_T;
-        if ((retval = xc_vcpu_setcontext(xc_handle, current_domid, cpu,
-                                &ctxt[cpu])))
-            goto out_error_domctl;
+        /* Try to enalbe Monitor Trap Flag for HVM, and fall back to TF
+         * if no MTF support
+         */
+        if ( !current_is_hvm ||
+             xc_domain_debug_control(xc_handle,
+                                     current_domid,
+                                     XEN_DOMCTL_DEBUG_OP_SINGLE_STEP_ON,
+                                     cpu) )
+        {
+            ctxt[cpu].c.user_regs.eflags |= PSL_T;
+            if ((retval = xc_vcpu_setcontext(xc_handle, current_domid, cpu,
+                                    &ctxt[cpu])))
+                goto out_error_domctl;
+        }
         /* FALLTHROUGH */
 
     case PTRACE_CONT:
@@ -538,15 +548,22 @@ xc_ptrace(
         {
             FOREACH_CPU(cpumap, index) {
                 cpu = index - 1;
-                if (fetch_regs(xc_handle, cpu, NULL))
-                    goto out_error;
-                /* Clear trace flag */
-                if ( ctxt[cpu].c.user_regs.eflags & PSL_T )
+                if ( !current_is_hvm ||
+                      xc_domain_debug_control(xc_handle,
+                                              current_domid,
+                                              XEN_DOMCTL_DEBUG_OP_SINGLE_STEP_OFF,
+                                              cpu) )
                 {
-                    ctxt[cpu].c.user_regs.eflags &= ~PSL_T;
-                    if ((retval = xc_vcpu_setcontext(xc_handle, current_domid,
-                                                cpu, &ctxt[cpu])))
-                        goto out_error_domctl;
+                    if (fetch_regs(xc_handle, cpu, NULL))
+                        goto out_error;
+                    /* Clear trace flag */
+                    if ( ctxt[cpu].c.user_regs.eflags & PSL_T )
+                    {
+                        ctxt[cpu].c.user_regs.eflags &= ~PSL_T;
+                        if ((retval = xc_vcpu_setcontext(xc_handle, current_domid,
+                                        cpu, &ctxt[cpu])))
+                            goto out_error_domctl;
+                    }
                 }
             }
         }
index 18d2f5f4d1b45ff00637b3aaf8c61ef6f5bba63d..b915ccaff2a5a8d9ea3fbe9562d7bdb4cf99e3e1 100644 (file)
@@ -1111,6 +1111,12 @@ int xc_domain_set_target(int xc_handle,
                          uint32_t domid,
                          uint32_t target);
 
+/* Control the domain for debug */
+int xc_domain_debug_control(int xc_handle,
+                            uint32_t domid,
+                            uint32_t sop,
+                            uint32_t vcpu);
+
 #if defined(__i386__) || defined(__x86_64__)
 int xc_cpuid_check(int xc,
                    const unsigned int *input,
index 50967ab71b6d4b8608a479457e2140a376aeab0f..8b4c40fea04f2ae65388a3949b09b649170f535b 100644 (file)
@@ -1022,6 +1022,32 @@ long arch_do_domctl(
     }
     break;
 
+    case XEN_DOMCTL_debug_op:
+    {
+        struct domain *d;
+        struct vcpu *v;
+
+        ret = -ESRCH;
+        d = rcu_lock_domain_by_id(domctl->domain);
+        if ( d == NULL )
+            break;
+
+        ret = -EINVAL;
+        if ( (domctl->u.debug_op.vcpu >= MAX_VIRT_CPUS) ||
+             ((v = d->vcpu[domctl->u.debug_op.vcpu]) == NULL) )
+            goto debug_op_out;
+
+        ret = -EINVAL;
+        if ( !is_hvm_domain(d))
+            goto debug_op_out;
+
+        ret = hvm_debug_op(v, domctl->u.debug_op.op);
+
+    debug_op_out:
+        rcu_unlock_domain(d);
+    }
+    break;
+
     default:
         ret = -ENOSYS;
         break;
index 9bda106ec2e281d8597ac662ab37d68c5c94d93c..fdcea74097b1945a29ccf732a916a617132ab9b0 100644 (file)
@@ -2700,6 +2700,32 @@ long do_hvm_op(unsigned long op, XEN_GUEST_HANDLE(void) arg)
     return rc;
 }
 
+int hvm_debug_op(struct vcpu *v, int32_t op)
+{
+    int rc;
+
+    switch ( op )
+    {
+        case XEN_DOMCTL_DEBUG_OP_SINGLE_STEP_ON:
+        case XEN_DOMCTL_DEBUG_OP_SINGLE_STEP_OFF:
+            rc = -ENOSYS;
+            if ( !cpu_has_monitor_trap_flag )
+                break;
+            rc = 0;
+            vcpu_pause(v);
+            v->arch.hvm_vcpu.single_step =
+                (op == XEN_DOMCTL_DEBUG_OP_SINGLE_STEP_ON);
+            vcpu_unpause(v); /* guest will latch new state */
+            break;
+        default:
+            rc = -ENOSYS;
+            break;
+    }
+
+    return rc;
+}
+
+
 /*
  * Local variables:
  * mode: C
index 2c45e26bfac68915544967955ead69c08925095b..afd9d419190c6aacf8f792e64586cfae54ac37af 100644 (file)
@@ -117,6 +117,14 @@ asmlinkage void vmx_intr_assist(void)
     unsigned int tpr_threshold = 0;
     enum hvm_intblk intblk;
 
+    /* Block event injection when single step with MTF. */
+    if ( unlikely(v->arch.hvm_vcpu.single_step) )
+    {
+        v->arch.hvm_vmx.exec_control |= CPU_BASED_MONITOR_TRAP_FLAG;
+        __vmwrite(CPU_BASED_VM_EXEC_CONTROL, v->arch.hvm_vmx.exec_control);
+        return;
+    }
+
     /* Crank the handle on interrupt state. */
     pt_update_irq(v);
     hvm_dirq_assist(v);
index 051443f7e7a9929490500de055629f2128a9c41c..83598c18182570bff509b62a77fe6152d94f5325 100644 (file)
@@ -99,6 +99,7 @@ static void vmx_init_vmcs_config(void)
            (opt_softtsc ? CPU_BASED_RDTSC_EXITING : 0));
     opt = (CPU_BASED_ACTIVATE_MSR_BITMAP |
            CPU_BASED_TPR_SHADOW |
+           CPU_BASED_MONITOR_TRAP_FLAG |
            CPU_BASED_ACTIVATE_SECONDARY_CONTROLS);
     _vmx_cpu_based_exec_control = adjust_vmx_controls(
         min, opt, MSR_IA32_VMX_PROCBASED_CTLS);
@@ -515,6 +516,9 @@ static int construct_vmcs(struct vcpu *v)
         v->arch.hvm_vmx.secondary_exec_control &= ~SECONDARY_EXEC_ENABLE_EPT;
     }
 
+    /* Do not enable Monitor Trap Flag unless start single step debug */
+    v->arch.hvm_vmx.exec_control &= ~CPU_BASED_MONITOR_TRAP_FLAG;
+
     __vmwrite(CPU_BASED_VM_EXEC_CONTROL, v->arch.hvm_vmx.exec_control);
     if ( cpu_has_vmx_secondary_exec_control )
         __vmwrite(SECONDARY_VM_EXEC_CONTROL,
@@ -867,7 +871,11 @@ void vmx_do_resume(struct vcpu *v)
     if ( unlikely(v->arch.hvm_vcpu.debug_state_latch != debug_state) )
     {
         unsigned long intercepts = __vmread(EXCEPTION_BITMAP);
-        unsigned long mask = (1U << TRAP_debug) | (1U << TRAP_int3);
+        unsigned long mask = 1u << TRAP_int3;
+
+        if ( !cpu_has_monitor_trap_flag )
+            mask |= 1u << TRAP_debug;
+
         v->arch.hvm_vcpu.debug_state_latch = debug_state;
         if ( debug_state )
             intercepts |= mask;
index ff2420f44887820cb3d02ee2a4045dfb439da6dd..1519a78d03395953b96f73f7be0e83916f7fb6c6 100644 (file)
@@ -1263,6 +1263,8 @@ void vmx_inject_hw_exception(int trap, int error_code)
             __restore_debug_registers(curr);
             write_debugreg(6, read_debugreg(6) | 0x4000);
         }
+        if ( cpu_has_monitor_trap_flag )
+            break;
     case TRAP_int3:
         if ( curr->domain->debugger_attached )
         {
@@ -2348,7 +2350,7 @@ asmlinkage void vmx_vmexit_handler(struct cpu_user_regs *regs)
              */
             exit_qualification = __vmread(EXIT_QUALIFICATION);
             write_debugreg(6, exit_qualification | 0xffff0ff0);
-            if ( !v->domain->debugger_attached )
+            if ( !v->domain->debugger_attached || cpu_has_monitor_trap_flag )
                 goto exit_and_crash;
             domain_pause_for_debugger();
             break;
@@ -2538,6 +2540,15 @@ asmlinkage void vmx_vmexit_handler(struct cpu_user_regs *regs)
         break;
     }
 
+    case EXIT_REASON_MONITOR_TRAP_FLAG:
+    {
+        v->arch.hvm_vmx.exec_control &= ~CPU_BASED_MONITOR_TRAP_FLAG;
+        __vmwrite(CPU_BASED_VM_EXEC_CONTROL, v->arch.hvm_vmx.exec_control);
+        if ( v->domain->debugger_attached && v->arch.hvm_vcpu.single_step )
+            domain_pause_for_debugger();
+        break;
+    }
+
     default:
     exit_and_crash:
         gdprintk(XENLOG_ERR, "Bad vmexit (reason %x)\n", exit_reason);
index a9bee16e10b57277a996f2079e4c061a5d02a819..b61c5660949ea57b2dcf6622f2b44197e95c9517 100644 (file)
@@ -321,4 +321,6 @@ static inline void hvm_set_info_guest(struct vcpu *v)
         return hvm_funcs.set_info_guest(v);
 }
 
+int hvm_debug_op(struct vcpu *v, int32_t op);
+
 #endif /* __ASM_X86_HVM_HVM_H__ */
index 6eb9d67b731cc67d1dd7455c4f37bab446964bf5..faea392f92ba161b4c2b19d356b478f638425416 100644 (file)
@@ -59,6 +59,7 @@ struct hvm_vcpu {
 
     bool_t              flag_dr_dirty;
     bool_t              debug_state_latch;
+    bool_t              single_step;
 
     union {
         struct arch_vmx_struct vmx;
index befe36c8c0d4689375ffce1979e2ebebffca0ae8..5337ec93dd827869e5f5f6244825b399edfb7dc6 100644 (file)
@@ -142,6 +142,7 @@ void vmx_vmcs_exit(struct vcpu *v);
 #define CPU_BASED_MOV_DR_EXITING              0x00800000
 #define CPU_BASED_UNCOND_IO_EXITING           0x01000000
 #define CPU_BASED_ACTIVATE_IO_BITMAP          0x02000000
+#define CPU_BASED_MONITOR_TRAP_FLAG           0x08000000
 #define CPU_BASED_ACTIVATE_MSR_BITMAP         0x10000000
 #define CPU_BASED_MONITOR_EXITING             0x20000000
 #define CPU_BASED_PAUSE_EXITING               0x40000000
@@ -186,6 +187,8 @@ extern bool_t cpu_has_vmx_ins_outs_instr_info;
     (vmx_secondary_exec_control & SECONDARY_EXEC_ENABLE_EPT)
 #define cpu_has_vmx_vpid \
     (vmx_secondary_exec_control & SECONDARY_EXEC_ENABLE_VPID)
+#define cpu_has_monitor_trap_flag \
+    (vmx_cpu_based_exec_control & CPU_BASED_MONITOR_TRAP_FLAG)
 
 /* GUEST_INTERRUPTIBILITY_INFO flags. */
 #define VMX_INTR_SHADOW_STI             0x00000001
index 563e8aea36bad6de1884dcc2a2f8f7a7b16306d8..21dec48dc41bde053579efae486bc07f6569de42 100644 (file)
@@ -96,6 +96,7 @@ void vmx_realmode(struct cpu_user_regs *regs);
 #define EXIT_REASON_INVALID_GUEST_STATE 33
 #define EXIT_REASON_MSR_LOADING         34
 #define EXIT_REASON_MWAIT_INSTRUCTION   36
+#define EXIT_REASON_MONITOR_TRAP_FLAG   37
 #define EXIT_REASON_MONITOR_INSTRUCTION 39
 #define EXIT_REASON_PAUSE_INSTRUCTION   40
 #define EXIT_REASON_MACHINE_CHECK       41
index b7075ac447e54372b745729aaf344b7a388b4048..4863f48eeceb93c1671a242a6a58bf9b95538a9a 100644 (file)
@@ -619,6 +619,17 @@ DEFINE_XEN_GUEST_HANDLE(xen_domctl_subscribe_t);
  */
 #define XEN_DOMCTL_suppress_spurious_page_faults 53
 
+#define XEN_DOMCTL_debug_op    54
+#define XEN_DOMCTL_DEBUG_OP_SINGLE_STEP_OFF         0
+#define XEN_DOMCTL_DEBUG_OP_SINGLE_STEP_ON          1
+struct xen_domctl_debug_op {
+    uint32_t op;   /* IN */
+    uint32_t vcpu; /* IN */
+};
+typedef struct xen_domctl_debug_op xen_domctl_debug_op_t;
+DEFINE_XEN_GUEST_HANDLE(xen_domctl_debug_op_t);
+
+
 struct xen_domctl {
     uint32_t cmd;
     uint32_t interface_version; /* XEN_DOMCTL_INTERFACE_VERSION */
@@ -658,6 +669,7 @@ struct xen_domctl {
         struct xen_domctl_set_opt_feature   set_opt_feature;
         struct xen_domctl_set_target        set_target;
         struct xen_domctl_subscribe         subscribe;
+        struct xen_domctl_debug_op          debug_op;
 #if defined(__i386__) || defined(__x86_64__)
         struct xen_domctl_cpuid             cpuid;
 #endif