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; } } }