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 rma_end (aka end of real mode memory at 0).
To use this code, a platform would set rma_end (find_rma_end), mark memory ranges occupied (add_known_ranges et al), initialize malloc in the spaces between (ranges_init_malloc), and optionally use the supplied vmlinux_alloc may be used. Signed-off-by: Milton Miller <[EMAIL PROTECTED]> --- vs 12172 rename rmo_end to rma_end (real mode area, as used in papr) removed section labels (now in ops.h) rediff ops.h, Makefile moved find_rma_end here (from kexec.c in a later patch) find_rma_end searches by node type for "memory", checks that the parent is the root node, then looks for a reg property with the first address/size pair starting at 0. Index: kernel/arch/powerpc/boot/memranges.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ kernel/arch/powerpc/boot/memranges.c 2007-09-20 17:51:42.000000000 -0500 @@ -0,0 +1,299 @@ +/* + * 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" + +void *rma_end; + +static struct { + void *start, *end; +} ranges[32]; +static int num_ranges; + +/** + * find_rma_end - find end of Real Mode Area + * Query the device tree to find the node of type memory that contains a + * reg starting with address 0, and update rma_end to point to the end + * of that region. On PAPR systems, only this RMA region is accessable + * in real mode. + */ +void find_rma_end(void) +{ + unsigned int na, ns, reg[4], *rp; char path[MAX_PATH_LEN]; + void *devp, *root; int rc; + + path[0] = '\0'; + root = finddevice("/"); + if (!root) + fatal("Ack, can't get device-tree root"); + rc = getprop(root, "#address-cells", &na, sizeof(na)); + if (rc != sizeof(na)) + fatal("Ack, no #address-cells in root"); + rc = getprop(root, "#size-cells", &ns, sizeof(ns)); + if (rc != sizeof(ns)) + fatal("Ack, no #size-cells in root"); + if (!na || !ns || na < 1 || na > 2 || ns < 1 || ns > 2) + fatal("find_rma_end: / #addr-cells or #size-cells unsupported"); + + devp = NULL; + while ((devp = find_node_by_devtype(devp, "memory"))) { + if (root != get_parent(devp)) { + printf("Ignoring memory node %s" + " --- not a child of root\n\r", + get_path(devp, path, sizeof(path))); + continue; + } + rc = getprop(devp, "reg", reg, sizeof(reg)); + if (rc < (na + ns) * sizeof(int)) { + printf("No valid reg property in memory node %s\n\r", + get_path(devp, path, sizeof(path))); + continue; + } + /* find the node where the first reg entry starts at 0 */ + rp = ®[0]; + if (*rp++) + continue; + if (na > 1 && *rp++) + continue; + + /* if >4G, limit it */ + if (ns > 1 && *rp++) { + rma_end = (void *)0xFFFFFFFF; + return; + } + + /* other wise use its size */ + rma_end = (void *)*rp; + return; + } + fatal("find_rma_end: didn't find memory at 0\n\r"); +} + +/** + * 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 rma_end. + */ +void add_occupied_range(void *start, void *end) +{ + int i, j; + + if (start == end) + return; + if (start > rma_end) + return; + if (end > rma_end) + end = rma_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 ? rma_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 rma_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 < rma_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: kernel/arch/powerpc/boot/ops.h =================================================================== --- kernel.orig/arch/powerpc/boot/ops.h 2007-09-17 22:13:14.000000000 -0500 +++ kernel/arch/powerpc/boot/ops.h 2007-09-20 17:51:18.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); @@ -99,6 +101,15 @@ int dt_xlate_addr(void *node, u32 *buf, int dt_is_compatible(void *node, const char *compat); void dt_get_reg_format(void *node, u32 *naddr, u32 *nsize); +/* memory ranges */ +extern void *rma_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 find_rma_end(void); +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: kernel/arch/powerpc/boot/Makefile =================================================================== --- kernel.orig/arch/powerpc/boot/Makefile 2007-09-17 22:13:14.000000000 -0500 +++ kernel/arch/powerpc/boot/Makefile 2007-09-20 17:51:18.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 \ 4xx.c ebony.c mv64x60.c mpsc.c mv64x60_i2c.c cuboot.c bamboo.c \ _______________________________________________ Linuxppc-dev mailing list Linuxppc-dev@ozlabs.org https://ozlabs.org/mailman/listinfo/linuxppc-dev