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

[Xen-changelog] [xen-unstable] vmx: Enable live-migration with EPT



# HG changeset patch
# User Keir Fraser <keir.fraser@xxxxxxxxxx>
# Date 1207818041 -3600
# Node ID 9153b99a7066b6b5098f61483e3147e81ad7258e
# Parent  1d3aaa6a8b870805e16dcf162223fb2edd9de26d
vmx: Enable live-migration with EPT

Signed-off-by: Xin Li <xin.b.li@xxxxxxxxx>
Signed-off-by: Jun Nakajima <jun.nakajima@xxxxxxxxx>
Signed-off-by: Xiaohui Xin <Xiaohui.xin@xxxxxxxxx>
Signed-off-by: Keir Fraser <keir.fraser@xxxxxxxxxx>
---
 xen/arch/x86/hvm/vmx/vmx.c        |   40 ++++++++++++++++--
 xen/arch/x86/mm/hap/hap.c         |    6 +-
 xen/arch/x86/mm/hap/p2m-ept.c     |   84 ++++++++++++++++++++++++++++++++++----
 xen/arch/x86/mm/p2m.c             |   16 ++++++-
 xen/include/asm-x86/hvm/vmx/vmx.h |   43 +++++++++++++++++++
 xen/include/asm-x86/p2m.h         |    4 +
 6 files changed, 178 insertions(+), 15 deletions(-)

