//////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: Legacy.cs - Primitive memory manager // // Note: // //#define TEST //#define VERBOSE //#define MP_VERBOSE //#define COMPLEX_TEST #if MARKSWEEPCOLLECTOR #define ALLOW_BOOT_ARGLIST // Cannot be used during boot for GCs w/ write barrier #endif #define NO__TRACE_PAGES using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; using System.GCs; using Microsoft.Singularity; using Microsoft.Singularity.Hal; using Microsoft.Bartok.Runtime; using Microsoft.Singularity.Eventing; namespace Microsoft.Singularity.Memory { [NoCCtor] [CLSCompliant(false)] public class FlatPages { // WARNING: don't initialize any static fields in this class // without manually running the class constructor at startup! //private const uint PageMask = MemoryManager.PageSize - 1; private const uint SmallSize = MemoryManager.PageSize; private static UIntPtr lowerLimit; private static UIntPtr upperLimit; private static UIntPtr pageBase; // Lowest page. private static UIntPtr pageCount; // Number of pages. private static SpinLock pageLock; private static UIntPtr baseAddress; private static ulong size; private static unsafe uint *pageTable; // We keep two lists of free pages: // The freeList has pages that can be used at any moment. // The saveList has pages that were reserved for growing a region. private static FreeNode freeListTail; private static FreeNode saveListTail; private static unsafe FreeNode *freeList; private static unsafe FreeNode *saveList; // // This represents the real time count of available memory // private static UIntPtr availableMemory; [NoHeapAllocation] [Inline] private unsafe static bool RangeWithinLimits(ulong addr, ulong size, UIntPtr upperLimit, UIntPtr lowerLimit, out UIntPtr baseAddress, out ulong newSize) { baseAddress = addr; newSize = size; if(addr + size < lowerLimit) { return false; } if(addr > upperLimit) { return false; } if((addr > lowerLimit) && ((addr + size) < upperLimit)) { return true; } //trim the limits to fit into the valid address range if(addr < lowerLimit) { baseAddress = lowerLimit; newSize = newSize - ((ulong)lowerLimit - addr); } if((addr + size) > upperLimit) { newSize = newSize - (((ulong)baseAddress + size) - (ulong)upperLimit); } return true; } internal static unsafe void Initialize() { Tracing.Log(Tracing.Debug, "FlatPages.Initialize() called"); // Initialize spinlock info pageLock = new SpinLock(SpinLock.Types.FlatPages); Platform p = Platform.ThePlatform; HistogramSize = 64; HistogramGranularityShift = 20; FreeMemoryCounters = null; CurrentSystemLogger = null; // Retrieve the highest RAM address MemoryManager.PhysicalMemoryLimits(out lowerLimit, out upperLimit); DebugStub.WriteLine("MemoryManager Physical limits {0:x8}...{1:x8}\n", __arglist(lowerLimit, upperLimit)); // Compute page count pageCount = Pad((upperLimit >> MemoryManager.PageBits) + 1, MemoryManager.PageSize / sizeof(uint)); // Find a spot for the page table. pageTable = null; // Figure out the lowest possible location for positioning the pageMap UIntPtr limit = Pad(lowerLimit, 0x00200000); // Make sure "limit" doesn't straddle some of the other non-SMAP regions if (p.BootAllocatedMemorySize != 0) { if ((p.BootAllocatedMemory < limit) && (limit < p.BootAllocatedMemory + p.BootAllocatedMemorySize)) { limit = MemoryManager.PagePad(p.BootAllocatedMemory + p.BootAllocatedMemorySize); } } if (p.KernelDllSize != 0) { if ((p.KernelDllBase < limit) && (limit < p.KernelDllBase + p.KernelDllSize)) { limit = MemoryManager.PagePad(p.KernelDllBase + p.KernelDllSize); } } DebugStub.WriteLine("KernelDllBase {0,8:x} size {1,8:x} limit {2,8:x} \n", __arglist(p.KernelDllBase, p.KernelDllSize, limit)); SMAPINFO *smap = (SMAPINFO*)p.Smap; for (uint i = 0; i < p.SmapCount; i++) { if (smap[i].type == (ulong)SMAPINFO.AddressType.Free) { if (RangeWithinLimits(smap[i].addr, smap[i].size, upperLimit, lowerLimit, out baseAddress, out size)){ if(baseAddress + size >= limit) { UIntPtr start = baseAddress; UIntPtr end = baseAddress + size; if (start < limit) { start = limit; } if ((end - start) >= pageCount * sizeof(uint)) { pageTable = (uint *) start; break; } } } } } if (pageTable == null) { DebugStub.WriteLine("pageTable == null, un-able to find page table memory\n"); DebugStub.Break(); } DebugStub.WriteLine("p.PhysicalBase 0x{0,8:x8} upperLimit: 0x{1,8:x8}, pageTable: 0x{2,8:x8}", __arglist(p.PhysicalBase, upperLimit, (UIntPtr)pageTable)); Tracing.Log(Tracing.Debug, "Limit of RAM={0,8:x}, entries={1:x}, table={2:x}", upperLimit, pageCount, (UIntPtr) pageTable); // Initialize all page descriptors to Unknown. SetPages(0, pageCount, MemoryManager.PageUnknown); // Second pass over SMAP, mark known RAM. // we use to bootloader knowledge to map our address space for (uint i = 0; i < p.SmapCount; i++) { if (RangeWithinLimits(smap[i].addr, smap[i].size, upperLimit, lowerLimit, out baseAddress, out size)) { if (smap[i].type == (ulong)SMAPINFO.AddressType.Free) { SetRange(baseAddress, size, MemoryManager.PageFree); } else if(smap[i].type == (ulong)SMAPINFO.AddressType.KernelNonGc) { SetRange(baseAddress, size, MemoryManager.KernelPageNonGC); } else if(smap[i].type == (ulong)SMAPINFO.AddressType.KernelStack) { SetRange(baseAddress, size, MemoryManager.KernelPageStack); } else { ; } } else { DebugStub.WriteLine("Ignoring Entry: Smap range {0:x8}...{1:x8} memory range {2:x8}...{3:x8}", __arglist( smap[i].addr, smap[i].addr + smap[i].size, lowerLimit, upperLimit) ); } } // Set non-physical pages as unknown SetRange(0, Platform.ThePlatform.PhysicalBase, MemoryManager.PageUnknown); // Record the page table memory. SetRange((UIntPtr) pageTable, pageCount * sizeof(uint), MemoryManager.KernelPageNonGC); // Initialize the free and save lists. fixed (FreeNode *tail = &freeListTail) { freeList = tail; FreeNode.Init(freeList, false); } fixed (FreeNode *tail = &saveListTail) { saveList = tail; FreeNode.Init(saveList, true); } availableMemory = UIntPtr.Zero; // Initialize the memory reservations MemoryReservations.Initialize(); uint *desc = pageTable; uint last = *desc & MemoryManager.SystemPageMask; UIntPtr begin = UIntPtr.Zero; for (UIntPtr i = UIntPtr.Zero; i < pageCount; i++) { uint val = *desc++ & MemoryManager.SystemPageMask; if (val != last) { if (last == MemoryManager.PageFree) { FreeNode.CreateAndInsert(freeList, AddrFromPage(begin), AddrFromPage(i) - AddrFromPage(begin)); } begin = i; last = val; } } } internal static void Finalize() { // Doesn't actually do anything. } [NoStackLinkCheckTrans] private static bool Lock() { bool enabled = Processor.DisableInterrupts(); #if SINGULARITY_MP pageLock.Acquire(Thread.CurrentThread); #endif // SINGULARITY_MP return enabled; } [NoStackLinkCheckTrans] private static void Unlock(bool iflag) { #if SINGULARITY_MP pageLock.Release(Thread.CurrentThread); #endif // SINGULARITY_MP Processor.RestoreInterrupts(iflag); } // Currently, we just return the BSP free list. In the // future, this should consult the ProcessorMemoryMap private static unsafe FreeNode* GetFreeList() { return freeList; } private static unsafe FreeNode* GetSaveList() { return saveList; } ////////////////////////////////////////////////////////////////////// // internal static UIntPtr PageCount { get { return pageCount; } } internal static unsafe uint * PageTable { get { return pageTable; } } [NoStackLinkCheckTrans] [FlatPagesLock] internal static UIntPtr StackAllocate(UIntPtr bytes, UIntPtr reserve, UIntPtr alignment, Process process, uint extra, bool kernelAllocation, bool initialStack) { #if NO__TRACE_PAGES #else Kernel.Waypoint(960); #endif UIntPtr got = new UIntPtr(); bool iflag = Lock(); try { got = RawAllocate(bytes, reserve, alignment, (process != null ? process.ProcessTag : MemoryManager.KernelPage) | (extra & MemoryManager.ExtraMask) | (uint)PageType.Stack, kernelAllocation); #if VERBOSE Tracing.Log(Tracing.Debug, "{0:x8} Allocate({1:x},{2:x},{3:x}", Kernel.AddressOf(process), bytes, reserve, alignment); #endif if (got != UIntPtr.Zero) { if (initialStack) { // increase our kernel stack reservation MemoryReservations.ThreadCreateNotification(); } if (process != null) { process.Allocated(bytes); } } } finally { Unlock(iflag); } #if NO__TRACE_PAGES #else Kernel.Waypoint(961); #endif return got; } [NoStackLinkCheckTrans] [FlatPagesLock] internal static void StackFree(UIntPtr addr, UIntPtr bytes, Process process, bool kernelAllocation, bool initialStack) { bool iflag = Lock(); try { RawFree(addr, bytes, process != null ? process.ProcessTag : MemoryManager.KernelPage, true, kernelAllocation); if (process != null) { process.Freed(bytes); } if (initialStack) { // release our kernel stack reservation MemoryReservations.ThreadDestroyNotification(); } } finally { Unlock(iflag); } return; } // // If process == null, its a kernel allocation, otherwise its // a user (SIP) allocation. // [NoStackLinkCheckTrans] [FlatPagesLock] internal static UIntPtr Allocate(UIntPtr bytes, UIntPtr reserve, UIntPtr alignment, Process process, uint extra, PageType type) { #if NO__TRACE_PAGES #else Kernel.Waypoint(960); #endif UIntPtr got = new UIntPtr(); bool iflag = Lock(); try { got = RawAllocate(bytes, reserve, alignment, (process != null ? process.ProcessTag : MemoryManager.KernelPage) | (extra & MemoryManager.ExtraMask) | (uint)type, true); #if VERBOSE Tracing.Log(Tracing.Debug, "{0:x8} Allocate({1:x},{2:x},{3:x}", Kernel.AddressOf(process), bytes, reserve, alignment); #endif if (process != null) { process.Allocated(bytes); } } finally { Unlock(iflag); } #if NO__TRACE_PAGES #else Kernel.Waypoint(961); #endif return got; } [FlatPagesLock] internal static UIntPtr AllocateBelow(UIntPtr limit, UIntPtr bytes, UIntPtr alignment, Process process, uint extra, PageType type) { UIntPtr got = new UIntPtr(); bool iflag = Lock(); try { got = RawAllocateBelow(limit, bytes, alignment, (process != null ? process.ProcessTag : MemoryManager.KernelPage) | (extra & MemoryManager.ExtraMask) | (uint)type); if (process != null) { process.Allocated(bytes); } } finally { Unlock(iflag); } return got; } [FlatPagesLock] internal static UIntPtr AllocateExtend(UIntPtr addr, UIntPtr bytes, Process process, uint extra, PageType type) { UIntPtr got = new UIntPtr(); bool iflag = Lock(); try { uint tag = (process != null ? process.ProcessTag : MemoryManager.KernelPage) | (extra & MemoryManager.ExtraMask) | (uint)type; got = RawAllocateExtend(addr, bytes, tag); if (got != UIntPtr.Zero && process != null) { process.Allocated(bytes); } } finally { Unlock(iflag); } return got; } [NoStackLinkCheckTrans] [FlatPagesLock] internal static void Free(UIntPtr addr, UIntPtr bytes, Process process) { bool iflag = Lock(); try { RawFree(addr, bytes, process != null ? process.ProcessTag : MemoryManager.KernelPage, false, true); if (process != null) { process.Freed(bytes); } } finally { Unlock(iflag); } } [FlatPagesLock] internal static unsafe UIntPtr FreeAll(Process process) { DebugStub.Assert(process != null, "FlatPages.FreeAll null process"); DebugStub.Assert(process.ProcessTag != MemoryManager.KernelPage, "FlatPages.FreeAll ProcessTag={0}", __arglist(process.ProcessTag)); uint tag = process.ProcessTag & MemoryManager.ProcessPageMask; uint *pageLimit = pageTable + pageCount; UIntPtr bytes = 0; Tracing.Log(Tracing.Debug, "FreeAll({0,8:x})", tag); for (uint *begin = pageTable; begin < pageLimit;) { uint *limit = begin; uint val = (*limit++) & MemoryManager.ProcessPageMask; #if VERBOSE unchecked { Tracing.Log(Tracing.Debug, " {0,8:x}: {1,8:x}", AddrFromPage((UIntPtr)(begin - pageTable)), val); } #endif if (val == tag) { while ((((*limit) & MemoryManager.ProcessPageMask) == tag) && (limit < pageLimit)) { limit++; } UIntPtr page = (UIntPtr)(begin - pageTable); UIntPtr size = (UIntPtr)(limit - begin); Tracing.Log(Tracing.Debug, " {0,8:x}..{1,8:x} : {2,8:x} [will free]", page << MemoryManager.PageBits, (page + size) << MemoryManager.PageBits, *begin); bool iflag = Lock(); UIntPtr sizeInBytes = (size << MemoryManager.PageBits); try { RawFree(AddrFromPage(page), sizeInBytes, tag, false, false); } finally { Unlock(iflag); } bytes += size; } else { while ((((*limit) & MemoryManager.ProcessPageMask) != tag) && (limit < pageLimit)) { limit++; } UIntPtr page = (UIntPtr)(begin - pageTable); UIntPtr size = (UIntPtr)(limit - begin); Tracing.Log(Tracing.Debug, "- {0,8:x}..{1,8:x} : {2,8:x} [will free]", page << MemoryManager.PageBits, (page + size) << MemoryManager.PageBits, *begin); } begin = limit; } if (process != null) { process.Freed(bytes * MemoryManager.PageSize); } return bytes * MemoryManager.PageSize; } [FlatPagesLock] internal static PageType Query(UIntPtr queryAddr, Process process, out UIntPtr regionAddr, out UIntPtr regionSize) { PageType type = new PageType(); bool iflag = Lock(); try { type = RawQuery(queryAddr, process != null ? process.ProcessTag : 0, out regionAddr, out regionSize); } finally { Unlock(iflag); } return type; } ////////////////////////////////////////////////////////////////////// // [NoStackLinkCheckTrans] private static unsafe UIntPtr RawAllocate(UIntPtr bytes, UIntPtr reserve, UIntPtr alignment, uint tag, bool kernelAllocation) { VTable.Assert(Processor.InterruptsDisabled()); #if NO__TRACE_PAGES #else Kernel.Waypoint(970); #endif if (alignment < MemoryManager.PageSize) { alignment = MemoryManager.PageSize; } if (reserve < bytes) { reserve = bytes; } #if VERBOSE Tracing.Log(Tracing.Debug, " size={0:x}, res={1:x}, aln={2:x}, tag={3:x}", bytes, reserve, alignment, tag); #endif // Check the allocation against memory reservations if (MemoryReservations.MemoryReservationExceeded((ulong)availableMemory, tag, kernelAllocation, bytes + reserve + alignment)) { return UIntPtr.Zero; } FreeNode * node = FreeNode.FindGoodFit(GetFreeList(), reserve, alignment); if (node == null) { node = FreeNode.FindGoodFit(GetFreeList(), bytes, alignment); if (node == null) { node = FreeNode.FindGoodFit(GetSaveList(), reserve, alignment); if (node == null) { node = FreeNode.FindGoodFit(GetSaveList(), bytes, alignment); if (node == null) { // TODO: We should try to combine free and save pages... // But for now, we just fail. DebugStub.WriteLine("Failed to allocate on the heap!!! {0} bytes kernelalloc {1}\n", __arglist(bytes, kernelAllocation)); return UIntPtr.Zero; } } } } UIntPtr addr = (UIntPtr)node; UIntPtr adjust = SpaceNotAligned(addr + node->bytes, alignment); UIntPtr found = node->bytes; #if VERBOSE Tracing.Log(Tracing.Debug, " 0. {0:x8}..{1:x8}: res={2:x}, adj={3:x}", addr, addr + found, reserve, adjust); #endif if (found > reserve + adjust) { // Put the extraneous pages in the free list. FreeNode.ReturnExtraBelow(GetFreeList(), ref addr, ref found, reserve + adjust); #if VERBOSE Tracing.Log(Tracing.Debug, " 1. {0:x8}..{1:x8}", addr, addr + found); #endif } #if ALLOW_BOOT_ARGLIST DebugStub.Assert (SpaceNotAligned(addr, alignment) == UIntPtr.Zero, "FlatPages.RawAllocate not aligned addr={0} alignment={1}", __arglist(addr, alignment)); #endif if (found > bytes) { // Put extra pages in the save list. FreeNode.ReturnExtraAbove(GetSaveList(), addr, ref found, bytes); #if VERBOSE Tracing.Log(Tracing.Debug, " 2. {0:x8}..{1:x8}", addr, addr + found); #endif } #if ALLOW_BOOT_ARGLIST DebugStub.Assert (found == bytes, "FlatPages.RawAllocate wrong amount found={0} bytes={1}", __arglist(found, bytes)); #endif SetRange(addr, found, tag); #if NO__TRACE_PAGES #else Kernel.Waypoint(971); #endif // // The owner information is not reliable for determining kernel // or SIP allocation when the page type is stack, so the logic // is a little extra complicated here. // if ((tag & MemoryManager.TypeMask) == (uint)PageType.Stack) { // Stack pages use the kernelAllocation flag if (kernelAllocation) { MemoryReservations.AllocationForStack((ulong)bytes); } else { MemoryReservations.SIPAllocationForStack((ulong)bytes); } } else if ((tag & MemoryManager.ProcessPageMask) != MemoryManager.KernelPage) { // process tag is reliable for non-stack allocations MemoryReservations.SIPAllocationForHeap((ulong)bytes); } else { MemoryReservations.AllocationForHeap((ulong)bytes); } LogMemoryOperation(kernelAllocation, tag, addr, bytes); return addr; } private static unsafe UIntPtr RawAllocateBelow(UIntPtr limit, UIntPtr bytes, UIntPtr alignment, uint tag) { VTable.Assert(Processor.InterruptsDisabled()); #if NO__TRACE_PAGES #else Kernel.Waypoint(972); #endif if (alignment < MemoryManager.PageSize) { alignment = MemoryManager.PageSize; } #if VERBOSE Tracing.Log(Tracing.Debug, "lim={0:x8}, size={1:x8}, align={2}, tag={3:x}", limit, bytes, alignment, tag); #endif // Check the allocation against memory reservations if (MemoryReservations.MemoryReservationExceeded((ulong)availableMemory, tag, false, bytes + alignment)) { return UIntPtr.Zero; } FreeNode * node = FreeNode.FindBelow(limit, GetFreeList(), bytes, alignment); if (node == null) { node = FreeNode.FindBelow(limit, GetSaveList(), bytes, alignment); if (node == null) { // TODO: We should try to combine free and save pages... // But for now, we just fail. return UIntPtr.Zero; } } UIntPtr addr = (UIntPtr)node; UIntPtr adjust = SpaceToAlign(addr, alignment); UIntPtr found = node->bytes; if (adjust != UIntPtr.Zero) { // Put the alignment pages in free list. FreeNode.ReturnExtraBelow(GetFreeList(), ref addr, ref found, found - adjust); } DebugStub.Assert (SpaceNotAligned(addr, alignment) == UIntPtr.Zero, "FlatPages.RawAllocateBelow not aligned addr={0} alignment={1}", __arglist(addr, alignment)); if (found > bytes) { // Put the extra pages in free list. #if VERBOSE Tracing.Log(Tracing.Debug, "found {0:x8}..{1:x8}, found={3:x8}, keep={4:x8}", addr, addr + found, found, bytes); #endif FreeNode.ReturnExtraAbove(GetFreeList(), addr, ref found, bytes); } DebugStub.Assert (found == bytes, "FlatPages.RawAllocateBelow wrong amount found={0} bytes={1}", __arglist(found, bytes)); SetRange(addr, found, tag); #if NO__TRACE_PAGES #else Kernel.Waypoint(973); #endif // Stacks are only allocated through RawAllocate() VTable.Assert((tag & MemoryManager.TypeMask) != (uint)PageType.Stack); if ((tag & MemoryManager.ProcessPageMask) != MemoryManager.KernelPage) { MemoryReservations.SIPAllocationForHeap((ulong)bytes); } else { MemoryReservations.AllocationForHeap((ulong)bytes); } LogMemoryOperation(false, tag, addr, bytes); return addr; } private static unsafe UIntPtr RawAllocateExtend(UIntPtr addr, UIntPtr bytes, uint tag) { VTable.Assert(Processor.InterruptsDisabled()); #if NO__TRACE_PAGES #else Kernel.Waypoint(974); #endif UIntPtr page = MemoryManager.PageFromAddr(addr); if (*(pageTable + page) != MemoryManager.PageFreeFirst) { Tracing.Log(Tracing.Error, "{0:x} is not first free page {1:x}.", addr, *(pageTable + page)); return UIntPtr.Zero; } // Check the allocation against memory reservations if (MemoryReservations.MemoryReservationExceeded((ulong)availableMemory, tag, false, bytes)) { return UIntPtr.Zero; } FreeNode *node = (FreeNode *)addr; if (node->bytes < bytes) { Tracing.Log(Tracing.Error, "Only {0:x} free bytes, not {1:x} as requested.", node->bytes, bytes); return UIntPtr.Zero; } #if VERBOSE Tracing.Log(Tracing.Debug, "addr={0:x8}, size={1:x8}, tag={2:x}", addr, bytes, tag); #endif // Remove the node from the list. FreeNode.Remove(node); UIntPtr found = node->bytes; if (found > bytes) { // Save the extra pages in the save list. FreeNode.ReturnExtraAbove(GetSaveList(), addr, ref found, bytes); } DebugStub.Assert (found == bytes, "FlatPages.RawAllocateExtend wrong amount found={0} bytes{1}", __arglist(found, bytes)); SetRange(addr, found, tag); #if NO__TRACE_PAGES #else Kernel.Waypoint(975); #endif // Stacks are only allocated through RawAllocate() VTable.Assert((tag & MemoryManager.TypeMask) != (uint)PageType.Stack); if ((tag & MemoryManager.ProcessPageMask) != MemoryManager.KernelPage) { MemoryReservations.SIPAllocationForHeap((ulong)bytes); } else { MemoryReservations.AllocationForHeap((ulong)bytes); } LogMemoryOperation(false, tag, addr, bytes); return addr; } [NoStackLinkCheckTrans] private static unsafe bool VerifyOwner(UIntPtr page, UIntPtr pages, uint tag) { tag &= MemoryManager.ProcessPageMask; for (UIntPtr i = UIntPtr.Zero; i < pages; i++) { if (((*(pageTable + page + i)) & MemoryManager.ProcessPageMask) != tag) { DebugStub.WriteLine("FlatPages.VerifyOwner page={0} i={1} tag={2} address 0x{3,8:x}", __arglist(page, i, tag, ((pageTable+page)))); return false; } #if false DebugStub.Assert (((*(pageTable + page + i)) & MemoryManager.ProcessPageMask) == tag, "FlatPages.VerifyOwner page={0} i={1} tag={2}", __arglist(page, i, tag)); #endif } return true; } [NoStackLinkCheckTrans] private static unsafe void RawFree(UIntPtr addr, UIntPtr bytes, uint tag, bool stack, bool kernelAllocation) { VTable.Assert(Processor.InterruptsDisabled()); UIntPtr bytesIn = bytes; #if NO__TRACE_PAGES #else Kernel.Waypoint(976); #endif #if VERBOSE Tracing.Log(Tracing.Debug, "adr={0:x}, size={1:x}, tag={2:x}", addr, bytes, tag); #endif bool result = VerifyOwner(MemoryManager.PageFromAddr(addr), MemoryManager.PagesFromBytes(bytes), tag); if(false == result) { DebugStub.WriteLine("VerifyOwner Failed for addr 0x{0,8:x} bytes {1} page {2} pages {3}\n", __arglist(addr, bytes, MemoryManager.PageFromAddr(addr), MemoryManager.PagesFromBytes(bytes))); DebugStub.Break(); } FreeNode *node = FreeNode.GetNodeAt(addr + bytes); FreeNode *prev = FreeNode.GetNodeFromLast(addr - MemoryManager.PageSize); SetRange(addr, bytes, MemoryManager.PageFree); // Try to combine with the previous region if it isn't a save region. if (prev != null && prev->isSave == false) { addr = (UIntPtr)prev; bytes += prev->bytes; FreeNode.Remove(prev); } // Try to combine with the next region even if it was a save region. if (node != null) { bytes += node->bytes; FreeNode.Remove(node); if (node->isSave) { // If next was save, then try to combine with the follower. node = FreeNode.GetNodeAt(addr + bytes); if (node != null) { bytes += node->bytes; FreeNode.Remove(node); } } } // Create the free node. FreeNode.CreateAndInsert(GetFreeList(), addr, bytes); #if NO__TRACE_PAGES #else Kernel.Waypoint(977); #endif // Stack allocations are ambiguous and must be called out separately if (stack) { if (kernelAllocation) { MemoryReservations.FreeForStack((ulong)bytesIn); } else { MemoryReservations.SIPFreeForStack((ulong)bytesIn); } } else if (tag != MemoryManager.KernelPage) { // Heap pages are reliable as per kernel/SIP MemoryReservations.SIPFreeForHeap((ulong)bytesIn); } else { MemoryReservations.FreeForHeap((ulong)bytesIn); } LogMemoryOperation(false, tag, addr, bytesIn); } private static unsafe PageType RawQuery(UIntPtr queryAddr, uint tag, out UIntPtr regionAddr, out UIntPtr regionSize) { VTable.Assert(Processor.InterruptsDisabled()); UIntPtr page = MemoryManager.PageFromAddr(queryAddr); UIntPtr startPage = page; UIntPtr limitPage = page + 1; PageType type; uint val = *(pageTable + startPage); bool used = ((val & MemoryManager.ProcessPageMask) != MemoryManager.SystemPage); if ((val & MemoryManager.ProcessPageMask) == MemoryManager.SystemPage) { // Found a system page. type = (tag == 0) ? (PageType)(val & MemoryManager.TypeMask) : PageType.Unknown; // Find the start of the SystemPage region. for (; startPage > UIntPtr.Zero; startPage--) { val = *(pageTable + startPage - 1); if ((val & MemoryManager.ProcessPageMask) != MemoryManager.SystemPage) { break; } } // Find the end of the SystemPage region for (; limitPage < pageCount; limitPage++) { val = *(pageTable + limitPage); if ((val & MemoryManager.ProcessPageMask) != MemoryManager.SystemPage) { break; } } } else { // Found a process page. uint ptag = val & MemoryManager.ProcessPageMask; type = (tag == 0 || ptag == tag) ? (PageType)(val & MemoryManager.TypeMask) : PageType.Unknown; if ((val & MemoryManager.TypeMask) == (uint)PageType.System) { // Find the start of the process code region. for (; startPage > UIntPtr.Zero; startPage--) { val = *(pageTable + startPage - 1); if ((val & MemoryManager.ProcessPageMask) != ptag || (val & MemoryManager.TypeMask) != (uint)PageType.System) { break; } } // Find the end of the process code region for (; limitPage < pageCount; limitPage++) { val = *(pageTable + limitPage); if ((val & MemoryManager.ProcessPageMask) != ptag || (val & MemoryManager.TypeMask) != (uint)PageType.System) { break; } } } else { // Find the start of the process region. for (; startPage > UIntPtr.Zero; startPage--) { val = *(pageTable + startPage - 1); if ((val & MemoryManager.ProcessPageMask) != ptag || (val & MemoryManager.TypeMask) == (uint)PageType.System) { break; } } // Find the end of the process region for (; limitPage < pageCount; limitPage++) { val = *(pageTable + limitPage); if ((val & MemoryManager.ProcessPageMask) != ptag || (val & MemoryManager.TypeMask) == (uint)PageType.System) { break; } } } } #if VERBOSE Tracing.Log(Tracing.Debug, "[{0:x8}..{1:x8}]", AddrFromPage(startPage), AddrFromPage(limitPage)); #endif regionAddr = AddrFromPage(startPage); regionSize = AddrFromPage(limitPage) - AddrFromPage(startPage); return type; } ////////////////////////////////////////////////////////////////////////// // private static unsafe void DumpQuery(UIntPtr addr) { UIntPtr regionAddr; UIntPtr regionSize; PageType type = RawQuery(addr, 0, out regionAddr, out regionSize); Tracing.Log(Tracing.Debug, " {0:x8} => {1:x8}..{2:x8} [{3:x}]", addr, regionAddr, regionAddr + regionSize, (uint)type); } private static unsafe void DumpFreeNodes(FreeNode *list) { DumpFreeNodes(list, list->isSave); } private static unsafe void DumpFreeNodes(FreeNode *list, bool isSave) { if (isSave) { DebugStub.WriteLine(" SaveList:"); } else { DebugStub.WriteLine(" FreeList:"); } for (FreeNode *node = list->next; node != list; node = node->next) { string fmt = " {0:x8}..{1:x8} prev={2:x8}, next={3:x8}, last={4:x8} "; if (node->isSave != isSave) { if (node->isSave) { fmt = " {0:x8}..{1:x8} prev={2:x8}, next={3:x8}, last={4:x8} [Save!]"; } else { fmt = " {0:x8}..{1:x8} prev={2:x8}, next={3:x8}, last={4:x8} [Free!]"; } } unchecked { DebugStub.WriteLine( fmt, __arglist( (UIntPtr)node, (UIntPtr)node + node->bytes, (UIntPtr)node->prev, (UIntPtr)node->next, (UIntPtr)node->last)); } } } internal static unsafe void Dump(string where) { DebugStub.WriteLine( "FlatPages.Dump: {0}", __arglist(where)); uint *descriptors = pageTable; uint last = *descriptors++ & MemoryManager.SystemPageMask; UIntPtr begin = UIntPtr.Zero; UIntPtr freePages = UIntPtr.Zero; UIntPtr usedPages = UIntPtr.Zero; UIntPtr unknownPages = UIntPtr.Zero; UIntPtr sharedPages = UIntPtr.Zero; for (UIntPtr i = (UIntPtr)1; i < pageCount; i++) { uint dsc = *descriptors++; uint val = dsc & MemoryManager.SystemPageMask; switch (val) { case MemoryManager.PageUnknown: unknownPages++; break; case MemoryManager.PageShared: sharedPages++; break; case MemoryManager.PageFree: freePages++; break; default: usedPages++; break; } if (dsc != last) { DebugStub.WriteLine( " {0:x8}..{1:x8} : {2:x8} : {3:x8}", __arglist( begin << MemoryManager.PageBits, i << MemoryManager.PageBits, last, (i - begin) << MemoryManager.PageBits)); last = dsc; begin = i; } } DebugStub.WriteLine(" {0:x8}..{1:x8} : {2:x8} : {3:x8}", __arglist( begin << MemoryManager.PageBits, pageCount << MemoryManager.PageBits, last, (pageCount - begin) << MemoryManager.PageBits)); DumpFreeNodes(GetFreeList(), false); DumpFreeNodes(GetSaveList(), true); DebugStub.WriteLine("Totals: free={0:x8}, used={1:x8}, unknown={2:x8}, reserved={3:x8}", __arglist( freePages << MemoryManager.PageBits, usedPages << MemoryManager.PageBits, unknownPages << MemoryManager.PageBits, sharedPages << MemoryManager.PageBits)); } ////////////////////////////////////////////////////////////////////// // [NoStackLinkCheckTrans] [NoHeapAllocation] private static unsafe void SetPages(UIntPtr startPage, UIntPtr pageCount, uint tag) { uint * descriptor = pageTable + startPage; #if VERY_VERBOSE Tracing.Log(Tracing.Audit, "SetPages(beg={0:x},num={1:x},val={2:x}", startPage << MemoryManager.PageBits, pageCount << MemoryManager.PageBits, tag); #endif while (pageCount > UIntPtr.Zero) { *descriptor++ = tag; pageCount--; } } [NoStackLinkCheckTrans] [NoHeapAllocation] internal static void SetRange(UIntPtr start, UIntPtr bytes, uint tag) { if (start > upperLimit) { return; } if (start + bytes > upperLimit) { bytes = upperLimit - start; } SetPages(MemoryManager.PageFromAddr(start), MemoryManager.PagesFromBytes(bytes), tag); } ////////////////////////////////////////////////////////////////////////// // public static UIntPtr GetMaxMemory() { return upperLimit; } [FlatPagesLock] public static unsafe UIntPtr GetFreeMemory() { #if TEST_MEMORY_TRACKING UIntPtr retval = 0; bool iflag = Lock(); try { uint *descriptors = pageTable; // Count free pages for (UIntPtr i = (UIntPtr)1; i < pageCount; i++) { uint dsc = *descriptors++; uint val = dsc & MemoryManager.SystemPageMask; if (val == MemoryManager.PageFree) { retval++; } } // // Make sure everything reports the same info // UIntPtr freeListSize = FreeNode.GetFreeListTotalSize(freeList); UIntPtr saveListSize = FreeNode.GetFreeListTotalSize(saveList); UIntPtr totalFreeList = freeListSize + saveListSize; if (((retval * MemoryManager.PageSize) != totalFreeList) || (totalFreeList != availableMemory)) { DebugStub.WriteLine("GetFreeMemory: Internal inconsistency"); DebugStub.WriteLine("Pages total 0x{0:x8}", __arglist(retval * MemoryManager.PageSize)); DebugStub.WriteLine("FreeList 0x{0:x8}, SaveList 0x{1:x8}, Total 0x{2:x8}", __arglist( (ulong)freeListSize, (ulong)saveListSize, ((ulong)freeListSize + (ulong)saveListSize))); DebugStub.WriteLine("availableMemory: 0x{0:x8}\n", __arglist(availableMemory)); DebugStub.Break(); } } finally { Unlock(iflag); } return retval * MemoryManager.PageSize; #else // // We track it in real time so it may be used by other components // at runtime. // return availableMemory; #endif } public static unsafe UIntPtr GetUsedMemory() { uint *descriptors = pageTable; UIntPtr retval = 0; // Count non-free pages for (UIntPtr i = (UIntPtr)1; i < pageCount; i++) { uint dsc = *descriptors++; uint val = dsc & MemoryManager.SystemPageMask; if (val != MemoryManager.PageFree) { retval++; } } return retval * MemoryManager.PageSize; } // // Memory monitoring support // [Inline] private static void LogMemoryOperation(bool isKernelMemory, uint tag, UIntPtr address, UIntPtr size) { if (CurrentSystemLogger != null) { ushort memoryType = (ushort)(tag & MemoryManager.TypeMask); if (isKernelMemory && ((tag & MemoryManager.TypeMask) == (uint)PageType.Stack)) { memoryType |= 0x8000; } CurrentSystemLogger.Log((ushort)((tag & MemoryManager.ProcessPageMask) >> 16), memoryType, address, size); } } public static void InitializeMemoryMonitoring() { CurrentSystemLogger = SystemAllocationLogger.Create("SystemMemoryLogger"); DebugStub.Assert(CurrentSystemLogger != null); FreeMemoryCounters = FreeMemoryDistribution.Create("FreeMemoryCounters", HistogramSize); DebugStub.Assert(FreeMemoryCounters != null); InitializeMemoryCounters(); } [FlatPagesLock] // moved into separate function to localize lock auditing private static void InitializeMemoryCounters() { bool iflag = Lock(); try { for (int i = 0; i < HistogramSize; i++) { FreeMemoryCounters.Buffer[i].BlockCount = 0; FreeMemoryCounters.Buffer[i].TotalBucketSize = 0; } unsafe { RegisterExitentFreeNodes(GetFreeList()); RegisterExitentFreeNodes(GetSaveList()); } } finally { Unlock(iflag); } } internal static int HistogramSize; internal static int HistogramGranularityShift; private static FreeMemoryDistribution FreeMemoryCounters; private static SystemAllocationLogger CurrentSystemLogger; [Inline] internal static int GetBucketIndex(UIntPtr bytes) { UIntPtr bucket = bytes >> 20; if (bucket < (UIntPtr)HistogramSize) { return (int)bucket; } return (HistogramSize - 1); } [NoStackLinkCheckTrans] internal static void RegisterFreeNode(UIntPtr blockSize) { if (FreeMemoryCounters != null) { int i = GetBucketIndex(blockSize); FreeMemoryCounters.Buffer[i].BlockCount += 1; FreeMemoryCounters.Buffer[i].TotalBucketSize += blockSize; } } [NoStackLinkCheckTrans] internal static void DeRegisterFreeNode(UIntPtr blockSize) { if (FreeMemoryCounters != null) { int i = GetBucketIndex(blockSize); DebugStub.Assert((FreeMemoryCounters.Buffer[i].BlockCount > 0), "ASSERT: FreeMemoryCounters.BlockCount > 0"); DebugStub.Assert((FreeMemoryCounters.Buffer[i].TotalBucketSize >= blockSize), "ASSERT: FreeMemoryCounters.TotalBucketSize > blockSize"); FreeMemoryCounters.Buffer[i].BlockCount -= 1; FreeMemoryCounters.Buffer[i].TotalBucketSize -= blockSize; } } private static unsafe void RegisterExitentFreeNodes(FreeNode *list) { for (FreeNode *node = list->next; node != list; node = node->next) { RegisterFreeNode(node->bytes); } } ////////////////////////////////////////////////////////////////////// // [Inline] internal static UIntPtr AddrFromPage(UIntPtr page) { return (page << MemoryManager.PageBits); } [Inline] private static UIntPtr Align(UIntPtr data, UIntPtr size) { return ((data) & ~(size - 1)); } [Inline] private static UIntPtr Pad(UIntPtr data, UIntPtr size) { return ((data + size - 1) & ~(size - 1)); } [Inline] private static UIntPtr SpaceToAlign(UIntPtr data, UIntPtr size) { return Pad(data, size) - data; } [Inline] private static UIntPtr SpaceNotAligned(UIntPtr data, UIntPtr size) { return ((data) & (size - 1)); } ////////////////////////////////////////////////////////////////////// // [StructLayout(LayoutKind.Sequential)] private struct LastNode { internal const uint Signature = 0xaa2222aa; internal const uint Removed = 0xee1111ee; internal uint signature; internal unsafe FreeNode * node; [NoStackLinkCheckTrans] internal static unsafe LastNode * Create(UIntPtr addr, FreeNode *node) { LastNode *last = (LastNode *)addr; last->signature = LastNode.Signature; last->node = node; node->last = last; #if VERBOSE Tracing.Log(Tracing.Debug, "addr={0:x8}, node={1:x8}", addr, (UIntPtr) last->node); #endif return last; } [NoStackLinkCheckTrans] internal static unsafe void Remove(LastNode *last) { last->signature = Removed; last->node = null; } [NoStackLinkCheckTrans] internal static unsafe void PrintLastNode(UIntPtr addr) { LastNode *last = (LastNode *)addr; DebugStub.WriteLine("ln.{1:x8} ", __arglist((UIntPtr)last->node)); } } ////////////////////////////////////////////////////////////////////// // [StructLayout(LayoutKind.Sequential)] private struct FreeNode { internal const uint Signature = 0x22aaaa22; internal const uint Removed = 0x11eeee11; internal uint signature; internal unsafe FreeNode * prev; internal unsafe FreeNode * next; internal unsafe LastNode * last; internal UIntPtr bytes; internal bool isSave; [NoStackLinkCheckTrans] internal static unsafe void Init(FreeNode *list, bool isSave) { list->signature = Signature; list->prev = list; list->next = list; list->last = null; list->bytes = 0; list->isSave = isSave; } [NoStackLinkCheckTrans] internal static unsafe bool Remove(FreeNode *node) { FreeNode * prev; FreeNode * next; availableMemory -= node->bytes; DeRegisterFreeNode(node->bytes); UIntPtr page = MemoryManager.PageFromAddr((UIntPtr)node); *(pageTable + page) = MemoryManager.PageFree; next = node->next; prev = node->prev; prev->next = next; next->prev = prev; if (node->last != null) { LastNode.Remove(node->last); } node->signature = Removed; return (next == prev); } [NoStackLinkCheckTrans] private static unsafe void InsertAsPrev(FreeNode *list, FreeNode *node) { FreeNode * prev; prev = list->prev; node->next = list; node->prev = prev; prev->next = node; list->prev = node; } [NoStackLinkCheckTrans] private static unsafe void InsertAsNext(FreeNode *list, FreeNode *node) { FreeNode * next; next = list->next; node->prev = list; node->next = next; next->prev = node; list->next = node; } [NoStackLinkCheckTrans] private static unsafe void InsertBySize(FreeNode *list, FreeNode *node) { #if ALLOW_BOOT_ARGLIST DebugStub.Assert(node->bytes > 0, "FlatPages.InsertBySize node->bytes={0}", __arglist(node->bytes)); #endif if (node->bytes <= SmallSize) { // If the size is pretty small, we insert from the back of the list... for (FreeNode *step = list->prev; step != list; step = step->prev) { if (step->bytes >= node->bytes) { InsertAsNext(step, node); return; } } InsertAsNext(list, node); } else { // Insert a region into the list by size. for (FreeNode *step = list; step->next != list; step = step->next) { if (step->next->bytes <= node->bytes) { InsertAsNext(step, node); return; } } InsertAsPrev(list, node); } } /////////////////////////////////////////////////////////// // FreeNode's new routines start here internal static unsafe void PrintFreeList(FreeNode *list) { int count = 0; DebugStub.WriteLine (" PRINT FREE LIST (tail.{0:x8} prev.{1:x8} next.{2:x8})", __arglist((UIntPtr)(list), (UIntPtr)list->prev, (UIntPtr)list->next)); DebugStub.WriteLine(" ---------------------------------------------------"); for (FreeNode *node = list->next; node != list; node = node->next) { DebugStub.Print (" [{0}] b.{1:x8} e.{2:x8} {3,8}KB p.{4:x8} n.{5:x8} l.{6:x8} -- ", __arglist( count, (UIntPtr)node, (UIntPtr)node + node->bytes, node->bytes/(1024), (UIntPtr)node->prev, (UIntPtr)node->next, (UIntPtr)node->last)); if (node->last != null) { LastNode.PrintLastNode((UIntPtr)(node->last)); } else { DebugStub.WriteLine(); } if (count++ > 20) { DebugStub.WriteLine("\n **** ERROR INFINITE LIST ****\n"); DebugStub.Break(); } } } internal static unsafe UIntPtr GetFreeListTotalSize(FreeNode *list) { UIntPtr size = 0; for (FreeNode *node = list->next; node != list; node = node->next) { size += node->bytes; } return size; } [NoStackLinkCheckTrans] internal static unsafe FreeNode* GetFreeNodeAtBreakAddr(FreeNode *list, UIntPtr breakAddr) { int count = 0; for (FreeNode *node = list->next; node != list; node = node->next) { if ((UIntPtr)node <= breakAddr && breakAddr < ((UIntPtr)node + node->bytes)) { return node; } if (count++ > 20) { DebugStub.WriteLine(" WARNING: Can't GetFreeNode ListTail.{0:x8} at {1:x8} after 20 iterations", __arglist((UIntPtr)list, breakAddr)); DebugStub.Break(); } } return null; } [NoStackLinkCheckTrans] internal static unsafe FreeNode * FindGoodFit(FreeNode *list, UIntPtr bytes, UIntPtr alignment) { #if ALLOW_BOOT_ARGLIST DebugStub.Assert(bytes > 0, "FlatPages.FindGoodFit bytes={0}", __arglist(bytes)); #endif // If it is a small allocation, we try to accelerate the search. if (bytes <= SmallSize && alignment <= MemoryManager.PageSize) { for (FreeNode *node = list->prev; node != list; node = node->prev) { if (node->bytes >= bytes) { Remove(node); return node; } } return null; } else { // First try to find a region closest in size to bytes... FreeNode *best = null; for (FreeNode *node = list->next; node != list; node = node->next) { if (bytes <= node->bytes) { UIntPtr full = SpaceToAlign((UIntPtr)node, alignment) + bytes; if (full <= node->bytes) { // If we find a candidate, remember it. best = node; if (full == node->bytes) { // Stop if it is the ideal region. break; } } } else { // Stop if we have a candidate and we've reach smaller regions. if (best != null) { break; } } } if (best != null) { Remove(best); } return best; } } [NoStackLinkCheckTrans] internal static unsafe FreeNode * FindBelow(UIntPtr limit, FreeNode *list, UIntPtr bytes, UIntPtr alignment) { DebugStub.Assert(bytes > 0, "FlatPages.FindBelow bytes={0}", __arglist(bytes)); // Try to find the first region below the limit address. for (FreeNode *node = list->next; node != list; node = node->next) { if ((UIntPtr)node + bytes < limit && node->bytes >= bytes) { UIntPtr full = SpaceToAlign((UIntPtr)node, alignment) + bytes; if ((UIntPtr)node + full < limit && node->bytes >= full) { Remove(node); return node; } } } return null; } [NoStackLinkCheckTrans] internal static unsafe FreeNode * GetNodeAt(UIntPtr addr) { UIntPtr page = MemoryManager.PageFromAddr(addr); if (*(pageTable + page) == MemoryManager.PageFreeFirst) { return (FreeNode *)addr; } return null; } [NoStackLinkCheckTrans] internal static unsafe FreeNode * GetNodeFromLast(UIntPtr addr) { UIntPtr page = MemoryManager.PageFromAddr(addr); if (*(pageTable + page) == MemoryManager.PageFree && *(pageTable + page + 1) != MemoryManager.PageFree) { return ((LastNode *)addr)->node; } if (*(pageTable + page) == MemoryManager.PageFreeFirst) { return (FreeNode *)addr; } return null; } [NoStackLinkCheckTrans] internal static unsafe FreeNode * Create(UIntPtr addr, UIntPtr bytes, bool isSave) { // Mark a page as a node in the free list, initialize the node struct. FreeNode * node = (FreeNode *)addr; #if VERY_VERBOSE Tracing.Log(Tracing.Debug, isSave ? "{0:x8}..{1:x8}, last={4:x8}" : "{0:x8}..{1:x8}, last={4:x8}", addr, addr+bytes, addr + bytes - MemoryManager.PageSize); #endif UIntPtr page = MemoryManager.PageFromAddr(addr); *(pageTable + page) = MemoryManager.PageFreeFirst; node->signature = FreeNode.Signature; node->bytes = bytes; node->isSave = isSave; node->prev = null; node->next = null; node->last = null; if (bytes > MemoryManager.PageSize) { LastNode.Create(addr + bytes - MemoryManager.PageSize, node); } return node; } [NoStackLinkCheckTrans] internal static unsafe void CreateAndInsert(FreeNode *list, UIntPtr addr, UIntPtr bytes) { FreeNode * node = Create(addr, bytes, list->isSave); // // This memory is available on the freeList, or the // SaveList. // // Upper level memory lock protects access to this field. // availableMemory += bytes; RegisterFreeNode(bytes); #if VERBOSE Tracing.Log(Tracing.Debug, list->isSave ? "({0:x8}, {1:x8}, true), prev={3:x8}, next={4:x8}, last={5:x8}" : "({0:x8}, {1:x8}, false), prev={3:x8}, next={4:x8}, last={5:x8}", addr, bytes, (UIntPtr) node->prev, (UIntPtr) node->next, (UIntPtr) node->last); #endif #if ALLOW_BOOT_ARGLIST DebugStub.Assert((bytes & MemoryManager.PageMask) == 0, "FlatPages.CreateAndInsert bytes={0}", __arglist(bytes)); DebugStub.Assert((node->bytes & MemoryManager.PageMask) == 0, "FlatPages.CreateAndInsert node->bytes={0}", __arglist(node->bytes)); #endif InsertBySize(list, node); } [NoStackLinkCheckTrans] internal static unsafe void ReturnExtraAbove(FreeNode *list, UIntPtr addr, ref UIntPtr found, UIntPtr keep) { CreateAndInsert(list, addr + keep, found - keep); found = keep; } [NoStackLinkCheckTrans] internal static unsafe void ReturnExtraBelow(FreeNode *list, ref UIntPtr addr, ref UIntPtr found, UIntPtr keep) { CreateAndInsert(list, addr, found - keep); addr = addr + found - keep; found = keep; } } } }