162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Implement primitive realloc(3) functionality. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author: Mark A. Greer <mgreer@mvista.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * 2006 (c) MontaVista, Software, Inc. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <stddef.h> 1162306a36Sopenharmony_ci#include "types.h" 1262306a36Sopenharmony_ci#include "page.h" 1362306a36Sopenharmony_ci#include "string.h" 1462306a36Sopenharmony_ci#include "ops.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define ENTRY_BEEN_USED 0x01 1762306a36Sopenharmony_ci#define ENTRY_IN_USE 0x02 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic struct alloc_info { 2062306a36Sopenharmony_ci unsigned long flags; 2162306a36Sopenharmony_ci unsigned long base; 2262306a36Sopenharmony_ci unsigned long size; 2362306a36Sopenharmony_ci} *alloc_tbl; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic unsigned long tbl_entries; 2662306a36Sopenharmony_cistatic unsigned long alloc_min; 2762306a36Sopenharmony_cistatic unsigned long next_base; 2862306a36Sopenharmony_cistatic unsigned long space_left; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* 3162306a36Sopenharmony_ci * First time an entry is used, its base and size are set. 3262306a36Sopenharmony_ci * An entry can be freed and re-malloc'd but its base & size don't change. 3362306a36Sopenharmony_ci * Should be smart enough for needs of bootwrapper. 3462306a36Sopenharmony_ci */ 3562306a36Sopenharmony_cistatic void *simple_malloc(unsigned long size) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci unsigned long i; 3862306a36Sopenharmony_ci struct alloc_info *p = alloc_tbl; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci if (size == 0) 4162306a36Sopenharmony_ci goto err_out; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci size = _ALIGN_UP(size, alloc_min); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci for (i=0; i<tbl_entries; i++, p++) 4662306a36Sopenharmony_ci if (!(p->flags & ENTRY_BEEN_USED)) { /* never been used */ 4762306a36Sopenharmony_ci if (size <= space_left) { 4862306a36Sopenharmony_ci p->base = next_base; 4962306a36Sopenharmony_ci p->size = size; 5062306a36Sopenharmony_ci p->flags = ENTRY_BEEN_USED | ENTRY_IN_USE; 5162306a36Sopenharmony_ci next_base += size; 5262306a36Sopenharmony_ci space_left -= size; 5362306a36Sopenharmony_ci return (void *)p->base; 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci goto err_out; /* not enough space left */ 5662306a36Sopenharmony_ci } 5762306a36Sopenharmony_ci /* reuse an entry keeping same base & size */ 5862306a36Sopenharmony_ci else if (!(p->flags & ENTRY_IN_USE) && (size <= p->size)) { 5962306a36Sopenharmony_ci p->flags |= ENTRY_IN_USE; 6062306a36Sopenharmony_ci return (void *)p->base; 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_cierr_out: 6362306a36Sopenharmony_ci return NULL; 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic struct alloc_info *simple_find_entry(void *ptr) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci unsigned long i; 6962306a36Sopenharmony_ci struct alloc_info *p = alloc_tbl; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci for (i=0; i<tbl_entries; i++,p++) { 7262306a36Sopenharmony_ci if (!(p->flags & ENTRY_BEEN_USED)) 7362306a36Sopenharmony_ci break; 7462306a36Sopenharmony_ci if ((p->flags & ENTRY_IN_USE) && 7562306a36Sopenharmony_ci (p->base == (unsigned long)ptr)) 7662306a36Sopenharmony_ci return p; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci return NULL; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic void simple_free(void *ptr) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci struct alloc_info *p = simple_find_entry(ptr); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (p != NULL) 8662306a36Sopenharmony_ci p->flags &= ~ENTRY_IN_USE; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci/* 9062306a36Sopenharmony_ci * Change size of area pointed to by 'ptr' to 'size'. 9162306a36Sopenharmony_ci * If 'ptr' is NULL, then its a malloc(). If 'size' is 0, then its a free(). 9262306a36Sopenharmony_ci * 'ptr' must be NULL or a pointer to a non-freed area previously returned by 9362306a36Sopenharmony_ci * simple_realloc() or simple_malloc(). 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_cistatic void *simple_realloc(void *ptr, unsigned long size) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci struct alloc_info *p; 9862306a36Sopenharmony_ci void *new; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci if (size == 0) { 10162306a36Sopenharmony_ci simple_free(ptr); 10262306a36Sopenharmony_ci return NULL; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (ptr == NULL) 10662306a36Sopenharmony_ci return simple_malloc(size); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci p = simple_find_entry(ptr); 10962306a36Sopenharmony_ci if (p == NULL) /* ptr not from simple_malloc/simple_realloc */ 11062306a36Sopenharmony_ci return NULL; 11162306a36Sopenharmony_ci if (size <= p->size) /* fits in current block */ 11262306a36Sopenharmony_ci return ptr; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci new = simple_malloc(size); 11562306a36Sopenharmony_ci memcpy(new, ptr, p->size); 11662306a36Sopenharmony_ci simple_free(ptr); 11762306a36Sopenharmony_ci return new; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci/* 12162306a36Sopenharmony_ci * Returns addr of first byte after heap so caller can see if it took 12262306a36Sopenharmony_ci * too much space. If so, change args & try again. 12362306a36Sopenharmony_ci */ 12462306a36Sopenharmony_civoid *simple_alloc_init(char *base, unsigned long heap_size, 12562306a36Sopenharmony_ci unsigned long granularity, unsigned long max_allocs) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci unsigned long heap_base, tbl_size; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci heap_size = _ALIGN_UP(heap_size, granularity); 13062306a36Sopenharmony_ci alloc_min = granularity; 13162306a36Sopenharmony_ci tbl_entries = max_allocs; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci tbl_size = tbl_entries * sizeof(struct alloc_info); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci alloc_tbl = (struct alloc_info *)_ALIGN_UP((unsigned long)base, 8); 13662306a36Sopenharmony_ci memset(alloc_tbl, 0, tbl_size); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci heap_base = _ALIGN_UP((unsigned long)alloc_tbl + tbl_size, alloc_min); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci next_base = heap_base; 14162306a36Sopenharmony_ci space_left = heap_size; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci platform_ops.malloc = simple_malloc; 14462306a36Sopenharmony_ci platform_ops.free = simple_free; 14562306a36Sopenharmony_ci platform_ops.realloc = simple_realloc; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci return (void *)(heap_base + heap_size); 14862306a36Sopenharmony_ci} 149