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