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

[Xen-devel] [PATCH RFC 7/9] xen: Handle resumed instruction based on previous mem_event reply


  • To: xen-devel@xxxxxxxxxxxxx
  • From: Razvan Cojocaru <rcojocaru@xxxxxxxxxxxxxxx>
  • Date: Wed, 2 Jul 2014 16:33:59 +0300
  • Cc: tim@xxxxxxx, Razvan Cojocaru <rcojocaru@xxxxxxxxxxxxxxx>
  • Comment: DomainKeys? See http://domainkeys.sourceforge.net/
  • Delivery-date: Wed, 02 Jul 2014 13:34:30 +0000
  • Domainkey-signature: a=rsa-sha1; q=dns; c=nofws; s=default; d=bitdefender.com; b=NHRH2o6X2ZJ37Rpzco69FcL2WsGW2RmJ07w7cm4kiYEBfj2mQZDlhqHPpgvbpoSA8WKbA1WoFwMoiodFJZRJfCPnKTX4YlO2WJ80Lm9tQZwEFWqQEkHsuGtnAd7ye7358Hb9QOp5HwwbmRhw19P2WnoFWRl89JwPled9rxHZiTpHNN7OLmE5TIy9aQZIIcfuYllHuSTPp9u1r8zN+GpziEtgWVAXZ5jE5vV+ySPyeK1y51E7guMxu1Zwk89XNCr8kyouTSXrcaIH3V4Z6YnRFh47Lhpaw3WRRKmo0Yh6VmV3m3JklKYN0eJn7v+3Ql/LLmo8joC8Gs6JmrJJupTzew==; h=Received:Received:Received:Received:From:To:Cc:Subject:Date:Message-Id:X-Mailer:In-Reply-To:References:X-BitDefender-Scanner:X-BitDefender-Spam:X-BitDefender-SpamStamp:X-BitDefender-CF-Stamp;
  • List-id: Xen developer discussion <xen-devel.lists.xen.org>

In a scenario where a page fault that triggered a mem_event occured,
p2m_mem_access_check() will now be able to either 1) emulate the
current instruction, 2) skip the current instruction, or 3) emulate
it, but don't allow it to perform any writes. Since some SSE2
instructions are problematic to emulate (Firefox uses some),
support for setting the A and D (accessed and dirty) bits has been
added (please see p2m_set_ad_bits()).

Signed-off-by: Razvan Cojocaru <rcojocaru@xxxxxxxxxxxxxxx>
---
 xen/arch/x86/domain.c          |    5 ++
 xen/arch/x86/hvm/emulate.c     |   10 ++++
 xen/arch/x86/mm/p2m.c          |  123 ++++++++++++++++++++++++++++++++++++++++
 xen/common/domain.c            |    3 +
 xen/include/asm-x86/domain.h   |    9 +++
 xen/include/public/mem_event.h |   14 +++--
 xen/include/xen/sched.h        |    5 ++
 7 files changed, 164 insertions(+), 5 deletions(-)

diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c
index e896210..5cd283b 100644
--- a/xen/arch/x86/domain.c
+++ b/xen/arch/x86/domain.c
@@ -407,6 +407,11 @@ int vcpu_initialise(struct vcpu *v)
 
     v->arch.flags = TF_kernel_mode;
 
+    /* By default, do not emulate */
+    v->arch.mem_event.emulate_flags = 0;
+    v->arch.mem_event.gpa = 0;
+    v->arch.mem_event.eip = 0;
+
     rc = mapcache_vcpu_init(v);
     if ( rc )
         return rc;
diff --git a/xen/arch/x86/hvm/emulate.c b/xen/arch/x86/hvm/emulate.c
index 1dc8c67..5e7113d 100644
--- a/xen/arch/x86/hvm/emulate.c
+++ b/xen/arch/x86/hvm/emulate.c
@@ -1372,6 +1372,16 @@ void hvm_emulate_one_full(bool_t nowrite)
     switch ( rc )
     {
     case X86EMUL_UNHANDLEABLE:
+        printk("Emulation failed @ %04x:%lx: "
+               "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+               hvmemul_get_seg_reg(x86_seg_cs, ctx)->sel,
+               ctx->insn_buf_eip,
+               ctx->insn_buf[0], ctx->insn_buf[1],
+               ctx->insn_buf[2], ctx->insn_buf[3],
+               ctx->insn_buf[4], ctx->insn_buf[5],
+               ctx->insn_buf[6], ctx->insn_buf[7],
+               ctx->insn_buf[8], ctx->insn_buf[9]);
+
         hvm_inject_hw_exception(TRAP_invalid_op, HVM_DELIVER_NO_ERROR_CODE);
         break;
     case X86EMUL_EXCEPTION:
diff --git a/xen/arch/x86/mm/p2m.c b/xen/arch/x86/mm/p2m.c
index 93252d9..4dd3f1b 100644
--- a/xen/arch/x86/mm/p2m.c
+++ b/xen/arch/x86/mm/p2m.c
@@ -1372,12 +1372,35 @@ static inline void 
p2m_mem_event_fill_regs(mem_event_request_t *req)
     req->regs.guest_x86_mode = hvm_guest_x86_mode(current);
 }
 
