1 // SPDX-License-Identifier: BSD-2-Clause 2 /* 3 * Copyright (c) 2016, 2022 Linaro Limited 4 */ 5 6 #include <assert.h> 7 #include <kernel/mutex.h> 8 #include <kernel/spinlock.h> 9 #include <kernel/tee_misc.h> 10 #include <kernel/user_mode_ctx.h> 11 #include <mm/core_memprot.h> 12 #include <mm/core_mmu.h> 13 #include <mm/pgt_cache.h> 14 #include <mm/tee_pager.h> 15 #include <stdlib.h> 16 #include <trace.h> 17 #include <util.h> 18 19 /* 20 * With pager enabled we allocate page table from the pager. 21 * 22 * For LPAE each page table is a complete page which is allocated and freed 23 * using the interface provided by the pager. 24 * 25 * For compat v7 page tables there's room for four page table in one page 26 * so we need to keep track of how much of an allocated page is used. When 27 * a page is completely unused it's returned to the pager. 28 * 29 * With pager disabled we have a static allocation of page tables instead. 30 * 31 * In all cases we limit the number of active page tables to 32 * PGT_CACHE_SIZE. This pool of page tables are shared between all 33 * threads. In case a thread can't allocate the needed number of pager 34 * tables it will release all its current tables and wait for some more to 35 * be freed. A threads allocated tables are freed each time a TA is 36 * unmapped so each thread should be able to allocate the needed tables in 37 * turn if needed. 38 */ 39 40 #if defined(CFG_CORE_PREALLOC_EL0_TBLS) || \ 41 (defined(CFG_WITH_PAGER) && !defined(CFG_WITH_LPAE)) 42 struct pgt_parent { 43 size_t num_used; 44 struct pgt_cache pgt_cache; 45 #if defined(CFG_CORE_PREALLOC_EL0_TBLS) 46 tee_mm_entry_t *mm; 47 SLIST_ENTRY(pgt_parent) link; 48 #endif 49 }; 50 #endif 51 52 #if defined(CFG_CORE_PREALLOC_EL0_TBLS) 53 54 /* 55 * Pick something large enough that tee_mm_alloc() doesn't have to be 56 * called for each needed translation table. 57 */ 58 #define PGT_PARENT_SIZE (4 * SMALL_PAGE_SIZE) 59 #define PGT_PARENT_TBL_COUNT (PGT_PARENT_SIZE / PGT_SIZE) 60 61 SLIST_HEAD(pgt_parent_list, pgt_parent); 62 static struct pgt_parent_list parent_list = SLIST_HEAD_INITIALIZER(parent_list); 63 static unsigned int parent_spinlock = SPINLOCK_UNLOCK; 64 65 static void free_pgt(struct pgt *pgt) 66 { 67 struct pgt_parent *parent = NULL; 68 uint32_t exceptions = 0; 69 70 exceptions = cpu_spin_lock_xsave(&parent_spinlock); 71 72 assert(pgt && pgt->parent); 73 parent = pgt->parent; 74 assert(parent->num_used <= PGT_PARENT_TBL_COUNT && 75 parent->num_used > 0); 76 if (parent->num_used == PGT_PARENT_TBL_COUNT) 77 SLIST_INSERT_HEAD(&parent_list, parent, link); 78 parent->num_used--; 79 80 if (!parent->num_used && SLIST_NEXT(SLIST_FIRST(&parent_list), link)) { 81 /* 82 * If this isn't the last pgt_parent with free entries we 83 * can free this. 84 */ 85 SLIST_REMOVE(&parent_list, parent, pgt_parent, link); 86 tee_mm_free(parent->mm); 87 free(parent); 88 } else { 89 SLIST_INSERT_HEAD(&parent->pgt_cache, pgt, link); 90 pgt->vabase = 0; 91 pgt->populated = false; 92 } 93 94 cpu_spin_unlock_xrestore(&parent_spinlock, exceptions); 95 } 96 97 static struct pgt_parent *alloc_pgt_parent(void) 98 { 99 struct pgt_parent *parent = NULL; 100 struct pgt *pgt = NULL; 101 uint8_t *tbl = NULL; 102 size_t sz = 0; 103 size_t n = 0; 104 105 sz = sizeof(*parent) + sizeof(*pgt) * PGT_PARENT_TBL_COUNT; 106 parent = calloc(1, sz); 107 if (!parent) 108 return NULL; 109 parent->mm = tee_mm_alloc(&tee_mm_sec_ddr, PGT_PARENT_SIZE); 110 if (!parent->mm) { 111 free(parent); 112 return NULL; 113 } 114 tbl = phys_to_virt(tee_mm_get_smem(parent->mm), MEM_AREA_TA_RAM, 115 PGT_PARENT_SIZE); 116 assert(tbl); /* "can't fail" */ 117 118 SLIST_INIT(&parent->pgt_cache); 119 pgt = (struct pgt *)(parent + 1); 120 for (n = 0; n < PGT_PARENT_TBL_COUNT; n++) { 121 pgt[n].parent = parent; 122 pgt[n].tbl = tbl + n * PGT_SIZE; 123 SLIST_INSERT_HEAD(&parent->pgt_cache, pgt + n, link); 124 } 125 126 return parent; 127 } 128 129 static struct pgt *alloc_pgt(vaddr_t vabase) 130 { 131 struct pgt_parent *parent = NULL; 132 uint32_t exceptions = 0; 133 struct pgt *pgt = NULL; 134 135 exceptions = cpu_spin_lock_xsave(&parent_spinlock); 136 137 parent = SLIST_FIRST(&parent_list); 138 if (!parent) { 139 parent = alloc_pgt_parent(); 140 if (!parent) 141 goto out; 142 143 SLIST_INSERT_HEAD(&parent_list, parent, link); 144 } 145 146 pgt = SLIST_FIRST(&parent->pgt_cache); 147 SLIST_REMOVE_HEAD(&parent->pgt_cache, link); 148 parent->num_used++; 149 assert(pgt && parent->num_used <= PGT_PARENT_TBL_COUNT); 150 if (parent->num_used == PGT_PARENT_TBL_COUNT) 151 SLIST_REMOVE_HEAD(&parent_list, link); 152 153 pgt->vabase = vabase; 154 out: 155 cpu_spin_unlock_xrestore(&parent_spinlock, exceptions); 156 return pgt; 157 } 158 159 static bool pgt_entry_matches(struct pgt *p, vaddr_t begin, vaddr_t last) 160 { 161 if (!p) 162 return false; 163 if (last <= begin) 164 return false; 165 return core_is_buffer_inside(p->vabase, CORE_MMU_PGDIR_SIZE, begin, 166 last - begin); 167 } 168 169 void pgt_flush_range(struct user_mode_ctx *uctx, vaddr_t begin, vaddr_t last) 170 { 171 struct pgt_cache *pgt_cache = &uctx->pgt_cache; 172 struct pgt *next_p = NULL; 173 struct pgt *p = NULL; 174 175 /* 176 * Do the special case where the first element in the list is 177 * removed first. 178 */ 179 p = SLIST_FIRST(pgt_cache); 180 while (pgt_entry_matches(p, begin, last)) { 181 SLIST_REMOVE_HEAD(pgt_cache, link); 182 free_pgt(p); 183 p = SLIST_FIRST(pgt_cache); 184 } 185 186 /* 187 * p either points to the first element in the list or it's NULL, 188 * if NULL the list is empty and we're done. 189 */ 190 if (!p) 191 return; 192 193 /* 194 * Do the common case where the next element in the list is 195 * removed. 196 */ 197 while (true) { 198 next_p = SLIST_NEXT(p, link); 199 if (!next_p) 200 break; 201 if (pgt_entry_matches(next_p, begin, last)) { 202 SLIST_REMOVE_AFTER(p, link); 203 free_pgt(next_p); 204 continue; 205 } 206 207 p = SLIST_NEXT(p, link); 208 } 209 } 210 211 void pgt_flush(struct user_mode_ctx *uctx) 212 { 213 struct pgt_cache *pgt_cache = &uctx->pgt_cache; 214 struct pgt *p = NULL; 215 216 while (true) { 217 p = SLIST_FIRST(pgt_cache); 218 if (!p) 219 break; 220 SLIST_REMOVE_HEAD(pgt_cache, link); 221 free_pgt(p); 222 } 223 } 224 225 void pgt_clear_range(struct user_mode_ctx *uctx, vaddr_t begin, vaddr_t end) 226 { 227 struct pgt_cache *pgt_cache = &uctx->pgt_cache; 228 struct pgt *p = NULL; 229 #ifdef CFG_WITH_LPAE 230 uint64_t *tbl = NULL; 231 #else 232 uint32_t *tbl = NULL; 233 #endif 234 unsigned int idx = 0; 235 unsigned int n = 0; 236 237 SLIST_FOREACH(p, pgt_cache, link) { 238 vaddr_t b = MAX(p->vabase, begin); 239 vaddr_t e = MIN(p->vabase + CORE_MMU_PGDIR_SIZE, end); 240 241 if (b >= e) 242 continue; 243 244 tbl = p->tbl; 245 idx = (b - p->vabase) / SMALL_PAGE_SIZE; 246 n = (e - b) / SMALL_PAGE_SIZE; 247 memset(tbl + idx, 0, n * sizeof(*tbl)); 248 } 249 } 250 251 static struct pgt *prune_before_va(struct pgt_cache *pgt_cache, struct pgt *p, 252 struct pgt *pp, vaddr_t va) 253 { 254 while (p && p->vabase < va) { 255 if (pp) { 256 assert(p == SLIST_NEXT(pp, link)); 257 SLIST_REMOVE_AFTER(pp, link); 258 free_pgt(p); 259 p = SLIST_NEXT(pp, link); 260 } else { 261 assert(p == SLIST_FIRST(pgt_cache)); 262 SLIST_REMOVE_HEAD(pgt_cache, link); 263 free_pgt(p); 264 p = SLIST_FIRST(pgt_cache); 265 } 266 } 267 268 return p; 269 } 270 271 bool pgt_check_avail(struct user_mode_ctx *uctx) 272 { 273 struct pgt_cache *pgt_cache = &uctx->pgt_cache; 274 struct vm_info *vm_info = &uctx->vm_info; 275 struct pgt *p = SLIST_FIRST(pgt_cache); 276 struct vm_region *r = NULL; 277 struct pgt *pp = NULL; 278 vaddr_t va = 0; 279 bool p_used = false; 280 281 /* 282 * Prune unused tables. This is normally not needed since 283 * pgt_flush_range() does this too, but in the error path of for 284 * instance vm_remap() such calls may not be done. So for increased 285 * robustness remove all unused translation tables before we may 286 * allocate new ones. 287 */ 288 TAILQ_FOREACH(r, &vm_info->regions, link) { 289 for (va = ROUNDDOWN(r->va, CORE_MMU_PGDIR_SIZE); 290 va < r->va + r->size; va += CORE_MMU_PGDIR_SIZE) { 291 if (!p_used) 292 p = prune_before_va(pgt_cache, p, pp, va); 293 if (!p) 294 goto prune_done; 295 296 if (p->vabase < va) { 297 pp = p; 298 p = SLIST_NEXT(pp, link); 299 if (!p) 300 goto prune_done; 301 p_used = false; 302 } 303 304 if (p->vabase == va) 305 p_used = true; 306 } 307 } 308 prune_done: 309 310 p = SLIST_FIRST(pgt_cache); 311 pp = NULL; 312 TAILQ_FOREACH(r, &vm_info->regions, link) { 313 for (va = ROUNDDOWN(r->va, CORE_MMU_PGDIR_SIZE); 314 va < r->va + r->size; va += CORE_MMU_PGDIR_SIZE) { 315 if (p && p->vabase < va) { 316 pp = p; 317 p = SLIST_NEXT(pp, link); 318 } 319 320 if (p) { 321 if (p->vabase == va) 322 continue; 323 assert(p->vabase > va); 324 } 325 326 p = alloc_pgt(va); 327 if (!p) 328 return false; 329 330 if (pp) 331 SLIST_INSERT_AFTER(pp, p, link); 332 else 333 SLIST_INSERT_HEAD(pgt_cache, p, link); 334 } 335 } 336 337 return true; 338 } 339 #else /* !CFG_CORE_PREALLOC_EL0_TBLS */ 340 341 #if defined(CFG_WITH_PAGER) && !defined(CFG_WITH_LPAE) 342 static struct pgt_parent pgt_parents[PGT_CACHE_SIZE / PGT_NUM_PGT_PER_PAGE]; 343 #else 344 345 static struct pgt_cache pgt_free_list = SLIST_HEAD_INITIALIZER(pgt_free_list); 346 #endif 347 348 /* 349 * When a user TA context is temporarily unmapped the used struct pgt's of 350 * the context (page tables holding valid physical pages) are saved in this 351 * cache in the hope that it will remain in the cache when the context is 352 * mapped again. 353 */ 354 static struct pgt_cache pgt_cache_list = SLIST_HEAD_INITIALIZER(pgt_cache_list); 355 356 static struct pgt pgt_entries[PGT_CACHE_SIZE]; 357 358 static struct mutex pgt_mu = MUTEX_INITIALIZER; 359 static struct condvar pgt_cv = CONDVAR_INITIALIZER; 360 361 #if defined(CFG_WITH_PAGER) && defined(CFG_WITH_LPAE) 362 /* 363 * Simple allocation of translation tables from pager, one translation 364 * table is one page. 365 */ 366 void pgt_init(void) 367 { 368 size_t n; 369 370 for (n = 0; n < PGT_CACHE_SIZE; n++) { 371 struct pgt *p = pgt_entries + n; 372 373 p->tbl = tee_pager_alloc(PGT_SIZE); 374 SLIST_INSERT_HEAD(&pgt_free_list, p, link); 375 } 376 } 377 #elif defined(CFG_WITH_PAGER) && !defined(CFG_WITH_LPAE) 378 /* 379 * Four translation tables per page -> need to keep track of the page 380 * allocated from the pager. 381 */ 382 void pgt_init(void) 383 { 384 size_t n; 385 size_t m; 386 387 COMPILE_TIME_ASSERT(PGT_CACHE_SIZE % PGT_NUM_PGT_PER_PAGE == 0); 388 COMPILE_TIME_ASSERT(PGT_SIZE * PGT_NUM_PGT_PER_PAGE == SMALL_PAGE_SIZE); 389 390 for (n = 0; n < ARRAY_SIZE(pgt_parents); n++) { 391 uint8_t *tbl = tee_pager_alloc(SMALL_PAGE_SIZE); 392 393 SLIST_INIT(&pgt_parents[n].pgt_cache); 394 for (m = 0; m < PGT_NUM_PGT_PER_PAGE; m++) { 395 struct pgt *p = pgt_entries + 396 n * PGT_NUM_PGT_PER_PAGE + m; 397 398 p->tbl = tbl + m * PGT_SIZE; 399 p->parent = &pgt_parents[n]; 400 SLIST_INSERT_HEAD(&pgt_parents[n].pgt_cache, p, link); 401 } 402 } 403 } 404 #else 405 /* Static allocation of translation tables */ 406 void pgt_init(void) 407 { 408 /* 409 * We're putting this in .nozi.* instead of .bss because .nozi.* already 410 * has a large alignment, while .bss has a small alignment. The current 411 * link script is optimized for small alignment in .bss 412 */ 413 static uint8_t pgt_tables[PGT_CACHE_SIZE][PGT_SIZE] 414 __aligned(PGT_SIZE) __section(".nozi.pgt_cache"); 415 size_t n; 416 417 for (n = 0; n < ARRAY_SIZE(pgt_tables); n++) { 418 struct pgt *p = pgt_entries + n; 419 420 p->tbl = pgt_tables[n]; 421 SLIST_INSERT_HEAD(&pgt_free_list, p, link); 422 } 423 } 424 #endif 425 426 #if defined(CFG_WITH_LPAE) || !defined(CFG_WITH_PAGER) 427 /* Simple allocation of translation tables from pager or static allocation */ 428 static struct pgt *pop_from_free_list(void) 429 { 430 struct pgt *p = SLIST_FIRST(&pgt_free_list); 431 432 if (p) { 433 SLIST_REMOVE_HEAD(&pgt_free_list, link); 434 memset(p->tbl, 0, PGT_SIZE); 435 p->populated = false; 436 } 437 return p; 438 } 439 440 static void push_to_free_list(struct pgt *p) 441 { 442 SLIST_INSERT_HEAD(&pgt_free_list, p, link); 443 #if defined(CFG_WITH_PAGER) 444 tee_pager_release_phys(p->tbl, PGT_SIZE); 445 #endif 446 } 447 #else 448 /* 449 * Four translation tables per page -> need to keep track of the page 450 * allocated from the pager. 451 */ 452 static struct pgt *pop_from_free_list(void) 453 { 454 size_t n; 455 456 for (n = 0; n < ARRAY_SIZE(pgt_parents); n++) { 457 struct pgt *p = SLIST_FIRST(&pgt_parents[n].pgt_cache); 458 459 if (p) { 460 SLIST_REMOVE_HEAD(&pgt_parents[n].pgt_cache, link); 461 pgt_parents[n].num_used++; 462 memset(p->tbl, 0, PGT_SIZE); 463 p->populated = false; 464 return p; 465 } 466 } 467 return NULL; 468 } 469 470 static void push_to_free_list(struct pgt *p) 471 { 472 SLIST_INSERT_HEAD(&p->parent->pgt_cache, p, link); 473 assert(p->parent->num_used > 0); 474 p->parent->num_used--; 475 if (!p->parent->num_used) { 476 vaddr_t va = (vaddr_t)p->tbl & ~SMALL_PAGE_MASK; 477 478 tee_pager_release_phys((void *)va, SMALL_PAGE_SIZE); 479 } 480 } 481 #endif 482 483 static void push_to_cache_list(struct pgt *pgt) 484 { 485 SLIST_INSERT_HEAD(&pgt_cache_list, pgt, link); 486 } 487 488 static bool match_pgt(struct pgt *pgt, vaddr_t vabase, void *ctx) 489 { 490 return pgt->ctx == ctx && pgt->vabase == vabase; 491 } 492 493 static struct pgt *pop_from_cache_list(vaddr_t vabase, void *ctx) 494 { 495 struct pgt *pgt; 496 struct pgt *p; 497 498 pgt = SLIST_FIRST(&pgt_cache_list); 499 if (!pgt) 500 return NULL; 501 if (match_pgt(pgt, vabase, ctx)) { 502 SLIST_REMOVE_HEAD(&pgt_cache_list, link); 503 return pgt; 504 } 505 506 while (true) { 507 p = SLIST_NEXT(pgt, link); 508 if (!p) 509 break; 510 if (match_pgt(p, vabase, ctx)) { 511 SLIST_REMOVE_AFTER(pgt, link); 512 break; 513 } 514 pgt = p; 515 } 516 return p; 517 } 518 519 static uint16_t get_num_used_entries(struct pgt *pgt __maybe_unused) 520 { 521 #ifdef CFG_PAGED_USER_TA 522 return pgt->num_used_entries; 523 #else 524 return 0; 525 #endif 526 } 527 528 static struct pgt *pop_least_used_from_cache_list(void) 529 { 530 struct pgt *pgt = NULL; 531 struct pgt *p_prev = NULL; 532 size_t least_used = 0; 533 size_t next_used = 0; 534 535 pgt = SLIST_FIRST(&pgt_cache_list); 536 if (!pgt) 537 return NULL; 538 least_used = get_num_used_entries(pgt); 539 540 while (true) { 541 if (!SLIST_NEXT(pgt, link)) 542 break; 543 next_used = get_num_used_entries(SLIST_NEXT(pgt, link)); 544 if (next_used <= least_used) { 545 p_prev = pgt; 546 least_used = next_used; 547 } 548 pgt = SLIST_NEXT(pgt, link); 549 } 550 551 if (p_prev) { 552 pgt = SLIST_NEXT(p_prev, link); 553 SLIST_REMOVE_AFTER(p_prev, link); 554 } else { 555 pgt = SLIST_FIRST(&pgt_cache_list); 556 SLIST_REMOVE_HEAD(&pgt_cache_list, link); 557 } 558 return pgt; 559 } 560 561 static void pgt_free_unlocked(struct pgt_cache *pgt_cache) 562 { 563 while (!SLIST_EMPTY(pgt_cache)) { 564 struct pgt *p = SLIST_FIRST(pgt_cache); 565 566 SLIST_REMOVE_HEAD(pgt_cache, link); 567 568 /* 569 * With paging enabled we free all tables which doesn't 570 * refer to any paged pages any longer. This reduces the 571 * pressure the pool of physical pages. 572 */ 573 if (IS_ENABLED(CFG_PAGED_USER_TA) && !get_num_used_entries(p)) { 574 tee_pager_pgt_save_and_release_entries(p); 575 p->ctx = NULL; 576 p->vabase = 0; 577 578 push_to_free_list(p); 579 continue; 580 } 581 582 push_to_cache_list(p); 583 } 584 } 585 586 static struct pgt *pop_from_some_list(vaddr_t vabase, void *ctx) 587 { 588 struct pgt *p = pop_from_cache_list(vabase, ctx); 589 590 if (p) 591 return p; 592 p = pop_from_free_list(); 593 if (!p) { 594 p = pop_least_used_from_cache_list(); 595 if (!p) 596 return NULL; 597 tee_pager_pgt_save_and_release_entries(p); 598 memset(p->tbl, 0, PGT_SIZE); 599 p->populated = false; 600 } 601 p->ctx = ctx; 602 p->vabase = vabase; 603 return p; 604 } 605 606 void pgt_flush(struct user_mode_ctx *uctx) 607 { 608 struct ts_ctx *ctx = uctx->ts_ctx; 609 struct pgt *pp = NULL; 610 struct pgt *p = NULL; 611 612 mutex_lock(&pgt_mu); 613 614 while (true) { 615 p = SLIST_FIRST(&pgt_cache_list); 616 if (!p) 617 goto out; 618 if (p->ctx != ctx) 619 break; 620 SLIST_REMOVE_HEAD(&pgt_cache_list, link); 621 tee_pager_pgt_save_and_release_entries(p); 622 p->ctx = NULL; 623 p->vabase = 0; 624 push_to_free_list(p); 625 } 626 627 pp = p; 628 while (true) { 629 p = SLIST_NEXT(pp, link); 630 if (!p) 631 break; 632 if (p->ctx == ctx) { 633 SLIST_REMOVE_AFTER(pp, link); 634 tee_pager_pgt_save_and_release_entries(p); 635 p->ctx = NULL; 636 p->vabase = 0; 637 push_to_free_list(p); 638 } else { 639 pp = p; 640 } 641 } 642 643 out: 644 mutex_unlock(&pgt_mu); 645 } 646 647 static void flush_pgt_entry(struct pgt *p) 648 { 649 tee_pager_pgt_save_and_release_entries(p); 650 p->ctx = NULL; 651 p->vabase = 0; 652 } 653 654 static bool pgt_entry_matches(struct pgt *p, void *ctx, vaddr_t begin, 655 vaddr_t last) 656 { 657 if (!p) 658 return false; 659 if (p->ctx != ctx) 660 return false; 661 if (last <= begin) 662 return false; 663 if (!core_is_buffer_inside(p->vabase, CORE_MMU_PGDIR_SIZE, begin, 664 last - begin)) 665 return false; 666 667 return true; 668 } 669 670 static void flush_ctx_range_from_list(struct pgt_cache *pgt_cache, void *ctx, 671 vaddr_t begin, vaddr_t last) 672 { 673 struct pgt *p; 674 struct pgt *next_p; 675 676 /* 677 * Do the special case where the first element in the list is 678 * removed first. 679 */ 680 p = SLIST_FIRST(pgt_cache); 681 while (pgt_entry_matches(p, ctx, begin, last)) { 682 flush_pgt_entry(p); 683 SLIST_REMOVE_HEAD(pgt_cache, link); 684 push_to_free_list(p); 685 p = SLIST_FIRST(pgt_cache); 686 } 687 688 /* 689 * p either points to the first element in the list or it's NULL, 690 * if NULL the list is empty and we're done. 691 */ 692 if (!p) 693 return; 694 695 /* 696 * Do the common case where the next element in the list is 697 * removed. 698 */ 699 while (true) { 700 next_p = SLIST_NEXT(p, link); 701 if (!next_p) 702 break; 703 if (pgt_entry_matches(next_p, ctx, begin, last)) { 704 flush_pgt_entry(next_p); 705 SLIST_REMOVE_AFTER(p, link); 706 push_to_free_list(next_p); 707 continue; 708 } 709 710 p = SLIST_NEXT(p, link); 711 } 712 } 713 714 void pgt_flush_range(struct user_mode_ctx *uctx, vaddr_t begin, vaddr_t last) 715 { 716 struct pgt_cache *pgt_cache = &uctx->pgt_cache; 717 struct ts_ctx *ctx = uctx->ts_ctx; 718 719 mutex_lock(&pgt_mu); 720 721 flush_ctx_range_from_list(pgt_cache, ctx, begin, last); 722 flush_ctx_range_from_list(&pgt_cache_list, ctx, begin, last); 723 724 condvar_broadcast(&pgt_cv); 725 mutex_unlock(&pgt_mu); 726 } 727 728 static void clear_ctx_range_from_list(struct pgt_cache *pgt_cache, 729 void *ctx, vaddr_t begin, vaddr_t end) 730 { 731 struct pgt *p = NULL; 732 #ifdef CFG_WITH_LPAE 733 uint64_t *tbl = NULL; 734 #else 735 uint32_t *tbl = NULL; 736 #endif 737 unsigned int idx = 0; 738 unsigned int n = 0; 739 740 SLIST_FOREACH(p, pgt_cache, link) { 741 vaddr_t b = MAX(p->vabase, begin); 742 vaddr_t e = MIN(p->vabase + CORE_MMU_PGDIR_SIZE, end); 743 744 if (p->ctx != ctx) 745 continue; 746 if (b >= e) 747 continue; 748 749 tbl = p->tbl; 750 idx = (b - p->vabase) / SMALL_PAGE_SIZE; 751 n = (e - b) / SMALL_PAGE_SIZE; 752 memset(tbl + idx, 0, n * sizeof(*tbl)); 753 } 754 } 755 756 void pgt_clear_range(struct user_mode_ctx *uctx, vaddr_t begin, vaddr_t end) 757 { 758 struct pgt_cache *pgt_cache = &uctx->pgt_cache; 759 struct ts_ctx *ctx = uctx->ts_ctx; 760 761 mutex_lock(&pgt_mu); 762 763 clear_ctx_range_from_list(pgt_cache, ctx, begin, end); 764 clear_ctx_range_from_list(&pgt_cache_list, ctx, begin, end); 765 766 mutex_unlock(&pgt_mu); 767 } 768 769 static bool pgt_alloc_unlocked(struct pgt_cache *pgt_cache, struct ts_ctx *ctx, 770 struct vm_info *vm_info) 771 { 772 struct vm_region *r = NULL; 773 struct pgt *pp = NULL; 774 struct pgt *p = NULL; 775 vaddr_t va = 0; 776 777 TAILQ_FOREACH(r, &vm_info->regions, link) { 778 for (va = ROUNDDOWN(r->va, CORE_MMU_PGDIR_SIZE); 779 va < r->va + r->size; va += CORE_MMU_PGDIR_SIZE) { 780 if (p && p->vabase == va) 781 continue; 782 p = pop_from_some_list(va, ctx); 783 if (!p) { 784 pgt_free_unlocked(pgt_cache); 785 return false; 786 } 787 if (pp) 788 SLIST_INSERT_AFTER(pp, p, link); 789 else 790 SLIST_INSERT_HEAD(pgt_cache, p, link); 791 pp = p; 792 } 793 } 794 795 return true; 796 } 797 798 bool pgt_check_avail(struct user_mode_ctx *uctx) 799 { 800 struct vm_info *vm_info = &uctx->vm_info; 801 struct vm_region *r = NULL; 802 size_t tbl_count = 0; 803 vaddr_t last_va = 0; 804 vaddr_t va = 0; 805 806 TAILQ_FOREACH(r, &vm_info->regions, link) { 807 for (va = ROUNDDOWN(r->va, CORE_MMU_PGDIR_SIZE); 808 va < r->va + r->size; va += CORE_MMU_PGDIR_SIZE) { 809 if (va == last_va) 810 continue; 811 tbl_count++; 812 last_va = va; 813 } 814 } 815 816 return tbl_count <= PGT_CACHE_SIZE; 817 } 818 819 void pgt_get_all(struct user_mode_ctx *uctx) 820 { 821 struct pgt_cache *pgt_cache = &uctx->pgt_cache; 822 struct vm_info *vm_info = &uctx->vm_info; 823 824 if (TAILQ_EMPTY(&vm_info->regions)) 825 return; 826 827 mutex_lock(&pgt_mu); 828 829 pgt_free_unlocked(pgt_cache); 830 while (!pgt_alloc_unlocked(pgt_cache, uctx->ts_ctx, vm_info)) { 831 assert(pgt_check_avail(uctx)); 832 DMSG("Waiting for page tables"); 833 condvar_broadcast(&pgt_cv); 834 condvar_wait(&pgt_cv, &pgt_mu); 835 } 836 837 mutex_unlock(&pgt_mu); 838 } 839 840 void pgt_put_all(struct user_mode_ctx *uctx) 841 { 842 struct pgt_cache *pgt_cache = &uctx->pgt_cache; 843 844 if (SLIST_EMPTY(pgt_cache)) 845 return; 846 847 mutex_lock(&pgt_mu); 848 849 pgt_free_unlocked(pgt_cache); 850 851 condvar_broadcast(&pgt_cv); 852 mutex_unlock(&pgt_mu); 853 } 854 855 struct pgt *pgt_pop_from_cache_list(vaddr_t vabase, struct ts_ctx *ctx) 856 { 857 struct pgt *pgt = NULL; 858 859 mutex_lock(&pgt_mu); 860 pgt = pop_from_cache_list(vabase, ctx); 861 mutex_unlock(&pgt_mu); 862 863 return pgt; 864 } 865 866 void pgt_push_to_cache_list(struct pgt *pgt) 867 { 868 mutex_lock(&pgt_mu); 869 push_to_cache_list(pgt); 870 mutex_unlock(&pgt_mu); 871 } 872 873 #endif /* !CFG_CORE_PREALLOC_EL0_TBLS */ 874