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 #define PGT_CACHE_SIZE ROUNDUP(CFG_PGT_CACHE_ENTRIES, PGT_NUM_PGT_PER_PAGE) 342 343 #if defined(CFG_WITH_PAGER) && !defined(CFG_WITH_LPAE) 344 static struct pgt_parent pgt_parents[PGT_CACHE_SIZE / PGT_NUM_PGT_PER_PAGE]; 345 #else 346 347 static struct pgt_cache pgt_free_list = SLIST_HEAD_INITIALIZER(pgt_free_list); 348 #endif 349 350 /* 351 * When a user TA context is temporarily unmapped the used struct pgt's of 352 * the context (page tables holding valid physical pages) are saved in this 353 * cache in the hope that it will remain in the cache when the context is 354 * mapped again. 355 */ 356 static struct pgt_cache pgt_cache_list = SLIST_HEAD_INITIALIZER(pgt_cache_list); 357 358 static struct pgt pgt_entries[PGT_CACHE_SIZE]; 359 360 static struct mutex pgt_mu = MUTEX_INITIALIZER; 361 static struct condvar pgt_cv = CONDVAR_INITIALIZER; 362 363 #if defined(CFG_WITH_PAGER) && defined(CFG_WITH_LPAE) 364 /* 365 * Simple allocation of translation tables from pager, one translation 366 * table is one page. 367 */ 368 void pgt_init(void) 369 { 370 size_t n; 371 372 for (n = 0; n < PGT_CACHE_SIZE; n++) { 373 struct pgt *p = pgt_entries + n; 374 375 p->tbl = tee_pager_alloc(PGT_SIZE); 376 SLIST_INSERT_HEAD(&pgt_free_list, p, link); 377 } 378 } 379 #elif defined(CFG_WITH_PAGER) && !defined(CFG_WITH_LPAE) 380 /* 381 * Four translation tables per page -> need to keep track of the page 382 * allocated from the pager. 383 */ 384 void pgt_init(void) 385 { 386 size_t n; 387 size_t m; 388 389 COMPILE_TIME_ASSERT(PGT_CACHE_SIZE % PGT_NUM_PGT_PER_PAGE == 0); 390 COMPILE_TIME_ASSERT(PGT_SIZE * PGT_NUM_PGT_PER_PAGE == SMALL_PAGE_SIZE); 391 392 for (n = 0; n < ARRAY_SIZE(pgt_parents); n++) { 393 uint8_t *tbl = tee_pager_alloc(SMALL_PAGE_SIZE); 394 395 SLIST_INIT(&pgt_parents[n].pgt_cache); 396 for (m = 0; m < PGT_NUM_PGT_PER_PAGE; m++) { 397 struct pgt *p = pgt_entries + 398 n * PGT_NUM_PGT_PER_PAGE + m; 399 400 p->tbl = tbl + m * PGT_SIZE; 401 p->parent = &pgt_parents[n]; 402 SLIST_INSERT_HEAD(&pgt_parents[n].pgt_cache, p, link); 403 } 404 } 405 } 406 #else 407 /* Static allocation of translation tables */ 408 void pgt_init(void) 409 { 410 /* 411 * We're putting this in .nozi.* instead of .bss because .nozi.* already 412 * has a large alignment, while .bss has a small alignment. The current 413 * link script is optimized for small alignment in .bss 414 */ 415 static uint8_t pgt_tables[PGT_CACHE_SIZE][PGT_SIZE] 416 __aligned(PGT_SIZE) __section(".nozi.pgt_cache"); 417 size_t n; 418 419 for (n = 0; n < ARRAY_SIZE(pgt_tables); n++) { 420 struct pgt *p = pgt_entries + n; 421 422 p->tbl = pgt_tables[n]; 423 SLIST_INSERT_HEAD(&pgt_free_list, p, link); 424 } 425 } 426 #endif 427 428 #if defined(CFG_WITH_LPAE) || !defined(CFG_WITH_PAGER) 429 /* Simple allocation of translation tables from pager or static allocation */ 430 static struct pgt *pop_from_free_list(void) 431 { 432 struct pgt *p = SLIST_FIRST(&pgt_free_list); 433 434 if (p) { 435 SLIST_REMOVE_HEAD(&pgt_free_list, link); 436 memset(p->tbl, 0, PGT_SIZE); 437 p->populated = false; 438 } 439 return p; 440 } 441 442 static void push_to_free_list(struct pgt *p) 443 { 444 SLIST_INSERT_HEAD(&pgt_free_list, p, link); 445 #if defined(CFG_WITH_PAGER) 446 tee_pager_release_phys(p->tbl, PGT_SIZE); 447 #endif 448 } 449 #else 450 /* 451 * Four translation tables per page -> need to keep track of the page 452 * allocated from the pager. 453 */ 454 static struct pgt *pop_from_free_list(void) 455 { 456 size_t n; 457 458 for (n = 0; n < ARRAY_SIZE(pgt_parents); n++) { 459 struct pgt *p = SLIST_FIRST(&pgt_parents[n].pgt_cache); 460 461 if (p) { 462 SLIST_REMOVE_HEAD(&pgt_parents[n].pgt_cache, link); 463 pgt_parents[n].num_used++; 464 memset(p->tbl, 0, PGT_SIZE); 465 p->populated = false; 466 return p; 467 } 468 } 469 return NULL; 470 } 471 472 static void push_to_free_list(struct pgt *p) 473 { 474 SLIST_INSERT_HEAD(&p->parent->pgt_cache, p, link); 475 assert(p->parent->num_used > 0); 476 p->parent->num_used--; 477 if (!p->parent->num_used) { 478 vaddr_t va = (vaddr_t)p->tbl & ~SMALL_PAGE_MASK; 479 480 tee_pager_release_phys((void *)va, SMALL_PAGE_SIZE); 481 } 482 } 483 #endif 484 485 static void push_to_cache_list(struct pgt *pgt) 486 { 487 SLIST_INSERT_HEAD(&pgt_cache_list, pgt, link); 488 } 489 490 static bool match_pgt(struct pgt *pgt, vaddr_t vabase, void *ctx) 491 { 492 return pgt->ctx == ctx && pgt->vabase == vabase; 493 } 494 495 static struct pgt *pop_from_cache_list(vaddr_t vabase, void *ctx) 496 { 497 struct pgt *pgt; 498 struct pgt *p; 499 500 pgt = SLIST_FIRST(&pgt_cache_list); 501 if (!pgt) 502 return NULL; 503 if (match_pgt(pgt, vabase, ctx)) { 504 SLIST_REMOVE_HEAD(&pgt_cache_list, link); 505 return pgt; 506 } 507 508 while (true) { 509 p = SLIST_NEXT(pgt, link); 510 if (!p) 511 break; 512 if (match_pgt(p, vabase, ctx)) { 513 SLIST_REMOVE_AFTER(pgt, link); 514 break; 515 } 516 pgt = p; 517 } 518 return p; 519 } 520 521 static uint16_t get_num_used_entries(struct pgt *pgt __maybe_unused) 522 { 523 #ifdef CFG_PAGED_USER_TA 524 return pgt->num_used_entries; 525 #else 526 return 0; 527 #endif 528 } 529 530 static struct pgt *pop_least_used_from_cache_list(void) 531 { 532 struct pgt *pgt = NULL; 533 struct pgt *p_prev = NULL; 534 size_t least_used = 0; 535 size_t next_used = 0; 536 537 pgt = SLIST_FIRST(&pgt_cache_list); 538 if (!pgt) 539 return NULL; 540 least_used = get_num_used_entries(pgt); 541 542 while (true) { 543 if (!SLIST_NEXT(pgt, link)) 544 break; 545 next_used = get_num_used_entries(SLIST_NEXT(pgt, link)); 546 if (next_used <= least_used) { 547 p_prev = pgt; 548 least_used = next_used; 549 } 550 pgt = SLIST_NEXT(pgt, link); 551 } 552 553 if (p_prev) { 554 pgt = SLIST_NEXT(p_prev, link); 555 SLIST_REMOVE_AFTER(p_prev, link); 556 } else { 557 pgt = SLIST_FIRST(&pgt_cache_list); 558 SLIST_REMOVE_HEAD(&pgt_cache_list, link); 559 } 560 return pgt; 561 } 562 563 static void pgt_free_unlocked(struct pgt_cache *pgt_cache) 564 { 565 while (!SLIST_EMPTY(pgt_cache)) { 566 struct pgt *p = SLIST_FIRST(pgt_cache); 567 568 SLIST_REMOVE_HEAD(pgt_cache, link); 569 570 /* 571 * With paging enabled we free all tables which doesn't 572 * refer to any paged pages any longer. This reduces the 573 * pressure the pool of physical pages. 574 */ 575 if (IS_ENABLED(CFG_PAGED_USER_TA) && !get_num_used_entries(p)) { 576 tee_pager_pgt_save_and_release_entries(p); 577 p->ctx = NULL; 578 p->vabase = 0; 579 580 push_to_free_list(p); 581 continue; 582 } 583 584 push_to_cache_list(p); 585 } 586 } 587 588 static struct pgt *pop_from_some_list(vaddr_t vabase, void *ctx) 589 { 590 struct pgt *p = pop_from_cache_list(vabase, ctx); 591 592 if (p) 593 return p; 594 p = pop_from_free_list(); 595 if (!p) { 596 p = pop_least_used_from_cache_list(); 597 if (!p) 598 return NULL; 599 tee_pager_pgt_save_and_release_entries(p); 600 memset(p->tbl, 0, PGT_SIZE); 601 p->populated = false; 602 } 603 p->ctx = ctx; 604 p->vabase = vabase; 605 return p; 606 } 607 608 void pgt_flush(struct user_mode_ctx *uctx) 609 { 610 struct ts_ctx *ctx = uctx->ts_ctx; 611 struct pgt *pp = NULL; 612 struct pgt *p = NULL; 613 614 mutex_lock(&pgt_mu); 615 616 while (true) { 617 p = SLIST_FIRST(&pgt_cache_list); 618 if (!p) 619 goto out; 620 if (p->ctx != ctx) 621 break; 622 SLIST_REMOVE_HEAD(&pgt_cache_list, link); 623 tee_pager_pgt_save_and_release_entries(p); 624 p->ctx = NULL; 625 p->vabase = 0; 626 push_to_free_list(p); 627 } 628 629 pp = p; 630 while (true) { 631 p = SLIST_NEXT(pp, link); 632 if (!p) 633 break; 634 if (p->ctx == ctx) { 635 SLIST_REMOVE_AFTER(pp, link); 636 tee_pager_pgt_save_and_release_entries(p); 637 p->ctx = NULL; 638 p->vabase = 0; 639 push_to_free_list(p); 640 } else { 641 pp = p; 642 } 643 } 644 645 out: 646 mutex_unlock(&pgt_mu); 647 } 648 649 static void flush_pgt_entry(struct pgt *p) 650 { 651 tee_pager_pgt_save_and_release_entries(p); 652 p->ctx = NULL; 653 p->vabase = 0; 654 } 655 656 static bool pgt_entry_matches(struct pgt *p, void *ctx, vaddr_t begin, 657 vaddr_t last) 658 { 659 if (!p) 660 return false; 661 if (p->ctx != ctx) 662 return false; 663 if (last <= begin) 664 return false; 665 if (!core_is_buffer_inside(p->vabase, CORE_MMU_PGDIR_SIZE, begin, 666 last - begin)) 667 return false; 668 669 return true; 670 } 671 672 static void flush_ctx_range_from_list(struct pgt_cache *pgt_cache, void *ctx, 673 vaddr_t begin, vaddr_t last) 674 { 675 struct pgt *p; 676 struct pgt *next_p; 677 678 /* 679 * Do the special case where the first element in the list is 680 * removed first. 681 */ 682 p = SLIST_FIRST(pgt_cache); 683 while (pgt_entry_matches(p, ctx, begin, last)) { 684 flush_pgt_entry(p); 685 SLIST_REMOVE_HEAD(pgt_cache, link); 686 push_to_free_list(p); 687 p = SLIST_FIRST(pgt_cache); 688 } 689 690 /* 691 * p either points to the first element in the list or it's NULL, 692 * if NULL the list is empty and we're done. 693 */ 694 if (!p) 695 return; 696 697 /* 698 * Do the common case where the next element in the list is 699 * removed. 700 */ 701 while (true) { 702 next_p = SLIST_NEXT(p, link); 703 if (!next_p) 704 break; 705 if (pgt_entry_matches(next_p, ctx, begin, last)) { 706 flush_pgt_entry(next_p); 707 SLIST_REMOVE_AFTER(p, link); 708 push_to_free_list(next_p); 709 continue; 710 } 711 712 p = SLIST_NEXT(p, link); 713 } 714 } 715 716 void pgt_flush_range(struct user_mode_ctx *uctx, vaddr_t begin, vaddr_t last) 717 { 718 struct pgt_cache *pgt_cache = &uctx->pgt_cache; 719 struct ts_ctx *ctx = uctx->ts_ctx; 720 721 mutex_lock(&pgt_mu); 722 723 flush_ctx_range_from_list(pgt_cache, ctx, begin, last); 724 flush_ctx_range_from_list(&pgt_cache_list, ctx, begin, last); 725 726 condvar_broadcast(&pgt_cv); 727 mutex_unlock(&pgt_mu); 728 } 729 730 static void clear_ctx_range_from_list(struct pgt_cache *pgt_cache, 731 void *ctx, vaddr_t begin, vaddr_t end) 732 { 733 struct pgt *p = NULL; 734 #ifdef CFG_WITH_LPAE 735 uint64_t *tbl = NULL; 736 #else 737 uint32_t *tbl = NULL; 738 #endif 739 unsigned int idx = 0; 740 unsigned int n = 0; 741 742 SLIST_FOREACH(p, pgt_cache, link) { 743 vaddr_t b = MAX(p->vabase, begin); 744 vaddr_t e = MIN(p->vabase + CORE_MMU_PGDIR_SIZE, end); 745 746 if (p->ctx != ctx) 747 continue; 748 if (b >= e) 749 continue; 750 751 tbl = p->tbl; 752 idx = (b - p->vabase) / SMALL_PAGE_SIZE; 753 n = (e - b) / SMALL_PAGE_SIZE; 754 memset(tbl + idx, 0, n * sizeof(*tbl)); 755 } 756 } 757 758 void pgt_clear_range(struct user_mode_ctx *uctx, vaddr_t begin, vaddr_t end) 759 { 760 struct pgt_cache *pgt_cache = &uctx->pgt_cache; 761 struct ts_ctx *ctx = uctx->ts_ctx; 762 763 mutex_lock(&pgt_mu); 764 765 clear_ctx_range_from_list(pgt_cache, ctx, begin, end); 766 clear_ctx_range_from_list(&pgt_cache_list, ctx, begin, end); 767 768 mutex_unlock(&pgt_mu); 769 } 770 771 static bool pgt_alloc_unlocked(struct pgt_cache *pgt_cache, struct ts_ctx *ctx, 772 struct vm_info *vm_info) 773 { 774 struct vm_region *r = NULL; 775 struct pgt *pp = NULL; 776 struct pgt *p = NULL; 777 vaddr_t va = 0; 778 779 TAILQ_FOREACH(r, &vm_info->regions, link) { 780 for (va = ROUNDDOWN(r->va, CORE_MMU_PGDIR_SIZE); 781 va < r->va + r->size; va += CORE_MMU_PGDIR_SIZE) { 782 if (p && p->vabase == va) 783 continue; 784 p = pop_from_some_list(va, ctx); 785 if (!p) { 786 pgt_free_unlocked(pgt_cache); 787 return false; 788 } 789 if (pp) 790 SLIST_INSERT_AFTER(pp, p, link); 791 else 792 SLIST_INSERT_HEAD(pgt_cache, p, link); 793 pp = p; 794 } 795 } 796 797 return true; 798 } 799 800 bool pgt_check_avail(struct user_mode_ctx *uctx) 801 { 802 struct vm_info *vm_info = &uctx->vm_info; 803 struct vm_region *r = NULL; 804 size_t tbl_count = 0; 805 vaddr_t last_va = 0; 806 vaddr_t va = 0; 807 808 TAILQ_FOREACH(r, &vm_info->regions, link) { 809 for (va = ROUNDDOWN(r->va, CORE_MMU_PGDIR_SIZE); 810 va < r->va + r->size; va += CORE_MMU_PGDIR_SIZE) { 811 if (va == last_va) 812 continue; 813 tbl_count++; 814 last_va = va; 815 } 816 } 817 818 return tbl_count <= PGT_CACHE_SIZE; 819 } 820 821 void pgt_get_all(struct user_mode_ctx *uctx) 822 { 823 struct pgt_cache *pgt_cache = &uctx->pgt_cache; 824 struct vm_info *vm_info = &uctx->vm_info; 825 826 if (TAILQ_EMPTY(&vm_info->regions)) 827 return; 828 829 mutex_lock(&pgt_mu); 830 831 pgt_free_unlocked(pgt_cache); 832 while (!pgt_alloc_unlocked(pgt_cache, uctx->ts_ctx, vm_info)) { 833 assert(pgt_check_avail(uctx)); 834 DMSG("Waiting for page tables"); 835 condvar_broadcast(&pgt_cv); 836 condvar_wait(&pgt_cv, &pgt_mu); 837 } 838 839 mutex_unlock(&pgt_mu); 840 } 841 842 void pgt_put_all(struct user_mode_ctx *uctx) 843 { 844 struct pgt_cache *pgt_cache = &uctx->pgt_cache; 845 846 if (SLIST_EMPTY(pgt_cache)) 847 return; 848 849 mutex_lock(&pgt_mu); 850 851 pgt_free_unlocked(pgt_cache); 852 853 condvar_broadcast(&pgt_cv); 854 mutex_unlock(&pgt_mu); 855 } 856 857 struct pgt *pgt_pop_from_cache_list(vaddr_t vabase, struct ts_ctx *ctx) 858 { 859 struct pgt *pgt = NULL; 860 861 mutex_lock(&pgt_mu); 862 pgt = pop_from_cache_list(vabase, ctx); 863 mutex_unlock(&pgt_mu); 864 865 return pgt; 866 } 867 868 void pgt_push_to_cache_list(struct pgt *pgt) 869 { 870 mutex_lock(&pgt_mu); 871 push_to_cache_list(pgt); 872 mutex_unlock(&pgt_mu); 873 } 874 875 #endif /* !CFG_CORE_PREALLOC_EL0_TBLS */ 876