diff mbox

[v2,4/5] x86/hvm: Add debug exception vm_events

Message ID 1461953253-32043-4-git-send-email-tamas@tklengyel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Tamas K Lengyel April 29, 2016, 6:07 p.m. UTC
Since in-guest debug exceptions are now unconditionally trapped to Xen, adding
a hook for vm_event subscribers to tap into this new always-on guest event. We
rename along the way hvm_event_breakpoint_type to hvm_event_type to better
match the various events that can be passed with it. We also introduce the
necessary monitor_op domctl's to enable subscribing to the events.

Signed-off-by: Tamas K Lengyel <tamas@tklengyel.com>
---
Cc: Razvan Cojocaru <rcojocaru@bitdefender.com>
Cc: Keir Fraser <keir@xen.org>
Cc: Jan Beulich <jbeulich@suse.com>
Cc: Andrew Cooper <andrew.cooper3@citrix.com>
Cc: Jun Nakajima <jun.nakajima@intel.com>
Cc: Kevin Tian <kevin.tian@intel.com>

v2: Rename hvm_monitor_event to hvm_monitor_debug
---
 tools/libxc/include/xenctrl.h       |  3 +-
 tools/libxc/xc_monitor.c            | 15 +++++++++
 tools/tests/xen-access/xen-access.c | 61 ++++++++++++++++++++++++++++++++-----
 xen/arch/x86/hvm/monitor.c          | 26 ++++++++++++++--
 xen/arch/x86/hvm/vmx/vmx.c          | 26 +++++++++++++---
 xen/arch/x86/monitor.c              | 16 ++++++++++
 xen/include/asm-x86/domain.h        |  2 ++
 xen/include/asm-x86/hvm/monitor.h   |  7 +++--
 xen/include/asm-x86/monitor.h       |  3 +-
 xen/include/public/domctl.h         |  6 ++++
 xen/include/public/vm_event.h       | 12 +++++++-
 11 files changed, 156 insertions(+), 21 deletions(-)

Comments

Jan Beulich May 2, 2016, 1:12 p.m. UTC | #1
>>> On 29.04.16 at 20:07, <tamas@tklengyel.com> wrote:
> @@ -229,8 +231,15 @@ struct vm_event_write_ctrlreg {
>      uint64_t old_value;
>  };
>  
> +struct vm_event_singlestep {
> +    uint64_t gfn;
> +};
> +
>  struct vm_event_debug {
>      uint64_t gfn;
> +    uint8_t type;        /* HVMOP_TRAP_* */
> +    uint8_t insn_length;
> +    uint8_t _pad[6];
>  };

This being an incompatible change - didn't you mean to increment some
version number?

Jan
Tamas K Lengyel May 2, 2016, 3:35 p.m. UTC | #2
On May 2, 2016 07:12, "Jan Beulich" <JBeulich@suse.com> wrote:
>
> >>> On 29.04.16 at 20:07, <tamas@tklengyel.com> wrote:
> > @@ -229,8 +231,15 @@ struct vm_event_write_ctrlreg {
> >      uint64_t old_value;
> >  };
> >
> > +struct vm_event_singlestep {
> > +    uint64_t gfn;
> > +};
> > +
> >  struct vm_event_debug {
> >      uint64_t gfn;
> > +    uint8_t type;        /* HVMOP_TRAP_* */
> > +    uint8_t insn_length;
> > +    uint8_t _pad[6];
> >  };
>
> This being an incompatible change - didn't you mean to increment some
> version number?
>
> Jan

I'm not sure. It would still work with clients compiled with the older
version of the header as the layout of the debug struct didnt change, was
just appended. The size of the request/response struct didn't change either
so technically this would still be backwards compatible.

