test_malloc_caps.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. /*
  2. * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Unlicense OR CC0-1.0
  5. */
  6. /*
  7. Tests for the capabilities-based memory allocator.
  8. */
  9. #include <esp_types.h>
  10. #include <stdio.h>
  11. #include "unity.h"
  12. #include "esp_attr.h"
  13. #include "esp_heap_caps.h"
  14. #include "spi_flash_mmap.h"
  15. #include "esp_memory_utils.h"
  16. #include "esp_private/spi_flash_os.h"
  17. #include <stdlib.h>
  18. #include <sys/param.h>
  19. #ifndef CONFIG_ESP_SYSTEM_MEMPROT_FEATURE
  20. TEST_CASE("Capabilities allocator test", "[heap]")
  21. {
  22. char *m1, *m2[10];
  23. int x;
  24. size_t free8start, free32start, free8, free32;
  25. /* It's important we printf() something before we take the empty heap sizes,
  26. as the first printf() in a task allocates heap resources... */
  27. printf("Testing capabilities allocator...\n");
  28. free8start = heap_caps_get_free_size(MALLOC_CAP_8BIT);
  29. free32start = heap_caps_get_free_size(MALLOC_CAP_32BIT);
  30. printf("Free 8bit-capable memory (start): %dK, 32-bit capable memory %dK\n", free8start, free32start);
  31. TEST_ASSERT(free32start >= free8start);
  32. printf("Allocating 10K of 8-bit capable RAM\n");
  33. m1= heap_caps_malloc(10*1024, MALLOC_CAP_8BIT);
  34. printf("--> %p\n", m1);
  35. free8 = heap_caps_get_free_size(MALLOC_CAP_8BIT);
  36. free32 = heap_caps_get_free_size(MALLOC_CAP_32BIT);
  37. printf("Free 8bit-capable memory (both reduced): %dK, 32-bit capable memory %dK\n", free8, free32);
  38. //Both should have gone down by 10K; 8bit capable ram is also 32-bit capable
  39. TEST_ASSERT(free8<=(free8start-10*1024));
  40. TEST_ASSERT(free32<=(free32start-10*1024));
  41. //Assume we got DRAM back
  42. TEST_ASSERT(esp_ptr_in_dram(m1));
  43. free(m1);
  44. //The goal here is to allocate from IRAM. Since there is no external IRAM (yet)
  45. //the following gives size of IRAM-only (not D/IRAM) memory.
  46. size_t free_iram = heap_caps_get_free_size(MALLOC_CAP_INTERNAL) -
  47. heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
  48. size_t alloc32 = MIN(free_iram / 2, 10*1024) & (~3);
  49. if(free_iram) {
  50. printf("Freeing; allocating %u bytes of 32K-capable RAM\n", alloc32);
  51. m1 = heap_caps_malloc(alloc32, MALLOC_CAP_32BIT);
  52. printf("--> %p\n", m1);
  53. //Check that we got IRAM back
  54. TEST_ASSERT((((int)m1)&0xFF000000)==0x40000000);
  55. free8 = heap_caps_get_free_size(MALLOC_CAP_8BIT);
  56. free32 = heap_caps_get_free_size(MALLOC_CAP_32BIT);
  57. printf("Free 8bit-capable memory (after 32-bit): %dK, 32-bit capable memory %dK\n", free8, free32);
  58. //Only 32-bit should have gone down by alloc32: 32-bit isn't necessarily 8bit capable
  59. TEST_ASSERT(free32<=(free32start-alloc32));
  60. TEST_ASSERT(free8==free8start);
  61. free(m1);
  62. } else {
  63. printf("This platform has no 32-bit only capable RAM, jumping to next test \n");
  64. }
  65. printf("Allocating impossible caps\n");
  66. m1= heap_caps_malloc(10*1024, MALLOC_CAP_8BIT|MALLOC_CAP_EXEC);
  67. printf("--> %p\n", m1);
  68. TEST_ASSERT(m1==NULL);
  69. if(free_iram) {
  70. printf("Testing changeover iram -> dram");
  71. // priorities will exhaust IRAM first, then start allocating from DRAM
  72. for (x=0; x<10; x++) {
  73. m2[x]= heap_caps_malloc(alloc32, MALLOC_CAP_32BIT);
  74. printf("--> %p\n", m2[x]);
  75. }
  76. TEST_ASSERT((((int)m2[0])&0xFF000000)==0x40000000);
  77. TEST_ASSERT((((int)m2[9])&0xFF000000)==0x3F000000);
  78. } else {
  79. printf("This platform has no IRAM-only so changeover will never occur, jumping to next test\n");
  80. }
  81. printf("Test if allocating executable code still gives IRAM, even with dedicated IRAM region depleted\n");
  82. if(free_iram) {
  83. // (the allocation should come from D/IRAM)
  84. free_iram = heap_caps_get_free_size(MALLOC_CAP_EXEC);
  85. m1= heap_caps_malloc(MIN(free_iram / 2, 10*1024), MALLOC_CAP_EXEC);
  86. printf("--> %p\n", m1);
  87. TEST_ASSERT((((int)m1)&0xFF000000)==0x40000000);
  88. for (x=0; x<10; x++) free(m2[x]);
  89. } else {
  90. // (the allocation should come from D/IRAM)
  91. free_iram = heap_caps_get_free_size(MALLOC_CAP_EXEC);
  92. m1= heap_caps_malloc(MIN(free_iram / 2, 10*1024), MALLOC_CAP_EXEC);
  93. printf("--> %p\n", m1);
  94. TEST_ASSERT((((int)m1)&0xFF000000)==0x40000000);
  95. }
  96. free(m1);
  97. printf("Done.\n");
  98. }
  99. #endif // CONFIG_ESP_SYSTEM_MEMPROT_FEATURE
  100. #ifdef CONFIG_ESP32_IRAM_AS_8BIT_ACCESSIBLE_MEMORY
  101. TEST_CASE("IRAM_8BIT capability test", "[heap]")
  102. {
  103. uint8_t *ptr;
  104. size_t free_size, free_size32, largest_free_size;
  105. /* need to print something as first printf allocates some heap */
  106. printf("IRAM_8BIT capability test\n");
  107. free_size = heap_caps_get_free_size(MALLOC_CAP_IRAM_8BIT);
  108. free_size32 = heap_caps_get_free_size(MALLOC_CAP_32BIT);
  109. largest_free_size = heap_caps_get_largest_free_block(MALLOC_CAP_IRAM_8BIT);
  110. ptr = heap_caps_malloc(largest_free_size, MALLOC_CAP_IRAM_8BIT);
  111. TEST_ASSERT((((int)ptr)&0xFF000000)==0x40000000);
  112. /* As the heap allocator may present an overhead for allocated blocks,
  113. * we need to check that the free heap size is now smaller or equal to the former free size. */
  114. TEST_ASSERT(heap_caps_get_free_size(MALLOC_CAP_IRAM_8BIT) <= (free_size - heap_caps_get_allocated_size(ptr)));
  115. TEST_ASSERT(heap_caps_get_free_size(MALLOC_CAP_32BIT) <= (free_size32 - heap_caps_get_allocated_size(ptr)));
  116. free(ptr);
  117. }
  118. #endif
  119. TEST_CASE("heap_caps metadata test", "[heap]")
  120. {
  121. /* need to print something as first printf allocates some heap */
  122. printf("heap_caps metadata test\n");
  123. heap_caps_print_heap_info(MALLOC_CAP_8BIT);
  124. multi_heap_info_t original;
  125. heap_caps_get_info(&original, MALLOC_CAP_8BIT);
  126. void *b = heap_caps_malloc(original.largest_free_block, MALLOC_CAP_8BIT);
  127. TEST_ASSERT_NOT_NULL(b);
  128. printf("After allocating %d bytes:\n", original.largest_free_block);
  129. heap_caps_print_heap_info(MALLOC_CAP_8BIT);
  130. multi_heap_info_t after;
  131. heap_caps_get_info(&after, MALLOC_CAP_8BIT);
  132. TEST_ASSERT(after.largest_free_block <= original.largest_free_block);
  133. TEST_ASSERT(after.total_free_bytes <= original.total_free_bytes);
  134. free(b);
  135. heap_caps_get_info(&after, MALLOC_CAP_8BIT);
  136. printf("\n\n After test, heap status:\n");
  137. heap_caps_print_heap_info(MALLOC_CAP_8BIT);
  138. /* Allow some leeway here, because LWIP sometimes allocates up to 144 bytes in the background
  139. as part of timer management.
  140. */
  141. TEST_ASSERT_INT32_WITHIN(200, after.total_free_bytes, original.total_free_bytes);
  142. TEST_ASSERT_INT32_WITHIN(200, after.largest_free_block, original.largest_free_block);
  143. TEST_ASSERT(after.minimum_free_bytes < original.total_free_bytes);
  144. }
  145. /* Small function runs from IRAM to check that malloc/free/realloc
  146. all work OK when cache is disabled...
  147. */
  148. #if !CONFIG_ESP_SYSTEM_MEMPROT_FEATURE && !CONFIG_HEAP_PLACE_FUNCTION_INTO_FLASH
  149. static IRAM_ATTR __attribute__((noinline)) bool iram_malloc_test(void)
  150. {
  151. spi_flash_guard_get()->start(); // Disables flash cache
  152. bool result = true;
  153. void *x = heap_caps_malloc(64, MALLOC_CAP_EXEC);
  154. result = result && (x != NULL);
  155. void *y = heap_caps_realloc(x, 32, MALLOC_CAP_EXEC);
  156. result = result && (y != NULL);
  157. heap_caps_free(y);
  158. spi_flash_guard_get()->end(); // Re-enables flash cache
  159. return result;
  160. }
  161. TEST_CASE("heap_caps_xxx functions work with flash cache disabled", "[heap]")
  162. {
  163. TEST_ASSERT( iram_malloc_test() );
  164. }
  165. #endif // CONFIG_ESP_SYSTEM_MEMPROT_FEATURE
  166. #ifdef CONFIG_HEAP_ABORT_WHEN_ALLOCATION_FAILS
  167. TEST_CASE("When enabled, allocation operation failure generates an abort", "[heap][reset=abort,SW_CPU_RESET]")
  168. {
  169. const size_t stupid_allocation_size = (128 * 1024 * 1024);
  170. void *ptr = heap_caps_malloc(stupid_allocation_size, MALLOC_CAP_DEFAULT);
  171. (void)ptr;
  172. TEST_FAIL_MESSAGE("should not be reached");
  173. }
  174. #endif
  175. static bool called_user_failed_hook = false;
  176. void heap_caps_alloc_failed_hook(size_t requested_size, uint32_t caps, const char *function_name)
  177. {
  178. printf("%s was called but failed to allocate %d bytes with 0x%lX capabilities. \n",function_name, requested_size, caps);
  179. called_user_failed_hook = true;
  180. }
  181. TEST_CASE("user provided alloc failed hook must be called when allocation fails", "[heap]")
  182. {
  183. TEST_ASSERT(heap_caps_register_failed_alloc_callback(heap_caps_alloc_failed_hook) == ESP_OK);
  184. const size_t stupid_allocation_size = (128 * 1024 * 1024);
  185. void *ptr = heap_caps_malloc(stupid_allocation_size, MALLOC_CAP_DEFAULT);
  186. TEST_ASSERT(called_user_failed_hook != false);
  187. called_user_failed_hook = false;
  188. ptr = heap_caps_realloc(ptr, stupid_allocation_size, MALLOC_CAP_DEFAULT);
  189. TEST_ASSERT(called_user_failed_hook != false);
  190. called_user_failed_hook = false;
  191. ptr = heap_caps_aligned_alloc(0x200, stupid_allocation_size, MALLOC_CAP_DEFAULT);
  192. TEST_ASSERT(called_user_failed_hook != false);
  193. (void)ptr;
  194. }
  195. TEST_CASE("allocation with invalid capability should also trigger the alloc failed hook", "[heap]")
  196. {
  197. const size_t allocation_size = 64;
  198. const uint32_t invalid_cap = MALLOC_CAP_INVALID;
  199. TEST_ASSERT(heap_caps_register_failed_alloc_callback(heap_caps_alloc_failed_hook) == ESP_OK);
  200. called_user_failed_hook = false;
  201. void *ptr = heap_caps_malloc(allocation_size, invalid_cap);
  202. TEST_ASSERT(called_user_failed_hook != false);
  203. called_user_failed_hook = false;
  204. ptr = heap_caps_realloc(ptr, allocation_size, invalid_cap);
  205. TEST_ASSERT(called_user_failed_hook != false);
  206. called_user_failed_hook = false;
  207. ptr = heap_caps_aligned_alloc(0x200, allocation_size, invalid_cap);
  208. TEST_ASSERT(called_user_failed_hook != false);
  209. (void)ptr;
  210. }
  211. #ifdef CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP
  212. /**
  213. * In MR 16031, the priority of RTC memory has been adjusted to the lowest.
  214. * RTC memory will not be consumed a lot during the startup process.
  215. */
  216. TEST_CASE("RTC memory shoule be lowest priority and its free size should be big enough", "[heap]")
  217. {
  218. const size_t allocation_size = 1024 * 4;
  219. void *ptr = NULL;
  220. size_t free_size = 0;
  221. ptr = heap_caps_malloc(allocation_size, MALLOC_CAP_DEFAULT);
  222. TEST_ASSERT_NOT_NULL(ptr);
  223. TEST_ASSERT(!esp_ptr_in_rtc_dram_fast(ptr));
  224. free_size = heap_caps_get_free_size(MALLOC_CAP_RTCRAM);
  225. TEST_ASSERT_GREATER_OR_EQUAL(1024 * 4, free_size);
  226. free(ptr);
  227. }
  228. #endif
  229. TEST_CASE("test memory protection features", "[heap][mem_prot]")
  230. {
  231. // try to allocate memory in IRAM and check that if memory protection is active,
  232. // no memory is being allocated
  233. uint32_t *iram_ptr = heap_caps_malloc(4, MALLOC_CAP_EXEC);
  234. #ifndef CONFIG_ESP_SYSTEM_MEMPROT_FEATURE
  235. // System memory protection not active, check that iram_ptr is not null
  236. // Check that iram_ptr is in IRAM
  237. TEST_ASSERT_NOT_NULL(iram_ptr);
  238. TEST_ASSERT(true == esp_ptr_in_iram(iram_ptr));
  239. // free the memory
  240. heap_caps_free(iram_ptr);
  241. #else
  242. // System memory protection is active, DIRAM seen as DRAM, iram_ptr should be null
  243. TEST_ASSERT_NULL(iram_ptr);
  244. #endif // CONFIG_ESP_SYSTEM_MEMPROT_FEATURE
  245. }