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()
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 /* Find free block of size >= len */
127 static hdrtype_t
volatile *find_free_block(int len
, struct memory_type
*type
)
130 hdrtype_t
volatile *ptr
= (hdrtype_t
volatile *)type
->start
;
132 /* Align the size. */
133 len
= ALIGN_UP(len
, HDRSIZE
);
135 if (!len
|| len
> MAX_SIZE
)
138 /* Make sure the region is setup correctly. */
139 if (!HAS_MAGIC(*ptr
)) {
140 size_t size
= (type
->end
- type
->start
) - HDRSIZE
;
141 *ptr
= FREE_BLOCK(size
);
142 #if CONFIG(LP_DEBUG_MALLOC)
143 type
->magic_initialized
= 1;
144 type
->minimal_free
= size
;
148 /* Find some free space. */
151 int size
= SIZE(header
);
153 if (!HAS_MAGIC(header
) || size
== 0) {
154 printf("memory allocator panic. (%s%s)\n",
155 !HAS_MAGIC(header
) ? " no magic " : "",
156 size
== 0 ? " size=0 " : "");
160 if ((header
& FLAG_FREE
) && len
<= size
)
163 ptr
= (hdrtype_t
volatile *)((uintptr_t)ptr
+ HDRSIZE
+ size
);
165 } while (ptr
< (hdrtype_t
*) type
->end
);
167 /* Nothing available. */
171 /* Mark the block with length 'len' as used */
172 static void use_block(hdrtype_t
volatile *ptr
, int len
)
174 /* Align the size. */
175 len
= ALIGN_UP(len
, HDRSIZE
);
177 hdrtype_t
volatile *nptr
= (hdrtype_t
volatile *)
178 ((uintptr_t)ptr
+ HDRSIZE
+ len
);
179 int size
= SIZE(*ptr
);
180 int nsize
= size
- (HDRSIZE
+ len
);
183 * If there is still room in this block, then mark it as such otherwise
184 * account the whole space for that block.
187 /* Mark the block as used. */
188 *ptr
= USED_BLOCK(len
);
190 /* Create a new free block. */
191 *nptr
= FREE_BLOCK(nsize
);
193 /* Mark the block as used. */
194 *ptr
= USED_BLOCK(size
);
198 static void *alloc(int len
, struct memory_type
*type
)
200 hdrtype_t
volatile *ptr
= find_free_block(len
, type
);
206 return (void *)((uintptr_t)ptr
+ HDRSIZE
);
209 static void _consolidate(struct memory_type
*type
)
211 void *ptr
= type
->start
;
213 while (ptr
< type
->end
) {
215 hdrtype_t hdr
= *((hdrtype_t
*) ptr
);
216 unsigned int size
= 0;
219 ptr
+= HDRSIZE
+ SIZE(hdr
);
224 nptr
= ptr
+ HDRSIZE
+ SIZE(hdr
);
226 while (nptr
< type
->end
) {
227 hdrtype_t nhdr
= *((hdrtype_t
*) nptr
);
229 if (!(IS_FREE(nhdr
)))
232 size
+= SIZE(nhdr
) + HDRSIZE
;
234 *((hdrtype_t
*) nptr
) = 0;
236 nptr
+= (HDRSIZE
+ SIZE(nhdr
));
239 *((hdrtype_t
*) ptr
) = FREE_BLOCK(size
);
247 struct memory_type
*type
= heap
;
249 /* No action occurs on NULL. */
254 if (ptr
< type
->start
|| ptr
>= type
->end
) {
256 if (ptr
< type
->start
|| ptr
>= type
->end
)
260 if (free_aligned(ptr
, type
)) return;
263 hdr
= *((hdrtype_t
*) ptr
);
265 /* Not our header (we're probably poisoned). */
273 *((hdrtype_t
*) ptr
) = FREE_BLOCK(SIZE(hdr
));
277 void *malloc(size_t size
)
279 return alloc(size
, heap
);
282 void *dma_malloc(size_t size
)
284 return alloc(size
, dma
);
287 void *calloc(size_t nmemb
, size_t size
)
289 size_t total
= nmemb
* size
;
290 void *ptr
= alloc(total
, heap
);
293 memset(ptr
, 0, total
);
298 void *realloc(void *ptr
, size_t size
)
301 hdrtype_t
volatile *block
;
303 struct memory_type
*type
= heap
;
306 return alloc(size
, type
);
308 pptr
= ptr
- HDRSIZE
;
310 if (!HAS_MAGIC(*((hdrtype_t
*) pptr
)))
313 if (ptr
< type
->start
|| ptr
>= type
->end
)
316 /* Get the original size of the block. */
317 osize
= SIZE(*((hdrtype_t
*) pptr
));
320 * Free the memory to update the tables - this won't touch the actual
321 * memory, so we can still use it for the copy after we have
322 * reallocated the new space.
326 block
= find_free_block(size
, type
);
330 ret
= (void *)((uintptr_t)block
+ HDRSIZE
);
333 * If ret == ptr, then no copy is needed. Otherwise, move the memory to
334 * the new location, which might be before the old one and overlap since
335 * the free() above includes a _consolidate().
338 memmove(ret
, ptr
, osize
> size
? size
: osize
);
340 /* Mark the block as used. */
341 use_block(block
, size
);
346 struct align_region_t
348 /* If alignment is 0 then the region represents a large region which
349 * has no metadata for tracking subelements. */
351 /* start in memory, and size in bytes */
354 /* layout within a region:
355 - num_elements bytes, 0: free, 1: used, 2: used, combines with next
356 - padding to alignment
360 start_data points to the start of the data section
363 /* number of free blocks sized "alignment" */
365 struct align_region_t
*next
;
368 static inline int region_is_large(const struct align_region_t
*r
)
370 return r
->alignment
== 0;
373 static inline int addr_in_region(const struct align_region_t
*r
, void *addr
)
375 return ((addr
>= r
->start_data
) && (addr
< r
->start_data
+ r
->size
));
378 /* num_elements == 0 indicates a large aligned region instead of a smaller
379 * region comprised of alignment-sized chunks. */
380 static struct align_region_t
*allocate_region(int alignment
, int num_elements
,
381 size_t size
, struct memory_type
*type
)
383 struct align_region_t
*r
;
386 #if CONFIG(LP_DEBUG_MALLOC)
387 printf("%s(old align_regions=%p, alignment=%u, num_elements=%u, size=%zu)\n",
388 __func__
, type
->align_regions
, alignment
, num_elements
, size
);
391 r
= malloc(sizeof(*r
));
396 memset(r
, 0, sizeof(*r
));
398 if (num_elements
!= 0) {
399 r
->alignment
= alignment
;
400 r
->size
= num_elements
* alignment
;
401 r
->free
= num_elements
;
402 /* Allocate enough memory for alignment requirements and
403 * metadata for each chunk. */
404 extra_space
= num_elements
;
406 /* Large aligned allocation. Set alignment = 0. */
412 r
->start
= alloc(r
->size
+ alignment
+ extra_space
, type
);
414 if (r
->start
== NULL
) {
419 r
->start_data
= (void *)ALIGN_UP((uintptr_t)r
->start
+ extra_space
,
422 /* Clear any (if requested) metadata. */
423 memset(r
->start
, 0, extra_space
);
425 /* Link the region with the rest. */
426 r
->next
= type
->align_regions
;
427 type
->align_regions
= r
;
432 static void try_free_region(struct align_region_t
**prev_link
)
434 struct align_region_t
*r
= *prev_link
;
436 /* All large regions are immediately free-able. Non-large regions
437 * need to be checked for the fully freed state. */
438 if (!region_is_large(r
)) {
439 if (r
->free
!= r
->size
/ r
->alignment
)
443 /* Unlink region from link list. */
444 *prev_link
= r
->next
;
446 /* Free the data and metadata. */
451 static int free_aligned(void* addr
, struct memory_type
*type
)
453 struct align_region_t
**prev_link
= &type
->align_regions
;
455 while (*prev_link
!= NULL
)
457 if (!addr_in_region(*prev_link
, addr
)) {
458 prev_link
= &((*prev_link
)->next
);
462 if (region_is_large(*prev_link
)) {
463 try_free_region(prev_link
);
467 int i
= (addr
-(*prev_link
)->start_data
)/(*prev_link
)->alignment
;
468 u8
*meta
= (*prev_link
)->start
;
472 (*prev_link
)->free
++;
475 (*prev_link
)->free
++;
476 try_free_region(prev_link
);
482 static void *alloc_aligned(size_t align
, size_t size
, struct memory_type
*type
)
484 /* Define a large request to be 1024 bytes for either alignment or
485 * size of allocation. */
486 const size_t large_request
= 1024;
488 if (size
== 0) return 0;
489 if (type
->align_regions
== 0) {
490 type
->align_regions
= malloc(sizeof(struct align_region_t
));
491 if (type
->align_regions
== NULL
)
493 memset(type
->align_regions
, 0, sizeof(struct align_region_t
));
495 struct align_region_t
*reg
= type
->align_regions
;
497 if (size
>= large_request
|| align
>= large_request
) {
498 reg
= allocate_region(align
, 0, size
, type
);
501 return reg
->start_data
;
507 if ((reg
->alignment
== align
) && (reg
->free
>= (size
+ align
- 1)/align
))
509 #if CONFIG(LP_DEBUG_MALLOC)
510 printf(" found memalign region. %u free, %zu required\n", reg
->free
, (size
+ align
- 1)/align
);
518 #if CONFIG(LP_DEBUG_MALLOC)
519 printf(" need to allocate a new memalign region\n");
521 /* get align regions */
522 reg
= allocate_region(align
, large_request
/align
, size
, type
);
523 #if CONFIG(LP_DEBUG_MALLOC)
524 printf(" ... returned %p\n", reg
);
528 /* Nothing available. */
532 int i
, count
= 0, target
= (size
+align
-1)/align
;
533 for (i
= 0; i
< (reg
->size
/align
); i
++)
535 if (((u8
*)reg
->start
)[i
] == 0)
538 if (count
== target
) {
540 for (i
=0; i
<target
-1; i
++)
542 ((u8
*)reg
->start
)[count
+i
]=2;
544 ((u8
*)reg
->start
)[count
+target
-1]=1;
546 return reg
->start_data
+(align
*count
);
552 /* The free space in this region is fragmented,
553 so we will move on and try the next one: */
555 goto look_further
; // end condition is once a new region is allocated - it always has enough space
558 void *memalign(size_t align
, size_t size
)
560 return alloc_aligned(align
, size
, heap
);
563 void *dma_memalign(size_t align
, size_t size
)
565 return alloc_aligned(align
, size
, dma
);
568 /* This is for debugging purposes. */
569 #if CONFIG(LP_DEBUG_MALLOC)
570 void print_malloc_map(void)
572 struct memory_type
*type
= heap
;
580 while (ptr
< type
->end
) {
581 hdrtype_t hdr
= *((hdrtype_t
*) ptr
);
583 if (!HAS_MAGIC(hdr
)) {
584 if (type
->magic_initialized
)
585 printf("%s: Poisoned magic - we're toast\n", type
->name
);
587 printf("%s: No magic yet - going to initialize\n", type
->name
);
591 /* FIXME: Verify the size of the block. */
593 printf("%s %x: %s (%llx bytes)\n", type
->name
,
594 (unsigned int)(ptr
- type
->start
),
595 hdr
& FLAG_FREE
? "FREE" : "USED", SIZE(hdr
));
598 free_memory
+= SIZE(hdr
);
600 ptr
+= HDRSIZE
+ SIZE(hdr
);
603 if (free_memory
&& (type
->minimal_free
> free_memory
))
604 type
->minimal_free
= free_memory
;
605 printf("%s: Maximum memory consumption: %zu bytes\n", type
->name
,
606 (type
->end
- type
->start
) - HDRSIZE
- type
->minimal_free
);