diff --git a/source/Cosmos.Core/Memory/RAT.cs b/source/Cosmos.Core/Memory/RAT.cs index 4a649e72e..2e573876e 100644 --- a/source/Cosmos.Core/Memory/RAT.cs +++ b/source/Cosmos.Core/Memory/RAT.cs @@ -3,175 +3,301 @@ using System.Linq; using System.Threading.Tasks; using Native = System.UInt32; -namespace Cosmos.Core.Memory { - unsafe static public class RAT { - // RAT: RAM Allocation Table - // - // A byte table which defines the code which owns the page. - // Owners can further subdivide table types on their own and RAT - // code must not assume anything about contents of pages other - // than who owns them. +namespace Cosmos.Core.Memory +{ + /// + /// RAT (RAM Allocation Table) class. + /// + unsafe static public class RAT + { + // RAT: RAM Allocation Table + // + // A byte table which defines the code which owns the page. + // Owners can further subdivide table types on their own and RAT + // code must not assume anything about contents of pages other + // than who owns them. - static public class PageType { - public const byte Empty = 0; + /// + /// PageType class. Used like a enum to define the type of the page. + /// + /// Only used to define page type. + static public class PageType + { + /// + /// Empty page. + /// + public const byte Empty = 0; - // Data Types from 1, special meanings from 255 down. - public const byte RAT = 1; - public const byte HeapSmall = 2; - public const byte HeapMedium = 3; - public const byte HeapLarge = 4; - // Code - // Stack - // Disk Cache + // Data Types from 1, special meanings from 255 down. + /// + /// RAT type page. + /// + public const byte RAT = 1; + /// + /// Small heap page. + /// + public const byte HeapSmall = 2; + /// + /// Medium heap page. + /// + public const byte HeapMedium = 3; + /// + /// Large heap page. + /// + public const byte HeapLarge = 4; + // Code + // Stack + // Disk Cache - // Extension of previous page. - public const byte Extension = 255; - } - - // Used to bypass certain checks that will fail during tests and debugging. - static internal bool Debug = false; - - // Native Intel page size - // x86 Page Size: 4k, 2m (PAE only), 4m - // x64 Page Size: 4k, 2m - public const Native PageSize = 4096; - - // Start of area usable for heap, and also start of heap. - static private byte* mRamStart; - // Size of heap - static private Native mRamSize; - // Calculated from mSize - static private Native mPageCount; - - // RAT - RAM Allocation Table (Covers Data area only) - // We need a pointer as the RAT can move around in future with dynamic RAM etc. - static private byte* mRAT; - - static public void Init(byte* aStartPtr, Native aSize) { - if ((Native)aStartPtr % PageSize != 0 && !Debug) { - throw new Exception("RAM start must be page aligned."); - } - - if (aSize % PageSize != 0) { - throw new Exception("RAM size must be page aligned."); - } - - mRamStart = aStartPtr; - mRamSize = aSize; - mPageCount = aSize / PageSize; - - // We need one status byte for each block. - // Intel blocks are 4k (10 bits). So for 4GB, this means - // 32 - 12 = 20 bits, 1 MB for a RAT for 4GB. 0.025% - Native xRatPageCount = (mPageCount - 1) / PageSize + 1; - Native xRatPageBytes = xRatPageCount * PageSize; - mRAT = mRamStart + mRamSize - xRatPageBytes; - for (byte* p = mRAT; p < mRAT + xRatPageBytes - xRatPageCount; p++) { - *p = PageType.Empty; - } - for (byte* p = mRAT + xRatPageBytes - xRatPageCount; p < mRAT + xRatPageBytes; p++) { - *p = PageType.RAT; - } - - Heap.Init(); - } - - static public Native GetPageCount(byte aType = 0) { - Native xResult = 0; - byte xType = 0; // Could us nullable type instead of this + xCounting, but this is faster. - bool xCounting = false; - for (byte* p = mRAT; p < mRAT + mPageCount; p++) { - if (*p == aType) { - xType = *p; - xResult++; - xCounting = true; - } else if (xCounting) { - if (xType == PageType.Extension) { - xResult++; - } else { - xCounting = false; - } + // Extension of previous page. + /// + /// Extension of pre-existing page. + /// + public const byte Extension = 255; } - } - return xResult; - } - static public void* AllocBytes(byte aType, Native aBytes) { - return AllocPages(aType, aBytes / PageSize); - } - static public void* AllocPages(byte aType, Native aPageCount = 1) { - byte* xPos = null; + /// + /// Debug flag. + /// + /// Used to bypass certain checks that will fail during tests and debugging. + static internal bool Debug = false; - // Could combine with an external method or delegate, but will slow things down - // unless we can force it to be inlined. - // - // Alloc single blocks at bottom, larger blocks at top to help reduce fragmentation. - Native xCount = 0; - if (aPageCount == 1) { - for (byte* p = mRAT; p < mRAT + mPageCount; p++) { - if (*p == PageType.Empty) { - xCount++; - if (xCount == aPageCount) { - xPos = p - xCount + 1; - break; + /// + /// Native Intel page size. + /// + /// + /// x86 Page Size: 4k, 2m (PAE only), 4m. + /// x64 Page Size: 4k, 2m + /// + public const Native PageSize = 4096; + + /// + /// Start of area usable for heap, and also start of heap. + /// + static private byte* mRamStart; + /// + /// Size of heap. + /// + static private Native mRamSize; + /// + /// Number of pages in the heap. + /// + /// Calculated from mSize. + static private Native mPageCount; + + /// + /// Pointer to the RAT. + /// + /// Covers Data area only. + // We need a pointer as the RAT can move around in future with dynamic RAM etc. + static private byte* mRAT; + + /// + /// Init RAT. + /// + /// A pointer to the start of the heap. + /// A heap size, in bytes. + /// Thrown if: + /// + /// RAM start or size is not page aligned. + /// + /// + static public void Init(byte* aStartPtr, Native aSize) + { + if ((Native)aStartPtr % PageSize != 0 && !Debug) + { + throw new Exception("RAM start must be page aligned."); } - } else { - xCount = 0; - } - } - } else { - // This loop will FAIL if mRAT is ever 0. This should be impossible though - // so we don't bother to account for such a case. xPos would also have issues. - for (byte* p = mRAT + mPageCount - 1; p >= mRAT; p--) { - if (*p == PageType.Empty) { - xCount++; - if (xCount == aPageCount) { - xPos = p; - break; + + if (aSize % PageSize != 0) + { + throw new Exception("RAM size must be page aligned."); } - } else { - xCount = 0; - } + + mRamStart = aStartPtr; + mRamSize = aSize; + mPageCount = aSize / PageSize; + + // We need one status byte for each block. + // Intel blocks are 4k (10 bits). So for 4GB, this means + // 32 - 12 = 20 bits, 1 MB for a RAT for 4GB. 0.025% + Native xRatPageCount = (mPageCount - 1) / PageSize + 1; + Native xRatPageBytes = xRatPageCount * PageSize; + mRAT = mRamStart + mRamSize - xRatPageBytes; + for (byte* p = mRAT; p < mRAT + xRatPageBytes - xRatPageCount; p++) + { + *p = PageType.Empty; + } + for (byte* p = mRAT + xRatPageBytes - xRatPageCount; p < mRAT + xRatPageBytes; p++) + { + *p = PageType.RAT; + } + + Heap.Init(); } - } - // If we found enough space, mark it as used. - if (xPos != null) { - byte* xResult = mRamStart + (xPos - mRAT) * PageSize; - *xPos = aType; - for (byte* p = xPos + 1; p < xPos + xCount; p++) { - *p = PageType.Extension; + /// + /// Get page count. + /// + /// A page type to count. + /// Native value. + static public Native GetPageCount(byte aType = 0) + { + Native xResult = 0; + byte xType = 0; // Could us nullable type instead of this + xCounting, but this is faster. + bool xCounting = false; + for (byte* p = mRAT; p < mRAT + mPageCount; p++) + { + if (*p == aType) + { + xType = *p; + xResult++; + xCounting = true; + } + else if (xCounting) + { + if (xType == PageType.Extension) + { + xResult++; + } + else + { + xCounting = false; + } + } + } + return xResult; } - return xResult; - } - return null; - } - - static public Native GetFirstRAT(void* aPtr) { - var xPos = (Native)((byte*)aPtr - mRamStart) / RAT.PageSize; - // See note about when mRAT = 0 in Alloc. - for (byte* p = mRAT + xPos; p >= mRAT; p--) { - if (*p != PageType.Extension) { - return (Native)(p - mRAT); + /// + /// Alloc a block with a given type and size. + /// + /// A type of block to alloc. + /// Number of bytes to alloc. + /// A pointer to the first page on success, null on failure. + static public void* AllocBytes(byte aType, Native aBytes) + { + return AllocPages(aType, aBytes / PageSize); } - } - throw new Exception("Page type not found. Likely RAT is rotten."); - } - static public byte GetPageType(void* aPtr) { - return mRAT[GetFirstRAT(aPtr)]; - } + /// + /// Alloc a given number of pages, all of the same type. + /// + /// A type of pages to alloc. + /// Number of pages to alloc. (default = 1) + /// A pointer to the first page on success, null on failure. + static public void* AllocPages(byte aType, Native aPageCount = 1) + { + byte* xPos = null; - static public void Free(Native aPageIdx) { - byte* p = mRAT + aPageIdx; - *p = PageType.Empty; - for (; p < mRAT + mPageCount; p++) { - if (*p != PageType.Extension) { - break; + // Could combine with an external method or delegate, but will slow things down + // unless we can force it to be inlined. + // + // Alloc single blocks at bottom, larger blocks at top to help reduce fragmentation. + Native xCount = 0; + if (aPageCount == 1) + { + for (byte* p = mRAT; p < mRAT + mPageCount; p++) + { + if (*p == PageType.Empty) + { + xCount++; + if (xCount == aPageCount) + { + xPos = p - xCount + 1; + break; + } + } + else + { + xCount = 0; + } + } + } + else + { + // This loop will FAIL if mRAT is ever 0. This should be impossible though + // so we don't bother to account for such a case. xPos would also have issues. + for (byte* p = mRAT + mPageCount - 1; p >= mRAT; p--) + { + if (*p == PageType.Empty) + { + xCount++; + if (xCount == aPageCount) + { + xPos = p; + break; + } + } + else + { + xCount = 0; + } + } + } + + // If we found enough space, mark it as used. + if (xPos != null) + { + byte* xResult = mRamStart + (xPos - mRAT) * PageSize; + *xPos = aType; + for (byte* p = xPos + 1; p < xPos + xCount; p++) + { + *p = PageType.Extension; + } + return xResult; + } + + return null; + } + + /// + /// Get the first RAT address. + /// + /// A pointer to the block. + /// Native value. + /// Thrown if page type is not found. + static public Native GetFirstRAT(void* aPtr) + { + var xPos = (Native)((byte*)aPtr - mRamStart) / RAT.PageSize; + // See note about when mRAT = 0 in Alloc. + for (byte* p = mRAT + xPos; p >= mRAT; p--) + { + if (*p != PageType.Extension) + { + return (Native)(p - mRAT); + } + } + throw new Exception("Page type not found. Likely RAT is rotten."); + } + + /// + /// Get the page type pointed by a pointer. + /// + /// A pointer to the page to get the type of. + /// byte value. + /// Thrown if page type is not found. + static public byte GetPageType(void* aPtr) + { + return mRAT[GetFirstRAT(aPtr)]; + } + + /// + /// Free page. + /// + /// A index to the page to be freed. + static public void Free(Native aPageIdx) + { + byte* p = mRAT + aPageIdx; + *p = PageType.Empty; + for (; p < mRAT + mPageCount; p++) + { + if (*p != PageType.Extension) + { + break; + } + *p = PageType.Empty; + } } - *p = PageType.Empty; - } } - } }