diff --git a/idl/comrogue/mutex.idl b/idl/comrogue/mutex.idl new file mode 100644 index 0000000..8e9f405 --- /dev/null +++ b/idl/comrogue/mutex.idl @@ -0,0 +1,58 @@ +/* + * 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. + */ +import "comrogue/objectbase.idl"; + +/*------------------ + * IMutex interface + *------------------ + */ + +[object, uuid(ace3dd2d-3623-4a0d-b004-6de350c3d5a9), pointer_default(unique)] +interface IMutex: IUnknown +{ + [unique] typedef IMutex *PMUTEX; + HRESULT Lock(void); + HRESULT TryLock(void); + HRESULT Unlock(void); +} + +/*------------------------- + * IMutexFactory interface + *------------------------- + */ + +[object, uuid(b85c7fb6-6878-431c-9d90-e05bff3a5142), pointer_default(unique)] +interface IMutexFactory: IUnknown +{ + [unique] typedef IMutexFactory *PMUTEXFACTORY; + HRESULT CreateMutex([out] IMutex **ppmtx); +} diff --git a/include/comrogue/heap.h b/include/comrogue/heap.h index d4506aa..0eb7945 100644 --- a/include/comrogue/heap.h +++ b/include/comrogue/heap.h @@ -38,6 +38,7 @@ #include #include #include +#include /*------------------------------------------------------------------------------------ * The raw heap data used to hold the heap internals. It's defined as opaque memory. @@ -56,10 +57,12 @@ typedef void (*PFNRAWHEAPDATAFREE)(PRAWHEAPDATA prhd); /* function that optional *-------------------- */ +#define STD_CHUNK_BITS 22 /* standard number of bits in a memory chunk - yields 4Mb chunks */ + CDECL_BEGIN extern HRESULT HeapCreate(PRAWHEAPDATA prhd, PFNRAWHEAPDATAFREE pfnFree, IChunkAllocator *pChunkAllocator, - IMalloc **ppHeap); + IMutexFactory *pMutexFactory, UINT32 nChunkBits, IMalloc **ppHeap); CDECL_END diff --git a/include/comrogue/internals/dlist.h b/include/comrogue/internals/dlist.h new file mode 100644 index 0000000..20e9443 --- /dev/null +++ b/include/comrogue/internals/dlist.h @@ -0,0 +1,162 @@ +/* + * 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. + */ +#ifndef __DLIST_H_INCLUDED +#define __DLIST_H_INCLUDED + +#ifndef __ASM__ + +#include + +/*---------------------------------------- + * Doubly-linked circular list operations + *---------------------------------------- + */ + +/* Declare a field in a structure to make it part of a doubly-linked list. */ +#define DLIST_FIELD_DECLARE(type, fieldname) \ + struct { type *pDLNext; type *pDLPrev; } fieldname + +/* Access "next" and "previous" elements. */ +#define dlistNext(ptr, fieldname) ((ptr)->fieldname.pDLNext) +#define dlistPrev(ptr, fieldname) ((ptr)->fieldname.pDLPrev) + +/* Initialize a node to be a list of only itself. */ +#define dlistNodeInit(ptr, fieldname) \ + do { dlistNext(ptr, fieldname) = dlistPrev(ptr, fieldname) = (ptr); } while (0) + +/* Insert a node before another in a list. */ +#define dlistInsertBefore(ptrInsBefore, ptrNew, fieldname) \ + do { dlistPrev(ptrNew, fieldname) = dlistPrev(ptrInsBefore, fieldname); \ + dlistNext(ptrNew, fieldname) = (ptrInsBefore); \ + dlistNext(dlistPrev(ptrInsBefore, fieldname), fieldname) = (ptrNew); \ + dlistPrev(ptrInsBefore) = (ptrNew); } while (0) + +/* Insert a node after another in a list. */ +#define dlistInsertAfter(ptrInsAfter, ptrNew, fieldname) \ + do { dlistNext(ptrNew, fieldname) = dlistNext(ptrInsAfter, fieldname); \ + dlistPrev(ptrNew, fieldname) = (ptrInsAfter); \ + dlistPrev(dlistNext(ptrInsAfter, fieldname), fieldname) = (ptrNew); \ + dlistNext(ptrInsAfter, fieldname) = (ptrNew); } while (0) + +/* Join two lists together. */ +#define dlistJoin(ptrA, ptrB, fieldname) \ + do { void *tmp; \ + dlistNext(dlistPrev(ptrA, fieldname), fieldname) = (ptrB); \ + dlistNext(dlistPrev(ptrB, fieldname), fieldname) = (ptrA); \ + tmp = dlistPrev(ptrA, fieldname); dlistPrev(ptrA, fieldname) = dlistPrev(ptrB, fieldname); \ + dlistPrev(ptrB, fieldname) = tmp; } while (0) + +/* Split a list into two lists. (Same code as joining) */ +#define dlistSplit(ptrA, ptrB, fieldname) dlistJoin(ptrA, ptrB, fieldname) + +/* Remove an element from a list. */ +#define dlistRemove(ptr, fieldname) \ + do { dlistPrev(dlistNext(ptr, fieldname), fieldname) = dlistPrev(ptr, fieldname); \ + dlistNext(dlistPrev(ptr, fieldname), fieldname) = dlistNext(ptr, fieldname); \ + dlistNodeInit(ptr, fieldname); } while (0) + +/* Declare a loop to iterate through a list. */ +#define dlistForEach(var, ptr, fieldname) \ + for ((var) = (ptr); (var); (var) = ((dlistNext(var, fieldname) != ptr) ? dlistNext(var, fieldname) : NULL)) + +/* Declare a loop to iterate through a list in reverse order. */ +#define dlistForEachReverse(var, ptr, fieldname) \ + for ((var) = ((ptr) ? dlistPrev(ptr, fieldname) : NULL); (var); \ + (var) = (((var) != (ptr)) ? dlistPrev(var, fieldname) : NULL)) + +/*------------------------------------ + * List header and operations thereon + *------------------------------------ + */ + +/* Declaration and static initializers for a list header field. */ +#define DLIST_HEAD_DECLARE(type, headfieldname) \ + struct { type *pDLHFirst; } headfieldname +#define DLIST_HEAD_INIT { NULL } +#define DLIST_HEAD_NAMED_INIT(headfieldname) \ + .headfieldname = DLIST_HEAD_INIT + +/* Pointers to the first and last elements of a list. */ +#define dlistFirst(hptr, fieldname) ((hptr)->pDLHFirst) +#define dlistLast(hptr, fieldname) (dlistFirst(hptr) ? dlistPrev(dlistFirst(hptr), fieldname) : NULL) + +/* Reinitializer for a list. */ +#define dlistListInit(hptr, fieldname) do { dlistFirst(hptr, fieldname) = NULL; } while (0) + +/* Move forward and back in a list, returning NULL if we go past the "end" in either case. */ +#define dlistListNext(hptr, ptr, fieldname) \ + ((dlistLast(hptr, fieldname) != (ptr)) ? dlistNext(ptr, fieldname) : NULL) +#define dlistListPrev(hptr, ptr, fieldname) \ + ((dlistFirst(hptr, fieldname) != (ptr)) ? dlistPrev(ptr, fieldname) : NULL) + +/* Insert an element before another one in a list. */ +#define dlistListInsertBefore(hptr, ptrInsBefore, ptrNew, fieldname) \ + do { dlistInsertBefore(ptrInsBefore, ptrNew, fieldname); \ + if (dlistFirst(hptr, fieldname) == (ptrInsBefore)) { dlistFirst(hptr, fieldname) = (ptrNew); } } while (0) + +/* Insert an element after another one in a list. */ +#define dlistListInsertAfter(hptr, ptrInsAfter, ptrNew, fieldname) \ + dlistInsertAfter(ptrInsAfter, ptrNew, fieldname) + +/* Insert an element as the first one in a list. */ +#define dlistListInsertFirst(hptr, ptrNew, fieldname) \ + do { if (dlistFirst(hptr, fieldname)) { dlistInsertBefore(dlistFirst(hptr, fieldname), ptrNew, fieldname); } \ + dlistFirst(hptr, fieldname) = (ptrNew); } while (0) + +/* Insert an element as the last one in a list. */ +#define dlistListInsertLast(hptr, ptrNew, fieldname) \ + do { if (dlistFirst(hptr, fieldname)) { dlistInsertBefore(dlistFirst(hptr, fieldname), ptrNew, fieldname); } \ + dlistFirst(hptr, fieldname) = dlistNext(ptrNew, fieldname); } while (0) + +/* Remove an element from a list. */ +#define dlistListRemove(hptr, ptr, fieldname) \ + do { if (dlistFirst(hptr, fieldname) == (ptr)) { dlistFirst(hptr, fieldname) = dlistNext(ptr, fieldname); } \ + if (dlistFirst(hptr, fieldname) != (ptr)) { dlistRemove(ptr, fieldname); } \ + else { dlistListInit(hptr, fieldname); } } while (0) + +/* Remove the first element from a list. */ +#define dlistListRemoveFirst(hptr, type, fieldname) \ + do { type *tmp = dlistFirst(hptr, fieldname); dlistListRemove(hptr, tmp, fieldname); } while (0) + +/* Remove the last element from a list. */ +#define dlistListRemoveLast(hptr, type, fieldname) \ + do { type *tmp = dlistLast(hptr, fieldname); dlistListRemove(hptr, tmp, fieldname); } while (0) + +/* Declare a loop to iterate through a list. */ +#define dlistListForEach(var, hptr, fieldname) dlistForEach(var, dlistFirst(hptr, fieldname), fieldname) + +/* Declare a loop to iterate through a list in reverse order. */ +#define dlistListForEachReverse(var, hptr, fieldname) dlistForEachReverse(var, dlistFirst(hptr, fieldname), fieldname) + +#endif /* __ASM__ */ + +#endif /* __DLIST_H_INCLUDED */ diff --git a/include/comrogue/internals/mmu.h b/include/comrogue/internals/mmu.h index 9e1f927..b56e7ce 100644 --- a/include/comrogue/internals/mmu.h +++ b/include/comrogue/internals/mmu.h @@ -53,6 +53,13 @@ #define SYS_PGTBL_SIZE 1024 /* page tables must be located on this boundary and are this size */ #define SYS_PGTBL_BITS 8 /* log2(SYS_PGTBL_SIZE/4), number of bits in a page table address */ #define SYS_PGTBL_ENTRIES 256 /* SYS_PGTBL_SIZE/4, number of entries in a page table */ +#define SYS_CACHELINE_SIZE 64 /* width of a cache line in bytes */ +#define SYS_CACHELINE_BITS 6 /* log2(SYS_CACHELINE_SIZE), number of bits in a cache line */ + +#define SYS_PAGE_MASK (SYS_PAGE_SIZE - 1) +#define SYS_CACHELINE_MASK (SYS_CACHELINE_SIZE - 1) + +#define SYS_CACHELINE_CEILING(sz) (((sz) + SYS_CACHELINE_MASK) & ~SYS_CACHELINE_MASK) /* Section descriptor bits */ #define TTBSEC_PXN 0x00000001 /* Privileged Execute-Never */ diff --git a/include/comrogue/scode.h b/include/comrogue/scode.h index f89da16..130c23f 100644 --- a/include/comrogue/scode.h +++ b/include/comrogue/scode.h @@ -181,5 +181,6 @@ #define MEMMGR_E_RECURSED SCODE_CAST(0x86010007) /* tried to recurse into page allocation */ #define MEMMGR_E_BADTAGS SCODE_CAST(0x86010008) /* invalid tags for freed page */ #define MEMMGR_E_BADHEAPDATASIZE SCODE_CAST(0x86010009) /* bad size of raw heap data block */ +#define MEMMGR_E_BADCHUNKSIZE SCODE_CAST(0x8601000A) /* bad chunk size for heap */ #endif /* __SCODE_H_INCLUDED */ diff --git a/include/comrogue/types.h b/include/comrogue/types.h index b9f84fc..fe1758e 100644 --- a/include/comrogue/types.h +++ b/include/comrogue/types.h @@ -89,8 +89,8 @@ typedef unsigned short wchar_t; #ifdef __COMROGUE_INTERNALS__ /* Internal system types */ -typedef UINT32 PHYSADDR; /* physical address */ -typedef UINT32 KERNADDR; /* kernel address */ +typedef UINT_PTR PHYSADDR; /* physical address */ +typedef UINT_PTR KERNADDR; /* kernel address */ typedef PHYSADDR *PPHYSADDR; typedef KERNADDR *PKERNADDR; diff --git a/kernel/lib/Makefile b/kernel/lib/Makefile index 0e1b243..8f6bb49 100644 --- a/kernel/lib/Makefile +++ b/kernel/lib/Makefile @@ -32,8 +32,9 @@ MAKEFLAGS += -rR CRBASEDIR := $(abspath ../..) include $(CRBASEDIR)/armcompile.mk -LIB_OBJS = divide.o qdivrem.o heap_toplevel.o intlib.o objhelp.o objhelp_enumconn.o objhelp_enumgeneric.o \ - objhelp_fixedcp.o rbtree.o str.o strcopymem.o strcomparemem.o strsetmem.o lib_guids.o +LIB_OBJS = divide.o qdivrem.o heap_toplevel.o heap_base.o heap_chunks.o intlib.o objhelp.o objhelp_enumconn.o \ + objhelp_enumgeneric.o objhelp_fixedcp.o rbtree.o str.o strcopymem.o strcomparemem.o \ + strsetmem.o lib_guids.o all: kernel-lib.o diff --git a/kernel/lib/heap_base.c b/kernel/lib/heap_base.c new file mode 100644 index 0000000..9cebd2b --- /dev/null +++ b/kernel/lib/heap_base.c @@ -0,0 +1,122 @@ +/* + * 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" + +static BOOL base_alloc_new_chunk(PHEAPDATA phd, SIZE_T szMinimum) +{ + BOOL fZero = FALSE; /* do we need this zeroed? (no) */ + SIZE_T szAdjusted; /* adjusted size to multiple of chunk size */ + PVOID pvNewChunk; /* pointer to new chunk */ + + szAdjusted = CHUNK_CEILING(phd, szMinimum); + pvNewChunk = _HeapChunkAlloc(phd, szAdjusted, phd->szChunk, TRUE, &fZero); + if (!pvNewChunk) + return TRUE; /* allocation failed */ + *((PPVOID)pvNewChunk) = phd->pvBasePages; + phd->pvBasePages = pvNewChunk; + phd->pvBaseNext = (PVOID)(((UINT_PTR)pvNewChunk) + sizeof(PVOID)); + phd->pvBasePast = (PVOID)(((UINT_PTR)pvNewChunk) + szAdjusted); + return FALSE; +} + +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))) + goto error0; + } + + rc = phd->pvBaseNext; /* perform the allocation */ + phd->pvBaseNext = (PVOID)((UINT_PTR)(phd->pvBaseNext) + szAdjusted); + +error0: + IMutex_Unlock(phd->pmtxBase); + return rc; +} + +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; +} + +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); +} + +HRESULT _HeapBaseSetup(PHEAPDATA phd) +{ + HRESULT hr = IMutexFactory_CreateMutex(phd->pMutexFactory, &(phd->pmtxBase)); + if (FAILED(hr)) + return hr; + phd->pexnBaseNodes = NULL; + return S_OK; +} + +void _HeapBaseShutdown(PHEAPDATA phd) +{ + /* TODO */ + IUnknown_Release(phd->pmtxBase); +} diff --git a/kernel/lib/heap_chunks.c b/kernel/lib/heap_chunks.c new file mode 100644 index 0000000..1c92b91 --- /dev/null +++ b/kernel/lib/heap_chunks.c @@ -0,0 +1,122 @@ +/* + * 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 "heap_internals.h" + +/*------------------------------------------------------------------------ + * Functions driving the red-black trees that contain EXTENT_NODE objects + *------------------------------------------------------------------------ + */ + +static INT32 compare_sizeaddr(PEXTENT_NODE pexnA, PEXTENT_NODE pexnB) +{ + SIZE_T szA = pexnA->sz; + SIZE_T szB = pexnB->sz; + INT32 rc = (szA > szB) - (szA < szB); + + if (rc == 0) + { + UINT_PTR addrA = (UINT_PTR)(pexnA->pv); + UINT_PTR addrB = (UINT_PTR)(pexnB->pv); + rc = (addrA > addrB) - (addrA < addrB); + } + return rc; +} + +static INT32 compare_addr(PEXTENT_NODE pexnA, PEXTENT_NODE pexnB) +{ + UINT_PTR addrA = (UINT_PTR)(pexnA->pv); + UINT_PTR addrB = (UINT_PTR)(pexnB->pv); + return (addrA > addrB) - (addrA < addrB); +} + +static PRBTREENODE get_sizeaddr_node(PEXTENT_NODE pexn) +{ + return &(pexn->rbtnSizeAddress); +} + +static PRBTREENODE get_addr_node(PEXTENT_NODE pexn) +{ + return &(pexn->rbtnAddress); +} + +static PEXTENT_NODE get_from_sizeaddr_node(PRBTREENODE prbtn) +{ + return (PEXTENT_NODE)(((UINT_PTR)prbtn) - OFFSETOF(EXTENT_NODE, rbtnSizeAddress)); +} + +static PEXTENT_NODE get_from_addr_node(PRBTREENODE prbtn) +{ + return (PEXTENT_NODE)(((UINT_PTR)prbtn) - OFFSETOF(EXTENT_NODE, rbtnAddress)); +} + +/*---------------------- + * Heap chunk functions + *---------------------- + */ + +PVOID _HeapChunkAlloc(PHEAPDATA phd, SIZE_T sz, SIZE_T szAlignment, BOOL fBase, BOOL *pfZeroed) +{ + return NULL; /* TODO */ +} + +void _HeapChunkUnmap(PHEAPDATA phd, PVOID pvChunk, SIZE_T sz) +{ + /* TODO */ +} + +void _HeapChunkDeAlloc(PHEAPDATA phd, PVOID pvChunk, SIZE_T sz, BOOL fUnmap) +{ + /* TODO */ +} + +HRESULT _HeapChunkSetup(PHEAPDATA phd) +{ + rbtInitTree(&(phd->rbtExtSizeAddr), (PFNTREECOMPARE)compare_sizeaddr, (PFNGETTREEKEY)get_from_sizeaddr_node, + (PFNGETTREENODEPTR)get_sizeaddr_node, (PFNGETFROMTREENODEPTR)get_from_sizeaddr_node); + rbtInitTree(&(phd->rbtExtAddr), (PFNTREECOMPARE)compare_addr, (PFNGETTREEKEY)get_from_addr_node, + (PFNGETTREENODEPTR)get_addr_node, (PFNGETFROMTREENODEPTR)get_from_addr_node); + return S_OK; +} + +extern void _HeapChunkShutdown(PHEAPDATA phd) +{ + /* TODO */ +} diff --git a/kernel/lib/heap_internals.h b/kernel/lib/heap_internals.h index 2ba5854..5362ae9 100644 --- a/kernel/lib/heap_internals.h +++ b/kernel/lib/heap_internals.h @@ -39,11 +39,32 @@ #ifndef __ASM__ +#include +#include #include #include #include +#include #include #include +#include + +/*------------------- + * Extent management + *------------------- + */ + +/* Tree of extents managed by the heap management code. */ +typedef struct tagEXTENT_NODE +{ + RBTREENODE rbtnSizeAddress; /* tree node for size and address ordering */ + RBTREENODE rbtnAddress; /* tree node for address ordering */ + /* TODO prof_ctx? */ + PVOID pv; /* base pointer to region */ + SIZE_T sz; /* size of region */ + BOOL fZeroed; /* is this extent zeroed? */ +} EXTENT_NODE, *PEXTENT_NODE; +typedef PEXTENT_NODE *PPEXTENT_NODE; /*---------------------------------- * The actual heap data declaration @@ -57,12 +78,59 @@ typedef struct tagHEAPDATA { UINT32 uiFlags; /* flags word */ PFNRAWHEAPDATAFREE pfnFreeRawHeapData; /* pointer to function that frees the raw heap data, if any */ IChunkAllocator *pChunkAllocator; /* chunk allocator pointer */ + IMutexFactory *pMutexFactory; /* mutex factory pointer */ FIXEDCPDATA fcpMallocSpy; /* connection point for IMallocSpy */ FIXEDCPDATA fcpSequentialStream; /* connection point for ISequentialStream for debugging */ IMallocSpy *pMallocSpy; /* IMallocSpy interface for the allocator */ ISequentialStream *pDebugStream; /* debugging output stream */ + UINT32 nChunkBits; /* number of bits in a chunk */ + UINT32 szChunk; /* size of a chunk */ + UINT32 uiChunkSizeMask; /* bitmask for a chunk */ + UINT32 cpgChunk; /* number of pages in a chunk */ + RBTREE rbtExtSizeAddr; /* tree ordering extents by size and address */ + RBTREE rbtExtAddr; /* tree ordering extents by address */ + IMutex *pmtxBase; /* base mutex */ + PVOID pvBasePages; /* pages being used for internal memory allocation */ + PVOID pvBaseNext; /* next allocation location */ + PVOID pvBasePast; /* address immediately past pvBasePages */ + PEXTENT_NODE pexnBaseNodes; /* pointer to base nodes */ } HEAPDATA, *PHEAPDATA; +/*------------------------------------- + * Internal chunk management functions + *------------------------------------- + */ + +/* Get chunk address for allocated address a. */ +#define CHUNK_ADDR2BASE(phd, a) ((PVOID)(((UINT_PTR)(a)) & ~((phd)->uiChunkSizeMask))) + +/* Get chunk offset of allocated address a. */ +#define CHUNK_ADDR2OFFSET(phd, a) ((SIZE_T)(((UINT_PTR)(a)) & (phd)->uiChunkSizeMask)) + +/* Return the smallest chunk size multiple that can contain a certain size. */ +#define CHUNK_CEILING(phd, sz) (((sz) + (phd)->uiChunkSizeMask) & ~((phd)->uiChunkSizeMask)) + +extern PVOID _HeapChunkAlloc(PHEAPDATA phd, SIZE_T sz, SIZE_T szAlignment, BOOL fBase, BOOL *pfZeroed); +extern void _HeapChunkUnmap(PHEAPDATA phd, PVOID pvChunk, SIZE_T sz); +extern void _HeapChunkDeAlloc(PHEAPDATA phd, PVOID pvChunk, SIZE_T sz, BOOL fUnmap); +extern HRESULT _HeapChunkSetup(PHEAPDATA phd); +extern void _HeapChunkShutdown(PHEAPDATA phd); + +/*------------------------------------ + * Internal base management functions + *------------------------------------ + */ + +CDECL_BEGIN + +extern PVOID _HeapBaseAlloc(PHEAPDATA phd, SIZE_T sz); +extern PEXTENT_NODE _HeapBaseNodeAlloc(PHEAPDATA phd); +extern void _HeapBaseNodeDeAlloc(PHEAPDATA phd, PEXTENT_NODE pexn); +extern HRESULT _HeapBaseSetup(PHEAPDATA phd); +extern void _HeapBaseShutdown(PHEAPDATA phd); + +CDECL_END + #endif /* __ASM__ */ #endif /* __COMROGUE_INTERNALS__ */ diff --git a/kernel/lib/heap_toplevel.c b/kernel/lib/heap_toplevel.c index 8c88e6c..8a4025f 100644 --- a/kernel/lib/heap_toplevel.c +++ b/kernel/lib/heap_toplevel.c @@ -34,12 +34,14 @@ */ #include #include +#include #include #include #include #include #include #include +#include #include "heap_internals.h" #include "enumgeneric.h" @@ -93,6 +95,23 @@ static UINT32 malloc_AddRef(IUnknown *pThis) return ++(((PHEAPDATA)pThis)->uiRefCount); } +/* + * Shuts down everything that was allocated at top level. + * + * Parameters: + * - phd = Pointer to HEAPDATA structure. + * + * Returns: + * Nothing. + */ +static void toplevel_shutdown(PHEAPDATA phd) +{ + ObjHlpFixedCpTeardown(&(phd->fcpMallocSpy)); + ObjHlpFixedCpTeardown(&(phd->fcpSequentialStream)); + IUnknown_Release(phd->pMutexFactory); + IUnknown_Release(phd->pChunkAllocator); +} + /* * Removes a reference from the heap data object. The object is freed when its reference count reaches 0. * @@ -112,9 +131,8 @@ static UINT32 malloc_Release(IUnknown *pThis) if ((rc == 0) && !(phd->uiFlags & PHDFLAGS_DELETING)) { phd->uiFlags |= PHDFLAGS_DELETING; - ObjHlpFixedCpTeardown(&(phd->fcpMallocSpy)); - ObjHlpFixedCpTeardown(&(phd->fcpSequentialStream)); - IUnknown_Release(phd->pChunkAllocator); + _HeapBaseShutdown(phd); + toplevel_shutdown(phd); if (phd->pfnFreeRawHeapData) { pfnFree = phd->pfnFreeRawHeapData; @@ -138,6 +156,9 @@ static PVOID malloc_Alloc(IMalloc *pThis, SIZE_T cb) return NULL; /* simulated memory failure */ } + if (cbActual == 0) + cbActual = 1; /* allocate at least SOMETHING */ + rc = NULL; /* TODO */ /* handle PostAlloc call */ @@ -184,6 +205,9 @@ static void malloc_Free(IMalloc *pThis, PVOID pv) PVOID pvActual = pv; /* actual heap block pointer */ BOOL fSpyed; /* were we allocated while currently spyed on? */ + if (!pv) + return; /* no effect if pointer is NULL */ + /* handle PreFree call */ if (phd->pMallocSpy) { @@ -431,15 +455,18 @@ static const SEG_RODATA struct IConnectionPointContainerVTable vtblConnectionPoi * "prhd" block. May be NULL. * - pChunkAllocator = Pointer to the IChunkAllocator interface used by the heap to allocate chunks of memory * for carving up by the heap. + * - pMutexFactory = Pointer to the IMutexFactory interface used to allocate IMutex objects. + * - nChunkBits = Number of "bits" in a memory chunk that gets allocated. * - ppHeap = Pointer location that will receive a pointer to the heap's IMalloc interface. * * Returns: * Standard HRESULT success/failure. */ HRESULT HeapCreate(PRAWHEAPDATA prhd, PFNRAWHEAPDATAFREE pfnFree, IChunkAllocator *pChunkAllocator, - IMalloc **ppHeap) + IMutexFactory *pMutexFactory, UINT32 nChunkBits, IMalloc **ppHeap) { PHEAPDATA phd; /* pointer to actual heap data */ + HRESULT hr; /* HRESULT of intermediate operations */ if (sizeof(RAWHEAPDATA) < sizeof(HEAPDATA)) return MEMMGR_E_BADHEAPDATASIZE; /* bogus size of raw heap data */ @@ -454,12 +481,37 @@ HRESULT HeapCreate(PRAWHEAPDATA prhd, PFNRAWHEAPDATAFREE pfnFree, IChunkAllocato phd->uiRefCount = 1; phd->uiFlags = 0; phd->pfnFreeRawHeapData = pfnFree; + phd->nChunkBits = nChunkBits; + phd->szChunk = 1 << nChunkBits; + if (phd->szChunk < SYS_PAGE_SIZE) + return MEMMGR_E_BADCHUNKSIZE; + phd->uiChunkSizeMask = phd->szChunk - 1; + phd->cpgChunk = phd->szChunk >> SYS_PAGE_BITS; + + /* Set up the top-level data. */ phd->pChunkAllocator = pChunkAllocator; IUnknown_AddRef(phd->pChunkAllocator); + phd->pMutexFactory = pMutexFactory; + IUnknown_AddRef(phd->pMutexFactory); ObjHlpFixedCpSetup(&(phd->fcpMallocSpy), (PUNKNOWN)phd, &IID_IMallocSpy, (IUnknown **)(&(phd->pMallocSpy)), 1, NULL); ObjHlpFixedCpSetup(&(phd->fcpSequentialStream), (PUNKNOWN)phd, &IID_ISequentialStream, (IUnknown **)(&(phd->pDebugStream)), 1, NULL); + /* Setup base pointers. */ + hr = _HeapBaseSetup(phd); + if (FAILED(hr)) + goto error0; + + + + *ppHeap = (IMalloc *)phd; return S_OK; + + /*error1:*/ + _HeapBaseShutdown(phd); +error0: + toplevel_shutdown(phd); + *ppHeap = NULL; + return hr; }