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