T243: Fix armv7 pmap code to work with new vnodes
authorSimon Gerber <simon.gerber@inf.ethz.ch>
Mon, 15 Aug 2016 11:50:34 +0000 (13:50 +0200)
committerSimon Gerber <simon.gerber@inf.ethz.ch>
Mon, 15 Aug 2016 11:59:50 +0000 (13:59 +0200)
Closes T243.

Signed-off-by: Simon Gerber <simon.gerber@inf.ethz.ch>

include/target/arm/barrelfish/pmap_target.h
lib/barrelfish/arch/arm/pmap_arch.c

index f718ab6..b070c81 100644 (file)
 
 #include <barrelfish/pmap.h>
 
-#define L2_PER_PAGE             4
-#define PTABLE_SIZE             (BASE_PAGE_SIZE / L2_PER_PAGE)
-#define L2_PAGE_IDX(idx)        ((idx) % L2_PER_PAGE)
-#define L2_PAGE_OFFSET(idx)     (L2_PAGE_IDX(idx) * PTABLE_SIZE)
-
-#define L2_IS_MAPPED(ptable, idx) \
-        (!capref_is_null((ptable)->u.vnode.mapped[idx]))
-
-
 /// Node in the meta-data, corresponds to an actual VNode object
 struct vnode {
     uint16_t      entry;       ///< Page table entry of this VNode
     bool          is_vnode;    ///< Is this a page table or a page mapping
     struct vnode  *next;       ///< Next entry in list of siblings
+    struct capref mapping;     ///< Mapping cap for this vnode
     union {
         struct {
             struct capref cap;           ///< Capability of this VNode
             struct capref invokable;     ///< Invokable copy of Capability of this VNode
             struct vnode  *children;     ///< Children of this VNode
-            struct capref mapped[L2_PER_PAGE]; // < mapping caps for mapped 1k tables
         } vnode; // for non-leaf node
         struct {
             struct capref   cap;         ///< Capability of this VNode
-            struct capref   mapping;     ///< Mapping cap for this vnode
             genvaddr_t      offset;      ///< Offset within mapped frame cap
             vregion_flags_t flags;       ///< Flags for mapping
-            uint16_t        pte_count;   ///< number of user page table entries consumed by this mapping
-            uint16_t        kernel_pte_count;   ///< number kernel ptes in this mapping
+            uint16_t        pte_count;   ///< number of page table entries consumed by this mapping
         } frame; // for leaf node (maps page(s)/section(s))
     } u;
 };
index f676633..5c0f088 100644 (file)
  * page table arrangement onto Barrelfish. The problem lies with
  * resource bootstrapping. The bootstrap ram allocator allocates pages.
  *