Tamas
Jan Beulich May 2, 2016, 3:40 p.m. UTC | #3
>>> On 02.05.16 at 17:35, <tamas@tklengyel.com> wrote:
> On May 2, 2016 07:12, "Jan Beulich" <JBeulich@suse.com> wrote:
>>
>> >>> On 29.04.16 at 20:07, <tamas@tklengyel.com> wrote:
>> > @@ -229,8 +231,15 @@ struct vm_event_write_ctrlreg {
>> >      uint64_t old_value;
>> >  };
>> >
>> > +struct vm_event_singlestep {
>> > +    uint64_t gfn;
>> > +};
>> > +
>> >  struct vm_event_debug {
>> >      uint64_t gfn;
>> > +    uint8_t type;        /* HVMOP_TRAP_* */
>> > +    uint8_t insn_length;
>> > +    uint8_t _pad[6];
>> >  };
>>
>> This being an incompatible change - didn't you mean to increment some
>> version number?
> 
> I'm not sure. It would still work with clients compiled with the older
> version of the header as the layout of the debug struct didnt change, was
> just appended. The size of the request/response struct didn't change either
> so technically this would still be backwards compatible.

But you also need to consider the other direction: Code compiled
against the new variant, but running on an older hypervisor would
expect the new fields to be valid, yet they can't be, and the caller
has no way to know.

Jan
Tamas K Lengyel May 2, 2016, 3:45 p.m. UTC | #4
On May 2, 2016 09:40, "Jan Beulich" <JBeulich@suse.com> wrote:
>
> >>> On 02.05.16 at 17:35, <tamas@tklengyel.com> wrote:
> > On May 2, 2016 07:12, "Jan Beulich" <JBeulich@suse.com> wrote:
> >>
> >> >>> On 29.04.16 at 20:07, <tamas@tklengyel.com> wrote:
> >> > @@ -229,8 +231,15 @@ struct vm_event_write_ctrlreg {
> >> >      uint64_t old_value;
> >> >  };
> >> >
> >> > +struct vm_event_singlestep {
> >> > +    uint64_t gfn;
> >> > +};
> >> > +
> >> >  struct vm_event_debug {
> >> >      uint64_t gfn;
> >> > +    uint8_t type;        /* HVMOP_TRAP_* */
> >> > +    uint8_t insn_length;
> >> > +    uint8_t _pad[6];
> >> >  };
> >>
> >> This being an incompatible change - didn't you mean to increment some
> >> version number?
> >
> > I'm not sure. It would still work with clients compiled with the older
> > version of the header as the layout of the debug struct didnt change,
was
> > just appended. The size of the request/response struct didn't change
either
> > so technically this would still be backwards compatible.
>
> But you also need to consider the other direction: Code compiled
> against the new variant, but running on an older hypervisor would
> expect the new fields to be valid, yet they can't be, and the caller
> has no way to know.

Fair point, will incrementnthe version.

Thanks!
Tamas
diff mbox

Patch

diff --git a/tools/libxc/include/xenctrl.h b/tools/libxc/include/xenctrl.h
index 4b75ae4..ff3ba9e 100644
--- a/tools/libxc/include/xenctrl.h
+++ b/tools/libxc/include/xenctrl.h
@@ -2162,7 +2162,8 @@  int xc_monitor_guest_request(xc_interface *xch, domid_t domain_id,
                              bool enable, bool sync);
 int xc_monitor_privileged_call(xc_interface *xch, domid_t domain_id,
                                bool enable);
