diff --git a/components/heap/heap_caps.c b/components/heap/heap_caps.c index 9cbe08a9e..c73264458 100644 --- a/components/heap/heap_caps.c +++ b/components/heap/heap_caps.c @@ -555,6 +555,21 @@ IRAM_ATTR void *heap_caps_aligned_alloc(size_t alignment, size_t size, int caps) return NULL; } +void *heap_caps_aligned_calloc(size_t alignment, size_t n, size_t size, uint32_t caps) +{ + size_t size_bytes; + if (__builtin_mul_overflow(n, size, &size_bytes)) { + return NULL; + } + + void *ptr = heap_caps_aligned_alloc(alignment,size_bytes, caps); + if(ptr != NULL) { + memset(ptr, 0, size_bytes); + } + + return ptr; +} + IRAM_ATTR void heap_caps_aligned_free(void *ptr) { if (ptr == NULL) { diff --git a/components/heap/include/esp_heap_caps.h b/components/heap/include/esp_heap_caps.h index 81f62b19e..a0711d0b2 100644 --- a/components/heap/include/esp_heap_caps.h +++ b/components/heap/include/esp_heap_caps.h @@ -99,6 +99,20 @@ void *heap_caps_realloc( void *ptr, size_t size, int caps); */ void *heap_caps_aligned_alloc(size_t alignment, size_t size, int caps); +/** + * @brief Allocate a aligned chunk of memory which has the given capabilities. The initialized value in the memory is set to zero. + * + * @param alignment How the pointer received needs to be aligned + * must be a power of two + * @param n Number of continuing chunks of memory to allocate + * @param size Size, in bytes, of a chunk 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_calloc(size_t alignment, size_t n, size_t size, uint32_t caps); + /** * @brief Used to deallocate memory previously allocated with heap_caps_aligned_alloc * diff --git a/components/heap/test/test_aligned_alloc_caps.c b/components/heap/test/test_aligned_alloc_caps.c index 909e654e7..613eb1eb2 100644 --- a/components/heap/test/test_aligned_alloc_caps.c +++ b/components/heap/test/test_aligned_alloc_caps.c @@ -67,4 +67,73 @@ TEST_CASE("Capabilities aligned allocator test", "[heap]") } #endif +} + +TEST_CASE("Capabilities aligned calloc test", "[heap]") +{ + uint32_t alignments = 0; + + printf("[ALIGNED_ALLOC] Allocating from default CAP: \n"); + + for(;alignments <= 1024; alignments++) { + uint8_t *buf = (uint8_t *)heap_caps_aligned_calloc(alignments, 1, (alignments + 137), MALLOC_CAP_DEFAULT); + if(((alignments & (alignments - 1)) != 0) || (!alignments)) { + TEST_ASSERT( buf == NULL ); + //printf("[ALIGNED_ALLOC] alignment: %u is not a power of two, don't allow allocation \n", aligments); + } else { + TEST_ASSERT( buf != NULL ); + printf("[ALIGNED_ALLOC] alignment required: %u \n", alignments); + printf("[ALIGNED_ALLOC] address of allocated memory: %p \n\n", (void *)buf); + //Address of obtained block must be aligned with selected value + TEST_ASSERT(((intptr_t)buf & (alignments - 1)) == 0); + + //Write some data, if it corrupts memory probably the heap + //canary verification will fail: + memset(buf, 0xA5, (alignments + 137)); + + heap_caps_aligned_free(buf); + } + } + + //Check if memory is initialized with zero: + uint8_t byte_array[1024]; + memset(&byte_array, 0, sizeof(byte_array)); + uint8_t *buf = (uint8_t *)heap_caps_aligned_calloc(1024, 1, 1024, MALLOC_CAP_DEFAULT); + TEST_ASSERT(memcmp(byte_array, buf, sizeof(byte_array)) == 0); + heap_caps_aligned_free(buf); + + //Same size, but different chunk: + buf = (uint8_t *)heap_caps_aligned_calloc(1024, 1024, 1, MALLOC_CAP_DEFAULT); + TEST_ASSERT(memcmp(byte_array, buf, sizeof(byte_array)) == 0); + heap_caps_aligned_free(buf); + + //Alloc from a non permitted area: + uint32_t *not_permitted_buf = (uint32_t *)heap_caps_aligned_calloc(alignments, 1, (alignments + 137), MALLOC_CAP_32BIT); + TEST_ASSERT( not_permitted_buf == NULL ); + +#if CONFIG_ESP32_SPIRAM_SUPPORT || CONFIG_ESP32S2_SPIRAM_SUPPORT + alignments = 0; + printf("[ALIGNED_ALLOC] Allocating from external memory: \n"); + + for(;alignments <= 1024 * 1024; alignments++) { + //Now try to take aligned memory from IRAM: + uint8_t *buf = (uint8_t *)(uint8_t *)heap_caps_aligned_calloc(alignments, 1, 10*1024, MALLOC_CAP_SPIRAM);; + if(((alignments & (alignments - 1)) != 0) || (!alignments)) { + TEST_ASSERT( buf == NULL ); + //printf("[ALIGNED_ALLOC] alignment: %u is not a power of two, don't allow allocation \n", aligments); + } else { + TEST_ASSERT( buf != NULL ); + printf("[ALIGNED_ALLOC] alignment required: %u \n", alignments); + printf("[ALIGNED_ALLOC] address of allocated memory: %p \n\n", (void *)buf); + //Address of obtained block must be aligned with selected value + TEST_ASSERT(((intptr_t)buf & (alignments - 1)) == 0); + + //Write some data, if it corrupts memory probably the heap + //canary verification will fail: + memset(buf, 0xA5, (10*1024)); + heap_caps_aligned_free(buf); + } + } +#endif + } \ No newline at end of file