[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Xen-devel] [PATCH v6 5/5] x86/vm_event: Add HVM debug exception vm_events



On 06/23/2016 08:07 PM, Tamas K Lengyel wrote:
> 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.
> 
> This patch also provides monitor subscribers to int3 events proper access
> to the instruction length necessary for accurate event-reinjection. Without
> this subscribers manually have to evaluate if the int3 instruction has any
> prefix attached which would change the instruction length.
> 
> Signed-off-by: Tamas K Lengyel <tamas@xxxxxxxxxxxxx>
> ---
> Cc: Ian Jackson <ian.jackson@xxxxxxxxxxxxx>
> Cc: Wei Liu <wei.liu2@xxxxxxxxxx>
> Cc: Razvan Cojocaru <rcojocaru@xxxxxxxxxxxxxxx>
> Cc: Jan Beulich <jbeulich@xxxxxxxx>
> Cc: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
> Cc: Jun Nakajima <jun.nakajima@xxxxxxxxx>
> Cc: Kevin Tian <kevin.tian@xxxxxxxxx>
> 
> v6: Add comment describing rc condition values for the monitor call
> v5: Transmit the proper insn_length for int3 events as well to fix
>      the current bug with prefixed int3 instructions.
> ---
>  tools/libxc/include/xenctrl.h       |  3 +-
>  tools/libxc/xc_monitor.c            | 25 +++++++++++++++
>  tools/tests/xen-access/xen-access.c | 63 
> ++++++++++++++++++++++++++++++++-----
>  xen/arch/x86/hvm/monitor.c          | 21 +++++++++++--
>  xen/arch/x86/hvm/vmx/vmx.c          | 55 +++++++++++++++++++++++++-------
>  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       | 14 +++++++--
>  11 files changed, 186 insertions(+), 29 deletions(-)
> 
> diff --git a/tools/libxc/include/xenctrl.h b/tools/libxc/include/xenctrl.h
> index 4f5d954..4a85b4a 100644
> --- a/tools/libxc/include/xenctrl.h
> +++ b/tools/libxc/include/xenctrl.h
> @@ -2165,7 +2165,8 @@ int xc_monitor_software_breakpoint(xc_interface *xch, 
> domid_t domain_id,
>                                     bool enable);
>  int xc_monitor_guest_request(xc_interface *xch, domid_t domain_id,
>                               bool enable, bool sync);
> -
> +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 78131b2..264992c 100644
> --- a/tools/libxc/xc_monitor.c
> +++ b/tools/libxc/xc_monitor.c
> @@ -156,3 +156,28 @@ int xc_monitor_emulate_each_rep(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
> + * c-file-style: "BSD"
> + * c-basic-offset: 4
> + * tab-width: 4
> + * indent-tabs-mode: nil
> + * End:
> + */
> diff --git a/tools/tests/xen-access/xen-access.c 
> b/tools/tests/xen-access/xen-access.c
> index f26e723..e615476 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");
>  #endif
>              fprintf(stderr,
>              "\n"
> @@ -354,10 +358,12 @@ 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 altp2m = 0;
> +    int debug = 0;
>      uint16_t altp2m_view_id = 0;
>  
>      char* progname = argv[0];
> @@ -391,11 +397,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") )
> @@ -406,11 +414,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;
>      }
>  #endif
>      else
> @@ -446,7 +460,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;
> @@ -493,7 +507,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);
> @@ -524,6 +538,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 (;;)
>      {
> @@ -534,6 +558,8 @@ int main(int argc, char *argv[])
>  
>              if ( breakpoint )
>                  rc = xc_monitor_software_breakpoint(xch, domain_id, 0);
> +            if ( debug )
> +                rc = xc_monitor_debug_exceptions(xch, domain_id, 0, 0);
>  
>              if ( altp2m )
>              {
> @@ -635,22 +661,22 @@ int main(int argc, char *argv[])
>                  rsp.u.mem_access = req.u.mem_access;
>                  break;
>              case VM_EVENT_REASON_SOFTWARE_BREAKPOINT:
> -                printf("Breakpoint: rip=%016"PRIx64", gfn=%"PRIx64" (vcpu 
> %d)\n",
> +                printf("Breakpoint: rip=%016"PRIx64" gfn=%"PRIx64" (vcpu 
> %d)\n",

You seem to have removed the comma here (',') after the first "PRIx64",
but ...

>                         req.data.regs.x86.rip,
>                         req.u.software_breakpoint.gfn,
>                         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);
>                      interrupted = -1;
>                      continue;
>                  }
> -
>                  break;
>              case VM_EVENT_REASON_SINGLESTEP:
>                  printf("Singlestep: rip=%016"PRIx64", vcpu %d, altp2m %u\n",
> @@ -669,6 +695,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",

... this newly added line uses the old style (with a comma after the
first "PRIx64"). Minor issue maybe, I just don't understand why the
first modification was made.

> +                       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 472926c..bbe5952 100644
> --- a/xen/arch/x86/hvm/monitor.c
> +++ b/xen/arch/x86/hvm/monitor.c
> @@ -87,12 +87,13 @@ 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;
>  
>      switch ( type )
>      {
> @@ -101,6 +102,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:
> @@ -108,6 +112,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:
> @@ -116,7 +131,7 @@ int hvm_monitor_breakpoint(unsigned long rip,
>  
>      req.vcpu_id = curr->vcpu_id;
>  
> -    return vm_event_monitor_traps(curr, 1, &req);
> +    return vm_event_monitor_traps(curr, sync, &req);


What would be a basic use-case for this event to be sent without pausing
the VCPU (i.e. with sync != 1)?


Thanks,
Razvan

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.