-
+int xc_monitor_debug_exceptions(xc_interface *xch, domid_t domain_id,
+                                bool enable, bool sync);
 /**
  * This function enables / disables emulation for each REP for a
  * REP-compatible instruction.
diff --git a/tools/libxc/xc_monitor.c b/tools/libxc/xc_monitor.c
index 072df70..e9b0343 100644
--- a/tools/libxc/xc_monitor.c
+++ b/tools/libxc/xc_monitor.c
@@ -171,6 +171,21 @@  int xc_monitor_privileged_call(xc_interface *xch, domid_t domain_id,
     return do_domctl(xch, &domctl);
 }
 
+int xc_monitor_debug_exceptions(xc_interface *xch, domid_t domain_id,
+                                bool enable, bool sync)
+{
+    DECLARE_DOMCTL;
+
+    domctl.cmd = XEN_DOMCTL_monitor_op;
+    domctl.domain = domain_id;
+    domctl.u.monitor_op.op = enable ? XEN_DOMCTL_MONITOR_OP_ENABLE
+                                    : XEN_DOMCTL_MONITOR_OP_DISABLE;
+    domctl.u.monitor_op.event = XEN_DOMCTL_MONITOR_EVENT_DEBUG_EXCEPTION;
+    domctl.u.monitor_op.u.debug_exception.sync = sync;
+
+    return do_domctl(xch, &domctl);
+}
+
 /*
  * Local variables:
  * mode: C
diff --git a/tools/tests/xen-access/xen-access.c b/tools/tests/xen-access/xen-access.c
index 33e8044..ae235e2 100644
--- a/tools/tests/xen-access/xen-access.c
+++ b/tools/tests/xen-access/xen-access.c
@@ -53,6 +53,10 @@ 
 #define ERROR(a, b...) fprintf(stderr, a "\n", ## b)
 #define PERROR(a, b...) fprintf(stderr, a ": %s\n", ## b, strerror(errno))
 
+/* From xen/include/asm-x86/processor.h */
+#define X86_TRAP_DEBUG  1
+#define X86_TRAP_INT3   3
+
 typedef struct vm_event {
     domid_t domain_id;
     xenevtchn_handle *xce_handle;
@@ -333,7 +337,7 @@  void usage(char* progname)
 {
     fprintf(stderr, "Usage: %s [-m] <domain_id> write|exec", progname);
 #if defined(__i386__) || defined(__x86_64__)
-            fprintf(stderr, "|breakpoint|altp2m_write|altp2m_exec");
+            fprintf(stderr, "|breakpoint|altp2m_write|altp2m_exec|debug");
 #elif defined(__arm__) || defined(__aarch64__)
             fprintf(stderr, "|privcall");
 #endif
@@ -356,11 +360,13 @@  int main(int argc, char *argv[])
     xc_interface *xch;
     xenmem_access_t default_access = XENMEM_access_rwx;
     xenmem_access_t after_first_access = XENMEM_access_rwx;
+    int memaccess = 0;
     int required = 0;
     int breakpoint = 0;
     int shutting_down = 0;
     int privcall = 0;
     int altp2m = 0;
+    int debug = 0;
     uint16_t altp2m_view_id = 0;
 
     char* progname = argv[0];
@@ -394,11 +400,13 @@  int main(int argc, char *argv[])
     {
         default_access = XENMEM_access_rx;
         after_first_access = XENMEM_access_rwx;
+        memaccess = 1;
     }
     else if ( !strcmp(argv[0], "exec") )
     {
         default_access = XENMEM_access_rw;
         after_first_access = XENMEM_access_rwx;
+        memaccess = 1;
     }
 #if defined(__i386__) || defined(__x86_64__)
     else if ( !strcmp(argv[0], "breakpoint") )
@@ -409,11 +417,17 @@  int main(int argc, char *argv[])
     {
         default_access = XENMEM_access_rx;
         altp2m = 1;
+        memaccess = 1;
     }
     else if ( !strcmp(argv[0], "altp2m_exec") )
     {
         default_access = XENMEM_access_rw;
         altp2m = 1;
+        memaccess = 1;
+    }
+    else if ( !strcmp(argv[0], "debug") )
+    {
+        debug = 1;
     }
 #elif defined(__arm__) || defined(__aarch64__)
     else if ( !strcmp(argv[0], "privcall") )
@@ -454,7 +468,7 @@  int main(int argc, char *argv[])
     }
 
     /* With altp2m we just create a new, restricted view of the memory */
-    if ( altp2m )
+    if ( memaccess && altp2m )
     {
         xen_pfn_t gfn = 0;
         unsigned long perm_set = 0;
@@ -501,7 +515,7 @@  int main(int argc, char *argv[])
         }
     }
 
