WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-devel

[Xen-devel] [PATCH] hvm emul: fix cmpxchg emulation to use an atomic ope

To: <xen-devel@xxxxxxxxxxxxxxxxxxx>
Subject: [Xen-devel] [PATCH] hvm emul: fix cmpxchg emulation to use an atomic operation
From: Patrick Colp <Patrick.Colp@xxxxxxxxxx>
Date: Thu, 6 Aug 2009 11:40:24 +0100
Delivery-date: Thu, 06 Aug 2009 03:42:56 -0700
Envelope-to: www-data@xxxxxxxxxxxxxxxxxxx
List-help: <mailto:xen-devel-request@lists.xensource.com?subject=help>
List-id: Xen developer discussion <xen-devel.lists.xensource.com>
List-post: <mailto:xen-devel@lists.xensource.com>
List-subscribe: <http://lists.xensource.com/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=subscribe>
List-unsubscribe: <http://lists.xensource.com/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=unsubscribe>
Sender: xen-devel-bounces@xxxxxxxxxxxxxxxxxxx
# HG changeset patch
# User Patrick Colp <Patrick.Colp@xxxxxxxxxx>
# Date 1249555177 -3600
# Node ID 684c8fc69d658d058246eb9edfc8eba187ae6f2c
# Parent  68e8b8379244e293c55875e7dc3692fc81d3d212
hvm emul: fix cmpxchg emulation to use an atomic operation.

Currently HVM cmpxchg emulation is done by doing a normal emulated write,
which is not atomic. This patch changes it to use a cmpxchg operation
instead, which is atomic.

Signed-off-by: Patrick Colp <Patrick.Colp@xxxxxxxxxx>

diff -r 68e8b8379244 -r 684c8fc69d65 xen/arch/x86/hvm/emulate.c
--- a/xen/arch/x86/hvm/emulate.c        Sun Aug 02 13:43:15 2009 +0100
+++ b/xen/arch/x86/hvm/emulate.c        Thu Aug 06 11:39:37 2009 +0100
@@ -520,6 +520,183 @@
     return X86EMUL_OKAY;
 }
 
