/ components / heap / heap_caps_init.c
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, &registered_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 = &regions[i - 1];
 71          soc_memory_region_t *b = &regions[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 = &regions[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(&registered_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(&registered_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, &registered_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(&registered_heaps_write_lock);
231      SLIST_INSERT_HEAD(&registered_heaps, p_new, next);
232      MULTI_HEAP_UNLOCK(&registered_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  }