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