mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'feature/wider_alignment_range_malloc' into 'master'
feature/wider alignment range malloc Closes IDF-648 See merge request espressif/esp-idf!6691
This commit is contained in:
commit
ec20cf01b5
@ -502,3 +502,81 @@ 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;
|
||||
}
|
||||
|
||||
//aligned alloc for now only supports default allocator or external
|
||||
//allocator.
|
||||
if((caps & (MALLOC_CAP_DEFAULT | MALLOC_CAP_SPIRAM)) == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//if caps requested are supported, clear undesired others:
|
||||
caps &= (MALLOC_CAP_DEFAULT | MALLOC_CAP_SPIRAM);
|
||||
|
||||
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) {
|
||||
//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;
|
||||
}
|
||||
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
|
||||
heap_t *heap = find_containing_heap(ptr);
|
||||
assert(heap != NULL && "free() target pointer is outside heap areas");
|
||||
multi_heap_aligned_free(heap->heap, ptr);
|
||||
}
|
||||
|
@ -85,6 +85,51 @@ 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
|
||||
*
|
||||
* @note Any memory allocated with heaps_caps_aligned_alloc() MUST
|
||||
* be freed with heap_caps_aligned_free() and CANNOT be passed to free()
|
||||
*
|
||||
*/
|
||||
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
|
||||
*
|
||||
* @note Any memory allocated with heap_caps_aligned_calloc() MUST
|
||||
* be freed with heap_caps_aligned_free() and CANNOT be passed to free()
|
||||
*/
|
||||
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
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
|
@ -29,6 +29,17 @@ extern "C" {
|
||||
/** @brief Opaque handle to a registered heap */
|
||||
typedef struct multi_heap_info *multi_heap_handle_t;
|
||||
|
||||
/**
|
||||
* @brief allocate a chunk of memory with specific alignment
|
||||
*
|
||||
* @param heap Handle to a registered heap.
|
||||
* @param size size in bytes of memory chunk
|
||||
* @param alignment how the memory must be aligned
|
||||
*
|
||||
* @return pointer to the memory allocated, NULL on failure
|
||||
*/
|
||||
void *multi_heap_aligned_alloc(multi_heap_handle_t heap, size_t size, size_t alignment);
|
||||
|
||||
/** @brief malloc() a buffer in a given heap
|
||||
*
|
||||
* Semantics are the same as standard malloc(), only the returned buffer will be allocated in the specified heap.
|
||||
@ -40,6 +51,14 @@ typedef struct multi_heap_info *multi_heap_handle_t;
|
||||
*/
|
||||
void *multi_heap_malloc(multi_heap_handle_t heap, size_t size);
|
||||
|
||||
/** @brief free() a buffer aligned in a given heap.
|
||||
*
|
||||
* @param heap Handle to a registered heap.
|
||||
* @param p NULL, or a pointer previously returned from multi_heap_aligned_alloc() for the same heap.
|
||||
*/
|
||||
void multi_heap_aligned_free(multi_heap_handle_t heap, void *p);
|
||||
|
||||
|
||||
/** @brief free() a buffer in a given heap.
|
||||
*
|
||||
* Semantics are the same as standard free(), only the argument 'p' must be NULL or have been allocated in the specified heap.
|
||||
|
@ -23,6 +23,7 @@ typedef const struct heap_block *multi_heap_block_handle_t;
|
||||
|
||||
If heap poisoning is enabled, wrapper functions call each of these.
|
||||
*/
|
||||
|
||||
void *multi_heap_malloc_impl(multi_heap_handle_t heap, size_t size);
|
||||
void multi_heap_free_impl(multi_heap_handle_t heap, void *p);
|
||||
void *multi_heap_realloc_impl(multi_heap_handle_t heap, void *p, size_t size);
|
||||
|
@ -45,6 +45,9 @@
|
||||
#define HEAD_CANARY_PATTERN 0xABBA1234
|
||||
#define TAIL_CANARY_PATTERN 0xBAAD5678
|
||||
|
||||
|
||||
#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
|
||||
|
||||
typedef struct {
|
||||
uint32_t head_canary;
|
||||
MULTI_HEAP_BLOCK_OWNER
|
||||
@ -182,6 +185,59 @@ static bool verify_fill_pattern(void *data, size_t size, bool print_errors, bool
|
||||
}
|
||||
#endif
|
||||
|
||||
void *multi_heap_aligned_alloc(multi_heap_handle_t heap, size_t size, size_t alignment)
|
||||
{
|
||||
if(heap == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(!size) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(!alignment) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//Alignment must be a power of two...
|
||||
if((alignment & (alignment - 1)) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(size > SIZE_MAX - POISON_OVERHEAD) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32_t overhead = (sizeof(uint32_t) + (alignment - 1) + POISON_OVERHEAD);
|
||||
|
||||
multi_heap_internal_lock(heap);
|
||||
poison_head_t *head = multi_heap_malloc_impl(heap, size + overhead);
|
||||
uint8_t *data = NULL;
|
||||
if (head != NULL) {
|
||||
data = poison_allocated_region(head, size + (overhead - POISON_OVERHEAD));
|
||||
#ifdef SLOW
|
||||
/* check everything we got back is FREE_FILL_PATTERN & swap for MALLOC_FILL_PATTERN */
|
||||
bool ret = verify_fill_pattern(data, size, true, true, true);
|
||||
assert( ret );
|
||||
#else
|
||||
(void)data;
|
||||
#endif
|
||||
} else {
|
||||
multi_heap_internal_unlock(heap);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//Lets align our new obtained block address:
|
||||
//and save information to recover original block pointer
|
||||
//to allow us to deallocate the memory when needed
|
||||
void *ptr = (void *)ALIGN_UP((uintptr_t)head + sizeof(uint32_t) + sizeof(poison_head_t), alignment);
|
||||
*((uint32_t *)ptr - 1) = (uint32_t)((uintptr_t)ptr - (uintptr_t)head);
|
||||
|
||||
multi_heap_internal_unlock(heap);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void *multi_heap_malloc(multi_heap_handle_t heap, size_t size)
|
||||
{
|
||||
if(size > SIZE_MAX - POISON_OVERHEAD) {
|
||||
@ -203,6 +259,30 @@ void *multi_heap_malloc(multi_heap_handle_t heap, size_t size)
|
||||
return data;
|
||||
}
|
||||
|
||||
void multi_heap_aligned_free(multi_heap_handle_t heap, void *p)
|
||||
{
|
||||
if(p == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
multi_heap_internal_lock(heap);
|
||||
|
||||
uint32_t offset = *((uint32_t *)p - 1);
|
||||
void *block_head = (void *)((uint8_t *)p - offset);
|
||||
block_head += sizeof(poison_head_t);
|
||||
|
||||
poison_head_t *head = verify_allocated_region(block_head, true);
|
||||
assert(head != NULL);
|
||||
block_head -= sizeof(poison_head_t);
|
||||
#ifdef SLOW
|
||||
/* replace everything with FREE_FILL_PATTERN, including the poison head/tail */
|
||||
memset(block_head, FREE_FILL_PATTERN, head->alloc_size + POISON_OVERHEAD);
|
||||
#endif
|
||||
|
||||
multi_heap_free_impl(heap, block_head);
|
||||
multi_heap_internal_unlock(heap);
|
||||
}
|
||||
|
||||
void multi_heap_free(multi_heap_handle_t heap, void *p)
|
||||
{
|
||||
if (p == NULL) {
|
||||
|
139
components/heap/test/test_aligned_alloc_caps.c
Normal file
139
components/heap/test/test_aligned_alloc_caps.c
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
Tests for the capabilities-based memory allocator.
|
||||
*/
|
||||
|
||||
#include <esp_types.h>
|
||||
#include <stdio.h>
|
||||
#include "unity.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_spi_flash.h"
|
||||
#include <stdlib.h>
|
||||
#include <sys/param.h>
|
||||
#include <string.h>
|
||||
|
||||
TEST_CASE("Capabilities aligned allocator 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_alloc(alignments, (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);
|
||||
}
|
||||
}
|
||||
|
||||
//Alloc from a non permitted area:
|
||||
uint32_t *not_permitted_buf = (uint32_t *)heap_caps_aligned_alloc(alignments, (alignments + 137), MALLOC_CAP_EXEC | 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 *)heap_caps_aligned_alloc(alignments, 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
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
}
|
@ -494,3 +494,51 @@ TEST_CASE("unaligned heaps", "[multi_heap]")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef CONFIG_HEAP_POISONING_NONE
|
||||
|
||||
TEST_CASE("multi_heap aligned allocations", "[multi_heap]")
|
||||
{
|
||||
uint8_t test_heap[1024 * 1024];
|
||||
multi_heap_handle_t heap = multi_heap_register(test_heap, sizeof(test_heap));
|
||||
uint32_t aligments = 0; // starts from alignment by 4-byte boundary
|
||||
size_t old_size = multi_heap_free_size(heap);
|
||||
size_t leakage = 1024;
|
||||
printf("[ALIGNED_ALLOC] heap_size before: %d \n", old_size);
|
||||
|
||||
printf("New heap:\n");
|
||||
multi_heap_dump(heap);
|
||||
printf("*********************\n");
|
||||
|
||||
for(;aligments < 500 * 1024; aligments++) {
|
||||
|
||||
//Use some stupid size value to test correct alignment even in strange
|
||||
//memory layout objects:
|
||||
uint8_t *buf = (uint8_t *)multi_heap_aligned_alloc(heap, (aligments + 137), aligments );
|
||||
if(((aligments & (aligments - 1)) != 0) || (!aligments)) {
|
||||
REQUIRE( buf == NULL );
|
||||
//printf("[ALIGNED_ALLOC] alignment: %u is not a power of two, don't allow allocation \n", aligments);
|
||||
} else {
|
||||
REQUIRE( buf != NULL );
|
||||
REQUIRE((intptr_t)buf >= (intptr_t)test_heap);
|
||||
REQUIRE((intptr_t)buf < (intptr_t)(test_heap + sizeof(test_heap)));
|
||||
|
||||
printf("[ALIGNED_ALLOC] alignment required: %u \n", aligments);
|
||||
//printf("[ALIGNED_ALLOC] allocated size: %d \n", multi_heap_get_allocated_size(heap, buf));
|
||||
printf("[ALIGNED_ALLOC] address of allocated memory: %p \n\n", (void *)buf);
|
||||
//Address of obtained block must be aligned with selected value
|
||||
REQUIRE(((intptr_t)buf & (aligments - 1)) == 0);
|
||||
|
||||
//Write some data, if it corrupts memory probably the heap
|
||||
//canary verification will fail:
|
||||
memset(buf, 0xA5, (aligments + 137));
|
||||
|
||||
multi_heap_aligned_free(heap, buf);
|
||||
}
|
||||
}
|
||||
|
||||
printf("[ALIGNED_ALLOC] heap_size after: %d \n", multi_heap_free_size(heap));
|
||||
REQUIRE((old_size - multi_heap_free_size(heap)) <= leakage);
|
||||
}
|
||||
|
||||
#endif
|
@ -94,6 +94,7 @@ void* memalign(size_t alignment, size_t n)
|
||||
extern void memalign_function_was_linked_but_unsupported_in_esp_idf(void);
|
||||
memalign_function_was_linked_but_unsupported_in_esp_idf();
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
int malloc_trim(size_t pad)
|
||||
|
Loading…
x
Reference in New Issue
Block a user