Updates xen_pt_update_entry function from xen/arch/arm/mm.c to
automatically split superpages as needed.
---
 xen/arch/arm/mm.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 78 insertions(+), 13 deletions(-)

diff --git a/xen/arch/arm/mm.c b/xen/arch/arm/mm.c
index 6301752..91b9c2b 100644
--- a/xen/arch/arm/mm.c
+++ b/xen/arch/arm/mm.c
@@ -753,8 +753,78 @@ static int create_xen_table(lpae_t *entry)
 }
 
 #define XEN_TABLE_MAP_FAILED 0
-#define XEN_TABLE_SUPER_PAGE 1
-#define XEN_TABLE_NORMAL_PAGE 2
+#define XEN_TABLE_NORMAL_PAGE 1
+
+/* More or less taken from p2m_split_superpage, without the p2m stuff */
+static bool xen_split_superpage(lpae_t *entry, unsigned int level,
+                                unsigned int target, const unsigned int 
*offsets)
+{
+    struct page_info *page;
+    lpae_t pte, *table;
+    unsigned int i;
+    bool rv = true;
+
+    mfn_t mfn = lpae_get_mfn(*entry);
+    unsigned int next_level = level + 1;
+    unsigned int level_order = XEN_PT_LEVEL_ORDER(next_level);
+
+    ASSERT(level < target);
+    ASSERT(lpae_is_superpage(*entry, level));
+
+    page = alloc_domheap_page(NULL, 0);
+    if ( !page )
+        return false;
+
+    table = __map_domain_page(page);
+
+    /*
+     * We are either splitting a first level 1G page into 512 second level
+     * 2M pages, or a second level 2M page into 512 third level 4K pages.
+     */
+    for ( i = 0; i < XEN_PT_LPAE_ENTRIES; i++ )
+    {
+        lpae_t *new_entry = table + i;
+
+        /*
+         * Use the content of the superpage entry and override
+         * the necessary fields. So the correct permission are kept.
+         */
+        pte = *entry;
+        lpae_set_mfn(pte, mfn_add(mfn, i << level_order));
+
+        /*
+         * First and second level pages set walk.table = 0, but third
+         * level entries set walk.table = 1.
+         */
+        pte.walk.table = (next_level == 3);
+
+        write_pte(new_entry, pte);
+    }
+
+    /*
+     * Shatter superpage in the page to the level we want to make the
+     * changes.
+     * This is done outside the loop to avoid checking the offset to
+     * know whether the entry should be shattered for every entry.
+     */
+    if ( next_level != target )
+        rv = xen_split_superpage(table + offsets[next_level],
+                                 level + 1, target, offsets);
+
+    clean_dcache_va_range(table, PAGE_SIZE);
+    unmap_domain_page(table);
+
+    /*
+     * Generate the entry for this new table we created,
+     * and write it back in place of the superpage entry.
+     */
+    pte = mfn_to_xen_entry(page_to_mfn(page), MT_NORMAL);
+    pte.pt.table = 1;
+    write_pte(entry, pte);
+    clean_dcache(*entry);
+
+    return rv;
+}
 
 /*
  * Take the currently mapped table, find the corresponding entry,
@@ -767,16 +837,15 @@ static int create_xen_table(lpae_t *entry)
  *  XEN_TABLE_MAP_FAILED: Either read_only was set and the entry
  *  was empty, or allocating a new page failed.
  *  XEN_TABLE_NORMAL_PAGE: next level mapped normally
- *  XEN_TABLE_SUPER_PAGE: The next entry points to a superpage.
  */
 static int xen_pt_next_level(bool read_only, unsigned int level,
-                             lpae_t **table, unsigned int offset)
+                             lpae_t **table, const unsigned int *offsets)
 {
     lpae_t *entry;
     int ret;
     mfn_t mfn;
 
-    entry = *table + offset;
+    entry = *table + offsets[level];
 
     if ( !lpae_is_valid(*entry) )
     {
@@ -790,7 +859,8 @@ static int xen_pt_next_level(bool read_only, unsigned int 
level,
 
     /* The function xen_pt_next_level is never called at the 3rd level */
     if ( lpae_is_mapping(*entry, level) )
-        return XEN_TABLE_SUPER_PAGE;
+        /* Shatter the superpage before continuing */
+        xen_split_superpage(entry, level, level + 1, offsets);
 
     mfn = lpae_get_mfn(*entry);
 
@@ -915,7 +985,7 @@ static int xen_pt_update_entry(mfn_t root, unsigned long 
virt,
     table = xen_map_table(root);
     for ( level = HYP_PT_ROOT_LEVEL; level < target; level++ )
     {
-        rc = xen_pt_next_level(read_only, level, &table, offsets[level]);
+        rc = xen_pt_next_level(read_only, level, &table, offsets);
         if ( rc == XEN_TABLE_MAP_FAILED )
         {
             /*
@@ -941,12 +1011,7 @@ static int xen_pt_update_entry(mfn_t root, unsigned long 
virt,
             break;
     }
 
-    if ( level != target )
-    {
-        mm_printk("%s: Shattering superpage is not supported\n", __func__);
-        rc = -EOPNOTSUPP;
-        goto out;
-    }
+    BUG_ON( level != target );
 
     entry = table + offsets[level];
 
-- 
2.7.4


Reply via email to