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

[Xen-devel] [PATCH RFC 08/14] xen: vmx: Added setup spp page structure.



From: Zhang Yi Z <yi.z.zhang@xxxxxxxxxxxxxxx>

The hardware uses the guest-physical address and bits 11:7 of the
address accessed to lookup the SPPT to fetch a write permission bit for
the 128 byte wide sub-page region being accessed within the 4K
guest-physical page. If the sub-page region write permission bit is set,
the write is allowed; otherwise the write is disallowed and results in
an EPT violation.

Guest-physical pages mapped via leaf EPT-paging-structures for which the
accumulated write-access bit and the SPP bits are both clear (0)
generate
EPT violations on memory writes accesses. Guest-physical pages mapped
via EPT-paging-structure for which the accumulated write-access bit is
set (1) allow writes, effectively ignoring the SPP bit on the leaf
EPT-paging structure.

Software will setup the spp page table level4,3,2 as well as EPT page
structure, and fill the level1 via the 32 bit bitmap per a single 4K
page.
Now it could be divided to 32 x 128 sub-pages.

Signed-off-by: Zhang Yi Z <yi.z.zhang@xxxxxxxxxxxxxxx>
---
 xen/arch/x86/mm/mem_access.c      | 35 +++++++++++++++
 xen/arch/x86/mm/p2m-ept.c         | 94 +++++++++++++++++++++++++++++++++++++++
 xen/include/asm-x86/hvm/vmx/vmx.h | 10 +++++
 xen/include/asm-x86/p2m.h         |  3 ++
 4 files changed, 142 insertions(+)

diff --git a/xen/arch/x86/mm/mem_access.c b/xen/arch/x86/mm/mem_access.c
index a471c74..1b97469 100644
--- a/xen/arch/x86/mm/mem_access.c
+++ b/xen/arch/x86/mm/mem_access.c
@@ -490,6 +490,41 @@ unlock_exit:
     return rc;
 }
 
+static u64 format_spp_spte(u32 spp_wp_bitmap)
+{
+       u64 new_spte = 0;
+       int i = 0;
+
+       /*
+        * One 4K page contains 32 sub-pages, in SPP table L4E, old bits
+        * are reserved, so we need to transfer u32 subpage write
+        * protect bitmap to u64 SPP L4E format.
+        */
+       while ( i < 32 ) {
+               if ( spp_wp_bitmap & (1ULL << i) )
+                       new_spte |= 1ULL << (i * 2);
+
+               i++;
+       }
+
+       return new_spte;
+}
+
+int p2m_set_spp_page_st(struct domain *d, gfn_t gfn, uint32_t access_map)
+{
+    struct p2m_domain *p2m = p2m_get_hostp2m(d);
+    u64 access = format_spp_spte(access_map);
+    unsigned long gfn_l = gfn_x(gfn);
+    int ret = -1;
+
+    p2m_lock(p2m);
+    if ( p2m->spp_set_entry )
+        ret = p2m->spp_set_entry(p2m, gfn_l, access);
+    p2m_unlock(p2m);
+
+    return ret;
+}
+
 /*
  * Local variables:
  * mode: C
diff --git a/xen/arch/x86/mm/p2m-ept.c b/xen/arch/x86/mm/p2m-ept.c
index c249286..c9dc29c 100644
--- a/xen/arch/x86/mm/p2m-ept.c
+++ b/xen/arch/x86/mm/p2m-ept.c
@@ -38,6 +38,8 @@
 
 #define is_epte_present(ept_entry)      ((ept_entry)->epte & 0x7)
 #define is_epte_superpage(ept_entry)    ((ept_entry)->sp)
+#define is_sppt_present(spp_entry)      ((spp_entry)->spp & 0x1)
+
 static inline bool_t is_epte_valid(ept_entry_t *e)
 {
     /* suppress_ve alone is not considered valid, so mask it off */
@@ -253,6 +255,22 @@ static int ept_set_middle_entry(struct p2m_domain *p2m, 
ept_entry_t *ept_entry)
     return 1;
 }
 
