xref: /optee_os/core/mm/pgt_cache.c (revision 52a75a2517f0ce8dd90b4ef4243d3d4665080f6d)
1*52a75a25SMarouene Boubakri // SPDX-License-Identifier: BSD-2-Clause
2*52a75a25SMarouene Boubakri /*
3*52a75a25SMarouene Boubakri  * Copyright (c) 2016, 2022 Linaro Limited
4*52a75a25SMarouene Boubakri  */
5*52a75a25SMarouene Boubakri 
6*52a75a25SMarouene Boubakri #include <assert.h>
7*52a75a25SMarouene Boubakri #include <kernel/mutex.h>
8*52a75a25SMarouene Boubakri #include <kernel/spinlock.h>
9*52a75a25SMarouene Boubakri #include <kernel/tee_misc.h>
10*52a75a25SMarouene Boubakri #include <kernel/user_mode_ctx.h>
11*52a75a25SMarouene Boubakri #include <mm/core_memprot.h>
12*52a75a25SMarouene Boubakri #include <mm/core_mmu.h>
13*52a75a25SMarouene Boubakri #include <mm/pgt_cache.h>
14*52a75a25SMarouene Boubakri #include <mm/tee_pager.h>
15*52a75a25SMarouene Boubakri #include <stdlib.h>
16*52a75a25SMarouene Boubakri #include <trace.h>
17*52a75a25SMarouene Boubakri #include <util.h>
18*52a75a25SMarouene Boubakri 
19*52a75a25SMarouene Boubakri /*
20*52a75a25SMarouene Boubakri  * With pager enabled we allocate page table from the pager.
21*52a75a25SMarouene Boubakri  *
22*52a75a25SMarouene Boubakri  * For LPAE each page table is a complete page which is allocated and freed
23*52a75a25SMarouene Boubakri  * using the interface provided by the pager.
24*52a75a25SMarouene Boubakri  *
25*52a75a25SMarouene Boubakri  * For compat v7 page tables there's room for four page table in one page
26*52a75a25SMarouene Boubakri  * so we need to keep track of how much of an allocated page is used. When
27*52a75a25SMarouene Boubakri  * a page is completely unused it's returned to the pager.
28*52a75a25SMarouene Boubakri  *
29*52a75a25SMarouene Boubakri  * With pager disabled we have a static allocation of page tables instead.
30*52a75a25SMarouene Boubakri  *
31*52a75a25SMarouene Boubakri  * In all cases we limit the number of active page tables to
32*52a75a25SMarouene Boubakri  * PGT_CACHE_SIZE.  This pool of page tables are shared between all
33*52a75a25SMarouene Boubakri  * threads. In case a thread can't allocate the needed number of pager
34*52a75a25SMarouene Boubakri  * tables it will release all its current tables and wait for some more to
35*52a75a25SMarouene Boubakri  * be freed. A threads allocated tables are freed each time a TA is
36*52a75a25SMarouene Boubakri  * unmapped so each thread should be able to allocate the needed tables in
37*52a75a25SMarouene Boubakri  * turn if needed.
38*52a75a25SMarouene Boubakri  */
39*52a75a25SMarouene Boubakri 
40*52a75a25SMarouene Boubakri #if defined(CFG_CORE_PREALLOC_EL0_TBLS) || \
41*52a75a25SMarouene Boubakri 	(defined(CFG_WITH_PAGER) && !defined(CFG_WITH_LPAE))
42*52a75a25SMarouene Boubakri struct pgt_parent {
43*52a75a25SMarouene Boubakri 	size_t num_used;
44*52a75a25SMarouene Boubakri 	struct pgt_cache pgt_cache;
45*52a75a25SMarouene Boubakri #if defined(CFG_CORE_PREALLOC_EL0_TBLS)
46*52a75a25SMarouene Boubakri 	tee_mm_entry_t *mm;
47*52a75a25SMarouene Boubakri 	SLIST_ENTRY(pgt_parent) link;
48*52a75a25SMarouene Boubakri #endif
49*52a75a25SMarouene Boubakri };
50*52a75a25SMarouene Boubakri #endif
51*52a75a25SMarouene Boubakri 
52*52a75a25SMarouene Boubakri #if defined(CFG_CORE_PREALLOC_EL0_TBLS)
53*52a75a25SMarouene Boubakri 
54*52a75a25SMarouene Boubakri /*
55*52a75a25SMarouene Boubakri  * Pick something large enough that tee_mm_alloc() doesn't have to be
56*52a75a25SMarouene Boubakri  * called for each needed translation table.
57*52a75a25SMarouene Boubakri  */
58*52a75a25SMarouene Boubakri #define PGT_PARENT_SIZE		(4 * SMALL_PAGE_SIZE)
59*52a75a25SMarouene Boubakri #define PGT_PARENT_TBL_COUNT	(PGT_PARENT_SIZE / PGT_SIZE)
60*52a75a25SMarouene Boubakri 
61*52a75a25SMarouene Boubakri SLIST_HEAD(pgt_parent_list, pgt_parent);
62*52a75a25SMarouene Boubakri static struct pgt_parent_list parent_list = SLIST_HEAD_INITIALIZER(parent_list);
63*52a75a25SMarouene Boubakri static unsigned int parent_spinlock = SPINLOCK_UNLOCK;
64*52a75a25SMarouene Boubakri 
65*52a75a25SMarouene Boubakri static void free_pgt(struct pgt *pgt)
66*52a75a25SMarouene Boubakri {
67*52a75a25SMarouene Boubakri 	struct pgt_parent *parent = NULL;
68*52a75a25SMarouene Boubakri 	uint32_t exceptions = 0;
69*52a75a25SMarouene Boubakri 
70*52a75a25SMarouene Boubakri 	exceptions = cpu_spin_lock_xsave(&parent_spinlock);
71*52a75a25SMarouene Boubakri 
72*52a75a25SMarouene Boubakri 	assert(pgt && pgt->parent);
73*52a75a25SMarouene Boubakri 	parent = pgt->parent;
74*52a75a25SMarouene Boubakri 	assert(parent->num_used <= PGT_PARENT_TBL_COUNT &&
75*52a75a25SMarouene Boubakri 	       parent->num_used > 0);
76*52a75a25SMarouene Boubakri 	if (parent->num_used == PGT_PARENT_TBL_COUNT)
77*52a75a25SMarouene Boubakri 		SLIST_INSERT_HEAD(&parent_list, parent, link);
78*52a75a25SMarouene Boubakri 	parent->num_used--;
79*52a75a25SMarouene Boubakri 
80*52a75a25SMarouene Boubakri 	if (!parent->num_used && SLIST_NEXT(SLIST_FIRST(&parent_list), link)) {
81*52a75a25SMarouene Boubakri 		/*
82*52a75a25SMarouene Boubakri 		 * If this isn't the last pgt_parent with free entries we
83*52a75a25SMarouene Boubakri 		 * can free this.
84*52a75a25SMarouene Boubakri 		 */
85*52a75a25SMarouene Boubakri 		SLIST_REMOVE(&parent_list, parent, pgt_parent, link);
86*52a75a25SMarouene Boubakri 		tee_mm_free(parent->mm);
87*52a75a25SMarouene Boubakri 		free(parent);
88*52a75a25SMarouene Boubakri 	} else {
89*52a75a25SMarouene Boubakri 		SLIST_INSERT_HEAD(&parent->pgt_cache, pgt, link);
90*52a75a25SMarouene Boubakri 		pgt->vabase = 0;
91*52a75a25SMarouene Boubakri 		pgt->populated = false;
92*52a75a25SMarouene Boubakri 	}
93*52a75a25SMarouene Boubakri 
94*52a75a25SMarouene Boubakri 	cpu_spin_unlock_xrestore(&parent_spinlock, exceptions);
95*52a75a25SMarouene Boubakri }
96*52a75a25SMarouene Boubakri 
97*52a75a25SMarouene Boubakri static struct pgt_parent *alloc_pgt_parent(void)
98*52a75a25SMarouene Boubakri {
99*52a75a25SMarouene Boubakri 	struct pgt_parent *parent = NULL;
100*52a75a25SMarouene Boubakri 	struct pgt *pgt = NULL;
101*52a75a25SMarouene Boubakri 	uint8_t *tbl = NULL;
102*52a75a25SMarouene Boubakri 	size_t sz = 0;
103*52a75a25SMarouene Boubakri 	size_t n = 0;
104*52a75a25SMarouene Boubakri 
105*52a75a25SMarouene Boubakri 	sz = sizeof(*parent) + sizeof(*pgt) * PGT_PARENT_TBL_COUNT;
106*52a75a25SMarouene Boubakri 	parent = calloc(1, sz);
107*52a75a25SMarouene Boubakri 	if (!parent)
108*52a75a25SMarouene Boubakri 		return NULL;
109*52a75a25SMarouene Boubakri 	parent->mm = tee_mm_alloc(&tee_mm_sec_ddr, PGT_PARENT_SIZE);
110*52a75a25SMarouene Boubakri 	if (!parent->mm) {
111*52a75a25SMarouene Boubakri 		free(parent);
112*52a75a25SMarouene Boubakri 		return NULL;
113*52a75a25SMarouene Boubakri 	}
114*52a75a25SMarouene Boubakri 	tbl = phys_to_virt(tee_mm_get_smem(parent->mm), MEM_AREA_TA_RAM,
115*52a75a25SMarouene Boubakri 			   PGT_PARENT_SIZE);
116*52a75a25SMarouene Boubakri 	assert(tbl); /* "can't fail" */
117*52a75a25SMarouene Boubakri 
118*52a75a25SMarouene Boubakri 	SLIST_INIT(&parent->pgt_cache);
119*52a75a25SMarouene Boubakri 	pgt = (struct pgt *)(parent + 1);
120*52a75a25SMarouene Boubakri 	for (n = 0; n < PGT_PARENT_TBL_COUNT; n++) {
121*52a75a25SMarouene Boubakri 		pgt[n].parent = parent;
122*52a75a25SMarouene Boubakri 		pgt[n].tbl = tbl + n * PGT_SIZE;
123*52a75a25SMarouene Boubakri 		SLIST_INSERT_HEAD(&parent->pgt_cache, pgt + n, link);
124*52a75a25SMarouene Boubakri 	}
125*52a75a25SMarouene Boubakri 
126*52a75a25SMarouene Boubakri 	return parent;
127*52a75a25SMarouene Boubakri }
128*52a75a25SMarouene Boubakri 
129*52a75a25SMarouene Boubakri static struct pgt *alloc_pgt(vaddr_t vabase)
130*52a75a25SMarouene Boubakri {
131*52a75a25SMarouene Boubakri 	struct pgt_parent *parent = NULL;
132*52a75a25SMarouene Boubakri 	uint32_t exceptions = 0;
133*52a75a25SMarouene Boubakri 	struct pgt *pgt = NULL;
134*52a75a25SMarouene Boubakri 
135*52a75a25SMarouene Boubakri 	exceptions = cpu_spin_lock_xsave(&parent_spinlock);
136*52a75a25SMarouene Boubakri 
137*52a75a25SMarouene Boubakri 	parent = SLIST_FIRST(&parent_list);
138*52a75a25SMarouene Boubakri 	if (!parent) {
139*52a75a25SMarouene Boubakri 		parent = alloc_pgt_parent();
140*52a75a25SMarouene Boubakri 		if (!parent)
141*52a75a25SMarouene Boubakri 			goto out;
142*52a75a25SMarouene Boubakri 
143*52a75a25SMarouene Boubakri 		SLIST_INSERT_HEAD(&parent_list, parent, link);
144*52a75a25SMarouene Boubakri 	}
145*52a75a25SMarouene Boubakri 
146*52a75a25SMarouene Boubakri 	pgt = SLIST_FIRST(&parent->pgt_cache);
147*52a75a25SMarouene Boubakri 	SLIST_REMOVE_HEAD(&parent->pgt_cache, link);
148*52a75a25SMarouene Boubakri 	parent->num_used++;
149*52a75a25SMarouene Boubakri 	assert(pgt && parent->num_used <= PGT_PARENT_TBL_COUNT);
150*52a75a25SMarouene Boubakri 	if (parent->num_used == PGT_PARENT_TBL_COUNT)
151*52a75a25SMarouene Boubakri 		SLIST_REMOVE_HEAD(&parent_list, link);
152*52a75a25SMarouene Boubakri 
153*52a75a25SMarouene Boubakri 	pgt->vabase = vabase;
154*52a75a25SMarouene Boubakri out:
155*52a75a25SMarouene Boubakri 	cpu_spin_unlock_xrestore(&parent_spinlock, exceptions);
156*52a75a25SMarouene Boubakri 	return pgt;
157*52a75a25SMarouene Boubakri }
158*52a75a25SMarouene Boubakri 
159*52a75a25SMarouene Boubakri static bool pgt_entry_matches(struct pgt *p, vaddr_t begin, vaddr_t last)
160*52a75a25SMarouene Boubakri {
161*52a75a25SMarouene Boubakri 	if (!p)
162*52a75a25SMarouene Boubakri 		return false;
163*52a75a25SMarouene Boubakri 	if (last <= begin)
164*52a75a25SMarouene Boubakri 		return false;
165*52a75a25SMarouene Boubakri 	return core_is_buffer_inside(p->vabase, CORE_MMU_PGDIR_SIZE, begin,
166*52a75a25SMarouene Boubakri 				     last - begin);
167*52a75a25SMarouene Boubakri }
168*52a75a25SMarouene Boubakri 
169*52a75a25SMarouene Boubakri void pgt_flush_range(struct user_mode_ctx *uctx, vaddr_t begin, vaddr_t last)
170*52a75a25SMarouene Boubakri {
171*52a75a25SMarouene Boubakri 	struct pgt_cache *pgt_cache = &uctx->pgt_cache;
172*52a75a25SMarouene Boubakri 	struct pgt *next_p = NULL;
173*52a75a25SMarouene Boubakri 	struct pgt *p = NULL;
174*52a75a25SMarouene Boubakri 
175*52a75a25SMarouene Boubakri 	/*
176*52a75a25SMarouene Boubakri 	 * Do the special case where the first element in the list is
177*52a75a25SMarouene Boubakri 	 * removed first.
178*52a75a25SMarouene Boubakri 	 */
179*52a75a25SMarouene Boubakri 	p = SLIST_FIRST(pgt_cache);
180*52a75a25SMarouene Boubakri 	while (pgt_entry_matches(p, begin, last)) {
181*52a75a25SMarouene Boubakri 		SLIST_REMOVE_HEAD(pgt_cache, link);
182*52a75a25SMarouene Boubakri 		free_pgt(p);
183*52a75a25SMarouene Boubakri 		p = SLIST_FIRST(pgt_cache);
184*52a75a25SMarouene Boubakri 	}
185*52a75a25SMarouene Boubakri 
186*52a75a25SMarouene Boubakri 	/*
187*52a75a25SMarouene Boubakri 	 * p either points to the first element in the list or it's NULL,
188*52a75a25SMarouene Boubakri 	 * if NULL the list is empty and we're done.
189*52a75a25SMarouene Boubakri 	 */
190*52a75a25SMarouene Boubakri 	if (!p)
191*52a75a25SMarouene Boubakri 		return;
192*52a75a25SMarouene Boubakri 
193*52a75a25SMarouene Boubakri 	/*
194*52a75a25SMarouene Boubakri 	 * Do the common case where the next element in the list is
195*52a75a25SMarouene Boubakri 	 * removed.
196*52a75a25SMarouene Boubakri 	 */
197*52a75a25SMarouene Boubakri 	while (true) {
198*52a75a25SMarouene Boubakri 		next_p = SLIST_NEXT(p, link);
199*52a75a25SMarouene Boubakri 		if (!next_p)
200*52a75a25SMarouene Boubakri 			break;
201*52a75a25SMarouene Boubakri 		if (pgt_entry_matches(next_p, begin, last)) {
202*52a75a25SMarouene Boubakri 			SLIST_REMOVE_AFTER(p, link);
203*52a75a25SMarouene Boubakri 			free_pgt(next_p);
204*52a75a25SMarouene Boubakri 			continue;
205*52a75a25SMarouene Boubakri 		}
206*52a75a25SMarouene Boubakri 
207*52a75a25SMarouene Boubakri 		p = SLIST_NEXT(p, link);
208*52a75a25SMarouene Boubakri 	}
209*52a75a25SMarouene Boubakri }
210*52a75a25SMarouene Boubakri 
211*52a75a25SMarouene Boubakri void pgt_flush(struct user_mode_ctx *uctx)
212*52a75a25SMarouene Boubakri {
213*52a75a25SMarouene Boubakri 	struct pgt_cache *pgt_cache = &uctx->pgt_cache;
214*52a75a25SMarouene Boubakri 	struct pgt *p = NULL;
215*52a75a25SMarouene Boubakri 
216*52a75a25SMarouene Boubakri 	while (true) {
217*52a75a25SMarouene Boubakri 		p = SLIST_FIRST(pgt_cache);
218*52a75a25SMarouene Boubakri 		if (!p)
219*52a75a25SMarouene Boubakri 			break;
220*52a75a25SMarouene Boubakri 		SLIST_REMOVE_HEAD(pgt_cache, link);
221*52a75a25SMarouene Boubakri 		free_pgt(p);
222*52a75a25SMarouene Boubakri 	}
223*52a75a25SMarouene Boubakri }
224*52a75a25SMarouene Boubakri 
225*52a75a25SMarouene Boubakri void pgt_clear_range(struct user_mode_ctx *uctx, vaddr_t begin, vaddr_t end)
226*52a75a25SMarouene Boubakri {
227*52a75a25SMarouene Boubakri 	struct pgt_cache *pgt_cache = &uctx->pgt_cache;
228*52a75a25SMarouene Boubakri 	struct pgt *p = NULL;
229*52a75a25SMarouene Boubakri #ifdef CFG_WITH_LPAE
230*52a75a25SMarouene Boubakri 	uint64_t *tbl = NULL;
231*52a75a25SMarouene Boubakri #else
232*52a75a25SMarouene Boubakri 	uint32_t *tbl = NULL;
233*52a75a25SMarouene Boubakri #endif
234*52a75a25SMarouene Boubakri 	unsigned int idx = 0;
235*52a75a25SMarouene Boubakri 	unsigned int n = 0;
236*52a75a25SMarouene Boubakri 
237*52a75a25SMarouene Boubakri 	SLIST_FOREACH(p, pgt_cache, link) {
238*52a75a25SMarouene Boubakri 		vaddr_t b = MAX(p->vabase, begin);
239*52a75a25SMarouene Boubakri 		vaddr_t e = MIN(p->vabase + CORE_MMU_PGDIR_SIZE, end);
240*52a75a25SMarouene Boubakri 
241*52a75a25SMarouene Boubakri 		if (b >= e)
242*52a75a25SMarouene Boubakri 			continue;
243*52a75a25SMarouene Boubakri 
244*52a75a25SMarouene Boubakri 		tbl = p->tbl;
245*52a75a25SMarouene Boubakri 		idx = (b - p->vabase) / SMALL_PAGE_SIZE;
246*52a75a25SMarouene Boubakri 		n = (e - b) / SMALL_PAGE_SIZE;
247*52a75a25SMarouene Boubakri 		memset(tbl + idx, 0, n * sizeof(*tbl));
248*52a75a25SMarouene Boubakri 	}
249*52a75a25SMarouene Boubakri }
250*52a75a25SMarouene Boubakri 
251*52a75a25SMarouene Boubakri static struct pgt *prune_before_va(struct pgt_cache *pgt_cache, struct pgt *p,
252*52a75a25SMarouene Boubakri 				   struct pgt *pp, vaddr_t va)
253*52a75a25SMarouene Boubakri {
254*52a75a25SMarouene Boubakri 	while (p && p->vabase < va) {
255*52a75a25SMarouene Boubakri 		if (pp) {
256*52a75a25SMarouene Boubakri 			assert(p == SLIST_NEXT(pp, link));
257*52a75a25SMarouene Boubakri 			SLIST_REMOVE_AFTER(pp, link);
258*52a75a25SMarouene Boubakri 			free_pgt(p);
259*52a75a25SMarouene Boubakri 			p = SLIST_NEXT(pp, link);
260*52a75a25SMarouene Boubakri 		} else {
261*52a75a25SMarouene Boubakri 			assert(p == SLIST_FIRST(pgt_cache));
262*52a75a25SMarouene Boubakri 			SLIST_REMOVE_HEAD(pgt_cache, link);
263*52a75a25SMarouene Boubakri 			free_pgt(p);
264*52a75a25SMarouene Boubakri 			p = SLIST_FIRST(pgt_cache);
265*52a75a25SMarouene Boubakri 		}
266*52a75a25SMarouene Boubakri 	}
267*52a75a25SMarouene Boubakri 
268*52a75a25SMarouene Boubakri 	return p;
269*52a75a25SMarouene Boubakri }
270*52a75a25SMarouene Boubakri 
271*52a75a25SMarouene Boubakri bool pgt_check_avail(struct user_mode_ctx *uctx)
272*52a75a25SMarouene Boubakri {
273*52a75a25SMarouene Boubakri 	struct pgt_cache *pgt_cache = &uctx->pgt_cache;
274*52a75a25SMarouene Boubakri 	struct vm_info *vm_info = &uctx->vm_info;
275*52a75a25SMarouene Boubakri 	struct pgt *p = SLIST_FIRST(pgt_cache);
276*52a75a25SMarouene Boubakri 	struct vm_region *r = NULL;
277*52a75a25SMarouene Boubakri 	struct pgt *pp = NULL;
278*52a75a25SMarouene Boubakri 	vaddr_t va = 0;
279*52a75a25SMarouene Boubakri 	bool p_used = false;
280*52a75a25SMarouene Boubakri 
281*52a75a25SMarouene Boubakri 	/*
282*52a75a25SMarouene Boubakri 	 * Prune unused tables. This is normally not needed since
283*52a75a25SMarouene Boubakri 	 * pgt_flush_range() does this too, but in the error path of for
284*52a75a25SMarouene Boubakri 	 * instance vm_remap() such calls may not be done. So for increased
285*52a75a25SMarouene Boubakri 	 * robustness remove all unused translation tables before we may
286*52a75a25SMarouene Boubakri 	 * allocate new ones.
287*52a75a25SMarouene Boubakri 	 */
288*52a75a25SMarouene Boubakri 	TAILQ_FOREACH(r, &vm_info->regions, link) {
289*52a75a25SMarouene Boubakri 		for (va = ROUNDDOWN(r->va, CORE_MMU_PGDIR_SIZE);
290*52a75a25SMarouene Boubakri 		     va < r->va + r->size; va += CORE_MMU_PGDIR_SIZE) {
291*52a75a25SMarouene Boubakri 			if (!p_used)
292*52a75a25SMarouene Boubakri 				p = prune_before_va(pgt_cache, p, pp, va);
293*52a75a25SMarouene Boubakri 			if (!p)
294*52a75a25SMarouene Boubakri 				goto prune_done;
295*52a75a25SMarouene Boubakri 
296*52a75a25SMarouene Boubakri 			if (p->vabase < va) {
297*52a75a25SMarouene Boubakri 				pp = p;
298*52a75a25SMarouene Boubakri 				p = SLIST_NEXT(pp, link);
299*52a75a25SMarouene Boubakri 				if (!p)
300*52a75a25SMarouene Boubakri 					goto prune_done;
301*52a75a25SMarouene Boubakri 				p_used = false;
302*52a75a25SMarouene Boubakri 			}
303*52a75a25SMarouene Boubakri 
304*52a75a25SMarouene Boubakri 			if (p->vabase == va)
305*52a75a25SMarouene Boubakri 				p_used = true;
306*52a75a25SMarouene Boubakri 		}
307*52a75a25SMarouene Boubakri 	}
308*52a75a25SMarouene Boubakri prune_done:
309*52a75a25SMarouene Boubakri 
310*52a75a25SMarouene Boubakri 	p = SLIST_FIRST(pgt_cache);
311*52a75a25SMarouene Boubakri 	pp = NULL;
312*52a75a25SMarouene Boubakri 	TAILQ_FOREACH(r, &vm_info->regions, link) {
313*52a75a25SMarouene Boubakri 		for (va = ROUNDDOWN(r->va, CORE_MMU_PGDIR_SIZE);
314*52a75a25SMarouene Boubakri 		     va < r->va + r->size; va += CORE_MMU_PGDIR_SIZE) {
315*52a75a25SMarouene Boubakri 			if (p && p->vabase < va) {
316*52a75a25SMarouene Boubakri 				pp = p;
317*52a75a25SMarouene Boubakri 				p = SLIST_NEXT(pp, link);
318*52a75a25SMarouene Boubakri 			}
319*52a75a25SMarouene Boubakri 
320*52a75a25SMarouene Boubakri 			if (p) {
321*52a75a25SMarouene Boubakri 				if (p->vabase == va)
322*52a75a25SMarouene Boubakri 					continue;
323*52a75a25SMarouene Boubakri 				assert(p->vabase > va);
324*52a75a25SMarouene Boubakri 			}
325*52a75a25SMarouene Boubakri 
326*52a75a25SMarouene Boubakri 			p = alloc_pgt(va);
327*52a75a25SMarouene Boubakri 			if (!p)
328*52a75a25SMarouene Boubakri 				return false;
329*52a75a25SMarouene Boubakri 
330*52a75a25SMarouene Boubakri 			if (pp)
331*52a75a25SMarouene Boubakri 				SLIST_INSERT_AFTER(pp, p, link);
332*52a75a25SMarouene Boubakri 			else
333*52a75a25SMarouene Boubakri 				SLIST_INSERT_HEAD(pgt_cache, p, link);
334*52a75a25SMarouene Boubakri 		}
335*52a75a25SMarouene Boubakri 	}
336*52a75a25SMarouene Boubakri 
337*52a75a25SMarouene Boubakri 	return true;
338*52a75a25SMarouene Boubakri }
339*52a75a25SMarouene Boubakri #else /* !CFG_CORE_PREALLOC_EL0_TBLS */
340*52a75a25SMarouene Boubakri 
341*52a75a25SMarouene Boubakri #if defined(CFG_WITH_PAGER) && !defined(CFG_WITH_LPAE)
342*52a75a25SMarouene Boubakri static struct pgt_parent pgt_parents[PGT_CACHE_SIZE / PGT_NUM_PGT_PER_PAGE];
343*52a75a25SMarouene Boubakri #else
344*52a75a25SMarouene Boubakri 
345*52a75a25SMarouene Boubakri static struct pgt_cache pgt_free_list = SLIST_HEAD_INITIALIZER(pgt_free_list);
346*52a75a25SMarouene Boubakri #endif
347*52a75a25SMarouene Boubakri 
348*52a75a25SMarouene Boubakri /*
349*52a75a25SMarouene Boubakri  * When a user TA context is temporarily unmapped the used struct pgt's of
350*52a75a25SMarouene Boubakri  * the context (page tables holding valid physical pages) are saved in this
351*52a75a25SMarouene Boubakri  * cache in the hope that it will remain in the cache when the context is
352*52a75a25SMarouene Boubakri  * mapped again.
353*52a75a25SMarouene Boubakri  */
354*52a75a25SMarouene Boubakri static struct pgt_cache pgt_cache_list = SLIST_HEAD_INITIALIZER(pgt_cache_list);
355*52a75a25SMarouene Boubakri 
356*52a75a25SMarouene Boubakri static struct pgt pgt_entries[PGT_CACHE_SIZE];
357*52a75a25SMarouene Boubakri 
358*52a75a25SMarouene Boubakri static struct mutex pgt_mu = MUTEX_INITIALIZER;
359*52a75a25SMarouene Boubakri static struct condvar pgt_cv = CONDVAR_INITIALIZER;
360*52a75a25SMarouene Boubakri 
361*52a75a25SMarouene Boubakri #if defined(CFG_WITH_PAGER) && defined(CFG_WITH_LPAE)
362*52a75a25SMarouene Boubakri /*
363*52a75a25SMarouene Boubakri  * Simple allocation of translation tables from pager, one translation
364*52a75a25SMarouene Boubakri  * table is one page.
365*52a75a25SMarouene Boubakri  */
366*52a75a25SMarouene Boubakri void pgt_init(void)
367*52a75a25SMarouene Boubakri {
368*52a75a25SMarouene Boubakri 	size_t n;
369*52a75a25SMarouene Boubakri 
370*52a75a25SMarouene Boubakri 	for (n = 0; n < PGT_CACHE_SIZE; n++) {
371*52a75a25SMarouene Boubakri 		struct pgt *p = pgt_entries + n;
372*52a75a25SMarouene Boubakri 
373*52a75a25SMarouene Boubakri 		p->tbl = tee_pager_alloc(PGT_SIZE);
374*52a75a25SMarouene Boubakri 		SLIST_INSERT_HEAD(&pgt_free_list, p, link);
375*52a75a25SMarouene Boubakri 	}
376*52a75a25SMarouene Boubakri }
377*52a75a25SMarouene Boubakri #elif defined(CFG_WITH_PAGER) && !defined(CFG_WITH_LPAE)
378*52a75a25SMarouene Boubakri /*
379*52a75a25SMarouene Boubakri  * Four translation tables per page -> need to keep track of the page
380*52a75a25SMarouene Boubakri  * allocated from the pager.
381*52a75a25SMarouene Boubakri  */
382*52a75a25SMarouene Boubakri void pgt_init(void)
383*52a75a25SMarouene Boubakri {
384*52a75a25SMarouene Boubakri 	size_t n;
385*52a75a25SMarouene Boubakri 	size_t m;
386*52a75a25SMarouene Boubakri 
387*52a75a25SMarouene Boubakri 	COMPILE_TIME_ASSERT(PGT_CACHE_SIZE % PGT_NUM_PGT_PER_PAGE == 0);
388*52a75a25SMarouene Boubakri 	COMPILE_TIME_ASSERT(PGT_SIZE * PGT_NUM_PGT_PER_PAGE == SMALL_PAGE_SIZE);
389*52a75a25SMarouene Boubakri 
390*52a75a25SMarouene Boubakri 	for (n = 0; n < ARRAY_SIZE(pgt_parents); n++) {
391*52a75a25SMarouene Boubakri 		uint8_t *tbl = tee_pager_alloc(SMALL_PAGE_SIZE);
392*52a75a25SMarouene Boubakri 
393*52a75a25SMarouene Boubakri 		SLIST_INIT(&pgt_parents[n].pgt_cache);
394*52a75a25SMarouene Boubakri 		for (m = 0; m < PGT_NUM_PGT_PER_PAGE; m++) {
395*52a75a25SMarouene Boubakri 			struct pgt *p = pgt_entries +
396*52a75a25SMarouene Boubakri 					n * PGT_NUM_PGT_PER_PAGE + m;
397*52a75a25SMarouene Boubakri 
398*52a75a25SMarouene Boubakri 			p->tbl = tbl + m * PGT_SIZE;
399*52a75a25SMarouene Boubakri 			p->parent = &pgt_parents[n];
400*52a75a25SMarouene Boubakri 			SLIST_INSERT_HEAD(&pgt_parents[n].pgt_cache, p, link);
401*52a75a25SMarouene Boubakri 		}
402*52a75a25SMarouene Boubakri 	}
403*52a75a25SMarouene Boubakri }
404*52a75a25SMarouene Boubakri #else
405*52a75a25SMarouene Boubakri /* Static allocation of translation tables */
406*52a75a25SMarouene Boubakri void pgt_init(void)
407*52a75a25SMarouene Boubakri {
408*52a75a25SMarouene Boubakri 	/*
409*52a75a25SMarouene Boubakri 	 * We're putting this in .nozi.* instead of .bss because .nozi.* already
410*52a75a25SMarouene Boubakri 	 * has a large alignment, while .bss has a small alignment. The current
411*52a75a25SMarouene Boubakri 	 * link script is optimized for small alignment in .bss
412*52a75a25SMarouene Boubakri 	 */
413*52a75a25SMarouene Boubakri 	static uint8_t pgt_tables[PGT_CACHE_SIZE][PGT_SIZE]
414*52a75a25SMarouene Boubakri 			__aligned(PGT_SIZE) __section(".nozi.pgt_cache");
415*52a75a25SMarouene Boubakri 	size_t n;
416*52a75a25SMarouene Boubakri 
417*52a75a25SMarouene Boubakri 	for (n = 0; n < ARRAY_SIZE(pgt_tables); n++) {
418*52a75a25SMarouene Boubakri 		struct pgt *p = pgt_entries + n;
419*52a75a25SMarouene Boubakri 
420*52a75a25SMarouene Boubakri 		p->tbl = pgt_tables[n];
421*52a75a25SMarouene Boubakri 		SLIST_INSERT_HEAD(&pgt_free_list, p, link);
422*52a75a25SMarouene Boubakri 	}
423*52a75a25SMarouene Boubakri }
424*52a75a25SMarouene Boubakri #endif
425*52a75a25SMarouene Boubakri 
426*52a75a25SMarouene Boubakri #if defined(CFG_WITH_LPAE) || !defined(CFG_WITH_PAGER)
427*52a75a25SMarouene Boubakri /* Simple allocation of translation tables from pager or static allocation */
428*52a75a25SMarouene Boubakri static struct pgt *pop_from_free_list(void)
429*52a75a25SMarouene Boubakri {
430*52a75a25SMarouene Boubakri 	struct pgt *p = SLIST_FIRST(&pgt_free_list);
431*52a75a25SMarouene Boubakri 
432*52a75a25SMarouene Boubakri 	if (p) {
433*52a75a25SMarouene Boubakri 		SLIST_REMOVE_HEAD(&pgt_free_list, link);
434*52a75a25SMarouene Boubakri 		memset(p->tbl, 0, PGT_SIZE);
435*52a75a25SMarouene Boubakri 		p->populated = false;
436*52a75a25SMarouene Boubakri 	}
437*52a75a25SMarouene Boubakri 	return p;
438*52a75a25SMarouene Boubakri }
439*52a75a25SMarouene Boubakri 
440*52a75a25SMarouene Boubakri static void push_to_free_list(struct pgt *p)
441*52a75a25SMarouene Boubakri {
442*52a75a25SMarouene Boubakri 	SLIST_INSERT_HEAD(&pgt_free_list, p, link);
443*52a75a25SMarouene Boubakri #if defined(CFG_WITH_PAGER)
444*52a75a25SMarouene Boubakri 	tee_pager_release_phys(p->tbl, PGT_SIZE);
445*52a75a25SMarouene Boubakri #endif
446*52a75a25SMarouene Boubakri }
447*52a75a25SMarouene Boubakri #else
448*52a75a25SMarouene Boubakri /*
449*52a75a25SMarouene Boubakri  * Four translation tables per page -> need to keep track of the page
450*52a75a25SMarouene Boubakri  * allocated from the pager.
451*52a75a25SMarouene Boubakri  */
452*52a75a25SMarouene Boubakri static struct pgt *pop_from_free_list(void)
453*52a75a25SMarouene Boubakri {
454*52a75a25SMarouene Boubakri 	size_t n;
455*52a75a25SMarouene Boubakri 
456*52a75a25SMarouene Boubakri 	for (n = 0; n < ARRAY_SIZE(pgt_parents); n++) {
457*52a75a25SMarouene Boubakri 		struct pgt *p = SLIST_FIRST(&pgt_parents[n].pgt_cache);
458*52a75a25SMarouene Boubakri 
459*52a75a25SMarouene Boubakri 		if (p) {
460*52a75a25SMarouene Boubakri 			SLIST_REMOVE_HEAD(&pgt_parents[n].pgt_cache, link);
461*52a75a25SMarouene Boubakri 			pgt_parents[n].num_used++;
462*52a75a25SMarouene Boubakri 			memset(p->tbl, 0, PGT_SIZE);
463*52a75a25SMarouene Boubakri 			p->populated = false;
464*52a75a25SMarouene Boubakri 			return p;
465*52a75a25SMarouene Boubakri 		}
466*52a75a25SMarouene Boubakri 	}
467*52a75a25SMarouene Boubakri 	return NULL;
468*52a75a25SMarouene Boubakri }
469*52a75a25SMarouene Boubakri 
470*52a75a25SMarouene Boubakri static void push_to_free_list(struct pgt *p)
471*52a75a25SMarouene Boubakri {
472*52a75a25SMarouene Boubakri 	SLIST_INSERT_HEAD(&p->parent->pgt_cache, p, link);
473*52a75a25SMarouene Boubakri 	assert(p->parent->num_used > 0);
474*52a75a25SMarouene Boubakri 	p->parent->num_used--;
475*52a75a25SMarouene Boubakri 	if (!p->parent->num_used) {
476*52a75a25SMarouene Boubakri 		vaddr_t va = (vaddr_t)p->tbl & ~SMALL_PAGE_MASK;
477*52a75a25SMarouene Boubakri 
478*52a75a25SMarouene Boubakri 		tee_pager_release_phys((void *)va, SMALL_PAGE_SIZE);
479*52a75a25SMarouene Boubakri 	}
480*52a75a25SMarouene Boubakri }
481*52a75a25SMarouene Boubakri #endif
482*52a75a25SMarouene Boubakri 
483*52a75a25SMarouene Boubakri static void push_to_cache_list(struct pgt *pgt)
484*52a75a25SMarouene Boubakri {
485*52a75a25SMarouene Boubakri 	SLIST_INSERT_HEAD(&pgt_cache_list, pgt, link);
486*52a75a25SMarouene Boubakri }
487*52a75a25SMarouene Boubakri 
488*52a75a25SMarouene Boubakri static bool match_pgt(struct pgt *pgt, vaddr_t vabase, void *ctx)
489*52a75a25SMarouene Boubakri {
490*52a75a25SMarouene Boubakri 	return pgt->ctx == ctx && pgt->vabase == vabase;
491*52a75a25SMarouene Boubakri }
492*52a75a25SMarouene Boubakri 
493*52a75a25SMarouene Boubakri static struct pgt *pop_from_cache_list(vaddr_t vabase, void *ctx)
494*52a75a25SMarouene Boubakri {
495*52a75a25SMarouene Boubakri 	struct pgt *pgt;
496*52a75a25SMarouene Boubakri 	struct pgt *p;
497*52a75a25SMarouene Boubakri 
498*52a75a25SMarouene Boubakri 	pgt = SLIST_FIRST(&pgt_cache_list);
499*52a75a25SMarouene Boubakri 	if (!pgt)
500*52a75a25SMarouene Boubakri 		return NULL;
501*52a75a25SMarouene Boubakri 	if (match_pgt(pgt, vabase, ctx)) {
502*52a75a25SMarouene Boubakri 		SLIST_REMOVE_HEAD(&pgt_cache_list, link);
503*52a75a25SMarouene Boubakri 		return pgt;
504*52a75a25SMarouene Boubakri 	}
505*52a75a25SMarouene Boubakri 
506*52a75a25SMarouene Boubakri 	while (true) {
507*52a75a25SMarouene Boubakri 		p = SLIST_NEXT(pgt, link);
508*52a75a25SMarouene Boubakri 		if (!p)
509*52a75a25SMarouene Boubakri 			break;
510*52a75a25SMarouene Boubakri 		if (match_pgt(p, vabase, ctx)) {
511*52a75a25SMarouene Boubakri 			SLIST_REMOVE_AFTER(pgt, link);
512*52a75a25SMarouene Boubakri 			break;
513*52a75a25SMarouene Boubakri 		}
514*52a75a25SMarouene Boubakri 		pgt = p;
515*52a75a25SMarouene Boubakri 	}
516*52a75a25SMarouene Boubakri 	return p;
517*52a75a25SMarouene Boubakri }
518*52a75a25SMarouene Boubakri 
519*52a75a25SMarouene Boubakri static uint16_t get_num_used_entries(struct pgt *pgt __maybe_unused)
520*52a75a25SMarouene Boubakri {
521*52a75a25SMarouene Boubakri #ifdef CFG_PAGED_USER_TA
522*52a75a25SMarouene Boubakri 	return pgt->num_used_entries;
523*52a75a25SMarouene Boubakri #else
524*52a75a25SMarouene Boubakri 	return 0;
525*52a75a25SMarouene Boubakri #endif
526*52a75a25SMarouene Boubakri }
527*52a75a25SMarouene Boubakri 
528*52a75a25SMarouene Boubakri static struct pgt *pop_least_used_from_cache_list(void)
529*52a75a25SMarouene Boubakri {
530*52a75a25SMarouene Boubakri 	struct pgt *pgt = NULL;
531*52a75a25SMarouene Boubakri 	struct pgt *p_prev = NULL;
532*52a75a25SMarouene Boubakri 	size_t least_used = 0;
533*52a75a25SMarouene Boubakri 	size_t next_used = 0;
534*52a75a25SMarouene Boubakri 
535*52a75a25SMarouene Boubakri 	pgt = SLIST_FIRST(&pgt_cache_list);
536*52a75a25SMarouene Boubakri 	if (!pgt)
537*52a75a25SMarouene Boubakri 		return NULL;
538*52a75a25SMarouene Boubakri 	least_used = get_num_used_entries(pgt);
539*52a75a25SMarouene Boubakri 
540*52a75a25SMarouene Boubakri 	while (true) {
541*52a75a25SMarouene Boubakri 		if (!SLIST_NEXT(pgt, link))
542*52a75a25SMarouene Boubakri 			break;
543*52a75a25SMarouene Boubakri 		next_used = get_num_used_entries(SLIST_NEXT(pgt, link));
544*52a75a25SMarouene Boubakri 		if (next_used <= least_used) {
545*52a75a25SMarouene Boubakri 			p_prev = pgt;
546*52a75a25SMarouene Boubakri 			least_used = next_used;
547*52a75a25SMarouene Boubakri 		}
548*52a75a25SMarouene Boubakri 		pgt = SLIST_NEXT(pgt, link);
549*52a75a25SMarouene Boubakri 	}
550*52a75a25SMarouene Boubakri 
551*52a75a25SMarouene Boubakri 	if (p_prev) {
552*52a75a25SMarouene Boubakri 		pgt = SLIST_NEXT(p_prev, link);
553*52a75a25SMarouene Boubakri 		SLIST_REMOVE_AFTER(p_prev, link);
554*52a75a25SMarouene Boubakri 	} else {
555*52a75a25SMarouene Boubakri 		pgt = SLIST_FIRST(&pgt_cache_list);
556*52a75a25SMarouene Boubakri 		SLIST_REMOVE_HEAD(&pgt_cache_list, link);
557*52a75a25SMarouene Boubakri 	}
558*52a75a25SMarouene Boubakri 	return pgt;
559*52a75a25SMarouene Boubakri }
560*52a75a25SMarouene Boubakri 
561*52a75a25SMarouene Boubakri static void pgt_free_unlocked(struct pgt_cache *pgt_cache)
562*52a75a25SMarouene Boubakri {
563*52a75a25SMarouene Boubakri 	while (!SLIST_EMPTY(pgt_cache)) {
564*52a75a25SMarouene Boubakri 		struct pgt *p = SLIST_FIRST(pgt_cache);
565*52a75a25SMarouene Boubakri 
566*52a75a25SMarouene Boubakri 		SLIST_REMOVE_HEAD(pgt_cache, link);
567*52a75a25SMarouene Boubakri 
568*52a75a25SMarouene Boubakri 		/*
569*52a75a25SMarouene Boubakri 		 * With paging enabled we free all tables which doesn't
570*52a75a25SMarouene Boubakri 		 * refer to any paged pages any longer. This reduces the
571*52a75a25SMarouene Boubakri 		 * pressure the pool of physical pages.
572*52a75a25SMarouene Boubakri 		 */
573*52a75a25SMarouene Boubakri 		if (IS_ENABLED(CFG_PAGED_USER_TA) && !get_num_used_entries(p)) {
574*52a75a25SMarouene Boubakri 			tee_pager_pgt_save_and_release_entries(p);
575*52a75a25SMarouene Boubakri 			p->ctx = NULL;
576*52a75a25SMarouene Boubakri 			p->vabase = 0;
577*52a75a25SMarouene Boubakri 
578*52a75a25SMarouene Boubakri 			push_to_free_list(p);
579*52a75a25SMarouene Boubakri 			continue;
580*52a75a25SMarouene Boubakri 		}
581*52a75a25SMarouene Boubakri 
582*52a75a25SMarouene Boubakri 		push_to_cache_list(p);
583*52a75a25SMarouene Boubakri 	}
584*52a75a25SMarouene Boubakri }
585*52a75a25SMarouene Boubakri 
586*52a75a25SMarouene Boubakri static struct pgt *pop_from_some_list(vaddr_t vabase, void *ctx)
587*52a75a25SMarouene Boubakri {
588*52a75a25SMarouene Boubakri 	struct pgt *p = pop_from_cache_list(vabase, ctx);
589*52a75a25SMarouene Boubakri 
590*52a75a25SMarouene Boubakri 	if (p)
591*52a75a25SMarouene Boubakri 		return p;
592*52a75a25SMarouene Boubakri 	p = pop_from_free_list();
593*52a75a25SMarouene Boubakri 	if (!p) {
594*52a75a25SMarouene Boubakri 		p = pop_least_used_from_cache_list();
595*52a75a25SMarouene Boubakri 		if (!p)
596*52a75a25SMarouene Boubakri 			return NULL;
597*52a75a25SMarouene Boubakri 		tee_pager_pgt_save_and_release_entries(p);
598*52a75a25SMarouene Boubakri 		memset(p->tbl, 0, PGT_SIZE);
599*52a75a25SMarouene Boubakri 		p->populated = false;
600*52a75a25SMarouene Boubakri 	}
601*52a75a25SMarouene Boubakri 	p->ctx = ctx;
602*52a75a25SMarouene Boubakri 	p->vabase = vabase;
603*52a75a25SMarouene Boubakri 	return p;
604*52a75a25SMarouene Boubakri }
605*52a75a25SMarouene Boubakri 
606*52a75a25SMarouene Boubakri void pgt_flush(struct user_mode_ctx *uctx)
607*52a75a25SMarouene Boubakri {
608*52a75a25SMarouene Boubakri 	struct ts_ctx *ctx = uctx->ts_ctx;
609*52a75a25SMarouene Boubakri 	struct pgt *pp = NULL;
610*52a75a25SMarouene Boubakri 	struct pgt *p = NULL;
611*52a75a25SMarouene Boubakri 
612*52a75a25SMarouene Boubakri 	mutex_lock(&pgt_mu);
613*52a75a25SMarouene Boubakri 
614*52a75a25SMarouene Boubakri 	while (true) {
615*52a75a25SMarouene Boubakri 		p = SLIST_FIRST(&pgt_cache_list);
616*52a75a25SMarouene Boubakri 		if (!p)
617*52a75a25SMarouene Boubakri 			goto out;
618*52a75a25SMarouene Boubakri 		if (p->ctx != ctx)
619*52a75a25SMarouene Boubakri 			break;
620*52a75a25SMarouene Boubakri 		SLIST_REMOVE_HEAD(&pgt_cache_list, link);
621*52a75a25SMarouene Boubakri 		tee_pager_pgt_save_and_release_entries(p);
622*52a75a25SMarouene Boubakri 		p->ctx = NULL;
623*52a75a25SMarouene Boubakri 		p->vabase = 0;
624*52a75a25SMarouene Boubakri 		push_to_free_list(p);
625*52a75a25SMarouene Boubakri 	}
626*52a75a25SMarouene Boubakri 
627*52a75a25SMarouene Boubakri 	pp = p;
628*52a75a25SMarouene Boubakri 	while (true) {
629*52a75a25SMarouene Boubakri 		p = SLIST_NEXT(pp, link);
630*52a75a25SMarouene Boubakri 		if (!p)
631*52a75a25SMarouene Boubakri 			break;
632*52a75a25SMarouene Boubakri 		if (p->ctx == ctx) {
633*52a75a25SMarouene Boubakri 			SLIST_REMOVE_AFTER(pp, link);
634*52a75a25SMarouene Boubakri 			tee_pager_pgt_save_and_release_entries(p);
635*52a75a25SMarouene Boubakri 			p->ctx = NULL;
636*52a75a25SMarouene Boubakri 			p->vabase = 0;
637*52a75a25SMarouene Boubakri 			push_to_free_list(p);
638*52a75a25SMarouene Boubakri 		} else {
639*52a75a25SMarouene Boubakri 			pp = p;
640*52a75a25SMarouene Boubakri 		}
641*52a75a25SMarouene Boubakri 	}
642*52a75a25SMarouene Boubakri 
643*52a75a25SMarouene Boubakri out:
644*52a75a25SMarouene Boubakri 	mutex_unlock(&pgt_mu);
645*52a75a25SMarouene Boubakri }
646*52a75a25SMarouene Boubakri 
647*52a75a25SMarouene Boubakri static void flush_pgt_entry(struct pgt *p)
648*52a75a25SMarouene Boubakri {
649*52a75a25SMarouene Boubakri 	tee_pager_pgt_save_and_release_entries(p);
650*52a75a25SMarouene Boubakri 	p->ctx = NULL;
651*52a75a25SMarouene Boubakri 	p->vabase = 0;
652*52a75a25SMarouene Boubakri }
653*52a75a25SMarouene Boubakri 
654*52a75a25SMarouene Boubakri static bool pgt_entry_matches(struct pgt *p, void *ctx, vaddr_t begin,
655*52a75a25SMarouene Boubakri 			      vaddr_t last)
656*52a75a25SMarouene Boubakri {
657*52a75a25SMarouene Boubakri 	if (!p)
658*52a75a25SMarouene Boubakri 		return false;
659*52a75a25SMarouene Boubakri 	if (p->ctx != ctx)
660*52a75a25SMarouene Boubakri 		return false;
661*52a75a25SMarouene Boubakri 	if (last <= begin)
662*52a75a25SMarouene Boubakri 		return false;
663*52a75a25SMarouene Boubakri 	if (!core_is_buffer_inside(p->vabase, CORE_MMU_PGDIR_SIZE, begin,
664*52a75a25SMarouene Boubakri 				   last - begin))
665*52a75a25SMarouene Boubakri 		return false;
666*52a75a25SMarouene Boubakri 
667*52a75a25SMarouene Boubakri 	return true;
668*52a75a25SMarouene Boubakri }
669*52a75a25SMarouene Boubakri 
670*52a75a25SMarouene Boubakri static void flush_ctx_range_from_list(struct pgt_cache *pgt_cache, void *ctx,
671*52a75a25SMarouene Boubakri 				      vaddr_t begin, vaddr_t last)
672*52a75a25SMarouene Boubakri {
673*52a75a25SMarouene Boubakri 	struct pgt *p;
674*52a75a25SMarouene Boubakri 	struct pgt *next_p;
675*52a75a25SMarouene Boubakri 
676*52a75a25SMarouene Boubakri 	/*
677*52a75a25SMarouene Boubakri 	 * Do the special case where the first element in the list is
678*52a75a25SMarouene Boubakri 	 * removed first.
679*52a75a25SMarouene Boubakri 	 */
680*52a75a25SMarouene Boubakri 	p = SLIST_FIRST(pgt_cache);
681*52a75a25SMarouene Boubakri 	while (pgt_entry_matches(p, ctx, begin, last)) {
682*52a75a25SMarouene Boubakri 		flush_pgt_entry(p);
683*52a75a25SMarouene Boubakri 		SLIST_REMOVE_HEAD(pgt_cache, link);
684*52a75a25SMarouene Boubakri 		push_to_free_list(p);
685*52a75a25SMarouene Boubakri 		p = SLIST_FIRST(pgt_cache);
686*52a75a25SMarouene Boubakri 	}
687*52a75a25SMarouene Boubakri 
688*52a75a25SMarouene Boubakri 	/*
689*52a75a25SMarouene Boubakri 	 * p either points to the first element in the list or it's NULL,
690*52a75a25SMarouene Boubakri 	 * if NULL the list is empty and we're done.
691*52a75a25SMarouene Boubakri 	 */
692*52a75a25SMarouene Boubakri 	if (!p)
693*52a75a25SMarouene Boubakri 		return;
694*52a75a25SMarouene Boubakri 
695*52a75a25SMarouene Boubakri 	/*
696*52a75a25SMarouene Boubakri 	 * Do the common case where the next element in the list is
697*52a75a25SMarouene Boubakri 	 * removed.
698*52a75a25SMarouene Boubakri 	 */
699*52a75a25SMarouene Boubakri 	while (true) {
700*52a75a25SMarouene Boubakri 		next_p = SLIST_NEXT(p, link);
701*52a75a25SMarouene Boubakri 		if (!next_p)
702*52a75a25SMarouene Boubakri 			break;
703*52a75a25SMarouene Boubakri 		if (pgt_entry_matches(next_p, ctx, begin, last)) {
704*52a75a25SMarouene Boubakri 			flush_pgt_entry(next_p);
705*52a75a25SMarouene Boubakri 			SLIST_REMOVE_AFTER(p, link);
706*52a75a25SMarouene Boubakri 			push_to_free_list(next_p);
707*52a75a25SMarouene Boubakri 			continue;
708*52a75a25SMarouene Boubakri 		}
709*52a75a25SMarouene Boubakri 
710*52a75a25SMarouene Boubakri 		p = SLIST_NEXT(p, link);
711*52a75a25SMarouene Boubakri 	}
712*52a75a25SMarouene Boubakri }
713*52a75a25SMarouene Boubakri 
714*52a75a25SMarouene Boubakri void pgt_flush_range(struct user_mode_ctx *uctx, vaddr_t begin, vaddr_t last)
715*52a75a25SMarouene Boubakri {
716*52a75a25SMarouene Boubakri 	struct pgt_cache *pgt_cache = &uctx->pgt_cache;
717*52a75a25SMarouene Boubakri 	struct ts_ctx *ctx = uctx->ts_ctx;
718*52a75a25SMarouene Boubakri 
719*52a75a25SMarouene Boubakri 	mutex_lock(&pgt_mu);
720*52a75a25SMarouene Boubakri 
721*52a75a25SMarouene Boubakri 	flush_ctx_range_from_list(pgt_cache, ctx, begin, last);
722*52a75a25SMarouene Boubakri 	flush_ctx_range_from_list(&pgt_cache_list, ctx, begin, last);
723*52a75a25SMarouene Boubakri 
724*52a75a25SMarouene Boubakri 	condvar_broadcast(&pgt_cv);
725*52a75a25SMarouene Boubakri 	mutex_unlock(&pgt_mu);
726*52a75a25SMarouene Boubakri }
727*52a75a25SMarouene Boubakri 
728*52a75a25SMarouene Boubakri static void clear_ctx_range_from_list(struct pgt_cache *pgt_cache,
729*52a75a25SMarouene Boubakri 				      void *ctx, vaddr_t begin, vaddr_t end)
730*52a75a25SMarouene Boubakri {
731*52a75a25SMarouene Boubakri 	struct pgt *p = NULL;
732*52a75a25SMarouene Boubakri #ifdef CFG_WITH_LPAE
733*52a75a25SMarouene Boubakri 	uint64_t *tbl = NULL;
734*52a75a25SMarouene Boubakri #else
735*52a75a25SMarouene Boubakri 	uint32_t *tbl = NULL;
736*52a75a25SMarouene Boubakri #endif
737*52a75a25SMarouene Boubakri 	unsigned int idx = 0;
738*52a75a25SMarouene Boubakri 	unsigned int n = 0;
739*52a75a25SMarouene Boubakri 
740*52a75a25SMarouene Boubakri 	SLIST_FOREACH(p, pgt_cache, link) {
741*52a75a25SMarouene Boubakri 		vaddr_t b = MAX(p->vabase, begin);
742*52a75a25SMarouene Boubakri 		vaddr_t e = MIN(p->vabase + CORE_MMU_PGDIR_SIZE, end);
743*52a75a25SMarouene Boubakri 
744*52a75a25SMarouene Boubakri 		if (p->ctx != ctx)
745*52a75a25SMarouene Boubakri 			continue;
746*52a75a25SMarouene Boubakri 		if (b >= e)
747*52a75a25SMarouene Boubakri 			continue;
748*52a75a25SMarouene Boubakri 
749*52a75a25SMarouene Boubakri 		tbl = p->tbl;
750*52a75a25SMarouene Boubakri 		idx = (b - p->vabase) / SMALL_PAGE_SIZE;
751*52a75a25SMarouene Boubakri 		n = (e - b) / SMALL_PAGE_SIZE;
752*52a75a25SMarouene Boubakri 		memset(tbl + idx, 0, n * sizeof(*tbl));
753*52a75a25SMarouene Boubakri 	}
754*52a75a25SMarouene Boubakri }
755*52a75a25SMarouene Boubakri 
756*52a75a25SMarouene Boubakri void pgt_clear_range(struct user_mode_ctx *uctx, vaddr_t begin, vaddr_t end)
757*52a75a25SMarouene Boubakri {
758*52a75a25SMarouene Boubakri 	struct pgt_cache *pgt_cache = &uctx->pgt_cache;
759*52a75a25SMarouene Boubakri 	struct ts_ctx *ctx = uctx->ts_ctx;
760*52a75a25SMarouene Boubakri 
761*52a75a25SMarouene Boubakri 	mutex_lock(&pgt_mu);
762*52a75a25SMarouene Boubakri 
763*52a75a25SMarouene Boubakri 	clear_ctx_range_from_list(pgt_cache, ctx, begin, end);
764*52a75a25SMarouene Boubakri 	clear_ctx_range_from_list(&pgt_cache_list, ctx, begin, end);
765*52a75a25SMarouene Boubakri 
766*52a75a25SMarouene Boubakri 	mutex_unlock(&pgt_mu);
767*52a75a25SMarouene Boubakri }
768*52a75a25SMarouene Boubakri 
769*52a75a25SMarouene Boubakri static bool pgt_alloc_unlocked(struct pgt_cache *pgt_cache, struct ts_ctx *ctx,
770*52a75a25SMarouene Boubakri 			       struct vm_info *vm_info)
771*52a75a25SMarouene Boubakri {
772*52a75a25SMarouene Boubakri 	struct vm_region *r = NULL;
773*52a75a25SMarouene Boubakri 	struct pgt *pp = NULL;
774*52a75a25SMarouene Boubakri 	struct pgt *p = NULL;
775*52a75a25SMarouene Boubakri 	vaddr_t va = 0;
776*52a75a25SMarouene Boubakri 
777*52a75a25SMarouene Boubakri 	TAILQ_FOREACH(r, &vm_info->regions, link) {
778*52a75a25SMarouene Boubakri 		for (va = ROUNDDOWN(r->va, CORE_MMU_PGDIR_SIZE);
779*52a75a25SMarouene Boubakri 		     va < r->va + r->size; va += CORE_MMU_PGDIR_SIZE) {
780*52a75a25SMarouene Boubakri 			if (p && p->vabase == va)
781*52a75a25SMarouene Boubakri 				continue;
782*52a75a25SMarouene Boubakri 			p = pop_from_some_list(va, ctx);
783*52a75a25SMarouene Boubakri 			if (!p) {
784*52a75a25SMarouene Boubakri 				pgt_free_unlocked(pgt_cache);
785*52a75a25SMarouene Boubakri 				return false;
786*52a75a25SMarouene Boubakri 			}
787*52a75a25SMarouene Boubakri 			if (pp)
788*52a75a25SMarouene Boubakri 				SLIST_INSERT_AFTER(pp, p, link);
789*52a75a25SMarouene Boubakri 			else
790*52a75a25SMarouene Boubakri 				SLIST_INSERT_HEAD(pgt_cache, p, link);
791*52a75a25SMarouene Boubakri 			pp = p;
792*52a75a25SMarouene Boubakri 		}
793*52a75a25SMarouene Boubakri 	}
794*52a75a25SMarouene Boubakri 
795*52a75a25SMarouene Boubakri 	return true;
796*52a75a25SMarouene Boubakri }
797*52a75a25SMarouene Boubakri 
798*52a75a25SMarouene Boubakri bool pgt_check_avail(struct user_mode_ctx *uctx)
799*52a75a25SMarouene Boubakri {
800*52a75a25SMarouene Boubakri 	struct vm_info *vm_info = &uctx->vm_info;
801*52a75a25SMarouene Boubakri 	struct vm_region *r = NULL;
802*52a75a25SMarouene Boubakri 	size_t tbl_count = 0;
803*52a75a25SMarouene Boubakri 	vaddr_t last_va = 0;
804*52a75a25SMarouene Boubakri 	vaddr_t va = 0;
805*52a75a25SMarouene Boubakri 
806*52a75a25SMarouene Boubakri 	TAILQ_FOREACH(r, &vm_info->regions, link) {
807*52a75a25SMarouene Boubakri 		for (va = ROUNDDOWN(r->va, CORE_MMU_PGDIR_SIZE);
808*52a75a25SMarouene Boubakri 		     va < r->va + r->size; va += CORE_MMU_PGDIR_SIZE) {
809*52a75a25SMarouene Boubakri 			if (va == last_va)
810*52a75a25SMarouene Boubakri 				continue;
811*52a75a25SMarouene Boubakri 			tbl_count++;
812*52a75a25SMarouene Boubakri 			last_va = va;
813*52a75a25SMarouene Boubakri 		}
814*52a75a25SMarouene Boubakri 	}
815*52a75a25SMarouene Boubakri 
816*52a75a25SMarouene Boubakri 	return tbl_count <= PGT_CACHE_SIZE;
817*52a75a25SMarouene Boubakri }
818*52a75a25SMarouene Boubakri 
819*52a75a25SMarouene Boubakri void pgt_get_all(struct user_mode_ctx *uctx)
820*52a75a25SMarouene Boubakri {
821*52a75a25SMarouene Boubakri 	struct pgt_cache *pgt_cache = &uctx->pgt_cache;
822*52a75a25SMarouene Boubakri 	struct vm_info *vm_info = &uctx->vm_info;
823*52a75a25SMarouene Boubakri 
824*52a75a25SMarouene Boubakri 	if (TAILQ_EMPTY(&vm_info->regions))
825*52a75a25SMarouene Boubakri 		return;
826*52a75a25SMarouene Boubakri 
827*52a75a25SMarouene Boubakri 	mutex_lock(&pgt_mu);
828*52a75a25SMarouene Boubakri 
829*52a75a25SMarouene Boubakri 	pgt_free_unlocked(pgt_cache);
830*52a75a25SMarouene Boubakri 	while (!pgt_alloc_unlocked(pgt_cache, uctx->ts_ctx, vm_info)) {
831*52a75a25SMarouene Boubakri 		assert(pgt_check_avail(uctx));
832*52a75a25SMarouene Boubakri 		DMSG("Waiting for page tables");
833*52a75a25SMarouene Boubakri 		condvar_broadcast(&pgt_cv);
834*52a75a25SMarouene Boubakri 		condvar_wait(&pgt_cv, &pgt_mu);
835*52a75a25SMarouene Boubakri 	}
836*52a75a25SMarouene Boubakri 
837*52a75a25SMarouene Boubakri 	mutex_unlock(&pgt_mu);
838*52a75a25SMarouene Boubakri }
839*52a75a25SMarouene Boubakri 
840*52a75a25SMarouene Boubakri void pgt_put_all(struct user_mode_ctx *uctx)
841*52a75a25SMarouene Boubakri {
842*52a75a25SMarouene Boubakri 	struct pgt_cache *pgt_cache = &uctx->pgt_cache;
843*52a75a25SMarouene Boubakri 
844*52a75a25SMarouene Boubakri 	if (SLIST_EMPTY(pgt_cache))
845*52a75a25SMarouene Boubakri 		return;
846*52a75a25SMarouene Boubakri 
847*52a75a25SMarouene Boubakri 	mutex_lock(&pgt_mu);
848*52a75a25SMarouene Boubakri 
849*52a75a25SMarouene Boubakri 	pgt_free_unlocked(pgt_cache);
850*52a75a25SMarouene Boubakri 
851*52a75a25SMarouene Boubakri 	condvar_broadcast(&pgt_cv);
852*52a75a25SMarouene Boubakri 	mutex_unlock(&pgt_mu);
853*52a75a25SMarouene Boubakri }
854*52a75a25SMarouene Boubakri 
855*52a75a25SMarouene Boubakri struct pgt *pgt_pop_from_cache_list(vaddr_t vabase, struct ts_ctx *ctx)
856*52a75a25SMarouene Boubakri {
857*52a75a25SMarouene Boubakri 	struct pgt *pgt = NULL;
858*52a75a25SMarouene Boubakri 
859*52a75a25SMarouene Boubakri 	mutex_lock(&pgt_mu);
860*52a75a25SMarouene Boubakri 	pgt = pop_from_cache_list(vabase, ctx);
861*52a75a25SMarouene Boubakri 	mutex_unlock(&pgt_mu);
862*52a75a25SMarouene Boubakri 
863*52a75a25SMarouene Boubakri 	return pgt;
864*52a75a25SMarouene Boubakri }
865*52a75a25SMarouene Boubakri 
866*52a75a25SMarouene Boubakri void pgt_push_to_cache_list(struct pgt *pgt)
867*52a75a25SMarouene Boubakri {
868*52a75a25SMarouene Boubakri 	mutex_lock(&pgt_mu);
869*52a75a25SMarouene Boubakri 	push_to_cache_list(pgt);
870*52a75a25SMarouene Boubakri 	mutex_unlock(&pgt_mu);
871*52a75a25SMarouene Boubakri }
872*52a75a25SMarouene Boubakri 
873*52a75a25SMarouene Boubakri #endif /* !CFG_CORE_PREALLOC_EL0_TBLS */
874