diff --git a/components/esp_rom/esp32c2/esp_rom_heap_tlsf_stub_table.h b/components/esp_rom/esp32c2/esp_rom_heap_tlsf_stub_table.h new file mode 100644 index 00000000000..8ab496a6a37 --- /dev/null +++ b/components/esp_rom/esp32c2/esp_rom_heap_tlsf_stub_table.h @@ -0,0 +1,53 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +#pragma once + +#include +#include "esp_rom_tlsf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief TLSF ROM vtable stub table for esp32c2. + * + * This struct mirrors the TLSF function-pointer vtable stored in ROM. + * Its layout MUST exactly match the ROM vtable for the target chip. + * It is used by tlsf_set_rom_patches() to intercept TLSF calls. + */ +struct heap_tlsf_stub_table_t { + tlsf_t (*tlsf_create)(void* mem); + tlsf_t (*tlsf_create_with_pool)(void* mem, size_t bytes); + pool_t (*tlsf_get_pool)(tlsf_t tlsf); + pool_t (*tlsf_add_pool)(tlsf_t tlsf, void* mem, size_t bytes); + void (*tlsf_remove_pool)(tlsf_t tlsf, pool_t pool); + + void* (*tlsf_malloc)(tlsf_t tlsf, size_t size); + void* (*tlsf_memalign)(tlsf_t tlsf, size_t align, size_t size); + void* (*tlsf_memalign_offs)(tlsf_t tlsf, size_t align, size_t size, size_t offset); + void* (*tlsf_realloc)(tlsf_t tlsf, void* ptr, size_t size); + void (*tlsf_free)(tlsf_t tlsf, void* ptr); + + size_t (*tlsf_block_size)(void* ptr); + size_t (*tlsf_size)(void); + size_t (*tlsf_align_size)(void); + size_t (*tlsf_block_size_min)(void); + size_t (*tlsf_block_size_max)(void); + size_t (*tlsf_pool_overhead)(void); + size_t (*tlsf_alloc_overhead)(void); + + void (*tlsf_walk_pool)(pool_t pool, tlsf_walker walker, void* user); + + int (*tlsf_check)(tlsf_t tlsf); + int (*tlsf_check_pool)(pool_t pool); +}; + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_rom/esp32c5/esp_rom_heap_tlsf_stub_table.h b/components/esp_rom/esp32c5/esp_rom_heap_tlsf_stub_table.h new file mode 100644 index 00000000000..79ec6dbb54f --- /dev/null +++ b/components/esp_rom/esp32c5/esp_rom_heap_tlsf_stub_table.h @@ -0,0 +1,53 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +#pragma once + +#include +#include "esp_rom_tlsf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief TLSF ROM vtable stub table for esp32c5. + * + * This struct mirrors the TLSF function-pointer vtable stored in ROM. + * Its layout MUST exactly match the ROM vtable for the target chip. + * It is used by tlsf_set_rom_patches() to intercept TLSF calls. + */ +struct heap_tlsf_stub_table_t { + tlsf_t (*tlsf_create)(void *mem, size_t max_bytes); + tlsf_t (*tlsf_create_with_pool)(void *mem, size_t pool_bytes, size_t max_bytes); + pool_t (*tlsf_get_pool)(tlsf_t tlsf); + pool_t (*tlsf_add_pool)(tlsf_t tlsf, void *mem, size_t bytes); + void (*tlsf_remove_pool)(tlsf_t tlsf, pool_t pool); + + void *(*tlsf_malloc)(tlsf_t tlsf, size_t size); + void *(*tlsf_memalign)(tlsf_t tlsf, size_t align, size_t size); + void *(*tlsf_memalign_offs)(tlsf_t tlsf, size_t align, size_t size, size_t offset); + void *(*tlsf_realloc)(tlsf_t tlsf, void *ptr, size_t size); + void (*tlsf_free)(tlsf_t tlsf, void *ptr); + + size_t (*tlsf_block_size)(void *ptr); + size_t (*tlsf_size)(tlsf_t tlsf); + size_t (*tlsf_align_size)(void); + size_t (*tlsf_block_size_min)(void); + size_t (*tlsf_block_size_max)(tlsf_t tlsf); + size_t (*tlsf_pool_overhead)(void); + size_t (*tlsf_alloc_overhead)(void); + + void (*tlsf_walk_pool)(pool_t pool, tlsf_walker walker, void *user); + + int (*tlsf_check)(tlsf_t tlsf); + int (*tlsf_check_pool)(pool_t pool); +}; + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_rom/esp32c6/esp_rom_heap_tlsf_stub_table.h b/components/esp_rom/esp32c6/esp_rom_heap_tlsf_stub_table.h new file mode 100644 index 00000000000..1297940fd94 --- /dev/null +++ b/components/esp_rom/esp32c6/esp_rom_heap_tlsf_stub_table.h @@ -0,0 +1,53 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +#pragma once + +#include +#include "esp_rom_tlsf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief TLSF ROM vtable stub table for esp32c6. + * + * This struct mirrors the TLSF function-pointer vtable stored in ROM. + * Its layout MUST exactly match the ROM vtable for the target chip. + * It is used by tlsf_set_rom_patches() to intercept TLSF calls. + */ +struct heap_tlsf_stub_table_t { + tlsf_t (*tlsf_create)(void* mem); + tlsf_t (*tlsf_create_with_pool)(void* mem, size_t bytes); + pool_t (*tlsf_get_pool)(tlsf_t tlsf); + pool_t (*tlsf_add_pool)(tlsf_t tlsf, void* mem, size_t bytes); + void (*tlsf_remove_pool)(tlsf_t tlsf, pool_t pool); + + void* (*tlsf_malloc)(tlsf_t tlsf, size_t size); + void* (*tlsf_memalign)(tlsf_t tlsf, size_t align, size_t size); + void* (*tlsf_memalign_offs)(tlsf_t tlsf, size_t align, size_t size, size_t offset); + void* (*tlsf_realloc)(tlsf_t tlsf, void* ptr, size_t size); + void (*tlsf_free)(tlsf_t tlsf, void* ptr); + + size_t (*tlsf_block_size)(void* ptr); + size_t (*tlsf_size)(void); + size_t (*tlsf_align_size)(void); + size_t (*tlsf_block_size_min)(void); + size_t (*tlsf_block_size_max)(void); + size_t (*tlsf_pool_overhead)(void); + size_t (*tlsf_alloc_overhead)(void); + + void (*tlsf_walk_pool)(pool_t pool, tlsf_walker walker, void* user); + + int (*tlsf_check)(tlsf_t tlsf); + int (*tlsf_check_pool)(pool_t pool); +}; + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_rom/esp32c61/esp_rom_heap_tlsf_stub_table.h b/components/esp_rom/esp32c61/esp_rom_heap_tlsf_stub_table.h new file mode 100644 index 00000000000..472b5b086e9 --- /dev/null +++ b/components/esp_rom/esp32c61/esp_rom_heap_tlsf_stub_table.h @@ -0,0 +1,53 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +#pragma once + +#include +#include "esp_rom_tlsf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief TLSF ROM vtable stub table for esp32c61. + * + * This struct mirrors the TLSF function-pointer vtable stored in ROM. + * Its layout MUST exactly match the ROM vtable for the target chip. + * It is used by tlsf_set_rom_patches() to intercept TLSF calls. + */ +struct heap_tlsf_stub_table_t { + tlsf_t (*tlsf_create)(void *mem, size_t max_bytes); + tlsf_t (*tlsf_create_with_pool)(void *mem, size_t pool_bytes, size_t max_bytes); + pool_t (*tlsf_get_pool)(tlsf_t tlsf); + pool_t (*tlsf_add_pool)(tlsf_t tlsf, void *mem, size_t bytes); + void (*tlsf_remove_pool)(tlsf_t tlsf, pool_t pool); + + void *(*tlsf_malloc)(tlsf_t tlsf, size_t size); + void *(*tlsf_memalign)(tlsf_t tlsf, size_t align, size_t size); + void *(*tlsf_memalign_offs)(tlsf_t tlsf, size_t align, size_t size, size_t offset); + void *(*tlsf_realloc)(tlsf_t tlsf, void *ptr, size_t size); + void (*tlsf_free)(tlsf_t tlsf, void *ptr); + + size_t (*tlsf_block_size)(void *ptr); + size_t (*tlsf_size)(tlsf_t tlsf); + size_t (*tlsf_align_size)(void); + size_t (*tlsf_block_size_min)(void); + size_t (*tlsf_block_size_max)(tlsf_t tlsf); + size_t (*tlsf_pool_overhead)(void); + size_t (*tlsf_alloc_overhead)(void); + + void (*tlsf_walk_pool)(pool_t pool, tlsf_walker walker, void *user); + + int (*tlsf_check)(tlsf_t tlsf); + int (*tlsf_check_pool)(pool_t pool); +}; + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_rom/esp32h2/esp_rom_heap_tlsf_stub_table.h b/components/esp_rom/esp32h2/esp_rom_heap_tlsf_stub_table.h new file mode 100644 index 00000000000..dfedf90477f --- /dev/null +++ b/components/esp_rom/esp32h2/esp_rom_heap_tlsf_stub_table.h @@ -0,0 +1,59 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +#pragma once + +#include +#include "esp_rom_tlsf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief TLSF ROM vtable stub table for esp32h2. + * + * This struct mirrors the TLSF function-pointer vtable stored in ROM. + * Its layout MUST exactly match the ROM vtable for the target chip. + * It is used by tlsf_set_rom_patches() to intercept TLSF calls. + */ +struct heap_tlsf_stub_table_t { + tlsf_t (*tlsf_create)(void *mem, size_t max_bytes); + tlsf_t (*tlsf_create_with_pool)(void *mem, size_t pool_bytes, size_t max_bytes); + pool_t (*tlsf_get_pool)(tlsf_t tlsf); + pool_t (*tlsf_add_pool)(tlsf_t tlsf, void *mem, size_t bytes); + void (*tlsf_remove_pool)(tlsf_t tlsf, pool_t pool); + + void *(*tlsf_malloc)(tlsf_t tlsf, size_t size); + void *(*tlsf_memalign)(tlsf_t tlsf, size_t align, size_t size); + void *(*tlsf_memalign_offs)(tlsf_t tlsf, size_t align, size_t size, size_t offset); + void *(*tlsf_realloc)(tlsf_t tlsf, void *ptr, size_t size); + void (*tlsf_free)(tlsf_t tlsf, void *ptr); + + size_t (*tlsf_block_size)(void *ptr); + size_t (*tlsf_size)(tlsf_t tlsf); + size_t (*tlsf_align_size)(void); + size_t (*tlsf_block_size_min)(void); + /* NOTE: The ROM signature is tlsf_block_size_max(control_t *control), but + * control_t is an internal TLSF type not exposed in any public header. + * Since tlsf_t is typedef'd as void* (same as control_t), the ABI is + * identical and we use tlsf_t here to keep this header self-contained. */ + size_t (*tlsf_block_size_max)(tlsf_t tlsf); + size_t (*tlsf_pool_overhead)(void); + size_t (*tlsf_alloc_overhead)(void); + + void (*tlsf_walk_pool)(pool_t pool, tlsf_walker walker, void *user); + + int (*tlsf_check)(tlsf_t tlsf); + int (*tlsf_check_pool)(pool_t pool); + + void *(*tlsf_malloc_addr)(tlsf_t tlsf, size_t size, void *address); +}; + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_rom/esp32h21/Kconfig.soc_caps.in b/components/esp_rom/esp32h21/Kconfig.soc_caps.in index 836a3e3874f..1cd1931209a 100644 --- a/components/esp_rom/esp32h21/Kconfig.soc_caps.in +++ b/components/esp_rom/esp32h21/Kconfig.soc_caps.in @@ -43,6 +43,10 @@ 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 diff --git a/components/esp_rom/esp32h21/esp_rom_caps.h b/components/esp_rom/esp32h21/esp_rom_caps.h index d08c8ecf726..386ab5ce4c1 100644 --- a/components/esp_rom/esp32h21/esp_rom_caps.h +++ b/components/esp_rom/esp32h21/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_SYSTIMER_INIT_PATCH (1) // ROM version initializes SYSTIMER without ETM #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 diff --git a/components/esp_rom/esp32h21/esp_rom_heap_tlsf_stub_table.h b/components/esp_rom/esp32h21/esp_rom_heap_tlsf_stub_table.h new file mode 100644 index 00000000000..c7771d64420 --- /dev/null +++ b/components/esp_rom/esp32h21/esp_rom_heap_tlsf_stub_table.h @@ -0,0 +1,59 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +#pragma once + +#include +#include "esp_rom_tlsf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief TLSF ROM vtable stub table for esp32h21. + * + * This struct mirrors the TLSF function-pointer vtable stored in ROM. + * Its layout MUST exactly match the ROM vtable for the target chip. + * It is used by tlsf_set_rom_patches() to intercept TLSF calls. + */ +struct heap_tlsf_stub_table_t { + tlsf_t (*tlsf_create)(void *mem, size_t max_bytes); + tlsf_t (*tlsf_create_with_pool)(void *mem, size_t pool_bytes, size_t max_bytes); + pool_t (*tlsf_get_pool)(tlsf_t tlsf); + pool_t (*tlsf_add_pool)(tlsf_t tlsf, void *mem, size_t bytes); + void (*tlsf_remove_pool)(tlsf_t tlsf, pool_t pool); + + void *(*tlsf_malloc)(tlsf_t tlsf, size_t size); + void *(*tlsf_memalign)(tlsf_t tlsf, size_t align, size_t size); + void *(*tlsf_memalign_offs)(tlsf_t tlsf, size_t align, size_t size, size_t offset); + void *(*tlsf_realloc)(tlsf_t tlsf, void *ptr, size_t size); + void (*tlsf_free)(tlsf_t tlsf, void *ptr); + + size_t (*tlsf_block_size)(void *ptr); + size_t (*tlsf_size)(tlsf_t tlsf); + size_t (*tlsf_align_size)(void); + size_t (*tlsf_block_size_min)(void); + /* NOTE: The ROM signature is tlsf_block_size_max(control_t *control), but + * control_t is an internal TLSF type not exposed in any public header. + * Since tlsf_t is typedef'd as void* (same as control_t), the ABI is + * identical and we use tlsf_t here to keep this header self-contained. */ + size_t (*tlsf_block_size_max)(tlsf_t tlsf); + size_t (*tlsf_pool_overhead)(void); + size_t (*tlsf_alloc_overhead)(void); + + void (*tlsf_walk_pool)(pool_t pool, tlsf_walker walker, void *user); + + int (*tlsf_check)(tlsf_t tlsf); + int (*tlsf_check_pool)(pool_t pool); + + void *(*tlsf_malloc_addr)(tlsf_t tlsf, size_t size, void *address); +}; + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_rom/esp32h4/Kconfig.soc_caps.in b/components/esp_rom/esp32h4/Kconfig.soc_caps.in index d40437c43ee..98589e1c651 100644 --- a/components/esp_rom/esp32h4/Kconfig.soc_caps.in +++ b/components/esp_rom/esp32h4/Kconfig.soc_caps.in @@ -43,6 +43,10 @@ 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 diff --git a/components/esp_rom/esp32h4/esp_rom_caps.h b/components/esp_rom/esp32h4/esp_rom_caps.h index 5276df390bf..eab18937769 100644 --- a/components/esp_rom/esp32h4/esp_rom_caps.h +++ b/components/esp_rom/esp32h4/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_SYSTIMER_INIT_PATCH (1) // ROM version initializes SYSTIMER without ETM #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_WITHOUT_REGI2C (1) // ROM has the regi2c bug or rom does not support regi2c function diff --git a/components/esp_rom/esp32h4/esp_rom_heap_tlsf_stub_table.h b/components/esp_rom/esp32h4/esp_rom_heap_tlsf_stub_table.h new file mode 100644 index 00000000000..2301e84144d --- /dev/null +++ b/components/esp_rom/esp32h4/esp_rom_heap_tlsf_stub_table.h @@ -0,0 +1,59 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +#pragma once + +#include +#include "esp_rom_tlsf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief TLSF ROM vtable stub table for esp32h4. + * + * This struct mirrors the TLSF function-pointer vtable stored in ROM. + * Its layout MUST exactly match the ROM vtable for the target chip. + * It is used by tlsf_set_rom_patches() to intercept TLSF calls. + */ +struct heap_tlsf_stub_table_t { + tlsf_t (*tlsf_create)(void *mem, size_t max_bytes); + tlsf_t (*tlsf_create_with_pool)(void *mem, size_t pool_bytes, size_t max_bytes); + pool_t (*tlsf_get_pool)(tlsf_t tlsf); + pool_t (*tlsf_add_pool)(tlsf_t tlsf, void *mem, size_t bytes); + void (*tlsf_remove_pool)(tlsf_t tlsf, pool_t pool); + + void *(*tlsf_malloc)(tlsf_t tlsf, size_t size); + void *(*tlsf_memalign)(tlsf_t tlsf, size_t align, size_t size); + void *(*tlsf_memalign_offs)(tlsf_t tlsf, size_t align, size_t size, size_t offset); + void *(*tlsf_realloc)(tlsf_t tlsf, void *ptr, size_t size); + void (*tlsf_free)(tlsf_t tlsf, void *ptr); + + size_t (*tlsf_block_size)(void *ptr); + size_t (*tlsf_size)(tlsf_t tlsf); + size_t (*tlsf_align_size)(void); + size_t (*tlsf_block_size_min)(void); + /* NOTE: The ROM signature is tlsf_block_size_max(control_t *control), but + * control_t is an internal TLSF type not exposed in any public header. + * Since tlsf_t is typedef'd as void* (same as control_t), the ABI is + * identical and we use tlsf_t here to keep this header self-contained. */ + size_t (*tlsf_block_size_max)(tlsf_t tlsf); + size_t (*tlsf_pool_overhead)(void); + size_t (*tlsf_alloc_overhead)(void); + + void (*tlsf_walk_pool)(pool_t pool, tlsf_walker walker, void *user); + + int (*tlsf_check)(tlsf_t tlsf); + int (*tlsf_check_pool)(pool_t pool); + + void *(*tlsf_malloc_addr)(tlsf_t tlsf, size_t size, void *address); +}; + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_rom/esp32s31/esp_rom_heap_tlsf_stub_table.h b/components/esp_rom/esp32s31/esp_rom_heap_tlsf_stub_table.h new file mode 100644 index 00000000000..3f72f32284e --- /dev/null +++ b/components/esp_rom/esp32s31/esp_rom_heap_tlsf_stub_table.h @@ -0,0 +1,59 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +#pragma once + +#include +#include "esp_rom_tlsf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief TLSF ROM vtable stub table for esp32s31. + * + * This struct mirrors the TLSF function-pointer vtable stored in ROM. + * Its layout MUST exactly match the ROM vtable for the target chip. + * It is used by tlsf_set_rom_patches() to intercept TLSF calls. + */ +struct heap_tlsf_stub_table_t { + tlsf_t (*tlsf_create)(void *mem, size_t max_bytes); + tlsf_t (*tlsf_create_with_pool)(void *mem, size_t pool_bytes, size_t max_bytes); + pool_t (*tlsf_get_pool)(tlsf_t tlsf); + pool_t (*tlsf_add_pool)(tlsf_t tlsf, void *mem, size_t bytes); + void (*tlsf_remove_pool)(tlsf_t tlsf, pool_t pool); + + void *(*tlsf_malloc)(tlsf_t tlsf, size_t size); + void *(*tlsf_memalign)(tlsf_t tlsf, size_t align, size_t size); + void *(*tlsf_memalign_offs)(tlsf_t tlsf, size_t align, size_t size, size_t offset); + void *(*tlsf_realloc)(tlsf_t tlsf, void *ptr, size_t size); + void (*tlsf_free)(tlsf_t tlsf, void *ptr); + + size_t (*tlsf_block_size)(void *ptr); + size_t (*tlsf_size)(tlsf_t tlsf); + size_t (*tlsf_align_size)(void); + size_t (*tlsf_block_size_min)(void); + /* NOTE: The ROM signature is tlsf_block_size_max(control_t *control), but + * control_t is an internal TLSF type not exposed in any public header. + * Since tlsf_t is typedef'd as void* (same as control_t), the ABI is + * identical and we use tlsf_t here to keep this header self-contained. */ + size_t (*tlsf_block_size_max)(tlsf_t tlsf); + size_t (*tlsf_pool_overhead)(void); + size_t (*tlsf_alloc_overhead)(void); + + void (*tlsf_walk_pool)(pool_t pool, tlsf_walker walker, void *user); + + int (*tlsf_check)(tlsf_t tlsf); + int (*tlsf_check_pool)(pool_t pool); + + void *(*tlsf_malloc_addr)(tlsf_t tlsf, size_t size, void *address); +}; + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_rom/include/esp_rom_tlsf.h b/components/esp_rom/include/esp_rom_tlsf.h index 89fdfb7533a..b9849b18a92 100644 --- a/components/esp_rom/include/esp_rom_tlsf.h +++ b/components/esp_rom/include/esp_rom_tlsf.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -75,6 +75,7 @@ typedef bool (*tlsf_walker)(void* ptr, size_t size, int used, void* user); /* Debugging. */ void tlsf_walk_pool(pool_t pool, tlsf_walker walker, void* user); +void tlsf_walk_pool_patched(pool_t pool, tlsf_walker walker, void* user); /* Returns nonzero if any internal consistency check fails. */ int tlsf_check(tlsf_t tlsf); int tlsf_check_pool(pool_t pool); @@ -100,14 +101,23 @@ typedef bool (*poison_check_pfunc_t)(void *start, size_t size, bool is_free, boo void tlsf_poison_fill_pfunc_set(poison_fill_pfunc_t pfunc); /*! - * @brief Set the function to call for checking memory region when - * poisoning is configured. + * @brief Set the function to call in tlsf_check for checking memory + * region when poisoning is configured. * * @param pfunc The callback function to trigger for checking * the content of a memory region. */ void tlsf_poison_check_pfunc_set(poison_check_pfunc_t pfunc); +/*! + * @brief Set the function to call in tlsf_walk_pool for checking memory + * region when poisoning is configured. + * + * @param pfunc The callback function to trigger for checking + * the content of a memory region. + */ +void tlsf_walk_pool_pfunc_set(poison_check_pfunc_t pfunc); + #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 index 34d5109696c..ae7aee1988a 100644 --- a/components/esp_rom/patches/esp_rom_multi_heap.c +++ b/components/esp_rom/patches/esp_rom_multi_heap.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -16,19 +16,13 @@ #include "sdkconfig.h" #include "esp_rom_multi_heap.h" +#include "esp_rom_tlsf.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; @@ -37,7 +31,7 @@ typedef struct multi_heap_info { void* heap_data; } heap_t; -extern void tlsf_walk_pool(pool_t pool, tlsf_walker walker, void* user); +/* Declare helper functions that are not in public headers */ 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); @@ -47,7 +41,7 @@ void multi_heap_walk(multi_heap_handle_t heap, multi_heap_walker_cb_t walker_fun assert(heap != NULL); multi_heap_internal_lock(heap); - tlsf_walk_pool(tlsf_get_pool(heap->heap_data), walker_func, user_data); + tlsf_walk_pool_patched(tlsf_get_pool(heap->heap_data), walker_func, user_data); multi_heap_internal_unlock(heap); } @@ -129,10 +123,11 @@ void* tlsf_find_containing_block(pool_t pool, void *ptr) while (block && !block_is_last(block)) { if (!block_is_free(block)) { - void *block_end = block_to_ptr(block) + block_size(block); - if (block_to_ptr(block) <= ptr && block_end > ptr) { + void *block_start = block_to_ptr(block); + void *block_end = block_start + block_size(block); + if (block_start <= ptr && block_end > ptr) { // we found the containing block, return - return block_to_ptr(block); + return block_start; } } diff --git a/components/esp_rom/patches/esp_rom_tlsf.c b/components/esp_rom/patches/esp_rom_tlsf.c index 09487be75b9..522f0fa4078 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-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -20,15 +20,51 @@ #include "esp_rom_caps.h" #include "esp_rom_tlsf.h" +#include "esp_rom_sys.h" #include "tlsf_block_functions.h" #include "tlsf_control_functions.h" -static poison_check_pfunc_t s_poison_check_region = NULL; +/* + * IMPORTANT! + * + * Several versions of the TLSF in ROM exist and can be divided in + * 3 categories: + * 1- Versions where tlsf_check and tlsf_walk_pool do not call + * the poison check hook (oldest versions) + * 2- Versions where tlsf_check does call the poison check hook, + * but tlsf_walk_pool still doesn't (intermediate versions) + * 3- Versions where both tlsf_check and tlsf_walk_pool call the + * the poison check hook (latest versions) + * + * Category 3 does not require the patch defined in this file. + * + * Category 2 requires the patch but only for tlsf_walk_pool. In those + * versions, a definition of tlsf_poison_check_pfunc_set is present in + * the TLSF ROM which makes the definition in this file unnecessary since + * the linker will keep the ROM definition. For this reason, tlsf_walk_pool_pfunc_set + * is defined and called in multi_heap_poisoning.c to make sure that the poison check + * hook also gets set in this file. The definition of tlsf_poison_check_pfunc_set could + * be removed from this file if not for the needs of category 1. + * + * Category 1 versions of the ROM TLSF do not provide a definition of + * tlsf_poison_check_pfunc_set, making the definition below necessary for + * any application to compile and link properly. The added patch of tlsf_walk_pool + * makes the patch of tlsf_check unnecessary in this file since tlsf_check was only + * calling the poison check hook for free blocks while tlsf_walk_pool calls the poison + * check hook for all blocks (free and used). For this reason, tlsf_poison_check_pfunc_set + * is left empty. + * */ + +static poison_check_pfunc_t s_walk_pool_check_region = NULL; void tlsf_poison_check_pfunc_set(poison_check_pfunc_t pfunc) { - s_poison_check_region = pfunc; +} + +void tlsf_walk_pool_pfunc_set(poison_check_pfunc_t pfunc) +{ + s_walk_pool_check_region = pfunc; } #define tlsf_insist_no_assert(x) { if (!(x)) { status--; } } @@ -51,7 +87,7 @@ static bool integrity_walker(void* ptr, size_t size, int used, void* user) tlsf_insist_no_assert(integ->prev_status == this_prev_status && "prev status incorrect"); tlsf_insist_no_assert(size == this_block_size && "block size incorrect"); - if (s_poison_check_region != NULL) + if (s_walk_pool_check_region != NULL) { /* block_size(block) returns the size of the usable memory when the block is allocated. * As the block under test is free, we need to subtract to the block size the next_free @@ -66,7 +102,7 @@ static bool integrity_walker(void* ptr, size_t size, int used, void* user) void* ptr_block = used ? (void*)block + block_start_offset : (void*)block + sizeof(block_header_t); - tlsf_insist_no_assert(s_poison_check_region(ptr_block, actual_free_block_size, !used, true)); + tlsf_insist_no_assert(s_walk_pool_check_region(ptr_block, actual_free_block_size, !used, true)); } integ->prev_status = this_status; @@ -82,7 +118,7 @@ static bool default_walker(void* ptr, size_t size, int used, void* user) return true; } -void tlsf_walk_pool(pool_t pool, tlsf_walker walker, void* user) +static void tlsf_walk_pool_impl(pool_t pool, tlsf_walker walker, void* user) { tlsf_walker pool_walker = walker ? walker : default_walker; block_header_t* block = @@ -103,11 +139,11 @@ void tlsf_walk_pool(pool_t pool, tlsf_walker walker, void* user) } } -int tlsf_check_pool(pool_t pool) +static int tlsf_check_pool_impl(pool_t pool) { /* Check that the blocks are physically correct. */ integrity_t integ = { 0, 0 }; - tlsf_walk_pool(pool, integrity_walker, &integ); + tlsf_walk_pool_impl(pool, integrity_walker, &integ); return integ.status; } @@ -116,38 +152,13 @@ int tlsf_check_pool(pool_t pool) /* Set up the TLSF ROM patches here */ -/*! - * @brief Structure to store all the functions of a TLSF implementation. - * The goal of this table is to change any of the address here in order - * to let the ROM code call another function implementation than the one - * in ROM. +/* + * struct heap_tlsf_stub_table_t is defined in the per-target header. + * Its layout exactly mirrors the TLSF vtable in ROM for the current target. + * The header is produced by gen_esp_rom_heap_tlsf_stub_table.py from the + * target's .rom.heap.ld file; run that script whenever the .ld file changes. */ -struct heap_tlsf_stub_table_t { - tlsf_t (*tlsf_create)(void* mem); - tlsf_t (*tlsf_create_with_pool)(void* mem, size_t bytes); - pool_t (*tlsf_get_pool)(tlsf_t tlsf); - pool_t (*tlsf_add_pool)(tlsf_t tlsf, void* mem, size_t bytes); - void (*tlsf_remove_pool)(tlsf_t tlsf, pool_t pool); - - void* (*tlsf_malloc)(tlsf_t tlsf, size_t size); - void* (*tlsf_memalign)(tlsf_t tlsf, size_t align, size_t size); - void* (*tlsf_memalign_offs)(tlsf_t tlsf, size_t align, size_t size, size_t offset); - void* (*tlsf_realloc)(tlsf_t tlsf, void* ptr, size_t size); - void (*tlsf_free)(tlsf_t tlsf, void* ptr); - - size_t (*tlsf_block_size)(void* ptr); - size_t (*tlsf_size)(void); - size_t (*tlsf_align_size)(void); - size_t (*tlsf_block_size_min)(void); - size_t (*tlsf_block_size_max)(void); - size_t (*tlsf_pool_overhead)(void); - size_t (*tlsf_alloc_overhead)(void); - - void (*tlsf_walk_pool)(pool_t pool, tlsf_walker walker, void* user); - - int (*tlsf_check)(tlsf_t tlsf); - int (*tlsf_check_pool)(pool_t pool); -}; +#include "esp_rom_heap_tlsf_stub_table.h" /* We need the original table from the ROM */ extern struct heap_tlsf_stub_table_t* heap_tlsf_table_ptr; @@ -169,8 +180,20 @@ void __attribute__((constructor)) tlsf_set_rom_patches(void) memcpy(&heap_tlsf_patch_table_ptr, heap_tlsf_table_ptr, sizeof(struct heap_tlsf_stub_table_t)); /* Set the patched function here */ - heap_tlsf_patch_table_ptr.tlsf_check_pool = tlsf_check_pool; + heap_tlsf_patch_table_ptr.tlsf_check_pool = tlsf_check_pool_impl; + heap_tlsf_patch_table_ptr.tlsf_walk_pool = tlsf_walk_pool_impl; /* Set our table as the one to use in the ROM code */ heap_tlsf_table_ptr = &heap_tlsf_patch_table_ptr; } + +/* + * @brief Helper function to call the patched tlsf_walk_pool. + * This is needed because the ROM's tlsf_walk_pool symbol cannot be overridden + * on chips where it's a strong symbol (not using PROVIDE in linker script). + * Other patch files can call this to access the patched version. + */ +void tlsf_walk_pool_patched(pool_t pool, tlsf_walker walker, void* user) +{ + heap_tlsf_table_ptr->tlsf_walk_pool(pool, walker, user); +} diff --git a/components/heap/multi_heap_poisoning.c b/components/heap/multi_heap_poisoning.c index 462a9e980d9..402df83db42 100644 --- a/components/heap/multi_heap_poisoning.c +++ b/components/heap/multi_heap_poisoning.c @@ -371,6 +371,7 @@ multi_heap_handle_t multi_heap_register(void *start, size_t size) #if CONFIG_HEAP_TLSF_USE_ROM_IMPL tlsf_poison_fill_pfunc_set(multi_heap_internal_poison_fill_region); tlsf_poison_check_pfunc_set(multi_heap_internal_check_block_poisoning); + tlsf_walk_pool_pfunc_set(multi_heap_internal_check_block_poisoning); #endif // CONFIG_HEAP_TLSF_USE_ROM_IMPL return multi_heap_register_impl(start, size); } diff --git a/components/heap/test_apps/heap_tests/main/test_malloc.c b/components/heap/test_apps/heap_tests/main/test_malloc.c index cbf7793634d..fd0b11d62d0 100644 --- a/components/heap/test_apps/heap_tests/main/test_malloc.c +++ b/components/heap/test_apps/heap_tests/main/test_malloc.c @@ -205,7 +205,7 @@ TEST_CASE("test get allocated size", "[heap]") void *ptr_array[iterations]; for (size_t i = 0; i < iterations; i++) { - ptr_array[i] = heap_caps_malloc(alloc_sizes[i], MALLOC_CAP_DEFAULT); + ptr_array[i] = heap_caps_malloc(alloc_sizes[i], MALLOC_CAP_INTERNAL); TEST_ASSERT_NOT_NULL(ptr_array[i]); // test that the heap_caps_get_allocated_size() returns the right number of bytes (aligned to 4 bytes @@ -218,7 +218,7 @@ TEST_CASE("test get allocated size", "[heap]") // when the pointer to the first, last (calculated from the requested size) and to a byte // in the middle of the chunk is passed as parameter TEST_ASSERT(aligned_size <= heap_caps_get_containing_block_size(ptr_array[i])); - TEST_ASSERT(aligned_size <= heap_caps_get_containing_block_size(ptr_array[i] + alloc_sizes[i])); + TEST_ASSERT(aligned_size <= heap_caps_get_containing_block_size(ptr_array[i] + alloc_sizes[i] - 1)); TEST_ASSERT(aligned_size <= heap_caps_get_containing_block_size(ptr_array[i] + (alloc_sizes[i] / 2))); heap_caps_free(ptr_array[i]); diff --git a/components/heap/test_apps/heap_tests/main/test_malloc_caps.c b/components/heap/test_apps/heap_tests/main/test_malloc_caps.c index 08d7cb55cef..f9b0601330c 100644 --- a/components/heap/test_apps/heap_tests/main/test_malloc_caps.c +++ b/components/heap/test_apps/heap_tests/main/test_malloc_caps.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -57,7 +57,7 @@ TEST_CASE("Capabilities allocator test", "[heap]") m1 = heap_caps_malloc(alloc32, MALLOC_CAP_32BIT); printf("--> %p\n", m1); //Check that we got IRAM back - TEST_ASSERT((((int)m1)&0xFF000000)==0x40000000); + TEST_ASSERT(esp_ptr_in_iram(m1)); free8 = heap_caps_get_free_size(MALLOC_CAP_8BIT); free32 = heap_caps_get_free_size(MALLOC_CAP_32BIT); printf("Free 8bit-capable memory (after 32-bit): %dK, 32-bit capable memory %dK\n", free8, free32); @@ -81,8 +81,8 @@ TEST_CASE("Capabilities allocator test", "[heap]") m2[x]= heap_caps_malloc(alloc32, MALLOC_CAP_32BIT); printf("--> %p\n", m2[x]); } - TEST_ASSERT((((int)m2[0])&0xFF000000)==0x40000000); - TEST_ASSERT((((int)m2[9])&0xFF000000)==0x3F000000); + TEST_ASSERT(esp_ptr_in_iram(m2[0])); + TEST_ASSERT(esp_ptr_in_dram(m2[9])); } else { printf("This platform has no IRAM-only so changeover will never occur, jumping to next test\n"); @@ -94,7 +94,7 @@ TEST_CASE("Capabilities allocator test", "[heap]") free_iram = heap_caps_get_free_size(MALLOC_CAP_EXEC); m1= heap_caps_malloc(MIN(free_iram / 2, 10*1024), MALLOC_CAP_EXEC); printf("--> %p\n", m1); - TEST_ASSERT((((int)m1)&0xFF000000)==0x40000000); + TEST_ASSERT(esp_ptr_in_iram(m1) || esp_ptr_in_diram_iram(m1)); for (x=0; x<10; x++) free(m2[x]); } else { @@ -102,7 +102,7 @@ TEST_CASE("Capabilities allocator test", "[heap]") free_iram = heap_caps_get_free_size(MALLOC_CAP_EXEC); m1= heap_caps_malloc(MIN(free_iram / 2, 10*1024), MALLOC_CAP_EXEC); printf("--> %p\n", m1); - TEST_ASSERT((((int)m1)&0xFF000000)==0x40000000); + TEST_ASSERT(esp_ptr_in_iram(m1) || esp_ptr_in_diram_iram(m1)); } free(m1); @@ -329,8 +329,16 @@ TEST_CASE("RTC memory should be lowest priority and its free size should be big TEST_ASSERT_NOT_NULL(ptr); TEST_ASSERT(!esp_ptr_in_rtc_dram_fast(ptr)); - free_size = heap_caps_get_free_size(MALLOC_CAP_RTCRAM); - TEST_ASSERT_GREATER_OR_EQUAL(1024 * 4, free_size); + const size_t min_rtc_size_since_init = heap_caps_get_minimum_free_size(MALLOC_CAP_RTCRAM); + const size_t rtc_free_size = heap_caps_get_free_size(MALLOC_CAP_RTCRAM); + TEST_ASSERT_EQUAL(min_rtc_size_since_init, rtc_free_size); + /* The ROM TLSF implementation uses a statically-sized control_t (sized for the + * maximum DRAM pool) which has much higher overhead on small pools like RTCRAM. + * The 80% threshold only applies when the IDF TLSF is used. */ + #if !CONFIG_HEAP_TLSF_USE_ROM_IMPL + const size_t max_rtc_size = SOC_RTC_DATA_HIGH - SOC_RTC_DATA_LOW; + TEST_ASSERT_GREATER_OR_EQUAL((max_rtc_size * 8) / 10, rtc_free_size); + #endif free(ptr); } diff --git a/components/heap/test_apps/heap_tests/pytest_heap.py b/components/heap/test_apps/heap_tests/pytest_heap.py index 37093d0eac5..879755c64f6 100644 --- a/components/heap/test_apps/heap_tests/pytest_heap.py +++ b/components/heap/test_apps/heap_tests/pytest_heap.py @@ -35,7 +35,7 @@ def test_heap_poisoning_qemu(dut: Dut) -> None: @pytest.mark.generic -@pytest.mark.parametrize('config', ['in_flash']) +@pytest.mark.parametrize('config', ['in_flash', 'in_rom']) @idf_parametrize('target', ['supported_targets'], indirect=['target']) def test_heap_in_flash(dut: Dut) -> None: dut.run_all_single_board_cases() diff --git a/components/heap/test_apps/heap_tests/sdkconfig.ci.in_rom b/components/heap/test_apps/heap_tests/sdkconfig.ci.in_rom new file mode 100644 index 00000000000..48332fdc492 --- /dev/null +++ b/components/heap/test_apps/heap_tests/sdkconfig.ci.in_rom @@ -0,0 +1,2 @@ +CONFIG_HEAP_TLSF_USE_ROM_IMPL=y +CONFIG_HEAP_PLACE_FUNCTION_INTO_FLASH=n