// SPDX-License-Identifier: BSD-2-Clause /* * Copyright (c) 2014, STMicroelectronics International N.V. */ #include #include #include #include #include #include #include static void *pmalloc(tee_mm_pool_t *pool, size_t size) { if (pool->flags & TEE_MM_POOL_NEX_MALLOC) return nex_malloc(size); else return malloc(size); } static void *pcalloc(tee_mm_pool_t *pool, size_t num_el, size_t size) { if (pool->flags & TEE_MM_POOL_NEX_MALLOC) return nex_calloc(num_el, size); else return calloc(num_el, size); } static void pfree(tee_mm_pool_t *pool, void *ptr) { if (pool->flags & TEE_MM_POOL_NEX_MALLOC) nex_free(ptr); else free(ptr); } bool tee_mm_init(tee_mm_pool_t *pool, paddr_t lo, paddr_size_t size, uint8_t shift, uint32_t flags) { paddr_size_t rounded = 0; paddr_t initial_lo = lo; if (pool == NULL) return false; lo = ROUNDUP(lo, 1 << shift); rounded = lo - initial_lo; size = ROUNDDOWN(size - rounded, 1 << shift); assert(((uint64_t)size >> shift) < (uint64_t)UINT32_MAX); pool->lo = lo; pool->size = size; pool->shift = shift; pool->flags = flags; pool->entry = pcalloc(pool, 1, sizeof(tee_mm_entry_t)); if (pool->entry == NULL) return false; if (pool->flags & TEE_MM_POOL_HI_ALLOC) pool->entry->offset = ((size - 1) >> shift) + 1; pool->entry->pool = pool; pool->lock = SPINLOCK_UNLOCK; return true; } void tee_mm_final(tee_mm_pool_t *pool) { if (pool == NULL || pool->entry == NULL) return; while (pool->entry->next != NULL) tee_mm_free(pool->entry->next); pfree(pool, pool->entry); pool->entry = NULL; } static void tee_mm_add(tee_mm_entry_t *p, tee_mm_entry_t *nn) { /* add to list */ nn->next = p->next; p->next = nn; } #ifdef CFG_WITH_STATS static size_t tee_mm_stats_allocated(tee_mm_pool_t *pool) { tee_mm_entry_t *entry; uint32_t sz = 0; if (!pool) return 0; entry = pool->entry; while (entry) { sz += entry->size; entry = entry->next; } return sz << pool->shift; } void tee_mm_get_pool_stats(tee_mm_pool_t *pool, struct malloc_stats *stats, bool reset) { uint32_t exceptions; if (!pool) return; memset(stats, 0, sizeof(*stats)); exceptions = cpu_spin_lock_xsave(&pool->lock); stats->size = pool->size; stats->max_allocated = pool->max_allocated; stats->allocated = tee_mm_stats_allocated(pool); if (reset) pool->max_allocated = 0; cpu_spin_unlock_xrestore(&pool->lock, exceptions); } static void update_max_allocated(tee_mm_pool_t *pool) { size_t sz = tee_mm_stats_allocated(pool); if (sz > pool->max_allocated) pool->max_allocated = sz; } #else /* CFG_WITH_STATS */ static inline void update_max_allocated(tee_mm_pool_t *pool __unused) { } #endif /* CFG_WITH_STATS */ tee_mm_entry_t *tee_mm_alloc(tee_mm_pool_t *pool, size_t size) { size_t psize; tee_mm_entry_t *entry; tee_mm_entry_t *nn; size_t remaining; uint32_t exceptions; /* Check that pool is initialized */ if (!pool || !pool->entry) return NULL; nn = pmalloc(pool, sizeof(tee_mm_entry_t)); if (!nn) return NULL; exceptions = cpu_spin_lock_xsave(&pool->lock); entry = pool->entry; if (!size) psize = 0; else psize = ((size - 1) >> pool->shift) + 1; /* find free slot */ if (pool->flags & TEE_MM_POOL_HI_ALLOC) { while (entry->next != NULL && psize > (entry->offset - entry->next->offset - entry->next->size)) entry = entry->next; } else { while (entry->next != NULL && psize > (entry->next->offset - entry->size - entry->offset)) entry = entry->next; } /* check if we have enough memory */ if (entry->next == NULL) { if (pool->flags & TEE_MM_POOL_HI_ALLOC) { /* * entry->offset is a "block count" offset from * pool->lo. The byte offset is * (entry->offset << pool->shift). * In the HI_ALLOC allocation scheme the memory is * allocated from the end of the segment, thus to * validate there is sufficient memory validate that * (entry->offset << pool->shift) > size. */ if ((entry->offset << pool->shift) < size) { /* out of memory */ goto err; } } else { if (!pool->size) panic("invalid pool"); remaining = pool->size; remaining -= ((entry->offset + entry->size) << pool->shift); if (remaining < size) { /* out of memory */ goto err; } } } tee_mm_add(entry, nn); if (pool->flags & TEE_MM_POOL_HI_ALLOC) nn->offset = entry->offset - psize; else nn->offset = entry->offset + entry->size; nn->size = psize; nn->pool = pool; update_max_allocated(pool); cpu_spin_unlock_xrestore(&pool->lock, exceptions); return nn; err: cpu_spin_unlock_xrestore(&pool->lock, exceptions); pfree(pool, nn); return NULL; } static inline bool fit_in_gap(tee_mm_pool_t *pool, tee_mm_entry_t *e, paddr_t offslo, paddr_t offshi) { if (pool->flags & TEE_MM_POOL_HI_ALLOC) { if (offshi > e->offset || (e->next != NULL && (offslo < e->next->offset + e->next->size)) || (offshi << pool->shift) - 1 > pool->size) /* memory not available */ return false; } else { if (offslo < (e->offset + e->size) || (e->next != NULL && (offshi > e->next->offset)) || (offshi << pool->shift) > pool->size) /* memory not available */ return false; } return true; } tee_mm_entry_t *tee_mm_alloc2(tee_mm_pool_t *pool, paddr_t base, size_t size) { tee_mm_entry_t *entry; paddr_t offslo; paddr_t offshi; tee_mm_entry_t *mm; uint32_t exceptions; /* Check that pool is initialized */ if (!pool || !pool->entry) return NULL; /* Wrapping and sanity check */ if ((base + size) < base || base < pool->lo) return NULL; mm = pmalloc(pool, sizeof(tee_mm_entry_t)); if (!mm) return NULL; exceptions = cpu_spin_lock_xsave(&pool->lock); entry = pool->entry; offslo = (base - pool->lo) >> pool->shift; offshi = ((base - pool->lo + size - 1) >> pool->shift) + 1; /* find slot */ if (pool->flags & TEE_MM_POOL_HI_ALLOC) { while (entry->next != NULL && offshi < entry->next->offset + entry->next->size) entry = entry->next; } else { while (entry->next != NULL && offslo > entry->next->offset) entry = entry->next; } /* Check that memory is available */ if (!fit_in_gap(pool, entry, offslo, offshi)) goto err; tee_mm_add(entry, mm); mm->offset = offslo; mm->size = offshi - offslo; mm->pool = pool; update_max_allocated(pool); cpu_spin_unlock_xrestore(&pool->lock, exceptions); return mm; err: cpu_spin_unlock_xrestore(&pool->lock, exceptions); pfree(pool, mm); return NULL; } void tee_mm_free(tee_mm_entry_t *p) { tee_mm_entry_t *entry; uint32_t exceptions; if (!p || !p->pool) return; exceptions = cpu_spin_lock_xsave(&p->pool->lock); entry = p->pool->entry; /* remove entry from list */ while (entry->next != NULL && entry->next != p) entry = entry->next; if (!entry->next) panic("invalid mm_entry"); entry->next = entry->next->next; cpu_spin_unlock_xrestore(&p->pool->lock, exceptions); pfree(p->pool, p); } size_t tee_mm_get_bytes(const tee_mm_entry_t *mm) { if (!mm || !mm->pool) return 0; else return mm->size << mm->pool->shift; } bool tee_mm_addr_is_within_range(const tee_mm_pool_t *pool, paddr_t addr) { return pool && addr >= pool->lo && addr <= (pool->lo + (pool->size - 1)); } bool tee_mm_is_empty(tee_mm_pool_t *pool) { bool ret; uint32_t exceptions; if (pool == NULL || pool->entry == NULL) return true; exceptions = cpu_spin_lock_xsave(&pool->lock); ret = pool->entry == NULL || pool->entry->next == NULL; cpu_spin_unlock_xrestore(&pool->lock, exceptions); return ret; } /* Physical Secure DDR pool */ tee_mm_pool_t tee_mm_sec_ddr; /* Virtual eSRAM pool */ tee_mm_pool_t tee_mm_vcore; /* Shared memory pool */ tee_mm_pool_t tee_mm_shm; tee_mm_entry_t *tee_mm_find(const tee_mm_pool_t *pool, paddr_t addr) { tee_mm_entry_t *entry = pool->entry; uint16_t offset = (addr - pool->lo) >> pool->shift; uint32_t exceptions; if (!tee_mm_addr_is_within_range(pool, addr)) return NULL; exceptions = cpu_spin_lock_xsave(&((tee_mm_pool_t *)pool)->lock); while (entry->next != NULL) { entry = entry->next; if ((offset >= entry->offset) && (offset < (entry->offset + entry->size))) { cpu_spin_unlock_xrestore(&((tee_mm_pool_t *)pool)->lock, exceptions); return entry; } } cpu_spin_unlock_xrestore(&((tee_mm_pool_t *)pool)->lock, exceptions); return NULL; } uintptr_t tee_mm_get_smem(const tee_mm_entry_t *mm) { return (mm->offset << mm->pool->shift) + mm->pool->lo; }