1 // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 #include <stdint.h>
15 #include <stdlib.h>
16 #include <stdbool.h>
17 #include <assert.h>
18 #include <string.h>
19 #include <stddef.h>
20 #include <stdio.h>
21 #include <sys/cdefs.h>
22 #include "heap_tlsf.h"
23 #include <multi_heap.h>
24 #include "multi_heap_internal.h"
25 
26 /* Note: Keep platform-specific parts in this header, this source
27    file should depend on libc only */
28 #include "multi_heap_platform.h"
29 
30 /* Defines compile-time configuration macros */
31 #include "multi_heap_config.h"
32 
33 #ifndef MULTI_HEAP_POISONING
34 /* if no heap poisoning, public API aliases directly to these implementations */
35 void *multi_heap_malloc(multi_heap_handle_t heap, size_t size)
36     __attribute__((alias("multi_heap_malloc_impl")));
37 
38 void *multi_heap_aligned_alloc(multi_heap_handle_t heap, size_t size, size_t alignment)
39     __attribute__((alias("multi_heap_aligned_alloc_impl")));
40 
41 void multi_heap_aligned_free(multi_heap_handle_t heap, void *p)
42     __attribute__((alias("multi_heap_free_impl")));
43 
44 void multi_heap_free(multi_heap_handle_t heap, void *p)
45     __attribute__((alias("multi_heap_free_impl")));
46 
47 void *multi_heap_realloc(multi_heap_handle_t heap, void *p, size_t size)
48     __attribute__((alias("multi_heap_realloc_impl")));
49 
50 size_t multi_heap_get_allocated_size(multi_heap_handle_t heap, void *p)
51     __attribute__((alias("multi_heap_get_allocated_size_impl")));
52 
53 multi_heap_handle_t multi_heap_register(void *start, size_t size)
54     __attribute__((alias("multi_heap_register_impl")));
55 
56 void multi_heap_get_info(multi_heap_handle_t heap, multi_heap_info_t *info)
57     __attribute__((alias("multi_heap_get_info_impl")));
58 
59 size_t multi_heap_free_size(multi_heap_handle_t heap)
60     __attribute__((alias("multi_heap_free_size_impl")));
61 
62 size_t multi_heap_minimum_free_size(multi_heap_handle_t heap)
63     __attribute__((alias("multi_heap_minimum_free_size_impl")));
64 
65 void *multi_heap_get_block_address(multi_heap_block_handle_t block)
66     __attribute__((alias("multi_heap_get_block_address_impl")));
67 
multi_heap_get_block_owner(multi_heap_block_handle_t block)68 void *multi_heap_get_block_owner(multi_heap_block_handle_t block)
69 {
70     return NULL;
71 }
72 
73 #endif
74 
75 #define ALIGN(X) ((X) & ~(sizeof(void *)-1))
76 #define ALIGN_UP(X) ALIGN((X)+sizeof(void *)-1)
77 #define ALIGN_UP_BY(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
78 
79 
80 typedef struct multi_heap_info {
81     void *lock;
82     size_t free_bytes;
83     size_t minimum_free_bytes;
84     size_t pool_size;
85     tlsf_t heap_data;
86 } heap_t;
87 
88 /* Return true if this block is free. */
is_free(const block_header_t * block)89 static inline bool is_free(const block_header_t *block)
90 {
91     return ((block->size & 0x01) != 0);
92 }
93 
94 /* Data size of the block (excludes this block's header) */
block_data_size(const block_header_t * block)95 static inline size_t block_data_size(const block_header_t *block)
96 {
97     return (block->size & ~0x03);
98 }
99 
100 /* Check a block is valid for this heap. Used to verify parameters. */
assert_valid_block(const heap_t * heap,const block_header_t * block)101 static void assert_valid_block(const heap_t *heap, const block_header_t *block)
102 {
103     pool_t pool = tlsf_get_pool(heap->heap_data);
104     void *ptr = block_to_ptr(block);
105 
106     MULTI_HEAP_ASSERT((ptr >= pool) &&
107                     (ptr < pool + heap->pool_size),
108                     (uintptr_t)ptr);
109 }
110 
multi_heap_get_block_address_impl(multi_heap_block_handle_t block)111 void *multi_heap_get_block_address_impl(multi_heap_block_handle_t block)
112 {
113     void *ptr = block_to_ptr(block);
114     return (ptr);
115 }
116 
multi_heap_get_allocated_size_impl(multi_heap_handle_t heap,void * p)117 size_t multi_heap_get_allocated_size_impl(multi_heap_handle_t heap, void *p)
118 {
119     return tlsf_block_size(p);
120 }
121 
multi_heap_register_impl(void * start_ptr,size_t size)122 multi_heap_handle_t multi_heap_register_impl(void *start_ptr, size_t size)
123 {
124     assert(start_ptr);
125     if(size < (tlsf_size() + tlsf_block_size_min() + sizeof(heap_t))) {
126         //Region too small to be a heap.
127         return NULL;
128     }
129 
130     heap_t *result = (heap_t *)start_ptr;
131     size -= sizeof(heap_t);
132 
133     result->heap_data = tlsf_create_with_pool(start_ptr + sizeof(heap_t), size);
134     if(!result->heap_data) {
135         return NULL;
136     }
137 
138     result->lock = NULL;
139     result->free_bytes = size - tlsf_size();
140     result->pool_size = size;
141     result->minimum_free_bytes = result->free_bytes;
142     return result;
143 }
144 
multi_heap_set_lock(multi_heap_handle_t heap,void * lock)145 void multi_heap_set_lock(multi_heap_handle_t heap, void *lock)
146 {
147     heap->lock = lock;
148 }
149 
multi_heap_internal_lock(multi_heap_handle_t heap)150 void inline multi_heap_internal_lock(multi_heap_handle_t heap)
151 {
152     MULTI_HEAP_LOCK(heap->lock);
153 }
154 
multi_heap_internal_unlock(multi_heap_handle_t heap)155 void inline multi_heap_internal_unlock(multi_heap_handle_t heap)
156 {
157     MULTI_HEAP_UNLOCK(heap->lock);
158 }
159 
multi_heap_get_first_block(multi_heap_handle_t heap)160 multi_heap_block_handle_t multi_heap_get_first_block(multi_heap_handle_t heap)
161 {
162     assert(heap != NULL);
163     pool_t pool = tlsf_get_pool(heap->heap_data);
164     block_header_t* block = offset_to_block(pool, -(int)block_header_overhead);
165 
166     return (multi_heap_block_handle_t)block;
167 }
168 
multi_heap_get_next_block(multi_heap_handle_t heap,multi_heap_block_handle_t block)169 multi_heap_block_handle_t multi_heap_get_next_block(multi_heap_handle_t heap, multi_heap_block_handle_t block)
170 {
171     assert(heap != NULL);
172     assert_valid_block(heap, block);
173     block_header_t* next = block_next(block);
174 
175     if(block_data_size(next) == 0) {
176         //Last block:
177         return NULL;
178     } else {
179         return (multi_heap_block_handle_t)next;
180     }
181 
182 }
183 
multi_heap_is_free(multi_heap_block_handle_t block)184 bool multi_heap_is_free(multi_heap_block_handle_t block)
185 {
186     return is_free(block);
187 }
188 
multi_heap_malloc_impl(multi_heap_handle_t heap,size_t size)189 void *multi_heap_malloc_impl(multi_heap_handle_t heap, size_t size)
190 {
191     if (size == 0 || heap == NULL) {
192         return NULL;
193     }
194 
195 
196     multi_heap_internal_lock(heap);
197     void *result = tlsf_malloc(heap->heap_data, size);
198     if(result) {
199         heap->free_bytes -= tlsf_block_size(result);
200         if (heap->free_bytes < heap->minimum_free_bytes) {
201             heap->minimum_free_bytes = heap->free_bytes;
202         }
203     }
204     multi_heap_internal_unlock(heap);
205 
206     return result;
207 }
208 
multi_heap_free_impl(multi_heap_handle_t heap,void * p)209 void multi_heap_free_impl(multi_heap_handle_t heap, void *p)
210 {
211 
212     if (heap == NULL || p == NULL) {
213         return;
214     }
215 
216     assert_valid_block(heap, p);
217 
218     multi_heap_internal_lock(heap);
219     heap->free_bytes += tlsf_block_size(p);
220     tlsf_free(heap->heap_data, p);
221     multi_heap_internal_unlock(heap);
222 }
223 
multi_heap_realloc_impl(multi_heap_handle_t heap,void * p,size_t size)224 void *multi_heap_realloc_impl(multi_heap_handle_t heap, void *p, size_t size)
225 {
226     assert(heap != NULL);
227 
228     if (p == NULL) {
229         return multi_heap_malloc_impl(heap, size);
230     }
231 
232     assert_valid_block(heap, p);
233 
234     if (heap == NULL) {
235         return NULL;
236     }
237 
238     multi_heap_internal_lock(heap);
239     size_t previous_block_size =  tlsf_block_size(p);
240     void *result = tlsf_realloc(heap->heap_data, p, size);
241     if(result) {
242         heap->free_bytes += previous_block_size;
243         heap->free_bytes -= tlsf_block_size(result);
244         if (heap->free_bytes < heap->minimum_free_bytes) {
245             heap->minimum_free_bytes = heap->free_bytes;
246         }
247     }
248 
249     multi_heap_internal_unlock(heap);
250 
251     return result;
252 }
253 
multi_heap_aligned_alloc_impl_offs(multi_heap_handle_t heap,size_t size,size_t alignment,size_t offset)254 void *multi_heap_aligned_alloc_impl_offs(multi_heap_handle_t heap, size_t size, size_t alignment, size_t offset)
255 {
256     if(heap == NULL) {
257         return NULL;
258     }
259 
260     if(!size) {
261         return NULL;
262     }
263 
264     //Alignment must be a power of two:
265     if(((alignment & (alignment - 1)) != 0) ||(!alignment)) {
266         return NULL;
267     }
268 
269     multi_heap_internal_lock(heap);
270     void *result = tlsf_memalign_offs(heap->heap_data, alignment, size, offset);
271     if(result) {
272         heap->free_bytes -= tlsf_block_size(result);
273         if(heap->free_bytes < heap->minimum_free_bytes) {
274             heap->minimum_free_bytes = heap->free_bytes;
275         }
276     }
277     multi_heap_internal_unlock(heap);
278 
279     return result;
280 }
281 
282 
multi_heap_aligned_alloc_impl(multi_heap_handle_t heap,size_t size,size_t alignment)283 void *multi_heap_aligned_alloc_impl(multi_heap_handle_t heap, size_t size, size_t alignment)
284 {
285     return multi_heap_aligned_alloc_impl_offs(heap, size, alignment, 0);
286 }
287 
multi_heap_check(multi_heap_handle_t heap,bool print_errors)288 bool multi_heap_check(multi_heap_handle_t heap, bool print_errors)
289 {
290     (void)print_errors;
291     bool valid = true;
292     assert(heap != NULL);
293 
294     multi_heap_internal_lock(heap);
295     if(tlsf_check(heap->heap_data)) {
296         valid = false;
297     }
298 
299     if(tlsf_check_pool(tlsf_get_pool(heap->heap_data))) {
300         valid = false;
301     }
302 
303     multi_heap_internal_unlock(heap);
304     return valid;
305 }
306 
multi_heap_dump_tlsf(void * ptr,size_t size,int used,void * user)307 static void multi_heap_dump_tlsf(void* ptr, size_t size, int used, void* user)
308 {
309     (void)user;
310     MULTI_HEAP_STDERR_PRINTF("Block %p data, size: %d bytes, Free: %s \n",
311                             (void *)ptr,
312                             size,
313                             used ? "No" : "Yes");
314 }
315 
multi_heap_dump(multi_heap_handle_t heap)316 void multi_heap_dump(multi_heap_handle_t heap)
317 {
318     assert(heap != NULL);
319 
320     multi_heap_internal_lock(heap);
321     MULTI_HEAP_STDERR_PRINTF("Showing data for heap: %p \n", (void *)heap);
322     tlsf_walk_pool(tlsf_get_pool(heap->heap_data), multi_heap_dump_tlsf, NULL);
323     multi_heap_internal_unlock(heap);
324 }
325 
multi_heap_free_size_impl(multi_heap_handle_t heap)326 size_t multi_heap_free_size_impl(multi_heap_handle_t heap)
327 {
328     if (heap == NULL) {
329         return 0;
330     }
331 
332     return heap->free_bytes;
333 }
334 
multi_heap_minimum_free_size_impl(multi_heap_handle_t heap)335 size_t multi_heap_minimum_free_size_impl(multi_heap_handle_t heap)
336 {
337     if (heap == NULL) {
338         return 0;
339     }
340 
341     return heap->minimum_free_bytes;
342 }
343 
multi_heap_get_info_tlsf(void * ptr,size_t size,int used,void * user)344 static void multi_heap_get_info_tlsf(void* ptr, size_t size, int used, void* user)
345 {
346     multi_heap_info_t *info = user;
347 
348     if(used) {
349         info->allocated_blocks++;
350     } else {
351         info->free_blocks++;
352 
353         if(size > info->largest_free_block ) {
354             info->largest_free_block = size;
355         }
356     }
357 
358     info->total_blocks++;
359 }
360 
multi_heap_get_info_impl(multi_heap_handle_t heap,multi_heap_info_t * info)361 void multi_heap_get_info_impl(multi_heap_handle_t heap, multi_heap_info_t *info)
362 {
363     memset(info, 0, sizeof(multi_heap_info_t));
364 
365     if (heap == NULL) {
366         return;
367     }
368 
369     multi_heap_internal_lock(heap);
370     tlsf_walk_pool(tlsf_get_pool(heap->heap_data), multi_heap_get_info_tlsf, info);
371     info->total_allocated_bytes = (heap->pool_size - tlsf_size()) - heap->free_bytes;
372     info->minimum_free_bytes = heap->minimum_free_bytes;
373     info->total_free_bytes = heap->free_bytes;
374     info->largest_free_block = info->largest_free_block ? 1 << (31 - __builtin_clz(info->largest_free_block)) : 0;
375     multi_heap_internal_unlock(heap);
376 }
377