1 /* 2 * Copyright 2003-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include <boot/heap.h> 8 #include <boot/platform.h> 9 #include <util/kernel_cpp.h> 10 11 #ifdef HEAP_TEST 12 # include <stdio.h> 13 #endif 14 #include <stdlib.h> 15 #include <string.h> 16 17 18 //#define TRACE_HEAP 19 #ifdef TRACE_HEAP 20 # define TRACE(format...) dprintf(format) 21 #else 22 # define TRACE(format...) do { } while (false) 23 #endif 24 25 26 /* This is a very simple malloc()/free() implementation - it only 27 * manages a free list. 28 * After heap_init() is called, all free memory is contained in one 29 * big chunk, the only entry in the free link list (which is a single 30 * linked list). 31 * When memory is allocated, the smallest free chunk that contains 32 * the requested size is split (or taken as a whole if it can't be 33 * splitted anymore), and it's lower half will be removed from the 34 * free list. 35 * The free list is ordered by size, starting with the smallest 36 * free chunk available. When a chunk is freed, it will be joint 37 * with its predecessor or successor, if possible. 38 * To ease list handling, the list anchor itself is a free chunk with 39 * size 0 that can't be allocated. 40 */ 41 42 #define DEBUG_ALLOCATIONS 43 // if defined, freed memory is filled with 0xcc 44 45 struct free_chunk { 46 uint32 size; 47 free_chunk *next; 48 49 uint32 Size() const; 50 free_chunk *Split(uint32 splitSize); 51 bool IsTouching(free_chunk *link); 52 free_chunk *Join(free_chunk *link); 53 void Remove(free_chunk *previous = NULL); 54 void Enqueue(); 55 56 void *AllocatedAddress() const; 57 static free_chunk *SetToAllocated(void *allocated); 58 }; 59 60 61 static void *sHeapBase; 62 static uint32 /*sHeapSize,*/ sMaxHeapSize, sAvailable; 63 static free_chunk sFreeAnchor; 64 65 66 /*! Returns the amount of bytes that can be allocated 67 in this chunk. 68 */ 69 uint32 70 free_chunk::Size() const 71 { 72 return size - sizeof(uint32); 73 } 74 75 76 /*! Splits the upper half at the requested location 77 and returns it. 78 */ 79 free_chunk * 80 free_chunk::Split(uint32 splitSize) 81 { 82 free_chunk *chunk = (free_chunk *)((uint8 *)this + sizeof(uint32) + splitSize); 83 chunk->size = size - splitSize - sizeof(uint32); 84 chunk->next = next; 85 86 size = splitSize + sizeof(uint32); 87 88 return chunk; 89 } 90 91 92 /*! Checks if the specified chunk touches this chunk, so 93 that they could be joined. 94 */ 95 bool 96 free_chunk::IsTouching(free_chunk *chunk) 97 { 98 return chunk 99 && (((uint8 *)this + size == (uint8 *)chunk) 100 || (uint8 *)chunk + chunk->size == (uint8 *)this); 101 } 102 103 104 /*! Joins the chunk to this chunk and returns the pointer 105 to the new chunk - which will either be one of the 106 two chunks. 107 Note, the chunks must be joinable, or else this method 108 doesn't work correctly. Use free_chunk::IsTouching() 109 to check if this method can be applied. 110 */ 111 free_chunk * 112 free_chunk::Join(free_chunk *chunk) 113 { 114 if (chunk < this) { 115 chunk->size += size; 116 chunk->next = next; 117 118 return chunk; 119 } 120 121 size += chunk->size; 122 next = chunk->next; 123 124 return this; 125 } 126 127 128 void 129 free_chunk::Remove(free_chunk *previous) 130 { 131 if (previous == NULL) { 132 // find the previous chunk in the list 133 free_chunk *chunk = sFreeAnchor.next; 134 135 while (chunk != NULL && chunk != this) { 136 previous = chunk; 137 chunk = chunk->next; 138 } 139 140 if (chunk == NULL) 141 panic("try to remove chunk that's not in list"); 142 } 143 144 previous->next = this->next; 145 this->next = NULL; 146 } 147 148 149 void 150 free_chunk::Enqueue() 151 { 152 free_chunk *chunk = sFreeAnchor.next, *last = &sFreeAnchor; 153 while (chunk && chunk->Size() < size) { 154 last = chunk; 155 chunk = chunk->next; 156 } 157 158 this->next = chunk; 159 last->next = this; 160 161 #ifdef DEBUG_ALLOCATIONS 162 memset((uint8*)this + sizeof(free_chunk), 0xcc, this->size - sizeof(free_chunk)); 163 #endif 164 } 165 166 167 void * 168 free_chunk::AllocatedAddress() const 169 { 170 return (void *)&next; 171 } 172 173 174 free_chunk * 175 free_chunk::SetToAllocated(void *allocated) 176 { 177 return (free_chunk *)((uint8 *)allocated - sizeof(uint32)); 178 } 179 180 181 // #pragma mark - 182 183 184 void 185 heap_release(stage2_args *args) 186 { 187 platform_release_heap(args, sHeapBase); 188 } 189 190 191 status_t 192 heap_init(stage2_args *args) 193 { 194 void *base, *top; 195 if (platform_init_heap(args, &base, &top) < B_OK) 196 return B_ERROR; 197 198 sHeapBase = base; 199 sMaxHeapSize = (uint8 *)top - (uint8 *)base; 200 sAvailable = sMaxHeapSize - sizeof(uint32); 201 202 // declare the whole heap as one chunk, and add it 203 // to the free list 204 205 free_chunk *chunk = (free_chunk *)base; 206 chunk->size = sMaxHeapSize; 207 chunk->next = NULL; 208 209 sFreeAnchor.size = 0; 210 sFreeAnchor.next = chunk; 211 212 return B_OK; 213 } 214 215 216 #if 0 217 char * 218 grow_heap(uint32 bytes) 219 { 220 char *start; 221 222 if (sHeapSize + bytes > sMaxHeapSize) 223 return NULL; 224 225 start = (char *)sHeapBase + sHeapSize; 226 memset(start, 0, bytes); 227 sHeapSize += bytes; 228 229 return start; 230 } 231 #endif 232 233 #ifdef HEAP_TEST 234 void 235 dump_chunks(void) 236 { 237 free_chunk *chunk = sFreeAnchor.next, *last = &sFreeAnchor; 238 while (chunk != NULL) { 239 last = chunk; 240 241 printf("\t%p: chunk size = %ld, end = %p, next = %p\n", chunk, chunk->size, (uint8 *)chunk + chunk->size, chunk->next); 242 chunk = chunk->next; 243 } 244 } 245 #endif 246 247 uint32 248 heap_available(void) 249 { 250 return sAvailable; 251 } 252 253 254 void * 255 malloc(size_t size) 256 { 257 if (sHeapBase == NULL || size == 0) 258 return NULL; 259 260 // align the size requirement to a 4 bytes boundary 261 size = (size + 3) & 0xfffffffc; 262 263 if (size > sAvailable) { 264 dprintf("malloc(): Out of memory!\n"); 265 return NULL; 266 } 267 268 free_chunk *chunk = sFreeAnchor.next, *last = &sFreeAnchor; 269 while (chunk && chunk->Size() < size) { 270 last = chunk; 271 chunk = chunk->next; 272 } 273 274 if (chunk == NULL) { 275 // could not find a free chunk as large as needed 276 dprintf("malloc(): Out of memory!\n"); 277 return NULL; 278 } 279 280 if (chunk->Size() > size + sizeof(free_chunk) + 4) { 281 // if this chunk is bigger than the requested size, 282 // we split it to form two chunks (with a minimal 283 // size of 4 allocatable bytes). 284 285 free_chunk *freeChunk = chunk->Split(size); 286 last->next = freeChunk; 287 288 // re-enqueue the free chunk at the correct position 289 freeChunk->Remove(last); 290 freeChunk->Enqueue(); 291 } else { 292 // remove the chunk from the free list 293 294 last->next = chunk->next; 295 } 296 297 sAvailable -= size + sizeof(uint32); 298 299 TRACE("malloc(%lu) -> %p\n", size, chunk->AllocatedAddress()); 300 return chunk->AllocatedAddress(); 301 } 302 303 304 void * 305 realloc(void *oldBuffer, size_t newSize) 306 { 307 // ToDo: improve this implementation! 308 309 if (newSize == 0) { 310 TRACE("realloc(%p, %lu) -> NULL\n", oldBuffer, newSize); 311 free(oldBuffer); 312 return NULL; 313 } 314 315 void *newBuffer = malloc(newSize); 316 if (newBuffer == NULL) 317 return NULL; 318 319 if (oldBuffer) { 320 free_chunk *oldChunk = free_chunk::SetToAllocated(oldBuffer); 321 322 if (newSize > oldChunk->size) 323 newSize = oldChunk->size; 324 325 memcpy(newBuffer, oldBuffer, newSize); 326 free(oldBuffer); 327 } 328 329 TRACE("realloc(%p, %lu) -> %p\n", oldBuffer, newSize, newBuffer); 330 return newBuffer; 331 } 332 333 334 void 335 free(void *allocated) 336 { 337 if (allocated == NULL) 338 return; 339 340 TRACE("free(%p)\n", allocated); 341 342 free_chunk *freedChunk = free_chunk::SetToAllocated(allocated); 343 344 #ifdef DEBUG_ALLOCATIONS 345 if (freedChunk->size > sMaxHeapSize) 346 panic("freed chunk %p clobbered (%lx)!\n", freedChunk, freedChunk->size); 347 { 348 free_chunk *chunk = sFreeAnchor.next; 349 while (chunk) { 350 if (chunk->size > sMaxHeapSize || freedChunk == chunk) 351 panic("invalid chunk in free list, or double free\n"); 352 chunk = chunk->next; 353 } 354 } 355 #endif 356 357 sAvailable += freedChunk->size; 358 359 // try to join the new free chunk with an existing one 360 // it may be joined with up to two chunks 361 362 free_chunk *chunk = sFreeAnchor.next, *last = &sFreeAnchor; 363 int32 joinCount = 0; 364 365 while (chunk) { 366 if (chunk->IsTouching(freedChunk)) { 367 // almost "insert" it into the list before joining 368 // because the next pointer is inherited by the chunk 369 freedChunk->next = chunk->next; 370 freedChunk = chunk->Join(freedChunk); 371 372 // remove the joined chunk from the list 373 last->next = freedChunk->next; 374 chunk = last; 375 376 if (++joinCount == 2) 377 break; 378 } 379 380 last = chunk; 381 chunk = chunk->next; 382 } 383 384 // enqueue the link at the right position; the 385 // free link queue is ordered by size 386 387 freedChunk->Enqueue(); 388 } 389 390