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

[Xen-changelog] [xen-unstable] mem_access: added INT3/CRx capture



# HG changeset patch
# User Joe Epstein <jepstein98@xxxxxxxxx>
# Date 1294401288 0
# Node ID d9dca2bfe6b2eb9a7ba0f3568244d4c075f97344
# Parent  76e07538870e593c1563a7af08b394e8797f6e48
mem_access: added INT3/CRx capture

* Allows a memory event listener to register for events on changes to
  CR0, CR3, and CR4, as well as INT3 instructions, as a part of the
  mem_access mechanism.  These events can be either synchronous or
  asynchronous.

* For INT3, the logic works independent of a debugger, and so both can
  be supported.

* The presence and type of listener are stored and accessed through
  HVM params.

* Changed the event mask handling to ensure that the right events are
  captured based on the listeners.

* Added the ability to inject HW/SW traps into a VCPU when it next
  resumes (rather than try to modify the existing IRQ injection
  code paths).  Only one trap to inject can be outstanding at a time.

Signed-off-by: Joe Epstein <jepstein98@xxxxxxxxx>
Acked-by: Keir Fraser <keir@xxxxxxx>
Acked-by: Tim Deegan <Tim.Deegan@xxxxxxxxxx>
---
 xen/arch/x86/hvm/hvm.c          |  164 +++++++++++++++++++++++++++++++++++++++-
 xen/arch/x86/hvm/vmx/vmcs.c     |    4 
 xen/arch/x86/hvm/vmx/vmx.c      |   72 ++++++++++++++---
 xen/include/asm-x86/hvm/hvm.h   |    8 +
 xen/include/asm-x86/hvm/vcpu.h  |    5 +
 xen/include/public/hvm/hvm_op.h |   22 +++++
 xen/include/public/hvm/params.h |   15 +++
 xen/include/public/mem_event.h  |    4 
 8 files changed, 278 insertions(+), 16 deletions(-)

diff -r 76e07538870e -r d9dca2bfe6b2 xen/arch/x86/hvm/hvm.c
--- a/xen/arch/x86/hvm/hvm.c    Fri Jan 07 11:54:45 2011 +0000
+++ b/xen/arch/x86/hvm/hvm.c    Fri Jan 07 11:54:48 2011 +0000
@@ -309,6 +309,15 @@ void hvm_do_resume(struct vcpu *v)
             return; /* bail */
         }
     }
