1/** 2 * \file 3 * \brief pmap management 4 */ 5 6/* 7 * Copyright (c) 2010,2015, ETH Zurich. 8 * Copyright (c) 2015, Hewlett Packard Enterprise Development LP. 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/* 17 * There was some minor difficulty here with mapping the cpus native 18 * page table arrangement onto Barrelfish. The problem lies with 19 * resource bootstrapping. The bootstrap ram allocator allocates pages. 20 * 21 * 22 * The natural division of bits is 12/10/12, corresponding to 4K 23 * L1 entries in the L1 table and 256 L2 entries per L2 24 * table. Unfortunately 256 entries consumes 1KB rather than a 25 * page (4KB) so we pretend here and in the kernel caps page 26 * code that the L1 has 1024 entries and L2 tables are 4KB in 27 * size. The 4KB constraint comes from ram_alloc_fixed 28 * allocating single pages and the difficulty in bootstrapping 29 * cap slots (alloc_node takes a single slot. 30 * 31 * For now this suffices, but might need to be revisited in future. 32 * 33 * An earlier cut at this, used the first 1KB from each 34 * allocation made from ram_alloc_fixed and wasted the remaining 35 * space. Aside from the space wasted it entailed a couple of minor 36 * platform ifdefs to work around the discrepency. 37 * 38 * Alternative fixes discussed include: 39 * 40 * 1. avoid the need to create vnodes before connecting to a 41 * real allocator (probably not plausible). 42 * 43 * 2. somehow make ram_alloc_fixed handle sub-page allocations 44 * (it's clunky, but perhaps we can give each domain a separate 45 * cnode full of 1k- sized RAM caps?) 46 * 47 * 3. handle the problem at the level of vnode_create (can't see how to 48 * do this) 49 * 50 * 4. waste the space -- doing this cleanly will require a new parameter 51 * to retype to prevent all 4 caps being created 52 * 53 * 5. introduce a new arm-specific version of vnode_create that creates 54 * 4 1k vnodes, and is only called from the ARM VM code. 55 * 56 */ 57 58#include <barrelfish/barrelfish.h> 59#include <barrelfish/caddr.h> 60#include <barrelfish/invocations_arch.h> 61#include <pmap_priv.h> 62#include <pmap_ds.h> // for selected pmap datastructure 63 64static inline paging_aarch64_flags_t 65vregion_flags_to_kpi_paging_flags(vregion_flags_t flags) 66{ 67 STATIC_ASSERT(0x1ff == VREGION_FLAGS_MASK, ""); 68 STATIC_ASSERT(0x0f == KPI_PAGING_FLAGS_MASK, ""); 69 STATIC_ASSERT(VREGION_FLAGS_READ == KPI_PAGING_FLAGS_READ, ""); 70 STATIC_ASSERT(VREGION_FLAGS_WRITE == KPI_PAGING_FLAGS_WRITE, ""); 71 STATIC_ASSERT(VREGION_FLAGS_EXECUTE == KPI_PAGING_FLAGS_EXECUTE, ""); 72 STATIC_ASSERT(VREGION_FLAGS_NOCACHE == KPI_PAGING_FLAGS_NOCACHE, ""); 73 if ((flags & VREGION_FLAGS_MPB) != 0) { 74 // XXX: ignore MPB flag on ARM, 75 // otherwise the assert below fires -AB 76 flags &= ~VREGION_FLAGS_MPB; 77 } 78 // XXX: Ignore VTD Snoop flag on AArch64 - this stuff really isn't 79 // portable -DC 80 flags &= ~VREGION_FLAGS_VTD_SNOOP; 81 if ((flags & VREGION_FLAGS_GUARD) != 0) { 82 flags = 0; 83 } 84 85 assert(0 == (~KPI_PAGING_FLAGS_MASK & (paging_aarch64_flags_t)flags)); 86 return (paging_aarch64_flags_t)flags; 87} 88 89static bool has_vnode(struct vnode *root, uint16_t entry, size_t len) 90{ 91 assert(root != NULL); 92 assert(root->v.is_vnode); 93 struct vnode *n; 94 95 uint32_t end_entry = entry + len; 96 97 pmap_foreach_child(root, n) { 98 assert(n); 99 if (n->v.is_vnode && n->v.entry == entry) { 100 return true; 101 } 102 // n is frame 103 uint32_t end = n->v.entry + n->v.u.frame.pte_count; 104 if (n->v.entry < entry && end > end_entry) { 105 return true; 106 } 107 if (n->v.entry >= entry && n->v.entry < end_entry) { 108 return true; 109 } 110 } 111 112 return false; 113} 114 115/** 116 * \brief Allocates a new VNode, adding it to the page table and our metadata 117 */ 118static errval_t alloc_vnode(struct pmap_aarch64 *pmap_aarch64, struct vnode *root, 119 enum objtype type, uint32_t entry, 120 struct vnode **retvnode) 121{ 122 assert(root->v.is_vnode); 123 errval_t err; 124 125 if (!retvnode) { 126 debug_printf("%s called without retvnode from %p, expect badness!\n", __FUNCTION__, __builtin_return_address(0)); 127 // XXX: should probably return error. 128 } 129 assert(retvnode); 130 131 struct vnode *newvnode = slab_alloc(&pmap_aarch64->p.m.slab); 132 if (newvnode == NULL) { 133 return LIB_ERR_SLAB_ALLOC_FAIL; 134 } 135 newvnode->v.is_vnode = true; 136 137 // The VNode capability 138 err = pmap_aarch64->p.slot_alloc->alloc(pmap_aarch64->p.slot_alloc, &newvnode->v.cap); 139 if (err_is_fail(err)) { 140 return err_push(err, LIB_ERR_SLOT_ALLOC); 141 } 142 143 assert(!capref_is_null(newvnode->v.cap)); 144 145 err = vnode_create(newvnode->v.cap, type); 146 if (err_is_fail(err)) { 147 return err_push(err, LIB_ERR_VNODE_CREATE); 148 } 149 150 assert(!capref_is_null(newvnode->v.cap)); 151 152 // XXX: need to make sure that vnode cap that we will invoke is in our cspace! 153 if (get_croot_addr(newvnode->v.cap) != CPTR_ROOTCN) { 154 // 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__); 155 assert(!capref_is_null(newvnode->v.cap)); 156 err = slot_alloc(&newvnode->v.u.vnode.invokable); 157 assert(!capref_is_null(newvnode->v.cap)); 158 assert(err_is_ok(err)); 159 assert(!capref_is_null(newvnode->v.cap)); 160 err = cap_copy(newvnode->v.u.vnode.invokable, newvnode->v.cap); 161 assert(err_is_ok(err)); 162 assert(!capref_is_null(newvnode->v.u.vnode.invokable)); 163 assert(!capref_is_null(newvnode->v.cap)); 164 165 assert(!capref_is_null(newvnode->v.cap)); 166 } else { 167 // debug_printf("vnode in our cspace: copying capref to invokable\n"); 168 assert(!capref_is_null(newvnode->v.cap)); 169 newvnode->v.u.vnode.invokable = newvnode->v.cap; 170 assert(!capref_is_null(newvnode->v.cap)); 171 } 172 assert(!capref_is_null(newvnode->v.cap)); 173 assert(!capref_is_null(newvnode->v.u.vnode.invokable)); 174 175 // set mapping cap to correct slot in mapping cnodes. 176 set_mapping_cap(&pmap_aarch64->p, newvnode, root, entry); 177 178 // Map it 179 err = vnode_map(root->v.u.vnode.invokable, newvnode->v.cap, entry, 180 KPI_PAGING_FLAGS_READ | KPI_PAGING_FLAGS_WRITE, 0, 1, newvnode->v.mapping); 181 if (err_is_fail(err)) { 182 return err_push(err, LIB_ERR_VNODE_MAP); 183 } 184 185 // The VNode meta data 186 newvnode->v.is_vnode = true; 187 newvnode->v.entry = entry; 188 pmap_vnode_init(&pmap_aarch64->p, newvnode); 189 pmap_vnode_insert_child(root, newvnode); 190 191#ifdef GLOBAL_MCN 192 /* allocate mapping cnodes */ 193 for (int i = 0; i < MCN_COUNT; i++) { 194 err = cnode_create_l2(&newvnode->u.vnode.mcn[i], &newvnode->u.vnode.mcnode[i]); 195 if (err_is_fail(err)) { 196 return err_push(err, LIB_ERR_PMAP_ALLOC_CNODE); 197 } 198 } 199#endif 200 201 *retvnode = newvnode; 202 return SYS_ERR_OK; 203} 204 205/** 206 * \brief Returns the vnode for the pagetable mapping a given vspace address 207 */ 208static errval_t get_ptable(struct pmap_aarch64 *pmap, 209 genvaddr_t vaddr, 210 struct vnode **ptable) 211{ 212 errval_t err; 213 struct vnode *root = &pmap->root; 214 struct vnode *pl1, *pl2, *pl3; 215 assert(root != NULL); 216 217 // L0 mapping 218 if ((pl1 = pmap_find_vnode(root, VMSAv8_64_L0_BASE(vaddr))) == NULL) { 219 err = alloc_vnode(pmap, root, ObjType_VNode_AARCH64_l1, 220 VMSAv8_64_L0_BASE(vaddr), &pl1); 221 if (err_is_fail(err)) { 222 return err_push(err, LIB_ERR_PMAP_ALLOC_VNODE); 223 } 224 } 225 226 // L1 mapping 227 if ((pl2 = pmap_find_vnode(pl1, VMSAv8_64_L1_BASE(vaddr))) == NULL) { 228 err = alloc_vnode(pmap, pl1, ObjType_VNode_AARCH64_l2, 229 VMSAv8_64_L1_BASE(vaddr), &pl2); 230 if (err_is_fail(err)) { 231 return err_push(err, LIB_ERR_PMAP_ALLOC_VNODE); 232 } 233 } 234 235 // L2 mapping 236 if ((pl3 = pmap_find_vnode(pl2, VMSAv8_64_L2_BASE(vaddr))) == NULL) { 237 err = alloc_vnode(pmap, pl2, ObjType_VNode_AARCH64_l3, 238 VMSAv8_64_L2_BASE(vaddr), &pl3); 239 if (err_is_fail(err)) { 240 return err_push(err, LIB_ERR_PMAP_ALLOC_VNODE); 241 } 242 } 243 244 assert(pl3 != NULL); 245 *ptable = pl3; 246 return SYS_ERR_OK; 247} 248 249static struct vnode *find_ptable(struct pmap_aarch64 *pmap, 250 genvaddr_t vaddr) 251{ 252 struct vnode *root = &pmap->root; 253 struct vnode *pl1, *pl2; 254 assert(root != NULL); 255 256 // L0 mapping 257 if((pl1 = pmap_find_vnode(root, VMSAv8_64_L0_BASE(vaddr))) == NULL) { 258 return NULL; 259 } 260 261 // L1 mapping 262 if((pl2 = pmap_find_vnode(pl1, VMSAv8_64_L1_BASE(vaddr))) == NULL) { 263 return NULL; 264 } 265 266 // L2 mapping 267 return pmap_find_vnode(pl2, VMSAv8_64_L2_BASE(vaddr)); 268} 269 270static errval_t do_single_map(struct pmap_aarch64 *pmap, genvaddr_t vaddr, genvaddr_t vend, 271 struct capref frame, size_t offset, size_t pte_count, 272 vregion_flags_t flags) 273{ 274 // Get the page table 275 struct vnode *ptable= NULL; 276 errval_t err = get_ptable(pmap, vaddr, &ptable); 277 if (err_is_fail(err)) { 278 return err_push(err, LIB_ERR_PMAP_GET_PTABLE); 279 } 280 281 flags &= ~(VREGION_FLAGS_LARGE | VREGION_FLAGS_HUGE); 282 paging_aarch64_flags_t pmap_flags = vregion_flags_to_kpi_paging_flags(flags); 283 284 uintptr_t idx = VMSAv8_64_L3_BASE(vaddr); 285 286 // Create user level datastructure for the mapping 287 bool has_page = has_vnode(ptable, idx, pte_count); 288 assert(!has_page); 289 290 struct vnode *page = slab_alloc(&pmap->p.m.slab); 291 assert(page); 292 293 page->v.is_vnode = false; 294 page->v.entry = idx; 295 page->v.cap = frame; 296 page->v.u.frame.offset = offset; 297 page->v.u.frame.flags = flags; 298 page->v.u.frame.pte_count = pte_count; 299 300 // only insert child in vtree after new vnode fully initialized 301 pmap_vnode_insert_child(ptable, page); 302 303 set_mapping_cap(&pmap->p, page, ptable, idx); 304 305 // Map entry into the page table 306 assert(!capref_is_null(ptable->v.u.vnode.invokable)); 307 err = vnode_map(ptable->v.u.vnode.invokable, frame, idx, 308 pmap_flags, offset, pte_count, page->v.mapping); 309 310 if (err_is_fail(err)) { 311 return err_push(err, LIB_ERR_VNODE_MAP); 312 } 313 314 return SYS_ERR_OK; 315} 316 317errval_t do_map(struct pmap *pmap_gen, genvaddr_t vaddr, 318 struct capref frame, size_t offset, size_t size, 319 vregion_flags_t flags, size_t *retoff, size_t *retsize) 320{ 321 errval_t err; 322 323 struct pmap_aarch64 *pmap = (struct pmap_aarch64 *)pmap_gen; 324 325 size = ROUND_UP(size, BASE_PAGE_SIZE); 326 size_t pte_count = DIVIDE_ROUND_UP(size, BASE_PAGE_SIZE); 327 genvaddr_t vend = vaddr + size; 328 329 if (VMSAv8_64_L012_BASE(vaddr) == VMSAv8_64_L012_BASE(vend - 1)) { 330 // fast path 331 err = do_single_map(pmap, vaddr, vend, frame, offset, pte_count, flags); 332 if (err_is_fail(err)) { 333 DEBUG_ERR(err, "[do_map] in fast path"); 334 return err_push(err, LIB_ERR_PMAP_DO_MAP); 335 } 336 } else { // multiple leaf page tables 337 // first leaf 338 uint32_t c = VMSAv8_64_PTABLE_NUM_ENTRIES - VMSAv8_64_L3_BASE(vaddr); 339 genvaddr_t temp_end = vaddr + c * BASE_PAGE_SIZE; 340 err = do_single_map(pmap, vaddr, temp_end, frame, offset, c, flags); 341 if (err_is_fail(err)) { 342 return err_push(err, LIB_ERR_PMAP_DO_MAP); 343 } 344 345 // map full leaves 346 while (VMSAv8_64_L012_BASE(temp_end) < VMSAv8_64_L012_BASE(vend)) { // update vars 347 vaddr = temp_end; 348 temp_end = vaddr + VMSAv8_64_PTABLE_NUM_ENTRIES * BASE_PAGE_SIZE; 349 offset += c * BASE_PAGE_SIZE; 350 c = VMSAv8_64_PTABLE_NUM_ENTRIES; 351 352 // do mapping 353 err = do_single_map(pmap, vaddr, temp_end, frame, offset, 354 VMSAv8_64_PTABLE_NUM_ENTRIES, flags); 355 if (err_is_fail(err)) { 356 return err_push(err, LIB_ERR_PMAP_DO_MAP); 357 } 358 } 359 360 // map remaining part 361 offset += c * BASE_PAGE_SIZE; 362 c = VMSAv8_64_L3_BASE(vend) - VMSAv8_64_L3_BASE(temp_end); 363 if (c) { 364 // do mapping 365 err = do_single_map(pmap, temp_end, vend, frame, offset, c, flags); 366 if (err_is_fail(err)) { 367 return err_push(err, LIB_ERR_PMAP_DO_MAP); 368 } 369 } 370 } 371 if (retoff) { 372 *retoff = offset; 373 } 374 if (retsize) { 375 *retsize = size; 376 } 377 //has_vnode_debug = false; 378 return SYS_ERR_OK; 379#if 0 380 errval_t err; 381 paging_aarch64_flags_t pmap_flags = vregion_flags_to_kpi_paging_flags(flags); 382 383 for (size_t i = offset; i < offset + size; i += BASE_PAGE_SIZE) { 384 385 vaddr += BASE_PAGE_SIZE; 386 } 387 388 if (retoff) { 389 *retoff = offset; 390 } 391 if (retsize) { 392 *retsize = size; 393 } 394 return SYS_ERR_OK; 395#endif 396} 397 398size_t 399max_slabs_required(size_t bytes) 400{ 401 //XXX: use the definitions here 402 size_t max_pages = DIVIDE_ROUND_UP(bytes, 4096); 403 size_t max_l3 = DIVIDE_ROUND_UP(max_pages, 512); 404 size_t max_l2 = DIVIDE_ROUND_UP(max_l3, 512); 405 size_t max_l1 = DIVIDE_ROUND_UP(max_l2, 512); 406 // Worst case, our mapping spans over two pdpts 407 return 2 * (max_l3 + max_l2 + max_l1); 408} 409/** 410 * \brief Create page mappings 411 * 412 * \param pmap The pmap object 413 * \param vaddr The virtual address to create the mapping for 414 * \param frame The frame cap to map in 415 * \param offset Offset into the frame cap 416 * \param size Size of the mapping 417 * \param flags Flags for the mapping 418 * \param retoff If non-NULL, filled in with adjusted offset of mapped region 419 * \param retsize If non-NULL, filled in with adjusted size of mapped region 420 */ 421static errval_t 422map(struct pmap *pmap, 423 genvaddr_t vaddr, 424 struct capref frame, 425 size_t offset, 426 size_t size, 427 vregion_flags_t flags, 428 size_t *retoff, 429 size_t *retsize) 430{ 431 errval_t err; 432 433 size += BASE_PAGE_OFFSET(offset); 434 size = ROUND_UP(size, BASE_PAGE_SIZE); 435 offset -= BASE_PAGE_OFFSET(offset); 436 437 const size_t slabs_reserve = 6; // == max_slabs_required(1) 438 size_t slabs_required = max_slabs_required(size) + slabs_reserve; 439 440 err = pmap_refill_slabs(pmap, slabs_required); 441 if (err_is_fail(err)) { 442 return err; 443 } 444 445 return do_map(pmap, vaddr, frame, offset, size, flags, retoff, retsize); 446} 447 448static errval_t do_single_unmap(struct pmap_aarch64 *pmap, genvaddr_t vaddr, 449 size_t pte_count) 450{ 451 errval_t err; 452 struct vnode *pt = find_ptable(pmap, vaddr); 453 if (pt) { 454 struct vnode *page = pmap_find_vnode(pt, VMSAv8_64_L3_BASE(vaddr)); 455 if (page && page->v.u.frame.pte_count == pte_count) { 456 err = vnode_unmap(pt->v.cap, page->v.mapping); 457 if (err_is_fail(err)) { 458 DEBUG_ERR(err, "vnode_unmap"); 459 return err_push(err, LIB_ERR_VNODE_UNMAP); 460 } 461 462 err = cap_delete(page->v.mapping); 463 if (err_is_fail(err)) { 464 return err_push(err, LIB_ERR_CAP_DELETE); 465 } 466#ifndef GLOBAL_MCN 467 err = pmap->p.slot_alloc->free(pmap->p.slot_alloc, page->v.mapping); 468 if (err_is_fail(err)) { 469 debug_printf("remove_empty_vnodes: slot_free (mapping): %s\n", 470 err_getstring(err)); 471 } 472#endif 473 pmap_remove_vnode(pt, page); 474 slab_free(&pmap->p.m.slab, page); 475 } 476 else { 477 return LIB_ERR_PMAP_FIND_VNODE; 478 } 479 } 480 481 return SYS_ERR_OK; 482} 483 484/** 485 * \brief Remove page mappings 486 * 487 * \param pmap The pmap object 488 * \param vaddr The start of the virtual addres to remove 489 * \param size The size of virtual address to remove 490 * \param retsize If non-NULL, filled in with the actual size removed 491 */ 492static errval_t 493unmap(struct pmap *pmap, 494 genvaddr_t vaddr, 495 size_t size, 496 size_t *retsize) 497{ 498 errval_t err, ret = SYS_ERR_OK; 499 struct pmap_aarch64 *pmap_aarch64 = (struct pmap_aarch64*)pmap; 500 size = ROUND_UP(size, BASE_PAGE_SIZE); 501 size_t pte_count = size / BASE_PAGE_SIZE; 502 genvaddr_t vend = vaddr + size; 503 504 if (VMSAv8_64_L012_BASE(vaddr) == VMSAv8_64_L012_BASE(vend - 1)) { 505 // fast path 506 err = do_single_unmap(pmap_aarch64, vaddr, pte_count); 507 if (err_is_fail(err)) { 508 return err_push(err, LIB_ERR_PMAP_UNMAP); 509 } 510 } else { // slow path 511 // unmap first leaf 512 uint32_t c = VMSAv8_64_PTABLE_NUM_ENTRIES - VMSAv8_64_L3_BASE(vaddr); 513 err = do_single_unmap(pmap_aarch64, vaddr, c); 514 if (err_is_fail(err)) { 515 return err_push(err, LIB_ERR_PMAP_UNMAP); 516 } 517 518 // unmap full leaves 519 vaddr += c * BASE_PAGE_SIZE; 520 while (VMSAv8_64_L012_BASE(vaddr) < VMSAv8_64_L012_BASE(vend)) { 521 c = VMSAv8_64_PTABLE_NUM_ENTRIES; 522 err = do_single_unmap(pmap_aarch64, vaddr, c); 523 if (err_is_fail(err)) { 524 return err_push(err, LIB_ERR_PMAP_UNMAP); 525 } 526 vaddr += c * BASE_PAGE_SIZE; 527 } 528 529 // unmap remaining part 530 c = VMSAv8_64_L3_BASE(vend) - VMSAv8_64_L3_BASE(vaddr); 531 if (c) { 532 err = do_single_unmap(pmap_aarch64, vaddr, c); 533 if (err_is_fail(err)) { 534 return err_push(err, LIB_ERR_PMAP_UNMAP); 535 } 536 } 537 } 538 539 if (retsize) { 540 *retsize = size; 541 } 542 543 return ret; 544} 545 546/** 547 * \brief Determine a suitable address for a given memory object 548 * 549 * \param pmap The pmap object 550 * \param memobj The memory object to determine the address for 551 * \param alignment Minimum alignment 552 * \param vaddr Pointer to return the determined address 553 * 554 * Relies on vspace.c code maintaining an ordered list of vregions 555 */ 556static errval_t 557determine_addr(struct pmap *pmap, 558 struct memobj *memobj, 559 size_t alignment, 560 genvaddr_t *retvaddr) 561{ 562 assert(pmap->vspace->head); 563 struct pmap_aarch64* pmap_aarch64 = (struct pmap_aarch64*)pmap; 564 genvaddr_t vaddr; 565 566 if (alignment == 0) { 567 alignment = BASE_PAGE_SIZE; 568 } else { 569 alignment = ROUND_UP(alignment, BASE_PAGE_SIZE); 570 } 571 size_t size = ROUND_UP(memobj->size, alignment); 572 573 struct vregion *walk = pmap->vspace->head; 574 // if there's space before the first object, map there 575 genvaddr_t minva = ROUND_UP(pmap_aarch64->min_mappable_va, alignment); 576 577 while (walk->next) { // Try to insert between existing mappings 578 genvaddr_t walk_base = vregion_get_base_addr(walk); 579 genvaddr_t walk_size = ROUND_UP(vregion_get_size(walk), BASE_PAGE_SIZE); 580 genvaddr_t walk_end = ROUND_UP(walk_base + walk_size, alignment); 581 genvaddr_t next_base = vregion_get_base_addr(walk->next); 582 583 // sanity-check for page alignment 584 assert(walk_base % BASE_PAGE_SIZE == 0); 585 assert(next_base % BASE_PAGE_SIZE == 0); 586 587 if (next_base > walk_end + size && walk_end > minva) { 588 vaddr = walk_end; 589 goto out; 590 } 591 592 walk = walk->next; 593 } 594 595 // place beyond last mapping with alignment 596 vaddr = ROUND_UP((vregion_get_base_addr(walk) 597 + ROUND_UP(vregion_get_size(walk), BASE_PAGE_SIZE)), 598 alignment); 599 600 601 602out: 603 // ensure that we haven't run out of the valid part of the address space 604 if (vaddr + memobj->size > pmap_aarch64->max_mappable_va) { 605 return LIB_ERR_OUT_OF_VIRTUAL_ADDR; 606 } 607 assert(retvaddr != NULL); 608 *retvaddr = vaddr; 609 610 return SYS_ERR_OK; 611} 612 613int pmap_selective_flush = 0; 614static errval_t do_single_modify_flags(struct pmap_aarch64 *pmap, genvaddr_t vaddr, 615 size_t pages, vregion_flags_t flags) 616{ 617 errval_t err = SYS_ERR_OK; 618 struct vnode *ptable = find_ptable(pmap, vaddr); 619 uint16_t ptentry = VMSAv8_64_L3_BASE(vaddr); 620 if (ptable) { 621 struct vnode *page = pmap_find_vnode(ptable, ptentry); 622 if (page) { 623 if (pmap_inside_region(ptable, ptentry, pages)) { 624 // we're modifying part of a valid mapped region 625 // arguments to invocation: invoke frame cap, first affected 626 // page (as offset from first page in mapping), #affected 627 // pages, new flags. Invocation should check compatibility of 628 // new set of flags with cap permissions. 629 size_t off = ptentry - page->v.entry; 630 flags &= ~(VREGION_FLAGS_LARGE | VREGION_FLAGS_HUGE); 631 // debug_printf("Vregion flags: %zx\n", flags); 632 paging_aarch64_flags_t pmap_flags = vregion_flags_to_kpi_paging_flags(flags); 633 // debug_printf("KPI flags: %zx\n", pmap_flags); 634 // VA hinting NYI on ARMv8, always passing 0 635 err = invoke_mapping_modify_flags(page->v.mapping, off, pages, pmap_flags, 0); 636 return err; 637 } else { 638 // overlaps some region border 639 return LIB_ERR_PMAP_EXISTING_MAPPING; 640 } 641 } 642 } 643 return SYS_ERR_OK; 644} 645 646/** 647 * \brief Modify page mapping 648 * 649 * \param pmap The pmap object 650 * \param vaddr The virtual address to unmap 651 * \param flags New flags for the mapping 652 * \param retsize If non-NULL, filled in with the actual size modified 653 */ 654static errval_t 655modify_flags(struct pmap *pmap, 656 genvaddr_t vaddr, 657 size_t size, 658 vregion_flags_t flags, 659 size_t *retsize) 660{ 661 errval_t err, ret = SYS_ERR_OK; 662 struct pmap_aarch64 *pmap_aarch64 = (struct pmap_aarch64*)pmap; 663 size = ROUND_UP(size, BASE_PAGE_SIZE); 664 size_t pte_count = size / BASE_PAGE_SIZE; 665 genvaddr_t vend = vaddr + size; 666 667 if (VMSAv8_64_L012_BASE(vaddr) == VMSAv8_64_L012_BASE(vend - 1)) { 668 // fast path 669 err = do_single_modify_flags(pmap_aarch64, vaddr, pte_count, flags); 670 if (err_is_fail(err)) { 671 return err_push(err, LIB_ERR_PMAP_UNMAP); 672 } 673 } else { // slow path 674 // unmap first leaf 675 uint32_t c = VMSAv8_64_PTABLE_NUM_ENTRIES - VMSAv8_64_L3_BASE(vaddr); 676 err = do_single_modify_flags(pmap_aarch64, vaddr, c, flags); 677 if (err_is_fail(err)) { 678 return err_push(err, LIB_ERR_PMAP_UNMAP); 679 } 680 681 // unmap full leaves 682 vaddr += c * BASE_PAGE_SIZE; 683 while (VMSAv8_64_L012_BASE(vaddr) < VMSAv8_64_L012_BASE(vend)) { 684 c = VMSAv8_64_PTABLE_NUM_ENTRIES; 685 err = do_single_modify_flags(pmap_aarch64, vaddr, c, flags); 686 if (err_is_fail(err)) { 687 return err_push(err, LIB_ERR_PMAP_UNMAP); 688 } 689 vaddr += c * BASE_PAGE_SIZE; 690 } 691 692 // unmap remaining part 693 c = VMSAv8_64_L3_BASE(vend) - VMSAv8_64_L3_BASE(vaddr); 694 if (c) { 695 err = do_single_modify_flags(pmap_aarch64, vaddr, c, flags); 696 if (err_is_fail(err)) { 697 return err_push(err, LIB_ERR_PMAP_UNMAP); 698 } 699 } 700 } 701 702 if (retsize) { 703 *retsize = size; 704 } 705 706 return ret; 707} 708 709/** 710 * \brief Query existing page mapping 711 * 712 * \param pmap The pmap object 713 * \param vaddr The virtual address to query 714 * \param retvaddr Returns the base virtual address of the mapping 715 * \param retsize Returns the actual size of the mapping 716 * \param retcap Returns the cap mapped at this address 717 * \param retoffset Returns the offset within the cap that is mapped 718 * \param retflags Returns the flags for this mapping 719 * 720 * All of the ret parameters are optional. 721 */ 722static errval_t lookup(struct pmap *pmap, genvaddr_t vaddr, 723 struct pmap_mapping_info *info) 724{ 725 USER_PANIC("NYI"); 726 return 0; 727} 728 729static struct pmap_funcs pmap_funcs = { 730 .determine_addr = determine_addr, 731 .map = map, 732 .unmap = unmap, 733 .modify_flags = modify_flags, 734 .lookup = lookup, 735 .serialise = pmap_serialise, 736 .deserialise = pmap_deserialise, 737}; 738 739/** 740 * \brief Initialize the pmap object 741 */ 742errval_t 743pmap_init(struct pmap *pmap, 744 struct vspace *vspace, 745 struct capref vnode, 746 struct slot_allocator *opt_slot_alloc) 747{ 748 struct pmap_aarch64* pmap_aarch64 = (struct pmap_aarch64*)pmap; 749 750 /* Generic portion */ 751 pmap->f = pmap_funcs; 752 pmap->vspace = vspace; 753 754 if (opt_slot_alloc != NULL) { 755 pmap->slot_alloc = opt_slot_alloc; 756 } else { /* use default allocator for this dispatcher */ 757 pmap->slot_alloc = get_default_slot_allocator(); 758 } 759 760 pmap_vnode_mgmt_init(pmap); 761 762 pmap_aarch64->root.v.is_vnode = true; 763 pmap_aarch64->root.v.cap = vnode; 764 pmap_aarch64->root.v.u.vnode.invokable = vnode; 765 766 if (get_croot_addr(vnode) != CPTR_ROOTCN) { 767 errval_t err = slot_alloc(&pmap_aarch64->root.v.u.vnode.invokable); 768 assert(err_is_ok(err)); 769 err = cap_copy(pmap_aarch64->root.v.u.vnode.invokable, vnode); 770 assert(err_is_ok(err)); 771 } 772 assert(!capref_is_null(pmap_aarch64->root.v.cap)); 773 assert(!capref_is_null(pmap_aarch64->root.v.u.vnode.invokable)); 774 pmap_vnode_init(pmap, &pmap_aarch64->root); 775 776#ifdef GLOBAL_MCN 777 /* 778 * Initialize root vnode mapping cnode 779 */ 780 if (pmap == get_current_pmap()) { 781 /* 782 * for now, for our own pmap, we use the left over slot allocator cnode to 783 * provide the mapping cnode for the first half of the root page table as 784 * we cannot allocate CNodes before establishing a connection to the 785 * memory server! 786 */ 787 pmap_aarch64->root.u.vnode.mcn[0].cnode = cnode_root; 788 pmap_aarch64->root.u.vnode.mcn[0].slot = ROOTCN_SLOT_ROOT_MAPPING; 789 pmap_aarch64->root.u.vnode.mcnode[0].croot = CPTR_ROOTCN; 790 pmap_aarch64->root.u.vnode.mcnode[0].cnode = ROOTCN_SLOT_ADDR(ROOTCN_SLOT_ROOT_MAPPING); 791 pmap_aarch64->root.u.vnode.mcnode[0].level = CNODE_TYPE_OTHER; 792 } else { 793 errval_t err; 794 err = cnode_create_l2(&pmap_aarch64->root.u.vnode.mcn[0], &pmap_aarch64->root.u.vnode.mcnode[0]); 795 if (err_is_fail(err)) { 796 return err_push(err, LIB_ERR_PMAP_ALLOC_CNODE); 797 } 798 } 799#endif 800 801 // choose a minimum mappable VA for most domains; enough to catch NULL 802 // pointer derefs with suitably large offsets 803 pmap_aarch64->min_mappable_va = 64 * 1024; 804 805 // maximum mappable VA is derived from X86_64_MEMORY_OFFSET in kernel 806 pmap_aarch64->max_mappable_va = (genvaddr_t)0xffffff8000000000; 807 808 return SYS_ERR_OK; 809} 810 811errval_t pmap_current_init(bool init_domain) 812{ 813 struct pmap_aarch64 *pmap_aarch64 = (struct pmap_aarch64*)get_current_pmap(); 814 815 pmap_vnode_mgmt_current_init((struct pmap *)pmap_aarch64); 816 817 return SYS_ERR_OK; 818} 819 820struct vnode_public *pmap_get_vroot(struct pmap *pmap) 821{ 822 struct pmap_aarch64 *pa64 = (struct pmap_aarch64 *)pmap; 823 return &pa64->root.v; 824} 825 826void pmap_set_min_mappable_va(struct pmap *pmap, lvaddr_t minva) 827{ 828 struct pmap_aarch64 *pa64 = (struct pmap_aarch64 *)pmap; 829 pa64->min_mappable_va = minva; 830} 831