Add a set of library routines to manage gross memory allocations.

This code uses an array in bss to store upto 32 entrys with merging
representing a range of memory below rmo_end (aka end of real mode
memory at 0).

To use this code, a platform would set rmo_end, call occupy_memory,
then then call init_malloc for fine grain allocation.

Also, an optional vmlinux_alloc.   

Signed-off-by: Milton Miller <[EMAIL PROTECTED]>
--- 
Fixed a bug when extending across multiple ranges.

Cleaned up documentation

Index: work.git/arch/powerpc/boot/memranges.c
===================================================================
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ work.git/arch/powerpc/boot/memranges.c      2007-07-10 03:48:29.000000000 
-0500
@@ -0,0 +1,242 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * Copyright IBM Corporation 2007
+ *
+ * Authors: Milton Miller <[EMAIL PROTECTED]>
+ */
+
+#include "ops.h"
+#include "stdio.h"
+#include "flatdevtree.h"
+#include "page.h"
+#include "types.h"
+
+extern char _start[], _end[];
+
+void *rmo_end;
+
+static struct {
+       void *start, *end;
+} ranges[32];
+static int num_ranges;
+
+/**
+ * add_occupied_range - mark a range as occupied
+ * @start: start of range pointer
+ * @end: end of range pointer
+ *
+ * Mark the range from @start to @end as occupied.
+ * Ignore anything above rmo_end.
+ */
+void add_occupied_range(void *start, void *end)
+{
+       int i, j;
+
+       if (start == end)
+               return;
+       if (start > rmo_end)
+               return;
+       if (end > rmo_end)
+               end = rmo_end;
+       if (start > end)
+               fatal("%s: BUG: start %p > end %p\n\r", __FUNCTION__,
+                               start, end);
+
+       printf("add %p %p: ", start, end);
+
+       for (i=0; i < num_ranges; i++)
+               if (start <= ranges[i].end)
+                       break;
+
+       /* extend and merge any overlapping ranges */
+       if (i < num_ranges && end >= ranges[i].start) {
+               ranges[i].start = min(start, ranges[i].start);
+               for (j=i; j < num_ranges; j++)
+                       if (end >= ranges[j].start)
+                               end = max(end, ranges[j].end);
+                       else
+                               break;
+               ranges[i].end = end;
+
+               if (j == i + 1) {
+                       printf("extending range %d to %p %p\n\r", i,
+                               ranges[i].start, ranges[i].end);
+               } else {
+                       printf("merged ranges %d to %d now %p %p\n\r", i, j,
+                               ranges[i].start, ranges[i].end);
+
+                       ++i;
+                       memmove(&ranges[i], &ranges[j],
+                               (num_ranges - j) * sizeof(ranges[0]));
+                       num_ranges -= j-i;
+               }
+       } else {
+               /* insert a new range */
+               if (num_ranges >= ARRAY_SIZE(ranges) - 1)
+                       fatal("Too many memory ranges to track\n");
+
+               printf("inserting range %d between %p and %p\n\r",
+                               i, i ? ranges[i-1].end : 0,
+                               i == num_ranges ? rmo_end : ranges[i].start);
+
+               memmove(&ranges[i+1], &ranges[i],
+                               (num_ranges - i) * sizeof(ranges[0]));
+               num_ranges++;
+
+               ranges[i].start = start;
+               ranges[i].end = end;
+       }
+}
+
+/**
+ * add_occupied_range_ulong - mark a range as occupied
+ * @start: start of block to occupy
+ * @end: start of block to occupy
+ *
+ * Call add_occupied_range() after casting to ulong @start and @end to
+ * void * pointers.
+ */
+void add_occupied_range_ulong(unsigned long start, unsigned long end)
+{
+       add_occupied_range((void *)start, (void *)end);
+}
+
+/**
+ * add_known_ranges - occupy some known regions
+ * @dt_blob: a flattend device tree to occupy, or NULL to skip
+ *
+ * call add_occupied_range() for the wrapper, loader supplied initrd,
+ * and, if not %NULL, the device tree blob @dt_blob and any reserved
+ * memory ranges therein.
+ */
+void add_known_ranges(struct boot_param_header *dt_blob)
+{
+       unsigned long long rstart, rlen, rend, *rsrv;
+
+       add_occupied_range(_start, _end);
+
+       add_occupied_range_ulong(loader_info.initrd_addr,
+               loader_info.initrd_addr + loader_info.initrd_size);
+
+       if (dt_blob == NULL)
+               return;
+
+       add_occupied_range(dt_blob, (void *)dt_blob + dt_blob->totalsize);
+
+       /* only support 8-byte reserve map.  Only care about < 4G */
+       rsrv = (void *)dt_blob + dt_blob->off_mem_rsvmap;
+       do {
+               rstart = *rsrv++;
+               rlen = *rsrv++;
+               rend = rstart + rlen;
+
+               if (rlen && rstart < UINT_MAX) {
+                       if (rend < UINT_MAX)
+                               add_occupied_range_ulong(rstart, rend);
+                       else
+                               add_occupied_range_ulong(rstart, UINT_MAX);
+               }
+       } while (rlen);
+}
+
+/**
+ * ranges_init_malloc - initialize malloc heap in a free memory range.
+ *
+ * Call simple_alloc_init using the largest gap between occupied ranges.
+ * Does not consider before the first or after the last range.
+ */
+void ranges_init_malloc(void)
+{
+       int i;
+       unsigned long size = 0;
+       void *heap_start, *heap_end;
+
+       /*
+        * Allow the beginning for the kernel and the end for
+        * other things the platform might want to have reserved.
+        */
+
+       heap_start = NULL;              /* avoid gcc warning */
+       for (i=1; i < num_ranges; i++) {
+               unsigned long newsize;
+
+               newsize = ranges[i].start - ranges[i-1].end;
+               if (newsize > size) {
+                       size = newsize;
+                       heap_start = ranges[i-1].end;
+               }
+       }
+
+       if (size < 4 * 1024 * 1024)
+               fatal("Can't find a sutiable gap (largest 0x%lx)", size);
+
+       printf("putting heap between %p and %p size 0x%lx\n\r", heap_start,
+                       heap_start + size, size);
+       heap_end = simple_alloc_init(heap_start, size * 7 / 8,
+                       PAGE_SIZE, /* max num alloc */ 4096);
+       if (heap_end > (heap_start + size))
+               fatal("heap alloc overflowed gap (%p)\n\r", heap_end);
+
+       add_occupied_range(heap_start, heap_end);
+}
+
+/**
+ * ranges_vmlinux_alloc - an optonal kernel allocator.
+ * @size: the image size of the kernel
+ *
+ * Searches for a location to put the kernel, then reserve that range
+ * and the area to which the kernel will relocate itself.   First try
+ * address %0.  If that is blocked by a previos call to add_occupied_range(),
+ * try malloc().  If that also fails search for free space between the
+ * occupied ranges or between the last range and rmo_end.
+ */
+void *ranges_vmlinux_alloc(unsigned long size)
+{
+       void *addr;
+       int i;
+
+       /* Assume _start to _end is occupied */
+       addr = (void *)0;
+       if (addr + size < ranges[0].start)
+               goto occupy;
+
+       addr = malloc(size);
+       if (addr)
+               goto out;
+
+       for (i=1; i < num_ranges; i++) {
+               if (size < ranges[i].start - ranges[i-1].end)
+                       goto occupy_range;
+       }
+       if (size < rmo_end - ranges[i-1].end)
+               goto occupy_range;
+
+       fatal("Unable to find a 0x%lx byte gap for the kernel\n", size);
+
+occupy_range:
+       addr = ranges[i-1].end;
+occupy:
+       add_occupied_range(addr, addr + size);
+out:
+       /*
+        * Assume the kernel will decompress to 0, but don't implicity
+        * create a new gap below the current first range.
+        */
+       if ((unsigned long)ranges[0].end < size)
+               add_occupied_range_ulong(0, size);
+
+       return addr;
+}
Index: work.git/arch/powerpc/boot/ops.h
===================================================================
--- work.git.orig/arch/powerpc/boot/ops.h       2007-07-10 03:48:20.000000000 
-0500
+++ work.git/arch/powerpc/boot/ops.h    2007-07-10 03:48:29.000000000 -0500
@@ -22,6 +22,8 @@
 
 typedef void (*kernel_entry_t)(unsigned long r3, unsigned long r4, void *r5);
 
