blob: 71b6fa47cf34d79e18f12a4f6c1d39d7821066c2 [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
Corey Tabaka84697242009-03-26 02:32:01 -040055extern int _heap_end;
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -070056
Travis Geiselbrechteb946052008-09-07 22:32:49 -070057#define HEAP_START ((unsigned long)&_end)
Corey Tabaka84697242009-03-26 02:32:01 -040058#define HEAP_LEN ((size_t)_heap_end - (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
240 // we always put a size field + base pointer + magic in front of the allocation
241 size += sizeof(struct alloc_struct_begin);
Travis Geiselbrechtbeb8d432009-06-28 11:31:21 -0700242#if DEBUG_HEAP
243 size += PADDING_SIZE;
244#endif
245
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700246 // make sure we allocate at least the size of a struct free_heap_chunk so that
247 // when we free it, we can create a struct free_heap_chunk struct and stick it
248 // in the spot
249 if (size < sizeof(struct free_heap_chunk))
250 size = sizeof(struct free_heap_chunk);
251
252 // round up size to a multiple of native pointer size
253 size = ROUNDUP(size, sizeof(void *));
254
255 // deal with nonzero alignments
256 if (alignment > 0) {
257 if (alignment < 16)
258 alignment = 16;
259
260 // add alignment for worst case fit
261 size += alignment;
262 }
263
264 // critical section
265 enter_critical_section();
266
267 // walk through the list
268 ptr = NULL;
269 struct free_heap_chunk *chunk;
270 list_for_every_entry(&theheap.free_list, chunk, struct free_heap_chunk, node) {
271 DEBUG_ASSERT((chunk->len % sizeof(void *)) == 0); // len should always be a multiple of pointer size
272
273 // is it big enough to service our allocation?
274 if (chunk->len >= size) {
275 ptr = chunk;
276
277 // remove it from the list
278 struct list_node *next_node = list_next(&theheap.free_list, &chunk->node);
279 list_delete(&chunk->node);
280
281 if (chunk->len > size + sizeof(struct free_heap_chunk)) {
282 // there's enough space in this chunk to create a new one after the allocation
283 struct free_heap_chunk *newchunk = heap_create_free_chunk((uint8_t *)ptr + size, chunk->len - size);
284
285 // truncate this chunk
286 chunk->len -= chunk->len - size;
287
288 // add the new one where chunk used to be
289 if (next_node)
290 list_add_before(next_node, &newchunk->node);
291 else
292 list_add_tail(&theheap.free_list, &newchunk->node);
293 }
294
295 // the allocated size is actually the length of this chunk, not the size requested
296 DEBUG_ASSERT(chunk->len >= size);
297 size = chunk->len;
298
Travis Geiselbrechtbeb8d432009-06-28 11:31:21 -0700299#if DEBUG_HEAP
300 memset(ptr, ALLOC_FILL, size);
301#endif
302
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700303 ptr = (void *)((addr_t)ptr + sizeof(struct alloc_struct_begin));
304
305 // align the output if requested
306 if (alignment > 0) {
307 ptr = (void *)ROUNDUP((addr_t)ptr, alignment);
308 }
309
310 struct alloc_struct_begin *as = (struct alloc_struct_begin *)ptr;
311 as--;
312 as->magic = HEAP_MAGIC;
313 as->ptr = (void *)chunk;
314 as->size = size;
Travis Geiselbrechtbeb8d432009-06-28 11:31:21 -0700315#if DEBUG_HEAP
316 as->padding_start = ((uint8_t *)ptr + original_size);
317 as->padding_size = (((addr_t)chunk + size) - ((addr_t)ptr + original_size));
318// printf("padding start %p, size %u, chunk %p, size %u\n", as->padding_start, as->padding_size, chunk, size);
319
320 memset(as->padding_start, PADDING_FILL, as->padding_size);
321#endif
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700322
323 break;
324 }
325 }
326
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700327 LTRACEF("returning ptr %p\n", ptr);
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700328
329// heap_dump();
330
331 exit_critical_section();
332
333 return ptr;
334}
335
Kinson Chik2cd211f2011-07-27 19:27:35 -0700336void *heap_realloc(void *ptr, size_t size)
337{
338 void * tmp_ptr = NULL;
339 size_t min_size;
340 struct alloc_struct_begin *as = (struct alloc_struct_begin *)ptr;
341 as--;
342
343 if (size != 0){
344 tmp_ptr = heap_alloc(size, 0);
345 if (ptr != NULL && tmp_ptr != NULL){
346 min_size = (size < as->size) ? size : as->size;
347 memcpy(tmp_ptr, ptr, min_size);
348 heap_free(ptr);
349 }
350 } else {
351 if (ptr != NULL)
352 heap_free(ptr);
353 }
354 return(tmp_ptr);
355}
356
357
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700358void heap_free(void *ptr)
359{
360 if (ptr == 0)
361 return;
362
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700363 LTRACEF("ptr %p\n", ptr);
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700364
365 // check for the old allocation structure
366 struct alloc_struct_begin *as = (struct alloc_struct_begin *)ptr;
367 as--;
368
369 DEBUG_ASSERT(as->magic == HEAP_MAGIC);
370
Travis Geiselbrechtbeb8d432009-06-28 11:31:21 -0700371#if DEBUG_HEAP
372 {
373 uint i;
374 uint8_t *pad = (uint8_t *)as->padding_start;
375
376 for (i = 0; i < as->padding_size; i++) {
377 if (pad[i] != PADDING_FILL) {
378 printf("free at %p scribbled outside the lines:\n", ptr);
379 hexdump(pad, as->padding_size);
380 panic("die\n");
381 }
382 }
383 }
384#endif
385
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700386 LTRACEF("allocation was %zd bytes long at ptr %p\n", as->size, as->ptr);
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700387
388 // looks good, create a free chunk and add it to the pool
389 enter_critical_section();
390 heap_insert_free_chunk(heap_create_free_chunk(as->ptr, as->size));
391 exit_critical_section();
392
393// heap_dump();
394}
395
396void heap_init(void)
397{
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700398 LTRACE_ENTRY;
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700399
400 // set the heap range
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700401 theheap.base = (void *)HEAP_START;
402 theheap.len = HEAP_LEN;
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700403
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700404 LTRACEF("base %p size %zd bytes\n", theheap.base, theheap.len);
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700405
406 // initialize the free list
407 list_initialize(&theheap.free_list);
408
409 // create an initial free chunk
410 heap_insert_free_chunk(heap_create_free_chunk(theheap.base, theheap.len));
411
412 // dump heap info
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700413// heap_dump();
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700414
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700415// dprintf(INFO, "running heap tests\n");
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700416// heap_test();
417}
418
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700419#if DEBUGLEVEL > 1
Travis Geiselbrecht39deded2009-01-24 20:12:27 -0800420#if WITH_LIB_CONSOLE
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700421
Travis Geiselbrecht39deded2009-01-24 20:12:27 -0800422#include <lib/console.h>
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700423
424static int cmd_heap(int argc, const cmd_args *argv);
425
426STATIC_COMMAND_START
Travis Geiselbrechtbeb8d432009-06-28 11:31:21 -0700427STATIC_COMMAND("heap", "heap debug commands", &cmd_heap)
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700428STATIC_COMMAND_END(heap);
429
430static int cmd_heap(int argc, const cmd_args *argv)
431{
432 if (argc < 2) {
433 printf("not enough arguments\n");
434 return -1;
435 }
436
437 if (strcmp(argv[1].str, "info") == 0) {
438 heap_dump();
439 } else {
440 printf("unrecognized command\n");
441 return -1;
442 }
443
444 return 0;
445}
446
447#endif
448#endif
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700449