Cosmos/source/Cosmos.Core/Heap.cs
2015-07-28 20:30:15 -04:00

145 lines
6.3 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Cosmos.Common;
using Cosmos.Debug.Kernel;
namespace Cosmos.Core
{
// This class must be static, as for creating objects, we need the heap
// this heap implementation it the very most basic one: no reentrancy, etc.
// Interrupts are disabled when trying to allocate a new block of memory.
public static unsafe partial class Heap
{
private static uint mEndOfRam;
private static void DoInitialize(uint aEndOfRam)
{
mEndOfRam = aEndOfRam;
//
}
private static bool mInitialized = false;
internal static void EnsureIsInitialized()
{
if (!mInitialized)
{
mInitialized = true;
DoInitialize((CPU.GetAmountOfRAM() - 1) * 1024 * 1024);
//DoInitialize(4 * 1024 * 1024, 16 * 1024 * 1024);
}
}
private static void ClearMemory(void* aStartAddress, uint aLength)
{
//TODO: Move to memory. Internal access only...
CPU.ZeroFill((uint)aStartAddress, aLength);
}
public static uint MemAlloc(uint aLength)
{
CPU.DisableInterrupts();
try
{
EnsureIsInitialized();
var xCurrentTableIdx = 0u;
DataLookupTable* xCurrentTable = GlobalSystemInfo.GlobalInformationTable->FirstDataLookupTable;
DataLookupTable* xPreviousTable = null;
uint xResult;
while (xCurrentTable != null)
{
DebugHex("Scanning DataLookupTable ", xCurrentTableIdx);
if (ScanDataLookupTable(xCurrentTableIdx, xCurrentTable, aLength, out xResult))
{
return xResult;
}
xCurrentTableIdx ++;
xPreviousTable = xCurrentTable;
xCurrentTable = xCurrentTable->Next;
}
// no tables found, lets
var xLastItem = xPreviousTable->Entries[DataLookupTable.EntriesPerTable - 1];
var xNextTablePointer = (DataLookupTable*)((uint)xLastItem.DataBlock + xLastItem.Size);
// the memory hasn't been cleared yet, so lets do that now.
ClearMemory(xNextTablePointer, GlobalSystemInfo.GetTotalDataLookupSize);
xPreviousTable->Next = xNextTablePointer;
xNextTablePointer->Previous = xPreviousTable;
xNextTablePointer->FirstByteAfterTable = (void*)((uint)xNextTablePointer + GlobalSystemInfo.GetTotalDataLookupSize);
if (!ScanDataLookupTable(xCurrentTableIdx, xPreviousTable, aLength, out xResult))
{
// Something seriously weird happened: we could create a new DataLookupTable (with new entries)
// but couldn't allocate a new handle from it.
DebugAndHalt("Something seriously weird happened: we could create a new DataLookupTable (with new entries), but couldn't allocate a new handle from it.");
}
return xResult;
}
finally
{
CPU.EnableInterrupts();
}
}
private static bool ScanDataLookupTable(uint aTableIdx, DataLookupTable* aTable, uint aSize, out uint aHandle)
{
for (int i = 0; i < DataLookupTable.EntriesPerTable; i++)
{
if (aTable->Entries[i].Size == 0)
{
// found an entry now. Let's set it
if (aTable->Next != null)
{
// once a handle is used, the size should be set. But at this point, it somehow got unset again.
// This should never occur.
DebugAndHalt("Found an entry which has no size, but there is a followup DataLookupTable");
}
void* xDataBlock;
// now we found ourself a free handle
if (i == 0)
{
// we don't have a previous handle yet, so we take the FirstByteAfterTable field of the DataLookupTable
// note: we're explicitly initializing all blocks, as memory hasn't been cleared yet.
xDataBlock = aTable->FirstByteAfterTable;
}
else
{
// We're not the very first handle being assigned, so calculate the start address using the previous block
xDataBlock = (void*)((uint)aTable->Entries[i - 1].DataBlock + aTable->Entries[i - 1].Size);
}
// make sure the memory is empty
ClearMemory(xDataBlock, aSize);
aTable->Entries[i].Size = aSize;
aTable->Entries[i].DataBlock = xDataBlock;
aTable->Entries[i].Refcount = 1;
var xResult = (uint)((aTableIdx * DataLookupTable.EntriesPerTable) + i + 1);
DebugHex("Returning handle ", xResult);
aHandle = (uint)aTable->Entries[i].DataBlock;
return true;
}
// Refcount == UInt32.MaxValue, it means that the block has been reclaimed, and can be reused now.
if (aTable->Entries[i].Refcount == UInt32.MaxValue)
{
// we can reuse this entry if its Size >= aLength
if (aTable->Entries[i].Size >= aSize)
{
// we can reuse this entry
aTable->Entries[i].Refcount = 1;
var xResult = (uint)((aTableIdx * DataLookupTable.EntriesPerTable) + i + 1);
DebugHex("Returning reused handle ", xResult);
aHandle = (uint)aTable->Entries[i].DataBlock;
return true;
}
}
}
aHandle = 0;
return false;
}
}
}