mem_event: Allow memory access listener to perform single step execution.
authorAravindh Puthiyaparambil <aravindh@virtuata.com>
Fri, 27 May 2011 17:44:26 +0000 (18:44 +0100)
committerAravindh Puthiyaparambil <aravindh@virtuata.com>
Fri, 27 May 2011 17:44:26 +0000 (18:44 +0100)
Add a new memory event that handles single step. This allows the
memory access listener to handle instructions that modify data within
the execution page.  This can be enabled in the listener by doing:
xc_set_hvm_param(xch, domain_id, HVM_PARAM_MEMORY_EVENT_SINGLE_STEP,
HVMPME_mode_sync)

Now the listener can start single stepping by:
xc_domain_debug_control(xch, domain_id,
XEN_DOMCTL_DEBUG_OP_SINGLE_STEP_ON, vcpu_id)

And stop single stepping by: xc_domain_debug_control(xch, domain_id,
XEN_DOMCTL_DEBUG_OP_SINGLE_STEP_OFF, vcpu_id)

Signed-off-by: Aravindh Puthiyaparambil <aravindh@virtuata.com>
Acked-by: Tim Deegan <Tim.Deegan@citrix.com>
xen/arch/x86/hvm/hvm.c
xen/arch/x86/hvm/vmx/vmcs.c
xen/arch/x86/hvm/vmx/vmx.c
xen/include/asm-x86/hvm/hvm.h
xen/include/public/hvm/params.h
xen/include/public/mem_event.h

index 210a6dfe4c7089ff436acbc3e2359c5b655e064a..aa283cc1a5e1c0d14890f0c2fc26b2982e374254 100644 (file)
@@ -3510,7 +3510,8 @@ long do_hvm_op(unsigned long op, XEN_GUEST_HANDLE(void) arg)
                     rc = -EPERM;
                 break;
             case HVM_PARAM_MEMORY_EVENT_INT3:
