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

[Xen-devel] [PATCH V5 4/5] xen, libxc: Request page fault injection via libxc



Extended HVMOP_inject_trap to allow asking for trap injection done
by the first available CPU, when it's in user mode and its CR3
matches the one for an interesting application inside the guest.
This mechanism allows bringing in swapped-out pages for inspection.

Signed-off-by: Razvan Cojocaru <rcojocaru@xxxxxxxxxxxxxxx>
Acked-by: Ian Campbell <ian.campbell@xxxxxxxxxx>
Reviewed-by: Kevin Tian <kevin.tian@xxxxxxxxx>

---
Changes since V4:
 - Allow per-domain trap injection without checking if CR3 matches
   for traps other than page faults.
 - Added comments to explain that trap injection requests are not
   serialized.

Changes since V3:
 - Now returning -EBUSY if the hypercall tries to set per-domain
   injection data when there's a pending injection on any of the
   domain's CPUs.
 - Minor code cleanup.

Changes since V2:
 - Renamed hvm_is_pf_requested() to hvm_can_inject_domain_pf().
 - Modified comments to better reflect the introspection-related
   purpose of added code.
---
 tools/libxc/xc_misc.c               |    5 +-
 tools/libxc/xenctrl.h               |    3 +-
 tools/tests/xen-access/xen-access.c |    2 +-
 xen/arch/x86/hvm/hvm.c              |  108 ++++++++++++++++++++++++++++++-----
 xen/include/asm-x86/hvm/domain.h    |    3 +
 xen/include/asm-x86/hvm/hvm.h       |    1 +
 xen/include/public/hvm/hvm_op.h     |    7 +++
 7 files changed, 111 insertions(+), 18 deletions(-)

diff --git a/tools/libxc/xc_misc.c b/tools/libxc/xc_misc.c
index e253a58..6773446 100644
--- a/tools/libxc/xc_misc.c
+++ b/tools/libxc/xc_misc.c
@@ -597,7 +597,7 @@ int xc_hvm_set_mem_type(
 int xc_hvm_inject_trap(
     xc_interface *xch, domid_t dom, int vcpu, uint32_t vector,
     uint32_t type, uint32_t error_code, uint32_t insn_len,
-    uint64_t cr2)
+    uint64_t cr2, uint64_t cr3)
 {
     DECLARE_HYPERCALL;
     DECLARE_HYPERCALL_BUFFER(struct xen_hvm_inject_trap, arg);
@@ -611,12 +611,13 @@ int xc_hvm_inject_trap(
     }
 
     arg->domid       = dom;
-    arg->vcpuid      = vcpu;
+    arg->vcpuid      = (vcpu == -1 ? (uint32_t)~0 : vcpu);
     arg->vector      = vector;
     arg->type        = type;
     arg->error_code  = error_code;
     arg->insn_len    = insn_len;
     arg->cr2         = cr2;
+    arg->cr3         = cr3;
 
     hypercall.op     = __HYPERVISOR_hvm_op;
     hypercall.arg[0] = HVMOP_inject_trap;
diff --git a/tools/libxc/xenctrl.h b/tools/libxc/xenctrl.h
index 514b241..0a0281c 100644
--- a/tools/libxc/xenctrl.h
+++ b/tools/libxc/xenctrl.h
@@ -1826,11 +1826,12 @@ int xc_hvm_set_mem_type(
 /*
  * Injects a hardware/software CPU trap, to take effect the next time the HVM 
  * resumes. 
+ * Cr3 is only taken into account if vcpu == -1 (wildcard for "any vcpu").
  */
 int xc_hvm_inject_trap(
     xc_interface *xch, domid_t dom, int vcpu, uint32_t vector,
     uint32_t type, uint32_t error_code, uint32_t insn_len,
-    uint64_t cr2);
+    uint64_t cr2, uint64_t cr3);
 
 /*
  *  LOGGING AND ERROR REPORTING
diff --git a/tools/tests/xen-access/xen-access.c 
b/tools/tests/xen-access/xen-access.c
index 6cb382d..f7456cc 100644
--- a/tools/tests/xen-access/xen-access.c
+++ b/tools/tests/xen-access/xen-access.c
@@ -604,7 +604,7 @@ int main(int argc, char *argv[])
                 /* Reinject */
                 rc = xc_hvm_inject_trap(
                     xch, domain_id, req.vcpu_id, 3,
-                    HVMOP_TRAP_sw_exc, -1, 0, 0);
+                    HVMOP_TRAP_sw_exc, -1, 0, 0, 0);
                 if (rc < 0)
                 {
                     ERROR("Error %d injecting int3\n", rc);
diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
index bb45593..0e7e45b 100644
--- a/xen/arch/x86/hvm/hvm.c
+++ b/xen/arch/x86/hvm/hvm.c
@@ -420,6 +420,31 @@ static bool_t hvm_wait_for_io(struct hvm_ioreq_vcpu *sv, 
ioreq_t *p)
     return 1;
 }
 