-    if ( !altp2m )
+    if ( memaccess && !altp2m )
     {
         /* Set the default access type and convert all pages to it */
         rc = xc_set_mem_access(xch, domain_id, default_access, ~0ull, 0);
@@ -542,6 +556,16 @@  int main(int argc, char *argv[])
         }
     }
 
+    if ( debug )
+    {
+        rc = xc_monitor_debug_exceptions(xch, domain_id, 1, 1);
+        if ( rc < 0 )
+        {
+            ERROR("Error %d setting debug exception listener with vm_event\n", rc);
+            goto exit;
+        }
+    }
+
     /* Wait for access */
     for (;;)
     {
@@ -552,9 +576,10 @@  int main(int argc, char *argv[])
 
             if ( breakpoint )
                 rc = xc_monitor_software_breakpoint(xch, domain_id, 0);
-
             if ( privcall )
                 rc = xc_monitor_privileged_call(xch, domain_id, 0);
+            if ( debug )
+                rc = xc_monitor_debug_exceptions(xch, domain_id, 0, 0);
 
             if ( altp2m )
             {
@@ -662,9 +687,10 @@  int main(int argc, char *argv[])
                        req.vcpu_id);
 
                 /* Reinject */
-                rc = xc_hvm_inject_trap(
-                    xch, domain_id, req.vcpu_id, 3,
-                    HVMOP_TRAP_sw_exc, -1, 0, 0);
+                rc = xc_hvm_inject_trap(xch, domain_id, req.vcpu_id,
+                                        X86_TRAP_INT3,
+                                        req.u.software_breakpoint.type, -1,
+                                        req.u.software_breakpoint.insn_length, 0);
                 if (rc < 0)
                 {
                     ERROR("Error %d injecting breakpoint\n", rc);
@@ -698,6 +724,27 @@  int main(int argc, char *argv[])
                 rsp.flags |= VM_EVENT_FLAG_TOGGLE_SINGLESTEP;
 
                 break;
+            case VM_EVENT_REASON_DEBUG_EXCEPTION:
+                printf("Debug exception: rip=%016"PRIx64", vcpu %d. Type: %u. Length: %u\n",
+                       req.data.regs.x86.rip,
+                       req.vcpu_id,
+                       req.u.debug_exception.type,
+                       req.u.debug_exception.insn_length);
+
+                /* Reinject */
+                rc = xc_hvm_inject_trap(xch, domain_id, req.vcpu_id,
+                                        X86_TRAP_DEBUG,
+                                        req.u.debug_exception.type, -1,
+                                        req.u.debug_exception.insn_length,
+                                        req.data.regs.x86.cr2);
+                if (rc < 0)
+                {
+                    ERROR("Error %d injecting breakpoint\n", rc);
+                    interrupted = -1;
+                    continue;
+                }
+
+                break;
             default:
                 fprintf(stderr, "UNKNOWN REASON CODE %d\n", req.reason);
             }
diff --git a/xen/arch/x86/hvm/monitor.c b/xen/arch/x86/hvm/monitor.c
index 76ed811..f91c00b 100644
--- a/xen/arch/x86/hvm/monitor.c
+++ b/xen/arch/x86/hvm/monitor.c
@@ -89,12 +89,14 @@  static inline unsigned long gfn_of_rip(unsigned long rip)
     return paging_gva_to_gfn(curr, sreg.base + rip, &pfec);
 }
 
