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