+static bool_t hvm_can_inject_domain_pf(struct vcpu *v)
+{
+    const struct domain *d = v->domain;
+    struct segment_register seg;
+    uint64_t mask;
+
+    hvm_get_segment_register(v, x86_seg_ss, &seg);
+
+    if ( seg.attr.fields.dpl != 3 ) /* Guest is not in user mode */
+        return 0;
+
+    if ( hvm_long_mode_enabled(v) )
+        mask = PADDR_MASK & PAGE_MASK; /* Bits 51:12. */
+    else if ( hvm_pae_enabled(v) )
+        mask = 0x00000000ffffffe0; /* Bits 31:5. */
+    else
+        mask = (uint32_t)PAGE_MASK; /* Bits 31:12. */
+
+    if ( (v->arch.hvm_vcpu.guest_cr[3] & mask) !=
+         (d->arch.hvm_domain.inject_trap.cr3 & mask) )
+        return 0;
+
+    return 1;
+}
+
 void hvm_do_resume(struct vcpu *v)
 {
     struct domain *d = v->domain;
@@ -450,12 +475,29 @@ void hvm_do_resume(struct vcpu *v)
         }
     }
 
+    /*
+     * Currently, injection requests are not serialized, to keep
+     * with the original behaviour of the per-VCPU operation of
+     * HVMOP_inject_trap.
+     */
+
     /* Inject pending hw/sw trap */
-    if ( v->arch.hvm_vcpu.inject_trap.vector != -1 ) 
+    if ( v->arch.hvm_vcpu.inject_trap.vector != -1 )
     {
         hvm_inject_trap(&v->arch.hvm_vcpu.inject_trap);
         v->arch.hvm_vcpu.inject_trap.vector = -1;
     }
+    /*
+     * Inject per-domain pending hw/sw trap (this will most likely
+     * be a page fault injected by memory introspection code).
+     */
+    else if ( d->arch.hvm_domain.inject_trap.vector != -1 && 
+              ( d->arch.hvm_domain.inject_trap.vector != TRAP_page_fault ||
+                hvm_can_inject_domain_pf(v) ) )
+    {
+        hvm_inject_trap(&d->arch.hvm_domain.inject_trap);
+        d->arch.hvm_domain.inject_trap.vector = -1;
+    }
 }
 
 static int hvm_alloc_ioreq_gmfn(struct domain *d, unsigned long *gmfn)
@@ -1473,9 +1515,10 @@ int hvm_domain_initialise(struct domain *d)
             printk(XENLOG_G_INFO "PVH guest must have HAP on\n");
             return -EINVAL;
         }
-
     }
 
+    d->arch.hvm_domain.inject_trap.vector = -1;
+
     spin_lock_init(&d->arch.hvm_domain.ioreq_server.lock);
     INIT_LIST_HEAD(&d->arch.hvm_domain.ioreq_server.list);
     spin_lock_init(&d->arch.hvm_domain.irq_lock);
@@ -6086,19 +6129,56 @@ long do_hvm_op(unsigned long op, 
XEN_GUEST_HANDLE_PARAM(void) arg)
             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.vector != -1 )
