linux/arch/powerpc/boot/simple_alloc.c
<<
>>
Prefs
   1/*
   2 * Implement primitive realloc(3) functionality.
   3 *
   4 * Author: Mark A. Greer <mgreer@mvista.com>
   5 *
   6 * 2006 (c) MontaVista, Software, Inc.  This file is licensed under
   7 * the terms of the GNU General Public License version 2.  This program
   8 * is licensed "as is" without any warranty of any kind, whether express
   9 * or implied.
  10 */
  11
  12#include <stddef.h>
  13#include "types.h"
  14#include "page.h"
  15#include "string.h"
  16#include "ops.h"
  17
  18#define ENTRY_BEEN_USED 0x01
  19#define ENTRY_IN_USE    0x02
  20
  21static struct alloc_info {
  22        unsigned long   flags;
  23        unsigned long   base;
  24        unsigned long   size;
  25} *alloc_tbl;
  26
  27static unsigned long tbl_entries;
  28static unsigned long alloc_min;
  29static unsigned long next_base;
  30static unsigned long space_left;
  31
  32/*
  33 * First time an entry is used, its base and size are set.
  34 * An entry can be freed and re-malloc'd but its base & size don't change.
  35 * Should be smart enough for needs of bootwrapper.
  36 */
  37static void *simple_malloc(unsigned long size)
  38{
  39        unsigned long i;
  40        struct alloc_info *p = alloc_tbl;
  41
  42        if (size == 0)
  43                goto err_out;
  44
  45        size = _ALIGN_UP(size, alloc_min);
  46
  47        for (i=0; i<tbl_entries; i++, p++)
  48                if (!(p->flags & ENTRY_BEEN_USED)) { /* never been used */
  49                        if (size <= space_left) {
  50                                p->base = next_base;
  51                                p->size = size;
  52                                p->flags = ENTRY_BEEN_USED | ENTRY_IN_USE;
  53                                next_base += size;
  54                                space_left -= size;
  55                                return (void *)p->base;
  56                        }
  57                        goto err_out; /* not enough space left */
  58                }
  59                /* reuse an entry keeping same base & size */
  60                else if (!(p->flags & ENTRY_IN_USE) && (size <= p->size)) {
  61                        p->flags |= ENTRY_IN_USE;
  62                        return (void *)p->base;
  63                }
  64err_out:
  65        return NULL;
  66}
  67
  68static struct alloc_info *simple_find_entry(void *ptr)
  69{
  70        unsigned long i;
  71        struct alloc_info *p = alloc_tbl;
  72
  73        for (i=0; i<tbl_entries; i++,p++) {
  74                if (!(p->flags & ENTRY_BEEN_USED))
  75                        break;
  76                if ((p->flags & ENTRY_IN_USE) &&
  77                    (p->base == (unsigned long)ptr))
  78                        return p;
  79        }
  80        return NULL;
  81}
  82
  83static void simple_free(void *ptr)
  84{
  85        struct alloc_info *p = simple_find_entry(ptr);
  86
  87        if (p != NULL)
  88                p->flags &= ~ENTRY_IN_USE;
  89}
  90
  91/*
  92 * Change size of area pointed to by 'ptr' to 'size'.
  93 * If 'ptr' is NULL, then its a malloc().  If 'size' is 0, then its a free().
  94 * 'ptr' must be NULL or a pointer to a non-freed area previously returned by
  95 * simple_realloc() or simple_malloc().
  96 */
  97static void *simple_realloc(void *ptr, unsigned long size)
  98{
  99        struct alloc_info *p;
 100        void *new;
 101
 102        if (size == 0) {
 103                simple_free(ptr);
 104                return NULL;
 105        }
 106
 107        if (ptr == NULL)
 108                return simple_malloc(size);
 109
 110        p = simple_find_entry(ptr);
 111        if (p == NULL) /* ptr not from simple_malloc/simple_realloc */
 112                return NULL;
 113        if (size <= p->size) /* fits in current block */
 114                return ptr;
 115
 116        new = simple_malloc(size);
 117        memcpy(new, ptr, p->size);
 118        simple_free(ptr);
 119        return new;
 120}
 121
 122/*
 123 * Returns addr of first byte after heap so caller can see if it took
 124 * too much space.  If so, change args & try again.
 125 */
 126void *simple_alloc_init(char *base, unsigned long heap_size,
 127                        unsigned long granularity, unsigned long max_allocs)
 128{
 129        unsigned long heap_base, tbl_size;
 130
 131        heap_size = _ALIGN_UP(heap_size, granularity);
 132        alloc_min = granularity;
 133        tbl_entries = max_allocs;
 134
 135        tbl_size = tbl_entries * sizeof(struct alloc_info);
 136
 137        alloc_tbl = (struct alloc_info *)_ALIGN_UP((unsigned long)base, 8);
 138        memset(alloc_tbl, 0, tbl_size);
 139
 140        heap_base = _ALIGN_UP((unsigned long)alloc_tbl + tbl_size, alloc_min);
 141
 142        next_base = heap_base;
 143        space_left = heap_size;
 144
 145        platform_ops.malloc = simple_malloc;
 146        platform_ops.free = simple_free;
 147        platform_ops.realloc = simple_realloc;
 148
 149        return (void *)(heap_base + heap_size);
 150}
 151