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