| /* |
| * Copyright (c) 2016 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/ztest.h> |
| #include <kernel_internal.h> |
| #include <zephyr/irq_offload.h> |
| #include <zephyr/sys/multi_heap.h> |
| #include "test_mheap.h" |
| |
| #define STACK_SIZE (512 + CONFIG_TEST_EXTRA_STACK_SIZE) |
| #define OVERFLOW_SIZE SIZE_MAX |
| |
| #define NMEMB 8 |
| #define SIZE 16 |
| #define BOUNDS (NMEMB * SIZE) |
| |
| #define N_MULTI_HEAPS 4 |
| #define MHEAP_BYTES 128 |
| |
| static struct sys_multi_heap multi_heap; |
| static char heap_mem[N_MULTI_HEAPS][MHEAP_BYTES]; |
| static struct sys_heap mheaps[N_MULTI_HEAPS]; |
| |
| K_SEM_DEFINE(thread_sem, 0, 1); |
| K_THREAD_STACK_DEFINE(tstack, STACK_SIZE); |
| struct k_thread tdata; |
| |
| static void tIsr_malloc_and_free(void *data) |
| { |
| ARG_UNUSED(data); |
| void *ptr; |
| |
| ptr = (char *)z_thread_malloc(BLK_SIZE_MIN); |
| zassert_not_null(ptr, "bytes allocation failed from system pool"); |
| k_free(ptr); |
| } |
| |
| static void thread_entry(void *p1, void *p2, void *p3) |
| { |
| void *ptr; |
| |
| k_current_get()->resource_pool = NULL; |
| |
| ptr = (char *)z_thread_malloc(BLK_SIZE_MIN); |
| zassert_is_null(ptr, "bytes allocation failed from system pool"); |
| |
| k_sem_give(&thread_sem); |
| } |
| |
| /*test cases*/ |
| |
| /** |
| * @brief Test to demonstrate k_malloc() and k_free() API usage |
| * |
| * @ingroup kernel_heap_tests |
| * |
| * @details The test allocates 4 blocks from heap memory pool |
| * using k_malloc() API. It also tries to allocate a block of size |
| * 64 bytes which fails as all the memory is allocated up. It then |
| * validates k_free() API by freeing up all the blocks which were |
| * allocated from the heap memory. |
| * |
| * @see k_malloc() |
| */ |
| ZTEST(mheap_api, test_mheap_malloc_free) |
| { |
| void *block[2 * BLK_NUM_MAX], *block_fail; |
| int nb; |
| |
| for (nb = 0; nb < ARRAY_SIZE(block); nb++) { |
| /** |
| * TESTPOINT: This routine provides traditional malloc() |
| * semantics. Memory is allocated from the heap memory pool. |
| */ |
| block[nb] = k_malloc(BLK_SIZE_MIN); |
| if (block[nb] == NULL) { |
| break; |
| } |
| } |
| |
| block_fail = k_malloc(BLK_SIZE_MIN); |
| /** TESTPOINT: Return NULL if fail.*/ |
| zassert_is_null(block_fail, NULL); |
| |
| for (int i = 0; i < nb; i++) { |
| /** |
| * TESTPOINT: This routine provides traditional free() |
| * semantics. The memory being returned must have been allocated |
| * from the heap memory pool. |
| */ |
| k_free(block[i]); |
| } |
| /** TESTPOINT: If ptr is NULL, no operation is performed.*/ |
| k_free(NULL); |
| |
| /** TESTPOINT: Return NULL if fail.*/ |
| block_fail = k_malloc(OVERFLOW_SIZE); |
| zassert_is_null(block_fail, NULL); |
| } |
| |
| /** |
| * @brief Test to demonstrate k_calloc() API functionality. |
| * |
| * @ingroup kernel_heap_tests |
| * |
| * @details The test validates k_calloc() API. When requesting a |
| * huge size of space or a space larger than heap memory, |
| * the API will return NULL. The 8 blocks of memory of |
| * size 16 bytes are allocated by k_calloc() API. When allocated using |
| * k_calloc() the memory buffers have to be zeroed. Check is done, if the |
| * blocks are memset to 0 and read/write is allowed. The test is then |
| * teared up by freeing all the blocks allocated. |
| * |
| * @see k_calloc() |
| */ |
| ZTEST(mheap_api, test_mheap_calloc) |
| { |
| char *mem; |
| |
| /* Requesting a huge size to validate overflow */ |
| mem = k_calloc(NMEMB, OVERFLOW_SIZE); |
| zassert_is_null(mem, "calloc operation failed"); |
| |
| /* Requesting a space large than heap memory lead to failure */ |
| mem = k_calloc(NMEMB * 3, SIZE); |
| zassert_is_null(mem, "calloc operation failed"); |
| |
| mem = k_calloc(NMEMB, SIZE); |
| zassert_not_null(mem, "calloc operation failed"); |
| |
| /* Memory should be zeroed and not crash us if we read/write to it */ |
| for (int i = 0; i < BOUNDS; i++) { |
| zassert_equal(mem[i], 0); |
| mem[i] = 1; |
| } |
| |
| k_free(mem); |
| } |
| |
| ZTEST(mheap_api, test_k_aligned_alloc) |
| { |
| void *r; |
| |
| /* |
| * Allow sizes that are not necessarily a multiple of the |
| * alignment. The backing allocator would naturally round up to |
| * some minimal block size. This would make k_aligned_alloc() |
| * more like posix_memalign() instead of aligned_alloc(), but |
| * the benefit is that k_malloc() can then just be a wrapper |
| * around k_aligned_alloc(). |
| */ |
| r = k_aligned_alloc(sizeof(void *), 1); |
| /* allocation succeeds */ |
| zassert_not_equal(NULL, r, "aligned alloc of 1 byte failed"); |
| /* r is suitably aligned */ |
| zassert_equal(0, (uintptr_t)r % sizeof(void *), |
| "%p not %u-byte-aligned", |
| r, sizeof(void *)); |
| k_free(r); |
| |
| /* allocate with > 8 byte alignment */ |
| r = k_aligned_alloc(16, 1); |
| /* allocation succeeds */ |
| zassert_not_equal(NULL, r, "16-byte-aligned alloc failed"); |
| /* r is suitably aligned */ |
| zassert_equal(0, (uintptr_t)r % 16, |
| "%p not 16-byte-aligned", r); |
| k_free(r); |
| } |
| |
| /** |
| * @brief Validate allocation and free from system heap memory pool. |
| |
| * @details Set heap memory as resource pool. It will success when alloc |
| * a block of memory smaller than the pool and will fail when alloc |
| * a block of memory larger than the pool. |
| * |
| * @ingroup kernel_heap_tests |
| * |
| * @see k_thread_system_pool_assign(), z_thread_malloc(), k_free() |
| */ |
| ZTEST(mheap_api, test_sys_heap_mem_pool_assign) |
| { |
| if (!IS_ENABLED(CONFIG_MULTITHREADING)) { |
| return; |
| } |
| |
| void *ptr; |
| |
| k_thread_system_pool_assign(k_current_get()); |
| ptr = (char *)z_thread_malloc(BLK_SIZE_MIN/2); |
| zassert_not_null(ptr, "bytes allocation failed from system pool"); |
| k_free(ptr); |
| |
| zassert_is_null((char *)z_thread_malloc(BLK_SIZE_MAX * 2), |
| "overflow check failed"); |
| } |
| |
| /** |
| * @brief Validate allocation and free from system heap memory pool |
| * in isr context. |
| * |
| * @details When in isr context, the kernel will successfully alloc a block of |
| * memory because in this situation, the kernel will assign the heap memory |
| * as resource pool. |
| * |
| * @ingroup kernel_heap_tests |
| * |
| * @see z_thread_malloc(), k_free() |
| */ |
| ZTEST(mheap_api, test_malloc_in_isr) |
| { |
| if (!IS_ENABLED(CONFIG_IRQ_OFFLOAD)) { |
| return; |
| } |
| |
| irq_offload((irq_offload_routine_t)tIsr_malloc_and_free, NULL); |
| } |
| |
| /** |
| * @brief Validate allocation and free failure when thread's resource pool |
| * is not assigned. |
| * |
| * @details When a thread's resource pool is not assigned, alloc memory will fail. |
| * |
| * @ingroup kernel_heap_tests |
| * |
| * @see z_thread_malloc() |
| */ |
| ZTEST(mheap_api, test_malloc_in_thread) |
| { |
| if (!IS_ENABLED(CONFIG_MULTITHREADING)) { |
| return; |
| } |
| |
| k_tid_t tid = k_thread_create(&tdata, tstack, STACK_SIZE, |
| thread_entry, NULL, NULL, NULL, |
| 0, 0, K_NO_WAIT); |
| |
| k_sem_take(&thread_sem, K_FOREVER); |
| |
| k_thread_abort(tid); |
| } |
| |
| void *multi_heap_choice(struct sys_multi_heap *mheap, void *cfg, |
| size_t align, size_t size) |
| { |
| struct sys_heap *h = &mheaps[(int)(long)cfg]; |
| |
| return sys_heap_aligned_alloc(h, align, size); |
| } |
| |
| ZTEST(mheap_api, test_multi_heap) |
| { |
| char *blocks[N_MULTI_HEAPS]; |
| |
| sys_multi_heap_init(&multi_heap, multi_heap_choice); |
| for (int i = 0; i < N_MULTI_HEAPS; i++) { |
| sys_heap_init(&mheaps[i], &heap_mem[i][0], MHEAP_BYTES); |
| sys_multi_heap_add_heap(&multi_heap, &mheaps[i], NULL); |
| } |
| |
| /* Allocate half the buffer from each heap, make sure it works |
| * and that the pointer is in the correct memory |
| */ |
| for (int i = 0; i < N_MULTI_HEAPS; i++) { |
| blocks[i] = sys_multi_heap_alloc(&multi_heap, (void *)(long)i, |
| MHEAP_BYTES / 2); |
| |
| zassert_not_null(blocks[i], "allocation failed"); |
| zassert_true(blocks[i] >= &heap_mem[i][0] && |
| blocks[i] < &heap_mem[i+1][0], |
| "allocation not in correct heap"); |
| } |
| |
| /* Make sure all heaps fail to allocate another */ |
| for (int i = 0; i < N_MULTI_HEAPS; i++) { |
| void *b = sys_multi_heap_alloc(&multi_heap, (void *)(long)i, |
| MHEAP_BYTES / 2); |
| |
| zassert_is_null(b, "second allocation succeeded?"); |
| } |
| |
| /* Free all blocks */ |
| for (int i = 0; i < N_MULTI_HEAPS; i++) { |
| sys_multi_heap_free(&multi_heap, blocks[i]); |
| } |
| |
| /* Allocate again to make sure they're still valid */ |
| for (int i = 0; i < N_MULTI_HEAPS; i++) { |
| blocks[i] = sys_multi_heap_alloc(&multi_heap, (void *)(long)i, |
| MHEAP_BYTES / 2); |
| zassert_not_null(blocks[i], "final re-allocation failed"); |
| } |
| } |