-                if ( d == current->domain ) 
+            case HVM_PARAM_MEMORY_EVENT_SINGLE_STEP:
+                if ( d == current->domain )
                 {
                     rc = -EPERM;
                     break;
@@ -3543,6 +3544,7 @@ long do_hvm_op(unsigned long op, XEN_GUEST_HANDLE(void) arg)
                 switch( a.index )
                 {
                 case HVM_PARAM_MEMORY_EVENT_INT3:
+                case HVM_PARAM_MEMORY_EVENT_SINGLE_STEP:
                 {
                     domain_pause(d);
                     domain_unpause(d); /* Causes guest to latch new status */
@@ -4066,11 +4068,21 @@ int hvm_debug_op(struct vcpu *v, int32_t op)
             rc = -ENOSYS;
             if ( !cpu_has_monitor_trap_flag )
                 break;
-            rc = 0;
-            vcpu_pause(v);
+
+            rc = mem_event_check_ring(v->domain);
+            /* rc ==0 p2m_mem_access_check() has already paused the vcpu */
+            if ( 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 */
+
+            /* rc ==0 p2m_mem_access_resume() will unpause the vcpu */
+            if ( rc < 0 )
+            {
+                vcpu_unpause(v); /* guest will latch new state */
+                rc = 0;
+            }
             break;
         default:
             rc = -ENOSYS;
@@ -4159,6 +4171,18 @@ int hvm_memory_event_int3(unsigned long gla)
                                   MEM_EVENT_REASON_INT3,
                                   gfn, 0, 1, gla);
 }
+
+int hvm_memory_event_single_step(unsigned long gla)
+{
+    uint32_t pfec = PFEC_page_present;
+    unsigned long gfn;
+    gfn = paging_gva_to_gfn(current, gla, &pfec);
+
+    return hvm_memory_event_traps(current->domain->arch.hvm_domain
+            .params[HVM_PARAM_MEMORY_EVENT_SINGLE_STEP],
+            MEM_EVENT_REASON_SINGLESTEP,
+            gfn, 0, 1, gla);
+}
 #endif /* __x86_64__ */
 
 int nhvm_vcpu_hostrestore(struct vcpu *v, struct cpu_user_regs *regs)
index b1d0efc2418f2f2ebcff4ba5a7a0e16c6f59f2fe..1b3762774c9954ba46606d3d3fa91b83017e43d1 100644 (file)
@@ -1081,8 +1081,9 @@ void vmx_do_resume(struct vcpu *v)
         hvm_asid_flush_vcpu(v);
     }
 
-    debug_state = v->domain->debugger_attached 
-                  || v->domain->arch.hvm_domain.params[HVM_PARAM_MEMORY_EVENT_INT3];
+    debug_state = v->domain->debugger_attached
+                  || v->domain->arch.hvm_domain.params[HVM_PARAM_MEMORY_EVENT_INT3]
+                  || v->domain->arch.hvm_domain.params[HVM_PARAM_MEMORY_EVENT_SINGLE_STEP];
 
     if ( unlikely(v->arch.hvm_vcpu.debug_state_latch != debug_state) )
     {
index 996bbfe9a8bb124179608dc4e8bc41bae06ecda7..0c7749a16ed4bd36658c87b1da59820728401dd6 100644 (file)
@@ -2494,8 +2494,12 @@ asmlinkage void vmx_vmexit_handler(struct cpu_user_regs *regs)
     case EXIT_REASON_MONITOR_TRAP_FLAG:
         v->arch.hvm_vmx.exec_control &= ~CPU_BASED_MONITOR_TRAP_FLAG;
         vmx_update_cpu_exec_control(v);
-        if ( v->domain->debugger_attached && v->arch.hvm_vcpu.single_step )
-            domain_pause_for_debugger();
+        if ( v->arch.hvm_vcpu.single_step ) {
+          hvm_memory_event_single_step(regs->eip);
+          if ( v->domain->debugger_attached )
+              domain_pause_for_debugger();
+        }
+
         break;
 
     case EXIT_REASON_PAUSE_INSTRUCTION:
index f9981414cd21bac88354b676e840690e9f57aa2c..46ebea2e0995ff35ecd8b7ae31574481eb240a0c 100644 (file)
@@ -414,6 +414,10 @@ void hvm_memory_event_cr3(unsigned long value, unsigned long old);
 void hvm_memory_event_cr4(unsigned long value, unsigned long old);
 /* Called for current VCPU on int3: returns -1 if no listener */
 int hvm_memory_event_int3(unsigned long gla);
+
+/* Called for current VCPU on single step: returns -1 if no listener */
+int hvm_memory_event_single_step(unsigned long gla);
+
 #else
 static inline void hvm_memory_event_cr0(unsigned long value, unsigned long old)
 { }
@@ -423,6 +427,8 @@ static inline void hvm_memory_event_cr4(unsigned long value, unsigned long old)
 { }
 static inline int hvm_memory_event_int3(unsigned long gla)
 { return 0; }
+static inline int hvm_memory_event_single_step(unsigned long gla)
+{ return 0; }
 #endif
 
 /*
index b3858aa8d00a5ae05dbaccff78b522d59f0e6697..1c5a1a996434c2c4cedebb0b7830656009a69909 100644 (file)
 
 /* Enable blocking memory events, async or sync (pause vcpu until response) 
  * onchangeonly indicates messages only on a change of value */
-#define HVM_PARAM_MEMORY_EVENT_CR0   20
-#define HVM_PARAM_MEMORY_EVENT_CR3   21
-#define HVM_PARAM_MEMORY_EVENT_CR4   22
-#define HVM_PARAM_MEMORY_EVENT_INT3  23
+#define HVM_PARAM_MEMORY_EVENT_CR0          20
+#define HVM_PARAM_MEMORY_EVENT_CR3          21
+#define HVM_PARAM_MEMORY_EVENT_CR4          22
+#define HVM_PARAM_MEMORY_EVENT_INT3         23
+#define HVM_PARAM_MEMORY_EVENT_SINGLE_STEP  25
 
 #define HVMPME_MODE_MASK       (3 << 0)
 #define HVMPME_mode_disabled   0
 /* Boolean: Enable nestedhvm (hvm only) */
 #define HVM_PARAM_NESTEDHVM    24
 
-#define HVM_NR_PARAMS          25
+#define HVM_NR_PARAMS          26
 
 #endif /* __XEN_PUBLIC_HVM_PARAMS_H__ */
index 93c824b86759b198d51920aa64fc6466d0fc43ac..45c15d3ef233940ea7ad738b91e9edae4af54728 100644 (file)
@@ -46,6 +46,7 @@
 #define MEM_EVENT_REASON_CR3         3    /* CR3 was hit: gfn is CR3 value */
 #define MEM_EVENT_REASON_CR4         4    /* CR4 was hit: gfn is CR4 value */
 #define MEM_EVENT_REASON_INT3        5    /* int3 was hit: gla/gfn are RIP */
+#define MEM_EVENT_REASON_SINGLESTEP  6    /* single step was invoked: gla/gfn are RIP */
 
 typedef struct mem_event_shared_page {
     uint32_t port;