+/* Translate a VA to an MFN, injecting a page-fault if we fail */
+#define BAD_GVA_TO_GFN (~0UL)
+#define BAD_GFN_TO_MFN (~1UL)
+#define READONLY_GFN   (~2UL)
+static mfn_t emulate_gva_to_mfn(
+    struct vcpu *v,
+    unsigned long vaddr)
+{
+    unsigned long gfn;
+    mfn_t mfn;
+    p2m_type_t p2mt;
+    uint32_t pfec = PFEC_page_present | PFEC_write_access;
+
+    /* Translate the VA to a GFN */
+    gfn = paging_gva_to_gfn(v, vaddr, &pfec);
+    if ( gfn == INVALID_GFN )
+    {
+        hvm_inject_exception(TRAP_page_fault, pfec, vaddr);
+        return _mfn(BAD_GVA_TO_GFN);
+    }
+
+    /* Translate the GFN to an MFN */
+    mfn = gfn_to_mfn(v->domain, gfn, &p2mt);
+        
+    if ( p2mt == p2m_ram_ro )
+        return _mfn(READONLY_GFN);
+    if ( !p2m_is_ram(p2mt) )
+        return _mfn(BAD_GFN_TO_MFN);
+
+    ASSERT(mfn_valid(mfn_x(mfn)));
+
+    return mfn;
+}
+
+/* Check that the user is allowed to perform this write. 
+ * Returns a mapped pointer to write to, or NULL for error. */
+#define MAPPING_UNHANDLEABLE ((void *)(unsigned long)X86EMUL_UNHANDLEABLE)
+#define MAPPING_EXCEPTION    ((void *)(unsigned long)X86EMUL_EXCEPTION)
+#define MAPPING_SILENT_FAIL  ((void *)(unsigned long)X86EMUL_OKAY)
+#define emulate_map_dest_failed(rc) ((unsigned long)(rc) <= 3)
+static void *emulate_map_dest(
+    struct vcpu *v,
+    unsigned long vaddr,
+    u32 bytes)
+{
+    unsigned long offset;
+    void *map = NULL;
+    mfn_t mfn1;
+    mfn_t mfn2;
+
+    mfn1 = emulate_gva_to_mfn(v, vaddr);
+    if ( !mfn_valid(mfn_x(mfn1)) )
+        return ((mfn_x(mfn1) == BAD_GVA_TO_GFN) ?
+                MAPPING_EXCEPTION :
+                (mfn_x(mfn1) == READONLY_GFN) ?
+                MAPPING_SILENT_FAIL : MAPPING_UNHANDLEABLE);
+
+    if ( likely(((vaddr + bytes - 1) & PAGE_MASK) == (vaddr & PAGE_MASK)) )
+    {
+        /* Whole write fits on a single page */
+        mfn2 = _mfn(INVALID_MFN);
+        map = map_domain_page(mfn_x(mfn1)) + (vaddr & ~PAGE_MASK);
+    }
+    else 
+    {
+        /* Cross-page emulated writes are only supported for HVM guests; 
+         * PV guests ought to know better */
+        mfn2 = emulate_gva_to_mfn(v, (vaddr + bytes - 1) & PAGE_MASK);
+        if ( !mfn_valid(mfn_x(mfn2)) ) 
+            return ((mfn_x(mfn2) == BAD_GVA_TO_GFN) ?
+                    MAPPING_EXCEPTION :
+                    (mfn_x(mfn2) == READONLY_GFN) ?
+                    MAPPING_SILENT_FAIL : MAPPING_UNHANDLEABLE);
+
+        /* Hack: we map the pages into the vcpu's LDT space, since we
+         * know that we're not going to need the LDT for HVM guests, 
+         * and only HVM guests are allowed unaligned writes. */
+        map = (void *)LDT_VIRT_START(v);
+        offset = l1_linear_offset((unsigned long) map);
+        l1e_write(&__linear_l1_table[offset],
+                  l1e_from_pfn(mfn_x(mfn1), __PAGE_HYPERVISOR));
+        l1e_write(&__linear_l1_table[offset + 1],
+                  l1e_from_pfn(mfn_x(mfn2), __PAGE_HYPERVISOR));
+        flush_tlb_local();
+        map += (vaddr & ~PAGE_MASK);
+    }
+
+    return map;
+}
+
+/* Tidy up after the emulated write: undo the mapping */
+static void emulate_unmap_dest(
+    struct vcpu *v,
+    void *addr,
+    u32 bytes,
+    unsigned long vaddr)
+{
+    if ( likely(((vaddr + bytes - 1) & PAGE_MASK) == (vaddr & PAGE_MASK)) )
+        unmap_domain_page(addr);
+    else
+    {
+        unsigned long offset;
+        /* Undo the hacky two-frame contiguous map. */
+        ASSERT(((unsigned long) addr & PAGE_MASK) == LDT_VIRT_START(v));
+        offset = l1_linear_offset((unsigned long) addr);
+        l1e_write(&__linear_l1_table[offset], l1e_empty());
+        l1e_write(&__linear_l1_table[offset + 1], l1e_empty());
+        flush_tlb_all();
+    }
+}
+
+static int hvm_x86_emulate_cmpxchg(
+    struct vcpu *v,
+    unsigned long vaddr,
+    unsigned long old,
+    unsigned long new,
+    unsigned int bytes)
+{
+    void *addr;
+    unsigned long prev;
+    int rc = X86EMUL_OKAY;
+
+    addr = emulate_map_dest(v, vaddr, bytes);
+    if ( emulate_map_dest_failed(addr) )
+        return (long)addr;
+
+    switch ( bytes )
+    {
+    case 1: prev = cmpxchg(((u8 *)addr), old, new);  break;
+    case 2: prev = cmpxchg(((u16 *)addr), old, new); break;
+    case 4: prev = cmpxchg(((u32 *)addr), old, new); break;
+    case 8: prev = cmpxchg(((u64 *)addr), old, new); break;
+    default:
+        gdprintk(XENLOG_ERR, "%s: cmpxchg of size %i is not supported\n",
+                 __func__, bytes);
+        prev = ~old;
+    }
+
+    if ( prev != old ) 
+        rc = X86EMUL_CMPXCHG_FAILED;
+
+    emulate_unmap_dest(v, addr, bytes, vaddr);
+
+    return rc;
+}
+
+#ifdef __i386__
+static int hvm_x86_emulate_cmpxchg8b(
+    struct vcpu *v,
+    unsigned long vaddr,
+    unsigned long old_lo,
+    unsigned long old_hi,
+    unsigned long new_lo,
+    unsigned long new_hi)
+{
+    void *addr;
+    u64 old, new, prev;
+    int rc = X86EMUL_OKAY;
+
+    addr = emulate_map_dest(v, vaddr, 8);
+    if ( emulate_map_dest_failed(addr) )
+        return (long)addr;
+
+    old = (((u64) old_hi) << 32) | (u64) old_lo;
+    new = (((u64) new_hi) << 32) | (u64) new_lo;
+
+    prev = cmpxchg(((u64 *)addr), old, new);
+
+    if ( prev != old )
+        rc = X86EMUL_CMPXCHG_FAILED;
+
+    emulate_unmap_dest(v, addr, 8, sh_ctxt);
+
+    return rc;
+}
+#endif
+
 static int hvmemul_cmpxchg(
     enum x86_segment seg,
     unsigned long offset,
@@ -528,8 +705,32 @@
     unsigned int bytes,
     struct x86_emulate_ctxt *ctxt)
 {
-    /* Fix this in case the guest is really relying on r-m-w atomicity. */
-    return hvmemul_write(seg, offset, p_new, bytes, ctxt);
+    struct hvm_emulate_ctxt *hvmemul_ctxt =
+        container_of(ctxt, struct hvm_emulate_ctxt, ctxt);
+    struct vcpu *v = current;
+    unsigned long addr, old[2], new[2];
+    unsigned long reps = 1;
+    int rc;
+
+    rc = hvmemul_virtual_to_linear(seg, offset, bytes, &reps, hvm_access_write,
+                                   hvmemul_ctxt, &addr);
+    if ( rc != X86EMUL_OKAY )
+        return rc;
+
+    old[0] = new[0] = 0;
+    memcpy(old, p_old, bytes);
+    memcpy(new, p_new, bytes);
+
+    if ( bytes <= sizeof(long) )
+        return hvm_x86_emulate_cmpxchg(v, addr, old[0], new[0], bytes);
+
+#ifdef __i386__
+    if ( bytes == 8 )
+        return hvm_x86_emulate_cmpxchg8b(v, addr, old[0], old[1],
+                                         new[0], new[1]);
+#endif
+
+    return X86EMUL_UNHANDLEABLE;
 }
 
 static int hvmemul_rep_ins(

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

<Prev in Thread] Current Thread [Next in Thread>