[U-Boot] [PATCH v9] [RFC] Add dmmalloc module for DM.
Tomas Hlavacek
tmshlvck at gmail.com
Thu Oct 25 01:49:50 CEST 2012
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 at 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 at 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 at 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 */
+
--
1.7.10.4
More information about the U-Boot
mailing list