+
+    /* Inject pending hw/sw trap */
+    if (v->arch.hvm_vcpu.inject_trap != -1) 
+    {
+        hvm_inject_exception(v->arch.hvm_vcpu.inject_trap, 
+                             v->arch.hvm_vcpu.inject_error_code, 
+                             v->arch.hvm_vcpu.inject_cr2);
+        v->arch.hvm_vcpu.inject_trap = -1;
+    }
 }
 
 static void hvm_init_ioreq_page(
@@ -948,6 +957,8 @@ int hvm_vcpu_initialise(struct vcpu *v)
 
     spin_lock_init(&v->arch.hvm_vcpu.tm_lock);
     INIT_LIST_HEAD(&v->arch.hvm_vcpu.tm_list);
+
+    v->arch.hvm_vcpu.inject_trap = -1;
 
 #ifdef CONFIG_COMPAT
     rc = setup_compat_arg_xlat(v);
@@ -3236,10 +3247,45 @@ long do_hvm_op(unsigned long op, XEN_GUE
             case HVM_PARAM_ACPI_IOPORTS_LOCATION:
                 rc = pmtimer_change_ioport(d, a.value);
                 break;
+            case HVM_PARAM_MEMORY_EVENT_CR0:
+            case HVM_PARAM_MEMORY_EVENT_CR3:
+            case HVM_PARAM_MEMORY_EVENT_CR4:
+                if ( d->domain_id == current->domain->domain_id )
+                    rc = -EPERM;
+                break;
+            case HVM_PARAM_MEMORY_EVENT_INT3:
+                if ( d->domain_id == current->domain->domain_id ) 
+                {
+                    rc = -EPERM;
+                    break;
+                }
+                if ( a.value & HVMPME_onchangeonly )
+                    rc = -EINVAL;
+                break;
             }
 
-            if ( rc == 0 )
+            if ( rc == 0 ) 
+            {
                 d->arch.hvm_domain.params[a.index] = a.value;
+
+                switch( a.index )
+                {
+                case HVM_PARAM_MEMORY_EVENT_INT3:
+                {
+                    domain_pause(d);
+                    domain_unpause(d); /* Causes guest to latch new status */
+                    break;
+                }
+                case HVM_PARAM_MEMORY_EVENT_CR3:
+                {
+                    for_each_vcpu ( d, v )
+                        hvm_funcs.update_guest_cr(v, 0); /* Latches new CR3 
mask through CR0 code */
+                    break;
+                }
+                }
+
+            }
+
         }
         else
         {
@@ -3657,6 +3703,44 @@ long do_hvm_op(unsigned long op, XEN_GUE
         break;
     }
 
+    case HVMOP_inject_trap: 
+    {
+        xen_hvm_inject_trap_t tr;
+        struct domain *d;
+        struct vcpu *v;
+
+        if ( copy_from_guest(&tr, arg, 1 ) )
+            return -EFAULT;
+
+        if ( current->domain->domain_id == tr.domid )
+            return -EPERM;
+
+        rc = rcu_lock_target_domain_by_id(tr.domid, &d);
+        if ( rc != 0 )
+            return rc;
+
+        rc = -EINVAL;
+        if ( !is_hvm_domain(d) )
+            goto param_fail8;
+
+        rc = -ENOENT;
+        if ( tr.vcpuid >= d->max_vcpus || (v = d->vcpu[tr.vcpuid]) == NULL )
+            goto param_fail8;
+        
+        if ( v->arch.hvm_vcpu.inject_trap != -1 )
+            rc = -EBUSY;
+        else 
+        {
+            v->arch.hvm_vcpu.inject_trap       = tr.trap;
+            v->arch.hvm_vcpu.inject_error_code = tr.error_code;
+            v->arch.hvm_vcpu.inject_cr2        = tr.cr2;
+        }
+
+    param_fail8:
+        rcu_unlock_domain(d);
+        break;
+    }
+
     default:
     {
         gdprintk(XENLOG_WARNING, "Bad HVM op %ld.\n", op);
@@ -3697,6 +3781,84 @@ int hvm_debug_op(struct vcpu *v, int32_t
     return rc;
 }
 
+static int hvm_memory_event_traps(long p, uint32_t reason,
+                                  unsigned long value, unsigned long old, 
+                                  bool_t gla_valid, unsigned long gla) 
+{
+    struct vcpu* v = current;
+    struct domain *d = v->domain;
+    mem_event_request_t req;
+    int rc;
+
+    if ( !(p & HVMPME_MODE_MASK) ) 
+        return 0;
+
+    if ( (p & HVMPME_onchangeonly) && (value == old) )
+        return 1;
+    
+    rc = mem_event_check_ring(d);
+    if ( rc )
+        return rc;
+    
+    memset(&req, 0, sizeof(req));
+    req.type = MEM_EVENT_TYPE_ACCESS;
+    req.reason = reason;
+    
+    if ( (p & HVMPME_MODE_MASK) == HVMPME_mode_sync ) 
+    {
+        req.flags |= MEM_EVENT_FLAG_VCPU_PAUSED;    
+        vcpu_pause_nosync(v);   
+    }
+
+    req.gfn = value;
+    req.vcpu_id = v->vcpu_id;
+    if ( gla_valid ) 
+    {
+        req.offset = gla & ((1 << PAGE_SHIFT) - 1);
+        req.gla = gla;
+        req.gla_valid = 1;
+    }
+    
+    mem_event_put_request(d, &req);      
+    
+    return 1;
+}
+
+void hvm_memory_event_cr0(unsigned long value, unsigned long old) 
+{
+    hvm_memory_event_traps(current->domain->arch.hvm_domain
+                             .params[HVM_PARAM_MEMORY_EVENT_CR0],
+                           MEM_EVENT_REASON_CR0,
+                           value, old, 0, 0);
+}
+
+void hvm_memory_event_cr3(unsigned long value, unsigned long old) 
+{
+    hvm_memory_event_traps(current->domain->arch.hvm_domain
+                             .params[HVM_PARAM_MEMORY_EVENT_CR3],
+                           MEM_EVENT_REASON_CR3,
+                           value, old, 0, 0);
+}
+
+void hvm_memory_event_cr4(unsigned long value, unsigned long old) 
+{
+    hvm_memory_event_traps(current->domain->arch.hvm_domain
+                             .params[HVM_PARAM_MEMORY_EVENT_CR4],
+                           MEM_EVENT_REASON_CR4,
+                           value, old, 0, 0);
+}
+
+int hvm_memory_event_int3(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_INT3],
+                                  MEM_EVENT_REASON_INT3,
+                                  gfn, 0, 1, gla);
+}
 
 /*
  * Local variables:
diff -r 76e07538870e -r d9dca2bfe6b2 xen/arch/x86/hvm/vmx/vmcs.c
--- a/xen/arch/x86/hvm/vmx/vmcs.c       Fri Jan 07 11:54:45 2011 +0000
+++ b/xen/arch/x86/hvm/vmx/vmcs.c       Fri Jan 07 11:54:48 2011 +0000
@@ -1082,7 +1082,9 @@ void vmx_do_resume(struct vcpu *v)
         hvm_asid_flush_vcpu(v);
     }
 
-    debug_state = v->domain->debugger_attached;
+    debug_state = v->domain->debugger_attached 
+                  || 
v->domain->arch.hvm_domain.params[HVM_PARAM_MEMORY_EVENT_INT3];
+
     if ( unlikely(v->arch.hvm_vcpu.debug_state_latch != debug_state) )
     {
         v->arch.hvm_vcpu.debug_state_latch = debug_state;
diff -r 76e07538870e -r d9dca2bfe6b2 xen/arch/x86/hvm/vmx/vmx.c
--- a/xen/arch/x86/hvm/vmx/vmx.c        Fri Jan 07 11:54:45 2011 +0000
+++ b/xen/arch/x86/hvm/vmx/vmx.c        Fri Jan 07 11:54:48 2011 +0000
@@ -1064,12 +1064,16 @@ static void vmx_update_guest_cr(struct v
 
         if ( paging_mode_hap(v->domain) )
         {
-            /* We manage GUEST_CR3 when guest CR0.PE is zero. */
+            /* We manage GUEST_CR3 when guest CR0.PE is zero or when cr3 
memevents are on */            
             uint32_t cr3_ctls = (CPU_BASED_CR3_LOAD_EXITING |
                                  CPU_BASED_CR3_STORE_EXITING);
             v->arch.hvm_vmx.exec_control &= ~cr3_ctls;
             if ( !hvm_paging_enabled(v) )
                 v->arch.hvm_vmx.exec_control |= cr3_ctls;
