| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488 |
- /*
- * Copyright (c) 2008-2009 Travis Geiselbrecht
- * Copyright (c) 2009 Corey Tabaka
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
- #include <debug.h>
- #include <err.h>
- #include <list.h>
- #include <rand.h>
- #include <string.h>
- #include <kernel/thread.h>
- #include <lib/heap.h>
- #include <platform/ram_console.h>
- #include <dev/mrdump.h>
- #include <arch/arm/mmu.h>
- #include <ram_console_common.h>
- #include <platform/boot_mode.h>
- #include <mblock.h>
- #define LOCAL_TRACE 0
- static uint64_t mblock_heap_start;
- #define DEBUG_HEAP 0
- #define ALLOC_FILL 0x99
- #define FREE_FILL 0x77
- #define PADDING_FILL 0x55
- #define PADDING_SIZE 64
- #define ROUNDUP(a, b) (((a) + ((b)-1)) & ~((b)-1))
- #define HEAP_MAGIC 'HEAP'
- #if WITH_STATIC_HEAP
- #if !defined(HEAP_START) || !defined(HEAP_LEN)
- #error WITH_STATIC_HEAP set but no HEAP_START or HEAP_LEN defined
- #endif
- #else
- // end of the binary
- extern int _end;
- // end of memory
- extern int _heap_end;
- #define HEAP_START ((unsigned long)&_end)
- #define HEAP_LEN ((size_t)_heap_end - (size_t)&_end)
- #endif
- struct free_heap_chunk {
- struct list_node node;
- size_t len;
- };
- struct heap {
- void *base;
- size_t len;
- struct list_node free_list;
- };
- // heap static vars
- static struct heap theheap;
- // structure placed at the beginning every allocation
- struct alloc_struct_begin {
- unsigned int magic;
- void *ptr;
- size_t size;
- #if DEBUG_HEAP
- void *padding_start;
- size_t padding_size;
- #endif
- };
- static void dump_free_chunk(struct free_heap_chunk *chunk)
- {
- dprintf(INFO, "\t\tbase %p, end 0x%lx, len 0x%zx\n", chunk, (vaddr_t)chunk + chunk->len, chunk->len);
- }
- static void heap_dump(void)
- {
- dprintf(INFO, "Heap dump:\n");
- dprintf(INFO, "\tbase %p, len 0x%zx\n", theheap.base, theheap.len);
- dprintf(INFO, "\tfree list:\n");
- struct free_heap_chunk *chunk;
- list_for_every_entry(&theheap.free_list, chunk, struct free_heap_chunk, node) {
- dump_free_chunk(chunk);
- }
- }
- static void heap_test(void)
- {
- void *ptr[16];
- ptr[0] = heap_alloc(8, 0);
- ptr[1] = heap_alloc(32, 0);
- ptr[2] = heap_alloc(7, 0);
- ptr[3] = heap_alloc(0, 0);
- ptr[4] = heap_alloc(98713, 0);
- ptr[5] = heap_alloc(16, 0);
- heap_free(ptr[5]);
- heap_free(ptr[1]);
- heap_free(ptr[3]);
- heap_free(ptr[0]);
- heap_free(ptr[4]);
- heap_free(ptr[2]);
- heap_dump();
- int i;
- for (i=0; i < 16; i++)
- ptr[i] = 0;
- for (i=0; i < 32768; i++) {
- unsigned int index = (unsigned int)rand() % 16;
- if ((i % (16*1024)) == 0)
- printf("pass %d\n", i);
- // printf("index 0x%x\n", index);
- if (ptr[index]) {
- // printf("freeing ptr[0x%x] = %p\n", index, ptr[index]);
- heap_free(ptr[index]);
- ptr[index] = 0;
- }
- unsigned int align = 1 << ((unsigned int)rand() % 8);
- ptr[index] = heap_alloc((unsigned int)rand() % 32768, align);
- // printf("ptr[0x%x] = %p, align 0x%x\n", index, ptr[index], align);
- DEBUG_ASSERT(((addr_t)ptr[index] % align) == 0);
- // heap_dump();
- }
- for (i=0; i < 16; i++) {
- if (ptr[i])
- heap_free(ptr[i]);
- }
- heap_dump();
- }
- // try to insert this free chunk into the free list, consuming the chunk by merging it with
- // nearby ones if possible. Returns base of whatever chunk it became in the list.
- static struct free_heap_chunk *heap_insert_free_chunk(struct free_heap_chunk *chunk)
- {
- #if DEBUGLEVEL > INFO
- vaddr_t chunk_end = (vaddr_t)chunk + chunk->len;
- #endif // DEBUGLEVEL > INFO
- // dprintf("%s: chunk ptr %p, size 0x%lx, chunk_end 0x%x\n", __FUNCTION__, chunk, chunk->len, chunk_end);
- struct free_heap_chunk *next_chunk;
- struct free_heap_chunk *last_chunk;
- // walk through the list, finding the node to insert before
- list_for_every_entry(&theheap.free_list, next_chunk, struct free_heap_chunk, node) {
- if (chunk < next_chunk) {
- #if DEBUGLEVEL > INFO
- DEBUG_ASSERT(chunk_end <= (vaddr_t)next_chunk);
- #endif // DEBUGLEVEL > INFO
- list_add_before(&next_chunk->node, &chunk->node);
- goto try_merge;
- }
- }
- // walked off the end of the list, add it at the tail
- list_add_tail(&theheap.free_list, &chunk->node);
- // try to merge with the previous chunk
- try_merge:
- last_chunk = list_prev_type(&theheap.free_list, &chunk->node, struct free_heap_chunk, node);
- if (last_chunk) {
- if ((vaddr_t)last_chunk + last_chunk->len == (vaddr_t)chunk) {
- // easy, just extend the previous chunk
- last_chunk->len += chunk->len;
- // remove ourself from the list
- list_delete(&chunk->node);
- // set the chunk pointer to the newly extended chunk, in case
- // it needs to merge with the next chunk below
- chunk = last_chunk;
- }
- }
- // try to merge with the next chunk
- if (next_chunk) {
- if ((vaddr_t)chunk + chunk->len == (vaddr_t)next_chunk) {
- // extend our chunk
- chunk->len += next_chunk->len;
- // remove them from the list
- list_delete(&next_chunk->node);
- }
- }
- return chunk;
- }
- struct free_heap_chunk *heap_create_free_chunk(void *ptr, size_t len)
- {
- DEBUG_ASSERT((len % sizeof(void *)) == 0); // size must be aligned on pointer boundary
- #if DEBUG_HEAP
- memset(ptr, FREE_FILL, len);
- #endif
- struct free_heap_chunk *chunk = (struct free_heap_chunk *)ptr;
- chunk->len = len;
- return chunk;
- }
- void *heap_alloc(size_t size, unsigned int alignment)
- {
- void *ptr;
- #if DEBUG_HEAP
- size_t original_size = size;
- #endif
- LTRACEF("size %zd, align %d\n", size, alignment);
- // alignment must be power of 2
- if (alignment & (alignment - 1))
- return NULL;
- // we always put a size field + base pointer + magic in front of the allocation
- size += sizeof(struct alloc_struct_begin);
- #if DEBUG_HEAP
- size += PADDING_SIZE;
- #endif
- // make sure we allocate at least the size of a struct free_heap_chunk so that
- // when we free it, we can create a struct free_heap_chunk struct and stick it
- // in the spot
- if (size < sizeof(struct free_heap_chunk))
- size = sizeof(struct free_heap_chunk);
- // round up size to a multiple of native pointer size
- size = ROUNDUP(size, sizeof(void *));
- // deal with nonzero alignments
- if (alignment > 0) {
- if (alignment < 16)
- alignment = 16;
- // add alignment for worst case fit
- size += alignment;
- }
- // critical section
- enter_critical_section();
- // walk through the list
- ptr = NULL;
- struct free_heap_chunk *chunk;
- list_for_every_entry(&theheap.free_list, chunk, struct free_heap_chunk, node) {
- DEBUG_ASSERT((chunk->len % sizeof(void *)) == 0); // len should always be a multiple of pointer size
- // is it big enough to service our allocation?
- if (chunk->len >= size) {
- ptr = chunk;
- // remove it from the list
- struct list_node *next_node = list_next(&theheap.free_list, &chunk->node);
- list_delete(&chunk->node);
- if (chunk->len > size + sizeof(struct free_heap_chunk)) {
- // there's enough space in this chunk to create a new one after the allocation
- struct free_heap_chunk *newchunk = heap_create_free_chunk((uint8_t *)ptr + size, chunk->len - size);
- // truncate this chunk
- chunk->len -= chunk->len - size;
- // add the new one where chunk used to be
- if (next_node)
- list_add_before(next_node, &newchunk->node);
- else
- list_add_tail(&theheap.free_list, &newchunk->node);
- }
- // the allocated size is actually the length of this chunk, not the size requested
- DEBUG_ASSERT(chunk->len >= size);
- size = chunk->len;
- #if DEBUG_HEAP
- memset(ptr, ALLOC_FILL, size);
- #endif
- ptr = (void *)((addr_t)ptr + sizeof(struct alloc_struct_begin));
- // align the output if requested
- if (alignment > 0) {
- ptr = (void *)ROUNDUP((addr_t)ptr, alignment);
- }
- struct alloc_struct_begin *as = (struct alloc_struct_begin *)ptr;
- as--;
- as->magic = HEAP_MAGIC;
- as->ptr = (void *)chunk;
- as->size = size;
- #if DEBUG_HEAP
- as->padding_start = ((uint8_t *)ptr + original_size);
- as->padding_size = (((addr_t)chunk + size) - ((addr_t)ptr + original_size));
- // printf("padding start %p, size %u, chunk %p, size %u\n", as->padding_start, as->padding_size, chunk, size);
- memset(as->padding_start, PADDING_FILL, as->padding_size);
- #endif
- break;
- }
- }
- LTRACEF("returning ptr %p\n", ptr);
- // heap_dump();
- exit_critical_section();
- return ptr;
- }
- void *heap_realloc(void *ptr, size_t size)
- {
- void * tmp_ptr = NULL;
- size_t min_size;
- struct alloc_struct_begin *as = (struct alloc_struct_begin *)ptr;
- as--;
- if (size != 0) {
- tmp_ptr = heap_alloc(size, 0);
- if (ptr != NULL && tmp_ptr != NULL) {
- min_size = (size < as->size) ? size : as->size;
- memcpy(tmp_ptr, ptr, min_size);
- heap_free(ptr);
- }
- } else {
- if (ptr != NULL)
- heap_free(ptr);
- }
- return (tmp_ptr);
- }
- void heap_free(void *ptr)
- {
- if (ptr == 0)
- return;
- LTRACEF("ptr %p\n", ptr);
- // check for the old allocation structure
- struct alloc_struct_begin *as = (struct alloc_struct_begin *)ptr;
- as--;
- DEBUG_ASSERT(as->magic == HEAP_MAGIC);
- #if DEBUG_HEAP
- {
- uint i;
- uint8_t *pad = (uint8_t *)as->padding_start;
- for (i = 0; i < as->padding_size; i++) {
- if (pad[i] != PADDING_FILL) {
- printf("free at %p scribbled outside the lines:\n", ptr);
- hexdump(pad, as->padding_size);
- panic("die\n");
- }
- }
- }
- #endif
- LTRACEF("allocation was %zd bytes long at ptr %p\n", as->size, as->ptr);
- // looks good, create a free chunk and add it to the pool
- enter_critical_section();
- heap_insert_free_chunk(heap_create_free_chunk(as->ptr, as->size));
- exit_critical_section();
- // heap_dump();
- }
- void heap_init(void)
- {
- LTRACE_ENTRY;
- // set the heap range
- if (HEAP_USE_MBLOCK && !ram_console_is_abnormal_boot()) {
- uint32_t mblock_heap_start_u32;
- mblock_heap_start = mblock_reserve_ext(&g_boot_arg->mblock_info,
- (uint64_t)MBLOCK_HEAP_SIZE, PAGE_SIZE, (uint64_t)MBLOCK_HEAP_LIMIT,
- 0, "lk_heap_mblock");;
- mblock_heap_start_u32 = (uint32_t)mblock_heap_start;
- if (!mblock_heap_start) {
- dprintf(CRITICAL, "fail to mblock reserve!");
- /* fallback to lk system heap */
- theheap.base = (void *)HEAP_START;
- theheap.len = HEAP_LEN;
- } else {
- theheap.base = (void *)mblock_heap_start_u32;
- theheap.len = MBLOCK_HEAP_SIZE;
- }
- } else {
- theheap.base = (void *)HEAP_START;
- theheap.len = HEAP_LEN;
- }
- dprintf(INFO, "heap base %p size %zd bytes\n", theheap.base, theheap.len);
- // initialize the free list
- list_initialize(&theheap.free_list);
- // create an initial free chunk
- heap_insert_free_chunk(heap_create_free_chunk(theheap.base, theheap.len));
- #ifdef MTK_3LEVEL_PAGETABLE
- extern ld_tt_l2_info_t ld_tt_l2_info;
- ld_tt_l2_info.heap_init_done = 1;
- #endif
- // dump heap info
- // heap_dump();
- // dprintf(INFO, "running heap tests\n");
- // heap_test();
- }
- void heap_deinit(void)
- {
- if (HEAP_USE_MBLOCK) {
- if (mblock_heap_start)
- mblock_create(&g_boot_arg->mblock_info,
- &g_boot_arg->orig_dram_info,
- (uint64_t)(mblock_heap_start & 0xffffffff), (uint64_t)(MBLOCK_HEAP_SIZE & 0xffffffff));
- }
- }
- #if DEBUGLEVEL > 1
- #if WITH_LIB_CONSOLE
- #include <lib/console.h>
- static int cmd_heap(int argc, const cmd_args *argv);
- STATIC_COMMAND_START
- STATIC_COMMAND("heap", "heap debug commands", &cmd_heap)
- STATIC_COMMAND_END(heap);
- static int cmd_heap(int argc, const cmd_args *argv)
- {
- if (argc < 2) {
- printf("not enough arguments\n");
- return -1;
- }
- if (strcmp(argv[1].str, "info") == 0) {
- heap_dump();
- } else {
- printf("unrecognized command\n");
- return -1;
- }
- return 0;
- }
- #endif
- #endif
|