diff -r 1d3aaa6a8b87 -r 9153b99a7066 xen/arch/x86/hvm/vmx/vmx.c
--- a/xen/arch/x86/hvm/vmx/vmx.c        Thu Apr 10 09:22:38 2008 +0100
+++ b/xen/arch/x86/hvm/vmx/vmx.c        Thu Apr 10 10:00:41 2008 +0100
@@ -2053,13 +2053,47 @@ static void vmx_wbinvd_intercept(void)
 
 static void ept_handle_violation(unsigned long qualification, paddr_t gpa)
 {
-    if ( unlikely(((qualification >> 7) & 0x3) != 0x3) )
-    {
-        domain_crash(current->domain);
+    unsigned long gla_validity = qualification & EPT_GLA_VALIDITY_MASK;
+    struct domain *d = current->domain;
+    unsigned long gfn = gpa >> PAGE_SHIFT;
+    mfn_t mfn;
+    p2m_type_t t;
+
+    if ( unlikely(qualification & EPT_GAW_VIOLATION) )
+    {
+        gdprintk(XENLOG_ERR, "EPT violation: guest physical address %"PRIpaddr
+                 " exceeded its width limit.\n", gpa);
+        goto crash;
+    }
+
+    if ( unlikely(gla_validity == EPT_GLA_VALIDITY_RSVD) ||
+         unlikely(gla_validity == EPT_GLA_VALIDITY_PDPTR_LOAD) )
+    {
+        gdprintk(XENLOG_ERR, "EPT violation: reserved bit or "
+                 "pdptr load violation.\n");
+        goto crash;
+    }
+
+    mfn = gfn_to_mfn(d, gfn, &t);
+    if ( p2m_is_ram(t) && paging_mode_log_dirty(d) )
+    {
+        paging_mark_dirty(d, mfn_x(mfn));
+        p2m_change_type(d, gfn, p2m_ram_logdirty, p2m_ram_rw);
+        flush_tlb_mask(d->domain_dirty_cpumask);
         return;
     }
 
+    /* This can only happen in log-dirty mode, writing back A/D bits. */
+    if ( unlikely(gla_validity == EPT_GLA_VALIDITY_GPT_WALK) )
+        goto crash;
+
+    ASSERT(gla_validity == EPT_GLA_VALIDITY_MATCH);
     handle_mmio();
+
+    return;
+
+ crash:
+    domain_crash(d);
 }
 
 static void vmx_failed_vmentry(unsigned int exit_reason,
diff -r 1d3aaa6a8b87 -r 9153b99a7066 xen/arch/x86/mm/hap/hap.c
--- a/xen/arch/x86/mm/hap/hap.c Thu Apr 10 09:22:38 2008 +0100
+++ b/xen/arch/x86/mm/hap/hap.c Thu Apr 10 10:00:41 2008 +0100
@@ -62,7 +62,7 @@ int hap_enable_log_dirty(struct domain *
     hap_unlock(d);
 
     /* set l1e entries of P2M table to be read-only. */
-    p2m_change_type_global(d, p2m_ram_rw, p2m_ram_logdirty);
+    p2m_change_entry_type_global(d, p2m_ram_rw, p2m_ram_logdirty);
     flush_tlb_mask(d->domain_dirty_cpumask);
     return 0;
 }
@@ -74,14 +74,14 @@ int hap_disable_log_dirty(struct domain 
     hap_unlock(d);
 
     /* set l1e entries of P2M table with normal mode */
-    p2m_change_type_global(d, p2m_ram_logdirty, p2m_ram_rw);
+    p2m_change_entry_type_global(d, p2m_ram_logdirty, p2m_ram_rw);
     return 0;
 }
 
 void hap_clean_dirty_bitmap(struct domain *d)
 {
     /* set l1e entries of P2M table to be read-only. */
-    p2m_change_type_global(d, p2m_ram_rw, p2m_ram_logdirty);
+    p2m_change_entry_type_global(d, p2m_ram_rw, p2m_ram_logdirty);
     flush_tlb_mask(d->domain_dirty_cpumask);
 }
 
diff -r 1d3aaa6a8b87 -r 9153b99a7066 xen/arch/x86/mm/hap/p2m-ept.c
--- a/xen/arch/x86/mm/hap/p2m-ept.c     Thu Apr 10 09:22:38 2008 +0100
+++ b/xen/arch/x86/mm/hap/p2m-ept.c     Thu Apr 10 10:00:41 2008 +0100
@@ -26,6 +26,26 @@
 #include <asm/hvm/vmx/vmx.h>
 #include <xen/iommu.h>
 
+static void ept_p2m_type_to_flags(ept_entry_t *entry, p2m_type_t type)
+{
+    switch(type)
+    {
+        case p2m_invalid:
+        case p2m_mmio_dm:
+        default:
+            return;
+        case p2m_ram_rw:
+        case p2m_mmio_direct:
+             entry->r = entry->w = entry->x = 1;
+            return;
+        case p2m_ram_logdirty:
+        case p2m_ram_ro:
+             entry->r = entry->x = 1;
+             entry->w = 0;
+            return;
+    }
+}
+
 static int ept_next_level(struct domain *d, bool_t read_only,
                           ept_entry_t **table, unsigned long *gfn_remainder,
                           u32 shift)
@@ -104,6 +124,7 @@ ept_set_entry(struct domain *d, unsigned
         ept_entry->avail2 = 0;
         /* last step */
         ept_entry->r = ept_entry->w = ept_entry->x = 1;
+        ept_p2m_type_to_flags(ept_entry, p2mt);
     }
     else
         ept_entry->epte = 0;
@@ -150,13 +171,10 @@ static mfn_t ept_get_entry(struct domain
     index = gfn_remainder;
     ept_entry = table + index;
 
-    if ( (ept_entry->epte & 0x7) == 0x7 )
-    {
-        if ( ept_entry->avail1 != p2m_invalid )
-        {
-            *t = ept_entry->avail1;
-            mfn = _mfn(ept_entry->mfn);
-        }
+    if ( ept_entry->avail1 != p2m_invalid )
+    {
+        *t = ept_entry->avail1;
+        mfn = _mfn(ept_entry->mfn);
     }
 
  out:
@@ -169,11 +187,63 @@ static mfn_t ept_get_entry_current(unsig
     return ept_get_entry(current->domain, gfn, t);
 }
 
+/* Walk the whole p2m table, changing any entries of the old type
+ * to the new type.  This is used in hardware-assisted paging to
+ * quickly enable or diable log-dirty tracking */
+
+static void ept_change_entry_type_global(struct domain *d,
+                                            p2m_type_t ot, p2m_type_t nt)
+{
+    ept_entry_t *l4e, *l3e, *l2e, *l1e;
+    int i4, i3, i2, i1;
+
+    if ( pagetable_get_pfn(d->arch.phys_table) == 0 )
+        return;
+
+    BUG_ON(EPT_DEFAULT_GAW != 3);
+
+    l4e = map_domain_page(mfn_x(pagetable_get_mfn(d->arch.phys_table)));
+    for (i4 = 0; i4 < EPT_PAGETABLE_ENTRIES; i4++ )
+    {
+        if ( !l4e[i4].epte || l4e[i4].sp_avail )
+            continue;
+        l3e = map_domain_page(l4e[i4].mfn);
+        for ( i3 = 0; i3 < EPT_PAGETABLE_ENTRIES; i3++ )
+        {
+            if ( !l3e[i3].epte || l3e[i3].sp_avail )
+                continue;
+            l2e = map_domain_page(l3e[i3].mfn);
+            for ( i2 = 0; i2 < EPT_PAGETABLE_ENTRIES; i2++ )
+            {
+                if ( !l2e[i2].epte || l2e[i2].sp_avail )
+                    continue;
+                l1e = map_domain_page(l2e[i2].mfn);
+                for ( i1  = 0; i1 < EPT_PAGETABLE_ENTRIES; i1++ )
+                {
+                    if ( !l1e[i1].epte )
+                        continue;
+                    if ( l1e[i1].avail1 != ot )
+                        continue;
+                    l1e[i1].avail1 = nt;
+                    ept_p2m_type_to_flags(l1e+i1, nt);
+                }
+                unmap_domain_page(l1e);
+            }
+            unmap_domain_page(l2e);
+        }
+        unmap_domain_page(l3e);
+    }
+    unmap_domain_page(l4e);
+
+    ept_sync_domain(d);
+}
+
 void ept_p2m_init(struct domain *d)
 {
     d->arch.p2m->set_entry = ept_set_entry;
     d->arch.p2m->get_entry = ept_get_entry;
     d->arch.p2m->get_entry_current = ept_get_entry_current;
+    d->arch.p2m->change_entry_type_global = ept_change_entry_type_global;
 }
 
 /*
diff -r 1d3aaa6a8b87 -r 9153b99a7066 xen/arch/x86/mm/p2m.c
--- a/xen/arch/x86/mm/p2m.c     Thu Apr 10 09:22:38 2008 +0100
+++ b/xen/arch/x86/mm/p2m.c     Thu Apr 10 10:00:41 2008 +0100
@@ -71,6 +71,8 @@
         spin_unlock(&(_p2m)->lock);                     \
     } while (0)
 
+#define p2m_locked_by_me(_p2m)                            \
+    (current->processor == (_p2m)->locker)
 
 /* Printouts */
 #define P2M_PRINTK(_f, _a...)                                \
@@ -418,12 +420,23 @@ int p2m_init(struct domain *d)
     p2m->set_entry = p2m_set_entry;
     p2m->get_entry = p2m_gfn_to_mfn;
     p2m->get_entry_current = p2m_gfn_to_mfn_current;
+    p2m->change_entry_type_global = p2m_change_type_global;
 
     if ( is_hvm_domain(d) && d->arch.hvm_domain.hap_enabled &&
          (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) )
         ept_p2m_init(d);
 
     return 0;
+}
+
+void p2m_change_entry_type_global(struct domain *d,
+                                  p2m_type_t ot, p2m_type_t nt)
+{
+    struct p2m_domain *p2m = d->arch.p2m;
+
+    p2m_lock(p2m);
+    p2m->change_entry_type_global(d, ot, nt);
+    p2m_unlock(p2m);
 }
 
 static inline
@@ -880,7 +893,7 @@ void p2m_change_type_global(struct domai
     if ( pagetable_get_pfn(d->arch.phys_table) == 0 )
         return;
 
-    p2m_lock(d->arch.p2m);
+    ASSERT(p2m_locked_by_me(d->arch.p2m));
 
 #if CONFIG_PAGING_LEVELS == 4
     l4e = map_domain_page(mfn_x(pagetable_get_mfn(d->arch.phys_table)));
@@ -952,7 +965,6 @@ void p2m_change_type_global(struct domai
     unmap_domain_page(l2e);
 #endif
 
-    p2m_unlock(d->arch.p2m);
 }
 
 /* Modify the p2m type of a single gfn from ot to nt, returning the 
diff -r 1d3aaa6a8b87 -r 9153b99a7066 xen/include/asm-x86/hvm/vmx/vmx.h
--- a/xen/include/asm-x86/hvm/vmx/vmx.h Thu Apr 10 09:22:38 2008 +0100
+++ b/xen/include/asm-x86/hvm/vmx/vmx.h Thu Apr 10 10:00:41 2008 +0100
@@ -344,4 +344,47 @@ void vmx_inject_nmi(struct vcpu *v);
 
 void ept_p2m_init(struct domain *d);
 
+/* EPT violation qualifications definitions */
+/* bit offset 0 in exit qualification */
+#define _EPT_READ_VIOLATION         0
+#define EPT_READ_VIOLATION          (1UL<<_EPT_READ_VIOLATION)
+/* bit offset 1 in exit qualification */
+#define _EPT_WRITE_VIOLATION        1
+#define EPT_WRITE_VIOLATION         (1UL<<_EPT_WRITE_VIOLATION)
+/* bit offset 2 in exit qualification */
+#define _EPT_EXEC_VIOLATION         2
+#define EPT_EXEC_VIOLATION          (1UL<<_EPT_EXEC_VIOLATION)
+
+/* bit offset 3 in exit qualification */
+#define _EPT_EFFECTIVE_READ         3
+#define EPT_EFFECTIVE_READ          (1UL<<_EPT_EFFECTIVE_READ)
+/* bit offset 4 in exit qualification */
+#define _EPT_EFFECTIVE_WRITE        4
+#define EPT_EFFECTIVE_WRITE         (1UL<<_EPT_EFFECTIVE_WRITE)
+/* bit offset 5 in exit qualification */
+#define _EPT_EFFECTIVE_EXEC         5
+#define EPT_EFFECTIVE_EXEC          (1UL<<_EPT_EFFECTIVE_EXEC)
+
+/* bit offset 6 in exit qualification */
+#define _EPT_GAW_VIOLATION          6
+#define EPT_GAW_VIOLATION           (1UL<<_EPT_GAW_VIOLATION)
+
+/* bits offset 7 & 8 in exit qualification */
+#define _EPT_GLA_VALIDITY           7
+#define EPT_GLA_VALIDITY_MASK       (3UL<<_EPT_GLA_VALIDITY)
+/* gla != gpa, when load PDPTR */
+#define EPT_GLA_VALIDITY_PDPTR_LOAD (0UL<<_EPT_GLA_VALIDITY)
+/* gla != gpa, during guest page table walking */
+#define EPT_GLA_VALIDITY_GPT_WALK   (1UL<<_EPT_GLA_VALIDITY)
+/* reserved */
+#define EPT_GLA_VALIDITY_RSVD       (2UL<<_EPT_GLA_VALIDITY)
+/* gla == gpa, normal case */
+#define EPT_GLA_VALIDITY_MATCH      (3UL<<_EPT_GLA_VALIDITY)
+
+#define EPT_EFFECTIVE_MASK          (EPT_EFFECTIVE_READ  |  \
+                                     EPT_EFFECTIVE_WRITE |  \
+                                     EPT_EFFECTIVE_EXEC)
+
+#define EPT_PAGETABLE_ENTRIES       512
+
 #endif /* __ASM_X86_HVM_VMX_VMX_H__ */
diff -r 1d3aaa6a8b87 -r 9153b99a7066 xen/include/asm-x86/p2m.h
--- a/xen/include/asm-x86/p2m.h Thu Apr 10 09:22:38 2008 +0100
+++ b/xen/include/asm-x86/p2m.h Thu Apr 10 10:00:41 2008 +0100
@@ -107,6 +107,9 @@ struct p2m_domain {
                                        p2m_type_t *p2mt);
     mfn_t              (*get_entry_current)(unsigned long gfn,
                                             p2m_type_t *p2mt);
+    void               (*change_entry_type_global)(struct domain *d,
+                                                   p2m_type_t ot,
+                                                   p2m_type_t nt);
 
     /* Highest guest frame that's ever been mapped in the p2m */
     unsigned long max_mapped_pfn;
@@ -218,6 +221,7 @@ void guest_physmap_remove_page(struct do
 
 /* Change types across all p2m entries in a domain */
 void p2m_change_type_global(struct domain *d, p2m_type_t ot, p2m_type_t nt);
+void p2m_change_entry_type_global(struct domain *d, p2m_type_t ot, p2m_type_t 
nt);
 
 /* Compare-exchange the type of a single p2m entry */
 p2m_type_t p2m_change_type(struct domain *d, unsigned long gfn,

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