xref: /optee_os/core/mm/pgt_cache.c (revision f15052a2a293c3a97db986c19359905d83df8a69)
152a75a25SMarouene Boubakri // SPDX-License-Identifier: BSD-2-Clause
252a75a25SMarouene Boubakri /*
352a75a25SMarouene Boubakri  * Copyright (c) 2016, 2022 Linaro Limited
452a75a25SMarouene Boubakri  */
552a75a25SMarouene Boubakri 
652a75a25SMarouene Boubakri #include <assert.h>
752a75a25SMarouene Boubakri #include <kernel/mutex.h>
852a75a25SMarouene Boubakri #include <kernel/spinlock.h>
952a75a25SMarouene Boubakri #include <kernel/tee_misc.h>
1052a75a25SMarouene Boubakri #include <kernel/user_mode_ctx.h>
1152a75a25SMarouene Boubakri #include <mm/core_memprot.h>
1252a75a25SMarouene Boubakri #include <mm/core_mmu.h>
1352a75a25SMarouene Boubakri #include <mm/pgt_cache.h>
1452a75a25SMarouene Boubakri #include <mm/tee_pager.h>
1552a75a25SMarouene Boubakri #include <stdlib.h>
1652a75a25SMarouene Boubakri #include <trace.h>
1752a75a25SMarouene Boubakri #include <util.h>
1852a75a25SMarouene Boubakri 
1952a75a25SMarouene Boubakri /*
2052a75a25SMarouene Boubakri  * With pager enabled we allocate page table from the pager.
2152a75a25SMarouene Boubakri  *
2252a75a25SMarouene Boubakri  * For LPAE each page table is a complete page which is allocated and freed
2352a75a25SMarouene Boubakri  * using the interface provided by the pager.
2452a75a25SMarouene Boubakri  *
2552a75a25SMarouene Boubakri  * For compat v7 page tables there's room for four page table in one page
2652a75a25SMarouene Boubakri  * so we need to keep track of how much of an allocated page is used. When
2752a75a25SMarouene Boubakri  * a page is completely unused it's returned to the pager.
2852a75a25SMarouene Boubakri  *
2952a75a25SMarouene Boubakri  * With pager disabled we have a static allocation of page tables instead.
3052a75a25SMarouene Boubakri  *
3152a75a25SMarouene Boubakri  * In all cases we limit the number of active page tables to
3252a75a25SMarouene Boubakri  * PGT_CACHE_SIZE.  This pool of page tables are shared between all
3352a75a25SMarouene Boubakri  * threads. In case a thread can't allocate the needed number of pager
3452a75a25SMarouene Boubakri  * tables it will release all its current tables and wait for some more to
3552a75a25SMarouene Boubakri  * be freed. A threads allocated tables are freed each time a TA is
3652a75a25SMarouene Boubakri  * unmapped so each thread should be able to allocate the needed tables in
3752a75a25SMarouene Boubakri  * turn if needed.
3852a75a25SMarouene Boubakri  */
3952a75a25SMarouene Boubakri 
4052a75a25SMarouene Boubakri #if defined(CFG_CORE_PREALLOC_EL0_TBLS) || \
4152a75a25SMarouene Boubakri 	(defined(CFG_WITH_PAGER) && !defined(CFG_WITH_LPAE))
4252a75a25SMarouene Boubakri struct pgt_parent {
4352a75a25SMarouene Boubakri 	size_t num_used;
4452a75a25SMarouene Boubakri 	struct pgt_cache pgt_cache;
4552a75a25SMarouene Boubakri #if defined(CFG_CORE_PREALLOC_EL0_TBLS)
4652a75a25SMarouene Boubakri 	tee_mm_entry_t *mm;
4752a75a25SMarouene Boubakri 	SLIST_ENTRY(pgt_parent) link;
4852a75a25SMarouene Boubakri #endif
4952a75a25SMarouene Boubakri };
5052a75a25SMarouene Boubakri #endif
5152a75a25SMarouene Boubakri 
5252a75a25SMarouene Boubakri #if defined(CFG_CORE_PREALLOC_EL0_TBLS)
5352a75a25SMarouene Boubakri 
5452a75a25SMarouene Boubakri /*
5552a75a25SMarouene Boubakri  * Pick something large enough that tee_mm_alloc() doesn't have to be
5652a75a25SMarouene Boubakri  * called for each needed translation table.
5752a75a25SMarouene Boubakri  */
5852a75a25SMarouene Boubakri #define PGT_PARENT_SIZE		(4 * SMALL_PAGE_SIZE)
5952a75a25SMarouene Boubakri #define PGT_PARENT_TBL_COUNT	(PGT_PARENT_SIZE / PGT_SIZE)
6052a75a25SMarouene Boubakri 
6152a75a25SMarouene Boubakri SLIST_HEAD(pgt_parent_list, pgt_parent);
6252a75a25SMarouene Boubakri static struct pgt_parent_list parent_list = SLIST_HEAD_INITIALIZER(parent_list);
6352a75a25SMarouene Boubakri static unsigned int parent_spinlock = SPINLOCK_UNLOCK;
6452a75a25SMarouene Boubakri 
6552a75a25SMarouene Boubakri static void free_pgt(struct pgt *pgt)
6652a75a25SMarouene Boubakri {
6752a75a25SMarouene Boubakri 	struct pgt_parent *parent = NULL;
6852a75a25SMarouene Boubakri 	uint32_t exceptions = 0;
6952a75a25SMarouene Boubakri 
7052a75a25SMarouene Boubakri 	exceptions = cpu_spin_lock_xsave(&parent_spinlock);
7152a75a25SMarouene Boubakri 
7252a75a25SMarouene Boubakri 	assert(pgt && pgt->parent);
7352a75a25SMarouene Boubakri 	parent = pgt->parent;
7452a75a25SMarouene Boubakri 	assert(parent->num_used <= PGT_PARENT_TBL_COUNT &&
7552a75a25SMarouene Boubakri 	       parent->num_used > 0);
7652a75a25SMarouene Boubakri 	if (parent->num_used == PGT_PARENT_TBL_COUNT)
7752a75a25SMarouene Boubakri 		SLIST_INSERT_HEAD(&parent_list, parent, link);
7852a75a25SMarouene Boubakri 	parent->num_used--;
7952a75a25SMarouene Boubakri 
8052a75a25SMarouene Boubakri 	if (!parent->num_used && SLIST_NEXT(SLIST_FIRST(&parent_list), link)) {
8152a75a25SMarouene Boubakri 		/*
8252a75a25SMarouene Boubakri 		 * If this isn't the last pgt_parent with free entries we
8352a75a25SMarouene Boubakri 		 * can free this.
8452a75a25SMarouene Boubakri 		 */
8552a75a25SMarouene Boubakri 		SLIST_REMOVE(&parent_list, parent, pgt_parent, link);
8652a75a25SMarouene Boubakri 		tee_mm_free(parent->mm);
8752a75a25SMarouene Boubakri 		free(parent);
8852a75a25SMarouene Boubakri 	} else {
8952a75a25SMarouene Boubakri 		SLIST_INSERT_HEAD(&parent->pgt_cache, pgt, link);
9052a75a25SMarouene Boubakri 		pgt->vabase = 0;
9152a75a25SMarouene Boubakri 		pgt->populated = false;
9252a75a25SMarouene Boubakri 	}
9352a75a25SMarouene Boubakri 
9452a75a25SMarouene Boubakri 	cpu_spin_unlock_xrestore(&parent_spinlock, exceptions);
9552a75a25SMarouene Boubakri }
9652a75a25SMarouene Boubakri 
9752a75a25SMarouene Boubakri static struct pgt_parent *alloc_pgt_parent(void)
9852a75a25SMarouene Boubakri {
9952a75a25SMarouene Boubakri 	struct pgt_parent *parent = NULL;
10052a75a25SMarouene Boubakri 	struct pgt *pgt = NULL;
10152a75a25SMarouene Boubakri 	uint8_t *tbl = NULL;
10252a75a25SMarouene Boubakri 	size_t sz = 0;
10352a75a25SMarouene Boubakri 	size_t n = 0;
10452a75a25SMarouene Boubakri 
10552a75a25SMarouene Boubakri 	sz = sizeof(*parent) + sizeof(*pgt) * PGT_PARENT_TBL_COUNT;
10652a75a25SMarouene Boubakri 	parent = calloc(1, sz);
10752a75a25SMarouene Boubakri 	if (!parent)
10852a75a25SMarouene Boubakri 		return NULL;
10952a75a25SMarouene Boubakri 	parent->mm = tee_mm_alloc(&tee_mm_sec_ddr, PGT_PARENT_SIZE);
11052a75a25SMarouene Boubakri 	if (!parent->mm) {
11152a75a25SMarouene Boubakri 		free(parent);
11252a75a25SMarouene Boubakri 		return NULL;
11352a75a25SMarouene Boubakri 	}
11452a75a25SMarouene Boubakri 	tbl = phys_to_virt(tee_mm_get_smem(parent->mm), MEM_AREA_TA_RAM,
11552a75a25SMarouene Boubakri 			   PGT_PARENT_SIZE);
11652a75a25SMarouene Boubakri 	assert(tbl); /* "can't fail" */
11752a75a25SMarouene Boubakri 
11852a75a25SMarouene Boubakri 	SLIST_INIT(&parent->pgt_cache);
11952a75a25SMarouene Boubakri 	pgt = (struct pgt *)(parent + 1);
12052a75a25SMarouene Boubakri 	for (n = 0; n < PGT_PARENT_TBL_COUNT; n++) {
12152a75a25SMarouene Boubakri 		pgt[n].parent = parent;
12252a75a25SMarouene Boubakri 		pgt[n].tbl = tbl + n * PGT_SIZE;
12352a75a25SMarouene Boubakri 		SLIST_INSERT_HEAD(&parent->pgt_cache, pgt + n, link);
12452a75a25SMarouene Boubakri 	}
12552a75a25SMarouene Boubakri 
12652a75a25SMarouene Boubakri 	return parent;
12752a75a25SMarouene Boubakri }
12852a75a25SMarouene Boubakri 
12952a75a25SMarouene Boubakri static struct pgt *alloc_pgt(vaddr_t vabase)
13052a75a25SMarouene Boubakri {
13152a75a25SMarouene Boubakri 	struct pgt_parent *parent = NULL;
13252a75a25SMarouene Boubakri 	uint32_t exceptions = 0;
13352a75a25SMarouene Boubakri 	struct pgt *pgt = NULL;
13452a75a25SMarouene Boubakri 
13552a75a25SMarouene Boubakri 	exceptions = cpu_spin_lock_xsave(&parent_spinlock);
13652a75a25SMarouene Boubakri 
13752a75a25SMarouene Boubakri 	parent = SLIST_FIRST(&parent_list);
13852a75a25SMarouene Boubakri 	if (!parent) {
13952a75a25SMarouene Boubakri 		parent = alloc_pgt_parent();
14052a75a25SMarouene Boubakri 		if (!parent)
14152a75a25SMarouene Boubakri 			goto out;
14252a75a25SMarouene Boubakri 
14352a75a25SMarouene Boubakri 		SLIST_INSERT_HEAD(&parent_list, parent, link);
14452a75a25SMarouene Boubakri 	}
14552a75a25SMarouene Boubakri 
14652a75a25SMarouene Boubakri 	pgt = SLIST_FIRST(&parent->pgt_cache);
14752a75a25SMarouene Boubakri 	SLIST_REMOVE_HEAD(&parent->pgt_cache, link);
14852a75a25SMarouene Boubakri 	parent->num_used++;
14952a75a25SMarouene Boubakri 	assert(pgt && parent->num_used <= PGT_PARENT_TBL_COUNT);
15052a75a25SMarouene Boubakri 	if (parent->num_used == PGT_PARENT_TBL_COUNT)
15152a75a25SMarouene Boubakri 		SLIST_REMOVE_HEAD(&parent_list, link);
15252a75a25SMarouene Boubakri 
15352a75a25SMarouene Boubakri 	pgt->vabase = vabase;
15452a75a25SMarouene Boubakri out:
15552a75a25SMarouene Boubakri 	cpu_spin_unlock_xrestore(&parent_spinlock, exceptions);
15652a75a25SMarouene Boubakri 	return pgt;
15752a75a25SMarouene Boubakri }
15852a75a25SMarouene Boubakri 
15952a75a25SMarouene Boubakri static bool pgt_entry_matches(struct pgt *p, vaddr_t begin, vaddr_t last)
16052a75a25SMarouene Boubakri {
16152a75a25SMarouene Boubakri 	if (!p)
16252a75a25SMarouene Boubakri 		return false;
16352a75a25SMarouene Boubakri 	if (last <= begin)
16452a75a25SMarouene Boubakri 		return false;
16552a75a25SMarouene Boubakri 	return core_is_buffer_inside(p->vabase, CORE_MMU_PGDIR_SIZE, begin,
16652a75a25SMarouene Boubakri 				     last - begin);
16752a75a25SMarouene Boubakri }
16852a75a25SMarouene Boubakri 
16952a75a25SMarouene Boubakri void pgt_flush_range(struct user_mode_ctx *uctx, vaddr_t begin, vaddr_t last)
17052a75a25SMarouene Boubakri {
17152a75a25SMarouene Boubakri 	struct pgt_cache *pgt_cache = &uctx->pgt_cache;
17252a75a25SMarouene Boubakri 	struct pgt *next_p = NULL;
17352a75a25SMarouene Boubakri 	struct pgt *p = NULL;
17452a75a25SMarouene Boubakri 
17552a75a25SMarouene Boubakri 	/*
17652a75a25SMarouene Boubakri 	 * Do the special case where the first element in the list is
17752a75a25SMarouene Boubakri 	 * removed first.
17852a75a25SMarouene Boubakri 	 */
17952a75a25SMarouene Boubakri 	p = SLIST_FIRST(pgt_cache);
18052a75a25SMarouene Boubakri 	while (pgt_entry_matches(p, begin, last)) {
18152a75a25SMarouene Boubakri 		SLIST_REMOVE_HEAD(pgt_cache, link);
18252a75a25SMarouene Boubakri 		free_pgt(p);
18352a75a25SMarouene Boubakri 		p = SLIST_FIRST(pgt_cache);
18452a75a25SMarouene Boubakri 	}
18552a75a25SMarouene Boubakri 
18652a75a25SMarouene Boubakri 	/*
18752a75a25SMarouene Boubakri 	 * p either points to the first element in the list or it's NULL,
18852a75a25SMarouene Boubakri 	 * if NULL the list is empty and we're done.
18952a75a25SMarouene Boubakri 	 */
19052a75a25SMarouene Boubakri 	if (!p)
19152a75a25SMarouene Boubakri 		return;
19252a75a25SMarouene Boubakri 
19352a75a25SMarouene Boubakri 	/*
19452a75a25SMarouene Boubakri 	 * Do the common case where the next element in the list is
19552a75a25SMarouene Boubakri 	 * removed.
19652a75a25SMarouene Boubakri 	 */
19752a75a25SMarouene Boubakri 	while (true) {
19852a75a25SMarouene Boubakri 		next_p = SLIST_NEXT(p, link);
19952a75a25SMarouene Boubakri 		if (!next_p)
20052a75a25SMarouene Boubakri 			break;
20152a75a25SMarouene Boubakri 		if (pgt_entry_matches(next_p, begin, last)) {
20252a75a25SMarouene Boubakri 			SLIST_REMOVE_AFTER(p, link);
20352a75a25SMarouene Boubakri 			free_pgt(next_p);
20452a75a25SMarouene Boubakri 			continue;
20552a75a25SMarouene Boubakri 		}
20652a75a25SMarouene Boubakri 
20752a75a25SMarouene Boubakri 		p = SLIST_NEXT(p, link);
20852a75a25SMarouene Boubakri 	}
20952a75a25SMarouene Boubakri }
21052a75a25SMarouene Boubakri 
21152a75a25SMarouene Boubakri void pgt_flush(struct user_mode_ctx *uctx)
21252a75a25SMarouene Boubakri {
21352a75a25SMarouene Boubakri 	struct pgt_cache *pgt_cache = &uctx->pgt_cache;
21452a75a25SMarouene Boubakri 	struct pgt *p = NULL;
21552a75a25SMarouene Boubakri 
21652a75a25SMarouene Boubakri 	while (true) {
21752a75a25SMarouene Boubakri 		p = SLIST_FIRST(pgt_cache);
21852a75a25SMarouene Boubakri 		if (!p)
21952a75a25SMarouene Boubakri 			break;
22052a75a25SMarouene Boubakri 		SLIST_REMOVE_HEAD(pgt_cache, link);
22152a75a25SMarouene Boubakri 		free_pgt(p);
22252a75a25SMarouene Boubakri 	}
22352a75a25SMarouene Boubakri }
22452a75a25SMarouene Boubakri 
22552a75a25SMarouene Boubakri void pgt_clear_range(struct user_mode_ctx *uctx, vaddr_t begin, vaddr_t end)
22652a75a25SMarouene Boubakri {
22752a75a25SMarouene Boubakri 	struct pgt_cache *pgt_cache = &uctx->pgt_cache;
22852a75a25SMarouene Boubakri 	struct pgt *p = NULL;
22952a75a25SMarouene Boubakri #ifdef CFG_WITH_LPAE
23052a75a25SMarouene Boubakri 	uint64_t *tbl = NULL;
23152a75a25SMarouene Boubakri #else
23252a75a25SMarouene Boubakri 	uint32_t *tbl = NULL;
23352a75a25SMarouene Boubakri #endif
23452a75a25SMarouene Boubakri 	unsigned int idx = 0;
23552a75a25SMarouene Boubakri 	unsigned int n = 0;
23652a75a25SMarouene Boubakri 
23752a75a25SMarouene Boubakri 	SLIST_FOREACH(p, pgt_cache, link) {
23852a75a25SMarouene Boubakri 		vaddr_t b = MAX(p->vabase, begin);
23952a75a25SMarouene Boubakri 		vaddr_t e = MIN(p->vabase + CORE_MMU_PGDIR_SIZE, end);
24052a75a25SMarouene Boubakri 
24152a75a25SMarouene Boubakri 		if (b >= e)
24252a75a25SMarouene Boubakri 			continue;
24352a75a25SMarouene Boubakri 
24452a75a25SMarouene Boubakri 		tbl = p->tbl;
24552a75a25SMarouene Boubakri 		idx = (b - p->vabase) / SMALL_PAGE_SIZE;
24652a75a25SMarouene Boubakri 		n = (e - b) / SMALL_PAGE_SIZE;
24752a75a25SMarouene Boubakri 		memset(tbl + idx, 0, n * sizeof(*tbl));
24852a75a25SMarouene Boubakri 	}
24952a75a25SMarouene Boubakri }
25052a75a25SMarouene Boubakri 
25152a75a25SMarouene Boubakri static struct pgt *prune_before_va(struct pgt_cache *pgt_cache, struct pgt *p,
25252a75a25SMarouene Boubakri 				   struct pgt *pp, vaddr_t va)
25352a75a25SMarouene Boubakri {
25452a75a25SMarouene Boubakri 	while (p && p->vabase < va) {
25552a75a25SMarouene Boubakri 		if (pp) {
25652a75a25SMarouene Boubakri 			assert(p == SLIST_NEXT(pp, link));
25752a75a25SMarouene Boubakri 			SLIST_REMOVE_AFTER(pp, link);
25852a75a25SMarouene Boubakri 			free_pgt(p);
25952a75a25SMarouene Boubakri 			p = SLIST_NEXT(pp, link);
26052a75a25SMarouene Boubakri 		} else {
26152a75a25SMarouene Boubakri 			assert(p == SLIST_FIRST(pgt_cache));
26252a75a25SMarouene Boubakri 			SLIST_REMOVE_HEAD(pgt_cache, link);
26352a75a25SMarouene Boubakri 			free_pgt(p);
26452a75a25SMarouene Boubakri 			p = SLIST_FIRST(pgt_cache);
26552a75a25SMarouene Boubakri 		}
26652a75a25SMarouene Boubakri 	}
26752a75a25SMarouene Boubakri 
26852a75a25SMarouene Boubakri 	return p;
26952a75a25SMarouene Boubakri }
27052a75a25SMarouene Boubakri 
27152a75a25SMarouene Boubakri bool pgt_check_avail(struct user_mode_ctx *uctx)
27252a75a25SMarouene Boubakri {
27352a75a25SMarouene Boubakri 	struct pgt_cache *pgt_cache = &uctx->pgt_cache;
27452a75a25SMarouene Boubakri 	struct vm_info *vm_info = &uctx->vm_info;
27552a75a25SMarouene Boubakri 	struct pgt *p = SLIST_FIRST(pgt_cache);
27652a75a25SMarouene Boubakri 	struct vm_region *r = NULL;
27752a75a25SMarouene Boubakri 	struct pgt *pp = NULL;
27852a75a25SMarouene Boubakri 	vaddr_t va = 0;
27952a75a25SMarouene Boubakri 	bool p_used = false;
28052a75a25SMarouene Boubakri 
28152a75a25SMarouene Boubakri 	/*
28252a75a25SMarouene Boubakri 	 * Prune unused tables. This is normally not needed since
28352a75a25SMarouene Boubakri 	 * pgt_flush_range() does this too, but in the error path of for
28452a75a25SMarouene Boubakri 	 * instance vm_remap() such calls may not be done. So for increased
28552a75a25SMarouene Boubakri 	 * robustness remove all unused translation tables before we may
28652a75a25SMarouene Boubakri 	 * allocate new ones.
28752a75a25SMarouene Boubakri 	 */
28852a75a25SMarouene Boubakri 	TAILQ_FOREACH(r, &vm_info->regions, link) {
28952a75a25SMarouene Boubakri 		for (va = ROUNDDOWN(r->va, CORE_MMU_PGDIR_SIZE);
29052a75a25SMarouene Boubakri 		     va < r->va + r->size; va += CORE_MMU_PGDIR_SIZE) {
29152a75a25SMarouene Boubakri 			if (!p_used)
29252a75a25SMarouene Boubakri 				p = prune_before_va(pgt_cache, p, pp, va);
29352a75a25SMarouene Boubakri 			if (!p)
29452a75a25SMarouene Boubakri 				goto prune_done;
29552a75a25SMarouene Boubakri 
29652a75a25SMarouene Boubakri 			if (p->vabase < va) {
29752a75a25SMarouene Boubakri 				pp = p;
29852a75a25SMarouene Boubakri 				p = SLIST_NEXT(pp, link);
29952a75a25SMarouene Boubakri 				if (!p)
30052a75a25SMarouene Boubakri 					goto prune_done;
30152a75a25SMarouene Boubakri 				p_used = false;
30252a75a25SMarouene Boubakri 			}
30352a75a25SMarouene Boubakri 
30452a75a25SMarouene Boubakri 			if (p->vabase == va)
30552a75a25SMarouene Boubakri 				p_used = true;
30652a75a25SMarouene Boubakri 		}
30752a75a25SMarouene Boubakri 	}
30852a75a25SMarouene Boubakri prune_done:
30952a75a25SMarouene Boubakri 
31052a75a25SMarouene Boubakri 	p = SLIST_FIRST(pgt_cache);
31152a75a25SMarouene Boubakri 	pp = NULL;
31252a75a25SMarouene Boubakri 	TAILQ_FOREACH(r, &vm_info->regions, link) {
31352a75a25SMarouene Boubakri 		for (va = ROUNDDOWN(r->va, CORE_MMU_PGDIR_SIZE);
31452a75a25SMarouene Boubakri 		     va < r->va + r->size; va += CORE_MMU_PGDIR_SIZE) {
31552a75a25SMarouene Boubakri 			if (p && p->vabase < va) {
31652a75a25SMarouene Boubakri 				pp = p;
31752a75a25SMarouene Boubakri 				p = SLIST_NEXT(pp, link);
31852a75a25SMarouene Boubakri 			}
31952a75a25SMarouene Boubakri 
32052a75a25SMarouene Boubakri 			if (p) {
32152a75a25SMarouene Boubakri 				if (p->vabase == va)
32252a75a25SMarouene Boubakri 					continue;
32352a75a25SMarouene Boubakri 				assert(p->vabase > va);
32452a75a25SMarouene Boubakri 			}
32552a75a25SMarouene Boubakri 
32652a75a25SMarouene Boubakri 			p = alloc_pgt(va);
32752a75a25SMarouene Boubakri 			if (!p)
32852a75a25SMarouene Boubakri 				return false;
32952a75a25SMarouene Boubakri 
33052a75a25SMarouene Boubakri 			if (pp)
33152a75a25SMarouene Boubakri 				SLIST_INSERT_AFTER(pp, p, link);
33252a75a25SMarouene Boubakri 			else
33352a75a25SMarouene Boubakri 				SLIST_INSERT_HEAD(pgt_cache, p, link);
33452a75a25SMarouene Boubakri 		}
33552a75a25SMarouene Boubakri 	}
33652a75a25SMarouene Boubakri 
33752a75a25SMarouene Boubakri 	return true;
33852a75a25SMarouene Boubakri }
33952a75a25SMarouene Boubakri #else /* !CFG_CORE_PREALLOC_EL0_TBLS */
34052a75a25SMarouene Boubakri 
341*f15052a2SEtienne Carriere #define PGT_CACHE_SIZE	ROUNDUP(CFG_PGT_CACHE_ENTRIES, PGT_NUM_PGT_PER_PAGE)
342*f15052a2SEtienne Carriere 
34352a75a25SMarouene Boubakri #if defined(CFG_WITH_PAGER) && !defined(CFG_WITH_LPAE)
34452a75a25SMarouene Boubakri static struct pgt_parent pgt_parents[PGT_CACHE_SIZE / PGT_NUM_PGT_PER_PAGE];
34552a75a25SMarouene Boubakri #else
34652a75a25SMarouene Boubakri 
34752a75a25SMarouene Boubakri static struct pgt_cache pgt_free_list = SLIST_HEAD_INITIALIZER(pgt_free_list);
34852a75a25SMarouene Boubakri #endif
34952a75a25SMarouene Boubakri 
35052a75a25SMarouene Boubakri /*
35152a75a25SMarouene Boubakri  * When a user TA context is temporarily unmapped the used struct pgt's of
35252a75a25SMarouene Boubakri  * the context (page tables holding valid physical pages) are saved in this
35352a75a25SMarouene Boubakri  * cache in the hope that it will remain in the cache when the context is
35452a75a25SMarouene Boubakri  * mapped again.
35552a75a25SMarouene Boubakri  */
35652a75a25SMarouene Boubakri static struct pgt_cache pgt_cache_list = SLIST_HEAD_INITIALIZER(pgt_cache_list);
35752a75a25SMarouene Boubakri 
35852a75a25SMarouene Boubakri static struct pgt pgt_entries[PGT_CACHE_SIZE];
35952a75a25SMarouene Boubakri 
36052a75a25SMarouene Boubakri static struct mutex pgt_mu = MUTEX_INITIALIZER;
36152a75a25SMarouene Boubakri static struct condvar pgt_cv = CONDVAR_INITIALIZER;
36252a75a25SMarouene Boubakri 
36352a75a25SMarouene Boubakri #if defined(CFG_WITH_PAGER) && defined(CFG_WITH_LPAE)
36452a75a25SMarouene Boubakri /*
36552a75a25SMarouene Boubakri  * Simple allocation of translation tables from pager, one translation
36652a75a25SMarouene Boubakri  * table is one page.
36752a75a25SMarouene Boubakri  */
36852a75a25SMarouene Boubakri void pgt_init(void)
36952a75a25SMarouene Boubakri {
37052a75a25SMarouene Boubakri 	size_t n;
37152a75a25SMarouene Boubakri 
37252a75a25SMarouene Boubakri 	for (n = 0; n < PGT_CACHE_SIZE; n++) {
37352a75a25SMarouene Boubakri 		struct pgt *p = pgt_entries + n;
37452a75a25SMarouene Boubakri 
37552a75a25SMarouene Boubakri 		p->tbl = tee_pager_alloc(PGT_SIZE);
37652a75a25SMarouene Boubakri 		SLIST_INSERT_HEAD(&pgt_free_list, p, link);
37752a75a25SMarouene Boubakri 	}
37852a75a25SMarouene Boubakri }
37952a75a25SMarouene Boubakri #elif defined(CFG_WITH_PAGER) && !defined(CFG_WITH_LPAE)
38052a75a25SMarouene Boubakri /*
38152a75a25SMarouene Boubakri  * Four translation tables per page -> need to keep track of the page
38252a75a25SMarouene Boubakri  * allocated from the pager.
38352a75a25SMarouene Boubakri  */
38452a75a25SMarouene Boubakri void pgt_init(void)
38552a75a25SMarouene Boubakri {
38652a75a25SMarouene Boubakri 	size_t n;
38752a75a25SMarouene Boubakri 	size_t m;
38852a75a25SMarouene Boubakri 
38952a75a25SMarouene Boubakri 	COMPILE_TIME_ASSERT(PGT_CACHE_SIZE % PGT_NUM_PGT_PER_PAGE == 0);
39052a75a25SMarouene Boubakri 	COMPILE_TIME_ASSERT(PGT_SIZE * PGT_NUM_PGT_PER_PAGE == SMALL_PAGE_SIZE);
39152a75a25SMarouene Boubakri 
39252a75a25SMarouene Boubakri 	for (n = 0; n < ARRAY_SIZE(pgt_parents); n++) {
39352a75a25SMarouene Boubakri 		uint8_t *tbl = tee_pager_alloc(SMALL_PAGE_SIZE);
39452a75a25SMarouene Boubakri 
39552a75a25SMarouene Boubakri 		SLIST_INIT(&pgt_parents[n].pgt_cache);
39652a75a25SMarouene Boubakri 		for (m = 0; m < PGT_NUM_PGT_PER_PAGE; m++) {
39752a75a25SMarouene Boubakri 			struct pgt *p = pgt_entries +
39852a75a25SMarouene Boubakri 					n * PGT_NUM_PGT_PER_PAGE + m;
39952a75a25SMarouene Boubakri 
40052a75a25SMarouene Boubakri 			p->tbl = tbl + m * PGT_SIZE;
40152a75a25SMarouene Boubakri 			p->parent = &pgt_parents[n];
40252a75a25SMarouene Boubakri 			SLIST_INSERT_HEAD(&pgt_parents[n].pgt_cache, p, link);
40352a75a25SMarouene Boubakri 		}
40452a75a25SMarouene Boubakri 	}
40552a75a25SMarouene Boubakri }
40652a75a25SMarouene Boubakri #else
40752a75a25SMarouene Boubakri /* Static allocation of translation tables */
40852a75a25SMarouene Boubakri void pgt_init(void)
40952a75a25SMarouene Boubakri {
41052a75a25SMarouene Boubakri 	/*
41152a75a25SMarouene Boubakri 	 * We're putting this in .nozi.* instead of .bss because .nozi.* already
41252a75a25SMarouene Boubakri 	 * has a large alignment, while .bss has a small alignment. The current
41352a75a25SMarouene Boubakri 	 * link script is optimized for small alignment in .bss
41452a75a25SMarouene Boubakri 	 */
41552a75a25SMarouene Boubakri 	static uint8_t pgt_tables[PGT_CACHE_SIZE][PGT_SIZE]
41652a75a25SMarouene Boubakri 			__aligned(PGT_SIZE) __section(".nozi.pgt_cache");
41752a75a25SMarouene Boubakri 	size_t n;
41852a75a25SMarouene Boubakri 
41952a75a25SMarouene Boubakri 	for (n = 0; n < ARRAY_SIZE(pgt_tables); n++) {
42052a75a25SMarouene Boubakri 		struct pgt *p = pgt_entries + n;
42152a75a25SMarouene Boubakri 
42252a75a25SMarouene Boubakri 		p->tbl = pgt_tables[n];
42352a75a25SMarouene Boubakri 		SLIST_INSERT_HEAD(&pgt_free_list, p, link);
42452a75a25SMarouene Boubakri 	}
42552a75a25SMarouene Boubakri }
42652a75a25SMarouene Boubakri #endif
42752a75a25SMarouene Boubakri 
42852a75a25SMarouene Boubakri #if defined(CFG_WITH_LPAE) || !defined(CFG_WITH_PAGER)
42952a75a25SMarouene Boubakri /* Simple allocation of translation tables from pager or static allocation */
43052a75a25SMarouene Boubakri static struct pgt *pop_from_free_list(void)
43152a75a25SMarouene Boubakri {
43252a75a25SMarouene Boubakri 	struct pgt *p = SLIST_FIRST(&pgt_free_list);
43352a75a25SMarouene Boubakri 
43452a75a25SMarouene Boubakri 	if (p) {
43552a75a25SMarouene Boubakri 		SLIST_REMOVE_HEAD(&pgt_free_list, link);
43652a75a25SMarouene Boubakri 		memset(p->tbl, 0, PGT_SIZE);
43752a75a25SMarouene Boubakri 		p->populated = false;
43852a75a25SMarouene Boubakri 	}
43952a75a25SMarouene Boubakri 	return p;
44052a75a25SMarouene Boubakri }
44152a75a25SMarouene Boubakri 
44252a75a25SMarouene Boubakri static void push_to_free_list(struct pgt *p)
44352a75a25SMarouene Boubakri {
44452a75a25SMarouene Boubakri 	SLIST_INSERT_HEAD(&pgt_free_list, p, link);
44552a75a25SMarouene Boubakri #if defined(CFG_WITH_PAGER)
44652a75a25SMarouene Boubakri 	tee_pager_release_phys(p->tbl, PGT_SIZE);
44752a75a25SMarouene Boubakri #endif
44852a75a25SMarouene Boubakri }
44952a75a25SMarouene Boubakri #else
45052a75a25SMarouene Boubakri /*
45152a75a25SMarouene Boubakri  * Four translation tables per page -> need to keep track of the page
45252a75a25SMarouene Boubakri  * allocated from the pager.
45352a75a25SMarouene Boubakri  */
45452a75a25SMarouene Boubakri static struct pgt *pop_from_free_list(void)
45552a75a25SMarouene Boubakri {
45652a75a25SMarouene Boubakri 	size_t n;
45752a75a25SMarouene Boubakri 
45852a75a25SMarouene Boubakri 	for (n = 0; n < ARRAY_SIZE(pgt_parents); n++) {
45952a75a25SMarouene Boubakri 		struct pgt *p = SLIST_FIRST(&pgt_parents[n].pgt_cache);
46052a75a25SMarouene Boubakri 
46152a75a25SMarouene Boubakri 		if (p) {
46252a75a25SMarouene Boubakri 			SLIST_REMOVE_HEAD(&pgt_parents[n].pgt_cache, link);
46352a75a25SMarouene Boubakri 			pgt_parents[n].num_used++;
46452a75a25SMarouene Boubakri 			memset(p->tbl, 0, PGT_SIZE);
46552a75a25SMarouene Boubakri 			p->populated = false;
46652a75a25SMarouene Boubakri 			return p;
46752a75a25SMarouene Boubakri 		}
46852a75a25SMarouene Boubakri 	}
46952a75a25SMarouene Boubakri 	return NULL;
47052a75a25SMarouene Boubakri }
47152a75a25SMarouene Boubakri 
47252a75a25SMarouene Boubakri static void push_to_free_list(struct pgt *p)
47352a75a25SMarouene Boubakri {
47452a75a25SMarouene Boubakri 	SLIST_INSERT_HEAD(&p->parent->pgt_cache, p, link);
47552a75a25SMarouene Boubakri 	assert(p->parent->num_used > 0);
47652a75a25SMarouene Boubakri 	p->parent->num_used--;
47752a75a25SMarouene Boubakri 	if (!p->parent->num_used) {
47852a75a25SMarouene Boubakri 		vaddr_t va = (vaddr_t)p->tbl & ~SMALL_PAGE_MASK;
47952a75a25SMarouene Boubakri 
48052a75a25SMarouene Boubakri 		tee_pager_release_phys((void *)va, SMALL_PAGE_SIZE);
48152a75a25SMarouene Boubakri 	}
48252a75a25SMarouene Boubakri }
48352a75a25SMarouene Boubakri #endif
48452a75a25SMarouene Boubakri 
48552a75a25SMarouene Boubakri static void push_to_cache_list(struct pgt *pgt)
48652a75a25SMarouene Boubakri {
48752a75a25SMarouene Boubakri 	SLIST_INSERT_HEAD(&pgt_cache_list, pgt, link);
48852a75a25SMarouene Boubakri }
48952a75a25SMarouene Boubakri 
49052a75a25SMarouene Boubakri static bool match_pgt(struct pgt *pgt, vaddr_t vabase, void *ctx)
49152a75a25SMarouene Boubakri {
49252a75a25SMarouene Boubakri 	return pgt->ctx == ctx && pgt->vabase == vabase;
49352a75a25SMarouene Boubakri }
49452a75a25SMarouene Boubakri 
49552a75a25SMarouene Boubakri static struct pgt *pop_from_cache_list(vaddr_t vabase, void *ctx)
49652a75a25SMarouene Boubakri {
49752a75a25SMarouene Boubakri 	struct pgt *pgt;
49852a75a25SMarouene Boubakri 	struct pgt *p;
49952a75a25SMarouene Boubakri 
50052a75a25SMarouene Boubakri 	pgt = SLIST_FIRST(&pgt_cache_list);
50152a75a25SMarouene Boubakri 	if (!pgt)
50252a75a25SMarouene Boubakri 		return NULL;
50352a75a25SMarouene Boubakri 	if (match_pgt(pgt, vabase, ctx)) {
50452a75a25SMarouene Boubakri 		SLIST_REMOVE_HEAD(&pgt_cache_list, link);
50552a75a25SMarouene Boubakri 		return pgt;
50652a75a25SMarouene Boubakri 	}
50752a75a25SMarouene Boubakri 
50852a75a25SMarouene Boubakri 	while (true) {
50952a75a25SMarouene Boubakri 		p = SLIST_NEXT(pgt, link);
51052a75a25SMarouene Boubakri 		if (!p)
51152a75a25SMarouene Boubakri 			break;
51252a75a25SMarouene Boubakri 		if (match_pgt(p, vabase, ctx)) {
51352a75a25SMarouene Boubakri 			SLIST_REMOVE_AFTER(pgt, link);
51452a75a25SMarouene Boubakri 			break;
51552a75a25SMarouene Boubakri 		}
51652a75a25SMarouene Boubakri 		pgt = p;
51752a75a25SMarouene Boubakri 	}
51852a75a25SMarouene Boubakri 	return p;
51952a75a25SMarouene Boubakri }
52052a75a25SMarouene Boubakri 
52152a75a25SMarouene Boubakri static uint16_t get_num_used_entries(struct pgt *pgt __maybe_unused)
52252a75a25SMarouene Boubakri {
52352a75a25SMarouene Boubakri #ifdef CFG_PAGED_USER_TA
52452a75a25SMarouene Boubakri 	return pgt->num_used_entries;
52552a75a25SMarouene Boubakri #else
52652a75a25SMarouene Boubakri 	return 0;
52752a75a25SMarouene Boubakri #endif
52852a75a25SMarouene Boubakri }
52952a75a25SMarouene Boubakri 
53052a75a25SMarouene Boubakri static struct pgt *pop_least_used_from_cache_list(void)
53152a75a25SMarouene Boubakri {
53252a75a25SMarouene Boubakri 	struct pgt *pgt = NULL;
53352a75a25SMarouene Boubakri 	struct pgt *p_prev = NULL;
53452a75a25SMarouene Boubakri 	size_t least_used = 0;
53552a75a25SMarouene Boubakri 	size_t next_used = 0;
53652a75a25SMarouene Boubakri 
53752a75a25SMarouene Boubakri 	pgt = SLIST_FIRST(&pgt_cache_list);
53852a75a25SMarouene Boubakri 	if (!pgt)
53952a75a25SMarouene Boubakri 		return NULL;
54052a75a25SMarouene Boubakri 	least_used = get_num_used_entries(pgt);
54152a75a25SMarouene Boubakri 
54252a75a25SMarouene Boubakri 	while (true) {
54352a75a25SMarouene Boubakri 		if (!SLIST_NEXT(pgt, link))
54452a75a25SMarouene Boubakri 			break;
54552a75a25SMarouene Boubakri 		next_used = get_num_used_entries(SLIST_NEXT(pgt, link));
54652a75a25SMarouene Boubakri 		if (next_used <= least_used) {
54752a75a25SMarouene Boubakri 			p_prev = pgt;
54852a75a25SMarouene Boubakri 			least_used = next_used;
54952a75a25SMarouene Boubakri 		}
55052a75a25SMarouene Boubakri 		pgt = SLIST_NEXT(pgt, link);
55152a75a25SMarouene Boubakri 	}
55252a75a25SMarouene Boubakri 
55352a75a25SMarouene Boubakri 	if (p_prev) {
55452a75a25SMarouene Boubakri 		pgt = SLIST_NEXT(p_prev, link);
55552a75a25SMarouene Boubakri 		SLIST_REMOVE_AFTER(p_prev, link);
55652a75a25SMarouene Boubakri 	} else {
55752a75a25SMarouene Boubakri 		pgt = SLIST_FIRST(&pgt_cache_list);
55852a75a25SMarouene Boubakri 		SLIST_REMOVE_HEAD(&pgt_cache_list, link);
55952a75a25SMarouene Boubakri 	}
56052a75a25SMarouene Boubakri 	return pgt;
56152a75a25SMarouene Boubakri }
56252a75a25SMarouene Boubakri 
56352a75a25SMarouene Boubakri static void pgt_free_unlocked(struct pgt_cache *pgt_cache)
56452a75a25SMarouene Boubakri {
56552a75a25SMarouene Boubakri 	while (!SLIST_EMPTY(pgt_cache)) {
56652a75a25SMarouene Boubakri 		struct pgt *p = SLIST_FIRST(pgt_cache);
56752a75a25SMarouene Boubakri 
56852a75a25SMarouene Boubakri 		SLIST_REMOVE_HEAD(pgt_cache, link);
56952a75a25SMarouene Boubakri 
57052a75a25SMarouene Boubakri 		/*
57152a75a25SMarouene Boubakri 		 * With paging enabled we free all tables which doesn't
57252a75a25SMarouene Boubakri 		 * refer to any paged pages any longer. This reduces the
57352a75a25SMarouene Boubakri 		 * pressure the pool of physical pages.
57452a75a25SMarouene Boubakri 		 */
57552a75a25SMarouene Boubakri 		if (IS_ENABLED(CFG_PAGED_USER_TA) && !get_num_used_entries(p)) {
57652a75a25SMarouene Boubakri 			tee_pager_pgt_save_and_release_entries(p);
57752a75a25SMarouene Boubakri 			p->ctx = NULL;
57852a75a25SMarouene Boubakri 			p->vabase = 0;
57952a75a25SMarouene Boubakri 
58052a75a25SMarouene Boubakri 			push_to_free_list(p);
58152a75a25SMarouene Boubakri 			continue;
58252a75a25SMarouene Boubakri 		}
58352a75a25SMarouene Boubakri 
58452a75a25SMarouene Boubakri 		push_to_cache_list(p);
58552a75a25SMarouene Boubakri 	}
58652a75a25SMarouene Boubakri }
58752a75a25SMarouene Boubakri 
58852a75a25SMarouene Boubakri static struct pgt *pop_from_some_list(vaddr_t vabase, void *ctx)
58952a75a25SMarouene Boubakri {
59052a75a25SMarouene Boubakri 	struct pgt *p = pop_from_cache_list(vabase, ctx);
59152a75a25SMarouene Boubakri 
59252a75a25SMarouene Boubakri 	if (p)
59352a75a25SMarouene Boubakri 		return p;
59452a75a25SMarouene Boubakri 	p = pop_from_free_list();
59552a75a25SMarouene Boubakri 	if (!p) {
59652a75a25SMarouene Boubakri 		p = pop_least_used_from_cache_list();
59752a75a25SMarouene Boubakri 		if (!p)
59852a75a25SMarouene Boubakri 			return NULL;
59952a75a25SMarouene Boubakri 		tee_pager_pgt_save_and_release_entries(p);
60052a75a25SMarouene Boubakri 		memset(p->tbl, 0, PGT_SIZE);
60152a75a25SMarouene Boubakri 		p->populated = false;
60252a75a25SMarouene Boubakri 	}
60352a75a25SMarouene Boubakri 	p->ctx = ctx;
60452a75a25SMarouene Boubakri 	p->vabase = vabase;
60552a75a25SMarouene Boubakri 	return p;
60652a75a25SMarouene Boubakri }
60752a75a25SMarouene Boubakri 
60852a75a25SMarouene Boubakri void pgt_flush(struct user_mode_ctx *uctx)
60952a75a25SMarouene Boubakri {
61052a75a25SMarouene Boubakri 	struct ts_ctx *ctx = uctx->ts_ctx;
61152a75a25SMarouene Boubakri 	struct pgt *pp = NULL;
61252a75a25SMarouene Boubakri 	struct pgt *p = NULL;
61352a75a25SMarouene Boubakri 
61452a75a25SMarouene Boubakri 	mutex_lock(&pgt_mu);
61552a75a25SMarouene Boubakri 
61652a75a25SMarouene Boubakri 	while (true) {
61752a75a25SMarouene Boubakri 		p = SLIST_FIRST(&pgt_cache_list);
61852a75a25SMarouene Boubakri 		if (!p)
61952a75a25SMarouene Boubakri 			goto out;
62052a75a25SMarouene Boubakri 		if (p->ctx != ctx)
62152a75a25SMarouene Boubakri 			break;
62252a75a25SMarouene Boubakri 		SLIST_REMOVE_HEAD(&pgt_cache_list, link);
62352a75a25SMarouene Boubakri 		tee_pager_pgt_save_and_release_entries(p);
62452a75a25SMarouene Boubakri 		p->ctx = NULL;
62552a75a25SMarouene Boubakri 		p->vabase = 0;
62652a75a25SMarouene Boubakri 		push_to_free_list(p);
62752a75a25SMarouene Boubakri 	}
62852a75a25SMarouene Boubakri 
62952a75a25SMarouene Boubakri 	pp = p;
63052a75a25SMarouene Boubakri 	while (true) {
63152a75a25SMarouene Boubakri 		p = SLIST_NEXT(pp, link);
63252a75a25SMarouene Boubakri 		if (!p)
63352a75a25SMarouene Boubakri 			break;
63452a75a25SMarouene Boubakri 		if (p->ctx == ctx) {
63552a75a25SMarouene Boubakri 			SLIST_REMOVE_AFTER(pp, link);
63652a75a25SMarouene Boubakri 			tee_pager_pgt_save_and_release_entries(p);
63752a75a25SMarouene Boubakri 			p->ctx = NULL;
63852a75a25SMarouene Boubakri 			p->vabase = 0;
63952a75a25SMarouene Boubakri 			push_to_free_list(p);
64052a75a25SMarouene Boubakri 		} else {
64152a75a25SMarouene Boubakri 			pp = p;
64252a75a25SMarouene Boubakri 		}
64352a75a25SMarouene Boubakri 	}
64452a75a25SMarouene Boubakri 
64552a75a25SMarouene Boubakri out:
64652a75a25SMarouene Boubakri 	mutex_unlock(&pgt_mu);
64752a75a25SMarouene Boubakri }
64852a75a25SMarouene Boubakri 
64952a75a25SMarouene Boubakri static void flush_pgt_entry(struct pgt *p)
65052a75a25SMarouene Boubakri {
65152a75a25SMarouene Boubakri 	tee_pager_pgt_save_and_release_entries(p);
65252a75a25SMarouene Boubakri 	p->ctx = NULL;
65352a75a25SMarouene Boubakri 	p->vabase = 0;
65452a75a25SMarouene Boubakri }
65552a75a25SMarouene Boubakri 
65652a75a25SMarouene Boubakri static bool pgt_entry_matches(struct pgt *p, void *ctx, vaddr_t begin,
65752a75a25SMarouene Boubakri 			      vaddr_t last)
65852a75a25SMarouene Boubakri {
65952a75a25SMarouene Boubakri 	if (!p)
66052a75a25SMarouene Boubakri 		return false;
66152a75a25SMarouene Boubakri 	if (p->ctx != ctx)
66252a75a25SMarouene Boubakri 		return false;
66352a75a25SMarouene Boubakri 	if (last <= begin)
66452a75a25SMarouene Boubakri 		return false;
66552a75a25SMarouene Boubakri 	if (!core_is_buffer_inside(p->vabase, CORE_MMU_PGDIR_SIZE, begin,
66652a75a25SMarouene Boubakri 				   last - begin))
66752a75a25SMarouene Boubakri 		return false;
66852a75a25SMarouene Boubakri 
66952a75a25SMarouene Boubakri 	return true;
67052a75a25SMarouene Boubakri }
67152a75a25SMarouene Boubakri 
67252a75a25SMarouene Boubakri static void flush_ctx_range_from_list(struct pgt_cache *pgt_cache, void *ctx,
67352a75a25SMarouene Boubakri 				      vaddr_t begin, vaddr_t last)
67452a75a25SMarouene Boubakri {
67552a75a25SMarouene Boubakri 	struct pgt *p;
67652a75a25SMarouene Boubakri 	struct pgt *next_p;
67752a75a25SMarouene Boubakri 
67852a75a25SMarouene Boubakri 	/*
67952a75a25SMarouene Boubakri 	 * Do the special case where the first element in the list is
68052a75a25SMarouene Boubakri 	 * removed first.
68152a75a25SMarouene Boubakri 	 */
68252a75a25SMarouene Boubakri 	p = SLIST_FIRST(pgt_cache);
68352a75a25SMarouene Boubakri 	while (pgt_entry_matches(p, ctx, begin, last)) {
68452a75a25SMarouene Boubakri 		flush_pgt_entry(p);
68552a75a25SMarouene Boubakri 		SLIST_REMOVE_HEAD(pgt_cache, link);
68652a75a25SMarouene Boubakri 		push_to_free_list(p);
68752a75a25SMarouene Boubakri 		p = SLIST_FIRST(pgt_cache);
68852a75a25SMarouene Boubakri 	}
68952a75a25SMarouene Boubakri 
69052a75a25SMarouene Boubakri 	/*
69152a75a25SMarouene Boubakri 	 * p either points to the first element in the list or it's NULL,
69252a75a25SMarouene Boubakri 	 * if NULL the list is empty and we're done.
69352a75a25SMarouene Boubakri 	 */
69452a75a25SMarouene Boubakri 	if (!p)
69552a75a25SMarouene Boubakri 		return;
69652a75a25SMarouene Boubakri 
69752a75a25SMarouene Boubakri 	/*
69852a75a25SMarouene Boubakri 	 * Do the common case where the next element in the list is
69952a75a25SMarouene Boubakri 	 * removed.
70052a75a25SMarouene Boubakri 	 */
70152a75a25SMarouene Boubakri 	while (true) {
70252a75a25SMarouene Boubakri 		next_p = SLIST_NEXT(p, link);
70352a75a25SMarouene Boubakri 		if (!next_p)
70452a75a25SMarouene Boubakri 			break;
70552a75a25SMarouene Boubakri 		if (pgt_entry_matches(next_p, ctx, begin, last)) {
70652a75a25SMarouene Boubakri 			flush_pgt_entry(next_p);
70752a75a25SMarouene Boubakri 			SLIST_REMOVE_AFTER(p, link);
70852a75a25SMarouene Boubakri 			push_to_free_list(next_p);
70952a75a25SMarouene Boubakri 			continue;
71052a75a25SMarouene Boubakri 		}
71152a75a25SMarouene Boubakri 
71252a75a25SMarouene Boubakri 		p = SLIST_NEXT(p, link);
71352a75a25SMarouene Boubakri 	}
71452a75a25SMarouene Boubakri }
71552a75a25SMarouene Boubakri 
71652a75a25SMarouene Boubakri void pgt_flush_range(struct user_mode_ctx *uctx, vaddr_t begin, vaddr_t last)
71752a75a25SMarouene Boubakri {
71852a75a25SMarouene Boubakri 	struct pgt_cache *pgt_cache = &uctx->pgt_cache;
71952a75a25SMarouene Boubakri 	struct ts_ctx *ctx = uctx->ts_ctx;
72052a75a25SMarouene Boubakri 
72152a75a25SMarouene Boubakri 	mutex_lock(&pgt_mu);
72252a75a25SMarouene Boubakri 
72352a75a25SMarouene Boubakri 	flush_ctx_range_from_list(pgt_cache, ctx, begin, last);
72452a75a25SMarouene Boubakri 	flush_ctx_range_from_list(&pgt_cache_list, ctx, begin, last);
72552a75a25SMarouene Boubakri 
72652a75a25SMarouene Boubakri 	condvar_broadcast(&pgt_cv);
72752a75a25SMarouene Boubakri 	mutex_unlock(&pgt_mu);
72852a75a25SMarouene Boubakri }
72952a75a25SMarouene Boubakri 
73052a75a25SMarouene Boubakri static void clear_ctx_range_from_list(struct pgt_cache *pgt_cache,
73152a75a25SMarouene Boubakri 				      void *ctx, vaddr_t begin, vaddr_t end)
73252a75a25SMarouene Boubakri {
73352a75a25SMarouene Boubakri 	struct pgt *p = NULL;
73452a75a25SMarouene Boubakri #ifdef CFG_WITH_LPAE
73552a75a25SMarouene Boubakri 	uint64_t *tbl = NULL;
73652a75a25SMarouene Boubakri #else
73752a75a25SMarouene Boubakri 	uint32_t *tbl = NULL;
73852a75a25SMarouene Boubakri #endif
73952a75a25SMarouene Boubakri 	unsigned int idx = 0;
74052a75a25SMarouene Boubakri 	unsigned int n = 0;
74152a75a25SMarouene Boubakri 
74252a75a25SMarouene Boubakri 	SLIST_FOREACH(p, pgt_cache, link) {
74352a75a25SMarouene Boubakri 		vaddr_t b = MAX(p->vabase, begin);
74452a75a25SMarouene Boubakri 		vaddr_t e = MIN(p->vabase + CORE_MMU_PGDIR_SIZE, end);
74552a75a25SMarouene Boubakri 
74652a75a25SMarouene Boubakri 		if (p->ctx != ctx)
74752a75a25SMarouene Boubakri 			continue;
74852a75a25SMarouene Boubakri 		if (b >= e)
74952a75a25SMarouene Boubakri 			continue;
75052a75a25SMarouene Boubakri 
75152a75a25SMarouene Boubakri 		tbl = p->tbl;
75252a75a25SMarouene Boubakri 		idx = (b - p->vabase) / SMALL_PAGE_SIZE;
75352a75a25SMarouene Boubakri 		n = (e - b) / SMALL_PAGE_SIZE;
75452a75a25SMarouene Boubakri 		memset(tbl + idx, 0, n * sizeof(*tbl));
75552a75a25SMarouene Boubakri 	}
75652a75a25SMarouene Boubakri }
75752a75a25SMarouene Boubakri 
75852a75a25SMarouene Boubakri void pgt_clear_range(struct user_mode_ctx *uctx, vaddr_t begin, vaddr_t end)
75952a75a25SMarouene Boubakri {
76052a75a25SMarouene Boubakri 	struct pgt_cache *pgt_cache = &uctx->pgt_cache;
76152a75a25SMarouene Boubakri 	struct ts_ctx *ctx = uctx->ts_ctx;
76252a75a25SMarouene Boubakri 
76352a75a25SMarouene Boubakri 	mutex_lock(&pgt_mu);
76452a75a25SMarouene Boubakri 
76552a75a25SMarouene Boubakri 	clear_ctx_range_from_list(pgt_cache, ctx, begin, end);
76652a75a25SMarouene Boubakri 	clear_ctx_range_from_list(&pgt_cache_list, ctx, begin, end);
76752a75a25SMarouene Boubakri 
76852a75a25SMarouene Boubakri 	mutex_unlock(&pgt_mu);
76952a75a25SMarouene Boubakri }
77052a75a25SMarouene Boubakri 
77152a75a25SMarouene Boubakri static bool pgt_alloc_unlocked(struct pgt_cache *pgt_cache, struct ts_ctx *ctx,
77252a75a25SMarouene Boubakri 			       struct vm_info *vm_info)
77352a75a25SMarouene Boubakri {
77452a75a25SMarouene Boubakri 	struct vm_region *r = NULL;
77552a75a25SMarouene Boubakri 	struct pgt *pp = NULL;
77652a75a25SMarouene Boubakri 	struct pgt *p = NULL;
77752a75a25SMarouene Boubakri 	vaddr_t va = 0;
77852a75a25SMarouene Boubakri 
77952a75a25SMarouene Boubakri 	TAILQ_FOREACH(r, &vm_info->regions, link) {
78052a75a25SMarouene Boubakri 		for (va = ROUNDDOWN(r->va, CORE_MMU_PGDIR_SIZE);
78152a75a25SMarouene Boubakri 		     va < r->va + r->size; va += CORE_MMU_PGDIR_SIZE) {
78252a75a25SMarouene Boubakri 			if (p && p->vabase == va)
78352a75a25SMarouene Boubakri 				continue;
78452a75a25SMarouene Boubakri 			p = pop_from_some_list(va, ctx);
78552a75a25SMarouene Boubakri 			if (!p) {
78652a75a25SMarouene Boubakri 				pgt_free_unlocked(pgt_cache);
78752a75a25SMarouene Boubakri 				return false;
78852a75a25SMarouene Boubakri 			}
78952a75a25SMarouene Boubakri 			if (pp)
79052a75a25SMarouene Boubakri 				SLIST_INSERT_AFTER(pp, p, link);
79152a75a25SMarouene Boubakri 			else
79252a75a25SMarouene Boubakri 				SLIST_INSERT_HEAD(pgt_cache, p, link);
79352a75a25SMarouene Boubakri 			pp = p;
79452a75a25SMarouene Boubakri 		}
79552a75a25SMarouene Boubakri 	}
79652a75a25SMarouene Boubakri 
79752a75a25SMarouene Boubakri 	return true;
79852a75a25SMarouene Boubakri }
79952a75a25SMarouene Boubakri 
80052a75a25SMarouene Boubakri bool pgt_check_avail(struct user_mode_ctx *uctx)
80152a75a25SMarouene Boubakri {
80252a75a25SMarouene Boubakri 	struct vm_info *vm_info = &uctx->vm_info;
80352a75a25SMarouene Boubakri 	struct vm_region *r = NULL;
80452a75a25SMarouene Boubakri 	size_t tbl_count = 0;
80552a75a25SMarouene Boubakri 	vaddr_t last_va = 0;
80652a75a25SMarouene Boubakri 	vaddr_t va = 0;
80752a75a25SMarouene Boubakri 
80852a75a25SMarouene Boubakri 	TAILQ_FOREACH(r, &vm_info->regions, link) {
80952a75a25SMarouene Boubakri 		for (va = ROUNDDOWN(r->va, CORE_MMU_PGDIR_SIZE);
81052a75a25SMarouene Boubakri 		     va < r->va + r->size; va += CORE_MMU_PGDIR_SIZE) {
81152a75a25SMarouene Boubakri 			if (va == last_va)
81252a75a25SMarouene Boubakri 				continue;
81352a75a25SMarouene Boubakri 			tbl_count++;
81452a75a25SMarouene Boubakri 			last_va = va;
81552a75a25SMarouene Boubakri 		}
81652a75a25SMarouene Boubakri 	}
81752a75a25SMarouene Boubakri 
81852a75a25SMarouene Boubakri 	return tbl_count <= PGT_CACHE_SIZE;
81952a75a25SMarouene Boubakri }
82052a75a25SMarouene Boubakri 
82152a75a25SMarouene Boubakri void pgt_get_all(struct user_mode_ctx *uctx)
82252a75a25SMarouene Boubakri {
82352a75a25SMarouene Boubakri 	struct pgt_cache *pgt_cache = &uctx->pgt_cache;
82452a75a25SMarouene Boubakri 	struct vm_info *vm_info = &uctx->vm_info;
82552a75a25SMarouene Boubakri 
82652a75a25SMarouene Boubakri 	if (TAILQ_EMPTY(&vm_info->regions))
82752a75a25SMarouene Boubakri 		return;
82852a75a25SMarouene Boubakri 
82952a75a25SMarouene Boubakri 	mutex_lock(&pgt_mu);
83052a75a25SMarouene Boubakri 
83152a75a25SMarouene Boubakri 	pgt_free_unlocked(pgt_cache);
83252a75a25SMarouene Boubakri 	while (!pgt_alloc_unlocked(pgt_cache, uctx->ts_ctx, vm_info)) {
83352a75a25SMarouene Boubakri 		assert(pgt_check_avail(uctx));
83452a75a25SMarouene Boubakri 		DMSG("Waiting for page tables");
83552a75a25SMarouene Boubakri 		condvar_broadcast(&pgt_cv);
83652a75a25SMarouene Boubakri 		condvar_wait(&pgt_cv, &pgt_mu);
83752a75a25SMarouene Boubakri 	}
83852a75a25SMarouene Boubakri 
83952a75a25SMarouene Boubakri 	mutex_unlock(&pgt_mu);
84052a75a25SMarouene Boubakri }
84152a75a25SMarouene Boubakri 
84252a75a25SMarouene Boubakri void pgt_put_all(struct user_mode_ctx *uctx)
84352a75a25SMarouene Boubakri {
84452a75a25SMarouene Boubakri 	struct pgt_cache *pgt_cache = &uctx->pgt_cache;
84552a75a25SMarouene Boubakri 
84652a75a25SMarouene Boubakri 	if (SLIST_EMPTY(pgt_cache))
84752a75a25SMarouene Boubakri 		return;
84852a75a25SMarouene Boubakri 
84952a75a25SMarouene Boubakri 	mutex_lock(&pgt_mu);
85052a75a25SMarouene Boubakri 
85152a75a25SMarouene Boubakri 	pgt_free_unlocked(pgt_cache);
85252a75a25SMarouene Boubakri 
85352a75a25SMarouene Boubakri 	condvar_broadcast(&pgt_cv);
85452a75a25SMarouene Boubakri 	mutex_unlock(&pgt_mu);
85552a75a25SMarouene Boubakri }
85652a75a25SMarouene Boubakri 
85752a75a25SMarouene Boubakri struct pgt *pgt_pop_from_cache_list(vaddr_t vabase, struct ts_ctx *ctx)
85852a75a25SMarouene Boubakri {
85952a75a25SMarouene Boubakri 	struct pgt *pgt = NULL;
86052a75a25SMarouene Boubakri 
86152a75a25SMarouene Boubakri 	mutex_lock(&pgt_mu);
86252a75a25SMarouene Boubakri 	pgt = pop_from_cache_list(vabase, ctx);
86352a75a25SMarouene Boubakri 	mutex_unlock(&pgt_mu);
86452a75a25SMarouene Boubakri 
86552a75a25SMarouene Boubakri 	return pgt;
86652a75a25SMarouene Boubakri }
86752a75a25SMarouene Boubakri 
86852a75a25SMarouene Boubakri void pgt_push_to_cache_list(struct pgt *pgt)
86952a75a25SMarouene Boubakri {
87052a75a25SMarouene Boubakri 	mutex_lock(&pgt_mu);
87152a75a25SMarouene Boubakri 	push_to_cache_list(pgt);
87252a75a25SMarouene Boubakri 	mutex_unlock(&pgt_mu);
87352a75a25SMarouene Boubakri }
87452a75a25SMarouene Boubakri 
87552a75a25SMarouene Boubakri #endif /* !CFG_CORE_PREALLOC_EL0_TBLS */
876