xref: /optee_os/core/mm/pgt_cache.c (revision 9f34db38245c9b3a4e6e7e63eb78a75e23ab2da3)
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