372 lines
12 KiB
C
372 lines
12 KiB
C
/*
|
|
* 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 <comrogue/types.h>
|
|
#include <comrogue/allocator.h>
|
|
#include <comrogue/internals/memmgr.h>
|
|
#include <comrogue/internals/rbtree.h>
|
|
#include <comrogue/internals/layout.h>
|
|
#include <comrogue/internals/mmu.h>
|
|
#include <comrogue/internals/seg.h>
|
|
#include <comrogue/internals/startup.h>
|
|
#include <comrogue/internals/trace.h>
|
|
#include "initfuncs.h"
|
|
|
|
#ifdef THIS_FILE
|
|
#undef THIS_FILE
|
|
DECLARE_THIS_FILE
|
|
#endif
|
|
|
|
/*------------------------------------------
|
|
* Operations with kernel address intervals
|
|
*------------------------------------------
|
|
*/
|
|
|
|
/* Definiton of an address interval */
|
|
typedef struct tagAINTERVAL {
|
|
KERNADDR kaFirst; /* first kernel address in the interval */
|
|
KERNADDR kaLast; /* first kernel address NOT in the interval */
|
|
} AINTERVAL, *PAINTERVAL;
|
|
typedef const AINTERVAL *PCAINTERVAL;
|
|
|
|
/*
|
|
* Compares two address intervals.
|
|
*
|
|
* Parameters:
|
|
* - paiLeft = Pointer to first address interval to compare.
|
|
* - paiRight = Pointer to second address interval to compare.
|
|
*
|
|
* Returns:
|
|
* - -1 = If the interval paiLeft is entirely before the interval paiRight.
|
|
* - 0 = If the interval paiLeft is entirely contained within (or equal to) the interval paiRight.
|
|
* - 1 = If the interval paiLeft is entirely after the interval paiRight.
|
|
*
|
|
* N.B.:
|
|
* It is an error if the intervals overlap without paiLeft being entirely contained within paiRight.
|
|
* (This should not happen.)
|
|
*/
|
|
static INT32 interval_compare(PCAINTERVAL paiLeft, PCAINTERVAL paiRight)
|
|
{
|
|
static DECLARE_STRING8_CONST(szFitCheck, "interval_compare fitcheck: [%08x,%08x] <?> [%08x,%08x]");
|
|
|
|
ASSERT(paiLeft->kaFirst < paiLeft->kaLast);
|
|
ASSERT(paiRight->kaFirst < paiRight->kaLast);
|
|
if ((paiLeft->kaFirst >= paiRight->kaFirst) && (paiLeft->kaLast <= paiRight->kaLast))
|
|
return 0;
|
|
if (paiLeft->kaLast <= paiRight->kaFirst)
|
|
return -1;
|
|
if (paiLeft->kaFirst >= paiRight->kaLast)
|
|
return 1;
|
|
/* if get here, bugbugbugbugbug */
|
|
TrPrintf8(szFitCheck, paiLeft->kaFirst, paiLeft->kaLast, paiRight->kaFirst, paiRight->kaLast);
|
|
/* TODO: bugcheck */
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Determines if two intervals are adjacent, that is, if the end of the first is the start of the next.
|
|
*
|
|
* Parameters:
|
|
* - paiLeft = Pointer to first address interval to compare.
|
|
* - paiRight = Pointer to second address interval to compare.
|
|
*
|
|
* Returns:
|
|
* TRUE if paiLeft is adjacent to paiRight, FALSE otherwise.
|
|
*/
|
|
static inline BOOL intervals_adjacent(PCAINTERVAL paiLeft, PCAINTERVAL paiRight)
|
|
{
|
|
return MAKEBOOL(paiLeft->kaLast == paiRight->kaFirst);
|
|
}
|
|
|
|
/*
|
|
* Returns the number of pages described by an interval.
|
|
*
|
|
* Parameters:
|
|
* - pai = The interval to test.
|
|
*
|
|
* Returns:
|
|
* The number of pages described by this interval.
|
|
*/
|
|
static inline UINT32 interval_numpages(PCAINTERVAL pai)
|
|
{
|
|
return (pai->kaLast - pai->kaFirst) >> SYS_PAGE_BITS;
|
|
}
|
|
|
|
/*
|
|
* Initializes an interval's start and end points.
|
|
*
|
|
* Parameters:
|
|
* - pai = Pointer to the interval to be initialized.
|
|
* - kaFirst = First address in the interval.
|
|
* - kaLast = Last address in the interval.
|
|
*
|
|
* Returns:
|
|
* pai.
|
|
*/
|
|
static inline PAINTERVAL init_interval(PAINTERVAL pai, KERNADDR kaFirst, KERNADDR kaLast)
|
|
{
|
|
pai->kaFirst = kaFirst;
|
|
pai->kaLast = kaLast;
|
|
return pai;
|
|
}
|
|
|
|
/*
|
|
* Initializes an interval to start at a specified location and cover a specific number of pages.
|
|
*
|
|
* Parameters:
|
|
* - pai = Pointer to the interval to be initialized.
|
|
* - kaBase = Base address of the interval.
|
|
* - cpg = Number of pages the interval is to contain.
|
|
*
|
|
* Returns:
|
|
* pai.
|
|
*/
|
|
static inline PAINTERVAL init_interval_pages(PAINTERVAL pai, KERNADDR kaBase, UINT32 cpg)
|
|
{
|
|
pai->kaFirst = kaBase;
|
|
pai->kaLast = kaBase + (cpg << SYS_PAGE_BITS);
|
|
return pai;
|
|
}
|
|
|
|
/*----------------------------------------
|
|
* Kernel address manipulation operations
|
|
*----------------------------------------
|
|
*/
|
|
|
|
/* Tree structure in which we store "free" address intervals. */
|
|
typedef struct tagADDRTREENODE {
|
|
RBTREENODE rbtn; /* tree node structure */
|
|
AINTERVAL ai; /* address interval this represents */
|
|
} ADDRTREENODE, *PADDRTREENODE;
|
|
|
|
/* Structure used in allocating address space. */
|
|
typedef struct tagALLOC_STRUC {
|
|
UINT32 cpgNeeded; /* count of number of pages needed */
|
|
PADDRTREENODE patnFound; /* pointer to "found" tree node */
|
|
} ALLOC_STRUC, *PALLOC_STRUC;
|
|
|
|
static RBTREE g_rbtFreeAddrs; /* free address tree */
|
|
static PMALLOC g_pMalloc = NULL; /* allocator we use */
|
|
|
|
/*
|
|
* Given a pointer to an ADDRTREENODE, returns its key value (pointer to its interval).
|
|
*
|
|
* Parameters:
|
|
* - patn = Pointer to the ADDRTREENODE.
|
|
*
|
|
* Returns:
|
|
* Pointer to its embedded address interval.
|
|
*/
|
|
static TREEKEY get_interval_from_addrtreenode(PVOID patn)
|
|
{
|
|
return (TREEKEY)(&(((PADDRTREENODE)patn)->ai));
|
|
}
|
|
|
|
/*
|
|
* Given a pointer to an ADDRTREENODE, returns a pointer to its embedded RBTREENODE.
|
|
*
|
|
* Parameters:
|
|
* - patn = Pointer to the ADDRTREENODE.
|
|
*
|
|
* Returns:
|
|
* Pointer to the embedded RBTREENODE.
|
|
*/
|
|
static PRBTREENODE get_rbtreenode_from_addrtreenode(PVOID patn)
|
|
{
|
|
return &(((PADDRTREENODE)patn)->rbtn);
|
|
}
|
|
|
|
/*
|
|
* Given a pointer to a RBTREENODE, returns a pointer to the ADDRTREENODE containing it.
|
|
*
|
|
* Parameters:
|
|
* - ptn = Pointer to the RBTREENODE.
|
|
*
|
|
* Returns:
|
|
* Pointer to the containing ADDRTREENODE.
|
|
*/
|
|
static PVOID get_addrtreenode_from_rbtreenode(PRBTREENODE ptn)
|
|
{
|
|
return (PVOID)(((PCHAR)ptn) - OFFSETOF(ADDRTREENODE, rbtn));
|
|
}
|
|
|
|
/*
|
|
* Inserts a kernel address range into the tree.
|
|
*
|
|
* Parameters:
|
|
* - kaFirst = First address in the range to be inserted.
|
|
* - kaLast = Last address in the range to be inserted.
|
|
*
|
|
* Returns:
|
|
* - Nothing.
|
|
*
|
|
* Side effects:
|
|
* Modifies g_rbtFreeAddrs; allocates space from the g_pMalloc heap.
|
|
*/
|
|
static void insert_into_tree(KERNADDR kaFirst, KERNADDR kaLast)
|
|
{
|
|
PADDRTREENODE pnode = IMalloc_Alloc(g_pMalloc, sizeof(ADDRTREENODE));
|
|
ASSERT(pnode);
|
|
rbtNewNode(&(pnode->rbtn));
|
|
init_interval(&(pnode->ai), kaFirst, kaLast);
|
|
RbtInsert(&g_rbtFreeAddrs, pnode);
|
|
}
|
|
|
|
/*
|
|
* Subfunction called from a tree walk to find a free address interval in the tree that can supply us with
|
|
* the number of pages we need.
|
|
*
|
|
* Parameters:
|
|
* - pUnused = Not used.
|
|
* - pnode = Current tree node we're walking over.
|
|
* - palloc = Pointer to allocation structure.
|
|
*
|
|
* Returns:
|
|
* FALSE if we found a node containing enough space (written to palloc->patnFound), TRUE otherwise.
|
|
*/
|
|
static BOOL alloc_check_space(PVOID pUnused, PADDRTREENODE pnode, PALLOC_STRUC palloc)
|
|
{
|
|
if (interval_numpages(&(pnode->ai)) >= palloc->cpgNeeded)
|
|
{
|
|
palloc->patnFound = pnode;
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Allocates a block of kernel addresses suitable to contain a certain number of pages.
|
|
*
|
|
* Parameters:
|
|
* - cpgNeeded = Number of pages of kernel address space that are needed.
|
|
*
|
|
* Returns:
|
|
* Base address of the block of address space we got.
|
|
*
|
|
* Side effects:
|
|
* May modify g_rbtFreeAddrs and free space to the g_pMalloc heap.
|
|
*
|
|
* N.B.:
|
|
* Running out of kernel address space should be a bug.
|
|
*/
|
|
KERNADDR _MmAllocKernelAddr(UINT32 cpgNeeded)
|
|
{
|
|
register KERNADDR rc; /* return from this function */
|
|
BOOL bResult; /* result of tree walk */
|
|
ALLOC_STRUC alloc_struc = { cpgNeeded, NULL }; /* allocation structure */
|
|
|
|
/* Walk the tree to find a block of free addresses that are big enough. */
|
|
bResult = RbtWalk(&g_rbtFreeAddrs, (PFNRBTWALK)alloc_check_space, &alloc_struc);
|
|
ASSERT(!bResult);
|
|
if (bResult)
|
|
{
|
|
/* TODO: bug check */
|
|
return 0;
|
|
}
|
|
|
|
/* We allocate address space from the start of the interval we found. */
|
|
rc = alloc_struc.patnFound->ai.kaFirst;
|
|
if (interval_numpages(&(alloc_struc.patnFound->ai)) == cpgNeeded)
|
|
{
|
|
/* This node is all used up by this allocation. Remove it from the tree and free it. */
|
|
RbtDelete(&g_rbtFreeAddrs, (TREEKEY)(&(alloc_struc.patnFound->ai)));
|
|
IMalloc_Free(g_pMalloc, alloc_struc.patnFound);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Chop off the number of pages we're taking. This does not change the ordering of nodes in the tree
|
|
* because we're just shortening this one's interval.
|
|
*/
|
|
alloc_struc.patnFound->ai.kaFirst += (cpgNeeded << SYS_PAGE_BITS);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* Frees a block of kernel addresses that was previously allocated.
|
|
*
|
|
* Parameters:
|
|
* - kaBase = Base address of the kernel address space region to be freed.
|
|
* - cpgToFree = Number of pages of kernel address space to be freed.
|
|
*
|
|
* Returns:
|
|
* Nothing.
|
|
*
|
|
* Side effects:
|
|
* May modify g_rbtFreeAddrs and allocate or free space in the g_pMalloc heap.
|
|
*/
|
|
void _MmFreeKernelAddr(KERNADDR kaBase, UINT32 cpgToFree)
|
|
{
|
|
register PADDRTREENODE patnPred, patnSucc; /* predecessor and successor pointers */
|
|
AINTERVAL aiFree; /* actual interval we're freeing */
|
|
|
|
init_interval_pages(&aiFree, kaBase, cpgToFree);
|
|
ASSERT(!RbtFind(&g_rbtFreeAddrs, (TREEKEY)(&aiFree)));
|
|
patnPred = (PADDRTREENODE)RbtFindPredecessor(&g_rbtFreeAddrs, (&aiFree));
|
|
patnSucc = (PADDRTREENODE)RbtFindSuccessor(&g_rbtFreeAddrs, (&aiFree));
|
|
if (patnPred && intervals_adjacent(&(patnPred->ai), &aiFree))
|
|
{
|
|
if (patnSucc && intervals_adjacent(&aiFree, &(patnSucc->ai)))
|
|
{ /* combine predecessor, interval, and successor into one big node */
|
|
RbtDelete(&g_rbtFreeAddrs, (TREEKEY)(&(patnPred->ai)));
|
|
patnPred->ai.kaLast = patnSucc->ai.kaLast;
|
|
IMalloc_Free(g_pMalloc, patnSucc);
|
|
}
|
|
else /* combine with predecessor */
|
|
patnPred->ai.kaLast = aiFree.kaLast;
|
|
}
|
|
else if (patnSucc && intervals_adjacent(&aiFree, &(patnSucc->ai)))
|
|
patnSucc->ai.kaFirst = aiFree.kaFirst; /* combine with successor */
|
|
else /* insert as a new address range */
|
|
insert_into_tree(aiFree.kaFirst, aiFree.kaLast);
|
|
}
|
|
|
|
/*
|
|
* Initializes the kernel address space management code.
|
|
*
|
|
* Parameters:
|
|
* - pstartup = Pointer to startup information block.
|
|
* - pmInitHeap = Pointer to initialization heap allocator.
|
|
*
|
|
* Returns:
|
|
* Nothing.
|
|
*/
|
|
SEG_INIT_CODE void _MmInitKernelSpace(PSTARTUP_INFO pstartup, PMALLOC pmInitHeap)
|
|
{
|
|
g_pMalloc = pmInitHeap;
|
|
IUnknown_AddRef(g_pMalloc);
|
|
rbtInitTree(&g_rbtFreeAddrs, (PFNTREECOMPARE)interval_compare, get_interval_from_addrtreenode,
|
|
get_rbtreenode_from_addrtreenode, get_addrtreenode_from_rbtreenode);
|
|
insert_into_tree(pstartup->vmaFirstFree, VMADDR_IO_BASE);
|
|
insert_into_tree(VMADDR_IO_BASE + (PAGE_COUNT_IO * SYS_PAGE_SIZE), VMADDR_KERNEL_NOMANS);
|
|
}
|