+
+            if ( v->domain->arch.hvm_domain.params[HVM_PARAM_MEMORY_EVENT_CR3] 
)
+                v->arch.hvm_vmx.exec_control |= CPU_BASED_CR3_LOAD_EXITING;
+
             vmx_update_cpu_exec_control(v);
 
             /* Changing CR0.PE can change some bits in real CR4. */
@@ -1252,9 +1256,12 @@ void vmx_inject_hw_exception(int trap, i
     unsigned long intr_info = __vmread(VM_ENTRY_INTR_INFO);
     struct vcpu *curr = current;
 
+    int type = X86_EVENTTYPE_HW_EXCEPTION;
+
     switch ( trap )
     {
     case TRAP_debug:
+        type = X86_EVENTTYPE_SW_EXCEPTION;
         if ( guest_cpu_user_regs()->eflags & X86_EFLAGS_TF )
         {
             __restore_debug_registers(curr);
@@ -1269,6 +1276,9 @@ void vmx_inject_hw_exception(int trap, i
             domain_pause_for_debugger();
             return;
         }
+
+        type = X86_EVENTTYPE_SW_EXCEPTION;
+        __vmwrite(VM_ENTRY_INSTRUCTION_LEN, 1); /* int3 */
     }
 
     if ( unlikely(intr_info & INTR_INFO_VALID_MASK) &&
@@ -1279,7 +1289,7 @@ void vmx_inject_hw_exception(int trap, i
             error_code = 0;
     }
 
-    __vmx_inject_exception(trap, X86_EVENTTYPE_HW_EXCEPTION, error_code);
+    __vmx_inject_exception(trap, type, error_code);
 
     if ( trap == TRAP_page_fault )
         HVMTRACE_LONG_2D(PF_INJECT, error_code,
@@ -1565,6 +1575,8 @@ static int mov_to_cr(int gp, int cr, str
     unsigned long value;
     struct vcpu *v = current;
     struct vlapic *vlapic = vcpu_vlapic(v);
+    int rc = 0;
+    unsigned long old;
 
     switch ( gp )
     {
@@ -1589,13 +1601,25 @@ static int mov_to_cr(int gp, int cr, str
     switch ( cr )
     {
     case 0:
-        return !hvm_set_cr0(value);
+        old = v->arch.hvm_vcpu.guest_cr[0];
+        rc = !hvm_set_cr0(value);
+        if (rc)
+            hvm_memory_event_cr0(value, old);
+        return rc;
 
     case 3:
-        return !hvm_set_cr3(value);
+        old = v->arch.hvm_vcpu.guest_cr[3];
+        rc = !hvm_set_cr3(value);
+        if (rc)
+            hvm_memory_event_cr3(value, old);        
+        return rc;
 
     case 4:
-        return !hvm_set_cr4(value);
+        old = v->arch.hvm_vcpu.guest_cr[4];
+        rc = !hvm_set_cr4(value);
+        if (rc)
+            hvm_memory_event_cr4(value, old);
+        return rc; 
 
     case 8:
         vlapic_set_reg(vlapic, APIC_TASKPRI, ((value & 0x0F) << 4));
@@ -1676,11 +1700,17 @@ static int vmx_cr_access(unsigned long e
         cr = exit_qualification & VMX_CONTROL_REG_ACCESS_NUM;
         mov_from_cr(cr, gp, regs);
         break;
-    case VMX_CONTROL_REG_ACCESS_TYPE_CLTS:
+    case VMX_CONTROL_REG_ACCESS_TYPE_CLTS: 
+    {
+        unsigned long old = v->arch.hvm_vcpu.guest_cr[0];
         v->arch.hvm_vcpu.guest_cr[0] &= ~X86_CR0_TS;
         vmx_update_guest_cr(v, 0);
+
+        hvm_memory_event_cr0(v->arch.hvm_vcpu.guest_cr[0], old);
+
         HVMTRACE_0D(CLTS);
         break;
+    }
     case VMX_CONTROL_REG_ACCESS_TYPE_LMSW:
         value = v->arch.hvm_vcpu.guest_cr[0];
         /* LMSW can: (1) set bits 0-3; (2) clear bits 1-3. */
@@ -2351,13 +2381,29 @@ asmlinkage void vmx_vmexit_handler(struc
                 goto exit_and_crash;
             domain_pause_for_debugger();
             break;
-        case TRAP_int3:
-            if ( !v->domain->debugger_attached )
-                goto exit_and_crash;
-            update_guest_eip(); /* Safe: INT3 */
-            current->arch.gdbsx_vcpu_event = TRAP_int3;
-            domain_pause_for_debugger();
-            break;
+        case TRAP_int3: 
+        {
+            if ( v->domain->debugger_attached )
+            {
+                update_guest_eip(); /* Safe: INT3 */            
+                current->arch.gdbsx_vcpu_event = TRAP_int3;
+                domain_pause_for_debugger();
+                break;
+            }
+            else {
+                int handled = hvm_memory_event_int3(regs->eip);
+                
+                if ( handled < 0 ) 
+                {
+                    vmx_inject_exception(TRAP_int3, HVM_DELIVER_NO_ERROR_CODE, 
0);
+                    break;
+                }
+                else if ( handled )
+                    break;
+            }
+
+            goto exit_and_crash;
+        }
         case TRAP_no_device:
             vmx_fpu_dirty_intercept();
             break;
diff -r 76e07538870e -r d9dca2bfe6b2 xen/include/asm-x86/hvm/hvm.h
--- a/xen/include/asm-x86/hvm/hvm.h     Fri Jan 07 11:54:45 2011 +0000
+++ b/xen/include/asm-x86/hvm/hvm.h     Fri Jan 07 11:54:48 2011 +0000
@@ -372,4 +372,12 @@ int hvm_x2apic_msr_read(struct vcpu *v, 
 int hvm_x2apic_msr_read(struct vcpu *v, unsigned int msr, uint64_t 
*msr_content);
 int hvm_x2apic_msr_write(struct vcpu *v, unsigned int msr, uint64_t 
msr_content);
 
+/* Called for current VCPU on crX changes by guest */
+void hvm_memory_event_cr0(unsigned long value, unsigned long old);
+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);
+
 #endif /* __ASM_X86_HVM_HVM_H__ */
diff -r 76e07538870e -r d9dca2bfe6b2 xen/include/asm-x86/hvm/vcpu.h
--- a/xen/include/asm-x86/hvm/vcpu.h    Fri Jan 07 11:54:45 2011 +0000
+++ b/xen/include/asm-x86/hvm/vcpu.h    Fri Jan 07 11:54:48 2011 +0000
@@ -114,6 +114,11 @@ struct hvm_vcpu {
     /* We may write up to m128 as a number of device-model transactions. */
     paddr_t mmio_large_write_pa;
     unsigned int mmio_large_write_bytes;
+
+    /* Pending hw/sw interrupt */
+    int           inject_trap;       /* -1 for nothing to inject */
+    int           inject_error_code;
+    unsigned long inject_cr2;
 };
 
 #endif /* __ASM_X86_HVM_VCPU_H__ */
diff -r 76e07538870e -r d9dca2bfe6b2 xen/include/public/hvm/hvm_op.h
--- a/xen/include/public/hvm/hvm_op.h   Fri Jan 07 11:54:45 2011 +0000
+++ b/xen/include/public/hvm/hvm_op.h   Fri Jan 07 11:54:48 2011 +0000
@@ -200,4 +200,26 @@ struct xen_hvm_get_mem_access {
 };
 typedef struct xen_hvm_get_mem_access xen_hvm_get_mem_access_t;
 DEFINE_XEN_GUEST_HANDLE(xen_hvm_get_mem_access_t);
+
+#define HVMOP_inject_trap            14
+/* Inject a trap into a VCPU, which will get taken up on the next
+ * scheduling of it. Note that the caller should know enough of the
+ * state of the CPU before injecting, to know what the effect of
+ * injecting the trap will be.
+ */
+struct xen_hvm_inject_trap {
+    /* Domain to be queried. */
+    domid_t domid;
+    /* VCPU */
+    uint32_t vcpuid;
+    /* Trap number */
+    uint32_t trap;
+    /* Error code, or -1 to skip */
+    uint32_t error_code;
+    /* CR2 for page faults */
+    uint64_t cr2;
+};
+typedef struct xen_hvm_inject_trap xen_hvm_inject_trap_t;
+DEFINE_XEN_GUEST_HANDLE(xen_hvm_inject_trap_t);
+
 #endif /* __XEN_PUBLIC_HVM_HVM_OP_H__ */
diff -r 76e07538870e -r d9dca2bfe6b2 xen/include/public/hvm/params.h
--- a/xen/include/public/hvm/params.h   Fri Jan 07 11:54:45 2011 +0000
+++ b/xen/include/public/hvm/params.h   Fri Jan 07 11:54:48 2011 +0000
@@ -124,6 +124,19 @@
  */
 #define HVM_PARAM_ACPI_IOPORTS_LOCATION 19
 
-#define HVM_NR_PARAMS          20
+/* 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 HVMPME_MODE_MASK       (3 << 0)
+#define HVMPME_mode_disabled   0
+#define HVMPME_mode_async      1
+#define HVMPME_mode_sync       2
+#define HVMPME_onchangeonly    (1 << 2)
+
+#define HVM_NR_PARAMS          24
 
 #endif /* __XEN_PUBLIC_HVM_PARAMS_H__ */
diff -r 76e07538870e -r d9dca2bfe6b2 xen/include/public/mem_event.h
--- a/xen/include/public/mem_event.h    Fri Jan 07 11:54:45 2011 +0000
+++ b/xen/include/public/mem_event.h    Fri Jan 07 11:54:48 2011 +0000
@@ -37,6 +37,10 @@
 /* Reasons for the memory event request */
 #define MEM_EVENT_REASON_UNKNOWN     0    /* typical reason */
 #define MEM_EVENT_REASON_VIOLATION   1    /* access violation, GFN is address 
*/
+#define MEM_EVENT_REASON_CR0         2    /* CR0 was hit: gfn is CR0 value */
+#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 */
 
 typedef struct mem_event_shared_page {
     uint32_t port;

_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog


 


Rackspace

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