/* * This file is part of the COMROGUE Operating System for Raspberry Pi * * Copyright (c) 2013, Eric J. Bowersox / Erbosoft Enterprises * All rights reserved. * * This program is free for commercial and non-commercial use as long as the following conditions are * adhered to. * * Copyright in this file remains Eric J. Bowersox and/or Erbosoft, and as such any copyright notices * in the code are not to be removed. * * Redistribution and use in source and binary forms, with or without modification, are permitted * provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of conditions and * the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and * the following disclaimer in the documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * "Raspberry Pi" is a trademark of the Raspberry Pi Foundation. */ #include #include #include #include #include #include "sizes.h" /*------------------------------------------------------------------------------------------------------------- * Initial heap implementation. Since this will only be used by initializer code and freed with the rest * of the initializer code and data, it doesn't need to be very efficient. The implementation is adapted from * the original K&R storage allocator, with modifications to implement the full IMalloc interface. *------------------------------------------------------------------------------------------------------------- */ extern PCHAR g_pInitHeapBlock; /* pointer to heap init block defined in assembly code */ typedef union tagBLOCK { struct { union tagBLOCK *pNextFree; /* pointer to next free block */ SIZE_T cblk; /* size of this free block (in blocks) */ } data; INT64 x; /* to force alignment */ } BLOCK, *PBLOCK; typedef struct tagINITHEAP { IMalloc hdr; /* object header must be first */ BLOCK blkBase; /* base "zero" block */ PBLOCK pblkLastAlloc; /* last allocated block */ } INITHEAP, *PINITHEAP; /* * Allocates a block of memory. * * Parameters: * - pThis = Base interface pointer. * - cb = Size of the memory block to be allocated, in bytes. * * Returns: * A pointer to the allocated block of memory, or NULL if memory could not be allocated. */ SEG_INIT_CODE static PVOID init_heap_Alloc(IMalloc *pThis, SIZE_T cb) { PINITHEAP pih = (PINITHEAP)pThis; register PBLOCK p, q; register SIZE_T nBlocks = 1 + (cb + sizeof(BLOCK) - 1) / sizeof(BLOCK); q = pih->pblkLastAlloc; for (p = q->data.pNextFree; ; q = p, p = p->data.pNextFree) { if (p->data.cblk >= nBlocks) { if (p->data.cblk == nBlocks) q->data.pNextFree = p->data.pNextFree; else { p->data.cblk -= nBlocks; p += p->data.cblk; p->data.cblk = nBlocks; } pih->pblkLastAlloc = q; return (PVOID)(p + 1); } if (p == pih->pblkLastAlloc) break; } return NULL; } /* * Determines whether this allocator was used to allocate a block of memory. * * Parameters: * - pThis = Base interface pointer. * - pv = Pointer to the memory block to test. If this parameter is NULL, -1 is returned. * * Returns: * - 1 = If the memory block was allocated by this allocator. * - 0 = If the memory block was not allocated by this allocator. * - -1 = If this method cannot determine whether the allocator allocated the memory block. */ SEG_INIT_CODE static INT32 init_heap_DidAlloc(IMalloc *pThis, PVOID pv) { register PCHAR p = (PCHAR)pv; if (!pv) return -1; /* not our business */ return ((p >= g_pInitHeapBlock) && (p < g_pInitHeapBlock + SIZE_INIT_HEAP)) ? 1 : 0; } /* * Frees a previously allocated block of memory. * * Parameters: * - pThis = Base interface pointer. * - pv = Pointer to the memory block to be freed. If this parameter is NULL, this method has no effect. * * Returns: * Nothing. * * Side effects: * After this call, the memory pointed to by pv is invalid and should not be used. */ SEG_INIT_CODE static void init_heap_Free(IMalloc *pThis, PVOID pv) { PINITHEAP pih = (PINITHEAP)pThis; register PBLOCK p, q; if (init_heap_DidAlloc(pThis, pv) != 1) return; /* not our business */ p = ((PBLOCK)pv) - 1; for (q = pih->pblkLastAlloc; !((p > q) && (p < q->data.pNextFree)); q = q->data.pNextFree) if ((q >= q->data.pNextFree) && ((p > q) || (p < q->data.pNextFree))) break; /* at one end or another */ if (p + p->data.cblk == q->data.pNextFree) { /* coalesce block with next (free) block */ p->data.cblk += q->data.pNextFree->data.cblk; p->data.pNextFree = q->data.pNextFree->data.pNextFree; } else p->data.pNextFree = q->data.pNextFree; /* chain to next free block */ if (q + q->data.cblk == p) { /* coalesce free block with previous (free) block */ q->data.cblk += p->data.cblk; q->data.pNextFree = p->data.pNextFree; } else q->data.pNextFree = p; /* chain to previous free block */ pih->pblkLastAlloc = q; } /* * Changes the size of a previously allocated block. * * Parameters: * - pThis = Base interface pointer. * - pv = Pointer to the block of memory to be reallocated. If this parameter is NULL, a block of memory * of size cb is allocated and returned. * - cb = The new size of the memory block to be reallocated, in bytes. If this parameter is 0 and pv is * not NULL, the block of memory pointed to by pv is freed and NULL is returned. * * Returns: * If pv is not NULL and cb is 0, NULL is always returned. Otherwise, NULL is returned if the block of memory * could not be reallocated, or the pointer to the reallocated block of memory is returned. */ SEG_INIT_CODE static PVOID init_heap_Realloc(IMalloc *pThis, PVOID pv, SIZE_T cb) { PINITHEAP pih = (PINITHEAP)pThis; SIZE_T nBlocksNew, nBlocksExtra; PVOID pNew; register PBLOCK p, pNext, q, qp; /* Handle degenerate cases */ if (!pv) return init_heap_Alloc(pThis, cb); if (cb == 0) { init_heap_Free(pThis, pv); return NULL; } if (init_heap_DidAlloc(pThis, pv) != 1) return NULL; /* not our business */ p = ((PBLOCK)pv) - 1; nBlocksNew = 1 + (cb + sizeof(BLOCK) - 1) / sizeof(BLOCK); if (nBlocksNew == p->data.cblk) return pv; /* nothing to do! */ if (nBlocksNew < p->data.cblk) { /* shrinking block - chop block in middle and free the upper end */ pNext = p + nBlocksNew; pNext->data.cblk = p->data.cblk - nBlocksNew; p->data.cblk = nBlocksNew; init_heap_Free(pThis, (PVOID)(pNext + 1)); return pv; } /* see if next block is free so we can expand in place */ nBlocksExtra = nBlocksNew - p->data.cblk; pNext = p + p->data.cblk; qp = pih->pblkLastAlloc; for (q = qp->data.pNextFree; ; qp = q, q = q->data.pNextFree) { if (q == pNext) { if (q->data.cblk < nBlocksExtra) break; /* cannot get enough blocks by combining next free block */ qp->data.pNextFree = q->data.pNextFree; /* remove block from free list */ if (q->data.cblk == nBlocksExtra) { /* take it all */ pih->pblkLastAlloc = qp->data.pNextFree; } else { /* chop in two, add first block to existing, free second block */ pNext += nBlocksExtra; pNext->data.cblk = q->data.cblk - nBlocksExtra; init_heap_Free(pThis, (PVOID)(pNext + 1)); } p->data.cblk = nBlocksNew; return pv; } if (q == pih->pblkLastAlloc) break; /* not found */ } /* last ditch effort: allocate new block and copy old contents in */ pNew = init_heap_Alloc(pThis, cb); if (!pNew) return NULL; /* cannot reallocate */ StrCopyMem(pv, pNew, (p->data.cblk - 1) * sizeof(BLOCK)); init_heap_Free(pThis, pv); return pNew; } /* * Returns the size of a previously-allocated block of memory. * * Parameters: * - pThis = Base interface pointer. * - pv = Pointer to the block of memory. * * Returns: * The size of the allocated block of memory in bytes, which may be greater than the size requested when * it was allocated. */ SEG_INIT_CODE static SIZE_T init_heap_GetSize(IMalloc *pThis, PVOID pv) { register PBLOCK p; if (init_heap_DidAlloc(pThis, pv) != 1) return (SIZE_T)(-1); /* not our business */ p = ((PBLOCK)pv) - 1; return (p->data.cblk - 1) * sizeof(BLOCK); } static const SEG_INIT_RODATA struct IMallocVTable vtblInitHeap = { .QueryInterface = ObjHlpStandardQueryInterface_IMalloc, .AddRef = ObjHlpStaticAddRefRelease, .Release = ObjHlpStaticAddRefRelease, .Alloc = init_heap_Alloc, .Realloc = init_heap_Realloc, .Free = init_heap_Free, .GetSize = init_heap_GetSize, .DidAlloc = init_heap_DidAlloc, .HeapMinimize = (void (*)(IMalloc *))ObjHlpDoNothingReturnVoid }; /* * Returns a reference to the initial heap. * * Parameters: * None. * * Returns: * A reference to the initial heap, in the form of a pointer to its IMalloc interface. */ SEG_INIT_CODE IMalloc *_MmGetInitHeap(void) { static SEG_INIT_DATA INITHEAP initheap = { .pblkLastAlloc = NULL }; register PBLOCK p; if (!(initheap.pblkLastAlloc)) { /* initialize fields of initheap */ initheap.hdr.pVTable = &vtblInitHeap; initheap.pblkLastAlloc = initheap.blkBase.data.pNextFree = &(initheap.blkBase); initheap.blkBase.data.cblk = 0; /* add g_pInitHeapBlock as the free block in the heap */ p = (PBLOCK)g_pInitHeapBlock; p->data.cblk = SIZE_INIT_HEAP / sizeof(BLOCK); init_heap_Free((IMalloc *)(&initheap), (PVOID)(p + 1)); } return (IMalloc *)(&initheap); }