core, pxe: Don't push on one stack and pop from the other in pxenv
[syslinux.git] / core / mem / malloc.c
blobb40c2f2142bcd26e8db131f27c76b339dc9cbbf1
1 /*
2 * malloc.c
4 * Very simple linked-list based malloc()/free().
5 */
7 #include <syslinux/firmware.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <errno.h>
11 #include <string.h>
12 #include <dprintf.h>
13 #include <minmax.h>
15 #include "malloc.h"
16 #include "thread.h"
18 DECLARE_INIT_SEMAPHORE(__malloc_semaphore, 1);
20 static void *__malloc_from_block(struct free_arena_header *fp,
21 size_t size, malloc_tag_t tag)
23 size_t fsize;
24 struct free_arena_header *nfp, *na;
25 unsigned int heap = ARENA_HEAP_GET(fp->a.attrs);
27 fsize = ARENA_SIZE_GET(fp->a.attrs);
29 /* We need the 2* to account for the larger requirements of a free block */
30 if ( fsize >= size+2*sizeof(struct arena_header) ) {
31 /* Bigger block than required -- split block */
32 nfp = (struct free_arena_header *)((char *)fp + size);
33 na = fp->a.next;
35 ARENA_TYPE_SET(nfp->a.attrs, ARENA_TYPE_FREE);
36 ARENA_HEAP_SET(nfp->a.attrs, heap);
37 ARENA_SIZE_SET(nfp->a.attrs, fsize-size);
38 nfp->a.tag = MALLOC_FREE;
39 #ifdef DEBUG_MALLOC
40 nfp->a.magic = ARENA_MAGIC;
41 #endif
42 ARENA_TYPE_SET(fp->a.attrs, ARENA_TYPE_USED);
43 ARENA_SIZE_SET(fp->a.attrs, size);
44 fp->a.tag = tag;
46 /* Insert into all-block chain */
47 nfp->a.prev = fp;
48 nfp->a.next = na;
49 na->a.prev = nfp;
50 fp->a.next = nfp;
52 /* Replace current block on free chain */
53 nfp->next_free = fp->next_free;
54 nfp->prev_free = fp->prev_free;
55 fp->next_free->prev_free = nfp;
56 fp->prev_free->next_free = nfp;
57 } else {
58 /* Allocate the whole block */
59 ARENA_TYPE_SET(fp->a.attrs, ARENA_TYPE_USED);
60 fp->a.tag = tag;
62 /* Remove from free chain */
63 fp->next_free->prev_free = fp->prev_free;
64 fp->prev_free->next_free = fp->next_free;
67 return (void *)(&fp->a + 1);
70 void *bios_malloc(size_t size, enum heap heap, malloc_tag_t tag)
72 struct free_arena_header *fp;
73 struct free_arena_header *head = &__core_malloc_head[heap];
74 void *p = NULL;
76 if (size) {
77 /* Add the obligatory arena header, and round up */
78 size = (size + 2 * sizeof(struct arena_header) - 1) & ARENA_SIZE_MASK;
80 for ( fp = head->next_free ; fp != head ; fp = fp->next_free ) {
81 if ( ARENA_SIZE_GET(fp->a.attrs) >= size ) {
82 /* Found fit -- allocate out of this block */
83 p = __malloc_from_block(fp, size, tag);
84 break;
89 return p;
92 static void *_malloc(size_t size, enum heap heap, malloc_tag_t tag)
94 void *p;
96 dprintf("_malloc(%zu, %u, %u) @ %p = ",
97 size, heap, tag, __builtin_return_address(0));
99 sem_down(&__malloc_semaphore, 0);
100 p = firmware->mem->malloc(size, heap, tag);
101 sem_up(&__malloc_semaphore);
103 dprintf("%p\n", p);
104 return p;
107 __export void *malloc(size_t size)
109 return _malloc(size, HEAP_MAIN, MALLOC_CORE);
112 __export void *lmalloc(size_t size)
114 void *p;
116 p = _malloc(size, HEAP_LOWMEM, MALLOC_CORE);
117 if (!p)
118 errno = ENOMEM;
119 return p;
122 void *pmapi_lmalloc(size_t size)
124 return _malloc(size, HEAP_LOWMEM, MALLOC_MODULE);
127 void *bios_realloc(void *ptr, size_t size)
129 struct free_arena_header *ah, *nah;
130 struct free_arena_header *head;
132 void *newptr;
133 size_t newsize, oldsize, xsize;
135 if (!ptr)
136 return malloc(size);
138 if (size == 0) {
139 free(ptr);
140 return NULL;
143 ah = (struct free_arena_header *)
144 ((struct arena_header *)ptr - 1);
146 head = &__core_malloc_head[ARENA_HEAP_GET(ah->a.attrs)];
148 #ifdef DEBUG_MALLOC
149 if (ah->a.magic != ARENA_MAGIC)
150 dprintf("failed realloc() magic check: %p\n", ptr);
151 #endif
153 /* Actual size of the old block */
154 //oldsize = ah->a.size;
155 oldsize = ARENA_SIZE_GET(ah->a.attrs);
157 /* Add the obligatory arena header, and round up */
158 newsize = (size + 2 * sizeof(struct arena_header) - 1) & ARENA_SIZE_MASK;
160 if (oldsize >= newsize && newsize >= (oldsize >> 2) &&
161 oldsize - newsize < 4096) {
162 /* This allocation is close enough already. */
163 return ptr;
164 } else {
165 xsize = oldsize;
167 nah = ah->a.next;
168 if ((char *)nah == (char *)ah + ARENA_SIZE_GET(ah->a.attrs) &&
169 ARENA_TYPE_GET(nah->a.attrs) == ARENA_TYPE_FREE &&
170 ARENA_SIZE_GET(nah->a.attrs) + oldsize >= newsize) {
171 //nah->a.type == ARENA_TYPE_FREE &&
172 //oldsize + nah->a.size >= newsize) {
173 /* Merge in subsequent free block */
174 ah->a.next = nah->a.next;
175 ah->a.next->a.prev = ah;
176 nah->next_free->prev_free = nah->prev_free;
177 nah->prev_free->next_free = nah->next_free;
178 ARENA_SIZE_SET(ah->a.attrs, ARENA_SIZE_GET(ah->a.attrs) +
179 ARENA_SIZE_GET(nah->a.attrs));
180 xsize = ARENA_SIZE_GET(ah->a.attrs);
183 if (xsize >= newsize) {
184 /* We can reallocate in place */
185 if (xsize >= newsize + 2 * sizeof(struct arena_header)) {
186 /* Residual free block at end */
187 nah = (struct free_arena_header *)((char *)ah + newsize);
188 ARENA_TYPE_SET(nah->a.attrs, ARENA_TYPE_FREE);
189 ARENA_SIZE_SET(nah->a.attrs, xsize - newsize);
190 ARENA_SIZE_SET(ah->a.attrs, newsize);
191 ARENA_HEAP_SET(nah->a.attrs, ARENA_HEAP_GET(ah->a.attrs));
193 #ifdef DEBUG_MALLOC
194 nah->a.magic = ARENA_MAGIC;
195 #endif
197 //nah->a.type = ARENA_TYPE_FREE;
198 //nah->a.size = xsize - newsize;
199 //ah->a.size = newsize;
201 /* Insert into block list */
202 nah->a.next = ah->a.next;
203 ah->a.next = nah;
204 nah->a.next->a.prev = nah;
205 nah->a.prev = ah;
207 /* Insert into free list */
208 if (newsize > oldsize) {
209 /* Hack: this free block is in the path of a memory object
210 which has already been grown at least once. As such, put
211 it at the *end* of the freelist instead of the beginning;
212 trying to save it for future realloc()s of the same block. */
213 nah->prev_free = head->prev_free;
214 nah->next_free = head;
215 head->prev_free = nah;
216 nah->prev_free->next_free = nah;
217 } else {
218 nah->next_free = head->next_free;
219 nah->prev_free = head;
220 head->next_free = nah;
221 nah->next_free->prev_free = nah;
224 /* otherwise, use up the whole block */
225 return ptr;
226 } else {
227 /* Last resort: need to allocate a new block and copy */
228 oldsize -= sizeof(struct arena_header);
229 newptr = malloc(size);
230 if (newptr) {
231 memcpy(newptr, ptr, min(size, oldsize));
232 free(ptr);
234 return newptr;
239 __export void *realloc(void *ptr, size_t size)
241 return firmware->mem->realloc(ptr, size);
244 __export void *zalloc(size_t size)
246 void *ptr;
248 ptr = malloc(size);
249 if (ptr)
250 memset(ptr, 0, size);
252 return ptr;