[Patch] TEST - prom-libc malloc
Paul Nasrat
pnasrat at redhat.com
Thu Apr 19 01:15:18 EST 2007
Przemek Iskra <sparky at pld-linux.org> sent in a patch to replace the
malloc in yaboot with one Ethan originally developed in prom-libc. It's
possible this might be of use in increasing the tftp filesize limit.
Various people have expressed interest in testing it out so I'm
resending a rediffed version against git HEAD to enable testing. This
probably will be destined for 1.3.15 not 1.3.14 though. Upping the tftp
limit and trying kernels/images over 6MB would be useful.
Paul
diff --git a/include/stdlib.h b/include/stdlib.h
index 0b5e99a..def230c 100644
--- a/include/stdlib.h
+++ b/include/stdlib.h
@@ -6,19 +6,30 @@
#define __STDLIB_H
#include "stdarg.h"
+#include "stddef.h"
-extern void malloc_init(void *bottom, unsigned long size);
-extern void malloc_dispose(void);
+extern int __alloc_freelist_init(void);
+//extern void malloc_init(void *bottom, unsigned long size);
+//extern void malloc_dispose(void);
extern void *malloc(unsigned int size);
extern void *realloc(void *ptr, unsigned int size);
extern void free (void *m);
-extern void mark (void **ptr);
-extern void release (void *ptr);
+//extern void mark (void **ptr);
+//extern void release (void *ptr);
extern int sprintf(char * buf, const char *fmt, ...);
extern int vsprintf(char *buf, const char *fmt, va_list args);
extern long simple_strtol(const char *cp,char **endp,unsigned int base);
#define strtol(x,y,z) simple_strtol(x,y,z)
+extern inline int atoi( const char *ptr );
+
+extern inline int
+atoi (const char *ptr)
+{
+ return (int) strtol (ptr, (char **) NULL, 10);
+}
+
+
#endif
diff --git a/lib/malloc.c b/lib/malloc.c
index 672bb3e..87cc7f5 100644
--- a/lib/malloc.c
+++ b/lib/malloc.c
@@ -21,87 +21,219 @@
#include "types.h"
#include "stddef.h"
+#include "string.h"
+
+#define MALLOC_DEBUG 1
/* Imported functions */
extern void prom_printf (char *fmt, ...);
+extern void *prom_claim (void *virt, unsigned int size, unsigned int align);
+
+/*
+ * memory allocation routines from prom-libc
+ */
+
+void free(void *memory);
-static char *malloc_ptr = 0;
-static char *malloc_top = 0;
-static char *last_alloc = 0;
+/*** bits/malloc.h ***/
+struct __alloc_area
+{
+ struct __alloc_area *next;
+ unsigned int length;
+ long long foo;
+};
+
+/*** __freelist.c ***/
+/* the list of free areas */
+struct __alloc_area *__freelist = NULL;
-void malloc_init(void *bottom, unsigned long size)
+/*** __alloc_freelist_init.c ***/
+int __alloc_freelist_init(void)
{
- malloc_ptr = bottom;
- malloc_top = bottom + size;
+ /* return if the freelist has already been set up. this will mean that
+ * one can call _init() to more than once without probs
+ */
+ if (__freelist)
+ return 0;
+
+ /* assign an initial area */
+ __freelist = (struct __alloc_area *) prom_claim(
+ (void *)MALLOCADDR, ((MALLOCSIZE / sizeof(struct __alloc_area))
+ * sizeof(struct __alloc_area)) , 0);
+ if ((void *)__freelist == (void *)-1)
+ return 1;
+ __freelist->next = NULL;
+ __freelist->length = (MALLOCSIZE / sizeof(struct __alloc_area)) - 1;
+ return 0;
}
-void malloc_dispose(void)
+
+/*** malloc.c ***/
+void *malloc(unsigned int bytes)
{
- malloc_ptr = 0;
- last_alloc = 0;
+ struct __alloc_area *previous;
+ struct __alloc_area *current;
+ unsigned int units;
+
+ /* __freelist is still NULL, set up the initial entry */
+ if (!__freelist)
+ __alloc_freelist_init();
+
+ /* determine the number of __alloc_area sized blocks needed to hold
+ * the number of bytes requested. needs to be an even number, otherwise
+ * the following header struct might be mis-aligned
+ */
+ units = (bytes + sizeof(struct __alloc_area) - 1) /
+ sizeof(struct __alloc_area);
+
+ previous = NULL;
+ for (current = __freelist; current; current = current->next) {
+ if (units < current->length + 1) {
+ /* make area smaller, to accommodate a new area of
+ * the required size, and its header
+ */
+ current->length -= units;
+ current->length -= 1;
+
+ /* move the pointer so that it points to the new area
+ * being set up
+ */
+ current += current->length + 1;
+ current->length = units;
+
+ /* return a pointer to the user that starts
+ * immediately after the area header
+ */
+ return (void *)(current + 1);
+
+ } else if (units == current->length) {
+ /* current area is exactly the right size */
+
+ /* unlink this area from the list */
+ if (previous)
+ previous->next = current->next;
+
+ return (void *)(current + 1);
+}
+ previous = current;
+ }
+ return NULL;
}
-void *malloc (unsigned int size)
+/*** realloc.c ***/
+void *realloc(void *old, unsigned int size)
{
- char *caddr;
+ struct __alloc_area *optr;
+ void *new;
+ unsigned int olen;
- if (!malloc_ptr)
+ if (old && !size) {
+ free(old);
return NULL;
- if ((malloc_ptr + size + sizeof(int)) > malloc_top) {
- prom_printf("malloc failed\n");
+ } else if (!old && !size)
return NULL;
+
+ if (!(new = malloc(size)))
+ return NULL;
+
+ if (old) {
+ optr = (struct __alloc_area *)old;
+ optr--;
+ olen = (optr->length * sizeof(struct __alloc_area));
+ memcpy(new, old, olen <= size ? olen : size);
+ free(old);
}
- *(int *)malloc_ptr = size;
- caddr = malloc_ptr + sizeof(int);
- malloc_ptr += size + sizeof(int);
- last_alloc = caddr;
- malloc_ptr = (char *) ((((unsigned int) malloc_ptr) + 3) & (~3));
- return caddr;
+ return new;
}
-void *realloc(void *ptr, unsigned int size)
+/*** free.c ***/
+/* join sequential areas in freelist */
+static void __alloc_freelist_join(void)
{
- char *caddr, *oaddr = ptr;
+ struct __alloc_area *previous, *current;
- if (!malloc_ptr)
- return NULL;
- if (oaddr == last_alloc) {
- if (oaddr + size > malloc_top) {
- prom_printf("realloc failed\n");
- return NULL;
- }
- *(int *)(oaddr - sizeof(int)) = size;
- malloc_ptr = oaddr + size;
- return oaddr;
+ previous = NULL;
+
+ /* loop through areas in the freelist */
+ for (current = __freelist; current; current = current->next) {
+ if (previous && previous + previous->length + 1 == current) {
+ /* current area starts immediately after the previous
+ * one, join them
+ */
+
+ /* increase previous area's length to include the
+ * current area's header and length
+ */
+ previous->length++;
+ previous->length += current->length;
+
+ /* take the current entry out of the list */
+ previous->next = current->next;
+ current = previous;
+ } else
+ previous = current;
}
- caddr = malloc(size);
- if (caddr != 0 && oaddr != 0)
- memcpy(caddr, oaddr, *(int *)(oaddr - sizeof(int)));
- return caddr;
}
-void free (void *m)
+void free(void *memory)
{
- if (!malloc_ptr)
+ struct __alloc_area *free;
+ struct __alloc_area *before;
+ struct __alloc_area *current;
+
+#ifdef MALLOC_DEBUG
+ /* check that memory isn't a NULL pointer (possibly from a failed
+ * malloc). this check is nice, but not compulsory
+ */
+ if (!memory) {
+ prom_printf("WARNING: attempt to free a NULL pointer\n");
return;
- if (m == last_alloc)
- malloc_ptr = (char *) last_alloc - sizeof(int);
}
+#endif /* MALLOC_DEBUG */
-void mark (void **ptr)
-{
- if (!malloc_ptr)
+ /* set free to point to the pointer supplied, move free one element so
+ * that it points to the actual header
+ */
+ free = (struct __alloc_area *)memory;
+ free--;
+
+#ifdef MALLOC_DEBUG
+ /* check that this pointer isn't already in the freelist, or a pointer
+ * to something that's inside something in the freelist. again, this
+ * check isn't essential for a non-broken program
+ */
+ for (current = __freelist; current; current = current->next)
+ if (free >= current && free < current + current->length) {
+ prom_printf("WARNING: memory area already in freelist\n");
return;
- *ptr = (void *) malloc_ptr;
}
+#endif /* MALLOC_DEBUG */
-void release (void *ptr)
-{
- if (!malloc_ptr)
- return;
- malloc_ptr = (char *) ptr;
+ /* free puts areas back in the freelist in order of the pointers, so
+ * that sequential areas can be joined later
+ */
+
+ /* try and find the area that immediately preceeds this one */
+ before = NULL;
+ for (current = __freelist; current && free > current;
+ current = current->next)
+ before = current;
+
+ if (before) {
+ /* put this area in just after the area before it */
+ free->next = before->next;
+ before->next = free;
+ } else {
+ /* nothing before this area, place at beginning of freelist */
+ free->next = __freelist;
+ __freelist = free;
+ }
+
+ /* join any sequential areas */
+ __alloc_freelist_join();
}
+// Should not be here
char *strdup(char const *str)
{
char *p = malloc(strlen(str) + 1);
diff --git a/second/yaboot.c b/second/yaboot.c
index 20296df..4af0129 100644
--- a/second/yaboot.c
+++ b/second/yaboot.c
@@ -194,6 +194,7 @@ yaboot_start (unsigned long r3, unsigned long r4, unsigned long r5)
/* Initialize OF interface */
prom_init ((prom_entry) r5);
+#if 0
/* Allocate some memory for malloc'ator */
malloc_base = prom_claim((void *)MALLOCADDR, MALLOCSIZE, 0);
if (malloc_base == (void *)-1) {
@@ -204,6 +205,13 @@ yaboot_start (unsigned long r3, unsigned long r4, unsigned long r5)
malloc_init(malloc_base, MALLOCSIZE);
DEBUG_F("Malloc buffer allocated at %p (%d bytes)\n",
malloc_base, MALLOCSIZE);
+#endif
+
+ if ( __alloc_freelist_init() ) {
+ prom_printf("Can't claim malloc buffer (%d bytes at 0x%08x)\n",
+ MALLOCSIZE, MALLOCADDR);
+ return -1;
+ }
/* A few useless DEBUG_F's */
DEBUG_F("reloc_offset : %ld (should be 0)\n", reloc_offset());
@@ -235,10 +243,8 @@ yaboot_start (unsigned long r3, unsigned long r4, unsigned long r5)
result = yaboot_main();
/* Get rid of malloc pool */
- malloc_dispose();
prom_release(malloc_base, MALLOCSIZE);
- DEBUG_F("Malloc buffer released. Exiting with code %d\n",
- result);
+ DEBUG_F("Malloc buffer released. Exiting with code %d\n", result);
/* Return to OF */
prom_exit();
More information about the Yaboot-devel
mailing list