| // SPDX-License-Identifier: BSD-2-Clause |
| /* |
| * Copyright 2018-2019 NXP |
| * |
| * Brief Memory management utilities. |
| * Primitive to allocate, free memory. |
| */ |
| #include <arm.h> |
| #include <caam_common.h> |
| #include <caam_trace.h> |
| #include <caam_utils_mem.h> |
| #include <mm/core_memprot.h> |
| #include <mm/tee_mmu.h> |
| #include <string.h> |
| |
| /* |
| * CAAM Descriptor address alignment |
| */ |
| #ifdef ARM64 |
| #define DESC_START_ALIGN (64 / 8) |
| #else |
| #define DESC_START_ALIGN (32 / 8) |
| #endif |
| |
| /* |
| * Check if pointer p is aligned with align |
| */ |
| #define IS_PTR_ALIGN(p, align) (((uintptr_t)(p) & ((align) - 1)) == 0) |
| |
| /* |
| * Check if size is aligned with align |
| */ |
| #define IS_SIZE_ALIGN(size, align) \ |
| ({ \ |
| __typeof__(size) _size = (size); \ |
| __typeof__(size) _sizeup = 0; \ |
| \ |
| _sizeup = ROUNDUP(_size, align); \ |
| (_sizeup == _size) ? 1 : 0; \ |
| }) |
| |
| /* |
| * Read the system cache line size. |
| * Get the value from the ARM system configration register |
| */ |
| static uint32_t read_cacheline_size(void) |
| { |
| uint32_t value = 0; |
| |
| #ifdef ARM64 |
| value = read_ctr_el0(); |
| #else |
| value = read_ctr(); |
| #endif /* ARM64 */ |
| value = CTR_WORD_SIZE |
| << ((value >> CTR_DMINLINE_SHIFT) & CTR_DMINLINE_MASK); |
| MEM_TRACE("System Cache Line size = %" PRIu32 " bytes", value); |
| |
| return value; |
| } |
| |
| #define MEM_TYPE_ZEROED BIT(0) /* Buffer filled with 0's */ |
| #define MEM_TYPE_ALIGN BIT(1) /* Address and size aligned on a cache line */ |
| |
| #if (CFG_CAAM_DBG & DBG_TRACE_MEM) |
| #define DEBUG_INFO_SIZE |
| #endif |
| |
| #define CAAM_MEM_INFO_HDR |
| #ifdef CAAM_MEM_INFO_HDR |
| struct __packed mem_info { |
| void *addr; |
| #ifdef DEBUG_INFO_SIZE |
| size_t size; |
| #endif /* DEBUG_INFO_SIZE */ |
| uint8_t type; |
| }; |
| |
| #define MEM_INFO_SIZE ROUNDUP(sizeof(struct mem_info), sizeof(void *)) |
| #define OF_MEM_INFO(p) (struct mem_info *)((uint8_t *)(p) - MEM_INFO_SIZE) |
| |
| /* |
| * Allocate an area of given size in bytes. Add the memory allocator |
| * information in the newly allocated area. |
| * |
| * @size Size in bytes to allocate |
| * @type Type of area to allocate (refer to MEM_TYPE_*) |
| */ |
| static void *mem_alloc(size_t size, uint8_t type) |
| { |
| struct mem_info *info = NULL; |
| vaddr_t ret_addr = 0; |
| void *ptr = NULL; |
| size_t alloc_size = 0; |
| uint32_t cacheline_size = 0; |
| |
| MEM_TRACE("alloc %zu bytes of type %" PRIu8, size, type); |
| |
| /* |
| * The mem_info header is added just before the returned |
| * buffer address |
| * |
| * -------------- |
| * | mem_info | |
| * -------------- |
| * | Buffer | |
| * -------------- |
| */ |
| if (ADD_OVERFLOW(size, MEM_INFO_SIZE, &alloc_size)) |
| return NULL; |
| |
| if (type & MEM_TYPE_ALIGN) { |
| /* |
| * Buffer must be aligned on a cache line: |
| * - Buffer start address aligned on a cache line |
| * - End of Buffer inside a cache line. |
| * |
| * If area's (mem info + buffer) to be allocated size is |
| * already cache line aligned add a cache line. |
| * |
| * Because Buffer address returned is moved up to a cache |
| * line start offset, add a cache line to full area allocated |
| * to ensure that end of the working buffer is in a cache line. |
| */ |
| cacheline_size = read_cacheline_size(); |
| if (size == cacheline_size) { |
| if (ADD_OVERFLOW(alloc_size, cacheline_size, |
| &alloc_size)) |
| return NULL; |
| } |
| |
| if (ADD_OVERFLOW(cacheline_size, |
| ROUNDUP(alloc_size, cacheline_size), |
| &alloc_size)) |
| return NULL; |
| } |
| |
| if (type & MEM_TYPE_ZEROED) |
| ptr = calloc(1, alloc_size); |
| else |
| ptr = malloc(alloc_size); |
| |
| if (!ptr) { |
| MEM_TRACE("alloc Error - NULL"); |
| return NULL; |
| } |
| |
| /* Calculate the return buffer address */ |
| ret_addr = (vaddr_t)ptr + MEM_INFO_SIZE; |
| if (type & MEM_TYPE_ALIGN) { |
| ret_addr = ROUNDUP(ret_addr, cacheline_size); |
| MEM_TRACE("alloc %p 0x%" PRIxVA " %zu vs %zu", ptr, ret_addr, |
| alloc_size, size); |
| } |
| |
| /* |
| * Add the mem_info header |
| */ |
| info = OF_MEM_INFO(ret_addr); |
| info->addr = ptr; |
| #ifdef DEBUG_INFO_SIZE |
| info->size = alloc_size; |
| #endif /* DEBUG_INFO_SIZE */ |
| info->type = type; |
| |
| MEM_TRACE("alloc returned %p -> %p", ptr, (void *)ret_addr); |
| |
| return (void *)ret_addr; |
| } |
| |
| /* |
| * Free allocated area |
| * |
| * @ptr area to free |
| */ |
| static void mem_free(void *ptr) |
| { |
| struct mem_info *info = NULL; |
| |
| if (!ptr) |
| return; |
| |
| info = OF_MEM_INFO(ptr); |
| MEM_TRACE("free %p info %p", ptr, info); |
| MEM_TRACE("free @%p - %zu bytes of type %" PRIu8, info->addr, |
| info->size, info->type); |
| |
| free(info->addr); |
| } |
| #else /* CAAM_MEM_INFO_HDR */ |
| /* |
| * Allocate an area of given size in bytes |
| * |
| * @size Size in bytes to allocate |
| * @type Type of area to allocate (refer to MEM_TYPE_*) |
| */ |
| static void *mem_alloc(size_t size, uint8_t type) |
| { |
| void *ptr = NULL; |
| size_t alloc_size = size; |
| uint32_t cacheline_size = 0; |
| |
| MEM_TRACE("alloc (normal) %zu bytes of type %" PRIu8, size, type); |
| |
| if (type & MEM_TYPE_ALIGN) { |
| cacheline_size = read_cacheline_size(); |
| if (ADD_OVERFLOW(alloc_size, |
| ROUNDUP(alloc_size, cacheline_size), |
| &alloc_size)) |
| return NULL; |
| } |
| |
| ptr = malloc(alloc_size); |
| if (!ptr) { |
| MEM_TRACE("alloc (normal) Error - NULL"); |
| return NULL; |
| } |
| |
| if (type & MEM_TYPE_ZEROED) |
| memset(ptr, 0, alloc_size); |
| |
| MEM_TRACE("alloc (normal) returned %p", ptr); |
| return ptr; |
| } |
| |
| /* |
| * Free allocated area |
| * |
| * @ptr area to free |
| */ |
| static void mem_free(void *ptr) |
| { |
| if (ptr) { |
| MEM_TRACE("free (normal) %p", ptr); |
| free(ptr); |
| } |
| } |
| #endif /* CAAM_MEM_INFO_HDR */ |
| |
| /* |
| * Allocate internal driver buffer aligned with a cache line. |
| * |
| * @buf [out] buffer allocated |
| * @size size in bytes of the memory to allocate |
| * @type Type of area to allocate (refer to MEM_TYPE_*) |
| */ |
| static enum caam_status mem_alloc_buf(struct caambuf *buf, size_t size, |
| uint8_t type) |
| { |
| buf->data = mem_alloc(size, type); |
| |
| if (!buf->data) |
| return CAAM_OUT_MEMORY; |
| |
| buf->paddr = virt_to_phys(buf->data); |
| if (!buf->paddr) { |
| caam_free_buf(buf); |
| return CAAM_OUT_MEMORY; |
| } |
| |
| buf->length = size; |
| buf->nocache = 0; |
| return CAAM_NO_ERROR; |
| } |
| |
| void *caam_calloc(size_t size) |
| { |
| return mem_alloc(size, MEM_TYPE_ZEROED); |
| } |
| |
| void *caam_calloc_align(size_t size) |
| { |
| return mem_alloc(size, MEM_TYPE_ZEROED | MEM_TYPE_ALIGN); |
| } |
| |
| void caam_free(void *ptr) |
| { |
| mem_free(ptr); |
| } |
| |
| uint32_t *caam_calloc_desc(uint8_t nbentries) |
| { |
| return mem_alloc(DESC_SZBYTES(nbentries), |
| MEM_TYPE_ZEROED | MEM_TYPE_ALIGN); |
| } |
| |
| void caam_free_desc(uint32_t **ptr) |
| { |
| mem_free(*ptr); |
| *ptr = NULL; |
| } |
| |
| enum caam_status caam_calloc_buf(struct caambuf *buf, size_t size) |
| { |
| return mem_alloc_buf(buf, size, MEM_TYPE_ZEROED); |
| } |
| |
| enum caam_status caam_calloc_align_buf(struct caambuf *buf, size_t size) |
| { |
| return mem_alloc_buf(buf, size, MEM_TYPE_ZEROED | MEM_TYPE_ALIGN); |
| } |
| |
| enum caam_status caam_alloc_align_buf(struct caambuf *buf, size_t size) |
| { |
| return mem_alloc_buf(buf, size, MEM_TYPE_ALIGN); |
| } |
| |
| void caam_free_buf(struct caambuf *buf) |
| { |
| if (buf) { |
| if (buf->data) { |
| caam_free(buf->data); |
| buf->data = NULL; |
| } |
| |
| buf->length = 0; |
| buf->paddr = 0; |
| buf->nocache = 0; |
| } |
| } |
| |
| void caam_sgtbuf_free(struct caamsgtbuf *data) |
| { |
| if (data->sgt_type) |
| caam_free(data->sgt); |
| else |
| caam_free(data->buf); |
| |
| data->sgt = NULL; |
| data->buf = NULL; |
| } |
| |
| enum caam_status caam_sgtbuf_alloc(struct caamsgtbuf *data) |
| { |
| if (!data) |
| return CAAM_BAD_PARAM; |
| |
| if (data->sgt_type) { |
| data->sgt = |
| caam_calloc(data->number * (sizeof(struct caamsgt) + |
| sizeof(struct caambuf))); |
| data->buf = (void *)(((uint8_t *)data->sgt) + |
| (data->number * sizeof(struct caamsgt))); |
| } else { |
| data->buf = caam_calloc(data->number * sizeof(struct caambuf)); |
| data->sgt = NULL; |
| } |
| |
| if (!data->buf || (!data->sgt && data->sgt_type)) { |
| caam_sgtbuf_free(data); |
| return CAAM_OUT_MEMORY; |
| } |
| |
| return CAAM_NO_ERROR; |
| } |
| |
| static bool caam_mem_is_cached_buf(void *buf, size_t size) |
| { |
| enum teecore_memtypes mtype = MEM_AREA_MAXTYPE; |
| bool is_cached = false; |
| struct user_ta_ctx *utc = NULL; |
| |
| /* |
| * First check if the buffer is a known memory area mapped |
| * with a type listed in the teecore_memtypes enum. |
| */ |
| mtype = core_mmu_get_type_by_pa(virt_to_phys(buf)); |
| if (mtype == MEM_AREA_MAXTYPE) { |
| /* User Area, check if cacheable */ |
| utc = to_user_ta_ctx(tee_mmu_get_ctx()); |
| if (tee_mmu_user_get_cache_attr(utc, buf) == |
| TEE_MATTR_CACHE_CACHED) |
| is_cached = true; |
| } else { |
| is_cached = core_vbuf_is(CORE_MEM_CACHED, buf, size); |
| } |
| |
| return is_cached; |
| } |
| |
| int caam_set_or_alloc_align_buf(void *orig, struct caambuf *dst, size_t size) |
| { |
| uint32_t cacheline_size = 0; |
| |
| if (caam_mem_is_cached_buf(orig, size)) { |
| /* |
| * Check if either orig pointer or size are aligned on the |
| * cache line size. |
| * If not, reallocate a buffer aligned on cache line. |
| */ |
| cacheline_size = read_cacheline_size(); |
| if (!IS_PTR_ALIGN(orig, cacheline_size) || |
| !IS_SIZE_ALIGN(size, cacheline_size)) { |
| if (caam_alloc_align_buf(dst, size) != CAAM_NO_ERROR) |
| return -1; |
| |
| return 1; |
| } |
| dst->nocache = 0; |
| } else { |
| dst->nocache = 1; |
| } |
| |
| dst->data = orig; |
| dst->paddr = virt_to_phys(dst->data); |
| if (!dst->paddr) |
| return -1; |
| |
| dst->length = size; |
| |
| return 0; |
| } |
| |
| enum caam_status caam_cpy_block_src(struct caamblock *block, |
| struct caambuf *src, size_t offset) |
| { |
| enum caam_status ret = CAAM_FAILURE; |
| size_t cpy_size = 0; |
| |
| /* Check if the temporary buffer is allocated, else allocate it */ |
| if (!block->buf.data) { |
| ret = caam_alloc_align_buf(&block->buf, block->max); |
| if (ret != CAAM_NO_ERROR) { |
| MEM_TRACE("Allocation Block buffer error"); |
| goto end_cpy; |
| } |
| } |
| |
| /* Calculate the number of bytes to copy in the block buffer */ |
| MEM_TRACE("Current buffer is %zu (%zu) bytes", block->filled, |
| block->max); |
| |
| cpy_size = block->max - block->filled; |
| cpy_size = MIN(cpy_size, src->length - offset); |
| |
| memcpy(&block->buf.data[block->filled], &src->data[offset], cpy_size); |
| |
| block->filled += cpy_size; |
| |
| ret = CAAM_NO_ERROR; |
| |
| end_cpy: |
| return ret; |
| } |