+struct boot_param_header;
+
 /* Platform specific operations */
 struct platform_ops {
        void    (*fixups)(void);
@@ -96,6 +98,14 @@ void send_slaves_to_kernel(void *vmlinux
 void slaves_are_low(void);
 void wait_slaves_moved(void);
 
+/* memory ranges */
+extern void *rmo_end;
+void add_occupied_range(void *start, void *end);
+void add_occupied_range_ulong(unsigned long start, unsigned long end);
+void add_known_ranges(struct boot_param_header *dt_blob);
+void ranges_init_malloc(void);
+void *ranges_vmlinux_alloc(unsigned long size);
+
 static inline void *finddevice(const char *name)
 {
        return (dt_ops.finddevice) ? dt_ops.finddevice(name) : NULL;
Index: work.git/arch/powerpc/boot/Makefile
===================================================================
--- work.git.orig/arch/powerpc/boot/Makefile    2007-07-10 03:48:20.000000000 
-0500
+++ work.git/arch/powerpc/boot/Makefile 2007-07-10 03:48:29.000000000 -0500
@@ -42,7 +42,7 @@ $(addprefix $(obj)/,$(zlib) gunzip_util.
        $(addprefix $(obj)/,$(zliblinuxheader)) $(addprefix 
$(obj)/,$(zlibheader))
 
 src-wlib := string.S crt0.S stdio.c main.c flatdevtree.c flatdevtree_misc.c \
-               marshal.c \
+               marshal.c memranges.c \
                ns16550.c serial.c simple_alloc.c div64.S util.S \
                gunzip_util.c elf_util.c $(zlib) devtree.c oflib.c ofconsole.c \
                44x.c ebony.c mv64x60.c mpsc.c mv64x60_i2c.c cuboot.c
_______________________________________________
Linuxppc-dev mailing list
Linuxppc-dev@ozlabs.org
https://ozlabs.org/mailman/listinfo/linuxppc-dev

Reply via email to