2
* Copyright (c) 2009 Martin Decky
3
* Copyright (c) 2009 Petr Tuma
6
* Redistribution and use in source and binary forms, with or without
7
* modification, are permitted provided that the following conditions
10
* - Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
* - Redistributions in binary form must reproduce the above copyright
13
* notice, this list of conditions and the following disclaimer in the
14
* documentation and/or other materials provided with the distribution.
15
* - The name of the author may not be used to endorse or promote products
16
* derived from this software without specific prior written permission.
18
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45
#include <adt/gcdlcm.h>
47
/* Magic used in heap headers. */
48
#define HEAP_BLOCK_HEAD_MAGIC 0xBEEF0101
50
/* Magic used in heap footers. */
51
#define HEAP_BLOCK_FOOT_MAGIC 0xBEEF0202
53
/** Allocation alignment (this also covers the alignment of fields
54
in the heap header and footer) */
58
* Either 4 * 256M on 32-bit architecures or 16 * 256M on 64-bit architectures
60
#define MAX_HEAP_SIZE (sizeof(uintptr_t) << 28)
65
#define STRUCT_OVERHEAD (sizeof(heap_block_head_t) + sizeof(heap_block_foot_t))
68
* Calculate real size of a heap block (with header and footer)
70
#define GROSS_SIZE(size) ((size) + STRUCT_OVERHEAD)
73
* Calculate net size of a heap block (without header and footer)
75
#define NET_SIZE(size) ((size) - STRUCT_OVERHEAD)
78
/** Header of a heap block
82
/* Size of the block (including header and footer) */
85
/* Indication of a free block */
88
/* A magic value to detect overwrite of heap header */
92
/** Footer of a heap block
96
/* Size of the block (including header and footer) */
99
/* A magic value to detect overwrite of heap footer */
103
/** Linker heap symbol */
106
/** Address of heap start */
107
static void *heap_start = 0;
109
/** Address of heap end */
110
static void *heap_end = 0;
112
/** Maximum heap size */
113
static size_t max_heap_size = (size_t) -1;
115
/** Current number of pages of heap area */
116
static size_t heap_pages = 0;
118
/** Initialize a heap block
120
* Fills in the structures related to a heap block.
122
* @param addr Address of the block.
123
* @param size Size of the block including the header and the footer.
124
* @param free Indication of a free block.
127
static void block_init(void *addr, size_t size, bool free)
129
/* Calculate the position of the header and the footer */
130
heap_block_head_t *head = (heap_block_head_t *) addr;
131
heap_block_foot_t *foot =
132
(heap_block_foot_t *) (addr + size - sizeof(heap_block_foot_t));
136
head->magic = HEAP_BLOCK_HEAD_MAGIC;
139
foot->magic = HEAP_BLOCK_FOOT_MAGIC;
142
/** Check a heap block
144
* Verifies that the structures related to a heap block still contain
145
* the magic constants. This helps detect heap corruption early on.
147
* @param addr Address of the block.
150
static void block_check(void *addr)
152
heap_block_head_t *head = (heap_block_head_t *) addr;
154
assert(head->magic == HEAP_BLOCK_HEAD_MAGIC);
156
heap_block_foot_t *foot =
157
(heap_block_foot_t *) (addr + head->size - sizeof(heap_block_foot_t));
159
assert(foot->magic == HEAP_BLOCK_FOOT_MAGIC);
160
assert(head->size == foot->size);
163
static bool grow_heap(size_t size)
168
size_t heap_size = (size_t) (heap_end - heap_start);
170
if ((max_heap_size != (size_t) -1) && (heap_size + size > max_heap_size))
173
size_t pages = (size - 1) / PAGE_SIZE + 1;
175
if (as_area_resize((void *) &_heap, (heap_pages + pages) * PAGE_SIZE, 0)
177
void *end = (void *) ALIGN_DOWN(((uintptr_t) &_heap) +
178
(heap_pages + pages) * PAGE_SIZE, BASE_ALIGN);
179
block_init(heap_end, end - heap_end, true);
188
static void shrink_heap(void)
193
/** Initialize the heap allocator
195
* Finds how much physical memory we have and creates
196
* the heap management structures that mark the whole
197
* physical memory as a single free block.
200
void __heap_init(void)
202
if (as_area_create((void *) &_heap, PAGE_SIZE,
203
AS_AREA_WRITE | AS_AREA_READ)) {
205
heap_start = (void *) ALIGN_UP((uintptr_t) &_heap, BASE_ALIGN);
207
(void *) ALIGN_DOWN(((uintptr_t) &_heap) + PAGE_SIZE, BASE_ALIGN);
209
/* Make the entire area one large block. */
210
block_init(heap_start, heap_end - heap_start, true);
214
uintptr_t get_max_heap_addr(void)
216
if (max_heap_size == (size_t) -1)
218
max((size_t) (heap_end - heap_start), MAX_HEAP_SIZE);
220
return ((uintptr_t) heap_start + max_heap_size);
223
static void split_mark(heap_block_head_t *cur, const size_t size)
225
assert(cur->size >= size);
227
/* See if we should split the block. */
228
size_t split_limit = GROSS_SIZE(size);
230
if (cur->size > split_limit) {
231
/* Block big enough -> split. */
232
void *next = ((void *) cur) + size;
233
block_init(next, cur->size - size, true);
234
block_init(cur, size, false);
236
/* Block too small -> use as is. */
241
/** Allocate a memory block
243
* @param size The size of the block to allocate.
244
* @param align Memory address alignment.
246
* @return the address of the block or NULL when not enough memory.
249
static void *malloc_internal(const size_t size, const size_t align)
254
size_t falign = lcm(align, BASE_ALIGN);
255
size_t real_size = GROSS_SIZE(ALIGN_UP(size, falign));
262
heap_block_head_t *cur = (heap_block_head_t *) heap_start;
264
while ((result == NULL) && ((void *) cur < heap_end)) {
267
/* Try to find a block that is free and large enough. */
268
if ((cur->free) && (cur->size >= real_size)) {
269
/* We have found a suitable block.
270
Check for alignment properties. */
271
void *addr = ((void *) cur) + sizeof(heap_block_head_t);
272
void *aligned = (void *) ALIGN_UP(addr, falign);
274
if (addr == aligned) {
275
/* Exact block start including alignment. */
276
split_mark(cur, real_size);
279
/* Block start has to be aligned */
280
size_t excess = (size_t) (aligned - addr);
282
if (cur->size >= real_size + excess) {
283
/* The current block is large enough to fit
284
data in including alignment */
285
if ((void *) cur > heap_start) {
286
/* There is a block before the current block.
287
This previous block can be enlarged to compensate
288
for the alignment excess */
289
heap_block_foot_t *prev_foot =
290
((void *) cur) - sizeof(heap_block_foot_t);
292
heap_block_head_t *prev_head =
293
(heap_block_head_t *) (((void *) cur) - prev_foot->size);
295
block_check(prev_head);
297
size_t reduced_size = cur->size - excess;
298
heap_block_head_t *next_head = ((void *) cur) + excess;
300
if ((!prev_head->free) && (excess >= STRUCT_OVERHEAD)) {
301
/* The previous block is not free and there is enough
302
space to fill in a new free block between the previous
304
block_init(cur, excess, true);
306
/* The previous block is free (thus there is no need to
307
induce additional fragmentation to the heap) or the
308
excess is small, thus just enlarge the previous block */
309
block_init(prev_head, prev_head->size + excess, prev_head->free);
312
block_init(next_head, reduced_size, true);
313
split_mark(next_head, real_size);
317
/* The current block is the first block on the heap.
318
We have to make sure that the alignment excess
319
is large enough to fit a new free block just
320
before the current block */
321
while (excess < STRUCT_OVERHEAD) {
326
/* Check for current block size again */
327
if (cur->size >= real_size + excess) {
328
size_t reduced_size = cur->size - excess;
329
cur = (heap_block_head_t *) (heap_start + excess);
331
block_init(heap_start, excess, true);
332
block_init(cur, reduced_size, true);
333
split_mark(cur, real_size);
341
/* Advance to the next block. */
342
cur = (heap_block_head_t *) (((void *) cur) + cur->size);
345
if ((result == NULL) && (!grown)) {
346
if (grow_heap(real_size)) {
355
void *malloc(const size_t size)
357
return malloc_internal(size, BASE_ALIGN);
360
void *memalign(const size_t align, const size_t size)
366
1 << (fnzb(max(sizeof(void *), align) - 1) + 1);
368
return malloc_internal(size, palign);
371
void *realloc(const void *addr, const size_t size)
376
/* Calculate the position of the header. */
377
heap_block_head_t *head =
378
(heap_block_head_t *) (addr - sizeof(heap_block_head_t));
380
assert((void *) head >= heap_start);
381
assert((void *) head < heap_end);
387
size_t real_size = GROSS_SIZE(ALIGN_UP(size, BASE_ALIGN));
388
size_t orig_size = head->size;
390
if (orig_size > real_size) {
392
if (orig_size - real_size >= STRUCT_OVERHEAD) {
393
/* Split the original block to a full block
394
and a tailing free block */
395
block_init((void *) head, real_size, false);
396
block_init((void *) head + real_size,
397
orig_size - real_size, true);
401
ptr = ((void *) head) + sizeof(heap_block_head_t);
403
/* Look at the next block. If it is free and the size is
404
sufficient then merge the two. */
405
heap_block_head_t *next_head =
406
(heap_block_head_t *) (((void *) head) + head->size);
408
if (((void *) next_head < heap_end) &&
409
(head->size + next_head->size >= real_size) &&
411
block_check(next_head);
412
block_init(head, head->size + next_head->size, false);
413
split_mark(head, real_size);
415
ptr = ((void *) head) + sizeof(heap_block_head_t);
419
memcpy(ptr, addr, NET_SIZE(orig_size));
428
/** Free a memory block
430
* @param addr The address of the block.
432
void free(const void *addr)
434
/* Calculate the position of the header. */
435
heap_block_head_t *head
436
= (heap_block_head_t *) (addr - sizeof(heap_block_head_t));
438
assert((void *) head >= heap_start);
439
assert((void *) head < heap_end);
444
/* Mark the block itself as free. */
447
/* Look at the next block. If it is free, merge the two. */
448
heap_block_head_t *next_head
449
= (heap_block_head_t *) (((void *) head) + head->size);
451
if ((void *) next_head < heap_end) {
452
block_check(next_head);
454
block_init(head, head->size + next_head->size, true);
457
/* Look at the previous block. If it is free, merge the two. */
458
if ((void *) head > heap_start) {
459
heap_block_foot_t *prev_foot =
460
(heap_block_foot_t *) (((void *) head) - sizeof(heap_block_foot_t));
462
heap_block_head_t *prev_head =
463
(heap_block_head_t *) (((void *) head) - prev_foot->size);
465
block_check(prev_head);
468
block_init(prev_head, prev_head->size + head->size, true);