/* * Copyright (c) 2018 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #ifdef CONFIG_MULTITHREADING #include #endif #include #include #include #ifdef CONFIG_MMU #include #endif #define LOG_LEVEL CONFIG_KERNEL_LOG_LEVEL #include LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL); #ifdef CONFIG_COMMON_LIBC_MALLOC #if (CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE != 0) /* Figure out where the malloc variables live */ # if Z_MALLOC_PARTITION_EXISTS K_APPMEM_PARTITION_DEFINE(z_malloc_partition); # define POOL_SECTION Z_GENERIC_SECTION(K_APP_DMEM_SECTION(z_malloc_partition)) # else # define POOL_SECTION __noinit # endif /* CONFIG_USERSPACE */ # if defined(CONFIG_MMU) && CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE < 0 # define ALLOCATE_HEAP_AT_STARTUP # endif # ifndef ALLOCATE_HEAP_AT_STARTUP /* Figure out alignment requirement */ # ifdef Z_MALLOC_PARTITION_EXISTS # ifdef CONFIG_MMU # define HEAP_ALIGN CONFIG_MMU_PAGE_SIZE # elif defined(CONFIG_MPU) # if defined(CONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT) && \ (CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE > 0) # if (CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE & (CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE - 1)) != 0 # error CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE must be power of two on this target # endif # define HEAP_ALIGN CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE # elif defined(CONFIG_ARM) || defined(CONFIG_ARM64) # define HEAP_ALIGN MAX(sizeof(double), CONFIG_ARM_MPU_REGION_MIN_ALIGN_AND_SIZE) # elif defined(CONFIG_ARC) # define HEAP_ALIGN MAX(sizeof(double), Z_ARC_MPU_ALIGN) # elif defined(CONFIG_RISCV) # define HEAP_ALIGN Z_POW2_CEIL(MAX(sizeof(double), Z_RISCV_STACK_GUARD_SIZE)) # else /* Default to 64-bytes; we'll get a run-time error if this doesn't work. */ # define HEAP_ALIGN 64 # endif /* CONFIG_ */ # endif /* elif CONFIG_MPU */ # endif /* else Z_MALLOC_PARTITION_EXISTS */ # ifndef HEAP_ALIGN # define HEAP_ALIGN sizeof(double) # endif # if CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE > 0 # define HEAP_STATIC /* Static allocation of heap in BSS */ # define HEAP_SIZE ROUND_UP(CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE, HEAP_ALIGN) # define HEAP_BASE POINTER_TO_UINT(malloc_arena) static POOL_SECTION unsigned char __aligned(HEAP_ALIGN) malloc_arena[HEAP_SIZE]; # else /* CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE > 0 */ /* * Heap base and size are determined based on the available unused SRAM, in the * interval from a properly aligned address after the linker symbol `_end`, to * the end of SRAM */ # define USED_RAM_END_ADDR POINTER_TO_UINT(&_end) /* * No partition, heap can just start wherever _end is, with * suitable alignment */ # define HEAP_BASE ROUND_UP(USED_RAM_END_ADDR, HEAP_ALIGN) # if defined(CONFIG_XTENSA) && (defined(CONFIG_SOC_FAMILY_INTEL_ADSP) \ || defined(CONFIG_HAS_ESPRESSIF_HAL)) extern char _heap_sentry[]; # define HEAP_SIZE ROUND_DOWN((POINTER_TO_UINT(_heap_sentry) - HEAP_BASE), HEAP_ALIGN) # else # define HEAP_SIZE ROUND_DOWN((KB((size_t) CONFIG_SRAM_SIZE) - \ ((size_t) HEAP_BASE - (size_t) CONFIG_SRAM_BASE_ADDRESS)), HEAP_ALIGN) # endif /* else CONFIG_XTENSA */ # endif /* else CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE > 0 */ # endif /* else ALLOCATE_HEAP_AT_STARTUP */ Z_LIBC_DATA static struct sys_heap z_malloc_heap; #ifdef CONFIG_MULTITHREADING Z_LIBC_DATA SYS_MUTEX_DEFINE(z_malloc_heap_mutex); static inline void malloc_lock(void) { int lock_ret; lock_ret = sys_mutex_lock(&z_malloc_heap_mutex, K_FOREVER); __ASSERT_NO_MSG(lock_ret == 0); } static inline void malloc_unlock(void) { (void) sys_mutex_unlock(&z_malloc_heap_mutex); } #else #define malloc_lock() #define malloc_unlock() #endif void *malloc(size_t size) { malloc_lock(); void *ret = sys_heap_aligned_alloc(&z_malloc_heap, __alignof__(z_max_align_t), size); if (ret == NULL && size != 0) { errno = ENOMEM; } malloc_unlock(); return ret; } void *aligned_alloc(size_t alignment, size_t size) { malloc_lock(); void *ret = sys_heap_aligned_alloc(&z_malloc_heap, alignment, size); if (ret == NULL && size != 0) { errno = ENOMEM; } malloc_unlock(); return ret; } #ifdef CONFIG_GLIBCXX_LIBCPP /* * GCC's libstdc++ may use this function instead of aligned_alloc due to a * bug in the configuration for "newlib" environments (which includes picolibc). * When toolchains including that bug fix can become a dependency for Zephyr, * this work-around can be removed. * * Note that aligned_alloc isn't defined to work as a replacement for * memalign as it requires that the size be a multiple of the alignment, * while memalign does not. However, the aligned_alloc implementation here * is just a wrapper around sys_heap_aligned_alloc which doesn't have that * requirement and so can be used by memalign. */ void *memalign(size_t alignment, size_t size) { return aligned_alloc(alignment, size); } #endif static int malloc_prepare(void) { void *heap_base = NULL; size_t heap_size; #ifdef ALLOCATE_HEAP_AT_STARTUP heap_size = k_mem_free_get(); if (heap_size != 0) { heap_base = k_mem_map(heap_size, K_MEM_PERM_RW); __ASSERT(heap_base != NULL, "failed to allocate heap of size %zu", heap_size); } #elif defined(Z_MALLOC_PARTITION_EXISTS) && \ defined(CONFIG_MPU) && \ defined(CONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT) /* Align size to power of two */ heap_size = 1; while (heap_size * 2 <= HEAP_SIZE) { heap_size *= 2; } /* Search for an aligned heap that fits within the available space */ while (heap_size >= HEAP_ALIGN) { heap_base = UINT_TO_POINTER(ROUND_UP(HEAP_BASE, heap_size)); if (POINTER_TO_UINT(heap_base) + heap_size <= HEAP_BASE + HEAP_SIZE) { break; } heap_size >>= 1; } #else heap_base = UINT_TO_POINTER(HEAP_BASE); heap_size = HEAP_SIZE; #endif #if Z_MALLOC_PARTITION_EXISTS && !defined(HEAP_STATIC) z_malloc_partition.start = POINTER_TO_UINT(heap_base); z_malloc_partition.size = heap_size; z_malloc_partition.attr = K_MEM_PARTITION_P_RW_U_RW; #endif sys_heap_init(&z_malloc_heap, heap_base, heap_size); return 0; } void *realloc(void *ptr, size_t requested_size) { malloc_lock(); void *ret = sys_heap_aligned_realloc(&z_malloc_heap, ptr, __alignof__(z_max_align_t), requested_size); if (ret == NULL && requested_size != 0) { errno = ENOMEM; } malloc_unlock(); return ret; } void free(void *ptr) { malloc_lock(); sys_heap_free(&z_malloc_heap, ptr); malloc_unlock(); } SYS_INIT(malloc_prepare, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_LIBC); #else /* No malloc arena */ void *malloc(size_t size) { ARG_UNUSED(size); LOG_ERR("CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE is 0"); errno = ENOMEM; return NULL; } void free(void *ptr) { ARG_UNUSED(ptr); } void *realloc(void *ptr, size_t size) { ARG_UNUSED(ptr); return malloc(size); } #endif /* else no malloc arena */ #endif /* CONFIG_COMMON_LIBC_MALLOC */ #ifdef CONFIG_COMMON_LIBC_CALLOC void *calloc(size_t nmemb, size_t size) { void *ret; if (size_mul_overflow(nmemb, size, &size)) { errno = ENOMEM; return NULL; } ret = malloc(size); if (ret != NULL) { (void)memset(ret, 0, size); } return ret; } #endif /* CONFIG_COMMON_LIBC_CALLOC */ #ifdef CONFIG_COMMON_LIBC_REALLOCARRAY void *reallocarray(void *ptr, size_t nmemb, size_t size) { if (size_mul_overflow(nmemb, size, &size)) { errno = ENOMEM; return NULL; } return realloc(ptr, size); } #endif /* CONFIG_COMMON_LIBC_REALLOCARRAY */