1 /*
2 * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include <stdbool.h>
7 #include <string.h>
8 #include <assert.h>
9 #include <stdio.h>
10 #include <sys/param.h>
11 #include "esp_attr.h"
12 #include "esp_heap_caps.h"
13 #include "multi_heap.h"
14 #include "esp_log.h"
15 #include "heap_private.h"
16 #include "esp_system.h"
17
18 #ifdef CONFIG_HEAP_USE_HOOKS
19 #define CALL_HOOK(hook, ...) { \
20 if (hook != NULL) { \
21 hook(__VA_ARGS__); \
22 } \
23 }
24 #else
25 #define CALL_HOOK(hook, ...) {}
26 #endif
27
28 /* Forward declaration for base function, put in IRAM.
29 * These functions don't check for errors after trying to allocate memory. */
30 static void *heap_caps_realloc_base( void *ptr, size_t size, uint32_t caps );
31 static void *heap_caps_calloc_base( size_t n, size_t size, uint32_t caps );
32 static void *heap_caps_malloc_base( size_t size, uint32_t caps );
33
34 /*
35 This file, combined with a region allocator that supports multiple heaps, solves the problem that the ESP32 has RAM
36 that's slightly heterogeneous. Some RAM can be byte-accessed, some allows only 32-bit accesses, some can execute memory,
37 some can be remapped by the MMU to only be accessed by a certain PID etc. In order to allow the most flexible memory
38 allocation possible, this code makes it possible to request memory that has certain capabilities. The code will then use
39 its knowledge of how the memory is configured along with a priority scheme to allocate that memory in the most sane way
40 possible. This should optimize the amount of RAM accessible to the code without hardwiring addresses.
41 */
42
43 static esp_alloc_failed_hook_t alloc_failed_callback;
44
45 #ifdef CONFIG_HEAP_ABORT_WHEN_ALLOCATION_FAILS
hex_to_str(char buf[8],uint32_t n)46 HEAP_IRAM_ATTR static void hex_to_str(char buf[8], uint32_t n)
47 {
48 for (int i = 0; i < 8; i++) {
49 uint8_t b4 = (n >> (28 - i * 4)) & 0b1111;
50 buf[i] = b4 <= 9 ? '0' + b4 : 'a' + b4 - 10;
51 }
52 }
fmt_abort_str(char dest[48],size_t size,uint32_t caps)53 HEAP_IRAM_ATTR static void fmt_abort_str(char dest[48], size_t size, uint32_t caps)
54 {
55 char sSize[8];
56 char sCaps[8];
57 hex_to_str(sSize, size);
58 hex_to_str(sCaps, caps);
59 memcpy(dest, "Mem alloc fail. size 0x00000000 caps 0x00000000", 48);
60 memcpy(dest + 23, sSize, 8);
61 memcpy(dest + 39, sCaps, 8);
62 }
63 #endif
64
65 /*
66 This takes a memory chunk in a region that can be addressed as both DRAM as well as IRAM. It will convert it to
67 IRAM in such a way that it can be later freed. It assumes both the address as well as the length to be word-aligned.
68 It returns a region that's 1 word smaller than the region given because it stores the original Dram address there.
69 */
dram_alloc_to_iram_addr(void * addr,size_t len)70 HEAP_IRAM_ATTR static void *dram_alloc_to_iram_addr(void *addr, size_t len)
71 {
72 uintptr_t dstart = (uintptr_t)addr; //First word
73 uintptr_t dend __attribute__((unused)) = dstart + len - 4; //Last word
74 assert(esp_ptr_in_diram_dram((void *)dstart));
75 assert(esp_ptr_in_diram_dram((void *)dend));
76 assert((dstart & 3) == 0);
77 assert((dend & 3) == 0);
78 #if SOC_DIRAM_INVERTED // We want the word before the result to hold the DRAM address
79 uint32_t *iptr = esp_ptr_diram_dram_to_iram((void *)dend);
80 #else
81 uint32_t *iptr = esp_ptr_diram_dram_to_iram((void *)dstart);
82 #endif
83 *iptr = dstart;
84 return iptr + 1;
85 }
86
heap_caps_alloc_failed(size_t requested_size,uint32_t caps,const char * function_name)87 HEAP_IRAM_ATTR NOINLINE_ATTR static void heap_caps_alloc_failed(size_t requested_size, uint32_t caps, const char *function_name)
88 {
89 if (alloc_failed_callback) {
90 alloc_failed_callback(requested_size, caps, function_name);
91 }
92
93 #ifdef CONFIG_HEAP_ABORT_WHEN_ALLOCATION_FAILS
94 char buf[48];
95 fmt_abort_str(buf, requested_size, caps);
96 esp_system_abort(buf);
97 #endif
98 }
99
heap_caps_register_failed_alloc_callback(esp_alloc_failed_hook_t callback)100 esp_err_t heap_caps_register_failed_alloc_callback(esp_alloc_failed_hook_t callback)
101 {
102 if (callback == NULL) {
103 return ESP_ERR_INVALID_ARG;
104 }
105
106 alloc_failed_callback = callback;
107
108 return ESP_OK;
109 }
110
heap_caps_match(const heap_t * heap,uint32_t caps)111 bool heap_caps_match(const heap_t *heap, uint32_t caps)
112 {
113 return heap->heap != NULL && ((get_all_caps(heap) & caps) == caps);
114 }
115
116
117 /*
118 This function should not be called directly as it does not
119 check for failure / call heap_caps_alloc_failed()
120 */
heap_caps_malloc_base(size_t size,uint32_t caps)121 HEAP_IRAM_ATTR static void *heap_caps_malloc_base( size_t size, uint32_t caps)
122 {
123 void *ret = NULL;
124
125 if (size == 0 || size > HEAP_SIZE_MAX ) {
126 // Avoids int overflow when adding small numbers to size, or
127 // calculating 'end' from start+size, by limiting 'size' to the possible range
128 return NULL;
129 }
130
131 if (caps & MALLOC_CAP_EXEC) {
132 //MALLOC_CAP_EXEC forces an alloc from IRAM. There is a region which has both this as well as the following
133 //caps, but the following caps are not possible for IRAM. Thus, the combination is impossible and we return
134 //NULL directly, even although our heap capabilities (based on soc_memory_tags & soc_memory_regions) would
135 //indicate there is a tag for this.
136 if ((caps & MALLOC_CAP_8BIT) || (caps & MALLOC_CAP_DMA)) {
137 return NULL;
138 }
139 caps |= MALLOC_CAP_32BIT; // IRAM is 32-bit accessible RAM
140 }
141
142 if (caps & MALLOC_CAP_32BIT) {
143 /* 32-bit accessible RAM should allocated in 4 byte aligned sizes
144 * (Future versions of ESP-IDF should possibly fail if an invalid size is requested)
145 */
146 size = (size + 3) & (~3); // int overflow checked above
147 }
148
149 for (int prio = 0; prio < SOC_MEMORY_TYPE_NO_PRIOS; prio++) {
150 //Iterate over heaps and check capabilities at this priority
151 heap_t *heap;
152 SLIST_FOREACH(heap, ®istered_heaps, next) {
153 if (heap->heap == NULL) {
154 continue;
155 }
156 if ((heap->caps[prio] & caps) != 0) {
157 //Heap has at least one of the caps requested. If caps has other bits set that this prio
158 //doesn't cover, see if they're available in other prios.
159 if ((get_all_caps(heap) & caps) == caps) {
160 //This heap can satisfy all the requested capabilities. See if we can grab some memory using it.
161 // If MALLOC_CAP_EXEC is requested but the DRAM and IRAM are on the same addresses (like on esp32c6)
162 // proceed as for a default allocation.
163 if ((caps & MALLOC_CAP_EXEC) && !esp_dram_match_iram() && esp_ptr_in_diram_dram((void *)heap->start)) {
164 //This is special, insofar that what we're going to get back is a DRAM address. If so,
165 //we need to 'invert' it (lowest address in DRAM == highest address in IRAM and vice-versa) and
166 //add a pointer to the DRAM equivalent before the address we're going to return.
167 ret = multi_heap_malloc(heap->heap, size + 4); // int overflow checked above
168
169 if (ret != NULL) {
170 uint32_t *iptr = dram_alloc_to_iram_addr(ret, size + 4); // int overflow checked above
171 CALL_HOOK(esp_heap_trace_alloc_hook, iptr, size, caps);
172 return iptr;
173 }
174 } else {
175 //Just try to alloc, nothing special.
176 ret = multi_heap_malloc(heap->heap, size);
177 if (ret != NULL) {
178 CALL_HOOK(esp_heap_trace_alloc_hook, ret, size, caps);
179 return ret;
180 }
181 }
182 }
183 }
184 }
185 }
186
187 //Nothing usable found.
188 return NULL;
189 }
190
191
192 /*
193 Routine to allocate a bit of memory with certain capabilities. caps is a bitfield of MALLOC_CAP_* bits.
194 */
heap_caps_malloc(size_t size,uint32_t caps)195 HEAP_IRAM_ATTR void *heap_caps_malloc( size_t size, uint32_t caps){
196
197 void* ptr = heap_caps_malloc_base(size, caps);
198
199
200 if (!ptr && size > 0){
201 heap_caps_alloc_failed(size, caps, __func__);
202 }
203
204 return ptr;
205 }
206
207
208 #define MALLOC_DISABLE_EXTERNAL_ALLOCS -1
209 //Dual-use: -1 (=MALLOC_DISABLE_EXTERNAL_ALLOCS) disables allocations in external memory, >=0 sets the limit for allocations preferring internal memory.
210 static int malloc_alwaysinternal_limit=MALLOC_DISABLE_EXTERNAL_ALLOCS;
211
heap_caps_malloc_extmem_enable(size_t limit)212 void heap_caps_malloc_extmem_enable(size_t limit)
213 {
214 malloc_alwaysinternal_limit=limit;
215 }
216
217 /*
218 Default memory allocation implementation. Should return standard 8-bit memory. malloc() essentially resolves to this function.
219 */
heap_caps_malloc_default(size_t size)220 HEAP_IRAM_ATTR void *heap_caps_malloc_default( size_t size )
221 {
222 if (malloc_alwaysinternal_limit==MALLOC_DISABLE_EXTERNAL_ALLOCS) {
223 return heap_caps_malloc( size, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL);
224 } else {
225
226 // use heap_caps_malloc_base() since we'll
227 // check for allocation failure ourselves
228
229 void *r;
230 if (size <= (size_t)malloc_alwaysinternal_limit) {
231 r=heap_caps_malloc_base( size, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL );
232 } else {
233 r=heap_caps_malloc_base( size, MALLOC_CAP_DEFAULT | MALLOC_CAP_SPIRAM );
234 }
235 if (r==NULL && size > 0) {
236 //try again while being less picky
237 r=heap_caps_malloc_base( size, MALLOC_CAP_DEFAULT );
238 }
239
240 // allocation failure?
241 if (r==NULL && size > 0){
242 heap_caps_alloc_failed(size, MALLOC_CAP_DEFAULT, __func__);
243 }
244
245 return r;
246 }
247 }
248
249 /*
250 Same for realloc()
251 Note: keep the logic in here the same as in heap_caps_malloc_default (or merge the two as soon as this gets more complex...)
252 */
heap_caps_realloc_default(void * ptr,size_t size)253 HEAP_IRAM_ATTR void *heap_caps_realloc_default( void *ptr, size_t size )
254 {
255 if (malloc_alwaysinternal_limit==MALLOC_DISABLE_EXTERNAL_ALLOCS) {
256 return heap_caps_realloc( ptr, size, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL );
257 } else {
258
259 // We use heap_caps_realloc_base() since we'll
260 // handle allocation failure ourselves
261
262 void *r;
263 if (size <= (size_t)malloc_alwaysinternal_limit) {
264 r=heap_caps_realloc_base( ptr, size, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL);
265 } else {
266 r=heap_caps_realloc_base( ptr, size, MALLOC_CAP_DEFAULT | MALLOC_CAP_SPIRAM);
267 }
268
269 if (r==NULL && size>0) {
270 //We needed to allocate memory, but we didn't. Try again while being less picky.
271 r=heap_caps_realloc_base( ptr, size, MALLOC_CAP_DEFAULT);
272 }
273
274 // allocation failure?
275 if (r==NULL && size>0){
276 heap_caps_alloc_failed(size, MALLOC_CAP_DEFAULT, __func__);
277 }
278 return r;
279 }
280 }
281
282 /*
283 Memory allocation as preference in decreasing order.
284 */
heap_caps_malloc_prefer(size_t size,size_t num,...)285 HEAP_IRAM_ATTR void *heap_caps_malloc_prefer( size_t size, size_t num, ... )
286 {
287 va_list argp;
288 va_start( argp, num );
289 void *r = NULL;
290 uint32_t caps = MALLOC_CAP_DEFAULT;
291 while (num--) {
292 caps = va_arg( argp, uint32_t );
293 r = heap_caps_malloc_base( size, caps );
294 if (r != NULL || size == 0) {
295 break;
296 }
297 }
298
299 if (r == NULL && size > 0){
300 heap_caps_alloc_failed(size, caps, __func__);
301 }
302 va_end( argp );
303 return r;
304 }
305
306 /*
307 Memory reallocation as preference in decreasing order.
308 */
heap_caps_realloc_prefer(void * ptr,size_t size,size_t num,...)309 HEAP_IRAM_ATTR void *heap_caps_realloc_prefer( void *ptr, size_t size, size_t num, ... )
310 {
311 va_list argp;
312 va_start( argp, num );
313 void *r = NULL;
314 uint32_t caps = MALLOC_CAP_DEFAULT;
315 while (num--) {
316 caps = va_arg( argp, uint32_t );
317 r = heap_caps_realloc_base( ptr, size, caps );
318 if (r != NULL || size == 0) {
319 break;
320 }
321 }
322
323 if (r == NULL && size > 0){
324 heap_caps_alloc_failed(size, caps, __func__);
325 }
326 va_end( argp );
327 return r;
328 }
329
330 /*
331 Memory callocation as preference in decreasing order.
332 */
heap_caps_calloc_prefer(size_t n,size_t size,size_t num,...)333 HEAP_IRAM_ATTR void *heap_caps_calloc_prefer( size_t n, size_t size, size_t num, ... )
334 {
335 va_list argp;
336 va_start( argp, num );
337 void *r = NULL;
338 uint32_t caps = MALLOC_CAP_DEFAULT;
339 while (num--) {
340 caps = va_arg( argp, uint32_t );
341 r = heap_caps_calloc_base( n, size, caps );
342 if (r != NULL || size == 0){
343 break;
344 }
345 }
346
347 if (r == NULL && size > 0){
348 heap_caps_alloc_failed(size, caps, __func__);
349 }
350 va_end( argp );
351 return r;
352 }
353
354 /* Find the heap which belongs to ptr, or return NULL if it's
355 not in any heap.
356
357 (This confirms if ptr is inside the heap's region, doesn't confirm if 'ptr'
358 is an allocated block or is some other random address inside the heap.)
359 */
find_containing_heap(void * ptr)360 HEAP_IRAM_ATTR static heap_t *find_containing_heap(void *ptr )
361 {
362 intptr_t p = (intptr_t)ptr;
363 heap_t *heap;
364 SLIST_FOREACH(heap, ®istered_heaps, next) {
365 if (heap->heap != NULL && p >= heap->start && p < heap->end) {
366 return heap;
367 }
368 }
369 return NULL;
370 }
371
heap_caps_free(void * ptr)372 HEAP_IRAM_ATTR void heap_caps_free( void *ptr)
373 {
374 if (ptr == NULL) {
375 return;
376 }
377
378 if (esp_ptr_in_diram_iram(ptr)) {
379 //Memory allocated here is actually allocated in the DRAM alias region and
380 //cannot be de-allocated as usual. dram_alloc_to_iram_addr stores a pointer to
381 //the equivalent DRAM address, though; free that.
382 uint32_t *dramAddrPtr = (uint32_t *)ptr;
383 ptr = (void *)dramAddrPtr[-1];
384 }
385
386 heap_t *heap = find_containing_heap(ptr);
387 assert(heap != NULL && "free() target pointer is outside heap areas");
388 multi_heap_free(heap->heap, ptr);
389
390 CALL_HOOK(esp_heap_trace_free_hook, ptr);
391 }
392
393 /*
394 This function should not be called directly as it does not
395 check for failure / call heap_caps_alloc_failed()
396 */
heap_caps_realloc_base(void * ptr,size_t size,uint32_t caps)397 HEAP_IRAM_ATTR static void *heap_caps_realloc_base( void *ptr, size_t size, uint32_t caps)
398 {
399 bool ptr_in_diram_case = false;
400 heap_t *heap = NULL;
401 void *dram_ptr = NULL;
402
403 if (ptr == NULL) {
404 return heap_caps_malloc_base(size, caps);
405 }
406
407 if (size == 0) {
408 heap_caps_free(ptr);
409 return NULL;
410 }
411
412 if (size > HEAP_SIZE_MAX) {
413 return NULL;
414 }
415
416 //The pointer to memory may be aliased, we need to
417 //recover the corresponding address before to manage a new allocation:
418 if(esp_ptr_in_diram_iram((void *)ptr)) {
419 uint32_t *dram_addr = (uint32_t *)ptr;
420 dram_ptr = (void *)dram_addr[-1];
421
422 heap = find_containing_heap(dram_ptr);
423 assert(heap != NULL && "realloc() pointer is outside heap areas");
424
425 //with pointers that reside on diram space, we avoid using
426 //the realloc implementation due to address translation issues,
427 //instead force a malloc/copy/free
428 ptr_in_diram_case = true;
429
430 } else {
431 heap = find_containing_heap(ptr);
432 assert(heap != NULL && "realloc() pointer is outside heap areas");
433 }
434
435 // are the existing heap's capabilities compatible with the
436 // requested ones?
437 bool compatible_caps = (caps & get_all_caps(heap)) == caps;
438
439 if (compatible_caps && !ptr_in_diram_case) {
440 // try to reallocate this memory within the same heap
441 // (which will resize the block if it can)
442 void *r = multi_heap_realloc(heap->heap, ptr, size);
443 if (r != NULL) {
444 CALL_HOOK(esp_heap_trace_alloc_hook, r, size, caps);
445 return r;
446 }
447 }
448
449 // if we couldn't do that, try to see if we can reallocate
450 // in a different heap with requested capabilities.
451 void *new_p = heap_caps_malloc_base(size, caps);
452 if (new_p != NULL) {
453 size_t old_size = 0;
454
455 //If we're dealing with aliased ptr, information regarding its containing
456 //heap can only be obtained with translated address.
457 if(ptr_in_diram_case) {
458 old_size = multi_heap_get_allocated_size(heap->heap, dram_ptr);
459 } else {
460 old_size = multi_heap_get_allocated_size(heap->heap, ptr);
461 }
462
463 assert(old_size > 0);
464 memcpy(new_p, ptr, MIN(size, old_size));
465 heap_caps_free(ptr);
466 return new_p;
467 }
468
469 return NULL;
470 }
471
heap_caps_realloc(void * ptr,size_t size,uint32_t caps)472 HEAP_IRAM_ATTR void *heap_caps_realloc( void *ptr, size_t size, uint32_t caps)
473 {
474 ptr = heap_caps_realloc_base(ptr, size, caps);
475
476
477 if (ptr == NULL && size > 0){
478 heap_caps_alloc_failed(size, caps, __func__);
479 }
480
481 return ptr;
482 }
483
484 /*
485 This function should not be called directly as it does not
486 check for failure / call heap_caps_alloc_failed()
487 */
heap_caps_calloc_base(size_t n,size_t size,uint32_t caps)488 HEAP_IRAM_ATTR static void *heap_caps_calloc_base( size_t n, size_t size, uint32_t caps)
489 {
490 void *result;
491 size_t size_bytes;
492
493 if (__builtin_mul_overflow(n, size, &size_bytes)) {
494 return NULL;
495 }
496
497 result = heap_caps_malloc_base(size_bytes, caps);
498 if (result != NULL) {
499 memset(result, 0, size_bytes);
500 }
501 return result;
502 }
503
heap_caps_calloc(size_t n,size_t size,uint32_t caps)504 HEAP_IRAM_ATTR void *heap_caps_calloc( size_t n, size_t size, uint32_t caps)
505 {
506 void* ptr = heap_caps_calloc_base(n, size, caps);
507
508
509 if (!ptr && size > 0){
510 heap_caps_alloc_failed(size, caps, __func__);
511 }
512
513 return ptr;
514 }
515
heap_caps_get_total_size(uint32_t caps)516 size_t heap_caps_get_total_size(uint32_t caps)
517 {
518 size_t total_size = 0;
519 heap_t *heap;
520 SLIST_FOREACH(heap, ®istered_heaps, next) {
521 if (heap_caps_match(heap, caps)) {
522 total_size += (heap->end - heap->start);
523 }
524 }
525 return total_size;
526 }
527
heap_caps_get_free_size(uint32_t caps)528 size_t heap_caps_get_free_size( uint32_t caps )
529 {
530 size_t ret = 0;
531 heap_t *heap;
532 SLIST_FOREACH(heap, ®istered_heaps, next) {
533 if (heap_caps_match(heap, caps)) {
534 ret += multi_heap_free_size(heap->heap);
535 }
536 }
537 return ret;
538 }
539
heap_caps_get_minimum_free_size(uint32_t caps)540 size_t heap_caps_get_minimum_free_size( uint32_t caps )
541 {
542 size_t ret = 0;
543 heap_t *heap;
544 SLIST_FOREACH(heap, ®istered_heaps, next) {
545 if (heap_caps_match(heap, caps)) {
546 ret += multi_heap_minimum_free_size(heap->heap);
547 }
548 }
549 return ret;
550 }
551
heap_caps_get_largest_free_block(uint32_t caps)552 size_t heap_caps_get_largest_free_block( uint32_t caps )
553 {
554 multi_heap_info_t info;
555 heap_caps_get_info(&info, caps);
556 return info.largest_free_block;
557 }
558
heap_caps_get_info(multi_heap_info_t * info,uint32_t caps)559 void heap_caps_get_info( multi_heap_info_t *info, uint32_t caps )
560 {
561 memset(info, 0, sizeof(multi_heap_info_t));
562
563 heap_t *heap;
564 SLIST_FOREACH(heap, ®istered_heaps, next) {
565 if (heap_caps_match(heap, caps)) {
566 multi_heap_info_t hinfo;
567 multi_heap_get_info(heap->heap, &hinfo);
568
569 info->total_free_bytes += hinfo.total_free_bytes;
570 info->total_allocated_bytes += hinfo.total_allocated_bytes;
571 info->largest_free_block = MAX(info->largest_free_block,
572 hinfo.largest_free_block);
573 info->minimum_free_bytes += hinfo.minimum_free_bytes;
574 info->allocated_blocks += hinfo.allocated_blocks;
575 info->free_blocks += hinfo.free_blocks;
576 info->total_blocks += hinfo.total_blocks;
577 }
578 }
579 }
580
heap_caps_print_heap_info(uint32_t caps)581 void heap_caps_print_heap_info( uint32_t caps )
582 {
583 multi_heap_info_t info;
584 printf("Heap summary for capabilities 0x%08"PRIX32":\n", caps);
585 heap_t *heap;
586 SLIST_FOREACH(heap, ®istered_heaps, next) {
587 if (heap_caps_match(heap, caps)) {
588 multi_heap_get_info(heap->heap, &info);
589
590 printf(" At 0x%08x len %d free %d allocated %d min_free %d\n",
591 heap->start, heap->end - heap->start, info.total_free_bytes, info.total_allocated_bytes, info.minimum_free_bytes);
592 printf(" largest_free_block %d alloc_blocks %d free_blocks %d total_blocks %d\n",
593 info.largest_free_block, info.allocated_blocks,
594 info.free_blocks, info.total_blocks);
595 }
596 }
597 printf(" Totals:\n");
598 heap_caps_get_info(&info, caps);
599
600 printf(" free %d allocated %d min_free %d largest_free_block %d\n", info.total_free_bytes, info.total_allocated_bytes, info.minimum_free_bytes, info.largest_free_block);
601 }
602
heap_caps_check_integrity(uint32_t caps,bool print_errors)603 bool heap_caps_check_integrity(uint32_t caps, bool print_errors)
604 {
605 bool all_heaps = caps & MALLOC_CAP_INVALID;
606 bool valid = true;
607
608 heap_t *heap;
609 SLIST_FOREACH(heap, ®istered_heaps, next) {
610 if (heap->heap != NULL
611 && (all_heaps || (get_all_caps(heap) & caps) == caps)) {
612 valid = multi_heap_check(heap->heap, print_errors) && valid;
613 }
614 }
615
616 return valid;
617 }
618
heap_caps_check_integrity_all(bool print_errors)619 bool heap_caps_check_integrity_all(bool print_errors)
620 {
621 return heap_caps_check_integrity(MALLOC_CAP_INVALID, print_errors);
622 }
623
heap_caps_check_integrity_addr(intptr_t addr,bool print_errors)624 bool heap_caps_check_integrity_addr(intptr_t addr, bool print_errors)
625 {
626 heap_t *heap = find_containing_heap((void *)addr);
627 if (heap == NULL) {
628 return false;
629 }
630 return multi_heap_check(heap->heap, print_errors);
631 }
632
heap_caps_dump(uint32_t caps)633 void heap_caps_dump(uint32_t caps)
634 {
635 bool all_heaps = caps & MALLOC_CAP_INVALID;
636 heap_t *heap;
637 SLIST_FOREACH(heap, ®istered_heaps, next) {
638 if (heap->heap != NULL
639 && (all_heaps || (get_all_caps(heap) & caps) == caps)) {
640 multi_heap_dump(heap->heap);
641 }
642 }
643 }
644
heap_caps_dump_all(void)645 void heap_caps_dump_all(void)
646 {
647 heap_caps_dump(MALLOC_CAP_INVALID);
648 }
649
heap_caps_get_allocated_size(void * ptr)650 size_t heap_caps_get_allocated_size( void *ptr )
651 {
652 heap_t *heap = find_containing_heap(ptr);
653 assert(heap);
654 size_t size = multi_heap_get_allocated_size(heap->heap, ptr);
655 return size;
656 }
657
heap_caps_aligned_alloc(size_t alignment,size_t size,uint32_t caps)658 HEAP_IRAM_ATTR void *heap_caps_aligned_alloc(size_t alignment, size_t size, uint32_t caps)
659 {
660 void *ret = NULL;
661
662 if(!alignment) {
663 return NULL;
664 }
665
666 //Alignment must be a power of two:
667 if((alignment & (alignment - 1)) != 0) {
668 return NULL;
669 }
670
671 if (size == 0) {
672 return NULL;
673 }
674
675 if (size > HEAP_SIZE_MAX) {
676 // Avoids int overflow when adding small numbers to size, or
677 // calculating 'end' from start+size, by limiting 'size' to the possible range
678 heap_caps_alloc_failed(size, caps, __func__);
679
680 return NULL;
681 }
682
683 for (int prio = 0; prio < SOC_MEMORY_TYPE_NO_PRIOS; prio++) {
684 //Iterate over heaps and check capabilities at this priority
685 heap_t *heap;
686 SLIST_FOREACH(heap, ®istered_heaps, next) {
687 if (heap->heap == NULL) {
688 continue;
689 }
690 if ((heap->caps[prio] & caps) != 0) {
691 //Heap has at least one of the caps requested. If caps has other bits set that this prio
692 //doesn't cover, see if they're available in other prios.
693 if ((get_all_caps(heap) & caps) == caps) {
694 //Just try to alloc, nothing special.
695 ret = multi_heap_aligned_alloc(heap->heap, size, alignment);
696 if (ret != NULL) {
697 CALL_HOOK(esp_heap_trace_alloc_hook, ret, size, caps);
698 return ret;
699 }
700 }
701 }
702 }
703 }
704
705 heap_caps_alloc_failed(size, caps, __func__);
706
707 //Nothing usable found.
708 return NULL;
709 }
710
heap_caps_aligned_free(void * ptr)711 HEAP_IRAM_ATTR void heap_caps_aligned_free(void *ptr)
712 {
713 heap_caps_free(ptr);
714 }
715
heap_caps_aligned_calloc(size_t alignment,size_t n,size_t size,uint32_t caps)716 void *heap_caps_aligned_calloc(size_t alignment, size_t n, size_t size, uint32_t caps)
717 {
718 size_t size_bytes;
719 if (__builtin_mul_overflow(n, size, &size_bytes)) {
720 return NULL;
721 }
722
723 void *ptr = heap_caps_aligned_alloc(alignment,size_bytes, caps);
724 if(ptr != NULL) {
725 memset(ptr, 0, size_bytes);
726 }
727
728 return ptr;
729 }
730