3 * Copyright (C) 2008 Advanced Micro Devices, Inc.
4 * Copyright (C) 2008-2010 coresystems GmbH
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * This is a classically weak malloc() implementation. We have a relatively
32 * small and static heap, so we take the easy route with an O(N) loop
33 * through the tree for every malloc() and free(). Obviously, this doesn't
34 * scale past a few hundred KB (if that).
36 * We're also susceptible to the usual buffer overrun poisoning, though the
37 * risk is within acceptable ranges for this implementation (don't overrun
38 * your buffers, kids!).
42 #include <libpayload.h>
48 struct align_region_t
* align_regions
;
49 #if CONFIG(LP_DEBUG_MALLOC)
50 int magic_initialized
;
56 extern char _heap
, _eheap
; /* Defined in the ldscript. */
58 static struct memory_type default_type
=
59 { (void *)&_heap
, (void *)&_eheap
, NULL
60 #if CONFIG(LP_DEBUG_MALLOC)
64 static struct memory_type
*const heap
= &default_type
;
65 static struct memory_type
*dma
= &default_type
;
67 typedef u64 hdrtype_t
;
68 #define HDRSIZE (sizeof(hdrtype_t))
70 #define SIZE_BITS ((HDRSIZE << 3) - 7)
71 #define MAGIC (((hdrtype_t)0x2a) << (SIZE_BITS + 1))
72 #define FLAG_FREE (((hdrtype_t)0x01) << (SIZE_BITS + 0))
73 #define MAX_SIZE ((((hdrtype_t)0x01) << SIZE_BITS) - 1)
75 #define SIZE(_h) ((_h) & MAX_SIZE)
77 #define _HEADER(_s, _f) ((hdrtype_t) (MAGIC | (_f) | ((_s) & MAX_SIZE)))
79 #define FREE_BLOCK(_s) _HEADER(_s, FLAG_FREE)
80 #define USED_BLOCK(_s) _HEADER(_s, 0)
82 #define IS_FREE(_h) (((_h) & (MAGIC | FLAG_FREE)) == (MAGIC | FLAG_FREE))
83 #define HAS_MAGIC(_h) (((_h) & MAGIC) == MAGIC)
85 static int free_aligned(void* addr
, struct memory_type
*type
);
86 void print_malloc_map(void);
88 void init_dma_memory(void *start
, u32 size
)
90 if (dma_initialized()) {
91 printf("ERROR: %s called twice!\n", __func__
);
96 * DMA memory might not be zeroed by coreboot on stage loading, so make
97 * sure we clear the magic cookie from last boot.
99 *(hdrtype_t
*)start
= 0;
101 dma
= malloc(sizeof(*dma
));
103 dma
->end
= start
+ size
;
104 dma
->align_regions
= NULL
;
106 #if CONFIG(LP_DEBUG_MALLOC)
107 dma
->minimal_free
= 0;
108 dma
->magic_initialized
= 0;
111 printf("Initialized cache-coherent DMA memory at [%p:%p]\n", start
, start
+ size
);
115 int dma_initialized(void)
120 /* For boards that don't initialize DMA we assume all locations are coherent */
121 int dma_coherent(const void *ptr
)
123 return !dma_initialized() || (dma
->start
<= ptr
&& dma
->end
> ptr
);
126 /* Get the range of memory that can be allocated by the dma allocator. */
127 void dma_allocator_range(void **start_out
, size_t *size_out
)
129 if (dma_initialized()) {
130 *start_out
= dma
->start
;
131 *size_out
= dma
->end
- dma
->start
;
138 /* Find free block of size >= len */
139 static hdrtype_t
volatile *find_free_block(int len
, struct memory_type
*type
)
142 hdrtype_t
volatile *ptr
= (hdrtype_t
volatile *)type
->start
;
144 /* Align the size. */
145 len
= ALIGN_UP(len
, HDRSIZE
);
147 if (!len
|| len
> MAX_SIZE
)
150 /* Make sure the region is setup correctly. */
151 if (!HAS_MAGIC(*ptr
)) {
152 size_t size
= (type
->end
- type
->start
) - HDRSIZE
;
153 *ptr
= FREE_BLOCK(size
);
154 #if CONFIG(LP_DEBUG_MALLOC)
155 type
->magic_initialized
= 1;
156 type
->minimal_free
= size
;
160 /* Find some free space. */
163 int size
= SIZE(header
);
165 if (!HAS_MAGIC(header
) || size
== 0) {
166 printf("memory allocator panic. (%s%s)\n",
167 !HAS_MAGIC(header
) ? " no magic " : "",
168 size
== 0 ? " size=0 " : "");
172 if ((header
& FLAG_FREE
) && len
<= size
)
175 ptr
= (hdrtype_t
volatile *)((uintptr_t)ptr
+ HDRSIZE
+ size
);
177 } while (ptr
< (hdrtype_t
*) type
->end
);
179 /* Nothing available. */
183 /* Mark the block with length 'len' as used */
184 static void use_block(hdrtype_t
volatile *ptr
, int len
)
186 /* Align the size. */
187 len
= ALIGN_UP(len
, HDRSIZE
);
189 hdrtype_t
volatile *nptr
= (hdrtype_t
volatile *)
190 ((uintptr_t)ptr
+ HDRSIZE
+ len
);
191 int size
= SIZE(*ptr
);
192 int nsize
= size
- (HDRSIZE
+ len
);
195 * If there is still room in this block, then mark it as such otherwise
196 * account the whole space for that block.
199 /* Mark the block as used. */
200 *ptr
= USED_BLOCK(len
);
202 /* Create a new free block. */
203 *nptr
= FREE_BLOCK(nsize
);
205 /* Mark the block as used. */
206 *ptr
= USED_BLOCK(size
);
210 static void *alloc(int len
, struct memory_type
*type
)
212 hdrtype_t
volatile *ptr
= find_free_block(len
, type
);
218 return (void *)((uintptr_t)ptr
+ HDRSIZE
);
221 static void _consolidate(struct memory_type
*type
)
223 void *ptr
= type
->start
;
225 while (ptr
< type
->end
) {
227 hdrtype_t hdr
= *((hdrtype_t
*) ptr
);
228 unsigned int size
= 0;
231 ptr
+= HDRSIZE
+ SIZE(hdr
);
236 nptr
= ptr
+ HDRSIZE
+ SIZE(hdr
);
238 while (nptr
< type
->end
) {
239 hdrtype_t nhdr
= *((hdrtype_t
*) nptr
);
241 if (!(IS_FREE(nhdr
)))
244 size
+= SIZE(nhdr
) + HDRSIZE
;
246 *((hdrtype_t
*) nptr
) = 0;
248 nptr
+= (HDRSIZE
+ SIZE(nhdr
));
251 *((hdrtype_t
*) ptr
) = FREE_BLOCK(size
);
259 struct memory_type
*type
= heap
;
261 /* No action occurs on NULL. */
266 if (ptr
< type
->start
|| ptr
>= type
->end
) {
268 if (ptr
< type
->start
|| ptr
>= type
->end
)
272 if (free_aligned(ptr
, type
)) return;
275 hdr
= *((hdrtype_t
*) ptr
);
277 /* Not our header (we're probably poisoned). */
285 *((hdrtype_t
*) ptr
) = FREE_BLOCK(SIZE(hdr
));
289 void *malloc(size_t size
)
291 return alloc(size
, heap
);
294 void *dma_malloc(size_t size
)
296 return alloc(size
, dma
);
299 void *calloc(size_t nmemb
, size_t size
)
301 size_t total
= nmemb
* size
;
302 void *ptr
= alloc(total
, heap
);
305 memset(ptr
, 0, total
);
310 void *realloc(void *ptr
, size_t size
)
313 hdrtype_t
volatile *block
;
315 struct memory_type
*type
= heap
;
318 return alloc(size
, type
);
320 pptr
= ptr
- HDRSIZE
;
322 if (!HAS_MAGIC(*((hdrtype_t
*) pptr
)))
325 if (ptr
< type
->start
|| ptr
>= type
->end
)
328 /* Get the original size of the block. */
329 osize
= SIZE(*((hdrtype_t
*) pptr
));
332 * Free the memory to update the tables - this won't touch the actual
333 * memory, so we can still use it for the copy after we have
334 * reallocated the new space.
338 block
= find_free_block(size
, type
);
342 ret
= (void *)((uintptr_t)block
+ HDRSIZE
);
345 * If ret == ptr, then no copy is needed. Otherwise, move the memory to
346 * the new location, which might be before the old one and overlap since
347 * the free() above includes a _consolidate().
350 memmove(ret
, ptr
, osize
> size
? size
: osize
);
352 /* Mark the block as used. */
353 use_block(block
, size
);
358 struct align_region_t
360 /* If alignment is 0 then the region represents a large region which
361 * has no metadata for tracking subelements. */
363 /* start in memory, and size in bytes */
366 /* layout within a region:
367 - num_elements bytes, 0: free, 1: used, 2: used, combines with next
368 - padding to alignment
372 start_data points to the start of the data section
375 /* number of free blocks sized "alignment" */
377 struct align_region_t
*next
;
380 static inline int region_is_large(const struct align_region_t
*r
)
382 return r
->alignment
== 0;
385 static inline int addr_in_region(const struct align_region_t
*r
, void *addr
)
387 return ((addr
>= r
->start_data
) && (addr
< r
->start_data
+ r
->size
));
390 /* num_elements == 0 indicates a large aligned region instead of a smaller
391 * region comprised of alignment-sized chunks. */
392 static struct align_region_t
*allocate_region(int alignment
, int num_elements
,
393 size_t size
, struct memory_type
*type
)
395 struct align_region_t
*r
;
398 #if CONFIG(LP_DEBUG_MALLOC)
399 printf("%s(old align_regions=%p, alignment=%u, num_elements=%u, size=%zu)\n",
400 __func__
, type
->align_regions
, alignment
, num_elements
, size
);
403 r
= malloc(sizeof(*r
));
408 memset(r
, 0, sizeof(*r
));
410 if (num_elements
!= 0) {
411 r
->alignment
= alignment
;
412 r
->size
= num_elements
* alignment
;
413 r
->free
= num_elements
;
414 /* Allocate enough memory for alignment requirements and
415 * metadata for each chunk. */
416 extra_space
= num_elements
;
418 /* Large aligned allocation. Set alignment = 0. */
424 r
->start
= alloc(r
->size
+ alignment
+ extra_space
, type
);
426 if (r
->start
== NULL
) {
431 r
->start_data
= (void *)ALIGN_UP((uintptr_t)r
->start
+ extra_space
,
434 /* Clear any (if requested) metadata. */
435 memset(r
->start
, 0, extra_space
);
437 /* Link the region with the rest. */
438 r
->next
= type
->align_regions
;
439 type
->align_regions
= r
;
444 static void try_free_region(struct align_region_t
**prev_link
)
446 struct align_region_t
*r
= *prev_link
;
448 /* All large regions are immediately free-able. Non-large regions
449 * need to be checked for the fully freed state. */
450 if (!region_is_large(r
)) {
451 if (r
->free
!= r
->size
/ r
->alignment
)
455 /* Unlink region from link list. */
456 *prev_link
= r
->next
;
458 /* Free the data and metadata. */
463 static int free_aligned(void* addr
, struct memory_type
*type
)
465 struct align_region_t
**prev_link
= &type
->align_regions
;
467 while (*prev_link
!= NULL
)
469 if (!addr_in_region(*prev_link
, addr
)) {
470 prev_link
= &((*prev_link
)->next
);
474 if (region_is_large(*prev_link
)) {
475 try_free_region(prev_link
);
479 int i
= (addr
-(*prev_link
)->start_data
)/(*prev_link
)->alignment
;
480 u8
*meta
= (*prev_link
)->start
;
484 (*prev_link
)->free
++;
487 (*prev_link
)->free
++;
488 try_free_region(prev_link
);
494 static void *alloc_aligned(size_t align
, size_t size
, struct memory_type
*type
)
496 /* Define a large request to be 1024 bytes for either alignment or
497 * size of allocation. */
498 const size_t large_request
= 1024;
500 if (size
== 0) return 0;
501 if (type
->align_regions
== 0) {
502 type
->align_regions
= malloc(sizeof(struct align_region_t
));
503 if (type
->align_regions
== NULL
)
505 memset(type
->align_regions
, 0, sizeof(struct align_region_t
));
507 struct align_region_t
*reg
= type
->align_regions
;
509 if (size
>= large_request
|| align
>= large_request
) {
510 reg
= allocate_region(align
, 0, size
, type
);
513 return reg
->start_data
;
519 if ((reg
->alignment
== align
) && (reg
->free
>= (size
+ align
- 1)/align
))
521 #if CONFIG(LP_DEBUG_MALLOC)
522 printf(" found memalign region. %u free, %zu required\n", reg
->free
, (size
+ align
- 1)/align
);
530 #if CONFIG(LP_DEBUG_MALLOC)
531 printf(" need to allocate a new memalign region\n");
533 /* get align regions */
534 reg
= allocate_region(align
, large_request
/align
, size
, type
);
535 #if CONFIG(LP_DEBUG_MALLOC)
536 printf(" ... returned %p\n", reg
);
540 /* Nothing available. */
544 int i
, count
= 0, target
= (size
+align
-1)/align
;
545 for (i
= 0; i
< (reg
->size
/align
); i
++)
547 if (((u8
*)reg
->start
)[i
] == 0)
550 if (count
== target
) {
552 for (i
=0; i
<target
-1; i
++)
554 ((u8
*)reg
->start
)[count
+i
]=2;
556 ((u8
*)reg
->start
)[count
+target
-1]=1;
558 return reg
->start_data
+(align
*count
);
564 /* The free space in this region is fragmented,
565 so we will move on and try the next one: */
567 goto look_further
; // end condition is once a new region is allocated - it always has enough space
570 void *memalign(size_t align
, size_t size
)
572 return alloc_aligned(align
, size
, heap
);
575 void *dma_memalign(size_t align
, size_t size
)
577 return alloc_aligned(align
, size
, dma
);
580 /* This is for debugging purposes. */
581 #if CONFIG(LP_DEBUG_MALLOC)
582 void print_malloc_map(void)
584 struct memory_type
*type
= heap
;
592 while (ptr
< type
->end
) {
593 hdrtype_t hdr
= *((hdrtype_t
*) ptr
);
595 if (!HAS_MAGIC(hdr
)) {
596 if (type
->magic_initialized
)
597 printf("%s: Poisoned magic - we're toast\n", type
->name
);
599 printf("%s: No magic yet - going to initialize\n", type
->name
);
603 /* FIXME: Verify the size of the block. */
605 printf("%s %x: %s (%llx bytes)\n", type
->name
,
606 (unsigned int)(ptr
- type
->start
),
607 hdr
& FLAG_FREE
? "FREE" : "USED", SIZE(hdr
));
610 free_memory
+= SIZE(hdr
);
612 ptr
+= HDRSIZE
+ SIZE(hdr
);
615 if (free_memory
&& (type
->minimal_free
> free_memory
))
616 type
->minimal_free
= free_memory
;
617 printf("%s: Maximum memory consumption: %zu bytes\n", type
->name
,
618 (type
->end
- type
->start
) - HDRSIZE
- type
->minimal_free
);