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