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, &registered_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, &registered_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, &registered_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, &registered_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, &registered_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, &registered_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, &registered_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, &registered_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, &registered_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, &registered_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