diff --git a/components/esp_rom/CMakeLists.txt b/components/esp_rom/CMakeLists.txt index dd4cc19a9a..3b05e51d59 100644 --- a/components/esp_rom/CMakeLists.txt +++ b/components/esp_rom/CMakeLists.txt @@ -44,11 +44,16 @@ else() endif() endif() - if(CONFIG_HEAP_TLSF_USE_ROM_IMPL AND (CONFIG_ESP_ROM_TLSF_CHECK_PATCH OR CONFIG_HEAP_TLSF_CHECK_PATCH)) + if(CONFIG_HEAP_TLSF_USE_ROM_IMPL AND CONFIG_ESP_ROM_TLSF_CHECK_PATCH) # This file shall be included in the build if TLSF in ROM is activated list(APPEND sources "patches/esp_rom_tlsf.c") endif() + if(CONFIG_HEAP_TLSF_USE_ROM_IMPL AND CONFIG_ESP_ROM_MULTI_HEAP_WALK_PATCH) + # This file shall be included in the build if TLSF in ROM is activated + list(APPEND sources "patches/esp_rom_multi_heap.c") + endif() + list(APPEND private_required_comp soc hal) endif() @@ -64,6 +69,8 @@ if(CONFIG_HAL_WDT_USE_ROM_IMPL) list(APPEND sources "patches/esp_rom_wdt.c") endif() + + if(CONFIG_ESP_ROM_HAS_FLASH_COUNT_PAGES_BUG OR CONFIG_ESP_ROM_HAS_CACHE_WRITEBACK_BUG) list(APPEND sources "patches/esp_rom_cache_esp32s2_esp32s3.c") endif() @@ -216,6 +223,10 @@ else() # Regular app build target_link_libraries(${COMPONENT_LIB} PRIVATE "-u tlsf_set_rom_patches") endif() + if((CONFIG_ESP_ROM_TLSF_CHECK_PATCH OR CONFIG_ESP_ROM_MULTI_HEAP_WALK_PATCH)) + target_link_libraries(${COMPONENT_LIB} PRIVATE "-u esp_rom_include_multi_heap_patch") + endif() + rom_linker_script("heap") endif() diff --git a/components/esp_rom/esp32c2/Kconfig.soc_caps.in b/components/esp_rom/esp32c2/Kconfig.soc_caps.in index ca4d67e28a..c1fa90c7e8 100644 --- a/components/esp_rom/esp32c2/Kconfig.soc_caps.in +++ b/components/esp_rom/esp32c2/Kconfig.soc_caps.in @@ -39,6 +39,14 @@ config ESP_ROM_HAS_HEAP_TLSF bool default y +config ESP_ROM_TLSF_CHECK_PATCH + bool + default y + +config ESP_ROM_MULTI_HEAP_WALK_PATCH + bool + default y + config ESP_ROM_HAS_LAYOUT_TABLE bool default y diff --git a/components/esp_rom/esp32c2/esp_rom_caps.h b/components/esp_rom/esp32c2/esp_rom_caps.h index eb7284de4c..1362fd77f4 100644 --- a/components/esp_rom/esp32c2/esp_rom_caps.h +++ b/components/esp_rom/esp32c2/esp_rom_caps.h @@ -15,6 +15,8 @@ #define ESP_ROM_HAS_HAL_WDT (1) // ROM has the implementation of Watchdog HAL driver #define ESP_ROM_HAS_HAL_SYSTIMER (1) // ROM has the implementation of Systimer HAL driver #define ESP_ROM_HAS_HEAP_TLSF (1) // ROM has the implementation of the tlsf and multi-heap library +#define ESP_ROM_TLSF_CHECK_PATCH (1) // ROM does not contain the patch of tlsf_check_pool() +#define ESP_ROM_MULTI_HEAP_WALK_PATCH (1) // ROM does not contain the patch of multi_heap_walk() #define ESP_ROM_HAS_LAYOUT_TABLE (1) // ROM has the layout table #define ESP_ROM_HAS_SPI_FLASH (1) // ROM has the implementation of SPI Flash driver #define ESP_ROM_HAS_NEWLIB (1) // ROM has newlib (at least parts of it) functions included diff --git a/components/esp_rom/esp32c5/beta3/esp32c5/Kconfig.soc_caps.in b/components/esp_rom/esp32c5/beta3/esp32c5/Kconfig.soc_caps.in index f2b89066e3..0f8129562c 100644 --- a/components/esp_rom/esp32c5/beta3/esp32c5/Kconfig.soc_caps.in +++ b/components/esp_rom/esp32c5/beta3/esp32c5/Kconfig.soc_caps.in @@ -51,6 +51,10 @@ config ESP_ROM_TLSF_CHECK_PATCH bool default y +config ESP_ROM_MULTI_HEAP_WALK_PATCH + bool + default y + config ESP_ROM_HAS_LAYOUT_TABLE bool default y diff --git a/components/esp_rom/esp32c5/beta3/esp32c5/esp_rom_caps.h b/components/esp_rom/esp32c5/beta3/esp32c5/esp_rom_caps.h index 00e7cfed2e..d6ecb2c6a9 100644 --- a/components/esp_rom/esp32c5/beta3/esp32c5/esp_rom_caps.h +++ b/components/esp_rom/esp32c5/beta3/esp32c5/esp_rom_caps.h @@ -18,6 +18,7 @@ #define ESP_ROM_HAS_HAL_SYSTIMER (1) // ROM has the implementation of Systimer HAL driver #define ESP_ROM_HAS_HEAP_TLSF (1) // ROM has the implementation of the tlsf and multi-heap library #define ESP_ROM_TLSF_CHECK_PATCH (1) // ROM does not contain the patch of tlsf_check_pool() +#define ESP_ROM_MULTI_HEAP_WALK_PATCH (1) // ROM does not contain the patch of multi_heap_walk() #define ESP_ROM_HAS_LAYOUT_TABLE (1) // ROM has the layout table #define ESP_ROM_HAS_SPI_FLASH (1) // ROM has the implementation of SPI Flash driver #define ESP_ROM_WITHOUT_REGI2C (1) // ROM has no regi2c APIs diff --git a/components/esp_rom/esp32c6/Kconfig.soc_caps.in b/components/esp_rom/esp32c6/Kconfig.soc_caps.in index 3a2a837056..f117be963f 100644 --- a/components/esp_rom/esp32c6/Kconfig.soc_caps.in +++ b/components/esp_rom/esp32c6/Kconfig.soc_caps.in @@ -51,6 +51,10 @@ config ESP_ROM_TLSF_CHECK_PATCH bool default y +config ESP_ROM_MULTI_HEAP_WALK_PATCH + bool + default y + config ESP_ROM_HAS_LAYOUT_TABLE bool default y diff --git a/components/esp_rom/esp32c6/esp_rom_caps.h b/components/esp_rom/esp32c6/esp_rom_caps.h index 1fb9480849..2b448d3d70 100644 --- a/components/esp_rom/esp32c6/esp_rom_caps.h +++ b/components/esp_rom/esp32c6/esp_rom_caps.h @@ -18,6 +18,7 @@ #define ESP_ROM_HAS_HAL_SYSTIMER (1) // ROM has the implementation of Systimer HAL driver #define ESP_ROM_HAS_HEAP_TLSF (1) // ROM has the implementation of the tlsf and multi-heap library #define ESP_ROM_TLSF_CHECK_PATCH (1) // ROM does not contain the patch of tlsf_check_pool() +#define ESP_ROM_MULTI_HEAP_WALK_PATCH (1) // ROM does not contain the patch of multi_heap_walk() #define ESP_ROM_HAS_LAYOUT_TABLE (1) // ROM has the layout table #define ESP_ROM_HAS_SPI_FLASH (1) // ROM has the implementation of SPI Flash driver #define ESP_ROM_HAS_REGI2C_BUG (1) // ROM has the regi2c bug diff --git a/components/esp_rom/esp32h2/Kconfig.soc_caps.in b/components/esp_rom/esp32h2/Kconfig.soc_caps.in index e6a8376ae5..d43ea1a707 100644 --- a/components/esp_rom/esp32h2/Kconfig.soc_caps.in +++ b/components/esp_rom/esp32h2/Kconfig.soc_caps.in @@ -43,6 +43,10 @@ config ESP_ROM_TLSF_CHECK_PATCH bool default y +config ESP_ROM_MULTI_HEAP_WALK_PATCH + bool + default y + config ESP_ROM_HAS_LAYOUT_TABLE bool default y diff --git a/components/esp_rom/esp32h2/esp_rom_caps.h b/components/esp_rom/esp32h2/esp_rom_caps.h index 186b94a09a..4ff14f74ac 100644 --- a/components/esp_rom/esp32h2/esp_rom_caps.h +++ b/components/esp_rom/esp32h2/esp_rom_caps.h @@ -16,6 +16,7 @@ #define ESP_ROM_HAS_HAL_SYSTIMER (1) // ROM has the implementation of Systimer HAL driver #define ESP_ROM_HAS_HEAP_TLSF (1) // ROM has the implementation of the tlsf and multi-heap library #define ESP_ROM_TLSF_CHECK_PATCH (1) // ROM does not contain the patch of tlsf_check_pool() +#define ESP_ROM_MULTI_HEAP_WALK_PATCH (1) // ROM does not contain the patch of multi_heap_walk() #define ESP_ROM_HAS_LAYOUT_TABLE (1) // ROM has the layout table #define ESP_ROM_HAS_SPI_FLASH (1) // ROM has the implementation of SPI Flash driver #define ESP_ROM_WITHOUT_REGI2C (1) // ROM has no regi2c APIs diff --git a/components/esp_rom/include/esp_rom_multi_heap.h b/components/esp_rom/include/esp_rom_multi_heap.h new file mode 100644 index 0000000000..b95ce4607d --- /dev/null +++ b/components/esp_rom/include/esp_rom_multi_heap.h @@ -0,0 +1,43 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Opaque handle to a registered heap */ +typedef struct multi_heap_info *multi_heap_handle_t; + +/** + * @brief Callback called when walking the given heap blocks of memory + * + * @param block_ptr Pointer to the block data + * @param block_size The size of the block + * @param block_used Block status. 0: free, 1: allocated + * @param user_data Opaque pointer to user defined data + * + * @return True if the walker is expected to continue the heap traversal + * False if the walker is expected to stop the traversal of the heap + */ +typedef bool (*multi_heap_walker_cb_t)(void *block_ptr, size_t block_size, int block_used, void *user_data); + +/** + * @brief Call the tlsf_walk_pool function of the heap given as parameter with + * the walker function passed as parameter + * + * @param heap The heap to traverse + * @param walker_func The walker to trigger on each block of the heap + * @param user_data Opaque pointer to user defined data + */ +void multi_heap_walk(multi_heap_handle_t heap, multi_heap_walker_cb_t walker_func, void *user_data); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_rom/patches/esp_rom_multi_heap.c b/components/esp_rom/patches/esp_rom_multi_heap.c new file mode 100644 index 0000000000..6a6682dbf3 --- /dev/null +++ b/components/esp_rom/patches/esp_rom_multi_heap.c @@ -0,0 +1,50 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * This file is a patch for the multi_heap.c file stored in ROM + * - added function multi_heap_walk + */ + +#include +#include +#include + +#include "esp_rom_multi_heap.h" + +// Hook to force the linker to include this file +void esp_rom_include_multi_heap_patch(void) +{ +} + +/*! + * @brief Opaque types for TLSF implementation + */ +typedef void* tlsf_t; +typedef void* pool_t; +typedef void* tlsf_walker; + +typedef struct multi_heap_info { + void *lock; + size_t free_bytes; + size_t minimum_free_bytes; + size_t pool_size; + void* heap_data; +} heap_t; + +extern void tlsf_walk_pool(pool_t pool, tlsf_walker walker, void* user); +extern pool_t tlsf_get_pool(tlsf_t tlsf); +extern void multi_heap_internal_lock(multi_heap_handle_t heap); +extern void multi_heap_internal_unlock(multi_heap_handle_t heap); + +void multi_heap_walk(multi_heap_handle_t heap, multi_heap_walker_cb_t walker_func, void *user_data) +{ + assert(heap != NULL); + + multi_heap_internal_lock(heap); + tlsf_walk_pool(tlsf_get_pool(heap->heap_data), walker_func, user_data); + multi_heap_internal_unlock(heap); +} diff --git a/components/esp_rom/patches/esp_rom_tlsf.c b/components/esp_rom/patches/esp_rom_tlsf.c index 06da058328..ad3af65d42 100644 --- a/components/esp_rom/patches/esp_rom_tlsf.c +++ b/components/esp_rom/patches/esp_rom_tlsf.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -15,6 +15,7 @@ #include #include #include +#include #include "esp_rom_caps.h" #include "esp_rom_tlsf.h" @@ -24,13 +25,17 @@ */ typedef void* tlsf_t; typedef void* pool_t; -typedef void* tlsf_walker; +typedef ptrdiff_t tlsfptr_t; /* ---------------------------------------------------------------- * Bring certain inline functions, macro and structures from the * tlsf ROM implementation to be able to compile the patch. * ---------------------------------------------------------------- */ +#if !defined (tlsf_assert) +#define tlsf_assert assert +#endif + #define tlsf_cast(t, exp) ((t) (exp)) #define block_header_free_bit (1 << 0) @@ -72,6 +77,30 @@ static inline __attribute__((always_inline)) block_header_t* block_from_ptr(cons tlsf_cast(unsigned char*, ptr) - block_start_offset); } +static inline __attribute__((always_inline)) block_header_t* offset_to_block(const void* ptr, size_t size) +{ + return tlsf_cast(block_header_t*, tlsf_cast(tlsfptr_t, ptr) + size); +} + +static inline __attribute__((always_inline)) int block_is_last(const block_header_t* block) +{ + return block_size(block) == 0; +} + +static inline __attribute__((always_inline)) void* block_to_ptr(const block_header_t* block) +{ + return tlsf_cast(void*, + tlsf_cast(unsigned char*, block) + block_start_offset); +} + +static inline __attribute__((always_inline)) block_header_t* block_next(const block_header_t* block) +{ + block_header_t* next = offset_to_block(block_to_ptr(block), + block_size(block) - block_header_overhead); + tlsf_assert(!block_is_last(block)); + return next; +} + /* ---------------------------------------------------------------- * End of the environment necessary to compile and link the patch * defined below @@ -92,7 +121,9 @@ typedef struct integrity_t int status; } integrity_t; -static void integrity_walker(void* ptr, size_t size, int used, void* user) +typedef bool (*tlsf_walker)(void* ptr, size_t size, int used, void* user); + +static bool integrity_walker(void* ptr, size_t size, int used, void* user) { block_header_t* block = block_from_ptr(ptr); integrity_t* integ = tlsf_cast(integrity_t*, user); @@ -124,9 +155,38 @@ static void integrity_walker(void* ptr, size_t size, int used, void* user) integ->prev_status = this_status; integ->status += status; + + return true; +} + +static bool default_walker(void* ptr, size_t size, int used, void* user) +{ + (void)user; + printf("\t%p %s size: %x (%p)\n", ptr, used ? "used" : "free", (unsigned int)size, block_from_ptr(ptr)); + return true; +} + +void tlsf_walk_pool(pool_t pool, tlsf_walker walker, void* user) +{ + tlsf_walker pool_walker = walker ? walker : default_walker; + block_header_t* block = + offset_to_block(pool, -(int)block_header_overhead); + + bool ret_val = true; + while (block && !block_is_last(block) && ret_val == true) + { + ret_val = pool_walker( + block_to_ptr(block), + block_size(block), + !block_is_free(block), + user); + + if (ret_val == true) { + block = block_next(block); + } + } } -extern void tlsf_walk_pool(pool_t pool, tlsf_walker walker, void* user); int tlsf_check_pool(pool_t pool) { /* Check that the blocks are physically correct. */ diff --git a/components/heap/Kconfig b/components/heap/Kconfig index e593e4707a..723ff3d300 100644 --- a/components/heap/Kconfig +++ b/components/heap/Kconfig @@ -119,15 +119,6 @@ menu "Heap memory debugging" features will be added and bugs will be fixed in the IDF source but cannot be synced to ROM. - config HEAP_TLSF_CHECK_PATCH - bool "Patch the tlsf_check_pool() for ROM HEAP TLSF implementation" - depends on HEAP_TLSF_USE_ROM_IMPL && IDF_TARGET_ESP32C2 && ESP32C2_REV_MIN_FULL < 200 - default y - help - ROM does not contain the patch of tlsf_check_pool() allowing perform - the integrity checking on used blocks. The patch to allow such check - needs to be applied. - config HEAP_PLACE_FUNCTION_INTO_FLASH bool "Force the entire heap component to be placed in flash memory" depends on !HEAP_TLSF_USE_ROM_IMPL diff --git a/components/heap/heap_caps.c b/components/heap/heap_caps.c index ca5ec1338f..9dea55673c 100644 --- a/components/heap/heap_caps.c +++ b/components/heap/heap_caps.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -15,6 +15,10 @@ #include "heap_private.h" #include "esp_system.h" +#if CONFIG_HEAP_TLSF_USE_ROM_IMPL +#include "esp_rom_multi_heap.h" +#endif + #ifdef CONFIG_HEAP_USE_HOOKS #define CALL_HOOK(hook, ...) { \ if (hook != NULL) { \ @@ -860,3 +864,46 @@ void *heap_caps_aligned_calloc(size_t alignment, size_t n, size_t size, uint32_t return ptr; } + +typedef struct walker_data { + void *opaque_ptr; + heap_caps_walker_cb_t cb_func; + heap_t *heap; +} walker_data_t; + +__attribute__((noinline)) static bool heap_caps_walker(void* block_ptr, size_t block_size, int block_used, void *user_data) +{ + walker_data_t *walker_data = (walker_data_t*)user_data; + + walker_heap_into_t heap_info = { + (intptr_t)walker_data->heap->start, + (intptr_t)walker_data->heap->end + }; + walker_block_info_t block_info = { + block_ptr, + block_size, + (bool)block_used + }; + + return walker_data->cb_func(heap_info, block_info, walker_data->opaque_ptr); +} + +void heap_caps_walk(uint32_t caps, heap_caps_walker_cb_t walker_func, void *user_data) +{ + assert(walker_func != NULL); + + bool all_heaps = caps & MALLOC_CAP_INVALID; + heap_t *heap; + SLIST_FOREACH(heap, ®istered_heaps, next) { + if (heap->heap != NULL + && (all_heaps || (get_all_caps(heap) & caps) == caps)) { + walker_data_t walker_data = {user_data, walker_func, heap}; + multi_heap_walk(heap->heap, heap_caps_walker, &walker_data); + } + } +} + +void heap_caps_walk_all(heap_caps_walker_cb_t walker_func, void *user_data) +{ + heap_caps_walk(MALLOC_CAP_INVALID, walker_func, user_data); +} diff --git a/components/heap/include/esp_heap_caps.h b/components/heap/include/esp_heap_caps.h index ec202359a3..4d590204e5 100644 --- a/components/heap/include/esp_heap_caps.h +++ b/components/heap/include/esp_heap_caps.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -444,6 +444,56 @@ void heap_caps_dump_all(void); */ size_t heap_caps_get_allocated_size( void *ptr ); +/** + * @brief Structure used to store heap related data passed to + * the walker callback function + */ +typedef struct walker_heap_info { + intptr_t start; ///< Start address of the heap in which the block is located + intptr_t end; ///< End address of the heap in which the block is located +} walker_heap_into_t; + +/** + * @brief Structure used to store block related data passed to + * the walker callback function + */ +typedef struct walker_block_info { + void *ptr; ///< Pointer to the block data + size_t size; ///< The size of the block + bool used; ///< Block status. True: used, False: free +} walker_block_info_t; + +/** + * @brief Function callback used to get information of memory block + * during calls to heap_caps_walk or heap_caps_walk_all + * + * @param heap_info See walker_heap_into_t + * @param block_info See walker_block_info_t + * @param user_data Opaque pointer to user defined data + * + * @return True to proceed with the heap traversal + * False to stop the traversal of the current heap and continue + * with the traversal of the next heap (if any) + */ +typedef bool (*heap_caps_walker_cb_t)(walker_heap_into_t heap_info, walker_block_info_t block_info, void *user_data); + +/** + * @brief Function called to walk through the heaps with the given set of capabilities + * + * @param caps The set of capabilities assigned to the heaps to walk through + * @param walker_func Callback called for each block of the heaps being traversed + * @param user_data Opaque pointer to user defined data + */ +void heap_caps_walk(uint32_t caps, heap_caps_walker_cb_t walker_func, void *user_data); + +/** + * @brief Function called to walk through all heaps defined by the heap component + * + * @param walker_func Callback called for each block of the heaps being traversed + * @param user_data Opaque pointer to user defined data + */ +void heap_caps_walk_all(heap_caps_walker_cb_t walker_func, void *user_data); + #ifdef __cplusplus } #endif diff --git a/components/heap/include/multi_heap.h b/components/heap/include/multi_heap.h index 9fa1e090a2..20e1ba977e 100644 --- a/components/heap/include/multi_heap.h +++ b/components/heap/include/multi_heap.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -207,6 +207,29 @@ size_t multi_heap_reset_minimum_free_bytes(multi_heap_handle_t heap); */ void multi_heap_restore_minimum_free_bytes(multi_heap_handle_t heap, const size_t new_minimum_free_bytes_value); +/** + * @brief Callback called when walking the given heap blocks of memory + * + * @param block_ptr Pointer to the block data + * @param block_size The size of the block + * @param block_used Block status. 0: free, 1: allocated + * @param user_data Opaque pointer to user defined data + * + * @return True if the walker is expected to continue the heap traversal + * False if the walker is expected to stop the traversal of the heap + */ +typedef bool (*multi_heap_walker_cb_t)(void *block_ptr, size_t block_size, int block_used, void *user_data); + +/** + * @brief Call the tlsf_walk_pool function of the heap given as parameter with + * the walker function passed as parameter + * + * @param heap The heap to traverse + * @param walker_func The walker to trigger on each block of the heap + * @param user_data Opaque pointer to user defined data + */ +void multi_heap_walk(multi_heap_handle_t heap, multi_heap_walker_cb_t walker_func, void *user_data); + #ifdef __cplusplus } #endif diff --git a/components/heap/multi_heap.c b/components/heap/multi_heap.c index 151113ac05..a92b4aa564 100644 --- a/components/heap/multi_heap.c +++ b/components/heap/multi_heap.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -355,13 +355,11 @@ bool multi_heap_check(multi_heap_handle_t heap, bool print_errors) return valid; } -__attribute__((noinline)) static void multi_heap_dump_tlsf(void* ptr, size_t size, int used, void* user) +__attribute__((noinline)) static bool multi_heap_dump_tlsf(void *ptr, size_t size, int used, void *user) { (void)user; - MULTI_HEAP_STDERR_PRINTF("Block %p data, size: %d bytes, Free: %s \n", - (void *)ptr, - size, - used ? "No" : "Yes"); + MULTI_HEAP_STDERR_PRINTF("Block %p data, size: %d bytes, Free: %s \n", (void *)ptr, size, used ? "No" : "Yes"); + return true; } void multi_heap_dump(multi_heap_handle_t heap) @@ -392,7 +390,7 @@ size_t multi_heap_minimum_free_size_impl(multi_heap_handle_t heap) return heap->minimum_free_bytes; } -__attribute__((noinline)) static void multi_heap_get_info_tlsf(void* ptr, size_t size, int used, void* user) +__attribute__((noinline)) static bool multi_heap_get_info_tlsf(void* ptr, size_t size, int used, void* user) { multi_heap_info_t *info = user; @@ -407,6 +405,7 @@ __attribute__((noinline)) static void multi_heap_get_info_tlsf(void* ptr, size_t } info->total_blocks++; + return true; } void multi_heap_get_info_impl(multi_heap_handle_t heap, multi_heap_info_t *info) @@ -431,6 +430,15 @@ void multi_heap_get_info_impl(multi_heap_handle_t heap, multi_heap_info_t *info) multi_heap_internal_unlock(heap); } +void multi_heap_walk(multi_heap_handle_t heap, multi_heap_walker_cb_t walker_func, void *user_data) +{ + assert(heap != NULL); + + multi_heap_internal_lock(heap); + tlsf_walk_pool(tlsf_get_pool(heap->heap_data), walker_func, user_data); + multi_heap_internal_unlock(heap); +} + #endif // CONFIG_HEAP_TLSF_USE_ROM_IMPL size_t multi_heap_reset_minimum_free_bytes(multi_heap_handle_t heap) diff --git a/components/heap/test_apps/heap_tests/main/CMakeLists.txt b/components/heap/test_apps/heap_tests/main/CMakeLists.txt index 0ed19610ce..1803d9f193 100644 --- a/components/heap/test_apps/heap_tests/main/CMakeLists.txt +++ b/components/heap/test_apps/heap_tests/main/CMakeLists.txt @@ -8,7 +8,8 @@ set(src_test "test_heap_main.c" "test_malloc.c" "test_realloc.c" "test_runtime_heap_reg.c" - "test_task_tracking.c") + "test_task_tracking.c" + "test_walker.c") idf_component_register(SRCS ${src_test} INCLUDE_DIRS "." diff --git a/components/heap/test_apps/heap_tests/main/test_walker.c b/components/heap/test_apps/heap_tests/main/test_walker.c new file mode 100644 index 0000000000..8db8dc449d --- /dev/null +++ b/components/heap/test_apps/heap_tests/main/test_walker.c @@ -0,0 +1,101 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#include "unity.h" +#include "stdio.h" + +#include "esp_heap_caps.h" +#include "esp_rom_sys.h" +#define PASS_CODE 0x9876BAAB +#define ALLOC_SIZE 16 + +static void calculate_block_metadata_size(size_t *header, size_t *footer) +{ + *header = 0; + *footer = 0; +#if !CONFIG_HEAP_POISONING_DISABLED + *header += 8; // sizeof(poison_head_t) + *footer += 4; // sizeof(poison_tail_t) +#endif +#if CONFIG_HEAP_TASK_TRACKING + *header += 4; // sizeof(TaskHandle_t) +#endif +} + +static bool block_found = false; +static bool heap_corrupted = false; +static bool heap_walker(walker_heap_into_t heap_info, walker_block_info_t block_info, void* user_data) +{ + if ((intptr_t)heap_info.end - (intptr_t)block_info.ptr < block_info.size) + { + heap_corrupted = true; + return false; + } + + TEST_ASSERT(*(uint32_t*)user_data == PASS_CODE); + TEST_ASSERT_NOT_NULL(block_info.ptr); + + size_t metadata_size_head = 0; + size_t metadata_size_tail = 0; + calculate_block_metadata_size(&metadata_size_head, &metadata_size_tail); + + /* look for the first 4 bytes pass code to identify the memory allocated in the test */ + const uint32_t pass_code = *((uint32_t*)block_info.ptr + (metadata_size_head / sizeof(uint32_t))); + if (pass_code == PASS_CODE) { + TEST_ASSERT(block_info.size == ALLOC_SIZE + metadata_size_head + metadata_size_tail); + TEST_ASSERT_TRUE(block_info.used); + block_found = true; + } + + return true; +} + +/* This test assures the proper functioning of heap_caps_walk and heap_caps_walk_all + * when a corruption occurs in a random heap. The callback which detects the corruption + * returns false to the walker which should result in the interruption of the heap traversal + * by the walker instead of a crash. + */ +TEST_CASE("heap walker", "[heap]") +{ + /* Allocate memory using the MALLOC_CAP_DEFAULT capability */ + void *default_ptr = heap_caps_malloc(ALLOC_SIZE, MALLOC_CAP_DEFAULT); + TEST_ASSERT_NOT_NULL(default_ptr); + + /* Write the pass code in the first word of the allocated memory */ + *((uint32_t*)default_ptr) = (uint32_t)PASS_CODE; + + /* call the heap_caps_walker to make sure the hook function is triggered + * and check that the allocated memory is found while walking the heap */ + uint32_t user_code = PASS_CODE; + heap_caps_walk_all(heap_walker, &user_code); + TEST_ASSERT_TRUE(block_found); +} + +/* This test assures the proper functioning of heap_caps_walk and heap_caps_walk_all + */ +TEST_CASE("heap walker corrupted heap detection", "[heap]") +{ + /* Allocate memory using the MALLOC_CAP_DEFAULT capability */ + void *default_ptr = heap_caps_malloc(ALLOC_SIZE, MALLOC_CAP_DEFAULT); + TEST_ASSERT_NOT_NULL(default_ptr); + + size_t metadata_size_head = 0; + size_t metadata_size_tail = 0; + calculate_block_metadata_size(&metadata_size_head, &metadata_size_tail); + (void)metadata_size_tail; + + /* corrupting the size field of the block metadata with a size bigger + * than the heap itself */ + *((uint32_t*)default_ptr - (metadata_size_head / 4) - 1) = 0xFF000000; + + /* Write the pass code in the first word of the allocated memory */ + *((uint32_t*)default_ptr) = (uint32_t)PASS_CODE; + + /* call the heap_caps_walker to make sure the hook function is triggered + * and check that the allocated memory is found while walking the heap */ + uint32_t user_code = PASS_CODE; + heap_caps_walk_all(heap_walker, &user_code); + TEST_ASSERT_TRUE(heap_corrupted); +} diff --git a/components/heap/tlsf b/components/heap/tlsf index d2e28f8724..8fc595fe22 160000 --- a/components/heap/tlsf +++ b/components/heap/tlsf @@ -1 +1 @@ -Subproject commit d2e28f872472ffc6a704faae65ddee1f24e2dfba +Subproject commit 8fc595fe223cd0b3b5d7b29eb86825e4bd38e6e8