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