/*******************************************************************/ /* WARNING */ /* This file should be identical in the Bartok and Singularity */ /* depots. Master copy resides in Bartok Depot. Changes should be */ /* made to Bartok Depot and propagated to Singularity Depot. */ /*******************************************************************/ // // Copyright (c) Microsoft Corporation. All rights reserved. // namespace System.GCs { using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; using Microsoft.Bartok.Runtime; #if SINGULARITY_KERNEL using Microsoft.Singularity; using Microsoft.Singularity.Memory; using Sing_MemoryManager = Microsoft.Singularity.Memory.MemoryManager; #elif SINGULARITY_PROCESS using Microsoft.Singularity; using Microsoft.Singularity.V1.Services; #endif [RequiredByBartok] abstract internal class PageTable { // WARNING: don't initialize any static fields in this class // without manually running the class constructor at startup! // The following are the several flavors for page table design: // 1. Centralized: Only one table for all the memory. There are two // cases within it: HIMEM or not. HIMEM forces the table to put beyond // 4G. HIMEM means for correctness testing, not for performance. // In both cases, an address is composed of two parts: // (Page number) (offset in the page) // 2. Otherwise: there are multiple page tables (DistributedPT). Two cases are allowed: // (1) FlatDistributedPT: All the page tables manage the same-sized // space. A page table itself is within the space it manages, and the // offset of it to the starting address of the space is fixed. // An address has three parts: // (Segment number)(page number in the segment)(Offset in the page) // Since a page table is located in a fixed offset of the segment it // manages, we do not need a segment table. The segment number // tells the segment address. With the fixed offset, we get the // the page table. // (2) two level hierarchy. An address has also three parts But // each page table can be put at an arbitrary place. So we need a // segment table to find the requested page table. Not implemented yet. But // is straightforward based on the flat distributed page table. // Note: to keep consistent with the previous central table, function Page() // will return (segment number)(page number in the segment) as the page number. // Note: In distributed case, pageTableCount refers to the total entries in all // page tables that have already been allocated. internal static UIntPtr pageTableCount; #if SINGULARITY protected static UIntPtr baseAddr; protected static UIntPtr limitAddr; #endif [RequiredByBartok] [AccessedByRuntime("referenced from brtforgc.asm/halforgc.asm")] internal static unsafe uint *halPageDescriptor; [Intrinsic] internal static PTType ptType; #if SINGULARITY_KERNEL internal const uint ProcessPageMask = Sing_MemoryManager.ProcessPageMask; internal static uint processTag; internal static unsafe void Initialize() { Tracing.Log(Tracing.Debug, "Initialize."); processTag = 0x10000; baseAddr = Sing_MemoryManager.KernelBaseAddr; pageTableCount = Sing_MemoryManager.KernelPageCount; limitAddr = baseAddr + pageTableCount << PageBits; halPageDescriptor = Sing_MemoryManager.KernelPageTable; } #elif SINGULARITY_PROCESS internal const uint ProcessPageMask = 0xffff0000u; internal static uint processTag; internal static unsafe void Initialize() { Tracing.Log(Tracing.Debug, "Initialize."); processTag = PageTableService.GetProcessTag(); baseAddr = PageTableService.GetBaseAddress(); pageTableCount = PageTableService.GetPageCount(); limitAddr = baseAddr + (pageTableCount << PageBits); halPageDescriptor = PageTableService.GetPageTable(); } #else private static uint processTag { get { VTable.NotReached("should not be using processTag outside of Singularity"); return 0; } set { VTable.NotReached("should not be using processTag outside of Singularity"); } } // Initialization sets up page table in the memory. The pagetable must be // set up before anything else (bootstrapmemory, GC,...) works. // This function is implemented in each subclass by "new static", which // is in general not ideal, but here since we explicitly call them by the class // names, and they do not call other "new static" functions, there // should not be any confusion. // Also note that every function in this class that have different implementations // in subclasses is implemented by testing ptType, and then calling the specific // class. We cannot use the same idea as GC does, which use an "instance" of // the specified GC type. The reason is that the instance needs allocate space // from bootstrapmemory, which however, at each allocation, needs to change // the pagetable. Since the pagetable has not generated an instance yet, the // bootstrapmemory cannot access the pagetable through an instance. [PreInitRefCounts] [NoStackLinkCheck] internal static unsafe void Initialize() { switch(ptType) { case PTType.CentralPT: { CentralPT.Initialize(); break; } case PTType.CentralPTHimem: { CentralPTHimem.Initialize(); break; } case PTType.FlatDistributedPT: { FlatDistributedPT.Initialize(); break; } case PTType.FlatDistributedPTTest: { FlatDistributedPTTest.Initialize(); break; } default: { VTable.NotReached("Unknown PT type: "+ptType); break; } } } #endif [System.Diagnostics.Conditional("SINGULARITY")] internal static void SetProcessTag(uint tag) { processTag = tag; } internal static bool Dump(ushort process) { UIntPtr pages = (UIntPtr) 0; UIntPtr freePages = UIntPtr.Zero; UIntPtr usedPages = UIntPtr.Zero; UIntPtr stackPages = UIntPtr.Zero; UIntPtr fixedPages = UIntPtr.Zero; UIntPtr sharedPages = UIntPtr.Zero; UIntPtr unknownPages = UIntPtr.Zero; for (UIntPtr i = (UIntPtr) 1; i < pageTableCount; i++) { if (Process(i) != process) { continue; } PageType type = Type(i); pages++; switch (type) { case PageType.UnusedDirty: case PageType.UnusedClean: case PageType.Unallocated: freePages++; break; case PageType.NonGC: case PageType.System: fixedPages++; break; case PageType.Stack: stackPages++; break; case PageType.Shared: sharedPages++; break; case PageType.Unknown: unknownPages++; break; case PageType.Owner2: case PageType.Owner3: type = PageType.Owner2; usedPages++; break; default: usedPages++; break; } } if (pages > 0) { VTable.DebugPrint("{0:d6}: free={1:d6}, use={2:d6}, stk={3:d6}, fix={4:d6}, shr={5:d6}, unk={6:d6}\n", __arglist(process, freePages, usedPages, stackPages, fixedPages, sharedPages, unknownPages)); return true; } return false; } internal static void Dump(String s) { VTable.DebugPrint("PageTable.Dump({0}):\n", __arglist(s)); ushort empty = 0; for (ushort proc = 0; proc < 10240; proc++) { if (Dump(proc)) { empty = 0; } else { empty++; } if (empty > 512) { break; } } Dump((ushort)0xffff); PageType lastType = Type(UIntPtr.Zero); ushort lastProcess = Process((UIntPtr) 1); UIntPtr begin = UIntPtr.Zero; UIntPtr freePages = UIntPtr.Zero; UIntPtr usedPages = UIntPtr.Zero; UIntPtr stackPages = UIntPtr.Zero; UIntPtr fixedPages = UIntPtr.Zero; UIntPtr sharedPages = UIntPtr.Zero; UIntPtr unknownPages = UIntPtr.Zero; for (UIntPtr i = (UIntPtr) 1; i < pageTableCount; i++) { PageType type = Type(i); switch (type) { case PageType.UnusedDirty: case PageType.UnusedClean: case PageType.Unallocated: freePages++; break; case PageType.NonGC: case PageType.System: fixedPages++; break; case PageType.Stack: stackPages++; break; case PageType.Shared: sharedPages++; break; case PageType.Unknown: unknownPages++; break; case PageType.Owner2: case PageType.Owner3: type = PageType.Owner2; usedPages++; break; default: usedPages++; break; } if ((type != lastType) || (Process(i) != lastProcess)) { #if NOTHING VTable.DebugPrint(" {0:x8}..{1:x8} ({2:d6} pages) : {3:x2} for {4:d5}\n", __arglist( ((ulong)begin) << PageBits, ((ulong)i) << PageBits, i - begin, (uint)lastType, lastProcess)); #endif lastType = type; lastProcess = Process(i); begin = i; } } #if NOTHING VTable.DebugPrint(" {0:x8}..{1:x8} ({2:d6} pages) : {3:x2} for {4:d5}\n", __arglist( ((ulong)begin) << PageBits, ((ulong)pageTableCount) << PageBits, pageTableCount - begin, (uint)lastType, lastProcess)); #endif VTable.DebugPrint(" =====: free={1:d6}, use={2:d6}, stk={3:d6}, fix={4:d6}, shr={5:d6}, unk={6:d6}\n", __arglist(0, freePages, usedPages, stackPages, fixedPages, sharedPages, unknownPages)); } internal static byte PageBits { [Inline] get { return 12; } } internal static UIntPtr PageSize { [Inline] get { return (UIntPtr)(1U << PageBits); } } internal static UIntPtr PageMask { [Inline] get { return PageSize - 1; } } internal static UIntPtr PageAlignMask { [Inline] get { return ~PageMask; } } [Inline] internal unsafe static PageType MyType(UIntPtr page) { #if SINGULARITY uint value = PageTableEntry(page); if ((value & ProcessPageMask) == processTag) { return (PageType)(value & 0xf); } else { return PageType.Unknown; } #else return Type(page); #endif } [Inline] internal unsafe static PageType Type(UIntPtr page) { VTable.Assert(page < pageTableCount, "page out of count"); return (PageType)(PageTableEntry(page) & 0xf); } [Inline] internal unsafe static void SetType(UIntPtr page, PageType newType) { VTable.Assert(page < pageTableCount); // for the Generational Collector, keep track of the max and min // page of each generation. switch(GC.gcType) { #if !SINGULARITY || ADAPTIVE_COPYING_COLLECTOR || SEMISPACE_COLLECTOR || SLIDING_COLLECTOR case GCType.AdaptiveCopyingCollector: case GCType.SemispaceCollector: case GCType.SlidingCollector: int generation = (int)newType; if (generation >= (int)GCs.GenerationalCollector.MIN_GENERATION && generation <= (int)GCs.GenerationalCollector.MAX_GENERATION) { if (page < GCs.GenerationalCollector.MinGenPage[generation]) { GCs.GenerationalCollector.MinGenPage[generation] = page; } if (page > GCs.GenerationalCollector.MaxGenPage[generation]) { GCs.GenerationalCollector.MaxGenPage[generation] = page; } } break; #endif #if !SINGULARITY || MARK_SWEEP_COLLECTOR || CONCURRENT_MS_COLLECTOR case GCType.MarkSweepCollector: case GCType.TableMarkSweepCollector: case GCType.ReferenceCountingCollector: case GCType.ConcurrentMSCollector: case GCType.AtomicRCCollector: case GCType.DeferredReferenceCountingCollector: case GCType.NullCollector: // do nothing if not a Generational Collector break; #endif default: VTable.Assert(false, "Unknown Garbage Collector."); break; } uint value = (PageTableEntry(page) & ~0xfU) | (uint)newType; SetPageTableEntry(page, value); } [System.Diagnostics.Conditional("DEBUG")] internal static void VerifyType(UIntPtr page, PageType pageType) { VTable.Assert(PageTable.Type(page) == pageType); } [Inline] internal static short Extra(UIntPtr page) { VTable.Assert(page < pageTableCount); return (short)((PageTableEntry(page) & 0xfff0) >> 4); } [Inline] internal static void SetExtra(UIntPtr page, short extra) { VTable.Assert(page < pageTableCount); uint value = (PageTableEntry(page) & ~0xfff0U) | ((uint) extra << 4); SetPageTableEntry(page, value); } [System.Diagnostics.Conditional("DEBUG")] internal static void VerifyExtra(UIntPtr page, short extra) { VTable.Assert(Extra(page) == extra); } [Inline] internal static ushort Process(UIntPtr page) { #if SINGULARITY VTable.Assert(page < pageTableCount); return (ushort)((PageTableEntry(page) & 0xffff0000u) >> 16); #else return 0; #endif } [Inline] [System.Diagnostics.Conditional("SINGULARITY")] internal unsafe static void SetProcess(UIntPtr page, ushort processId) { VTable.Assert(page < pageTableCount); uint value = (PageTableEntry(page) & ~0xffff0000U) | ((uint) processId << 16); SetPageTableEntry(page, value); } #if SINGULARITY [Inline] private unsafe static uint ProcessTag(UIntPtr page) { VTable.Assert(page < pageTableCount); return PageTableEntry(page) & 0xffff0000u; } #endif [Inline] [System.Diagnostics.Conditional("SINGULARITY")] private unsafe static void SetProcessTag(UIntPtr page, uint processTag) { VTable.Assert(page < pageTableCount); uint value = (PageTableEntry(page) & ~0xffff0000U) | processTag; SetPageTableEntry(page, value); } [Inline] internal static void SetType(UIntPtr startPage, UIntPtr pageCount, PageType newType) { while (pageCount > UIntPtr.Zero) { SetType(startPage, newType); startPage++; pageCount--; } } [System.Diagnostics.Conditional("DEBUG")] internal static void VerifyType(UIntPtr startPage, UIntPtr pageCount, PageType pageType) { while (pageCount > UIntPtr.Zero) { VerifyType(startPage, pageType); startPage++; pageCount--; } } [Inline] internal static void SetExtra(UIntPtr startPage, UIntPtr pageCount, short extra) { while (pageCount > UIntPtr.Zero) { SetExtra(startPage, extra); startPage++; pageCount--; } } [System.Diagnostics.Conditional("DEBUG")] internal static void VerifyExtra(UIntPtr startPage, UIntPtr pageCount, short extra) { while (pageCount > UIntPtr.Zero) { VerifyExtra(startPage, extra); startPage++; pageCount--; } } [Inline] [System.Diagnostics.Conditional("SINGULARITY")] internal static void SetProcess(UIntPtr startPage, UIntPtr pageCount) { uint processId = processTag; PageTable.SetProcessTag(startPage, pageCount, processTag); } [Inline] [System.Diagnostics.Conditional("SINGULARITY")] internal static void SetProcess(UIntPtr startPage, UIntPtr pageCount, ushort processId) { while (pageCount > UIntPtr.Zero) { SetProcess(startPage, processId); startPage++; pageCount--; } } [Inline] [System.Diagnostics.Conditional("SINGULARITY")] internal static void SetProcessTag(UIntPtr startPage, UIntPtr pageCount, uint processTag) { while (pageCount > UIntPtr.Zero) { SetProcessTag(startPage, processTag); startPage++; pageCount--; } } [Inline] internal static bool IsGcPage(UIntPtr page) { return IsGcPage(Type(page)); } [Inline] internal static bool IsMyGcPage(UIntPtr page) { return (PageTable.IsMyPage(page) && PageTable.IsGcPage(page)); } // NOTE: The following 5 functions are dependent on the definition of // System.GCs.PageType. [Inline] internal static bool IsGcPage(PageType pageType) { return pageType >= PageType.Owner0; } [Inline] internal static bool IsZombiePage(PageType pageType) { return (pageType & PageType.TypeMask) == PageType.ZombieMask; } [Inline] internal static bool IsLiveGcPage(PageType pageType) { return ( (pageType >= PageType.Owner0) && (pageType < PageType.ZombieMask)); } [Inline] internal static PageType ZombieToLive(PageType pageType) { VTable.Assert(IsZombiePage(pageType), "not a zombie page"); PageType type = (PageType)(pageType - PageType.Zombie); return type; } [Inline] internal static PageType LiveToZombie(PageType pageType) { VTable.Assert(IsLiveGcPage(pageType), "not a live GC page"); return (pageType | PageType.Zombie); } // End of PageType dependent functions [Inline] internal static bool IsNonGcPage(PageType pageType) { return pageType == PageType.NonGC; } [Inline] internal static bool IsStackPage(PageType pageType) { return pageType == PageType.Stack; } [Inline] internal static bool IsSystemPage(PageType pageType) { return pageType == PageType.System; } [Inline] internal static bool IsSharedPage(PageType pageType) { return pageType == PageType.Shared; } [Inline] internal static bool IsUnknownPage(PageType pageType) { return pageType == PageType.Unknown; } [Inline] internal static bool IsUnallocatedPage(PageType pageType) { return pageType == PageType.Unallocated; } [Inline] internal static bool IsUnusedPage(UIntPtr page) { return IsUnusedPageType(PageTable.Type(page)); } [Inline] internal static bool IsUnusedPageType(PageType pageType) { return (pageType == PageType.UnusedDirty || pageType == PageType.UnusedClean); } [Inline] internal unsafe static bool IsMyPage(UIntPtr page) { #if SINGULARITY return PageTable.ProcessTag(page) == processTag; #else return true; #endif } [Inline] internal static bool IsForeignAddr(UIntPtr addr) { #if SINGULARITY return (addr < baseAddr) || (addr >= limitAddr); #else return false; #endif } [Inline] internal static UIntPtr Page(UIntPtr addr) { #if SINGULARITY VTable.Assert((addr >= baseAddr) && (addr < limitAddr)); return (addr - baseAddr) >> PageBits; #else return (addr >> PageBits); #endif } [Inline] internal static UIntPtr PageCount(UIntPtr size) { return ((size + PageMask) >> PageBits); } [Inline] internal static UIntPtr PageAddr(UIntPtr page) { #if SINGULARITY return ((page << PageBits) + baseAddr); #else return (page << PageBits); #endif } [Inline] internal static UIntPtr RegionSize(UIntPtr pageCount) { return (pageCount << PageBits); } [Inline] internal static bool PageAligned(UIntPtr addr) { return ((addr & PageMask) == UIntPtr.Zero); } [Inline] internal static UIntPtr PageAlign(UIntPtr addr) { return addr & PageAlignMask; } [Inline] internal static UIntPtr PagePad(UIntPtr addr) { return ((addr + PageMask) & ~PageMask); } [Inline] internal static UIntPtr PadBytesToPages(UIntPtr size) { return ((size + PageSize - 1) >> PageBits); } // The following functions have an implementation in each specific subclass. To avoid // any potential "new static" confusion, we have the names with "Impl" suffix. [Inline] internal static unsafe uint PageTableEntry(UIntPtr page) { switch(ptType) { case PTType.CentralPT: case PTType.CentralPTHimem: return CentralPT.PageTableEntryImpl(page); #if !SINGULARITY case PTType.FlatDistributedPT: case PTType.FlatDistributedPTTest: return FlatDistributedPT.PageTableEntryImpl(page); #endif default: { VTable.NotReached("Unknown PT type: "+ptType); return 0xffffffff; } } } [Inline] internal static unsafe void SetPageTableEntry(UIntPtr page, uint value) { switch(ptType) { case PTType.CentralPT: case PTType.CentralPTHimem: { CentralPT.SetPageTableEntryImpl(page, value); break; } #if !SINGULARITY case PTType.FlatDistributedPT: case PTType.FlatDistributedPTTest: { FlatDistributedPT.SetPageTableEntryImpl(page, value); break; } #endif default: { VTable.NotReached("Unknown PT type: "+ptType); break; } } } internal static unsafe void CreateNewPageTablesIfNecessary(UIntPtr startPage, UIntPtr pageCount) { #if !SINGULARITY if (ptType == PTType.FlatDistributedPT ||ptType == PTType.FlatDistributedPTTest) { FlatDistributedPT.CreateNewPageTablesIfNecessaryImpl(startPage, pageCount); } #endif } [Inline] internal static unsafe bool ValidMemAddress(void* addr) { #if !SINGULARITY if (ptType == PTType.CentralPTHimem) { return CentralPTHimem.ValidMemAddressImpl(addr); } #endif return true; } } }