- *
- * The natural division of bits is 12/10/12, corresponding to 4K
- * L1 entries in the L1 table and 256 L2 entries per L2
- * table. Unfortunately 256 entries consumes 1KB rather than a
- * page (4KB) so we pretend here and in the kernel caps page
- * code that the L1 has 1024 entries and L2 tables are 4KB in
- * size. The 4KB constraint comes from ram_alloc_fixed
- * allocating single pages and the difficulty in bootstrapping
- * cap slots (alloc_node takes a single slot.
- *
- * For now this suffices, but might need to be revisited in future.
- *
- * An earlier cut at this, used the first 1KB from each
- * allocation made from ram_alloc_fixed and wasted the remaining
- * space. Aside from the space wasted it entailed a couple of minor
- * platform ifdefs to work around the discrepency.
- *
- * Alternative fixes discussed include:
- *
- * 1. avoid the need to create vnodes before connecting to a
- *    real allocator (probably not plausible).
- *
- * 2. somehow make ram_alloc_fixed handle sub-page allocations
- *    (it's clunky, but perhaps we can give each domain a separate
- *     cnode full of 1k- sized RAM caps?)
- *
- * 3. handle the problem at the level of vnode_create (can't see how to
- *    do this)
- *
- * 4. waste the space -- doing this cleanly will require a new parameter
- * to retype to prevent all 4 caps being created
- *
- * 5. introduce a new arm-specific version of vnode_create that creates
- * 4 1k vnodes, and is only called from the ARM VM code.
+ * After reworking retype to be range based, we can now select to create a
+ * single 1kB vnode from a 4kB frame, so we currently waste 3kB when creating
+ * ARM l2 vnodes before we have a connection to the memory server.
  *
  */
 
 #define META_DATA_RESERVED_SPACE (BASE_PAGE_SIZE * 1024)
 // increased above value from 128 for pandaboard port
 
-// Convenience macros to figure out user space page table indices
-// we use 10 bits for both L1 and L2 tables in user space, even though
-// in hardware we use 12 bits for L1 and 8 bits for L2.
-#define ARM_USER_L1_OFFSET(addr) ((uintptr_t)(addr >> 22) & 0x3ffu)
-#define ARM_USER_L2_OFFSET(addr) ((uintptr_t)(addr >> 12) & 0x3ffu)
-
 static inline uintptr_t
 vregion_flags_to_kpi_paging_flags(vregion_flags_t flags)
 {
@@ -147,47 +110,20 @@ static bool has_vnode(struct vnode *root, uint32_t entry, size_t len,
             __FUNCTION__, entry, end_entry, only_pages);
 #endif
 
-    bool found_pages = false;
     for (n = root->u.vnode.children; n; n = n->next) {
         // region to check [entry .. end_entry)
-        if (n->is_vnode) {
-#ifdef DEBUG_HAS_VNODE
-            debug_printf("%s: looking at vnode: entry = %d, mapped = %"PRIx8"\n",
-                    __FUNCTION__, n->entry, n->u.vnode.mapped);
-#endif
-            // n is page table, we need to check if each mapped 1k L2 overlaps
-            // with the region to check [entry .. end_entry)
-            for (int i = 0; i < L2_PER_PAGE; i++) {
-                // ith 1k L2 is mapped
-#ifdef DEBUG_HAS_VNODE
-                debug_printf("%s: n->u.vnode.mapped[%d] == %d\n",
-                        __FUNCTION__, i, L2_IS_MAPPED(n, i));
-#endif
-                if (L2_IS_MAPPED(n, i)) {
-#ifdef DEBUG_HAS_VNODE
-                    debug_printf("%s: check overlapping: %"PRIu32"--%"PRIu32
-                            " <> %"PRIu32"--%"PRIu32"\n",
-                            __FUNCTION__, entry, end_entry,
-                            n->entry + i, n->entry + i + 1);
-#endif
-                    if (is_overlapping(entry, end_entry, n->entry + i, n->entry + i + 1)) {
-                        if (only_pages) {
-                            uint16_t rec_start = i * PTABLE_SIZE;
-#ifdef DEBUG_HAS_VNODE
-                            debug_printf("%s: checking recursively in %"PRIu16"--%"PRIu16"\n",
-                                    __FUNCTION__, rec_start, rec_start + PTABLE_SIZE);
-#endif
-                            found_pages = found_pages
-                                || has_vnode(n, rec_start, PTABLE_SIZE, true);
-                            if (found_pages) {
-                                return true;
-                            }
-                        } else {
-                            return true;
-                        }
-                    }
-                }
+        if (n->is_vnode && n->entry >= entry && n->entry < end_entry) {
+            if (only_pages) {
+                return has_vnode(n, 0, ARM_L2_TABLE_BYTES, true);
             }
+#ifdef LIBBARRELFISH_DEBUG_PMAP
+            debug_printf("1: found page table inside our region\n");
+#endif
+            return true;
+        } else if (n->is_vnode) {
+            // all other vnodes do not overlap with us, so go to next
+            assert(n->entry < entry || n->entry >= end_entry);
+            continue;
         } else {
             // not vnode
             uint32_t end = n->entry + n->u.frame.pte_count;
@@ -225,15 +161,15 @@ static struct vnode *find_vnode(struct vnode *root, uint16_t entry)
 
     for(n = root->u.vnode.children; n != NULL; n = n->next) {
         if (n->is_vnode &&
-            is_overlapping(entry, entry + 1, n->entry, n->entry + L2_PER_PAGE)) {
+            is_overlapping(entry, entry + 1, n->entry, n->entry + 1)) {
 #ifdef DEBUG_FIND_VNODE
             debug_printf("%s: found ptable at [%"PRIu16"--%"PRIu16"]\n",
-                    __FUNCTION__, n->entry, n->entry + L2_PER_PAGE);
+                    __FUNCTION__, n->entry, n->entry + 1);
 #endif
             return n;
         }
         else if (n->is_vnode) {
-            assert(!is_overlapping(entry, entry + 1, n->entry, n->entry + L2_PER_PAGE));
+            assert(!is_overlapping(entry, entry + 1, n->entry, n->entry + 1));
             // ignore all other vnodes;
             continue;
         }
@@ -301,32 +237,6 @@ static void remove_vnode(struct vnode *root, struct vnode *item)
     USER_PANIC("Should not get here");
 }
 
-static void unmap_l2_table(struct vnode *root, struct vnode *n, uint16_t e)
-{
-    errval_t err;
-    if (L2_IS_MAPPED(n, e)) {
-        err = vnode_unmap(root->u.vnode.invokable, n->u.vnode.mapped[e]);
-        if (err_is_fail(err)) {
-            debug_printf("remove_empty_vnodes: vnode_unmap: %s\n",
-                    err_getstring(err));
-            abort();
-        }
-        err = cap_delete(n->u.vnode.mapped[e]);
-        if (err_is_fail(err)) {
-            debug_printf("remove_empty_vnodes: vnode_unmap: %s\n",
-                    err_getstring(err));
-            abort();
-        }
-        err = slot_free(n->u.vnode.mapped[e]);
-        if (err_is_fail(err)) {
-            debug_printf("remove_empty_vnodes: vnode_unmap: %s\n",
-                    err_getstring(err));
-            abort();
-        }
-        n->u.vnode.mapped[e] = NULL_CAP;
-    }
-}
-
 /**
  * \brief (recursively) remove empty page tables in region [entry ..
  * entry+len) in vnode `root`.
@@ -351,11 +261,10 @@ static void remove_empty_vnodes(struct slab_allocator *vnode_alloc, struct vnode
         // page tables
         assert(n->is_vnode);
 
-        if (entry < n->entry && end_entry >= n->entry + L2_PER_PAGE) {
-            // whole entry in region: completely unmap&free L2 page
-            for (int i = 0; i < L2_PER_PAGE; i++) {
-                unmap_l2_table(root, n, i);
-            }
+        // Unmap vnode if it is in range [entry .. entry+len)
+        if (n->entry >= entry && n->entry < end_entry) {
+            err = vnode_unmap(root->u.vnode.invokable, n->mapping);
+            assert(err_is_ok(err));
 
             if (!capcmp(n->u.vnode.cap, n->u.vnode.invokable)) {
                 // delete invokable pt cap if it's a real copy
@@ -370,24 +279,6 @@ static void remove_empty_vnodes(struct slab_allocator *vnode_alloc, struct vnode
             // remove vnode from list
             remove_vnode(root, n);
             slab_free(vnode_alloc, n);
-        } else if (entry >= n->entry && entry < n->entry + L2_PER_PAGE) {
-            // tail end of vnode in region:
-            uint16_t e = entry - n->entry;
-#ifdef DEBUG_REMOVE_EMPTY_VNODES
-            debug_printf("overlap: %"PRIu16" entries\n", e);
-#endif
-            for (; e < L2_PER_PAGE; e++) {
-                unmap_l2_table(root, n, e);
-            }
-        } else if (end_entry > n->entry && end_entry < n->entry + L2_PER_PAGE) {
-            // start of vnode in region
-            uint16_t e = end_entry - n->entry;
-#ifdef DEBUG_REMOVE_EMPTY_VNODES
-            debug_printf("overlap: %"PRIu16" entries\n", e);
-#endif
-            for (int i = 0; i < e; i++) {
-                unmap_l2_table(root, n, i);
-            }
         }
     }
 }
@@ -418,22 +309,28 @@ static errval_t alloc_vnode(struct pmap_arm *pmap_arm, struct vnode *root,
     if (err_is_fail(err)) {
         return err_push(err, LIB_ERR_VNODE_CREATE);
     }
-    for (int i = 0; i < L2_PER_PAGE; i++) {
-        newvnode->u.vnode.mapped[i] = NULL_CAP;
-    }
 
     // XXX: do we need to put master copy in other cspace?
     newvnode->u.vnode.invokable = newvnode->u.vnode.cap;
 
     // The VNode meta data
-    newvnode->entry            = ROUND_DOWN(entry, L2_PER_PAGE);
-    assert(newvnode->entry % L2_PER_PAGE == 0);
+    newvnode->entry            = entry;
     newvnode->next             = root->u.vnode.children;
-    // no entries mapped
-    memset(newvnode->u.vnode.mapped, 0, sizeof(newvnode->u.vnode.mapped));
     root->u.vnode.children     = newvnode;
     newvnode->u.vnode.children = NULL;
 
+    err = slot_alloc(&newvnode->mapping);
+    if (err_is_fail(err)) {
+        return err_push(err, LIB_ERR_SLOT_ALLOC);
+    }
+
+    err = vnode_map(root->u.vnode.invokable, newvnode->u.vnode.cap,
+            entry, KPI_PAGING_FLAGS_READ | KPI_PAGING_FLAGS_WRITE, 0, 1,
+            newvnode->mapping);
+    if (err_is_fail(err)) {
+        return err_push(err, LIB_ERR_PMAP_MAP);
+    }
+
     if (retvnode) {
         *retvnode = newvnode;
     }
@@ -454,7 +351,6 @@ static errval_t get_ptable(struct pmap_arm  *pmap,
     // of L2 is 1 page of L2 entries (4 tables) so we use 10 bits for the L1
     // idx here
     uintptr_t idx = ARM_L1_OFFSET(vaddr);
-    uintptr_t page_idx = L2_PAGE_IDX(idx);
     if ((*ptable = find_vnode(&pmap->root, idx)) == NULL)
     {
         // L1 table entries point to L2 tables so allocate an L2
@@ -462,11 +358,6 @@ static errval_t get_ptable(struct pmap_arm  *pmap,
 
         struct vnode *tmp = NULL; // Tmp variable for passing to alloc_vnode
 
-#ifdef DEBUG_GET_PTABLE
-        uintptr_t fidx = ROUND_DOWN(idx, L2_PER_PAGE);
-        debug_printf("allocating 4 x L2, entries = %"PRIuPTR"--%z"PRIuPTR"\n",
-                 fidx, fidx+4);
-#endif
         errval_t err = alloc_vnode(pmap, &pmap->root, ObjType_VNode_ARM_l2,
                                    idx, &tmp);
         if (err_is_fail(err)) {
@@ -489,45 +380,7 @@ static errval_t get_ptable(struct pmap_arm  *pmap,
     assert(pt->is_vnode);
 #ifdef DEBUG_GET_PTABLE
     debug_printf("have ptable: %p\n", pt);
-    debug_printf("page_idx = %d\n", page_idx);
-    debug_printf("l2_is_mapped: %d\n", L2_IS_MAPPED(pt, page_idx));
-#endif
-    if (!L2_IS_MAPPED(pt, page_idx)) {
-#ifdef DEBUG_GET_PTABLE
-        debug_printf("need to map entry %d\n", page_idx);
-#endif
-        errval_t err;
-        uintptr_t offset = L2_PAGE_OFFSET(idx);
-#ifdef DEBUG_GET_PTABLE
-        debug_printf("mapping L1 entry %d at offset %"PRIuPTR"\n", idx, offset);
-#endif
-
-        // create copy of ptable cap for this index, if it doesn't exist
-        // already
-#ifdef DEBUG_GET_PTABLE
-        debug_printf("allocating slot for mapping cap for chunk %d\n", page_idx);
-#endif
-        err = slot_alloc(&pt->u.vnode.mapped[page_idx]);
-        if (err_is_fail(err)) {
-            return err_push(err, LIB_ERR_VNODE_MAP);
-        }
-
-#ifdef DEBUG_GET_PTABLE
-        debug_printf("calling vnode_map() for chunk %d\n", page_idx);
 #endif
-        // map single 1k ptable
-        err = vnode_map(pmap->root.u.vnode.invokable, pt->u.vnode.cap, idx,
-                KPI_PAGING_FLAGS_READ | KPI_PAGING_FLAGS_WRITE, offset, 1,
-                pt->u.vnode.mapped[page_idx]);
-
-        if (err_is_fail(err)) {
-            errval_t err2 = slot_free(pt->u.vnode.mapped[page_idx]);
-            if (err_is_fail(err2)) {
-                err = err_push(err, err2);
-            }
-            return err_push(err, LIB_ERR_VNODE_MAP);
-        }
-    }
 
     return SYS_ERR_OK;
 }
@@ -573,7 +426,7 @@ static errval_t do_single_map(struct pmap_arm *pmap, genvaddr_t vaddr, genvaddr_
     } else {
 #ifdef LIBBARRELFISH_DEBUG_PMAP
         debug_printf("%s: 4k path: mapping %"PRIxGENVADDR", %zu entries\n", __FUNCTION__, vaddr, pte_count);
-        debug_printf("4k path: L1 entry: %zu\n", ARM_USER_L1_OFFSET(vaddr));
+        debug_printf("4k path: L1 entry: %zu\n", ARM_L1_OFFSET(vaddr));
 #endif
         //4k mapping
         // XXX: reassess the following note -SG
@@ -585,7 +438,7 @@ static errval_t do_single_map(struct pmap_arm *pmap, genvaddr_t vaddr, genvaddr_
             DEBUG_ERR(err, "get_ptable() in do_single_map");
             return err_push(err, LIB_ERR_PMAP_GET_PTABLE);
         }
-        entry = ARM_USER_L2_OFFSET(vaddr);
+        entry = ARM_L2_OFFSET(vaddr);
 #ifdef LIBBARRELFISH_DEBUG_PMAP
         debug_printf("%s: 4k path: L2 entry=%zu\n", __FUNCTION__, entry);
         debug_printf("%s: ptable->is_vnode = %d\n",
@@ -597,11 +450,6 @@ static errval_t do_single_map(struct pmap_arm *pmap, genvaddr_t vaddr, genvaddr_
     flags &= ~(VREGION_FLAGS_LARGE | VREGION_FLAGS_HUGE);
     uintptr_t pmap_flags = vregion_flags_to_kpi_paging_flags(flags);
 
-    uintptr_t user_pte_count = pte_count;
-    if (is_large) {
-        user_pte_count = DIVIDE_ROUND_UP(pte_count, L2_PER_PAGE);
-    }
-
     // check if there is an overlapping mapping
     if (has_vnode(ptable, entry, pte_count, false)) {
 #ifdef LIBBARRELFISH_DEBUG_PMAP
@@ -633,10 +481,9 @@ static errval_t do_single_map(struct pmap_arm *pmap, genvaddr_t vaddr, genvaddr_
     ptable->u.vnode.children = page;
     page->u.frame.cap = frame;
     page->u.frame.flags = flags;
-    page->u.frame.pte_count = user_pte_count;
-    page->u.frame.kernel_pte_count = pte_count;
+    page->u.frame.pte_count = pte_count;
 
-    err = slot_alloc(&page->u.frame.mapping);
+    err = slot_alloc(&page->mapping);
     if (err_is_fail(err)) {
         return err_push(err, LIB_ERR_SLOT_ALLOC);
     }
@@ -644,9 +491,9 @@ static errval_t do_single_map(struct pmap_arm *pmap, genvaddr_t vaddr, genvaddr_
     // Map entry into the page table
     err = vnode_map(ptable->u.vnode.invokable, frame, entry,
                     pmap_flags, offset, pte_count,
-                    page->u.frame.mapping);
+                    page->mapping);
     if (err_is_fail(err)) {
-        errval_t err2 = slot_free(page->u.frame.mapping);
+        errval_t err2 = slot_free(page->mapping);
         if (err_is_fail(err2)) {
                 err = err_push(err, err2);
         }
@@ -784,9 +631,9 @@ max_slabs_required(size_t bytes)
     // Perform a slab allocation for every page (do_map -> slab_alloc)
     size_t pages     = DIVIDE_ROUND_UP(bytes, BASE_PAGE_SIZE);
     // Perform a slab allocation for every L2 (get_ptable -> find_vnode)
-    size_t l2entries = DIVIDE_ROUND_UP(pages, 256 * 4);
+    size_t l2entries = DIVIDE_ROUND_UP(pages, ARM_L2_MAX_ENTRIES);
     // Perform a slab allocation for every L1 (do_map -> find_vnode)
-    size_t l1entries = DIVIDE_ROUND_UP(l2entries, 1024);
+    size_t l1entries = DIVIDE_ROUND_UP(l2entries, ARM_L1_MAX_ENTRIES);
     return pages + l2entries + l1entries;
 }
 static size_t max_slabs_required_large(size_t bytes)
@@ -959,25 +806,25 @@ static errval_t do_single_unmap(struct pmap_arm *pmap, genvaddr_t vaddr,
     // pt->is_vnode == non-large mapping
     if (pt && pt->is_vnode) {
         // analog to do_single_map we use 10 bits for tracking pages in user space -SG
-        struct vnode *page = find_vnode(pt, ARM_USER_L2_OFFSET(vaddr));
+        struct vnode *page = find_vnode(pt, ARM_L2_OFFSET(vaddr));
         if (page && page->u.frame.pte_count == pte_count) {
 #ifdef LIBBARRELFISH_DEBUG_PMAP
         debug_printf("page unmap: pt entry: %zu, entry = %zu, pte_count = %hu\n",
                 pt->entry, page->entry, page->u.frame.pte_count);
 #endif
-            err = vnode_unmap(pt->u.vnode.cap, page->u.frame.mapping);
+            err = vnode_unmap(pt->u.vnode.cap, page->mapping);
             if (err_is_fail(err)) {
                 DEBUG_ERR(err, "vnode_unmap");
                 return err_push(err, LIB_ERR_VNODE_UNMAP);
             }
 
             // cleanup mapping cap
-            err = cap_delete(page->u.frame.mapping);
+            err = cap_delete(page->mapping);
             if (err_is_fail(err)) {
                 DEBUG_ERR(err, "cap_delete");
                 return err_push(err, LIB_ERR_CAP_DELETE);
             }
-            err = slot_free(page->u.frame.mapping);
+            err = slot_free(page->mapping);
             if (err_is_fail(err)) {
                 return err_push(err, LIB_ERR_SLOT_FREE);
             }
@@ -993,19 +840,19 @@ static errval_t do_single_unmap(struct pmap_arm *pmap, genvaddr_t vaddr,
         debug_printf("section unmap: entry = %zu, pte_count = %zu\n",
                 pt->entry, pt->u.frame.kernel_pte_count);
 #endif
-        err = vnode_unmap(pmap->root.u.vnode.cap, pt->u.frame.mapping);
+        err = vnode_unmap(pmap->root.u.vnode.cap, pt->mapping);
         if (err_is_fail(err)) {
             DEBUG_ERR(err, "vnode_unmap");
             return err_push(err, LIB_ERR_VNODE_UNMAP);
         }
 
         // cleanup mapping cap
-        err = cap_delete(pt->u.frame.mapping);
+        err = cap_delete(pt->mapping);
         if (err_is_fail(err)) {
             DEBUG_ERR(err, "cap_delete");
             return err_push(err, LIB_ERR_CAP_DELETE);
         }
-        err = slot_free(pt->u.frame.mapping);
+        err = slot_free(pt->mapping);
         if (err_is_fail(err)) {
             return err_push(err, LIB_ERR_SLOT_FREE);
         }
@@ -1166,8 +1013,8 @@ static errval_t determine_addr_raw(struct pmap *pmap, size_t size,
 
     // compile pdir free list
     // barrelfish treats L1 as 1024 entries
-    bool f[1024];
-    for (int i = 0; i < 1024; i++) {
+    bool f[ARM_L1_MAX_ENTRIES];
+    for (int i = 0; i < ARM_L1_MAX_ENTRIES; i++) {
         f[i] = true;
     }
     f[walk_pdir->entry] = false;
@@ -1207,7 +1054,7 @@ static errval_t do_single_modify_flags(struct pmap_arm *pmap, genvaddr_t vaddr,
 {
     errval_t err = SYS_ERR_OK;
     struct vnode *ptable = find_ptable(pmap, vaddr);
-    uint16_t ptentry = ARM_USER_L2_OFFSET(vaddr);
+    uint16_t ptentry = ARM_L2_OFFSET(vaddr);
     if (ptable) {
         struct vnode *page = find_vnode(ptable, ptentry);
         if (page) {
@@ -1220,7 +1067,7 @@ static errval_t do_single_modify_flags(struct pmap_arm *pmap, genvaddr_t vaddr,
                 size_t off = ptentry - page->entry;
                 uintptr_t pmap_flags = vregion_flags_to_kpi_paging_flags(flags);
                 // VA hinting NYI on ARM, so we always pass 0 for va_hint
-                err = invoke_mapping_modify_flags(page->u.frame.mapping,
+                err = invoke_mapping_modify_flags(page->mapping,
                         off, pages, pmap_flags, 0);
                 printf("invoke_frame_modify_flags returned error: %s (%"PRIuERRV")\n",
                         err_getstring(err), err);