diff options
| -rw-r--r-- | common/cmd_bootm.c | 14 | ||||
| -rw-r--r-- | include/image.h | 2 | ||||
| -rw-r--r-- | include/lmb.h | 54 | ||||
| -rw-r--r-- | lib_generic/Makefile | 1 | ||||
| -rw-r--r-- | lib_generic/lmb.c | 280 | 
5 files changed, 351 insertions, 0 deletions
| diff --git a/common/cmd_bootm.c b/common/cmd_bootm.c index e5ed16774..92c18d059 100644 --- a/common/cmd_bootm.c +++ b/common/cmd_bootm.c @@ -34,6 +34,7 @@  #include <zlib.h>  #include <bzlib.h>  #include <environment.h> +#include <lmb.h>  #include <asm/byteorder.h>  #ifdef CFG_HUSH_PARSER @@ -118,8 +119,19 @@ int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])  	ulong		image_start, image_end;  	ulong		load_start, load_end; +	struct lmb lmb; +  	memset ((void *)&images, 0, sizeof (images));  	images.verify = getenv_verify(); +	images.lmb = &lmb; + +	lmb_init(&lmb); + +#ifdef CFG_SDRAM_BASE +	lmb_add(&lmb, CFG_SDRAM_BASE, gd->bd->bi_memsize); +#else +	lmb_add(&lmb, 0, gd->bd->bi_memsize); +#endif  	/* get kernel image header, start address and length */  	os_hdr = get_kernel (cmdtp, flag, argc, argv, @@ -237,6 +249,8 @@ int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])  	show_boot_progress (8); +	lmb_reserve(&lmb, load_start, (load_end - load_start)); +  	switch (os) {  	default:			/* handled by (original) Linux case */  	case IH_OS_LINUX: diff --git a/include/image.h b/include/image.h index 025ec0fcf..cb4acd8a9 100644 --- a/include/image.h +++ b/include/image.h @@ -37,6 +37,7 @@  #include <command.h>  #ifndef USE_HOSTCC +#include <lmb.h>  #include <linux/string.h>  #include <asm/u-boot.h> @@ -203,6 +204,7 @@ typedef struct bootm_headers {  	char		*fit_uname_fdt;	/* FDT blob node unit name */  #endif  	int		verify;		/* getenv("verify")[0] != 'n' */ +	struct lmb	*lmb;		/* for memory mgmt */  #endif  } bootm_headers_t; diff --git a/include/lmb.h b/include/lmb.h new file mode 100644 index 000000000..cc64cbbc7 --- /dev/null +++ b/include/lmb.h @@ -0,0 +1,54 @@ +#ifndef _LINUX_LMB_H +#define _LINUX_LMB_H +#ifdef __KERNEL__ + +#include <asm/types.h> +/* + * Logical memory blocks. + * + * Copyright (C) 2001 Peter Bergner, IBM Corp. + * + * 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. + */ + +#define MAX_LMB_REGIONS 8 + +struct lmb_property { +	ulong base; +	ulong size; +}; + +struct lmb_region { +	unsigned long cnt; +	ulong size; +	struct lmb_property region[MAX_LMB_REGIONS+1]; +}; + +struct lmb { +	struct lmb_region memory; +	struct lmb_region reserved; +}; + +extern struct lmb lmb; + +extern void lmb_init(struct lmb *lmb); +extern long lmb_add(struct lmb *lmb, ulong base, ulong size); +extern long lmb_reserve(struct lmb *lmb, ulong base, ulong size); +extern ulong lmb_alloc(struct lmb *lmb, ulong size, ulong align); +extern ulong lmb_alloc_base(struct lmb *lmb, ulong size, ulong align, ulong max_addr); +extern ulong __lmb_alloc_base(struct lmb *lmb, ulong size, ulong align, ulong max_addr); +extern int lmb_is_reserved(struct lmb *lmb, ulong addr); + +extern void lmb_dump_all(struct lmb *lmb); + +static inline ulong +lmb_size_bytes(struct lmb_region *type, unsigned long region_nr) +{ +	return type->region[region_nr].size; +} +#endif /* __KERNEL__ */ + +#endif /* _LINUX_LMB_H */ diff --git a/lib_generic/Makefile b/lib_generic/Makefile index 9713353dd..b10793bd4 100644 --- a/lib_generic/Makefile +++ b/lib_generic/Makefile @@ -34,6 +34,7 @@ COBJS-y += crc32.o  COBJS-y += ctype.o  COBJS-y += display_options.o  COBJS-y += div64.o +COBJS-y += lmb.o  COBJS-y += ldiv.o  COBJS-y += sha1.o  COBJS-y += string.o diff --git a/lib_generic/lmb.c b/lib_generic/lmb.c new file mode 100644 index 000000000..3b8c805e8 --- /dev/null +++ b/lib_generic/lmb.c @@ -0,0 +1,280 @@ +/* + * Procedures for maintaining information about logical memory blocks. + * + * Peter Bergner, IBM Corp.	June 2001. + * Copyright (C) 2001 Peter Bergner. + * + *      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. + */ + +#include <common.h> +#include <lmb.h> + +#define LMB_ALLOC_ANYWHERE	0 + +void lmb_dump_all(struct lmb *lmb) +{ +#ifdef DEBUG +	unsigned long i; + +	debug("lmb_dump_all:\n"); +	debug("    memory.cnt		   = 0x%lx\n", lmb->memory.cnt); +	debug("    memory.size		   = 0x%08x\n", lmb->memory.size); +	for (i=0; i < lmb->memory.cnt ;i++) { +		debug("    memory.reg[0x%x].base   = 0x%08x\n", i, +			lmb->memory.region[i].base); +		debug("		   .size   = 0x%08x\n", +			lmb->memory.region[i].size); +	} + +	debug("\n    reserved.cnt	   = 0x%lx\n", lmb->reserved.cnt); +	debug("    reserved.size	   = 0x%08x\n", lmb->reserved.size); +	for (i=0; i < lmb->reserved.cnt ;i++) { +		debug("    reserved.reg[0x%x].base = 0x%08x\n", i, +			lmb->reserved.region[i].base); +		debug("		     .size = 0x%08x\n", +			lmb->reserved.region[i].size); +	} +#endif /* DEBUG */ +} + +static unsigned long lmb_addrs_overlap(ulong base1, +		ulong size1, ulong base2, ulong size2) +{ +	return ((base1 < (base2+size2)) && (base2 < (base1+size1))); +} + +static long lmb_addrs_adjacent(ulong base1, ulong size1, +		ulong base2, ulong size2) +{ +	if (base2 == base1 + size1) +		return 1; +	else if (base1 == base2 + size2) +		return -1; + +	return 0; +} + +static long lmb_regions_adjacent(struct lmb_region *rgn, +		unsigned long r1, unsigned long r2) +{ +	ulong base1 = rgn->region[r1].base; +	ulong size1 = rgn->region[r1].size; +	ulong base2 = rgn->region[r2].base; +	ulong size2 = rgn->region[r2].size; + +	return lmb_addrs_adjacent(base1, size1, base2, size2); +} + +static void lmb_remove_region(struct lmb_region *rgn, unsigned long r) +{ +	unsigned long i; + +	for (i = r; i < rgn->cnt - 1; i++) { +		rgn->region[i].base = rgn->region[i + 1].base; +		rgn->region[i].size = rgn->region[i + 1].size; +	} +	rgn->cnt--; +} + +/* Assumption: base addr of region 1 < base addr of region 2 */ +static void lmb_coalesce_regions(struct lmb_region *rgn, +		unsigned long r1, unsigned long r2) +{ +	rgn->region[r1].size += rgn->region[r2].size; +	lmb_remove_region(rgn, r2); +} + +void lmb_init(struct lmb *lmb) +{ +	/* Create a dummy zero size LMB which will get coalesced away later. +	 * This simplifies the lmb_add() code below... +	 */ +	lmb->memory.region[0].base = 0; +	lmb->memory.region[0].size = 0; +	lmb->memory.cnt = 1; +	lmb->memory.size = 0; + +	/* Ditto. */ +	lmb->reserved.region[0].base = 0; +	lmb->reserved.region[0].size = 0; +	lmb->reserved.cnt = 1; +	lmb->reserved.size = 0; +} + +/* This routine called with relocation disabled. */ +static long lmb_add_region(struct lmb_region *rgn, ulong base, ulong size) +{ +	unsigned long coalesced = 0; +	long adjacent, i; + +	if ((rgn->cnt == 1) && (rgn->region[0].size == 0)) { +		rgn->region[0].base = base; +		rgn->region[0].size = size; +		return 0; +	} + +	/* First try and coalesce this LMB with another. */ +	for (i=0; i < rgn->cnt; i++) { +		ulong rgnbase = rgn->region[i].base; +		ulong rgnsize = rgn->region[i].size; + +		if ((rgnbase == base) && (rgnsize == size)) +			/* Already have this region, so we're done */ +			return 0; + +		adjacent = lmb_addrs_adjacent(base,size,rgnbase,rgnsize); +		if ( adjacent > 0 ) { +			rgn->region[i].base -= size; +			rgn->region[i].size += size; +			coalesced++; +			break; +		} +		else if ( adjacent < 0 ) { +			rgn->region[i].size += size; +			coalesced++; +			break; +		} +	} + +	if ((i < rgn->cnt-1) && lmb_regions_adjacent(rgn, i, i+1) ) { +		lmb_coalesce_regions(rgn, i, i+1); +		coalesced++; +	} + +	if (coalesced) +		return coalesced; +	if (rgn->cnt >= MAX_LMB_REGIONS) +		return -1; + +	/* Couldn't coalesce the LMB, so add it to the sorted table. */ +	for (i = rgn->cnt-1; i >= 0; i--) { +		if (base < rgn->region[i].base) { +			rgn->region[i+1].base = rgn->region[i].base; +			rgn->region[i+1].size = rgn->region[i].size; +		} else { +			rgn->region[i+1].base = base; +			rgn->region[i+1].size = size; +			break; +		} +	} + +	if (base < rgn->region[0].base) { +		rgn->region[0].base = base; +		rgn->region[0].size = size; +	} + +	rgn->cnt++; + +	return 0; +} + +/* This routine may be called with relocation disabled. */ +long lmb_add(struct lmb *lmb, ulong base, ulong size) +{ +	struct lmb_region *_rgn = &(lmb->memory); + +	return lmb_add_region(_rgn, base, size); +} + +long lmb_reserve(struct lmb *lmb, ulong base, ulong size) +{ +	struct lmb_region *_rgn = &(lmb->reserved); + +	return lmb_add_region(_rgn, base, size); +} + +long lmb_overlaps_region(struct lmb_region *rgn, ulong base, +				ulong size) +{ +	unsigned long i; + +	for (i=0; i < rgn->cnt; i++) { +		ulong rgnbase = rgn->region[i].base; +		ulong rgnsize = rgn->region[i].size; +		if ( lmb_addrs_overlap(base,size,rgnbase,rgnsize) ) { +			break; +		} +	} + +	return (i < rgn->cnt) ? i : -1; +} + +ulong lmb_alloc(struct lmb *lmb, ulong size, ulong align) +{ +	return lmb_alloc_base(lmb, size, align, LMB_ALLOC_ANYWHERE); +} + +ulong lmb_alloc_base(struct lmb *lmb, ulong size, ulong align, ulong max_addr) +{ +	ulong alloc; + +	alloc = __lmb_alloc_base(lmb, size, align, max_addr); + +	if (alloc == 0) +		printf("ERROR: Failed to allocate 0x%lx bytes below 0x%lx.\n", +		      size, max_addr); + +	return alloc; +} + +static ulong lmb_align_down(ulong addr, ulong size) +{ +	return addr & ~(size - 1); +} + +static ulong lmb_align_up(ulong addr, ulong size) +{ +	return (addr + (size - 1)) & ~(size - 1); +} + +ulong __lmb_alloc_base(struct lmb *lmb, ulong size, ulong align, ulong max_addr) +{ +	long i, j; +	ulong base = 0; + +	for (i = lmb->memory.cnt-1; i >= 0; i--) { +		ulong lmbbase = lmb->memory.region[i].base; +		ulong lmbsize = lmb->memory.region[i].size; + +		if (max_addr == LMB_ALLOC_ANYWHERE) +			base = lmb_align_down(lmbbase + lmbsize - size, align); +		else if (lmbbase < max_addr) { +			base = min(lmbbase + lmbsize, max_addr); +			base = lmb_align_down(base - size, align); +		} else +			continue; + +		while ((lmbbase <= base) && +		       ((j = lmb_overlaps_region(&(lmb->reserved), base, size)) >= 0) ) +			base = lmb_align_down(lmb->reserved.region[j].base - size, +					      align); + +		if ((base != 0) && (lmbbase <= base)) +			break; +	} + +	if (i < 0) +		return 0; + +	if (lmb_add_region(&(lmb->reserved), base, lmb_align_up(size, align)) < 0) +		return 0; + +	return base; +} + +int lmb_is_reserved(struct lmb *lmb, ulong addr) +{ +	int i; + +	for (i = 0; i < lmb->reserved.cnt; i++) { +		ulong upper = lmb->reserved.region[i].base + +			lmb->reserved.region[i].size - 1; +		if ((addr >= lmb->reserved.region[i].base) && (addr <= upper)) +			return 1; +	} +	return 0; +} |