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, ®istered_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, ®istered_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, ®istered_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, ®istered_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, ®istered_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, ®istered_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, ®istered_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, ®istered_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, ®istered_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, ®istered_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