1/** 2 * \file 3 * \brief Pmap code common for x86 archs 4 */ 5 6/* 7 * Copyright (c) 2011, ETH Zurich. 8 * Copyright (c) 2014, HP Labs. 9 * All rights reserved. 10 * 11 * This file is distributed under the terms in the attached LICENSE file. 12 * If you do not find this file, copies can be found by writing to: 13 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group. 14 */ 15 16#include <barrelfish/barrelfish.h> 17#include <barrelfish/pmap.h> 18#include "target/x86/pmap_x86.h" 19#include <pmap_priv.h> // for set_mapping_cap 20#include <pmap_ds.h> // for selected pmap datastructure 21 22// For tracing 23#include <trace/trace.h> 24#include <trace_definitions/trace_defs.h> 25 26/** 27 * has_vnode() checks whether there exists any vnodes in `len` slots 28 * starting from `entry` in vnode `root`, if `only_pages` is set, page table 29 * vnodes are ignored 30 */ 31bool has_vnode(struct vnode *root, uint32_t entry, size_t len, 32 bool only_pages) 33{ 34 assert(root != NULL); 35 assert(root->v.is_vnode); 36 struct vnode *n; 37 38 uint32_t end_entry = entry + len; 39 40 // region we check [entry .. end_entry) 41 pmap_foreach_child(root, n) { 42 assert(n); 43 // n is page table, we need to check if it's anywhere inside the 44 // region to check [entry .. end_entry) 45 // this amounts to n->entry == entry for len = 1 46 if (n->v.is_vnode && n->v.entry >= entry && n->v.entry < end_entry) { 47 if (only_pages) { 48 return has_vnode(n, 0, PTABLE_ENTRIES, true); 49 } 50#ifdef LIBBARRELFISH_DEBUG_PMAP 51 debug_printf("1: found page table inside our region\n"); 52#endif 53 return true; 54 } else if (n->v.is_vnode) { 55 // all other vnodes do not overlap with us, so go to next 56 assert(n->v.entry < entry || n->v.entry >= end_entry); 57 continue; 58 } 59 // this remains the same regardless of `only_pages`. 60 // n is frame [n->v.entry .. end) 61 // 3 cases: 62 // 1) entry < n->v.entry && end_entry >= end --> n is a strict subset of 63 // our region 64 // 2) entry inside n (entry >= n->v.entry && entry < end) 65 // 3) end_entry inside n (end_entry >= n->v.entry && end_entry < end) 66 uint32_t end = n->v.entry + n->v.u.frame.pte_count; 67 if (entry < n->v.entry && end_entry >= end) { 68#ifdef LIBBARRELFISH_DEBUG_PMAP 69 debug_printf("2: found a strict subset of our region: (%" 70 PRIu32"--%"PRIu32") < (%"PRIu32"--%"PRIu32")\n", 71 n->v.entry, end, entry, end_entry); 72#endif 73 return true; 74 } 75 if (entry >= n->v.entry && entry < end) { 76#ifdef LIBBARRELFISH_DEBUG_PMAP 77 debug_printf("3: found a region starting inside our region: (%" 78 PRIu32"--%"PRIu32") <> (%"PRIu32"--%"PRIu32")\n", 79 n->v.entry, end, entry, end_entry); 80#endif 81 return true; 82 } 83 if (end_entry > n->v.entry && end_entry < end) { 84#ifdef LIBBARRELFISH_DEBUG_PMAP 85 debug_printf("4: found a region ending inside our region: (%" 86 PRIu32"--%"PRIu32") <> (%"PRIu32"--%"PRIu32")\n", 87 n->v.entry, end, entry, end_entry); 88#endif 89 return true; 90 } 91 } 92 93 return false; 94} 95 96 97/** 98 * \brief Allocates a new VNode, adding it to the page table and our metadata 99 */ 100errval_t alloc_vnode(struct pmap_x86 *pmap, struct vnode *root, 101 enum objtype type, uint32_t entry, 102 struct vnode **retvnode, genvaddr_t base) 103{ 104 errval_t err; 105 106 struct vnode *newvnode = slab_alloc(&pmap->p.m.slab); 107 if (newvnode == NULL) { 108 return LIB_ERR_SLAB_ALLOC_FAIL; 109 } 110 111 // The VNode capability 112 err = pmap->p.slot_alloc->alloc(pmap->p.slot_alloc, &newvnode->v.cap); 113 if (err_is_fail(err)) { 114 return err_push(err, LIB_ERR_SLOT_ALLOC); 115 } 116 pmap->used_cap_slots ++; 117 118 err = vnode_create(newvnode->v.cap, type); 119 if (err_is_fail(err)) { 120 return err_push(err, LIB_ERR_VNODE_CREATE); 121 } 122 123 // XXX: need to make sure that vnode cap that we will invoke is in our cspace! 124 if (get_croot_addr(newvnode->v.cap) != CPTR_ROOTCN) { 125 // debug_printf("%s: creating vnode for another domain in that domain's cspace; need to copy vnode cap to our cspace to make it invokable\n", __FUNCTION__); 126 err = slot_alloc(&newvnode->v.u.vnode.invokable); 127 assert(err_is_ok(err)); 128 pmap->used_cap_slots ++; 129 err = cap_copy(newvnode->v.u.vnode.invokable, newvnode->v.cap); 130 assert(err_is_ok(err)); 131 } else { 132 // debug_printf("vnode in our cspace: copying capref to invokable\n"); 133 newvnode->v.u.vnode.invokable = newvnode->v.cap; 134 } 135 assert(!capref_is_null(newvnode->v.cap)); 136 assert(!capref_is_null(newvnode->v.u.vnode.invokable)); 137 138 set_mapping_cap(&pmap->p, newvnode, root, entry); 139 pmap->used_cap_slots ++; 140 assert(!capref_is_null(newvnode->v.mapping)); 141 142 // Map it 143 err = vnode_map(root->v.u.vnode.invokable, newvnode->v.cap, entry, 144 PTABLE_ACCESS_DEFAULT, 0, 1, newvnode->v.mapping); 145 if (err_is_fail(err)) { 146 DEBUG_ERR(err, "pmap_x86: vnode_map\n"); 147 return err_push(err, LIB_ERR_VNODE_MAP); 148 } 149 150 // The VNode meta data 151 newvnode->v.is_vnode = true; 152 newvnode->is_cloned = false; 153 newvnode->is_pinned = false; 154 newvnode->v.entry = entry; 155 newvnode->v.type = type; 156 pmap_vnode_init(&pmap->p, newvnode); 157 pmap_vnode_insert_child(root, newvnode); 158 newvnode->u.vnode.virt_base = 0; 159 newvnode->u.vnode.page_table_frame = NULL_CAP; 160 newvnode->u.vnode.base = base; 161 162#if GLOBAL_MCN 163 /* allocate mapping cnodes */ 164 for (int i = 0; i < MCN_COUNT; i++) { 165 err = cnode_create_l2(&newvnode->u.vnode.mcn[i], &newvnode->u.vnode.mcnode[i]); 166 if (err_is_fail(err)) { 167 return err_push(err, LIB_ERR_PMAP_ALLOC_CNODE); 168 } 169 } 170#endif 171 172 *retvnode = newvnode; 173 return SYS_ERR_OK; 174} 175 176void remove_empty_vnodes(struct pmap_x86 *pmap, struct vnode *root, 177 uint32_t entry, size_t len) 178{ 179 errval_t err; 180 uint32_t end_entry = entry + len; 181 struct vnode *n; 182 pmap_foreach_child(root, n) { 183 assert(n); 184 if (n->v.entry >= entry && n->v.entry < end_entry) { 185 // sanity check and skip leaf entries 186 if (!n->v.is_vnode || n->is_pinned) { 187 continue; 188 } 189 // here we know that all vnodes we're interested in are 190 // page tables 191 assert(n->v.is_vnode); 192 if (n->v.u.vnode.children) { 193 remove_empty_vnodes(pmap, n, 0, PTABLE_ENTRIES); 194 } 195 196 // unmap 197 err = vnode_unmap(root->v.cap, n->v.mapping); 198 if (err_is_fail(err)) { 199 debug_printf("remove_empty_vnodes: vnode_unmap: %s\n", 200 err_getstring(err)); 201 } 202 203 // delete mapping cap first: underlying cap needs to exist for 204 // this to work properly! 205 err = cap_delete(n->v.mapping); 206 if (err_is_fail(err)) { 207 debug_printf("remove_empty_vnodes: cap_delete (mapping): %s\n", 208 err_getstring(err)); 209 } 210#ifndef GLOBAL_MCN 211 err = pmap->p.slot_alloc->free(pmap->p.slot_alloc, n->v.mapping); 212 if (err_is_fail(err)) { 213 debug_printf("remove_empty_vnodes: slot_free (mapping): %s\n", 214 err_getstring(err)); 215 } 216#endif 217 assert(pmap->used_cap_slots > 0); 218 pmap->used_cap_slots --; 219 // delete capability 220 err = cap_delete(n->v.cap); 221 if (err_is_fail(err)) { 222 debug_printf("remove_empty_vnodes: cap_delete (vnode): %s\n", 223 err_getstring(err)); 224 } 225 if (!capcmp(n->v.cap, n->v.u.vnode.invokable)) { 226 // invokable is always allocated in our cspace 227 err = cap_destroy(n->v.u.vnode.invokable); 228 if (err_is_fail(err)) { 229 debug_printf("remove_empty_vnodes: cap_delete (vnode.invokable): %s\n", 230 err_getstring(err)); 231 232 } 233 } 234 err = pmap->p.slot_alloc->free(pmap->p.slot_alloc, n->v.cap); 235 if (err_is_fail(err)) { 236 debug_printf("remove_empty_vnodes: slot_free (vnode): %s\n", 237 err_getstring(err)); 238 } 239 assert(pmap->used_cap_slots > 0); 240 pmap->used_cap_slots --; 241 242 // remove vnode from list 243 pmap_remove_vnode(root, n); 244 245#if GLOBAL_MCN 246 /* delete mapping cap cnodes */ 247 for (int x = 0; x < MCN_COUNT; x++) { 248 err = cap_destroy(n->u.vnode.mcn[x]); 249 if (err_is_fail(err)) { 250 debug_printf("%s: cap_destroy(mapping cn %d): %s\n", 251 __FUNCTION__, x, err_getcode(err)); 252 } 253 } 254#endif 255 pmap_vnode_free(&pmap->p, n); 256 } 257 } 258} 259 260 261 262/** 263 * \brief Determine a suitable address for a given memory object 264 * 265 * \param pmap The pmap object 266 * \param memobj The memory object to determine the address for 267 * \param alignment Minimum alignment 268 * \param retvaddr Pointer to return the determined address 269 * 270 * Relies on vspace.c code maintaining an ordered list of vregions 271 */ 272errval_t pmap_x86_determine_addr(struct pmap *pmap, struct memobj *memobj, 273 size_t alignment, genvaddr_t *retvaddr) 274{ 275 trace_event(TRACE_SUBSYS_MEMORY, TRACE_EVENT_MEMORY_DETADDR, 0); 276 struct pmap_x86 *pmapx = (struct pmap_x86 *)pmap; 277 genvaddr_t vaddr; 278 279 struct vregion *walk = pmap->vspace->head; 280 assert(walk != NULL); // assume there's always at least one existing entry 281 282 if (alignment == 0) { 283 alignment = BASE_PAGE_SIZE; 284 } else { 285 alignment = ROUND_UP(alignment, BASE_PAGE_SIZE); 286 } 287 size_t size = ROUND_UP(memobj->size, alignment); 288 289 // if there's space before the first object, map it there 290 genvaddr_t minva = ROUND_UP(pmapx->min_mappable_va, alignment); 291 if (minva + size <= vregion_get_base_addr(walk)) { 292 vaddr = minva; 293 goto out; 294 } 295 296 while (walk->next) { // Try to insert between existing mappings 297 genvaddr_t walk_base = vregion_get_base_addr(walk); 298 genvaddr_t walk_size = ROUND_UP(vregion_get_size(walk), BASE_PAGE_SIZE); 299 genvaddr_t walk_end = ROUND_UP(walk_base + walk_size, alignment); 300 genvaddr_t next_base = vregion_get_base_addr(walk->next); 301 302 // sanity-check for page alignment 303 assert(walk_base % BASE_PAGE_SIZE == 0); 304 assert(next_base % BASE_PAGE_SIZE == 0); 305 306 if (next_base > walk_end + size) { 307 vaddr = walk_end; 308 goto out; 309 } 310 311 walk = walk->next; 312 } 313 314 // Place beyond last mapping, with alignment 315 vaddr = ROUND_UP((vregion_get_base_addr(walk) 316 + ROUND_UP(vregion_get_size(walk), BASE_PAGE_SIZE)), 317 alignment); 318 319 out: 320 // Ensure that we haven't run out of address space 321 if (vaddr + memobj->size > pmapx->max_mappable_va) { 322 trace_event(TRACE_SUBSYS_MEMORY, TRACE_EVENT_MEMORY_DETADDR, 1); 323 return LIB_ERR_OUT_OF_VIRTUAL_ADDR; 324 } 325 326 assert(retvaddr != NULL); 327 *retvaddr = vaddr; 328 329 trace_event(TRACE_SUBSYS_MEMORY, TRACE_EVENT_MEMORY_DETADDR, 1); 330 return SYS_ERR_OK; 331} 332 333errval_t pmap_x86_measure_res(struct pmap *pmap, struct pmap_res_info *buf) 334{ 335 assert(buf); 336 struct pmap_x86 *x86 = (struct pmap_x86 *)pmap; 337 338 size_t free_slabs = 0; 339 size_t used_slabs = 0; 340 // Count allocated and free slabs in bytes; this accounts for all vnodes 341 for (struct slab_head *sh = pmap->m.slab.slabs; sh != NULL; sh = sh->next) { 342 free_slabs += sh->free; 343 used_slabs += sh->total - sh->free; 344 } 345 buf->vnode_used = used_slabs * pmap->m.slab.blocksize; 346 buf->vnode_free = free_slabs * pmap->m.slab.blocksize; 347 348 // Report capability slots in use by pmap 349 buf->slots_used = x86->used_cap_slots; 350 351 return SYS_ERR_OK; 352} 353 354struct vnode_public *pmap_get_vroot(struct pmap *pmap) { 355 struct pmap_x86 *pmapx = (struct pmap_x86 *)pmap; 356 return &pmapx->root.v; 357} 358 359void pmap_set_min_mappable_va(struct pmap *pmap, lvaddr_t minva) 360{ 361 struct pmap_x86 *pmapx = (struct pmap_x86 *)pmap; 362 pmapx->min_mappable_va = minva; 363} 364