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