1 /*
2  * Copyright (c) 2020 Intel Corporation.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/ztest.h>
8 #include <zephyr/irq_offload.h>
9 #include "test_kheap.h"
10 
11 #define STACK_SIZE (512 + CONFIG_TEST_EXTRA_STACK_SIZE)
12 K_THREAD_STACK_DEFINE(tstack, STACK_SIZE);
13 struct k_thread tdata;
14 
15 K_HEAP_DEFINE(k_heap_test, HEAP_SIZE);
16 
17 #define ALLOC_SIZE_1 1024
18 #define ALLOC_SIZE_2 1536
19 #define ALLOC_SIZE_3 2049
20 #define CALLOC_NUM   256
21 #define CALLOC_SIZE  sizeof(uint32_t)
22 
tIsr_kheap_alloc_nowait(void * data)23 static void tIsr_kheap_alloc_nowait(void *data)
24 {
25 	ARG_UNUSED(data);
26 
27 	char *p = (char *)k_heap_alloc(&k_heap_test, ALLOC_SIZE_1, K_NO_WAIT);
28 
29 	zassert_not_null(p, "k_heap_alloc operation failed");
30 	k_heap_free(&k_heap_test, p);
31 }
32 
thread_alloc_heap(void * p1,void * p2,void * p3)33 static void thread_alloc_heap(void *p1, void *p2, void *p3)
34 {
35 	char *p;
36 
37 	k_timeout_t timeout = Z_TIMEOUT_MS(200);
38 
39 	p = (char *)k_heap_alloc(&k_heap_test, ALLOC_SIZE_2, K_NO_WAIT);
40 
41 	zassert_is_null(p, "k_heap_alloc should fail but did not");
42 
43 	p = (char *)k_heap_alloc(&k_heap_test, ALLOC_SIZE_2, timeout);
44 
45 	zassert_not_null(p, "k_heap_alloc failed to allocate memory");
46 
47 	k_heap_free(&k_heap_test, p);
48 }
49 
thread_alloc_heap_null(void * p1,void * p2,void * p3)50 static void thread_alloc_heap_null(void *p1, void *p2, void *p3)
51 {
52 	char *p;
53 
54 	k_timeout_t timeout = Z_TIMEOUT_MS(200);
55 
56 	p = (char *)k_heap_alloc(&k_heap_test, ALLOC_SIZE_2, K_NO_WAIT);
57 
58 	zassert_is_null(p, "k_heap_alloc should fail but did not");
59 
60 	p = (char *)k_heap_alloc(&k_heap_test, ALLOC_SIZE_2, timeout);
61 
62 	zassert_is_null(p, "k_heap_alloc should fail but did not");
63 
64 	k_heap_free(&k_heap_test, p);
65 }
66 
67 /*test cases*/
68 
69 /* These need to be adjacent in BSS */
70 volatile uint32_t heap_guard0;
71 K_HEAP_DEFINE(tiny_heap, 1);
72 volatile uint32_t heap_guard1;
73 
74 /** @brief Test a minimum-size static k_heap
75  *  @ingroup kernel_kheap_api_tests
76  *
77  * @details Create a minimum size (1-byte) static heap, verify that it
78  * works to allocate that byte at runtime and that it doesn't overflow
79  * its memory bounds.
80  */
ZTEST(k_heap_api,test_k_heap_min_size)81 ZTEST(k_heap_api, test_k_heap_min_size)
82 {
83 	const uint32_t guard_bits = 0x5a5a5a5a;
84 
85 	/* Make sure static initialization didn't scribble on them */
86 	zassert_true(heap_guard0 == 0 && heap_guard1 == 0,
87 		     "static heap initialization overran buffer");
88 
89 	heap_guard0 = guard_bits;
90 	heap_guard1 = guard_bits;
91 
92 	char *p0 = k_heap_alloc(&tiny_heap, 1, K_NO_WAIT);
93 	char *p1 = k_heap_alloc(&tiny_heap, 1, K_NO_WAIT);
94 
95 	zassert_not_null(p0, "allocation failed");
96 	zassert_is_null(p1, "second allocation unexpectedly succeeded");
97 
98 	*p0 = 0xff;
99 	k_heap_free(&tiny_heap, p0);
100 
101 	zassert_equal(heap_guard0, guard_bits, "heap overran buffer");
102 	zassert_equal(heap_guard1, guard_bits, "heap overran buffer");
103 }
104 
105 /**
106  * @brief Test to demonstrate k_heap_alloc() and k_heap_free() API usage
107  *
108  * @ingroup kernel_kheap_api_tests
109  *
110  * @details The test allocates 1024 bytes from 2048 byte heap,
111  * and checks if allocation is successful or not
112  *
113  * @see k_heap_malloc(), k_heap_Free()
114  */
ZTEST(k_heap_api,test_k_heap_alloc)115 ZTEST(k_heap_api, test_k_heap_alloc)
116 {
117 	k_timeout_t timeout = Z_TIMEOUT_US(TIMEOUT);
118 	char *p = (char *)k_heap_alloc(&k_heap_test, ALLOC_SIZE_1, timeout);
119 
120 	zassert_not_null(p, "k_heap_alloc operation failed");
121 
122 	for (int i = 0; i < ALLOC_SIZE_1; i++) {
123 		p[i] = '0';
124 	}
125 	k_heap_free(&k_heap_test, p);
126 }
127 
128 
129 /**
130  * @brief Test to demonstrate k_heap_alloc() and k_heap_free() API usage
131  *
132  * @ingroup kernel_kheap_api_tests
133  *
134  * @details The test allocates 2049 bytes, which is greater than the heap
135  * size(2048 bytes), and checks for NULL return from k_heap_alloc
136  *
137  * @see k_heap_malloc(), k_heap_Free()
138  */
ZTEST(k_heap_api,test_k_heap_alloc_fail)139 ZTEST(k_heap_api, test_k_heap_alloc_fail)
140 {
141 
142 	k_timeout_t timeout = Z_TIMEOUT_US(TIMEOUT);
143 	char *p = (char *)k_heap_alloc(&k_heap_test, ALLOC_SIZE_3, timeout);
144 
145 	zassert_is_null(p, NULL);
146 
147 	k_heap_free(&k_heap_test, p);
148 }
149 
150 
151 /**
152  * @brief Test to demonstrate k_heap_free() API functionality.
153  *
154  * @ingroup kernel_kheap_api_tests
155  *
156  * @details The test validates k_heap_free()
157  * API, by using below steps
158  * 1. allocate the memory from the heap,
159  * 2. free the allocated memory
160  * 3. allocate  memory more than the first allocation.
161  * the allocation in the 3rd step should succeed if k_heap_free()
162  * works as expected
163  *
164  * @see k_heap_alloc, k_heap_free()
165  */
ZTEST(k_heap_api,test_k_heap_free)166 ZTEST(k_heap_api, test_k_heap_free)
167 {
168 	k_timeout_t timeout = Z_TIMEOUT_US(TIMEOUT);
169 	char *p = (char *)k_heap_alloc(&k_heap_test, ALLOC_SIZE_1, timeout);
170 
171 	zassert_not_null(p, "k_heap_alloc operation failed");
172 	k_heap_free(&k_heap_test, p);
173 	p = (char *)k_heap_alloc(&k_heap_test, ALLOC_SIZE_2, timeout);
174 	zassert_not_null(p, "k_heap_alloc operation failed");
175 	for (int i = 0; i < ALLOC_SIZE_2; i++) {
176 		p[i] = '0';
177 	}
178 	k_heap_free(&k_heap_test, p);
179 }
180 
181 /**
182  * @brief Validate allocation and free heap memory in isr context.
183  *
184  * @details The test validates k_heap_alloc() in isr context, the timeout
185  * param should be K_NO_WAIT, because this situation isn't allow to wait.
186  *
187  * @ingroup kernel_heap_tests
188  */
ZTEST(k_heap_api,test_kheap_alloc_in_isr_nowait)189 ZTEST(k_heap_api, test_kheap_alloc_in_isr_nowait)
190 {
191 	irq_offload((irq_offload_routine_t)tIsr_kheap_alloc_nowait, NULL);
192 }
193 
194 /**
195  * @brief Validate the k_heap support wait between different threads.
196  *
197  * @details In main thread alloc a buffer from the heap, then run the
198  * child thread. If there isn't enough space in the heap, the child thread
199  * will wait timeout long until main thread free the buffer to heap.
200  *
201  * @ingroup kernel_heap_tests
202  */
ZTEST(k_heap_api,test_k_heap_alloc_pending)203 ZTEST(k_heap_api, test_k_heap_alloc_pending)
204 {
205 	/*
206 	 * Allocate first to make sure subsequent allocations
207 	 * either fail (K_NO_WAIT) or pend.
208 	 */
209 	char *p = (char *)k_heap_alloc(&k_heap_test, ALLOC_SIZE_2, K_NO_WAIT);
210 
211 	zassert_not_null(p, "k_heap_alloc operation failed");
212 
213 	/* Create a thread which will pend on allocation */
214 	k_tid_t tid = k_thread_create(&tdata, tstack, STACK_SIZE,
215 				      thread_alloc_heap, NULL, NULL, NULL,
216 				      K_PRIO_PREEMPT(5), 0, K_NO_WAIT);
217 
218 	/* Sleep long enough for child thread to go into pending */
219 	k_msleep(5);
220 
221 	/*
222 	 * Free memory so the child thread can finish memory allocation
223 	 * without failing.
224 	 */
225 	k_heap_free(&k_heap_test, p);
226 
227 	k_thread_join(tid, K_FOREVER);
228 }
229 
230 /**
231  * @brief Validate the k_heap alloc_pending_null support.
232  *
233  * @details In main thread alloc two buffer from the heap, then run the
234  * child thread which alloc a buffer larger than remaining space. The child thread
235  * will wait timeout long until main thread free one of the buffer to heap, space in
236  * the heap is still not enough and then return null after timeout.
237  *
238  * @ingroup kernel_heap_tests
239  */
ZTEST(k_heap_api,test_k_heap_alloc_pending_null)240 ZTEST(k_heap_api, test_k_heap_alloc_pending_null)
241 {
242 	/*
243 	 * Allocate first to make sure subsequent allocations
244 	 * either fail (K_NO_WAIT) or pend.
245 	 */
246 	char *p = (char *)k_heap_alloc(&k_heap_test, ALLOC_SIZE_1, K_NO_WAIT);
247 	char *q = (char *)k_heap_alloc(&k_heap_test, 512, K_NO_WAIT);
248 
249 	zassert_not_null(p, "k_heap_alloc operation failed");
250 	zassert_not_null(q, "k_heap_alloc operation failed");
251 
252 	/* Create a thread which will pend on allocation */
253 	k_tid_t tid = k_thread_create(&tdata, tstack, STACK_SIZE,
254 				      thread_alloc_heap_null, NULL, NULL, NULL,
255 				      K_PRIO_PREEMPT(5), 0, K_NO_WAIT);
256 
257 	/* Sleep long enough for child thread to go into pending */
258 	k_msleep(5);
259 
260 	/*
261 	 * Free some memory but new thread will still not be able
262 	 * to finish memory allocation without error.
263 	 */
264 	k_heap_free(&k_heap_test, q);
265 
266 	k_thread_join(tid, K_FOREVER);
267 
268 	k_heap_free(&k_heap_test, p);
269 }
270 
271 /**
272  * @brief Test to demonstrate k_heap_calloc() and k_heap_free() API usage
273  *
274  * @ingroup kernel_kheap_api_tests
275  *
276  * @details The test allocates 256 unsigned integers of 4 bytes for a
277  * total of 1024 bytes from the 2048 byte heap. It checks if allocation
278  * and initialization are successful or not
279  *
280  * @see k_heap_calloc(), k_heap_free()
281  */
ZTEST(k_heap_api,test_k_heap_calloc)282 ZTEST(k_heap_api, test_k_heap_calloc)
283 {
284 	k_timeout_t timeout = Z_TIMEOUT_US(TIMEOUT);
285 	uint32_t *p = (uint32_t *)k_heap_calloc(&k_heap_test, CALLOC_NUM, CALLOC_SIZE, timeout);
286 
287 	zassert_not_null(p, "k_heap_calloc operation failed");
288 	for (int i = 0; i < CALLOC_NUM; i++) {
289 		zassert_equal(p[i], 0U);
290 	}
291 
292 	k_heap_free(&k_heap_test, p);
293 }
294