diff --git a/components/heap/heap_caps.c b/components/heap/heap_caps.c index 54018b0dec..322ce23a89 100644 --- a/components/heap/heap_caps.c +++ b/components/heap/heap_caps.c @@ -502,3 +502,94 @@ size_t heap_caps_get_allocated_size( void *ptr ) size_t size = multi_heap_get_allocated_size(heap->heap, ptr); return size; } + +IRAM_ATTR void *heap_caps_aligned_alloc(size_t alignment, size_t size, int caps) +{ + void *ret = NULL; + + if(!alignment) { + return NULL; + } + + //Alignment must be a power of two: + if((alignment & (alignment - 1)) != 0) { + return NULL; + } + + if (size > HEAP_SIZE_MAX) { + // Avoids int overflow when adding small numbers to size, or + // calculating 'end' from start+size, by limiting 'size' to the possible range + return NULL; + } + + if (caps & MALLOC_CAP_EXEC) { + //MALLOC_CAP_EXEC forces an alloc from IRAM. There is a region which has both this as well as the following + //caps, but the following caps are not possible for IRAM. Thus, the combination is impossible and we return + //NULL directly, even although our heap capabilities (based on soc_memory_tags & soc_memory_regions) would + //indicate there is a tag for this. + if ((caps & MALLOC_CAP_8BIT) || (caps & MALLOC_CAP_DMA)) { + return NULL; + } + caps |= MALLOC_CAP_32BIT; // IRAM is 32-bit accessible RAM + } + + if (caps & MALLOC_CAP_32BIT) { + /* 32-bit accessible RAM should allocated in 4 byte aligned sizes + * (Future versions of ESP-IDF should possibly fail if an invalid size is requested) + */ + size = (size + 3) & (~3); // int overflow checked above + } + + for (int prio = 0; prio < SOC_MEMORY_TYPE_NO_PRIOS; prio++) { + //Iterate over heaps and check capabilities at this priority + heap_t *heap; + SLIST_FOREACH(heap, ®istered_heaps, next) { + if (heap->heap == NULL) { + continue; + } + if ((heap->caps[prio] & caps) != 0) { + //Heap has at least one of the caps requested. If caps has other bits set that this prio + //doesn't cover, see if they're available in other prios. + if ((get_all_caps(heap) & caps) == caps) { + //This heap can satisfy all the requested capabilities. See if we can grab some memory using it. + if ((caps & MALLOC_CAP_EXEC) && esp_ptr_in_diram_dram((void *)heap->start)) { + //This is special, insofar that what we're going to get back is a DRAM address. If so, + //we need to 'invert' it (lowest address in DRAM == highest address in IRAM and vice-versa) and + //add a pointer to the DRAM equivalent before the address we're going to return. + ret = multi_heap_aligned_alloc(heap->heap, size + 4, alignment); // int overflow checked above + if (ret != NULL) { + return dram_alloc_to_iram_addr(ret, size + 4); // int overflow checked above + } + } else { + //Just try to alloc, nothing special. + ret = multi_heap_aligned_alloc(heap->heap, size, alignment); + if (ret != NULL) { + return ret; + } + } + } + } + } + } + //Nothing usable found. + return NULL; +} + +IRAM_ATTR void heap_caps_aligned_free(void *ptr) +{ + if (ptr == NULL) { + return; + } + + if (esp_ptr_in_diram_iram(ptr)) { + //Memory allocated here is actually allocated in the DRAM alias region and + //cannot be de-allocated as usual. dram_alloc_to_iram_addr stores a pointer to + //the equivalent DRAM address, though; free that. + uint32_t *dramAddrPtr = (uint32_t *)ptr; + ptr = (void *)dramAddrPtr[-1]; + } + + heap_t *heap = find_containing_heap(ptr); + assert(heap != NULL && "free() target pointer is outside heap areas"); + multi_heap_aligned_free(heap->heap, ptr); +} diff --git a/components/heap/include/esp_heap_caps.h b/components/heap/include/esp_heap_caps.h index cd91445227..81f62b19eb 100644 --- a/components/heap/include/esp_heap_caps.h +++ b/components/heap/include/esp_heap_caps.h @@ -85,6 +85,30 @@ void heap_caps_free( void *ptr); */ void *heap_caps_realloc( void *ptr, size_t size, int caps); +/** + * @brief Allocate a aligned chunk of memory which has the given capabilities + * + * Equivalent semantics to libc aligned_alloc(), for capability-aware memory. + * @param alignment How the pointer received needs to be aligned + * must be a power of two + * @param size Size, in bytes, of the amount of memory to allocate + * @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type + * of memory to be returned + * + * @return A pointer to the memory allocated on success, NULL on failure + */ +void *heap_caps_aligned_alloc(size_t alignment, size_t size, int caps); + +/** + * @brief Used to deallocate memory previously allocated with heap_caps_aligned_alloc + * + * @param ptr Pointer to the memory allocated + * @note This function is aimed to deallocate only memory allocated with + * heap_caps_aligned_alloc, memory allocated with heap_caps_malloc + * MUST not be passed to this function + */ +void heap_caps_aligned_free(void *ptr); + /** * @brief Allocate a chunk of memory which has the given capabilities. The initialized value in the memory is set to zero. *