1 /* *********************************************************************
2 * Broadcom Common Firmware Environment (CFE)
4 * Local memory manager File: cfe_malloc.c
6 * This routine is used to manage memory allocated within the
7 * firmware. You give it a chunk of memory to manage, and then
8 * these routines manage suballocations from there.
10 * Author: Mitch Lichtenberg (mpl@broadcom.com)
12 *********************************************************************
14 * Copyright 2000,2001,2002,2003
15 * Broadcom Corporation. All rights reserved.
17 * This software is furnished under license and may be used and
18 * copied only in accordance with the following terms and
19 * conditions. Subject to these conditions, you may download,
20 * copy, install, use, modify and distribute modified or unmodified
21 * copies of this software in source and/or binary form. No title
22 * or ownership is transferred hereby.
24 * 1) Any source code used, modified or distributed must reproduce
25 * and retain this copyright notice and list of conditions
26 * as they appear in the source file.
28 * 2) No right is granted to use any trade name, trademark, or
29 * logo of Broadcom Corporation. The "Broadcom Corporation"
30 * name may not be used to endorse or promote products derived
31 * from this software without the prior written permission of
32 * Broadcom Corporation.
34 * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR
35 * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED
36 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
37 * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT
38 * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN
39 * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT,
40 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
41 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
42 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
43 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
44 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
45 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF
46 * THE POSSIBILITY OF SUCH DAMAGE.
47 ********************************************************************* */
55 #include "lib_types.h"
56 #include "lib_printf.h"
57 #include "lib_malloc.h"
60 /* *********************************************************************
62 ********************************************************************* */
64 #define MEMNODE_SEAL 0xFAAFA123 /* just some random constant */
67 /* *********************************************************************
69 ********************************************************************* */
71 typedef enum { memnode_free
= 0, memnode_alloc
} memnode_status_t
;
73 typedef struct memnode_s
{
75 struct memnode_s
*next
; /* pointer to next node */
76 unsigned int length
; /* length of the entire data section */
77 memnode_status_t status
; /* alloc/free status */
78 unsigned char *data
; /* points to actual user data */
79 void *memnodeptr
; /* memnode back pointer (see comments) */
83 memnode_t
*root
; /* pointer to root node */
84 unsigned char *base
; /* base of memory region */
85 unsigned int length
; /* size of memory region */
88 #define memnode_data(t,m) (t) (((memnode_t *) (m))+1)
90 /* *********************************************************************
92 ********************************************************************* */
94 mempool_t kmempool
; /* default pool */
96 /* *********************************************************************
97 * kmeminit(pool,buffer,length)
99 * Initialize the memory manager, given a pointer to an area
100 * of memory and a size. This routine simply initializes the
101 * root node to be a single block of empty space.
104 * pool - pool pointer
105 * buffer - beginning of buffer area, must be pointer-aligned
106 * length - length of buffer area
110 ********************************************************************* */
113 void kmeminit(mempool_t
*pool
,unsigned char *buffer
,int length
)
115 pool
->root
= (memnode_t
*) buffer
;
116 pool
->root
->seal
= MEMNODE_SEAL
;
117 pool
->root
->length
= length
- sizeof(memnode_t
);
118 pool
->root
->data
= memnode_data(unsigned char *,pool
->root
);
119 pool
->root
->status
= memnode_free
;
120 pool
->root
->next
= NULL
;
123 pool
->length
= length
;
127 /* *********************************************************************
130 * Returns the base address of the specified memory pool
133 * pool - pool pointer
136 * pointer to beginning of pool's memory
137 ********************************************************************* */
138 void *kmempoolbase(mempool_t
*pool
)
143 /* *********************************************************************
146 * Returns the total size of the specified memory pool
149 * pool - pool pointer
152 * size of pool in bytes
153 ********************************************************************* */
155 int kmempoolsize(mempool_t
*pool
)
160 /* *********************************************************************
163 * Compact the memory blocks, coalescing consectutive free blocks
167 * pool - pool descriptor
171 ********************************************************************* */
173 static void kmemcompact(mempool_t
*pool
)
181 for (m
= pool
->root
; m
; m
= m
->next
) {
183 /* Check seal to be sure that we're doing ok */
185 if (m
->seal
!= MEMNODE_SEAL
) {
187 printf("Memory list corrupted!\n");
193 * If we're not on the last block and both this
194 * block and the next one are free, combine them
198 (m
->status
== memnode_free
) &&
199 (m
->next
->status
== memnode_free
)) {
200 m
->length
+= sizeof(memnode_t
) + m
->next
->length
;
202 m
->next
= m
->next
->next
;
206 /* Keep going till we make a pass without doing anything. */
208 } while (compacted
> 0);
212 /* *********************************************************************
215 * Return some memory to the pool.
218 * ptr - pointer to something allocated via kmalloc()
222 ********************************************************************* */
224 void kfree(mempool_t
*pool
,void *ptr
)
229 if (((unsigned char *) ptr
< pool
->base
) ||
230 ((unsigned char *) ptr
>= (pool
->base
+pool
->length
))) {
232 printf("Pointer %08X does not belong to pool %08X\n",ptr
,pool
);
237 backptr
= (memnode_t
**) (((unsigned char *) ptr
) - sizeof(memnode_t
*));
240 if (m
->seal
!= MEMNODE_SEAL
) {
242 printf("Invalid node freed: %08X\n",m
);
247 m
->status
= memnode_free
;
253 void lib_outofmemory(void);
254 void lib_outofmemory(void)
256 xprintf("PANIC: out of memory!\n");
259 /* *********************************************************************
260 * kmalloc(pool,size,align)
262 * Allocate some memory from the pool.
265 * pool - pool structure
266 * size - size of item to allocate
267 * align - alignment (must be zero or a power of 2)
270 * pointer to data, or NULL if no memory left
271 ********************************************************************* */
273 void *kmalloc(mempool_t
*pool
,unsigned int size
,unsigned int align
)
279 uintptr_t realsize
= 0;
285 * Everything should be aligned by at least the
289 ptralign
= (uintptr_t) align
;
290 if (ptralign
< sizeof(void *)) ptralign
= sizeof(uint64_t);
293 * Everything should be at least a multiple of the
297 if (size
== 0) size
= sizeof(void *);
298 if (size
& (sizeof(void *)-1)) {
299 size
+= sizeof(void *);
300 size
&= ~(sizeof(void *)-1);
304 * Find a memnode at least big enough to hold the storage we
308 for (m
= pool
->root
; m
; m
= m
->next
) {
310 if (m
->status
== memnode_alloc
) continue;
313 * If we wanted a particular alignment, we will
314 * need to adjust the size.
317 daddr
= memnode_data(uintptr_t,m
);
319 if (daddr
& (ptralign
-1)) {
320 extra
= size
+ (ptralign
- (daddr
& (ptralign
-1)));
322 realsize
= size
+ extra
;
324 if (m
->length
< realsize
) continue;
329 * If m is null, there's no memory left.
338 * Otherwise, use this block. Calculate the address of the data
339 * to preserve the alignment.
342 if (daddr
& (ptralign
-1)) {
344 daddr
&= ~(ptralign
-1);
347 /* Mark this node as allocated. */
349 m
->data
= (unsigned char *) daddr
;
350 m
->status
= memnode_alloc
;
353 * Okay, this is ugly. Store a pointer to the original
354 * memnode just before what we've allocated. It's guaranteed
355 * to be aligned at least well enough for this pointer.
356 * If for some reason the memnode was already exactly
357 * aligned, backing up will put us inside the memnode
358 * structure itself... that's why the memnodeptr field
359 * is there, as a placeholder for this eventuality.
362 backptr
= (memnode_t
**) (m
->data
- sizeof(memnode_t
*));
366 * See if we need to split it.
367 * Don't bother to split if the resulting size will be
368 * less than MINBLKSIZE bytes
371 if (m
->length
- realsize
< MINBLKSIZE
) {
376 * Split this block. Align the address on a pointer-size
381 if (daddr
& (uintptr_t)(sizeof(void *)-1)) {
382 daddr
+= (uintptr_t)sizeof(void *);
383 daddr
&= ~(uintptr_t)(sizeof(void *)-1);
386 blkend
= memnode_data(uintptr_t,m
) + (uintptr_t)(m
->length
);
388 newm
= (memnode_t
*) daddr
;
390 newm
->next
= m
->next
;
391 m
->length
= (unsigned int) (daddr
- memnode_data(uintptr_t,m
));
393 m
->status
= memnode_alloc
;
394 newm
->seal
= MEMNODE_SEAL
;
395 newm
->data
= memnode_data(unsigned char *,newm
);
396 newm
->length
= (unsigned int) (blkend
- memnode_data(uintptr_t,newm
));
397 newm
->status
= memnode_free
;
403 int kmemstats(mempool_t
*pool
,memstats_t
*stats
)
409 stats
->mem_totalbytes
= pool
->length
;
410 stats
->mem_allocbytes
= 0;
411 stats
->mem_freebytes
= 0;
412 stats
->mem_allocnodes
= 0;
413 stats
->mem_freenodes
= 0;
414 stats
->mem_largest
= 0;
416 for (m
= pool
->root
; m
; m
= m
->next
) {
418 stats
->mem_allocnodes
++;
419 stats
->mem_allocbytes
+= m
->length
;
422 stats
->mem_freenodes
++;
423 stats
->mem_freebytes
+= m
->length
;
424 if (m
->length
> stats
->mem_largest
) {
425 stats
->mem_largest
= m
->length
;
429 daddr
= memnode_data(uintptr_t,m
);
430 if (m
->seal
!= MEMNODE_SEAL
) {
433 if (m
->next
&& ((daddr
+ m
->length
) != (uintptr_t) m
->next
)) {
436 if (m
->next
&& (m
->next
< m
)) {
439 if (m
->data
< (unsigned char *) m
) {
442 if (m
->status
== memnode_alloc
) {
443 backptr
= (memnode_t
**) (m
->data
- sizeof(void *));
454 /* *********************************************************************
457 * Check the consistency of the memory pool.
460 * pool - pool pointer
463 * 0 - pool is consistent
464 * -1 - pool is corrupt
465 ********************************************************************* */
468 int kmemchk(mempool_t
*pool
,int verbose
)
474 for (m
= pool
->root
; m
; m
= m
->next
) {
476 printf("%08X: Next=%08X Len=%5u %s Data=%08X ",
478 m
->status
? "alloc" : "free ",
481 daddr
= memnode_data(uintptr_t,m
);
482 if (m
->seal
!= MEMNODE_SEAL
) {
483 if (verbose
) printf("BadSeal ");
486 if (m
->next
&& (daddr
+ m
->length
!= (unsigned int) m
->next
)) {
487 if (verbose
) printf("BadLength ");
490 if (m
->next
&& (m
->next
< m
)) {
491 if (verbose
) printf("BadOrder ");
494 if (m
->data
< (unsigned char *) m
) {
495 if (verbose
) printf("BadData ");
498 if (m
->status
== memnode_alloc
) {
499 backptr
= (memnode_t
**) (m
->data
- sizeof(void *));
501 if (verbose
) printf("BadBackPtr ");
505 if (verbose
) printf("\n");
512 #define MEMSIZE 1024*1024
514 unsigned char *ptrs
[4096];
515 unsigned int sizes
[4096];
517 /* *********************************************************************
520 * Test program for the memory allocator
527 ********************************************************************* */
530 void main(int argc
,char *argv
[])
538 mempool_t
*pool
= &kmempool
;
540 mem
= malloc(MEMSIZE
);
541 kmeminit(pool
,mem
,MEMSIZE
);
548 if (items
== 4096) break;
549 size
= rand() % 1024;
550 ptrs
[items
] = kmalloc(pool
,size
,1<<(rand() & 7));
551 if (!ptrs
[items
]) break;
557 printf("%d items allocated, %d total bytes\n",items
,totalsize
);
559 if (kmemchk(pool
,0) < 0) {
564 /* Scramble the pointers */
574 sizes
[0] = sizes
[idx
];
580 /* now free a random number of elements */
582 nfree
= rand() % items
;
585 for (idx
= nfree
; idx
< items
; idx
++) {
586 kfree(pool
,ptrs
[idx
]);
587 totalsize
-= sizes
[idx
];
591 if (kmemchk(pool
,0) < 0) {
608 #endif /* TESTPROG */