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