blob: a4835f9e40808758710a7e355e43d765b1f459e3 [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{
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700159#if DEBUGLEVEL > INFO
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700160 vaddr_t chunk_end = (vaddr_t)chunk + chunk->len;
161#endif
162
163// dprintf("%s: chunk ptr %p, size 0x%lx, chunk_end 0x%x\n", __FUNCTION__, chunk, chunk->len, chunk_end);
164
165 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) {
171 DEBUG_ASSERT(chunk_end <= (vaddr_t)next_chunk);
172
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 kumarbc73dfc2014-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
258 size = ROUNDUP(size, sizeof(void *));
259
260 // deal with nonzero alignments
261 if (alignment > 0) {
262 if (alignment < 16)
263 alignment = 16;
264
265 // add alignment for worst case fit
vijay kumarbc73dfc2014-07-22 17:06:28 +0530266 if(size > (size + alignment))
267 {
268 dprintf(CRITICAL, "invalid input alignment\n");
269 return NULL;
270 }
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700271 size += alignment;
272 }
273
274 // critical section
275 enter_critical_section();
276
277 // walk through the list
278 ptr = NULL;
279 struct free_heap_chunk *chunk;
280 list_for_every_entry(&theheap.free_list, chunk, struct free_heap_chunk, node) {
281 DEBUG_ASSERT((chunk->len % sizeof(void *)) == 0); // len should always be a multiple of pointer size
282
283 // is it big enough to service our allocation?
284 if (chunk->len >= size) {
285 ptr = chunk;
286
287 // remove it from the list
288 struct list_node *next_node = list_next(&theheap.free_list, &chunk->node);
289 list_delete(&chunk->node);
290
291 if (chunk->len > size + sizeof(struct free_heap_chunk)) {
292 // there's enough space in this chunk to create a new one after the allocation
293 struct free_heap_chunk *newchunk = heap_create_free_chunk((uint8_t *)ptr + size, chunk->len - size);
294
295 // truncate this chunk
296 chunk->len -= chunk->len - size;
297
298 // add the new one where chunk used to be
299 if (next_node)
300 list_add_before(next_node, &newchunk->node);
301 else
302 list_add_tail(&theheap.free_list, &newchunk->node);
303 }
304
305 // the allocated size is actually the length of this chunk, not the size requested
306 DEBUG_ASSERT(chunk->len >= size);
307 size = chunk->len;
308
Travis Geiselbrechtbeb8d432009-06-28 11:31:21 -0700309#if DEBUG_HEAP
310 memset(ptr, ALLOC_FILL, size);
311#endif
312
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700313 ptr = (void *)((addr_t)ptr + sizeof(struct alloc_struct_begin));
314
315 // align the output if requested
316 if (alignment > 0) {
317 ptr = (void *)ROUNDUP((addr_t)ptr, alignment);
318 }
319
320 struct alloc_struct_begin *as = (struct alloc_struct_begin *)ptr;
321 as--;
322 as->magic = HEAP_MAGIC;
323 as->ptr = (void *)chunk;
324 as->size = size;
Travis Geiselbrechtbeb8d432009-06-28 11:31:21 -0700325#if DEBUG_HEAP
326 as->padding_start = ((uint8_t *)ptr + original_size);
327 as->padding_size = (((addr_t)chunk + size) - ((addr_t)ptr + original_size));
328// printf("padding start %p, size %u, chunk %p, size %u\n", as->padding_start, as->padding_size, chunk, size);
329
330 memset(as->padding_start, PADDING_FILL, as->padding_size);
331#endif
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700332
333 break;
334 }
335 }
336
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700337 LTRACEF("returning ptr %p\n", ptr);
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700338
339// heap_dump();
340
341 exit_critical_section();
342
343 return ptr;
344}
345
Kinson Chik2cd211f2011-07-27 19:27:35 -0700346void *heap_realloc(void *ptr, size_t size)
347{
348 void * tmp_ptr = NULL;
349 size_t min_size;
350 struct alloc_struct_begin *as = (struct alloc_struct_begin *)ptr;
351 as--;
352
353 if (size != 0){
354 tmp_ptr = heap_alloc(size, 0);
355 if (ptr != NULL && tmp_ptr != NULL){
356 min_size = (size < as->size) ? size : as->size;
357 memcpy(tmp_ptr, ptr, min_size);
358 heap_free(ptr);
359 }
360 } else {
361 if (ptr != NULL)
362 heap_free(ptr);
363 }
364 return(tmp_ptr);
365}
366
367
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700368void heap_free(void *ptr)
369{
370 if (ptr == 0)
371 return;
372
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700373 LTRACEF("ptr %p\n", ptr);
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700374
375 // check for the old allocation structure
376 struct alloc_struct_begin *as = (struct alloc_struct_begin *)ptr;
377 as--;
378
379 DEBUG_ASSERT(as->magic == HEAP_MAGIC);
380
Travis Geiselbrechtbeb8d432009-06-28 11:31:21 -0700381#if DEBUG_HEAP
382 {
383 uint i;
384 uint8_t *pad = (uint8_t *)as->padding_start;
385
386 for (i = 0; i < as->padding_size; i++) {
387 if (pad[i] != PADDING_FILL) {
388 printf("free at %p scribbled outside the lines:\n", ptr);
389 hexdump(pad, as->padding_size);
390 panic("die\n");
391 }
392 }
393 }
394#endif
395
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700396 LTRACEF("allocation was %zd bytes long at ptr %p\n", as->size, as->ptr);
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700397
398 // looks good, create a free chunk and add it to the pool
399 enter_critical_section();
400 heap_insert_free_chunk(heap_create_free_chunk(as->ptr, as->size));
401 exit_critical_section();
402
403// heap_dump();
404}
405
406void heap_init(void)
407{
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700408 LTRACE_ENTRY;
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700409
410 // set the heap range
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700411 theheap.base = (void *)HEAP_START;
412 theheap.len = HEAP_LEN;
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700413
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700414 LTRACEF("base %p size %zd bytes\n", theheap.base, theheap.len);
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700415
416 // initialize the free list
417 list_initialize(&theheap.free_list);
418
419 // create an initial free chunk
420 heap_insert_free_chunk(heap_create_free_chunk(theheap.base, theheap.len));
421
422 // dump heap info
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700423// heap_dump();
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700424
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700425// dprintf(INFO, "running heap tests\n");
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700426// heap_test();
427}
428
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700429#if DEBUGLEVEL > 1
Travis Geiselbrecht39deded2009-01-24 20:12:27 -0800430#if WITH_LIB_CONSOLE
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700431
Travis Geiselbrecht39deded2009-01-24 20:12:27 -0800432#include <lib/console.h>
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700433
434static int cmd_heap(int argc, const cmd_args *argv);
435
436STATIC_COMMAND_START
Travis Geiselbrechtbeb8d432009-06-28 11:31:21 -0700437STATIC_COMMAND("heap", "heap debug commands", &cmd_heap)
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700438STATIC_COMMAND_END(heap);
439
440static int cmd_heap(int argc, const cmd_args *argv)
441{
442 if (argc < 2) {
443 printf("not enough arguments\n");
444 return -1;
445 }
446
447 if (strcmp(argv[1].str, "info") == 0) {
448 heap_dump();
449 } else {
450 printf("unrecognized command\n");
451 return -1;
452 }
453
454 return 0;
455}
456
457#endif
458#endif
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700459