-int hvm_monitor_breakpoint(unsigned long rip,
-                         enum hvm_monitor_breakpoint_type type)
+int hvm_monitor_debug(unsigned long rip, enum hvm_monitor_debug_type type,
+                      unsigned long trap_type, unsigned long insn_length)
 {
     struct vcpu *curr = current;
     struct arch_domain *ad = &curr->domain->arch;
     vm_event_request_t req = {};
+    bool_t sync;
+    int rc;
 
     switch ( type )
     {
@@ -103,6 +105,9 @@  int hvm_monitor_breakpoint(unsigned long rip,
             return 0;
         req.reason = VM_EVENT_REASON_SOFTWARE_BREAKPOINT;
         req.u.software_breakpoint.gfn = gfn_of_rip(rip);
+        req.u.software_breakpoint.type = trap_type;
+        req.u.software_breakpoint.insn_length = insn_length;
+        sync = 1;
         break;
 
     case HVM_MONITOR_SINGLESTEP_BREAKPOINT:
@@ -110,6 +115,17 @@  int hvm_monitor_breakpoint(unsigned long rip,
             return 0;
         req.reason = VM_EVENT_REASON_SINGLESTEP;
         req.u.singlestep.gfn = gfn_of_rip(rip);
+        sync = 1;
+        break;
+
+    case HVM_MONITOR_DEBUG_EXCEPTION:
+        if ( !ad->monitor.debug_exception_enabled )
+            return 0;
+        req.reason = VM_EVENT_REASON_DEBUG_EXCEPTION;
+        req.u.debug_exception.gfn = gfn_of_rip(rip);
+        req.u.debug_exception.type = trap_type;
+        req.u.debug_exception.insn_length = insn_length;
+        sync = !!ad->monitor.debug_exception_sync;
         break;
 
     default:
@@ -119,7 +135,11 @@  int hvm_monitor_breakpoint(unsigned long rip,
     req.vcpu_id = curr->vcpu_id;
     vm_event_fill_regs(&req);
 
-    return vm_event_monitor_traps(curr, 1, &req);
+    rc = vm_event_monitor_traps(curr, sync, &req);
+    if ( type == HVM_MONITOR_DEBUG_EXCEPTION && rc == 1 && !sync )
+        rc = 0;
+
+    return rc;
 }
 
 /*
diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c
index 476a606..ac546ab 100644
--- a/xen/arch/x86/hvm/vmx/vmx.c
+++ b/xen/arch/x86/hvm/vmx/vmx.c
@@ -3373,9 +3373,25 @@  void vmx_vmexit_handler(struct cpu_user_regs *regs)
             HVMTRACE_1D(TRAP_DEBUG, exit_qualification);
             write_debugreg(6, exit_qualification | DR_STATUS_RESERVED_ONE);
             if ( !v->domain->debugger_attached )
-                vmx_propagate_intr(intr_info);
+            {
+                unsigned long insn_length = 0;
+                int handled;
+                unsigned long trap_type = MASK_EXTR(intr_info,
+                                                    INTR_INFO_INTR_TYPE_MASK);
+
+                if( trap_type >= X86_EVENTTYPE_SW_INTERRUPT )
+                    __vmread(VM_EXIT_INSTRUCTION_LEN, &insn_length);
+
+                handled = hvm_monitor_debug(regs->eip,
+                                            HVM_MONITOR_DEBUG_EXCEPTION,
+                                            trap_type, insn_length);
+                if ( handled <= 0 )
+                    vmx_propagate_intr(intr_info);
+
+            }
             else
                 domain_pause_for_debugger();
+
             break;
         case TRAP_int3: 
         {
@@ -3389,8 +3405,9 @@  void vmx_vmexit_handler(struct cpu_user_regs *regs)
             }
             else {
                 int handled =
-                    hvm_monitor_breakpoint(regs->eip,
-                                           HVM_MONITOR_SOFTWARE_BREAKPOINT);
+                        hvm_monitor_debug(regs->eip,
+                                          HVM_MONITOR_SOFTWARE_BREAKPOINT,
+                                          X86_EVENTTYPE_SW_EXCEPTION, 1);
 
                 if ( handled < 0 ) 
                 {
@@ -3717,8 +3734,7 @@  void vmx_vmexit_handler(struct cpu_user_regs *regs)
         vmx_update_cpu_exec_control(v);
         if ( v->arch.hvm_vcpu.single_step )
         {
-            hvm_monitor_breakpoint(regs->eip,
-                                   HVM_MONITOR_SINGLESTEP_BREAKPOINT);
+            hvm_monitor_debug(regs->eip, HVM_MONITOR_SINGLESTEP_BREAKPOINT, 0, 0);
             if ( v->domain->debugger_attached )
                 domain_pause_for_debugger();
         }
diff --git a/xen/arch/x86/monitor.c b/xen/arch/x86/monitor.c
index 621f91a..e8b79f6 100644
--- a/xen/arch/x86/monitor.c
+++ b/xen/arch/x86/monitor.c
@@ -124,6 +124,22 @@  int arch_monitor_domctl_event(struct domain *d,
         break;
     }
 
+    case XEN_DOMCTL_MONITOR_EVENT_DEBUG_EXCEPTION:
+    {
+        bool_t old_status = ad->monitor.debug_exception_enabled;
+
+        if ( unlikely(old_status == requested_status) )
+            return -EEXIST;
+
+        domain_pause(d);
+        ad->monitor.debug_exception_enabled = requested_status;
+        ad->monitor.debug_exception_sync = requested_status ?
+                                            mop->u.debug_exception.sync :
+                                            0;
+        domain_unpause(d);
+        break;
+    }
+
     default:
         /*
          * Should not be reached unless arch_monitor_get_capabilities() is
diff --git a/xen/include/asm-x86/domain.h b/xen/include/asm-x86/domain.h
index 165e533..1cf97c3 100644
--- a/xen/include/asm-x86/domain.h
+++ b/xen/include/asm-x86/domain.h
@@ -405,6 +405,8 @@  struct arch_domain
         unsigned int mov_to_msr_extended         : 1;
         unsigned int singlestep_enabled          : 1;
         unsigned int software_breakpoint_enabled : 1;
+        unsigned int debug_exception_enabled     : 1;
+        unsigned int debug_exception_sync        : 1;
     } monitor;
 
     /* Mem_access emulation control */
diff --git a/xen/include/asm-x86/hvm/monitor.h b/xen/include/asm-x86/hvm/monitor.h
index 1d3c668..422bc11 100644
--- a/xen/include/asm-x86/hvm/monitor.h
+++ b/xen/include/asm-x86/hvm/monitor.h
@@ -23,10 +23,11 @@ 
 #include <xen/paging.h>
 #include <public/vm_event.h>
 
-enum hvm_monitor_breakpoint_type
+enum hvm_monitor_debug_type
 {
     HVM_MONITOR_SOFTWARE_BREAKPOINT,
     HVM_MONITOR_SINGLESTEP_BREAKPOINT,
+    HVM_MONITOR_DEBUG_EXCEPTION,
 };
 
 /*
@@ -39,8 +40,8 @@  bool_t hvm_monitor_cr(unsigned int index, unsigned long value,
 #define hvm_monitor_crX(cr, new, old) \
     hvm_monitor_cr(VM_EVENT_X86_##cr, new, old)
 void hvm_monitor_msr(unsigned int msr, uint64_t value);
-int hvm_monitor_breakpoint(unsigned long rip,
-                           enum hvm_monitor_breakpoint_type type);
+int hvm_monitor_debug(unsigned long rip, enum hvm_monitor_debug_type type,
+                      unsigned long trap_type, unsigned long insn_length);
 
 #endif /* __ASM_X86_HVM_MONITOR_H__ */
 
diff --git a/xen/include/asm-x86/monitor.h b/xen/include/asm-x86/monitor.h
index 446b2af..240a94e 100644
--- a/xen/include/asm-x86/monitor.h
+++ b/xen/include/asm-x86/monitor.h
@@ -64,7 +64,8 @@  static inline uint32_t arch_monitor_get_capabilities(struct domain *d)
     capabilities = (1U << XEN_DOMCTL_MONITOR_EVENT_WRITE_CTRLREG) |
                    (1U << XEN_DOMCTL_MONITOR_EVENT_MOV_TO_MSR) |
                    (1U << XEN_DOMCTL_MONITOR_EVENT_SOFTWARE_BREAKPOINT) |
-                   (1U << XEN_DOMCTL_MONITOR_EVENT_GUEST_REQUEST);
+                   (1U << XEN_DOMCTL_MONITOR_EVENT_GUEST_REQUEST) |
+                   (1U << XEN_DOMCTL_MONITOR_EVENT_DEBUG_EXCEPTION);
 
     /* Since we know this is on VMX, we can just call the hvm func */
     if ( hvm_is_singlestep_supported() )
diff --git a/xen/include/public/domctl.h b/xen/include/public/domctl.h
index 35adce2..b69b1bb 100644
--- a/xen/include/public/domctl.h
+++ b/xen/include/public/domctl.h
@@ -1081,6 +1081,7 @@  DEFINE_XEN_GUEST_HANDLE(xen_domctl_psr_cmt_op_t);
 #define XEN_DOMCTL_MONITOR_EVENT_SOFTWARE_BREAKPOINT   3
 #define XEN_DOMCTL_MONITOR_EVENT_GUEST_REQUEST         4
 #define XEN_DOMCTL_MONITOR_EVENT_PRIVILEGED_CALL       5
+#define XEN_DOMCTL_MONITOR_EVENT_DEBUG_EXCEPTION       6
 
 struct xen_domctl_monitor_op {
     uint32_t op; /* XEN_DOMCTL_MONITOR_OP_* */
@@ -1116,6 +1117,11 @@  struct xen_domctl_monitor_op {
             /* Pause vCPU until response */
             uint8_t sync;
         } guest_request;
+
+        struct {
+            /* Pause vCPU until response */
+            uint8_t sync;
+        } debug_exception;
     } u;
 };
 typedef struct xen_domctl_monitor_op xen_domctl_monitor_op_t;
diff --git a/xen/include/public/vm_event.h b/xen/include/public/vm_event.h
index f039207..6c85532 100644
--- a/xen/include/public/vm_event.h
+++ b/xen/include/public/vm_event.h
@@ -121,6 +121,8 @@ 
 #define VM_EVENT_REASON_GUEST_REQUEST           8
 /* Privileged call executed (e.g. SMC) */
 #define VM_EVENT_REASON_PRIVILEGED_CALL         9
+/* A debug exception was caught */
+#define VM_EVENT_REASON_DEBUG_EXCEPTION         10
 
 /* Supported values for the vm_event_write_ctrlreg index. */
 #define VM_EVENT_X86_CR0    0
@@ -229,8 +231,15 @@  struct vm_event_write_ctrlreg {
     uint64_t old_value;
 };
 
+struct vm_event_singlestep {
+    uint64_t gfn;
+};
+
 struct vm_event_debug {
     uint64_t gfn;
+    uint8_t type;        /* HVMOP_TRAP_* */
+    uint8_t insn_length;
+    uint8_t _pad[6];
 };
 
 struct vm_event_mov_to_msr {
@@ -274,7 +283,8 @@  typedef struct vm_event_st {
         struct vm_event_write_ctrlreg         write_ctrlreg;
         struct vm_event_mov_to_msr            mov_to_msr;
         struct vm_event_debug                 software_breakpoint;
-        struct vm_event_debug                 singlestep;
+        struct vm_event_singlestep            singlestep;
+        struct vm_event_debug                 debug_exception;
     } u;
 
     union {