Select one of the symbols to view example projects that use it.
 
Outline
#include <stdbool.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <sys/param.h>
#include "esp_attr.h"
#include "multi_heap.h"
#include "esp_log.h"
#include "heap_private.h"
#define CALL_HOOK
#define UNALIGNED_MEM_ALIGNMENT_BYTES
dram_alloc_to_iram_addr(void *, size_t)
heap_caps_free(void *)
aligned_or_unaligned_alloc(multi_heap_handle_t, size_t, size_t, size_t)
heap_caps_aligned_alloc_base(size_t, size_t, uint32_t)
heap_caps_malloc_base(size_t, uint32_t)
heap_caps_realloc_base(void *, size_t, uint32_t)
heap_caps_calloc_base(size_t, size_t, uint32_t)
Files
loading...
SourceVuESP-IDF Framework and ExamplesESP-IDFcomponents/heap/heap_caps_base.c
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
/* * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 *//* ... */ #include <stdbool.h> #include <string.h> #include <assert.h> #include <stdio.h> #include <sys/param.h> #include "esp_attr.h" #include "multi_heap.h" #include "esp_log.h" #include "heap_private.h"9 includes #ifdef CONFIG_HEAP_USE_HOOKS #define CALL_HOOK(hook, ...) { \ if (hook != NULL) { \ hook(__VA_ARGS__); \ }{...} \ }{...} /* ... */#else #define CALL_HOOK(hook, ...) {} #endif //This is normally provided by the heap-memalign-hw component. extern void esp_heap_adjust_alignment_to_hw(size_t *p_alignment, size_t *p_size, uint32_t *p_caps); //Default alignment the multiheap allocator / tlsf will align 'unaligned' memory to, in bytes #define UNALIGNED_MEM_ALIGNMENT_BYTES 4 /* This takes a memory chunk in a region that can be addressed as both DRAM as well as IRAM. It will convert it to IRAM in such a way that it can be later freed. It assumes both the address as well as the length to be word-aligned. It returns a region that's 1 word smaller than the region given because it stores the original Dram address there. *//* ... */ HEAP_IRAM_ATTR static void *dram_alloc_to_iram_addr(void *addr, size_t len) { uintptr_t dstart = (uintptr_t)addr; //First word uintptr_t dend __attribute__((unused)) = dstart + len - 4; //Last word assert(esp_ptr_in_diram_dram((void *)dstart) || esp_ptr_in_rtc_dram_fast((void *)dstart)); assert(esp_ptr_in_diram_dram((void *)dend) || esp_ptr_in_rtc_dram_fast((void *)dend)); assert((dstart & 3) == 0); assert((dend & 3) == 0); #if SOC_DIRAM_INVERTED // We want the word before the result to hold the DRAM address uint32_t *iptr = esp_ptr_diram_dram_to_iram((void *)dend); #else uint32_t *iptr = NULL; if (esp_ptr_in_rtc_dram_fast((void *)dstart)) { iptr = esp_ptr_rtc_dram_to_iram((void *)dstart); }{...} else { iptr = esp_ptr_diram_dram_to_iram((void *)dstart); }{...} #endif/* ... */ *iptr = dstart; return iptr + 1; }{ ... } HEAP_IRAM_ATTR void heap_caps_free( void *ptr) { if (ptr == NULL) { return; }{...} if (esp_ptr_in_diram_iram(ptr) || esp_ptr_in_rtc_iram_fast(ptr)) { //Memory allocated here is actually allocated in the DRAM alias region and //cannot be de-allocated as usual. dram_alloc_to_iram_addr stores a pointer to //the equivalent DRAM address, though; free that. uint32_t *dramAddrPtr = (uint32_t *)ptr; ptr = (void *)dramAddrPtr[-1]; }{...} void *block_owner_ptr = MULTI_HEAP_REMOVE_BLOCK_OWNER_OFFSET(ptr); heap_t *heap = find_containing_heap(block_owner_ptr); assert(heap != NULL && "free() target pointer is outside heap areas"); multi_heap_free(heap->heap, block_owner_ptr); CALL_HOOK(esp_heap_trace_free_hook, ptr); }{ ... } HEAP_IRAM_ATTR static inline void *aligned_or_unaligned_alloc(multi_heap_handle_t heap, size_t size, size_t alignment, size_t offset) { if (alignment<=UNALIGNED_MEM_ALIGNMENT_BYTES) { //alloc and friends align to 32-bit by default return multi_heap_malloc(heap, size); }{...} else { return multi_heap_aligned_alloc_offs(heap, size, alignment, offset); }{...} }{ ... } /* This function should not be called directly as it does not check for failure / call heap_caps_alloc_failed() Note that this function does 'unaligned' alloc calls if alignment <= UNALIGNED_MEM_ALIGNMENT_BYTES (=4) as the allocator will align to that value by default. *//* ... */ HEAP_IRAM_ATTR NOINLINE_ATTR void *heap_caps_aligned_alloc_base(size_t alignment, size_t size, uint32_t caps) { void *ret = NULL; // Alignment, size and caps may need to be modified because of hardware requirements. esp_heap_adjust_alignment_to_hw(&alignment, &size, &caps); // remove block owner size to HEAP_SIZE_MAX rather than adding the block owner size // to size to prevent overflows. if (size == 0 || size > MULTI_HEAP_REMOVE_BLOCK_OWNER_SIZE(HEAP_SIZE_MAX) ) { // Avoids int overflow when adding small numbers to size, or // calculating 'end' from start+size, by limiting 'size' to the possible range return NULL; }{...} if (caps & MALLOC_CAP_EXEC) { //MALLOC_CAP_EXEC forces an alloc from IRAM. There is a region which has both this as well as the following //caps, but the following caps are not possible for IRAM. Thus, the combination is impossible and we return //NULL directly, even although our heap capabilities (based on soc_memory_tags & soc_memory_regions) would //indicate there is a tag for this. if ((caps & MALLOC_CAP_8BIT) || (caps & MALLOC_CAP_DMA)) { return NULL; }{...} caps |= MALLOC_CAP_32BIT; // IRAM is 32-bit accessible RAM }{...} if (caps & MALLOC_CAP_32BIT) { /* 32-bit accessible RAM should allocated in 4 byte aligned sizes * (Future versions of ESP-IDF should possibly fail if an invalid size is requested) *//* ... */ size = (size + 3) & (~3); // int overflow checked above }{...} for (int prio = 0; prio < SOC_MEMORY_TYPE_NO_PRIOS; prio++) { //Iterate over heaps and check capabilities at this priority heap_t *heap; SLIST_FOREACH(heap, &registered_heaps, next) { if (heap->heap == NULL) { continue; }{...} if ((heap->caps[prio] & caps) != 0) { //Heap has at least one of the caps requested. If caps has other bits set that this prio //doesn't cover, see if they're available in other prios. if ((get_all_caps(heap) & caps) == caps) { //This heap can satisfy all the requested capabilities. See if we can grab some memory using it. // If MALLOC_CAP_EXEC is requested but the DRAM and IRAM are on the same addresses (like on esp32c6) // proceed as for a default allocation. if (((caps & MALLOC_CAP_EXEC) && !esp_dram_match_iram()) && (esp_ptr_in_diram_dram((void *)heap->start) || esp_ptr_in_rtc_dram_fast((void *)heap->start))) { //This is special, insofar that what we're going to get back is a DRAM address. If so, //we need to 'invert' it (lowest address in DRAM == highest address in IRAM and vice-versa) and //add a pointer to the DRAM equivalent before the address we're going to return. ret = aligned_or_unaligned_alloc(heap->heap, MULTI_HEAP_ADD_BLOCK_OWNER_SIZE(size) + 4, alignment, MULTI_HEAP_BLOCK_OWNER_SIZE()); // int overflow checked above if (ret != NULL) { MULTI_HEAP_SET_BLOCK_OWNER(ret); ret = MULTI_HEAP_ADD_BLOCK_OWNER_OFFSET(ret); uint32_t *iptr = dram_alloc_to_iram_addr(ret, size + 4); // int overflow checked above CALL_HOOK(esp_heap_trace_alloc_hook, iptr, size, caps); return iptr; }{...} }{...} else { //Just try to alloc, nothing special. ret = aligned_or_unaligned_alloc(heap->heap, MULTI_HEAP_ADD_BLOCK_OWNER_SIZE(size), alignment, MULTI_HEAP_BLOCK_OWNER_SIZE()); if (ret != NULL) { MULTI_HEAP_SET_BLOCK_OWNER(ret); ret = MULTI_HEAP_ADD_BLOCK_OWNER_OFFSET(ret); CALL_HOOK(esp_heap_trace_alloc_hook, ret, size, caps); return ret; }{...} }{...} }{...} }{...} }{...} }{...} //Nothing usable found. return NULL; }{ ... } //Wrapper for heap_caps_aligned_alloc_base as that can also do unaligned allocs. HEAP_IRAM_ATTR NOINLINE_ATTR void *heap_caps_malloc_base( size_t size, uint32_t caps) { return heap_caps_aligned_alloc_base(UNALIGNED_MEM_ALIGNMENT_BYTES, size, caps); }{ ... } /* This function should not be called directly as it does not check for failure / call heap_caps_alloc_failed() *//* ... */ HEAP_IRAM_ATTR NOINLINE_ATTR void *heap_caps_realloc_base( void *ptr, size_t size, uint32_t caps) { bool ptr_in_diram_case = false; heap_t *heap = NULL; void *dram_ptr = NULL; //See if memory needs alignment because of hardware reasons. size_t alignment = UNALIGNED_MEM_ALIGNMENT_BYTES; esp_heap_adjust_alignment_to_hw(&alignment, &size, &caps); if (ptr == NULL) { return heap_caps_aligned_alloc_base(alignment, size, caps); }{...} if (size == 0) { heap_caps_free(ptr); return NULL; }{...} // remove block owner size to HEAP_SIZE_MAX rather than adding the block owner size // to size to prevent overflows. if (size > MULTI_HEAP_REMOVE_BLOCK_OWNER_SIZE(HEAP_SIZE_MAX)) { return NULL; }{...} //The pointer to memory may be aliased, we need to //recover the corresponding address before to manage a new allocation: if(esp_ptr_in_diram_iram((void *)ptr)) { uint32_t *dram_addr = (uint32_t *)ptr; dram_ptr = (void *)dram_addr[-1]; dram_ptr = MULTI_HEAP_REMOVE_BLOCK_OWNER_OFFSET(dram_ptr); heap = find_containing_heap(dram_ptr); assert(heap != NULL && "realloc() pointer is outside heap areas"); //with pointers that reside on diram space, we avoid using //the realloc implementation due to address translation issues, //instead force a malloc/copy/free ptr_in_diram_case = true; }{...} else { heap = find_containing_heap(ptr); assert(heap != NULL && "realloc() pointer is outside heap areas"); }{...} // shift ptr by block owner offset. Since the ptr returned to the user // does not include the block owner bytes (that are located at the // beginning of the allocated memory) we have to add them back before // processing the realloc. ptr = MULTI_HEAP_REMOVE_BLOCK_OWNER_OFFSET(ptr); // are the existing heap's capabilities compatible with the // requested ones? bool compatible_caps = (caps & get_all_caps(heap)) == caps; //Note we don't try realloc() on memory that needs to be aligned, that is handled //by the fallthrough code. if (compatible_caps && !ptr_in_diram_case && alignment<=UNALIGNED_MEM_ALIGNMENT_BYTES) { // try to reallocate this memory within the same heap // (which will resize the block if it can) void *r = multi_heap_realloc(heap->heap, ptr, MULTI_HEAP_ADD_BLOCK_OWNER_SIZE(size)); if (r != NULL) { MULTI_HEAP_SET_BLOCK_OWNER(r); r = MULTI_HEAP_ADD_BLOCK_OWNER_OFFSET(r); CALL_HOOK(esp_heap_trace_alloc_hook, r, size, caps); return r; }{...} }{...} // if we couldn't do that, try to see if we can reallocate // in a different heap with requested capabilities. void *new_p = heap_caps_aligned_alloc_base(alignment, size, caps); if (new_p != NULL) { size_t old_size = 0; //If we're dealing with aliased ptr, information regarding its containing //heap can only be obtained with translated address. if(ptr_in_diram_case) { old_size = multi_heap_get_allocated_size(heap->heap, dram_ptr); }{...} else { old_size = multi_heap_get_allocated_size(heap->heap, ptr); }{...} assert(old_size > 0); // do not copy the block owner bytes memcpy(new_p, MULTI_HEAP_ADD_BLOCK_OWNER_OFFSET(ptr), MIN(size, old_size)); // add the block owner bytes to ptr since they are removed in heap_caps_free heap_caps_free(MULTI_HEAP_ADD_BLOCK_OWNER_OFFSET(ptr)); return new_p; }{...} return NULL; }{ ... } /* This function should not be called directly as it does not check for failure / call heap_caps_alloc_failed() *//* ... */ HEAP_IRAM_ATTR void *heap_caps_calloc_base( size_t n, size_t size, uint32_t caps) { void *result; size_t size_bytes; if (__builtin_mul_overflow(n, size, &size_bytes)) { return NULL; }{...} result = heap_caps_malloc_base(size_bytes, caps); if (result != NULL) { memset(result, 0, size_bytes); }{...} return result; }{ ... }
Details
Show:
from
Types: Columns:
This file uses the notable symbols shown below. Click anywhere in the file to view more details.