heap.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. /*
  2. * Copyright (c) 2008-2009 Travis Geiselbrecht
  3. * Copyright (c) 2009 Corey Tabaka
  4. *
  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>
  28. #include <string.h>
  29. #include <kernel/thread.h>
  30. #include <lib/heap.h>
  31. #include <platform/ram_console.h>
  32. #include <dev/mrdump.h>
  33. #include <arch/arm/mmu.h>
  34. #include <ram_console_common.h>
  35. #include <platform/boot_mode.h>
  36. #include <mblock.h>
  37. #define LOCAL_TRACE 0
  38. static uint64_t mblock_heap_start;
  39. #define DEBUG_HEAP 0
  40. #define ALLOC_FILL 0x99
  41. #define FREE_FILL 0x77
  42. #define PADDING_FILL 0x55
  43. #define PADDING_SIZE 64
  44. #define ROUNDUP(a, b) (((a) + ((b)-1)) & ~((b)-1))
  45. #define HEAP_MAGIC 'HEAP'
  46. #if WITH_STATIC_HEAP
  47. #if !defined(HEAP_START) || !defined(HEAP_LEN)
  48. #error WITH_STATIC_HEAP set but no HEAP_START or HEAP_LEN defined
  49. #endif
  50. #else
  51. // end of the binary
  52. extern int _end;
  53. // end of memory
  54. extern int _heap_end;
  55. #define HEAP_START ((unsigned long)&_end)
  56. #define HEAP_LEN ((size_t)_heap_end - (size_t)&_end)
  57. #endif
  58. struct free_heap_chunk {
  59. struct list_node node;
  60. size_t len;
  61. };
  62. struct heap {
  63. void *base;
  64. size_t len;
  65. struct list_node free_list;
  66. };
  67. // heap static vars
  68. static struct heap theheap;
  69. // structure placed at the beginning every allocation
  70. struct alloc_struct_begin {
  71. unsigned int magic;
  72. void *ptr;
  73. size_t size;
  74. #if DEBUG_HEAP
  75. void *padding_start;
  76. size_t padding_size;
  77. #endif
  78. };
  79. static void dump_free_chunk(struct free_heap_chunk *chunk)
  80. {
  81. dprintf(INFO, "\t\tbase %p, end 0x%lx, len 0x%zx\n", chunk, (vaddr_t)chunk + chunk->len, chunk->len);
  82. }
  83. static void heap_dump(void)
  84. {
  85. dprintf(INFO, "Heap dump:\n");
  86. dprintf(INFO, "\tbase %p, len 0x%zx\n", theheap.base, theheap.len);
  87. dprintf(INFO, "\tfree list:\n");
  88. struct free_heap_chunk *chunk;
  89. list_for_every_entry(&theheap.free_list, chunk, struct free_heap_chunk, node) {
  90. dump_free_chunk(chunk);
  91. }
  92. }
  93. static void heap_test(void)
  94. {
  95. void *ptr[16];
  96. ptr[0] = heap_alloc(8, 0);
  97. ptr[1] = heap_alloc(32, 0);
  98. ptr[2] = heap_alloc(7, 0);
  99. ptr[3] = heap_alloc(0, 0);
  100. ptr[4] = heap_alloc(98713, 0);
  101. ptr[5] = heap_alloc(16, 0);
  102. heap_free(ptr[5]);
  103. heap_free(ptr[1]);
  104. heap_free(ptr[3]);
  105. heap_free(ptr[0]);
  106. heap_free(ptr[4]);
  107. heap_free(ptr[2]);
  108. heap_dump();
  109. int i;
  110. for (i=0; i < 16; i++)
  111. ptr[i] = 0;
  112. for (i=0; i < 32768; i++) {
  113. unsigned int index = (unsigned int)rand() % 16;
  114. if ((i % (16*1024)) == 0)
  115. printf("pass %d\n", i);
  116. // printf("index 0x%x\n", index);
  117. if (ptr[index]) {
  118. // printf("freeing ptr[0x%x] = %p\n", index, ptr[index]);
  119. heap_free(ptr[index]);
  120. ptr[index] = 0;
  121. }
  122. unsigned int align = 1 << ((unsigned int)rand() % 8);
  123. ptr[index] = heap_alloc((unsigned int)rand() % 32768, align);
  124. // printf("ptr[0x%x] = %p, align 0x%x\n", index, ptr[index], align);
  125. DEBUG_ASSERT(((addr_t)ptr[index] % align) == 0);
  126. // heap_dump();
  127. }
  128. for (i=0; i < 16; i++) {
  129. if (ptr[i])
  130. heap_free(ptr[i]);
  131. }
  132. heap_dump();
  133. }
  134. // try to insert this free chunk into the free list, consuming the chunk by merging it with
  135. // nearby ones if possible. Returns base of whatever chunk it became in the list.
  136. static struct free_heap_chunk *heap_insert_free_chunk(struct free_heap_chunk *chunk)
  137. {
  138. #if DEBUGLEVEL > INFO
  139. vaddr_t chunk_end = (vaddr_t)chunk + chunk->len;
  140. #endif // DEBUGLEVEL > INFO
  141. // dprintf("%s: chunk ptr %p, size 0x%lx, chunk_end 0x%x\n", __FUNCTION__, chunk, chunk->len, chunk_end);
  142. struct free_heap_chunk *next_chunk;
  143. struct free_heap_chunk *last_chunk;
  144. // walk through the list, finding the node to insert before
  145. list_for_every_entry(&theheap.free_list, next_chunk, struct free_heap_chunk, node) {
  146. if (chunk < next_chunk) {
  147. #if DEBUGLEVEL > INFO
  148. DEBUG_ASSERT(chunk_end <= (vaddr_t)next_chunk);
  149. #endif // DEBUGLEVEL > INFO
  150. list_add_before(&next_chunk->node, &chunk->node);
  151. goto try_merge;
  152. }
  153. }
  154. // walked off the end of the list, add it at the tail
  155. list_add_tail(&theheap.free_list, &chunk->node);
  156. // try to merge with the previous chunk
  157. try_merge:
  158. last_chunk = list_prev_type(&theheap.free_list, &chunk->node, struct free_heap_chunk, node);
  159. if (last_chunk) {
  160. if ((vaddr_t)last_chunk + last_chunk->len == (vaddr_t)chunk) {
  161. // easy, just extend the previous chunk
  162. last_chunk->len += chunk->len;
  163. // remove ourself from the list
  164. list_delete(&chunk->node);
  165. // set the chunk pointer to the newly extended chunk, in case
  166. // it needs to merge with the next chunk below
  167. chunk = last_chunk;
  168. }
  169. }
  170. // try to merge with the next chunk
  171. if (next_chunk) {
  172. if ((vaddr_t)chunk + chunk->len == (vaddr_t)next_chunk) {
  173. // extend our chunk
  174. chunk->len += next_chunk->len;
  175. // remove them from the list
  176. list_delete(&next_chunk->node);
  177. }
  178. }
  179. return chunk;
  180. }
  181. struct free_heap_chunk *heap_create_free_chunk(void *ptr, size_t len)
  182. {
  183. DEBUG_ASSERT((len % sizeof(void *)) == 0); // size must be aligned on pointer boundary
  184. #if DEBUG_HEAP
  185. memset(ptr, FREE_FILL, len);
  186. #endif
  187. struct free_heap_chunk *chunk = (struct free_heap_chunk *)ptr;
  188. chunk->len = len;
  189. return chunk;
  190. }
  191. void *heap_alloc(size_t size, unsigned int alignment)
  192. {
  193. void *ptr;
  194. #if DEBUG_HEAP
  195. size_t original_size = size;
  196. #endif
  197. LTRACEF("size %zd, align %d\n", size, alignment);
  198. // alignment must be power of 2
  199. if (alignment & (alignment - 1))
  200. return NULL;
  201. // we always put a size field + base pointer + magic in front of the allocation
  202. size += sizeof(struct alloc_struct_begin);
  203. #if DEBUG_HEAP
  204. size += PADDING_SIZE;
  205. #endif
  206. // make sure we allocate at least the size of a struct free_heap_chunk so that
  207. // when we free it, we can create a struct free_heap_chunk struct and stick it
  208. // in the spot
  209. if (size < sizeof(struct free_heap_chunk))
  210. size = sizeof(struct free_heap_chunk);
  211. // round up size to a multiple of native pointer size
  212. size = ROUNDUP(size, sizeof(void *));
  213. // deal with nonzero alignments
  214. if (alignment > 0) {
  215. if (alignment < 16)
  216. alignment = 16;
  217. // add alignment for worst case fit
  218. size += alignment;
  219. }
  220. // critical section
  221. enter_critical_section();
  222. // walk through the list
  223. ptr = NULL;
  224. struct free_heap_chunk *chunk;
  225. list_for_every_entry(&theheap.free_list, chunk, struct free_heap_chunk, node) {
  226. DEBUG_ASSERT((chunk->len % sizeof(void *)) == 0); // len should always be a multiple of pointer size
  227. // is it big enough to service our allocation?
  228. if (chunk->len >= size) {
  229. ptr = chunk;
  230. // remove it from the list
  231. struct list_node *next_node = list_next(&theheap.free_list, &chunk->node);
  232. list_delete(&chunk->node);
  233. if (chunk->len > size + sizeof(struct free_heap_chunk)) {
  234. // there's enough space in this chunk to create a new one after the allocation
  235. struct free_heap_chunk *newchunk = heap_create_free_chunk((uint8_t *)ptr + size, chunk->len - size);
  236. // truncate this chunk
  237. chunk->len -= chunk->len - size;
  238. // add the new one where chunk used to be
  239. if (next_node)
  240. list_add_before(next_node, &newchunk->node);
  241. else
  242. list_add_tail(&theheap.free_list, &newchunk->node);
  243. }
  244. // the allocated size is actually the length of this chunk, not the size requested
  245. DEBUG_ASSERT(chunk->len >= size);
  246. size = chunk->len;
  247. #if DEBUG_HEAP
  248. memset(ptr, ALLOC_FILL, size);
  249. #endif
  250. ptr = (void *)((addr_t)ptr + sizeof(struct alloc_struct_begin));
  251. // align the output if requested
  252. if (alignment > 0) {
  253. ptr = (void *)ROUNDUP((addr_t)ptr, alignment);
  254. }
  255. struct alloc_struct_begin *as = (struct alloc_struct_begin *)ptr;
  256. as--;
  257. as->magic = HEAP_MAGIC;
  258. as->ptr = (void *)chunk;
  259. as->size = size;
  260. #if DEBUG_HEAP
  261. as->padding_start = ((uint8_t *)ptr + original_size);
  262. as->padding_size = (((addr_t)chunk + size) - ((addr_t)ptr + original_size));
  263. // printf("padding start %p, size %u, chunk %p, size %u\n", as->padding_start, as->padding_size, chunk, size);
  264. memset(as->padding_start, PADDING_FILL, as->padding_size);
  265. #endif
  266. break;
  267. }
  268. }
  269. LTRACEF("returning ptr %p\n", ptr);
  270. // heap_dump();
  271. exit_critical_section();
  272. return ptr;
  273. }
  274. void *heap_realloc(void *ptr, size_t size)
  275. {
  276. void * tmp_ptr = NULL;
  277. size_t min_size;
  278. struct alloc_struct_begin *as = (struct alloc_struct_begin *)ptr;
  279. as--;
  280. if (size != 0) {
  281. tmp_ptr = heap_alloc(size, 0);
  282. if (ptr != NULL && tmp_ptr != NULL) {
  283. min_size = (size < as->size) ? size : as->size;
  284. memcpy(tmp_ptr, ptr, min_size);
  285. heap_free(ptr);
  286. }
  287. } else {
  288. if (ptr != NULL)
  289. heap_free(ptr);
  290. }
  291. return (tmp_ptr);
  292. }
  293. void heap_free(void *ptr)
  294. {
  295. if (ptr == 0)
  296. return;
  297. LTRACEF("ptr %p\n", ptr);
  298. // check for the old allocation structure
  299. struct alloc_struct_begin *as = (struct alloc_struct_begin *)ptr;
  300. as--;
  301. DEBUG_ASSERT(as->magic == HEAP_MAGIC);
  302. #if DEBUG_HEAP
  303. {
  304. uint i;
  305. uint8_t *pad = (uint8_t *)as->padding_start;
  306. for (i = 0; i < as->padding_size; i++) {
  307. if (pad[i] != PADDING_FILL) {
  308. printf("free at %p scribbled outside the lines:\n", ptr);
  309. hexdump(pad, as->padding_size);
  310. panic("die\n");
  311. }
  312. }
  313. }
  314. #endif
  315. LTRACEF("allocation was %zd bytes long at ptr %p\n", as->size, as->ptr);
  316. // looks good, create a free chunk and add it to the pool
  317. enter_critical_section();
  318. heap_insert_free_chunk(heap_create_free_chunk(as->ptr, as->size));
  319. exit_critical_section();
  320. // heap_dump();
  321. }
  322. void heap_init(void)
  323. {
  324. LTRACE_ENTRY;
  325. // set the heap range
  326. if (HEAP_USE_MBLOCK && !ram_console_is_abnormal_boot()) {
  327. uint32_t mblock_heap_start_u32;
  328. mblock_heap_start = mblock_reserve_ext(&g_boot_arg->mblock_info,
  329. (uint64_t)MBLOCK_HEAP_SIZE, PAGE_SIZE, (uint64_t)MBLOCK_HEAP_LIMIT,
  330. 0, "lk_heap_mblock");;
  331. mblock_heap_start_u32 = (uint32_t)mblock_heap_start;
  332. if (!mblock_heap_start) {
  333. dprintf(CRITICAL, "fail to mblock reserve!");
  334. /* fallback to lk system heap */
  335. theheap.base = (void *)HEAP_START;
  336. theheap.len = HEAP_LEN;
  337. } else {
  338. theheap.base = (void *)mblock_heap_start_u32;
  339. theheap.len = MBLOCK_HEAP_SIZE;
  340. }
  341. } else {
  342. theheap.base = (void *)HEAP_START;
  343. theheap.len = HEAP_LEN;
  344. }
  345. dprintf(INFO, "heap base %p size %zd bytes\n", theheap.base, theheap.len);
  346. // initialize the free list
  347. list_initialize(&theheap.free_list);
  348. // create an initial free chunk
  349. heap_insert_free_chunk(heap_create_free_chunk(theheap.base, theheap.len));
  350. #ifdef MTK_3LEVEL_PAGETABLE
  351. extern ld_tt_l2_info_t ld_tt_l2_info;
  352. ld_tt_l2_info.heap_init_done = 1;
  353. #endif
  354. // dump heap info
  355. // heap_dump();
  356. // dprintf(INFO, "running heap tests\n");
  357. // heap_test();
  358. }
  359. void heap_deinit(void)
  360. {
  361. if (HEAP_USE_MBLOCK) {
  362. if (mblock_heap_start)
  363. mblock_create(&g_boot_arg->mblock_info,
  364. &g_boot_arg->orig_dram_info,
  365. (uint64_t)(mblock_heap_start & 0xffffffff), (uint64_t)(MBLOCK_HEAP_SIZE & 0xffffffff));
  366. }
  367. }
  368. #if DEBUGLEVEL > 1
  369. #if WITH_LIB_CONSOLE
  370. #include <lib/console.h>
  371. static int cmd_heap(int argc, const cmd_args *argv);
  372. STATIC_COMMAND_START
  373. STATIC_COMMAND("heap", "heap debug commands", &cmd_heap)
  374. STATIC_COMMAND_END(heap);
  375. static int cmd_heap(int argc, const cmd_args *argv)
  376. {
  377. if (argc < 2) {
  378. printf("not enough arguments\n");
  379. return -1;
  380. }
  381. if (strcmp(argv[1].str, "info") == 0) {
  382. heap_dump();
  383. } else {
  384. printf("unrecognized command\n");
  385. return -1;
  386. }
  387. return 0;
  388. }
  389. #endif
  390. #endif