heap_caps_init.c 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. /*
  2. * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. #include "heap_private.h"
  7. #include <assert.h>
  8. #include <string.h>
  9. #include <sys/lock.h>
  10. #include "esp_log.h"
  11. #include "multi_heap.h"
  12. #include "multi_heap_platform.h"
  13. #include "esp_heap_caps_init.h"
  14. #include "heap_memory_layout.h"
  15. static const char *TAG = "heap_init";
  16. /* Linked-list of registered heaps */
  17. struct registered_heap_ll registered_heaps;
  18. static void register_heap(heap_t *region)
  19. {
  20. size_t heap_size = region->end - region->start;
  21. assert(heap_size <= HEAP_SIZE_MAX);
  22. region->heap = multi_heap_register((void *)region->start, heap_size);
  23. if (region->heap != NULL) {
  24. ESP_EARLY_LOGD(TAG, "New heap initialised at %p", region->heap);
  25. }
  26. }
  27. void heap_caps_enable_nonos_stack_heaps(void)
  28. {
  29. heap_t *heap;
  30. SLIST_FOREACH(heap, &registered_heaps, next) {
  31. // Assume any not-yet-registered heap is
  32. // a nonos-stack heap
  33. if (heap->heap == NULL) {
  34. register_heap(heap);
  35. if (heap->heap != NULL) {
  36. multi_heap_set_lock(heap->heap, &heap->heap_mux);
  37. }
  38. }
  39. }
  40. }
  41. /* Initialize the heap allocator to use all of the memory not
  42. used by static data or reserved for other purposes
  43. */
  44. void heap_caps_init(void)
  45. {
  46. #ifdef CONFIG_HEAP_TLSF_USE_ROM_IMPL
  47. extern void multi_heap_in_rom_init(void);
  48. multi_heap_in_rom_init();
  49. #endif
  50. /* Get the array of regions that we can use for heaps
  51. (with reserved memory removed already.)
  52. */
  53. size_t num_regions = soc_get_available_memory_region_max_count();
  54. soc_memory_region_t regions[num_regions];
  55. num_regions = soc_get_available_memory_regions(regions);
  56. //The heap allocator will treat every region given to it as separate. In order to get bigger ranges of contiguous memory,
  57. //it's useful to coalesce adjacent regions that have the same type.
  58. for (size_t i = 1; i < num_regions; i++) {
  59. soc_memory_region_t *a = &regions[i - 1];
  60. soc_memory_region_t *b = &regions[i];
  61. if (b->start == (intptr_t)(a->start + a->size) && b->type == a->type ) {
  62. a->type = -1;
  63. b->start = a->start;
  64. b->size += a->size;
  65. }
  66. }
  67. /* Count the heaps left after merging */
  68. size_t num_heaps = 0;
  69. for (size_t i = 0; i < num_regions; i++) {
  70. if (regions[i].type != -1) {
  71. num_heaps++;
  72. }
  73. }
  74. /* Start by allocating the registered heap data on the stack.
  75. Once we have a heap to copy it to, we will copy it to a heap buffer.
  76. */
  77. heap_t temp_heaps[num_heaps];
  78. size_t heap_idx = 0;
  79. ESP_EARLY_LOGI(TAG, "Initializing. RAM available for dynamic allocation:");
  80. for (size_t i = 0; i < num_regions; i++) {
  81. soc_memory_region_t *region = &regions[i];
  82. const soc_memory_type_desc_t *type = &soc_memory_types[region->type];
  83. heap_t *heap = &temp_heaps[heap_idx];
  84. if (region->type == -1) {
  85. continue;
  86. }
  87. heap_idx++;
  88. assert(heap_idx <= num_heaps);
  89. memcpy(heap->caps, type->caps, sizeof(heap->caps));
  90. heap->start = region->start;
  91. heap->end = region->start + region->size;
  92. MULTI_HEAP_LOCK_INIT(&heap->heap_mux);
  93. if (type->startup_stack) {
  94. /* Will be registered when OS scheduler starts */
  95. heap->heap = NULL;
  96. } else {
  97. register_heap(heap);
  98. }
  99. SLIST_NEXT(heap, next) = NULL;
  100. ESP_EARLY_LOGI(TAG, "At %08X len %08X (%d KiB): %s",
  101. region->start, region->size, region->size / 1024, type->name);
  102. }
  103. assert(heap_idx == num_heaps);
  104. /* Allocate the permanent heap data that we'll use as a linked list at runtime.
  105. Allocate this part of data contiguously, even though it's a linked list... */
  106. assert(SLIST_EMPTY(&registered_heaps));
  107. heap_t *heaps_array = NULL;
  108. for (size_t i = 0; i < num_heaps; i++) {
  109. if (heap_caps_match(&temp_heaps[i], MALLOC_CAP_8BIT|MALLOC_CAP_INTERNAL)) {
  110. /* use the first DRAM heap which can fit the data */
  111. heaps_array = multi_heap_malloc(temp_heaps[i].heap, sizeof(heap_t) * num_heaps);
  112. if (heaps_array != NULL) {
  113. break;
  114. }
  115. }
  116. }
  117. assert(heaps_array != NULL); /* if NULL, there's not enough free startup heap space */
  118. memcpy(heaps_array, temp_heaps, sizeof(heap_t)*num_heaps);
  119. /* Iterate the heaps and set their locks, also add them to the linked list. */
  120. for (size_t i = 0; i < num_heaps; i++) {
  121. if (heaps_array[i].heap != NULL) {
  122. multi_heap_set_lock(heaps_array[i].heap, &heaps_array[i].heap_mux);
  123. }
  124. if (i == 0) {
  125. SLIST_INSERT_HEAD(&registered_heaps, &heaps_array[0], next);
  126. } else {
  127. SLIST_INSERT_AFTER(&heaps_array[i-1], &heaps_array[i], next);
  128. }
  129. }
  130. }
  131. esp_err_t heap_caps_add_region(intptr_t start, intptr_t end)
  132. {
  133. if (start == 0) {
  134. return ESP_ERR_INVALID_ARG;
  135. }
  136. for (size_t i = 0; i < soc_memory_region_count; i++) {
  137. const soc_memory_region_t *region = &soc_memory_regions[i];
  138. // Test requested start only as 'end' may be in a different region entry, assume 'end' has same caps
  139. if (region->start <= start && (intptr_t)(region->start + region->size) > start) {
  140. const uint32_t *caps = soc_memory_types[region->type].caps;
  141. return heap_caps_add_region_with_caps(caps, start, end);
  142. }
  143. }
  144. return ESP_ERR_NOT_FOUND;
  145. }
  146. /* This API is used for internal test purpose and hence its not marked as static */
  147. bool heap_caps_check_add_region_allowed(intptr_t heap_start, intptr_t heap_end, intptr_t start, intptr_t end)
  148. {
  149. /*
  150. * We assume that in any region, the "start" must be stictly less than the end.
  151. * Specially, the 3rd scenario can be allowed. For example, allocate memory from heap,
  152. * then change the capability and call this function to create a new region for special
  153. * application.
  154. * In the following chart, 'start = start' and 'end = end' is contained in 4th scenario.
  155. * This all equal scenario is incorrect because the same region cannot be add twice. For example,
  156. * add the .bss memory to region twice, if not do the check, it will cause exception.
  157. *
  158. * the existing heap region s(tart) e(nd)
  159. * |----------------------|
  160. *
  161. * 1.add region (e1<s) |-----| correct: bool condition_1 = end < heap_start;
  162. *
  163. * 2.add region (s2<s && e2>s) |-----------------| wrong: bool condition_2 = start < heap_start && end > heap_start;
  164. * |---------------------------------| wrong
  165. *
  166. * 3.add region (s3>=s && e3<e) |---------------| correct: bool condition_3 = start >= heap_start && end < heap_end;
  167. * |--------------| correct
  168. *
  169. * 4.add region (s4<e && e4>e) |------------------------| wrong: bool condition_4 = start < heap_end && end > heap_end;
  170. * |---------------------| wrong
  171. *
  172. * 5.add region (s5>=e) |----| correct: bool condition_5 = start >= heap_end;
  173. */
  174. bool condition_2 = start < heap_start && end > heap_start; // if true then region not allowed
  175. bool condition_4 = start < heap_end && end > heap_end; // if true then region not allowed
  176. return (condition_2 || condition_4) ? false: true;
  177. }
  178. esp_err_t heap_caps_add_region_with_caps(const uint32_t caps[], intptr_t start, intptr_t end)
  179. {
  180. esp_err_t err = ESP_FAIL;
  181. if (caps == NULL || start == 0 || end == 0 || end <= start) {
  182. return ESP_ERR_INVALID_ARG;
  183. }
  184. //Check if region overlaps the start and/or end of an existing region. If so, the
  185. //region is invalid (or maybe added twice)
  186. heap_t *heap;
  187. SLIST_FOREACH(heap, &registered_heaps, next) {
  188. if (!heap_caps_check_add_region_allowed(heap->start, heap->end, start, end)) {
  189. ESP_EARLY_LOGD(TAG, "invalid overlap detected with existing heap region");
  190. return ESP_FAIL;
  191. }
  192. }
  193. heap_t *p_new = heap_caps_malloc(sizeof(heap_t), MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT);
  194. if (p_new == NULL) {
  195. err = ESP_ERR_NO_MEM;
  196. goto done;
  197. }
  198. memcpy(p_new->caps, caps, sizeof(p_new->caps));
  199. p_new->start = start;
  200. p_new->end = end;
  201. MULTI_HEAP_LOCK_INIT(&p_new->heap_mux);
  202. p_new->heap = multi_heap_register((void *)start, end - start);
  203. SLIST_NEXT(p_new, next) = NULL;
  204. if (p_new->heap == NULL) {
  205. err = ESP_ERR_INVALID_SIZE;
  206. goto done;
  207. }
  208. multi_heap_set_lock(p_new->heap, &p_new->heap_mux);
  209. /* (This insertion is atomic to registered_heaps, so
  210. we don't need to worry about thread safety for readers,
  211. only for writers. */
  212. static multi_heap_lock_t registered_heaps_write_lock = MULTI_HEAP_LOCK_STATIC_INITIALIZER;
  213. MULTI_HEAP_LOCK(&registered_heaps_write_lock);
  214. SLIST_INSERT_HEAD(&registered_heaps, p_new, next);
  215. MULTI_HEAP_UNLOCK(&registered_heaps_write_lock);
  216. err = ESP_OK;
  217. done:
  218. if (err != ESP_OK) {
  219. free(p_new);
  220. }
  221. return err;
  222. }