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