-            rc = -EBUSY;
-        else 
+
+        /*
+         * Currently, injection requests are not serialized, to keep
+         * with the original behaviour of the per-VCPU operation of
+         * HVMOP_inject_trap.
+         */
+        if ( tr.vcpuid == (uint32_t)~0 ) /* Any VCPU. */
         {
-            v->arch.hvm_vcpu.inject_trap.vector = tr.vector;
-            v->arch.hvm_vcpu.inject_trap.type = tr.type;
-            v->arch.hvm_vcpu.inject_trap.error_code = tr.error_code;
-            v->arch.hvm_vcpu.inject_trap.insn_len = tr.insn_len;
-            v->arch.hvm_vcpu.inject_trap.cr2 = tr.cr2;
-            rc = 0;
+            unsigned int i;
+
+            for ( i = 0; i < d->max_vcpus; i++ )
+                if ( (v = d->vcpu[i]) != NULL &&
+                     v->arch.hvm_vcpu.inject_trap.vector != -1 )
+                {
+                    rc = -EBUSY;
+                    break;
+                }
+
+            if ( d->arch.hvm_domain.inject_trap.vector != -1 )
+                rc = -EBUSY;
+
+            if ( rc != -EBUSY )
+            {
+                d->arch.hvm_domain.inject_trap.vector = tr.vector;
+                d->arch.hvm_domain.inject_trap.type = tr.type;
+                d->arch.hvm_domain.inject_trap.error_code = tr.error_code;
+                d->arch.hvm_domain.inject_trap.insn_len = tr.insn_len;
+                d->arch.hvm_domain.inject_trap.cr2 = tr.cr2;
+                d->arch.hvm_domain.inject_trap.cr3 = tr.cr3;
+                rc = 0;
+            }
+        }
+        else /* Specific VCPU. */
+        {
+            if ( tr.vcpuid >= d->max_vcpus || (v = d->vcpu[tr.vcpuid]) == NULL 
)
+                goto param_fail8;
+
+            if ( v->arch.hvm_vcpu.inject_trap.vector != -1 ||
+                 d->arch.hvm_domain.inject_trap.vector != -1 )
+                rc = -EBUSY;
+            else
+            {
+                v->arch.hvm_vcpu.inject_trap.vector = tr.vector;
+                v->arch.hvm_vcpu.inject_trap.type = tr.type;
+                v->arch.hvm_vcpu.inject_trap.error_code = tr.error_code;
+                v->arch.hvm_vcpu.inject_trap.insn_len = tr.insn_len;
+                v->arch.hvm_vcpu.inject_trap.cr2 = tr.cr2;
+                v->arch.hvm_vcpu.inject_trap.cr3 = tr.cr3;
+                rc = 0;
+            }
         }
 
     param_fail8:
diff --git a/xen/include/asm-x86/hvm/domain.h b/xen/include/asm-x86/hvm/domain.h
index 30d4aa3..b432874 100644
--- a/xen/include/asm-x86/hvm/domain.h
+++ b/xen/include/asm-x86/hvm/domain.h
@@ -146,6 +146,9 @@ struct hvm_domain {
         struct vmx_domain vmx;
         struct svm_domain svm;
     };
+
+    /* Pending hw/sw interrupt (.vector = -1 means nothing pending). */
+    struct hvm_trap     inject_trap;
 };
 
 #define hap_enabled(d)  ((d)->arch.hvm_domain.hap_enabled)
diff --git a/xen/include/asm-x86/hvm/hvm.h b/xen/include/asm-x86/hvm/hvm.h
index 121d053..3b0bde9 100644
--- a/xen/include/asm-x86/hvm/hvm.h
+++ b/xen/include/asm-x86/hvm/hvm.h
@@ -78,6 +78,7 @@ struct hvm_trap {
     int           error_code;   /* HVM_DELIVER_NO_ERROR_CODE if n/a */
     int           insn_len;     /* Instruction length */ 
     unsigned long cr2;          /* Only for TRAP_page_fault h/w exception */
+    unsigned long cr3;          /* Only for TRAP_page_fault h/w exception */
 };
 
 /*
diff --git a/xen/include/public/hvm/hvm_op.h b/xen/include/public/hvm/hvm_op.h
index eeb0a60..eb018d4 100644
--- a/xen/include/public/hvm/hvm_op.h
+++ b/xen/include/public/hvm/hvm_op.h
@@ -197,6 +197,13 @@ struct xen_hvm_inject_trap {
     uint32_t insn_len;
     /* CR2 for page faults */
     uint64_aligned_t cr2;
+    /*
+     * Currently only used if vcpuid == ~0 (wildcard for any VCPU).
+     * In that case, injection data is set per-domain, and any VCPU
+     * running will inject the trap. For TRAP_page_fault, the CR3
+     * of the currently running process will have to match cr3.
+     */
+    uint64_aligned_t cr3;
 };
 typedef struct xen_hvm_inject_trap xen_hvm_inject_trap_t;
 DEFINE_XEN_GUEST_HANDLE(xen_hvm_inject_trap_t);
-- 
1.7.9.5


_______________________________________________
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®.