blob: a4094fb680f36ff1f60448bb188f7cad9c1e0d54 [file] [log] [blame]
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -07001/*
Travis Geiselbrechtbeb8d432009-06-28 11:31:21 -07002 * Copyright (c) 2008-2009 Travis Geiselbrecht
Corey Tabaka84697242009-03-26 02:32:01 -04003 * Copyright (c) 2009 Corey Tabaka
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -07004 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files
7 * (the "Software"), to deal in the Software without restriction,
8 * including without limitation the rights to use, copy, modify, merge,
9 * publish, distribute, sublicense, and/or sell copies of the Software,
10 * and to permit persons to whom the Software is furnished to do so,
11 * subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24#include <debug.h>
25#include <err.h>
26#include <list.h>
27#include <rand.h>
Travis Geiselbrechteb946052008-09-07 22:32:49 -070028#include <string.h>
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -070029#include <kernel/thread.h>
Travis Geiselbrechteb946052008-09-07 22:32:49 -070030#include <lib/heap.h>
31
32#define LOCAL_TRACE 0
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -070033
Travis Geiselbrechtbeb8d432009-06-28 11:31:21 -070034#define DEBUG_HEAP 0
35#define ALLOC_FILL 0x99
36#define FREE_FILL 0x77
37#define PADDING_FILL 0x55
38#define PADDING_SIZE 64
39
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -070040#define ROUNDUP(a, b) (((a) + ((b)-1)) & ~((b)-1))
41
42#define HEAP_MAGIC 'HEAP'
43
Travis Geiselbrechteb946052008-09-07 22:32:49 -070044#if WITH_STATIC_HEAP
45
46#if !defined(HEAP_START) || !defined(HEAP_LEN)
47#error WITH_STATIC_HEAP set but no HEAP_START or HEAP_LEN defined
48#endif
49
50#else
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -070051// end of the binary
52extern int _end;
53
54// end of memory
Channagoud Kadabic4343642012-09-25 17:40:05 +053055extern int _end_of_ram;
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -070056
Travis Geiselbrechteb946052008-09-07 22:32:49 -070057#define HEAP_START ((unsigned long)&_end)
Channagoud Kadabic4343642012-09-25 17:40:05 +053058#define HEAP_LEN ((size_t)&_end_of_ram - (size_t)&_end)
Travis Geiselbrechteb946052008-09-07 22:32:49 -070059#endif
60
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -070061struct free_heap_chunk {
62 struct list_node node;
63 size_t len;
64};
65
66struct heap {
67 void *base;
68 size_t len;
69 struct list_node free_list;
70};
71
72// heap static vars
73static struct heap theheap;
74
75// structure placed at the beginning every allocation
76struct alloc_struct_begin {
77 unsigned int magic;
78 void *ptr;
79 size_t size;
Travis Geiselbrechtbeb8d432009-06-28 11:31:21 -070080#if DEBUG_HEAP
81 void *padding_start;
82 size_t padding_size;
83#endif
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -070084};
85
86static void dump_free_chunk(struct free_heap_chunk *chunk)
87{
Travis Geiselbrechteb946052008-09-07 22:32:49 -070088 dprintf(INFO, "\t\tbase %p, end 0x%lx, len 0x%zx\n", chunk, (vaddr_t)chunk + chunk->len, chunk->len);
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -070089}
90
91static void heap_dump(void)
92{
Travis Geiselbrechteb946052008-09-07 22:32:49 -070093 dprintf(INFO, "Heap dump:\n");
94 dprintf(INFO, "\tbase %p, len 0x%zx\n", theheap.base, theheap.len);
95 dprintf(INFO, "\tfree list:\n");
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -070096
97 struct free_heap_chunk *chunk;
98 list_for_every_entry(&theheap.free_list, chunk, struct free_heap_chunk, node) {
99 dump_free_chunk(chunk);
100 }
101}
102
103static void heap_test(void)
104{
105 void *ptr[16];
106
107 ptr[0] = heap_alloc(8, 0);
108 ptr[1] = heap_alloc(32, 0);
109 ptr[2] = heap_alloc(7, 0);
110 ptr[3] = heap_alloc(0, 0);
111 ptr[4] = heap_alloc(98713, 0);
112 ptr[5] = heap_alloc(16, 0);
113
114 heap_free(ptr[5]);
115 heap_free(ptr[1]);
116 heap_free(ptr[3]);
117 heap_free(ptr[0]);
118 heap_free(ptr[4]);
119 heap_free(ptr[2]);
120
121 heap_dump();
122
123 int i;
124 for (i=0; i < 16; i++)
125 ptr[i] = 0;
126
127 for (i=0; i < 32768; i++) {
128 unsigned int index = (unsigned int)rand() % 16;
129
130 if ((i % (16*1024)) == 0)
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700131 printf("pass %d\n", i);
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700132
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700133// printf("index 0x%x\n", index);
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700134 if (ptr[index]) {
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700135// printf("freeing ptr[0x%x] = %p\n", index, ptr[index]);
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700136 heap_free(ptr[index]);
137 ptr[index] = 0;
138 }
139 unsigned int align = 1 << ((unsigned int)rand() % 8);
140 ptr[index] = heap_alloc((unsigned int)rand() % 32768, align);
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700141// printf("ptr[0x%x] = %p, align 0x%x\n", index, ptr[index], align);
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700142
143 DEBUG_ASSERT(((addr_t)ptr[index] % align) == 0);
144// heap_dump();
145 }
146
147 for (i=0; i < 16; i++) {
148 if (ptr[i])
149 heap_free(ptr[i]);
150 }
151
152 heap_dump();
153}
154
155// try to insert this free chunk into the free list, consuming the chunk by merging it with
156// nearby ones if possible. Returns base of whatever chunk it became in the list.
157static struct free_heap_chunk *heap_insert_free_chunk(struct free_heap_chunk *chunk)
158{
P.V. Phani Kumar11e4c682015-10-05 18:17:46 +0530159#if DEBUG_HEAP
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700160 vaddr_t chunk_end = (vaddr_t)chunk + chunk->len;
P.V. Phani Kumar11e4c682015-10-05 18:17:46 +0530161 dprintf(CRITICAL,"%s: chunk ptr %p, size 0x%lx, chunk_end 0x%x\n",
162 __FUNCTION__, chunk, chunk->len, chunk_end);
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700163#endif
164
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700165 struct free_heap_chunk *next_chunk;
166 struct free_heap_chunk *last_chunk;
167
168 // walk through the list, finding the node to insert before
169 list_for_every_entry(&theheap.free_list, next_chunk, struct free_heap_chunk, node) {
170 if (chunk < next_chunk) {
P.V. Phani Kumar11e4c682015-10-05 18:17:46 +0530171 DEBUG_ASSERT(((vaddr_t)chunk + chunk->len) <= (vaddr_t)next_chunk);
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700172
173 list_add_before(&next_chunk->node, &chunk->node);
174
175 goto try_merge;
176 }
177 }
178
179 // walked off the end of the list, add it at the tail
180 list_add_tail(&theheap.free_list, &chunk->node);
181
182 // try to merge with the previous chunk
183try_merge:
184 last_chunk = list_prev_type(&theheap.free_list, &chunk->node, struct free_heap_chunk, node);
185 if (last_chunk) {
186 if ((vaddr_t)last_chunk + last_chunk->len == (vaddr_t)chunk) {
187 // easy, just extend the previous chunk
188 last_chunk->len += chunk->len;
189
190 // remove ourself from the list
191 list_delete(&chunk->node);
192
193 // set the chunk pointer to the newly extended chunk, in case
194 // it needs to merge with the next chunk below
195 chunk = last_chunk;
196 }
197 }
198
199 // try to merge with the next chunk
200 if (next_chunk) {
201 if ((vaddr_t)chunk + chunk->len == (vaddr_t)next_chunk) {
202 // extend our chunk
203 chunk->len += next_chunk->len;
204
205 // remove them from the list
206 list_delete(&next_chunk->node);
207 }
208 }
209
210 return chunk;
211}
212
213struct free_heap_chunk *heap_create_free_chunk(void *ptr, size_t len)
214{
215 DEBUG_ASSERT((len % sizeof(void *)) == 0); // size must be aligned on pointer boundary
216
Travis Geiselbrechtbeb8d432009-06-28 11:31:21 -0700217#if DEBUG_HEAP
218 memset(ptr, FREE_FILL, len);
219#endif
220
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700221 struct free_heap_chunk *chunk = (struct free_heap_chunk *)ptr;
222 chunk->len = len;
223
224 return chunk;
225}
226
227void *heap_alloc(size_t size, unsigned int alignment)
228{
229 void *ptr;
Travis Geiselbrechtbeb8d432009-06-28 11:31:21 -0700230#if DEBUG_HEAP
231 size_t original_size = size;
232#endif
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700233
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700234 LTRACEF("size %zd, align %d\n", size, alignment);
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700235
236 // alignment must be power of 2
237 if (alignment & (alignment - 1))
238 return NULL;
239
vijay kumar6390f202014-07-22 17:06:28 +0530240 if(size > (size + sizeof(struct alloc_struct_begin)))
241 {
242 dprintf(CRITICAL, "invalid input size\n");
243 return NULL;
244 }
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700245 // we always put a size field + base pointer + magic in front of the allocation
246 size += sizeof(struct alloc_struct_begin);
Travis Geiselbrechtbeb8d432009-06-28 11:31:21 -0700247#if DEBUG_HEAP
248 size += PADDING_SIZE;
249#endif
250
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700251 // make sure we allocate at least the size of a struct free_heap_chunk so that
252 // when we free it, we can create a struct free_heap_chunk struct and stick it
253 // in the spot
254 if (size < sizeof(struct free_heap_chunk))
255 size = sizeof(struct free_heap_chunk);
256
257 // round up size to a multiple of native pointer size
c_aupadh022d17a2018-01-25 14:41:35 +0530258 if(size > (size + sizeof(void *)))
259 {
260 dprintf(CRITICAL, "invalid input size\n");
261 return NULL;
262 }
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700263 size = ROUNDUP(size, sizeof(void *));
264
265 // deal with nonzero alignments
266 if (alignment > 0) {
267 if (alignment < 16)
268 alignment = 16;
269
270 // add alignment for worst case fit
vijay kumar6390f202014-07-22 17:06:28 +0530271 if(size > (size + alignment))
272 {
273 dprintf(CRITICAL, "invalid input alignment\n");
274 return NULL;
275 }
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700276 size += alignment;
277 }
278
279 // critical section
280 enter_critical_section();
281
282 // walk through the list
283 ptr = NULL;
284 struct free_heap_chunk *chunk;
285 list_for_every_entry(&theheap.free_list, chunk, struct free_heap_chunk, node) {
286 DEBUG_ASSERT((chunk->len % sizeof(void *)) == 0); // len should always be a multiple of pointer size
287
288 // is it big enough to service our allocation?
289 if (chunk->len >= size) {
290 ptr = chunk;
291
292 // remove it from the list
293 struct list_node *next_node = list_next(&theheap.free_list, &chunk->node);
294 list_delete(&chunk->node);
295
296 if (chunk->len > size + sizeof(struct free_heap_chunk)) {
297 // there's enough space in this chunk to create a new one after the allocation
298 struct free_heap_chunk *newchunk = heap_create_free_chunk((uint8_t *)ptr + size, chunk->len - size);
299
300 // truncate this chunk
301 chunk->len -= chunk->len - size;
302
303 // add the new one where chunk used to be
304 if (next_node)
305 list_add_before(next_node, &newchunk->node);
306 else
307 list_add_tail(&theheap.free_list, &newchunk->node);
308 }
309
310 // the allocated size is actually the length of this chunk, not the size requested
311 DEBUG_ASSERT(chunk->len >= size);
312 size = chunk->len;
313
Travis Geiselbrechtbeb8d432009-06-28 11:31:21 -0700314#if DEBUG_HEAP
315 memset(ptr, ALLOC_FILL, size);
316#endif
317
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700318 ptr = (void *)((addr_t)ptr + sizeof(struct alloc_struct_begin));
319
320 // align the output if requested
321 if (alignment > 0) {
322 ptr = (void *)ROUNDUP((addr_t)ptr, alignment);
323 }
324
325 struct alloc_struct_begin *as = (struct alloc_struct_begin *)ptr;
326 as--;
327 as->magic = HEAP_MAGIC;
328 as->ptr = (void *)chunk;
329 as->size = size;
Travis Geiselbrechtbeb8d432009-06-28 11:31:21 -0700330#if DEBUG_HEAP
331 as->padding_start = ((uint8_t *)ptr + original_size);
332 as->padding_size = (((addr_t)chunk + size) - ((addr_t)ptr + original_size));
333// printf("padding start %p, size %u, chunk %p, size %u\n", as->padding_start, as->padding_size, chunk, size);
334
335 memset(as->padding_start, PADDING_FILL, as->padding_size);
336#endif
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700337
338 break;
339 }
340 }
341
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700342 LTRACEF("returning ptr %p\n", ptr);
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700343
344// heap_dump();
345
346 exit_critical_section();
347
348 return ptr;
349}
350
Kinson Chik2cd211f2011-07-27 19:27:35 -0700351void *heap_realloc(void *ptr, size_t size)
352{
353 void * tmp_ptr = NULL;
354 size_t min_size;
355 struct alloc_struct_begin *as = (struct alloc_struct_begin *)ptr;
356 as--;
357
358 if (size != 0){
359 tmp_ptr = heap_alloc(size, 0);
360 if (ptr != NULL && tmp_ptr != NULL){
361 min_size = (size < as->size) ? size : as->size;
362 memcpy(tmp_ptr, ptr, min_size);
363 heap_free(ptr);
364 }
365 } else {
366 if (ptr != NULL)
367 heap_free(ptr);
368 }
369 return(tmp_ptr);
370}
371
372
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700373void heap_free(void *ptr)
374{
375 if (ptr == 0)
376 return;
377
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700378 LTRACEF("ptr %p\n", ptr);
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700379
380 // check for the old allocation structure
381 struct alloc_struct_begin *as = (struct alloc_struct_begin *)ptr;
382 as--;
383
384 DEBUG_ASSERT(as->magic == HEAP_MAGIC);
385
Travis Geiselbrechtbeb8d432009-06-28 11:31:21 -0700386#if DEBUG_HEAP
387 {
388 uint i;
389 uint8_t *pad = (uint8_t *)as->padding_start;
390
391 for (i = 0; i < as->padding_size; i++) {
392 if (pad[i] != PADDING_FILL) {
393 printf("free at %p scribbled outside the lines:\n", ptr);
394 hexdump(pad, as->padding_size);
395 panic("die\n");
396 }
397 }
398 }
399#endif
400
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700401 LTRACEF("allocation was %zd bytes long at ptr %p\n", as->size, as->ptr);
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700402
403 // looks good, create a free chunk and add it to the pool
404 enter_critical_section();
405 heap_insert_free_chunk(heap_create_free_chunk(as->ptr, as->size));
406 exit_critical_section();
407
408// heap_dump();
409}
410
411void heap_init(void)
412{
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700413 LTRACE_ENTRY;
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700414
415 // set the heap range
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700416 theheap.base = (void *)HEAP_START;
417 theheap.len = HEAP_LEN;
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700418
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700419 LTRACEF("base %p size %zd bytes\n", theheap.base, theheap.len);
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700420
421 // initialize the free list
422 list_initialize(&theheap.free_list);
423
424 // create an initial free chunk
425 heap_insert_free_chunk(heap_create_free_chunk(theheap.base, theheap.len));
426
427 // dump heap info
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700428// heap_dump();
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700429
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700430// dprintf(INFO, "running heap tests\n");
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700431// heap_test();
432}
433
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700434#if DEBUGLEVEL > 1
Travis Geiselbrecht39deded2009-01-24 20:12:27 -0800435#if WITH_LIB_CONSOLE
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700436
Travis Geiselbrecht39deded2009-01-24 20:12:27 -0800437#include <lib/console.h>
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700438
439static int cmd_heap(int argc, const cmd_args *argv);
440
441STATIC_COMMAND_START
Travis Geiselbrechtbeb8d432009-06-28 11:31:21 -0700442STATIC_COMMAND("heap", "heap debug commands", &cmd_heap)
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700443STATIC_COMMAND_END(heap);
444
445static int cmd_heap(int argc, const cmd_args *argv)
446{
447 if (argc < 2) {
448 printf("not enough arguments\n");
449 return -1;
450 }
451
452 if (strcmp(argv[1].str, "info") == 0) {
453 heap_dump();
454 } else {
455 printf("unrecognized command\n");
456 return -1;
457 }
458
459 return 0;
460}
461
462#endif
463#endif
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700464