
Add pointer to the first early heap into GD structure. Implement simple early_malloc and early_free functions. Prepare for additional heaps and automated heap initialization. Add temporary early_malloc_active function (to be replaced in future by more coarse DM init flags). Add DM specific malloc calls - dmmalloc, dmfree, dmrealloc and dmcalloc.
Signed-off-by: Tomas Hlavacek tmshlvck@gmail.com --- Changes in v9: - Rework early_malloc to keep track of allocated block size. - Add early_free and dmfree functions. - Rework dmrealloc. - Add kerneldoc comments to dmmalloc.h.
Changes in v8: - Add dmcalloc() implmentation. - Add comments to function prototypes in dmmalloc.h.
Changes in v7: - Rework check of first heap in early_brk().
Changes in v6: - Move dmmalloc() and all dm* functions to dmmalloc.h. - Fix bool expression in early_malloc_active().
arch/arm/include/asm/global_data.h | 3 + arch/avr32/include/asm/global_data.h | 3 + arch/blackfin/include/asm/global_data.h | 3 + arch/m68k/include/asm/global_data.h | 3 + arch/microblaze/include/asm/global_data.h | 3 + arch/mips/include/asm/global_data.h | 3 + arch/nds32/include/asm/global_data.h | 3 + arch/nios2/include/asm/global_data.h | 3 + arch/openrisc/include/asm/global_data.h | 3 + arch/powerpc/include/asm/global_data.h | 3 + arch/sandbox/include/asm/global_data.h | 3 + arch/sh/include/asm/global_data.h | 3 + arch/sparc/include/asm/global_data.h | 3 + arch/x86/include/asm/global_data.h | 3 + common/Makefile | 1 + common/dmmalloc.c | 188 ++++++++++++++++++++++++++++ include/dmmalloc.h | 194 +++++++++++++++++++++++++++++ 17 files changed, 425 insertions(+) create mode 100644 common/dmmalloc.c create mode 100644 include/dmmalloc.h
diff --git a/arch/arm/include/asm/global_data.h b/arch/arm/include/asm/global_data.h index 2b9af93..9045829 100644 --- a/arch/arm/include/asm/global_data.h +++ b/arch/arm/include/asm/global_data.h @@ -82,6 +82,9 @@ typedef struct global_data { unsigned long post_log_res; /* success of POST test */ unsigned long post_init_f_time; /* When post_init_f started */ #endif +#ifdef CONFIG_SYS_EARLY_MALLOC + void *early_heap; /* heap for early_malloc */ +#endif } gd_t;
#include <asm-generic/global_data_flags.h> diff --git a/arch/avr32/include/asm/global_data.h b/arch/avr32/include/asm/global_data.h index bf661e2..f18f480 100644 --- a/arch/avr32/include/asm/global_data.h +++ b/arch/avr32/include/asm/global_data.h @@ -48,6 +48,9 @@ typedef struct global_data { #endif void **jt; /* jump table */ char env_buf[32]; /* buffer for getenv() before reloc. */ +#ifdef CONFIG_SYS_EARLY_MALLOC + void *early_heap; /* heap for early_malloc */ +#endif } gd_t;
#include <asm-generic/global_data_flags.h> diff --git a/arch/blackfin/include/asm/global_data.h b/arch/blackfin/include/asm/global_data.h index d91e5a4..0725d55 100644 --- a/arch/blackfin/include/asm/global_data.h +++ b/arch/blackfin/include/asm/global_data.h @@ -57,6 +57,9 @@ typedef struct global_data {
void **jt; /* jump table */ char env_buf[32]; /* buffer for getenv() before reloc. */ +#ifdef CONFIG_SYS_EARLY_MALLOC + void *early_heap; /* heap for early_malloc */ +#endif } gd_t;
#include <asm-generic/global_data_flags.h> diff --git a/arch/m68k/include/asm/global_data.h b/arch/m68k/include/asm/global_data.h index 0cdb11c..ab73499 100644 --- a/arch/m68k/include/asm/global_data.h +++ b/arch/m68k/include/asm/global_data.h @@ -66,6 +66,9 @@ typedef struct global_data { #endif void **jt; /* Standalone app jump table */ char env_buf[32]; /* buffer for getenv() before reloc. */ +#ifdef CONFIG_SYS_EARLY_MALLOC + void *early_heap; /* heap for early_malloc */ +#endif } gd_t;
#include <asm-generic/global_data_flags.h> diff --git a/arch/microblaze/include/asm/global_data.h b/arch/microblaze/include/asm/global_data.h index 2111c7c..f991e5d 100644 --- a/arch/microblaze/include/asm/global_data.h +++ b/arch/microblaze/include/asm/global_data.h @@ -46,6 +46,9 @@ typedef struct global_data { unsigned long fb_base; /* base address of frame buffer */ void **jt; /* jump table */ char env_buf[32]; /* buffer for getenv() before reloc. */ +#ifdef CONFIG_SYS_EARLY_MALLOC + void *early_heap; /* heap for early_malloc */ +#endif } gd_t;
#include <asm-generic/global_data_flags.h> diff --git a/arch/mips/include/asm/global_data.h b/arch/mips/include/asm/global_data.h index a735a8a..8167d39 100644 --- a/arch/mips/include/asm/global_data.h +++ b/arch/mips/include/asm/global_data.h @@ -59,6 +59,9 @@ typedef struct global_data { unsigned long env_valid; /* Checksum of Environment valid? */ void **jt; /* jump table */ char env_buf[32]; /* buffer for getenv() before reloc. */ +#ifdef CONFIG_SYS_EARLY_MALLOC + void *early_heap; /* heap for early_malloc */ +#endif } gd_t;
#include <asm-generic/global_data_flags.h> diff --git a/arch/nds32/include/asm/global_data.h b/arch/nds32/include/asm/global_data.h index b1feb2c..f7480e9 100644 --- a/arch/nds32/include/asm/global_data.h +++ b/arch/nds32/include/asm/global_data.h @@ -63,6 +63,9 @@ typedef struct global_data {
void **jt; /* jump table */ char env_buf[32]; /* buffer for getenv() before reloc. */ +#ifdef CONFIG_SYS_EARLY_MALLOC + void *early_heap; /* heap for early_malloc */ +#endif } gd_t;
#include <asm-generic/global_data_flags.h> diff --git a/arch/nios2/include/asm/global_data.h b/arch/nios2/include/asm/global_data.h index 413b485..8881e31 100644 --- a/arch/nios2/include/asm/global_data.h +++ b/arch/nios2/include/asm/global_data.h @@ -42,6 +42,9 @@ typedef struct global_data { #endif void **jt; /* Standalone app jump table */ char env_buf[32]; /* buffer for getenv() before reloc. */ +#ifdef CONFIG_SYS_EARLY_MALLOC + void *early_heap; /* heap for early_malloc */ +#endif } gd_t;
#include <asm-generic/global_data_flags.h> diff --git a/arch/openrisc/include/asm/global_data.h b/arch/openrisc/include/asm/global_data.h index 96f3f1c..6ed6a15 100644 --- a/arch/openrisc/include/asm/global_data.h +++ b/arch/openrisc/include/asm/global_data.h @@ -44,6 +44,9 @@ typedef struct global_data { unsigned long fb_base; /* base address of frame buffer */ void **jt; /* jump table */ char env_buf[32]; /* buffer for getenv() before reloc. */ +#ifdef CONFIG_SYS_EARLY_MALLOC + void *early_heap; /* heap for early_malloc */ +#endif } gd_t;
#include <asm-generic/global_data_flags.h> diff --git a/arch/powerpc/include/asm/global_data.h b/arch/powerpc/include/asm/global_data.h index 374fc6d..f8d440b 100644 --- a/arch/powerpc/include/asm/global_data.h +++ b/arch/powerpc/include/asm/global_data.h @@ -182,6 +182,9 @@ typedef struct global_data { #endif void **jt; /* jump table */ char env_buf[32]; /* buffer for getenv() before reloc. */ +#ifdef CONFIG_SYS_EARLY_MALLOC + void *early_heap; /* heap for early_malloc */ +#endif } gd_t;
#include <asm-generic/global_data_flags.h> diff --git a/arch/sandbox/include/asm/global_data.h b/arch/sandbox/include/asm/global_data.h index 78a751d..6ee20e2 100644 --- a/arch/sandbox/include/asm/global_data.h +++ b/arch/sandbox/include/asm/global_data.h @@ -46,6 +46,9 @@ typedef struct global_data { const void *fdt_blob; /* Our device tree, NULL if none */ void **jt; /* jump table */ char env_buf[32]; /* buffer for getenv() before reloc. */ +#ifdef CONFIG_SYS_EARLY_MALLOC + void *early_heap; /* heap for early_malloc */ +#endif } gd_t;
#include <asm-generic/global_data_flags.h> diff --git a/arch/sh/include/asm/global_data.h b/arch/sh/include/asm/global_data.h index 9a2c193..6e0ae54 100644 --- a/arch/sh/include/asm/global_data.h +++ b/arch/sh/include/asm/global_data.h @@ -42,6 +42,9 @@ typedef struct global_data unsigned long env_valid; /* Checksum of Environment valid */ void **jt; /* Standalone app jump table */ char env_buf[32]; /* buffer for getenv() before reloc. */ +#ifdef CONFIG_SYS_EARLY_MALLOC + void *early_heap; /* heap for early_malloc */ +#endif } gd_t;
#include <asm-generic/global_data_flags.h> diff --git a/arch/sparc/include/asm/global_data.h b/arch/sparc/include/asm/global_data.h index aa63b35..9be8f27 100644 --- a/arch/sparc/include/asm/global_data.h +++ b/arch/sparc/include/asm/global_data.h @@ -74,6 +74,9 @@ typedef struct global_data { #endif void **jt; /* jump table */ char env_buf[32]; /* buffer for getenv() before reloc. */ +#ifdef CONFIG_SYS_EARLY_MALLOC + void *early_heap; /* heap for early_malloc */ +#endif } gd_t;
#include <asm-generic/global_data_flags.h> diff --git a/arch/x86/include/asm/global_data.h b/arch/x86/include/asm/global_data.h index bce999f..60384a5 100644 --- a/arch/x86/include/asm/global_data.h +++ b/arch/x86/include/asm/global_data.h @@ -57,6 +57,9 @@ typedef struct global_data { unsigned long reset_status; /* reset status register at boot */ void **jt; /* jump table */ char env_buf[32]; /* buffer for getenv() before reloc. */ +#ifdef CONFIG_SYS_EARLY_MALLOC + void *early_heap; /* heap for early_malloc */ +#endif } gd_t;
static inline gd_t *get_fs_gd_ptr(void) diff --git a/common/Makefile b/common/Makefile index fdfead7..bfb4d7a 100644 --- a/common/Makefile +++ b/common/Makefile @@ -209,6 +209,7 @@ COBJS-y += dlmalloc.o COBJS-y += image.o COBJS-y += memsize.o COBJS-y += stdio.o +COBJS-$(CONFIG_DM) += dmmalloc.o
COBJS := $(sort $(COBJS-y)) diff --git a/common/dmmalloc.c b/common/dmmalloc.c new file mode 100644 index 0000000..41589dd --- /dev/null +++ b/common/dmmalloc.c @@ -0,0 +1,188 @@ +/* + * (C) Copyright 2012 + * Tomas Hlavacek (tmshlvck@gmail.com) + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> /* for ROUND_UP */ +#include <asm/u-boot.h> +#include <asm/global_data.h> /* for gd_t and gd */ +#include <asm/types.h> /* for phys_addr_t and size_addt_t */ + +#include <dmmalloc.h> +#include <malloc.h> + +#include <linux/compiler.h> + +DECLARE_GLOBAL_DATA_PTR; + +#ifdef CONFIG_SYS_EARLY_MALLOC +__weak struct early_heap_header *early_brk(size_t size) +{ + struct early_heap_header *h; + struct early_block_header *b; + + if (gd->early_heap != NULL) + return NULL; + + h = (struct early_heap_header *)CONFIG_SYS_EARLY_HEAP_ADDR; + b = (struct early_block_header *)(h + 1); + + size = CONFIG_SYS_EARLY_HEAP_SIZE; + h->size = size; + h->early_heap_next = NULL; + b->size = size - sizeof(struct early_heap_header) - + sizeof(struct early_block_header); + b->size = BLOCK_SET_FREE(b->size); + + return h; +} + +static struct early_block_header *find_free_space(struct early_heap_header *h, + size_t size) +{ + struct early_block_header *b; + + b = (struct early_block_header *)(h+1); + while ((phys_addr_t)b + sizeof(struct early_block_header) + < (phys_addr_t)h + h->size) { + if (BLOCK_FREE(b->size) && (BLOCK_SIZE(b->size) >= size)) + return b; + b = (struct early_block_header *)((phys_addr_t)b + + sizeof(struct early_block_header) + + BLOCK_SIZE(b->size)); + } + + return NULL; +} + +static struct early_block_header *split_block(struct early_block_header *b, + size_t size) +{ + struct early_block_header *nb; + + if ((BLOCK_SIZE(b->size) < size) || (BLOCK_USED(b->size))) + return NULL; + + if (BLOCK_SIZE(b->size) <= (size + sizeof(struct early_block_header))) + return b; + + nb = (struct early_block_header *)((phys_addr_t)b + + sizeof(struct early_block_header) + size); + nb->size = b->size - size - sizeof(struct early_block_header); + b->size = size; + + return b; +} + +void *early_malloc(size_t size) +{ + struct early_heap_header *h; + struct early_block_header *b; + + size = roundup(size, sizeof(phys_addr_t)); + if (size == 0) + return NULL; + + if (gd->early_heap == NULL) + gd->early_heap = early_brk(size); + + if (gd->early_heap == NULL) { + debug("early_brk failed to initialize heap\n"); + return NULL; + } + + h = gd->early_heap; + while (1) { + b = find_free_space(h, size); + if (b != NULL) + break; + + if (h->early_heap_next != NULL) + h = h->early_heap_next; + else + break; + } + + if (b == NULL) { + h->early_heap_next = early_brk(size+ + sizeof(struct early_heap_header)+ + sizeof(struct early_block_header)); + h = h->early_heap_next; + if (h == NULL) { + debug("early_brk failed to extend heap by %d B\n", + size); + return NULL; + } + + b = find_free_space(h, size); + if (b == NULL) { + debug("early_malloc failed to extend heap by %d B\n", + size); + return NULL; + } + } + + if (b->size != size) + b = split_block(b, size); + if (b == NULL) { + debug("early_malloc failed to split block to %d B\n", size); + return NULL; + } + + b->size = BLOCK_SET_USED(b->size); + + return BLOCK_DATA(b); +} + +void early_free(void *addr) +{ + struct early_block_header *h = BLOCK_HEADER(addr); + assert(BLOCK_USED(h->size)); + h->size = BLOCK_SET_FREE(h->size); +} + +int early_malloc_active(void) +{ + return ((gd->flags & GD_FLG_RELOC) != GD_FLG_RELOC); +} + +void early_heap_dump(struct early_heap_header *h) +{ + struct early_block_header *b; + + debug("heap: h=%p, h->size=%d\n", h, h->size); + + b = (struct early_block_header *)(h+1); + while ((phys_addr_t)b + sizeof(struct early_block_header) + < (phys_addr_t)h + h->size) { + debug("block: h=%p h->size=%d b=%p b->size=%d b->(used)=%d\n", + h, h->size, b, BLOCK_SIZE(b->size), + BLOCK_USED(b->size)); + assert(BLOCK_SIZE(b->size) > 0); + b = (struct early_block_header *)((phys_addr_t)b + + sizeof(struct early_block_header) + + BLOCK_SIZE(b->size)); + } + debug("--- heap dump end ---\n"); +} + +#endif /* CONFIG_SYS_EARLY_MALLOC */ + diff --git a/include/dmmalloc.h b/include/dmmalloc.h new file mode 100644 index 0000000..a241e19 --- /dev/null +++ b/include/dmmalloc.h @@ -0,0 +1,194 @@ +/* + * (C) Copyright 2012 + * Tomas Hlavacek (tmshlvck@gmail.com) + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef __INCLUDE_DMMALLOC_H +#define __INCLUDE_DMMALLOC_H + +#include <config.h> +#include <linux/stddef.h> /* for size_t */ +#include <malloc.h> + +#if (!defined(CONFIG_SYS_EARLY_HEAP_ADDR)) || \ + (!defined(CONFIG_SYS_EARLY_HEAP_SIZE)) +#undef CONFIG_SYS_EARLY_MALLOC +#endif /* CONFIG_SYS_EARLY_HEAP_ADDR */ + +#ifdef CONFIG_SYS_EARLY_MALLOC +struct early_heap_header { + size_t size; + void *early_heap_next; +}; + +struct early_block_header { + size_t size; +}; + +#define BLOCK_DATA(header) ((void *)(((struct early_block_header *)header)+1)) +#define BLOCK_HEADER(addr) (((struct early_block_header *)addr)-1) +#define BLOCK_USED_FLAG 0x80000000 +#define BLOCK_SIZE(size) (size & (~BLOCK_USED_FLAG)) +#define BLOCK_USED(size) ((size & BLOCK_USED_FLAG) == BLOCK_USED_FLAG) +#define BLOCK_FREE(size) (!BLOCK_USED(size)) +#define BLOCK_SET_FREE(size) BLOCK_SIZE(size) +#define BLOCK_SET_USED(size) (size | BLOCK_USED_FLAG) + +/** + * early_brk() - obtain address of the heap + * @size: Minimal size of the new early heap to be allocated. + * + * Function returns a new heap pointer. + * + * Allocate and initialize early_heap at least size bytes long. + * This function can be platform dependent or board dependent but sensible + * default is provided. + */ +struct early_heap_header *early_brk(size_t size); + +/** + * early_malloc() - malloc operating on the early_heap(s) + * @size: Size in bytes. + * + * Function returns a pointer to the allocated block. + */ +void *early_malloc(size_t size); + +/** + * early_free() - free operating on the early_heap(s) + * @addr: Pointer to the allocated block to be released. + */ +void early_free(void *addr); + +/** + * early_malloc_active() - indicate if the early mallocator is active + * + * Function returns true when the early_malloc and early_free are used and + * false otherwise. + */ +int early_malloc_active(void); + +/** + * early_heap_dump() - print blocks contained in an early_heap + * @h: Address of the early heap. + */ +void early_heap_dump(struct early_heap_header *h); + +#endif /* CONFIG_SYS_EARLY_MALLOC */ + +#ifdef CONFIG_DM + +/* + * DM versions of malloc* functions. In early init it calls early_malloc. + * It wraps around normal malloc* functions afterwards. + */ + +/** + * dmmalloc() - malloc working seamlessly in early as well as in RAM stages + * @size: Size of the block to be allocated. + * + * Function returns an address of the newly allocated block when successful + * or NULL otherwise. + */ +static inline void *dmmalloc(size_t size) +{ +#ifdef CONFIG_SYS_EARLY_MALLOC + if (early_malloc_active()) + return early_malloc(size); +#endif /* CONFIG_SYS_EARLY_MALLOC */ + return malloc(size); +} + +/** + * dmfree() - free working seamlessly in early as well as in RAM stages + * @ptr: Pointer to the allocated block to be released. + */ +static inline void dmfree(void *ptr) +{ +#ifdef CONFIG_SYS_EARLY_MALLOC + if (early_malloc_active()) { + early_free(ptr); + return; + } +#endif /* CONFIG_SYS_EARLY_MALLOC */ + free(ptr); +} + +/** + * dmcalloc() - calloc working seamlessly in early as well as in RAM stages + * @n: Number of elements to be allocated. + * @elem_size: Size of elements to be allocated. + * + * Function returns a pointer to newly the allocated area (n*elem_size) long. + */ +static inline void *dmcalloc(size_t n, size_t elem_size) +{ +#ifdef CONFIG_SYS_EARLY_MALLOC + char *addr; + int size = elem_size * n; + + if (early_malloc_active()) { + addr = early_malloc(size); + memset(addr, 0, size); + return addr; + } +#endif /* CONFIG_SYS_EARLY_MALLOC */ + return calloc(n, elem_size); +} + +/** + * dmrealloc() - realloc working seamlessly in early as well as in RAM stages + * @oldaddr: Pointer to the old memory block. + * @bytes: New size to of the block to be reallocated. + * + * Function returns an address of the newly allocated block when successful + * or NULL otherwise. + * + * Data are copied from the block specified by oldaddr to the new block. + */ +static inline void *dmrealloc(void *oldaddr, size_t bytes) +{ +#ifdef CONFIG_SYS_EARLY_MALLOC + char *addr; + struct early_block_header *h; + if (early_malloc_active()) { + addr = dmmalloc(bytes); + if (addr == NULL) + return NULL; + + h = BLOCK_HEADER(oldaddr); + if (BLOCK_FREE(h->size)) + return NULL; + + if (bytes > BLOCK_SIZE(h->size)) + bytes = BLOCK_SIZE(h->size); + + memcpy(addr, oldaddr, bytes); + dmfree(oldaddr); + return addr; + } +#endif /* CONFIG_SYS_EARLY_MALLOC */ + return realloc(oldaddr, bytes); +} + +#endif /* CONFIG_DM */ +#endif /* __INCLUDE_DMMALLOC_H */ +