OpenCore  1.0.4
OpenCore Bootloader
Loading...
Searching...
No Matches
UmmMalloc.c
Go to the documentation of this file.
1/* ----------------------------------------------------------------------------
2 * umm_malloc.c - a memory allocator for embedded systems (microcontrollers)
3 *
4 * The MIT License (MIT)
5 *
6 * Copyright (c) 2015 Ralph Hempel
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in all
16 * copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 * SOFTWARE.
25 *
26 * ----------------------------------------------------------------------------
27 *
28 * R.Hempel 2007-09-22 - Original
29 * R.Hempel 2008-12-11 - Added MIT License biolerplate
30 * - realloc() now looks to see if previous block is free
31 * - made common operations functions
32 * R.Hempel 2009-03-02 - Added macros to disable tasking
33 * - Added function to dump heap and check for valid free
34 * pointer
35 * R.Hempel 2009-03-09 - Changed name to umm_malloc to avoid conflicts with
36 * the mm_malloc() library functions
37 * - Added some test code to assimilate a free block
38 * with the very block if possible. Complicated and
39 * not worth the grief.
40 * D.Frank 2014-04-02 - Fixed heap configuration when UMM_TEST_MAIN is NOT set,
41 * added user-dependent configuration file umm_malloc_cfg.h
42 * R.Hempel 2016-12-04 - Add support for Unity test framework
43 * - Reorganize source files to avoid redundant content
44 * - Move integrity and poison checking to separate file
45 * R.Hempel 2017-12-29 - Fix bug in realloc when requesting a new block that
46 * results in OOM error - see Issue 11
47 * vit9696 2018-02-07 - Changed types, masks and limits to support 32-bit pools
48 * - Removed realloc and calloc I do not need
49 * - Added pointer range check in free to detect memory that
50 * was not allocated by us
51 * - Made pool initialization external to avoid memset deps
52 * and to support initialization state
53 * - Switched to UEFI types, pragmas, renamed external API
54 * ----------------------------------------------------------------------------
55 */
56
57#include <Library/OcMemoryLib.h>
58
59STATIC UINT8 *default_umm_heap;
61
62#define UMM_MALLOC_CFG_HEAP_SIZE default_umm_heap_size
63#define UMM_MALLOC_CFG_HEAP_ADDR default_umm_heap
64
65#define UMM_BEST_FIT
66
67#define DBGLOG_DEBUG(format, ...) do { } while (0)
68#define DBGLOG_TRACE(froamt, ...) do { } while (0)
69
70#define UMM_CRITICAL_ENTRY()
71#define UMM_CRITICAL_EXIT()
72
73/* ------------------------------------------------------------------------- */
74
75#pragma pack(1)
76
77typedef struct umm_ptr_t {
78 UINT32 next;
79 UINT32 prev;
81
82typedef struct umm_block_t {
83 union {
86 union {
88 UINT8 data[4];
91
92#pragma pack()
93
94#define UMM_FREELIST_MASK (0x80000000)
95#define UMM_BLOCKNO_MASK (0x7FFFFFFF)
96
97/* ------------------------------------------------------------------------- */
98
100UINT32 umm_numblocks = 0;
101
102#define UMM_NUMBLOCKS (umm_numblocks)
103
104/* ------------------------------------------------------------------------ */
105
106#define UMM_BLOCK(b) (umm_heap[b])
107
108#define UMM_NBLOCK(b) (UMM_BLOCK(b).header.used.next)
109#define UMM_PBLOCK(b) (UMM_BLOCK(b).header.used.prev)
110#define UMM_NFREE(b) (UMM_BLOCK(b).body.free.next)
111#define UMM_PFREE(b) (UMM_BLOCK(b).body.free.prev)
112#define UMM_DATA(b) (UMM_BLOCK(b).body.data)
113
114/* ------------------------------------------------------------------------ */
115
116STATIC UINT32 umm_blocks( UINT32 size ) {
117
118 /*
119 * The calculation of the block size is not too difficult, but there are
120 * a few little things that we need to be mindful of.
121 *
122 * When a block removed from the free list, the space used by the free
123 * pointers is available for data. That's what the first calculation
124 * of size is doing.
125 */
126
127 if( size <= (sizeof(((umm_block *)0)->body)) )
128 return( 1 );
129
130 /*
131 * If it's for more than that, then we need to figure out the number of
132 * additional whole blocks the size of an umm_block are required.
133 */
134
135 size -= ( 1 + (sizeof(((umm_block *)0)->body)) );
136
137 return( 2 + size/(sizeof(umm_block)) );
138}
139
140/* ------------------------------------------------------------------------ */
141/*
142 * Split the block `c` into two blocks: `c` and `c + blocks`.
143 *
144 * - `new_freemask` should be `0` if `c + blocks` used, or `UMM_FREELIST_MASK`
145 * otherwise.
146 *
147 * Note that free pointers are NOT modified by this function.
148 */
149STATIC VOID umm_split_block( UINT32 c,
150 UINT32 blocks,
151 UINT32 new_freemask ) {
152
153 UMM_NBLOCK(c+blocks) = (UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) | new_freemask;
154 UMM_PBLOCK(c+blocks) = c;
155
156 UMM_PBLOCK(UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) = (c+blocks);
157 UMM_NBLOCK(c) = (c+blocks);
158}
159
160/* ------------------------------------------------------------------------ */
161
162STATIC VOID umm_disconnect_from_free_list( UINT32 c ) {
163 /* Disconnect this block from the FREE list */
164
167
168 /* And clear the free block indicator */
169
170 UMM_NBLOCK(c) &= (~UMM_FREELIST_MASK);
171}
172
173/* ------------------------------------------------------------------------
174 * The umm_assimilate_up() function assumes that UMM_NBLOCK(c) does NOT
175 * have the UMM_FREELIST_MASK bit set!
176 */
177
178STATIC VOID umm_assimilate_up( UINT32 c ) {
179
181 /*
182 * The next block is a free block, so assimilate up and remove it from
183 * the free list
184 */
185
186 DBGLOG_DEBUG( "Assimilate up to next block, which is FREE\n" );
187
188 /* Disconnect the next block from the FREE list */
189
191
192 /* Assimilate the next block with this one */
193
196 }
197}
198
199/* ------------------------------------------------------------------------
200 * The umm_assimilate_down() function assumes that UMM_NBLOCK(c) does NOT
201 * have the UMM_FREELIST_MASK bit set!
202 */
203
204STATIC UINT32 umm_assimilate_down( UINT32 c, UINT32 freemask ) {
205
206 UMM_NBLOCK(UMM_PBLOCK(c)) = UMM_NBLOCK(c) | freemask;
208
209 return( UMM_PBLOCK(c) );
210}
211
212/* ------------------------------------------------------------------------ */
213
214VOID umm_init( VOID ) {
215 /* init heap pointer and size, and memset it to 0 */
218
219 /*
220 * This is done at allocation step!
221 * memset(umm_heap, 0x00, UMM_MALLOC_CFG_HEAP_SIZE);
222 */
223
224 /* setup initial blank heap structure */
225 {
226 /* index of the 0th `umm_block` */
227 CONST UINT32 block_0th = 0;
228 /* index of the 1st `umm_block` */
229 CONST UINT32 block_1th = 1;
230 /* index of the latest `umm_block` */
231 CONST UINT32 block_last = UMM_NUMBLOCKS - 1;
232
233 /* setup the 0th `umm_block`, which just points to the 1st */
234 UMM_NBLOCK(block_0th) = block_1th;
235 UMM_NFREE(block_0th) = block_1th;
236 UMM_PFREE(block_0th) = block_1th;
237
238 /*
239 * Now, we need to set the whole heap space as a huge free block. We should
240 * not touch the 0th `umm_block`, since it's special: the 0th `umm_block`
241 * is the head of the free block list. It's a part of the heap invariant.
242 *
243 * See the detailed explanation at the beginning of the file.
244 */
245
246 /*
247 * 1th `umm_block` has pointers:
248 *
249 * - next `umm_block`: the latest one
250 * - prev `umm_block`: the 0th
251 *
252 * Plus, it's a free `umm_block`, so we need to apply `UMM_FREELIST_MASK`
253 *
254 * And it's the last free block, so the next free block is 0.
255 */
256 UMM_NBLOCK(block_1th) = block_last | UMM_FREELIST_MASK;
257 UMM_NFREE(block_1th) = 0;
258 UMM_PBLOCK(block_1th) = block_0th;
259 UMM_PFREE(block_1th) = block_0th;
260
261 /*
262 * latest `umm_block` has pointers:
263 *
264 * - next `umm_block`: 0 (meaning, there are no more `umm_blocks`)
265 * - prev `umm_block`: the 1st
266 *
267 * It's not a free block, so we don't touch NFREE / PFREE at all.
268 */
269 UMM_NBLOCK(block_last) = 0;
270 UMM_PBLOCK(block_last) = block_1th;
271 }
272}
273
274/* ------------------------------------------------------------------------ */
275
276BOOLEAN UmmInitialized ( VOID ) {
277 return default_umm_heap != NULL;
278}
279
280/* ------------------------------------------------------------------------ */
281
282VOID UmmSetHeap( VOID *heap, UINT32 size ) {
283 default_umm_heap = (UINT8 *)heap;
285 umm_init();
286}
287
288/* ------------------------------------------------------------------------ */
289
290BOOLEAN UmmFree( VOID *ptr ) {
291
292 UINT32 c;
293 UINT8 *cptr = (UINT8 *)ptr;
294
295 /* If we are not initialised, reuturn false! */
296 if ( !UmmInitialized() )
297 return FALSE;
298
299 /* If we're being asked to free a NULL pointer, well that's just silly! */
300
301 if( (VOID *)0 == ptr ) {
302 DBGLOG_DEBUG( "free a null pointer -> do nothing\n" );
303
304 return FALSE;
305 }
306
307 /* If we're being asked to free an unrelated pointer, return FALSE as well! */
308
309 if (cptr < default_umm_heap || cptr >= default_umm_heap + UMM_MALLOC_CFG_HEAP_SIZE)
310 return FALSE;
311
312 /*
313 * FIXME: At some point it might be a good idea to add a check to make sure
314 * that the pointer we're being asked to free up is actually within
315 * the umm_heap!
316 *
317 * NOTE: See the new umm_info() function that you can use to see if a ptr is
318 * on the free list!
319 */
320
321 /* Protect the critical section... */
323
324 /* Figure out which block we're in. Note the use of truncated division... */
325
326 c = (UINT32)((((UINT8 *)ptr)-(UINT8 *)(&(umm_heap[0])))/sizeof(umm_block));
327
328 DBGLOG_DEBUG( "Freeing block %6i\n", c );
329
330 /* Now let's assimilate this block with the next one if possible. */
331
333
334 /* Then assimilate with the previous block if possible */
335
337
338 DBGLOG_DEBUG( "Assimilate down to next block, which is FREE\n" );
339
341 } else {
342 /*
343 * The previous block is not a free block, so add this one to the head
344 * of the free list
345 */
346
347 DBGLOG_DEBUG( "Just add to head of free list\n" );
348
349 UMM_PFREE(UMM_NFREE(0)) = c;
350 UMM_NFREE(c) = UMM_NFREE(0);
351 UMM_PFREE(c) = 0;
352 UMM_NFREE(0) = c;
353
355 }
356
357 /* Release the critical section... */
359
360 return TRUE;
361}
362
363/* ------------------------------------------------------------------------ */
364
365VOID *UmmMalloc( UINT32 size ) {
366 UINT32 blocks;
367 UINT32 blockSize = 0;
368
369 UINT32 bestSize;
370 UINT32 bestBlock;
371
372 UINT32 cf;
373
374 /* If we are not initialised, reuturn false! */
375 if ( !UmmInitialized() )
376 return NULL;
377
378 /*
379 * the very first thing we do is figure out if we're being asked to allocate
380 * a size of 0 - and if we are we'll simply return a null pointer. if not
381 * then reduce the size by 1 byte so that the subsequent calculations on
382 * the number of blocks to allocate are easier...
383 */
384
385 if( 0 == size ) {
386 DBGLOG_DEBUG( "malloc a block of 0 bytes -> do nothing\n" );
387
388 return( (VOID *)NULL );
389 }
390
391 /* Protect the critical section... */
393
394 blocks = umm_blocks( size );
395
396 /*
397 * Now we can scan through the free list until we find a space that's big
398 * enough to hold the number of blocks we need.
399 *
400 * This part may be customized to be a best-fit, worst-fit, or first-fit
401 * algorithm
402 */
403
404 cf = UMM_NFREE(0);
405
406 bestBlock = UMM_NFREE(0);
407 bestSize = 0x7FFFFFFF;
408
409 while( cf ) {
410 blockSize = (UMM_NBLOCK(cf) & UMM_BLOCKNO_MASK) - cf;
411
412 DBGLOG_TRACE( "Looking at block %6i size %6i\n", cf, blockSize );
413
414#if defined UMM_BEST_FIT
415 if( (blockSize >= blocks) && (blockSize < bestSize) ) {
416 bestBlock = cf;
417 bestSize = blockSize;
418 }
419#elif defined UMM_FIRST_FIT
420 /* This is the first block that fits! */
421 if( (blockSize >= blocks) )
422 break;
423#else
424# error "No UMM_*_FIT is defined - check umm_malloc_cfg.h"
425#endif
426
427 cf = UMM_NFREE(cf);
428 }
429
430 if( 0x7FFFFFFF != bestSize ) {
431 cf = bestBlock;
432 blockSize = bestSize;
433 }
434
435 if( UMM_NBLOCK(cf) & UMM_BLOCKNO_MASK && blockSize >= blocks ) {
436 /*
437 * This is an existing block in the memory heap, we just need to split off
438 * what we need, unlink it from the free list and mark it as in use, and
439 * link the rest of the block back into the freelist as if it was a new
440 * block on the free list...
441 */
442
443 if( blockSize == blocks ) {
444 /* It's an exact fit and we don't neet to split off a block. */
445 DBGLOG_DEBUG( "Allocating %6i blocks starting at %6i - exact\n", blocks, cf );
446
447 /* Disconnect this block from the FREE list */
448
450
451 } else {
452 /* It's not an exact fit and we need to split off a block. */
453 DBGLOG_DEBUG( "Allocating %6i blocks starting at %6i - existing\n", blocks, cf );
454
455 /*
456 * split current free block `cf` into two blocks. The first one will be
457 * returned to user, so it's not free, and the second one will be free.
458 */
459 umm_split_block( cf, blocks, UMM_FREELIST_MASK /*new block is free*/ );
460
461 /*
462 * `umm_split_block()` does not update the free pointers (it affects
463 * only free flags), but effectively we've just moved beginning of the
464 * free block from `cf` to `cf + blocks`. So we have to adjust pointers
465 * to and from adjacent free blocks.
466 */
467
468 /* previous free block */
469 UMM_NFREE( UMM_PFREE(cf) ) = cf + blocks;
470 UMM_PFREE( cf + blocks ) = UMM_PFREE(cf);
471
472 /* next free block */
473 UMM_PFREE( UMM_NFREE(cf) ) = cf + blocks;
474 UMM_NFREE( cf + blocks ) = UMM_NFREE(cf);
475 }
476 } else {
477 /* Out of memory */
478
479 DBGLOG_DEBUG( "Can't allocate %5i blocks\n", blocks );
480
481 /* Release the critical section... */
483
484 return( (VOID *)NULL );
485 }
486
487 /* Release the critical section... */
489
490 return( (VOID *)&UMM_DATA(cf) );
491}
492
493/* ------------------------------------------------------------------------ */
UINT32 size
#define DBGLOG_DEBUG(format,...)
Definition UmmMalloc.c:67
BOOLEAN UmmFree(VOID *ptr)
Definition UmmMalloc.c:290
umm_block * umm_heap
Definition UmmMalloc.c:99
#define UMM_PFREE(b)
Definition UmmMalloc.c:111
VOID umm_init(VOID)
Definition UmmMalloc.c:214
#define UMM_NBLOCK(b)
Definition UmmMalloc.c:108
#define UMM_FREELIST_MASK
Definition UmmMalloc.c:94
UINT32 umm_numblocks
Definition UmmMalloc.c:100
STATIC VOID umm_assimilate_up(UINT32 c)
Definition UmmMalloc.c:178
#define UMM_CRITICAL_ENTRY()
Definition UmmMalloc.c:70
#define UMM_BLOCKNO_MASK
Definition UmmMalloc.c:95
STATIC UINT8 * default_umm_heap
Definition UmmMalloc.c:59
STATIC UINT32 umm_blocks(UINT32 size)
Definition UmmMalloc.c:116
struct umm_block_t umm_block
#define UMM_MALLOC_CFG_HEAP_ADDR
Definition UmmMalloc.c:63
#define UMM_DATA(b)
Definition UmmMalloc.c:112
VOID UmmSetHeap(VOID *heap, UINT32 size)
Definition UmmMalloc.c:282
#define UMM_CRITICAL_EXIT()
Definition UmmMalloc.c:71
STATIC UINT32 umm_assimilate_down(UINT32 c, UINT32 freemask)
Definition UmmMalloc.c:204
#define UMM_MALLOC_CFG_HEAP_SIZE
Definition UmmMalloc.c:62
STATIC VOID umm_split_block(UINT32 c, UINT32 blocks, UINT32 new_freemask)
Definition UmmMalloc.c:149
#define UMM_PBLOCK(b)
Definition UmmMalloc.c:109
#define DBGLOG_TRACE(froamt,...)
Definition UmmMalloc.c:68
STATIC VOID umm_disconnect_from_free_list(UINT32 c)
Definition UmmMalloc.c:162
struct umm_ptr_t umm_ptr
#define UMM_NFREE(b)
Definition UmmMalloc.c:110
STATIC UINT32 default_umm_heap_size
Definition UmmMalloc.c:60
#define UMM_NUMBLOCKS
Definition UmmMalloc.c:102
VOID * UmmMalloc(UINT32 size)
Definition UmmMalloc.c:365
BOOLEAN UmmInitialized(VOID)
Definition UmmMalloc.c:276
umm_ptr free
Definition UmmMalloc.c:87
union umm_block_t::@96 header
union umm_block_t::@97 body
umm_ptr used
Definition UmmMalloc.c:84
UINT8 data[4]
Definition UmmMalloc.c:88
UINT32 next
Definition UmmMalloc.c:78
UINT32 prev
Definition UmmMalloc.c:79