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