1 /*
2  * Copyright (c) 2016 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <ztest.h>
8 #include <kernel_internal.h>
9 #include <irq_offload.h>
10 #include "test_mheap.h"
11 
12 #define STACK_SIZE (512 + CONFIG_TEST_EXTRA_STACKSIZE)
13 #define OVERFLOW_SIZE    SIZE_MAX
14 
15 K_SEM_DEFINE(thread_sem, 0, 1);
16 K_THREAD_STACK_DEFINE(tstack, STACK_SIZE);
17 struct k_thread tdata;
18 
tIsr_malloc_and_free(void * data)19 static void tIsr_malloc_and_free(void *data)
20 {
21 	ARG_UNUSED(data);
22 	void *ptr;
23 
24 	ptr = (char *)z_thread_malloc(BLK_SIZE_MIN);
25 	zassert_not_null(ptr, "bytes allocation failed from system pool");
26 	k_free(ptr);
27 }
28 
thread_entry(void * p1,void * p2,void * p3)29 static void thread_entry(void *p1, void *p2, void *p3)
30 {
31 	void *ptr;
32 
33 	k_current_get()->resource_pool = NULL;
34 
35 	ptr = (char *)z_thread_malloc(BLK_SIZE_MIN);
36 	zassert_is_null(ptr, "bytes allocation failed from system pool");
37 
38 	k_sem_give(&thread_sem);
39 }
40 
41 /*test cases*/
42 
43 /**
44  * @brief Test to demonstrate k_malloc() and k_free() API usage
45  *
46  * @ingroup kernel_heap_tests
47  *
48  * @details The test allocates 4 blocks from heap memory pool
49  * using k_malloc() API. It also tries to allocate a block of size
50  * 64 bytes which fails as all the memory is allocated up. It then
51  * validates k_free() API by freeing up all the blocks which were
52  * allocated from the heap memory.
53  *
54  * @see k_malloc()
55  */
test_mheap_malloc_free(void)56 void test_mheap_malloc_free(void)
57 {
58 	void *block[2 * BLK_NUM_MAX], *block_fail;
59 	int nb;
60 
61 	for (nb = 0; nb < ARRAY_SIZE(block); nb++) {
62 		/**
63 		 * TESTPOINT: This routine provides traditional malloc()
64 		 * semantics. Memory is allocated from the heap memory pool.
65 		 */
66 		block[nb] = k_malloc(BLK_SIZE_MIN);
67 		if (block[nb] == NULL) {
68 			break;
69 		}
70 	}
71 
72 	block_fail = k_malloc(BLK_SIZE_MIN);
73 	/** TESTPOINT: Return NULL if fail.*/
74 	zassert_is_null(block_fail, NULL);
75 
76 	for (int i = 0; i < nb; i++) {
77 		/**
78 		 * TESTPOINT: This routine provides traditional free()
79 		 * semantics. The memory being returned must have been allocated
80 		 * from the heap memory pool.
81 		 */
82 		k_free(block[i]);
83 	}
84 	/** TESTPOINT: If ptr is NULL, no operation is performed.*/
85 	k_free(NULL);
86 
87 	/** TESTPOINT: Return NULL if fail.*/
88 	block_fail = k_malloc(OVERFLOW_SIZE);
89 	zassert_is_null(block_fail, NULL);
90 }
91 
92 #define NMEMB   8
93 #define SIZE    16
94 #define BOUNDS  (NMEMB * SIZE)
95 
96 /**
97  * @brief Test to demonstrate k_calloc() API functionality.
98  *
99  * @ingroup kernel_heap_tests
100  *
101  * @details The test validates k_calloc() API. When requesting a
102  * huge size of space or a space larger than heap memory,
103  * the API will return NULL. The 8 blocks of memory of
104  * size 16 bytes are allocated by k_calloc() API. When allocated using
105  * k_calloc() the memory buffers have to be zeroed. Check is done, if the
106  * blocks are memset to 0 and read/write is allowed. The test is then
107  * teared up by freeing all the blocks allocated.
108  *
109  * @see k_calloc()
110  */
test_mheap_calloc(void)111 void test_mheap_calloc(void)
112 {
113 	char *mem;
114 
115 	/* Requesting a huge size to validate overflow */
116 	mem = k_calloc(NMEMB, OVERFLOW_SIZE);
117 	zassert_is_null(mem, "calloc operation failed");
118 
119 	/* Requesting a space large than heap memory lead to failure */
120 	mem = k_calloc(NMEMB * 3, SIZE);
121 	zassert_is_null(mem, "calloc operation failed");
122 
123 	mem = k_calloc(NMEMB, SIZE);
124 	zassert_not_null(mem, "calloc operation failed");
125 
126 	/* Memory should be zeroed and not crash us if we read/write to it */
127 	for (int i = 0; i < BOUNDS; i++) {
128 		zassert_equal(mem[i], 0, NULL);
129 		mem[i] = 1;
130 	}
131 
132 	k_free(mem);
133 }
134 
test_k_aligned_alloc(void)135 void test_k_aligned_alloc(void)
136 {
137 	void *r;
138 
139 	/*
140 	 * Allow sizes that are not necessarily a multiple of the
141 	 * alignment. The backing allocator would naturally round up to
142 	 * some minimal block size. This would make k_aligned_alloc()
143 	 * more like posix_memalign() instead of aligned_alloc(), but
144 	 * the benefit is that k_malloc() can then just be a wrapper
145 	 * around k_aligned_alloc().
146 	 */
147 	r = k_aligned_alloc(sizeof(void *), 1);
148 	/* allocation succeeds */
149 	zassert_not_equal(NULL, r, "aligned alloc of 1 byte failed");
150 	/* r is suitably aligned */
151 	zassert_equal(0, (uintptr_t)r % sizeof(void *),
152 		"%p not %u-byte-aligned",
153 		r, sizeof(void *));
154 	k_free(r);
155 
156 	/* allocate with > 8 byte alignment */
157 	r = k_aligned_alloc(16, 1);
158 	/* allocation succeeds */
159 	zassert_not_equal(NULL, r, "16-byte-aligned alloc failed");
160 	/* r is suitably aligned */
161 	zassert_equal(0, (uintptr_t)r % 16,
162 		"%p not 16-byte-aligned", r);
163 	k_free(r);
164 }
165 
166 /**
167  * @brief Validate allocation and free from system heap memory pool.
168 
169  * @details Set heap memory as resource pool. It will success when alloc
170  * a block of memory smaller than the pool and will fail when alloc
171  * a block of memory larger than the pool.
172  *
173  * @ingroup kernel_heap_tests
174  *
175  * @see k_thread_system_pool_assign(), z_thread_malloc(), k_free()
176  */
test_sys_heap_mem_pool_assign(void)177 void test_sys_heap_mem_pool_assign(void)
178 {
179 	if (!IS_ENABLED(CONFIG_MULTITHREADING)) {
180 		return;
181 	}
182 
183 	void *ptr;
184 
185 	k_thread_system_pool_assign(k_current_get());
186 	ptr = (char *)z_thread_malloc(BLK_SIZE_MIN/2);
187 	zassert_not_null(ptr, "bytes allocation failed from system pool");
188 	k_free(ptr);
189 
190 	zassert_is_null((char *)z_thread_malloc(BLK_SIZE_MAX * 2),
191 						"overflow check failed");
192 }
193 
194 /**
195  * @brief Validate allocation and free from system heap memory pool
196  * in isr context.
197  *
198  * @details When in isr context, the kernel will successfully alloc a block of
199  * memory because in this situation, the kernel will assign the heap memory
200  * as resource pool.
201  *
202  * @ingroup kernel_heap_tests
203  *
204  * @see z_thread_malloc(), k_free()
205  */
test_malloc_in_isr(void)206 void test_malloc_in_isr(void)
207 {
208 	if (!IS_ENABLED(CONFIG_IRQ_OFFLOAD)) {
209 		return;
210 	}
211 
212 	irq_offload((irq_offload_routine_t)tIsr_malloc_and_free, NULL);
213 }
214 
215 /**
216  * @brief Validate allocation and free failure when thread's resource pool
217  * is not assigned.
218  *
219  * @details When a thread's resource pool is not assigned, alloc memory will fail.
220  *
221  * @ingroup kernel_heap_tests
222  *
223  * @see z_thread_malloc()
224  */
test_malloc_in_thread(void)225 void test_malloc_in_thread(void)
226 {
227 	if (!IS_ENABLED(CONFIG_MULTITHREADING)) {
228 		return;
229 	}
230 
231 	k_tid_t tid = k_thread_create(&tdata, tstack, STACK_SIZE,
232 			      thread_entry, NULL, NULL, NULL,
233 			      0, 0, K_NO_WAIT);
234 
235 	k_sem_take(&thread_sem, K_FOREVER);
236 
237 	k_thread_abort(tid);
238 }
239