+static void p2m_set_ad_bits(struct vcpu *v, struct p2m_domain *p2m,
+                            paddr_t ga)
+{
+    struct hvm_hw_cpu ctxt;
+    uint32_t pfec = 0;
+    const struct paging_mode *pg_mode = v->arch.paging.mode;
+
+    hvm_funcs.save_cpu_ctxt(v, &ctxt);
+
+    if ( guest_cpu_user_regs()->eip == v->sse_pg_dirty.eip
+         && ga == v->sse_pg_dirty.gla )
+    {
+        pfec = 2;
+        pg_mode->p2m_ga_to_gfn(v, p2m, ctxt.cr3, ga, &pfec, NULL);
+    }
+    else
+        pg_mode->p2m_ga_to_gfn(v, p2m, ctxt.cr3, ga, &pfec, NULL);
+
+    v->sse_pg_dirty.eip = guest_cpu_user_regs()->eip;
+    v->sse_pg_dirty.gla = ga;
+}
+
 bool_t p2m_mem_access_check(paddr_t gpa, bool_t gla_valid, unsigned long gla, 
                           bool_t access_r, bool_t access_w, bool_t access_x,
                           mem_event_request_t **req_ptr)
 {
     struct vcpu *v = current;
     unsigned long gfn = gpa >> PAGE_SHIFT;
+    unsigned long exit_qualification;
     struct domain *d = v->domain;    
     struct p2m_domain* p2m = p2m_get_hostp2m(d);
     mfn_t mfn;
@@ -1385,6 +1408,9 @@ bool_t p2m_mem_access_check(paddr_t gpa, bool_t 
gla_valid, unsigned long gla,
     p2m_access_t p2ma;
     mem_event_request_t *req;
     int rc;
+    unsigned long eip = 0;
+
+    __vmread(EXIT_QUALIFICATION, &exit_qualification);
 
     /* First, handle rx2rw conversion automatically.
      * These calls to p2m->set_entry() must succeed: we have the gfn
@@ -1437,6 +1463,48 @@ bool_t p2m_mem_access_check(paddr_t gpa, bool_t 
gla_valid, unsigned long gla,
             return 1;
         }
     }
+    else
+    {
+        /* There's a mem_event listener */
+        if ( (exit_qualification & EPT_GLA_FAULT) == 0 ) /* don't send a 
mem_event */
+        {
+            v->arch.mem_event.emulate_flags = 0;
+            v->arch.mem_event.gpa = 0;
+            v->arch.mem_event.eip = 0;
+
+            p2m_set_ad_bits(v, p2m, gla);
+            return 1;
+        }
+    }
+
+    eip = guest_cpu_user_regs()->eip;
+
+    if ( v->arch.mem_event.gpa != gpa || v->arch.mem_event.eip != eip )
+    {
+        v->arch.mem_event.emulate_flags = 0;
+        v->arch.mem_event.gpa = gpa;
+        v->arch.mem_event.eip = eip;
+    }
+
+    if ( v->arch.mem_event.emulate_flags )
+    {
+        if ( v->arch.mem_event.emulate_flags & MEM_EVENT_FLAG_SKIP_INSTR )
+        {
+            struct hvm_emulate_ctxt ctx[1] = {};
+            int length;
+
+            hvm_emulate_prepare(ctx, guest_cpu_user_regs());
+            length = hvm_get_insn_length(ctx);
+            guest_cpu_user_regs()->eip += length;
+        }
+        else if ( v->arch.mem_event.emulate_flags & 
MEM_EVENT_FLAG_EMULATE_NOWRITE )
+            hvm_emulate_one_full(1);
+        else
+            hvm_emulate_one_full(0);
+
+        v->arch.mem_event.emulate_flags = 0;
+        return 1;
+    }
 
     *req_ptr = NULL;
     req = xzalloc(mem_event_request_t);
@@ -1481,6 +1549,61 @@ void p2m_mem_access_resume(struct domain *d)
     {
         if ( rsp.flags & MEM_EVENT_FLAG_DUMMY )
             continue;
+
+        /* Mark vcpu for skipping one instruction upon rescheduling */
+        if ( rsp.flags & MEM_EVENT_FLAG_EMULATE )
+        {
+            xenmem_access_t access;
+            int violation = 1;
+
+            d->vcpu[rsp.vcpu_id]->arch.mem_event.emulate_flags = 0;
+
+            if ( p2m_get_mem_access(d, rsp.gfn, &access) == 0 )
+            {
+                violation = 0;
+
+                switch (access)
+                {
+                case XENMEM_access_n:
+                case XENMEM_access_n2rwx:
+                default:
+                    violation = rsp.access_r || rsp.access_w || rsp.access_x;
+                    break;
+
+                case XENMEM_access_r:
+                    violation = rsp.access_w || rsp.access_x;
+                    break;
+
+                case XENMEM_access_w:
+                    violation = rsp.access_r || rsp.access_x;
+                    break;
+
+                case XENMEM_access_x:
+                    violation = rsp.access_r || rsp.access_w;
+                    break;
+
+                case XENMEM_access_rx:
+                case XENMEM_access_rx2rw:
+                    violation = rsp.access_w;
+                    break;
+
+                case XENMEM_access_wx:
+                    violation = rsp.access_r;
+                    break;
+
+                case XENMEM_access_rw:
+                    violation = rsp.access_x;
+                    break;
+
+                case XENMEM_access_rwx:
+                    break;
+                }
+            }
+
+            if ( violation )
+                d->vcpu[rsp.vcpu_id]->arch.mem_event.emulate_flags = rsp.flags;
+        }
+
         /* Unpause domain */
         if ( rsp.flags & MEM_EVENT_FLAG_VCPU_PAUSED )
             vcpu_unpause(d->vcpu[rsp.vcpu_id]);
diff --git a/xen/common/domain.c b/xen/common/domain.c
index 9402924..44d2919 100644
--- a/xen/common/domain.c
+++ b/xen/common/domain.c
@@ -121,6 +121,9 @@ struct vcpu *alloc_vcpu(
     v->domain = d;
     v->vcpu_id = vcpu_id;
 
+    v->sse_pg_dirty.eip = 0;
+    v->sse_pg_dirty.gla = 0;
+
     spin_lock_init(&v->virq_lock);
 
     tasklet_init(&v->continue_hypercall_tasklet, NULL, 0);
diff --git a/xen/include/asm-x86/domain.h b/xen/include/asm-x86/domain.h
index abf55fb..0fa4d3d 100644
--- a/xen/include/asm-x86/domain.h
+++ b/xen/include/asm-x86/domain.h
@@ -446,6 +446,15 @@ struct arch_vcpu
 
     /* A secondary copy of the vcpu time info. */
     XEN_GUEST_HANDLE(vcpu_time_info_t) time_info_guest;
+
+    /* Should we emulate the next matching instruction on VCPU resume
+     * after a mem_event? */
+    struct {
+        uint32_t emulate_flags;
+        unsigned long gpa;
+        unsigned long eip;
+    } mem_event;
+
 } __cacheline_aligned;
 
 /* Shorthands to improve code legibility. */
diff --git a/xen/include/public/mem_event.h b/xen/include/public/mem_event.h
index 5fa2858..8880a74 100644
--- a/xen/include/public/mem_event.h
+++ b/xen/include/public/mem_event.h
@@ -31,11 +31,15 @@
 #include "io/ring.h"
 
 /* Memory event flags */
-#define MEM_EVENT_FLAG_VCPU_PAUSED  (1 << 0)
-#define MEM_EVENT_FLAG_DROP_PAGE    (1 << 1)
-#define MEM_EVENT_FLAG_EVICT_FAIL   (1 << 2)
-#define MEM_EVENT_FLAG_FOREIGN      (1 << 3)
-#define MEM_EVENT_FLAG_DUMMY        (1 << 4)
+#define MEM_EVENT_FLAG_VCPU_PAUSED     (1 << 0)
+#define MEM_EVENT_FLAG_DROP_PAGE       (1 << 1)
+#define MEM_EVENT_FLAG_EVICT_FAIL      (1 << 2)
+#define MEM_EVENT_FLAG_FOREIGN         (1 << 3)
+#define MEM_EVENT_FLAG_DUMMY           (1 << 4)
+#define MEM_EVENT_FLAG_EMULATE         (1 << 5)
+#define MEM_EVENT_FLAG_EMULATE_NOWRITE (1 << 6)
+#define MEM_EVENT_FLAG_SKIP_INSTR      (1 << 7)
+
 
 /* Reasons for the memory event request */
 #define MEM_EVENT_REASON_UNKNOWN     0    /* typical reason */
diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h
index fe78a9a..567a124 100644
--- a/xen/include/xen/sched.h
+++ b/xen/include/xen/sched.h
@@ -245,6 +245,11 @@ struct vcpu
     struct evtchn_fifo_vcpu *evtchn_fifo;
 
     struct arch_vcpu arch;
+
+    struct {
+           unsigned long eip;
+           unsigned long gla;
+    } sse_pg_dirty;
 };
 
 /* Per-domain lock can be recursively acquired in fault handlers. */
-- 
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®.