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