adding support for modify flags on large/huge pages
authorReto Achermann <reto.achermann@inf.ethz.ch>
Mon, 23 Feb 2015 09:54:29 +0000 (10:54 +0100)
committerSimon Gerber <simon.gerber@inf.ethz.ch>
Thu, 11 Jun 2015 15:48:07 +0000 (17:48 +0200)
Signed-off-by: Simon Gerber <simon.gerber@inf.ethz.ch>

kernel/arch/x86_64/page_mappings_arch.c
kernel/include/target/x86_64/paging_kernel_target.h
lib/barrelfish/target/x86_64/pmap_target.c
lib/barrelfish/vspace/memobj_one_frame.c

index ba63b71..abbe285 100644 (file)
@@ -92,7 +92,7 @@ static errval_t x86_64_non_ptable(struct capability *dest, cslot_t slot,
 
                 // check offset within frame
                 genpaddr_t off = offset;
-              
+
                 if (off + pte_count * X86_64_LARGE_PAGE_SIZE > get_size(src)) {
                     return SYS_ERR_FRAME_OFFSET_INVALID;
                 }
@@ -453,6 +453,24 @@ errval_t page_mappings_modify_flags(struct capability *frame, size_t offset,
 {
     struct cte *mapping = cte_for_cap(frame);
     struct mapping_info *info = &mapping->mapping_info;
+    struct cte *leaf_pt;
+    errval_t err;
+    err = mdb_find_cap_for_address(info->pte, &leaf_pt);
+    if (err_is_fail(err)) {
+        return err;
+    }
+    genvaddr_t vaddr;
+    size_t entry2 = (info->pte - get_address(&leaf_pt->cap)) /
+                    PTABLE_ENTRY_SIZE;
+    err = compile_vaddr(leaf_pt, entry2, &vaddr);
+    if (err_is_fail(err)) {
+        if (err_no(err) == SYS_ERR_VNODE_NOT_INSTALLED) {
+            debug(SUBSYS_PAGING, "couldn't reconstruct virtual address\n");
+        }
+        else {
+            return err;
+        }
+    }
 
     /* Calculate page access protection flags */
     // Get frame cap rights
@@ -468,10 +486,30 @@ errval_t page_mappings_modify_flags(struct capability *frame, size_t offset,
     /* Calculate location of page table entries we need to modify */
     lvaddr_t base = local_phys_to_mem(info->pte) + offset;
 
-    for (int i = 0; i < pages; i++) {
-        union x86_64_ptable_entry *entry =
-            (union x86_64_ptable_entry *)base + i;
-        paging_x86_64_modify_flags(entry, flags);
+    switch(leaf_pt->cap.type) {
+        case ObjType_VNode_x86_64_ptable :
+            for (int i = 0; i < pages; i++) {
+                union x86_64_ptable_entry *entry =
+                    (union x86_64_ptable_entry *)base + i;
+                paging_x86_64_modify_flags(entry, flags);
+            }
+            break;
+        case ObjType_VNode_x86_64_pdir :
+            for (int i = 0; i < pages; i++) {
+                union x86_64_ptable_entry *entry =
+                    (union x86_64_ptable_entry *)base + i;
+                paging_x86_64_modify_flags_large(entry, flags);
+            }
+            break;
+        case ObjType_VNode_x86_64_pdpt :
+            for (int i = 0; i < pages; i++) {
+                union x86_64_ptable_entry *entry =
+                    (union x86_64_ptable_entry *)base + i;
+                paging_x86_64_modify_flags_huge(entry, flags);
+            }
+            break;
+        default:
+            return SYS_ERR_WRONG_MAPPING;
     }
 
     /* do full TLB flush */
index caf03ac..0c81acc 100644 (file)
@@ -231,7 +231,7 @@ static inline void paging_x86_64_map_huge(union x86_64_ptable_entry *entry,
     tmp.huge.execute_disable = bitmap & X86_64_PTABLE_EXECUTE_DISABLE ? 1 : 0;
     tmp.huge.always1 = 1;
     tmp.huge.base_addr = base >> X86_64_HUGE_PAGE_BITS;
-    
+
     *entry = tmp;
 }
 
@@ -266,7 +266,7 @@ static inline void paging_x86_64_map_large(union x86_64_ptable_entry *entry,
     tmp.large.execute_disable = bitmap & X86_64_PTABLE_EXECUTE_DISABLE ? 1 : 0;
     tmp.large.always1 = 1;
     tmp.large.base_addr = base >> 21;
-    
+
     *entry = tmp;
 }
 
@@ -304,6 +304,71 @@ static inline void paging_x86_64_map(union x86_64_ptable_entry * NONNULL entry,
     *entry = tmp;
 }
 
+
+/**
+ * \brief Modify flags of a huge page.
+ *
+ * From small page table entry, pointed to by 'entry', maps physical address
+ * 'base' with page attribute bitmap 'bitmap'.
+ *
+ * \param entry         Pointer to page table entry to map from.
+ * \param bitmap        Bitmap to apply to page attributes.
+ */
+static inline void paging_x86_64_modify_flags_huge(union x86_64_ptable_entry * entry,
+                                                   uint64_t bitmap)
+{
+    union x86_64_ptable_entry tmp = *entry;
+
+    tmp.huge.present = bitmap & X86_64_PTABLE_PRESENT ? 1 : 0;
+    tmp.huge.read_write = bitmap & X86_64_PTABLE_READ_WRITE ? 1 : 0;
+    tmp.huge.user_supervisor = bitmap & X86_64_PTABLE_USER_SUPERVISOR ? 1 : 0;
+    tmp.huge.write_through = bitmap & X86_64_PTABLE_WRITE_THROUGH ? 1 : 0;
+    tmp.huge.cache_disabled = bitmap & X86_64_PTABLE_CACHE_DISABLED ? 1 : 0;
+#ifdef __k1om__
+    tmp.huge.global = 0;
+#else
+    tmp.huge.global = bitmap & X86_64_PTABLE_GLOBAL_PAGE ? 1 : 0;
+#endif
+    tmp.huge.attr_index = bitmap & X86_64_PTABLE_ATTR_INDEX ? 1 : 0;
+    tmp.huge.execute_disable = bitmap & X86_64_PTABLE_EXECUTE_DISABLE ? 1 : 0;
+    tmp.huge.always1 = 1;
+
+    *entry = tmp;
+}
+
+
+/**
+ * \brief Modify flags of a large page.
+ *
+ * From small page table entry, pointed to by 'entry', maps physical address
+ * 'base' with page attribute bitmap 'bitmap'.
+ *
+ * \param entry         Pointer to page table entry to map from.
+ * \param bitmap        Bitmap to apply to page attributes.
+ */
+static inline void paging_x86_64_modify_flags_large(union x86_64_ptable_entry *entry,
+                                              uint64_t bitmap)
+{
+    union x86_64_ptable_entry tmp = *entry;
+
+    tmp.large.present = bitmap & X86_64_PTABLE_PRESENT ? 1 : 0;
+    tmp.large.read_write = bitmap & X86_64_PTABLE_READ_WRITE ? 1 : 0;
+    tmp.large.user_supervisor = bitmap & X86_64_PTABLE_USER_SUPERVISOR ? 1 : 0;
+    tmp.large.write_through = bitmap & X86_64_PTABLE_WRITE_THROUGH ? 1 : 0;
+    tmp.large.cache_disabled = bitmap & X86_64_PTABLE_CACHE_DISABLED ? 1 : 0;
+#ifdef __k1om__
+    /* The Xeon Phi has no support for global pages */
+    tmp.large.global = 0;
+#else
+    tmp.large.global = bitmap & X86_64_PTABLE_GLOBAL_PAGE ? 1 : 0;
+#endif
+    tmp.large.attr_index = bitmap & X86_64_PTABLE_ATTR_INDEX ? 1 : 0;
+    tmp.large.execute_disable = bitmap & X86_64_PTABLE_EXECUTE_DISABLE ? 1 : 0;
+    tmp.large.always1 = 1;
+
+    *entry = tmp;
+}
+
 /**
  * \brief Modify flags of a normal (small) page.
  *
index 0303b95..02c9c85 100644 (file)
@@ -814,29 +814,41 @@ static errval_t do_single_modify_flags(struct pmap_x86 *pmap, genvaddr_t vaddr,
                                        size_t pages, vregion_flags_t flags)
 {
     errval_t err = SYS_ERR_OK;
-    struct vnode *ptable = find_ptable(pmap, vaddr);
+
+    struct vnode *pt = NULL, *page = NULL;
+
+    if (!find_mapping(pmap, vaddr, &pt, &page)) {
+        return LIB_ERR_PMAP_FIND_VNODE;
+    }
+    assert(pt && pt->is_vnode && page && !page->is_vnode);
+
     uint16_t ptentry = X86_64_PTABLE_BASE(vaddr);
-    if (ptable) {
-        struct vnode *page = find_vnode(ptable, ptentry);
-        if (page) {
-            if (inside_region(ptable, ptentry, pages)) {
-                // we're modifying part of a valid mapped region
-                // arguments to invocation: invoke frame cap, first affected
-                // page (as offset from first page in mapping), #affected
-                // pages, new flags. Invocation mask flags based on capability
-                // access permissions.
-                size_t off = ptentry - page->entry;
-                paging_x86_64_flags_t pmap_flags = vregion_to_pmap_flag(flags);
-                err = invoke_frame_modify_flags(page->u.frame.cap, off, pages, pmap_flags);
-                printf("invoke_frame_modify_flags returned error: %s (%"PRIuERRV")\n",
-                        err_getstring(err), err);
-                return err;
-            } else {
-                // overlaps some region border
-                return LIB_ERR_PMAP_EXISTING_MAPPING;
-            }
-        }
+    if (is_large_page(page)) {
+        //large 2M page
+        ptentry = X86_64_PDIR_BASE(vaddr);
+    } else if (is_huge_page(page)) {
+        //huge 1GB page
+        ptentry = X86_64_PDPT_BASE(vaddr);
     }
+
+    if (inside_region(pt, ptentry, pages)) {
+        // we're modifying part of a valid mapped region
+        // arguments to invocation: invoke frame cap, first affected
+        // page (as offset from first page in mapping), #affected
+        // pages, new flags. Invocation mask flags based on capability
+        // access permissions.
+        size_t off = ptentry - page->entry;
+        paging_x86_64_flags_t pmap_flags = vregion_to_pmap_flag(flags);
+        err = invoke_frame_modify_flags(page->u.frame.cap, off, pages, pmap_flags);
+        //printf("invoke_frame_modify_flags returned error: %s (%"PRIuERRV")\n",
+        //                 err_getstring(err), err);
+        return err;
+    } else {
+        // overlaps some region border
+        return LIB_ERR_PMAP_EXISTING_MAPPING;
+    }
+
+
     return SYS_ERR_OK;
 }
 
@@ -854,17 +866,44 @@ static errval_t modify_flags(struct pmap *pmap, genvaddr_t vaddr, size_t size,
 {
     errval_t err;
     struct pmap_x86 *x86 = (struct pmap_x86 *)pmap;
-    size = ROUND_UP(size, X86_64_BASE_PAGE_SIZE);
-    size_t pages = size / X86_64_BASE_PAGE_SIZE;
+
+    //determine if we unmap a larger page
+    struct vnode* page = NULL;
+
+    if (!find_mapping(x86, vaddr, NULL, &page)) {
+        //TODO: better error --> LIB_ERR_PMAP_NOT_MAPPED
+        return LIB_ERR_PMAP_UNMAP;
+    }
+
+    assert(!page->is_vnode);
+
+    size_t page_size = X86_64_BASE_PAGE_SIZE;
+    size_t table_base = X86_64_PTABLE_BASE(vaddr);
+    uint8_t map_bits= X86_64_BASE_PAGE_BITS + X86_64_PTABLE_BITS;
+    if (is_large_page(page)) {
+        //large 2M page
+        page_size = X86_64_LARGE_PAGE_SIZE;
+        table_base = X86_64_PDIR_BASE(vaddr);
+        map_bits = X86_64_LARGE_PAGE_BITS + X86_64_PTABLE_BITS;
+    } else if (is_huge_page(page)) {
+        //huge 1GB page
+        page_size = X86_64_HUGE_PAGE_SIZE;
+        table_base = X86_64_PDPT_BASE(vaddr);
+        map_bits = X86_64_HUGE_PAGE_BITS + X86_64_PTABLE_BITS;
+    }
+
+    // TODO: match new policy of map when implemented
+    size = ROUND_UP(size, page_size);
     genvaddr_t vend = vaddr + size;
 
+    size_t pages = size / page_size;
+
     // vaddr and vend specify begin and end of the region (inside a mapping)
     // that should receive the new set of flags
     //
-    // TODO: figure out page_size etc of original mapping
-    uint8_t map_bits = X86_64_BASE_PAGE_BITS + X86_64_PTABLE_BITS;
-
-    if (is_same_pdir(vaddr, vend)) {
+    if (is_same_pdir(vaddr, vend) ||
+        (is_same_pdpt(vaddr, vend) && is_large_page(page)) ||
+        (is_same_pml4(vaddr, vend) && is_huge_page(page))) {
         // fast path
         err = do_single_modify_flags(x86, vaddr, pages, flags);
         if (err_is_fail(err)) {
@@ -873,25 +912,26 @@ static errval_t modify_flags(struct pmap *pmap, genvaddr_t vaddr, size_t size,
     }
     else { // slow path
         // modify first part
-        uint32_t c = X86_64_PTABLE_SIZE - X86_64_PTABLE_BASE(vaddr);
+        uint32_t c = X86_64_PTABLE_SIZE - table_base;
         err = do_single_modify_flags(x86, vaddr, c, flags);
         if (err_is_fail(err)) {
             return err_push(err, LIB_ERR_PMAP_MODIFY_FLAGS);
         }
 
         // modify full leaves
-        vaddr += c * X86_64_BASE_PAGE_SIZE;
+        vaddr += c * page_size;
         while (get_addr_prefix(vaddr, map_bits) < get_addr_prefix(vend, map_bits)) {
             c = X86_64_PTABLE_SIZE;
             err = do_single_modify_flags(x86, vaddr, X86_64_PTABLE_SIZE, flags);
             if (err_is_fail(err)) {
                 return err_push(err, LIB_ERR_PMAP_MODIFY_FLAGS);
             }
-            vaddr += c * X86_64_BASE_PAGE_SIZE;
+            vaddr += c * page_size;
         }
 
         // modify remaining part
-        c = X86_64_PTABLE_BASE(vend) - X86_64_PTABLE_BASE(vaddr);
+        c = get_addr_prefix(vend, map_bits-X86_64_PTABLE_BITS) -
+                get_addr_prefix(vaddr, map_bits-X86_64_PTABLE_BITS);
         if (c) {
             err = do_single_modify_flags(x86, vaddr, c, flags);
             if (err_is_fail(err)) {
index 7739f73..7ac6f19 100644 (file)
@@ -98,7 +98,19 @@ static errval_t unmap_region(struct memobj *memobj, struct vregion *vregion)
 static errval_t protect(struct memobj *memobj, struct vregion *vregion,
                         genvaddr_t offset, size_t range, vs_prot_flags_t flags)
 {
-    USER_PANIC("NYI");
+    struct vspace *vspace = vregion_get_vspace(vregion);
+    struct pmap *pmap = vspace_get_pmap(vspace);
+    genvaddr_t base = vregion_get_base_addr(vregion);
+    genvaddr_t vregion_offset = vregion_get_offset(vregion);
+    errval_t err;
+    size_t ret_size;
+    err = pmap->f.modify_flags(pmap, base + offset + vregion_offset, range,
+                               flags, &ret_size);
+    if (err_is_fail(err)) {
+        return err_push(err, LIB_ERR_PMAP_MODIFY_FLAGS);
+    }
+
+    return SYS_ERR_OK;
 }
 
 /**