+static int spp_set_middle_entry(struct p2m_domain *p2m, spp_entry_t *spp_entry)
+{
+    struct page_info *pg;
+
+    pg = p2m_alloc_ptp(p2m, 0);
+    if ( pg == NULL )
+        return 0;
+
+    spp_entry->spp = 0;
+    spp_entry->mfn = page_to_mfn(pg);
+
+    spp_entry->present = 1;
+
+    return 1;
+}
+
 /* free ept sub tree behind an entry */
 static void ept_free_entry(struct p2m_domain *p2m, ept_entry_t *ept_entry, int 
level)
 {
@@ -323,6 +341,44 @@ static bool_t ept_split_super_page(struct p2m_domain *p2m,
     return rv;
 }
 
+static int spp_next_level(struct p2m_domain *p2m,
+                          spp_entry_t **table, unsigned long *gfn_remainder,
+                          int next_level)
+{
+    unsigned long mfn;
+    spp_entry_t *spp_entry, e;
+    u32 shift, index;
+
+    shift = next_level * EPT_TABLE_ORDER;
+
+    index = *gfn_remainder >> shift;
+
+    /* index must be falling into the page */
+    ASSERT(index < EPT_PAGETABLE_ENTRIES);
+
+    spp_entry = (*table) + index;
+
+    /* ept_next_level() is called (sometimes) without a lock.  Read
+     * the entry once, and act on the "cached" entry after that to
+     * avoid races. */
+    e.spp = read_atomic(&(spp_entry->spp));
+
+    if ( !is_sppt_present(&e) )
+    {
+        if ( !spp_set_middle_entry(p2m, spp_entry) )
+            return GUEST_TABLE_MAP_FAILED;
+        else
+            e.spp = read_atomic(&(spp_entry->spp)); /* Refresh */
+    }
+
+    mfn = e.mfn;
+    unmap_domain_page(*table);
+    *table = map_domain_page(_mfn(mfn));
+    *gfn_remainder &= (1UL << shift) - 1;
+    return GUEST_TABLE_NORMAL_PAGE;
+}
+
+
 /* Take the currently mapped table, find the corresponding gfn entry,
  * and map the next table, if available.  If the entry is empty
  * and read_only is set, 
@@ -709,6 +765,43 @@ out:
     return rc;
 }
 
+static int
+spp_set_entry(struct p2m_domain *p2m, unsigned long gfn, u64 access)
+{
+    struct spp_data *spp = &p2m->spptp;
+    unsigned long gfn_remainder = gfn;
+    spp_entry_t *table;
+    u64 *pspp_bitmap;
+    u64 old_spp_bitmap;
+    unsigned int i;
+    int ret, rc = 0;
+
+    ASSERT(spp);
+    table = 
map_domain_page(_mfn(pagetable_get_pfn(p2m_get_spp_pagetable(p2m))));
+
+    for ( i = 3; i > 0; i-- )
+    {
+        ret = spp_next_level(p2m, &table, &gfn_remainder, i);
+        if ( ret != GUEST_TABLE_NORMAL_PAGE )
+        {
+            printk("dazhang1 error oc ret = %x\n", ret);
+            rc = -1;
+            goto out;
+        }
+    }
+
+    pspp_bitmap = (u64 *) (table + gfn_remainder);
+    old_spp_bitmap = read_atomic(pspp_bitmap);
+    if( old_spp_bitmap != access )
+    {
+        write_atomic(pspp_bitmap, access);
+    }
+
+out:
+    unmap_domain_page(table);
+    return rc;
+}
+
 /*
  * ept_set_entry() computes 'need_modify_vtd_table' for itself,
  * by observing whether any gfn->mfn translations are modified.
@@ -1309,6 +1402,7 @@ int ept_p2m_init(struct p2m_domain *p2m)
     if ( cpu_has_vmx_ept_spp )
     {
         p2m->update_ept_spp_wp = ept_spp_update_wp;
+        p2m->spp_set_entry = spp_set_entry;
     }
 
     if ( !zalloc_cpumask_var(&ept->invalidate) )
diff --git a/xen/include/asm-x86/hvm/vmx/vmx.h 
b/xen/include/asm-x86/hvm/vmx/vmx.h
index 18383b8..655ce80 100644
--- a/xen/include/asm-x86/hvm/vmx/vmx.h
+++ b/xen/include/asm-x86/hvm/vmx/vmx.h
@@ -52,6 +52,16 @@ typedef union {
     u64 epte;
 } ept_entry_t;
 
+typedef union {
+    struct {
+        u64 present     :   1,  /* bit 0 - spp middle table is present */
+        reserved        :   11, /* bit 1:11 - reserved */
+        mfn             :   40, /* bit 12:51 - Machine physical frame number */
+        reserved2       :   12; /* bit 52:63 - reserved */
+    };
+    u64 spp;
+} spp_entry_t;
+
 typedef struct {
     /*use lxe[0] to save result */
     ept_entry_t lxe[5];
diff --git a/xen/include/asm-x86/p2m.h b/xen/include/asm-x86/p2m.h
index adbc1c6..b94ebb2 100644
--- a/xen/include/asm-x86/p2m.h
+++ b/xen/include/asm-x86/p2m.h
@@ -268,6 +268,9 @@ struct p2m_domain {
     long               (*audit_p2m)(struct p2m_domain *p2m);
     int                (*update_ept_spp_wp)(struct p2m_domain *p2m,
                                  unsigned long gfn);
+    int                (*spp_set_entry)(struct p2m_domain *p2m,
+                                unsigned long gfn,
+                                u64 access);
 
     /*
      * P2M updates may require TLBs to be flushed (invalidated).
-- 
2.7.4


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel

 


Rackspace

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