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