/* * 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. */ /* * This code is based on/inspired by jemalloc-3.3.1. Please see LICENSE.jemalloc for further details. */ #include #include #include #include #include #include #include #include "heap_internals.h" /*----------------------------------- * Base memory allocation functions. *----------------------------------- */ /* * Allocate a new chunk of memory for use by the base allocator, and point to it with the allocator pointers. * * Parameters: * - phd = Pointer to the HEAPDATA block. * - szMinimum = Minimum number of bytes we need to allocate. * * Returns: * - TRUE = Allocation failed. * - FALSE = Allocation succeeded. */ static BOOL base_alloc_new_chunk(PHEAPDATA phd, SIZE_T szMinimum) { SIZE_T szAdjusted; /* adjusted size to multiple of chunk size */ PVOID pvNewChunk; /* pointer to new chunk */ PVOID pvTemp; /* temporary pointer */ BOOL fZero = FALSE; /* do we need this zeroed? (no) */ szAdjusted = CHUNK_CEILING(phd, szMinimum); pvNewChunk = _HeapChunkAlloc(phd, szAdjusted, phd->szChunk, TRUE, &fZero); if (!pvNewChunk) return TRUE; /* allocation failed */ *((PPVOID)pvNewChunk) = phd->pvBasePages; pvTemp = (PVOID)(((UINT_PTR)pvNewChunk) + sizeof(PVOID)); *((SIZE_T *)pvTemp) = szAdjusted; phd->pvBasePages = pvNewChunk; phd->pvBaseNext = (PVOID)(((UINT_PTR)pvNewChunk) + sizeof(PVOID) + sizeof(SIZE_T)); phd->pvBasePast = (PVOID)(((UINT_PTR)pvNewChunk) + szAdjusted); return FALSE; } /* * Allocate a chunk of memory from the base allocator. * * Parameters: * - phd = Pointer to the HEAPDATA block. * - sz = Number of bytes to allocate. * * Returns: * - NULL = Allocation failed. * - Other = Pointer to the allocated block of memory. */ PVOID _HeapBaseAlloc(PHEAPDATA phd, SIZE_T sz) { PVOID rc = NULL; /* return from this function */ SIZE_T szAdjusted; /* adjusted size */ szAdjusted = SYS_CACHELINE_CEILING(sz); /* round up to cache line size */ IMutex_Lock(phd->pmtxBase); if (((UINT_PTR)(phd->pvBaseNext) + szAdjusted) > (UINT_PTR)(phd->pvBasePast)) { /* allocate new chunk if we don't have enough space for the allocation */ if (base_alloc_new_chunk(phd, szAdjusted + sizeof(PVOID) + sizeof(SIZE_T))) goto error0; /* failed */ } rc = phd->pvBaseNext; /* perform the allocation */ phd->pvBaseNext = (PVOID)((UINT_PTR)(phd->pvBaseNext) + szAdjusted); error0: IMutex_Unlock(phd->pmtxBase); return rc; } /* * Allocate a new EXTENT_NODE from the base allocator. * * Parameters: * - phd = Pointer to the HEAPDATA block. * * Returns: * - NULL = Allocation failed. * - Other = Pointer to the new EXTENT_NODE. */ PEXTENT_NODE _HeapBaseNodeAlloc(PHEAPDATA phd) { PEXTENT_NODE rc = NULL; /* return from this function */ IMutex_Lock(phd->pmtxBase); if (phd->pexnBaseNodes) { /* pull a node off the free list */ rc = phd->pexnBaseNodes; phd->pexnBaseNodes = *((PPEXTENT_NODE)rc); } IMutex_Unlock(phd->pmtxBase); if (!rc) /* use base allocator to get one */ rc = (PEXTENT_NODE)_HeapBaseAlloc(phd, sizeof(EXTENT_NODE)); return rc; } /* * Returns an EXTENT_NODE to the free list. * * Parameters: * - phd = Pointer to the HEAPDATA block. * - pexn = Pointer to the EXTENT_NODE to be freed. * * Returns: * Nothing. */ void _HeapBaseNodeDeAlloc(PHEAPDATA phd, PEXTENT_NODE pexn) { /* add it to the free list */ IMutex_Lock(phd->pmtxBase); *((PPEXTENT_NODE)pexn) = phd->pexnBaseNodes; phd->pexnBaseNodes = pexn; IMutex_Unlock(phd->pmtxBase); } /* * Set up the base allocator. * * Parameters: * - phd = Pointer to the HEAPDATA block. * * Returns: * Standard HRESULT success/failure indicator. */ HRESULT _HeapBaseSetup(PHEAPDATA phd) { HRESULT hr = IMutexFactory_CreateMutex(phd->pMutexFactory, &(phd->pmtxBase)); if (FAILED(hr)) return hr; phd->pexnBaseNodes = NULL; return S_OK; } /* * Shut down the base allocator. * * Parameters: * - phd = Pointer to the HEAPDATA block. * * Returns: * Nothing. */ void _HeapBaseShutdown(PHEAPDATA phd) { PVOID pChunk; /* the chunk we're working on */ PVOID pNextChunk; /* the next chunk to be dealt with */ PVOID pvTemp; /* temporary pointer */ SIZE_T szChunk; /* size of allocated chunk */ /* All allocated "base" chunks come straight from the IChunkAllocator, so it's safe to free them this way. */ pChunk = phd->pvBasePages; while (pChunk) { pNextChunk = *((PPVOID)pChunk); pvTemp = (PVOID)(((UINT_PTR)pChunk) + sizeof(PVOID)); szChunk = *((SIZE_T *)pvTemp); IChunkAllocator_FreeChunk(phd->pChunkAllocator, pChunk, szChunk); pChunk = pNextChunk; } IUnknown_Release(phd->pmtxBase); }