904 lines
31 KiB
C#
904 lines
31 KiB
C#
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Microsoft Research Singularity
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// File: VirtualMemoryRange.cs
|
|
//
|
|
// Note:
|
|
//
|
|
// A VirtualMemoryRange is a window of contiguous virtual memory that supports
|
|
// general-purpose use. It comes with a (very) simple page allocator and a
|
|
// page table that tracks how each virtual page is being used, to support a GC.
|
|
//
|
|
|
|
#if PAGING
|
|
|
|
using System;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Runtime.InteropServices;
|
|
using System.Threading;
|
|
using System.GCs;
|
|
|
|
using Microsoft.Singularity;
|
|
|
|
namespace Microsoft.Singularity.Memory
|
|
{
|
|
//
|
|
// We need to create VirtualMemoryRanges very early, before we have the use
|
|
// of a GCable heap. But later on, it's very useful to be able to pass around
|
|
// references to them as objects. So, we have the struct, which primitive
|
|
// parts of the system use directly, and then a wrapper object (below) for general
|
|
// use.
|
|
//
|
|
[NoCCtor]
|
|
[CLSCompliant(false)]
|
|
public struct VirtualMemoryRange_struct
|
|
{
|
|
/////////////////////////////////////
|
|
// PRIVATE FIELDS
|
|
/////////////////////////////////////
|
|
|
|
// The address range that we *manage*
|
|
private readonly UIntPtr rangeBase;
|
|
private readonly UIntPtr rangeLimit;
|
|
|
|
// The address range that our page table *describes*
|
|
private readonly UIntPtr descrBase;
|
|
private readonly UIntPtr descrLimit;
|
|
|
|
// Data begins above the rangeBase since we need room for our
|
|
// page table.
|
|
private readonly UIntPtr dataStart;
|
|
|
|
// Number of data pages we *describe*
|
|
private readonly UIntPtr describedPages;
|
|
|
|
// One uint per page in our range
|
|
private unsafe readonly uint* pageTable;
|
|
|
|
|
|
// Protects non-readonly fields
|
|
private static SpinLock rangeLock;
|
|
|
|
// The next address to be alloced
|
|
private UIntPtr nextAlloc;
|
|
|
|
// Statistics
|
|
private ulong allocatedBytes;
|
|
private ulong allocatedCount;
|
|
private ulong freedBytes;
|
|
private ulong freedCount;
|
|
|
|
/////////////////////////////////////
|
|
// PUBLIC METHODS
|
|
/////////////////////////////////////
|
|
|
|
//
|
|
// The range of memory turned over to a VirtualMemoryRange structure
|
|
// must not have *any* mapped pages in it to start out with
|
|
//
|
|
// A VirtualMemoryRange can build a pagetable that *describes*
|
|
// more memory than it *manages* (this supports some kernel GC
|
|
// oddities). In that case, pages out-of-range are marked
|
|
// as PageType.Unknown. Obviously, allocation requests are never
|
|
// satisfied with out-of-bounds data.
|
|
//
|
|
internal unsafe VirtualMemoryRange_struct(
|
|
UIntPtr baseAddr, UIntPtr limitAddr,
|
|
UIntPtr descrBaseAddr, UIntPtr descrLimitAddr,
|
|
ProtectionDomain inDomain)
|
|
{
|
|
DebugStub.Assert(MemoryManager.IsPageAligned(baseAddr));
|
|
DebugStub.Assert(MemoryManager.IsPageAligned(limitAddr));
|
|
|
|
// The descriptive range can't be smaller than the managed range
|
|
DebugStub.Assert(descrLimitAddr >= limitAddr);
|
|
DebugStub.Assert(descrBaseAddr <= baseAddr);
|
|
|
|
descrBase = descrBaseAddr;
|
|
descrLimit = descrLimitAddr;
|
|
rangeBase = baseAddr;
|
|
rangeLimit = limitAddr;
|
|
rangeLock = new SpinLock();
|
|
|
|
// Figure out how many pages we need for a page table
|
|
UIntPtr pageTableSize = descrLimit - descrBase;
|
|
pageTableSize = MemoryManager.PagesFromBytes(pageTableSize);
|
|
pageTableSize *= 4; // one uint per page
|
|
pageTableSize = MemoryManager.PagePad(pageTableSize);
|
|
|
|
dataStart = baseAddr + pageTableSize;
|
|
nextAlloc = dataStart;
|
|
|
|
describedPages = MemoryManager.PagesFromBytes(descrLimit - descrBase);
|
|
|
|
// Commit and prepare the page table
|
|
pageTable = (uint*)baseAddr;
|
|
|
|
bool success = MemoryManager.CommitAndMapRange(
|
|
baseAddr, baseAddr + pageTableSize, inDomain);
|
|
|
|
if (!success) {
|
|
Kernel.Panic("Couldn't get pages to create a new VirtualMemoryRange page table");
|
|
}
|
|
|
|
allocatedBytes = 0;
|
|
allocatedCount = 0;
|
|
freedBytes = 0;
|
|
freedCount = 0;
|
|
|
|
// Describe the pages outside our range as Unknown
|
|
if (descrBase < rangeBase) {
|
|
SetPages(descrBase, rangeBase, MemoryManager.PageUnknown);
|
|
}
|
|
|
|
if (descrLimit > rangeLimit) {
|
|
SetPages(rangeLimit, descrLimit, MemoryManager.PageUnknown);
|
|
}
|
|
|
|
// The page-table pages are in use by the System
|
|
SetPages((UIntPtr)pageTable,
|
|
(UIntPtr)pageTable + pageTableSize,
|
|
MemoryManager.KernelPageNonGC);
|
|
|
|
// Describe pages in-range as Free
|
|
SetPages(dataStart, rangeLimit, MemoryManager.PageFree);
|
|
|
|
#if DEBUG
|
|
// Check that our data range is pristine
|
|
for (UIntPtr stepAddr = dataStart;
|
|
stepAddr < rangeLimit;
|
|
stepAddr += MemoryManager.PageSize) {
|
|
DebugStub.Assert(!VMManager.IsPageMapped(stepAddr));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// Allocates but does not commit a new range of memory.
|
|
//
|
|
internal UIntPtr Reserve(UIntPtr numPages, Process process,
|
|
uint extra, PageType type)
|
|
{
|
|
DebugStub.Assert(numPages > 0);
|
|
bool iflag = Lock();
|
|
|
|
try {
|
|
return ReserveInternal(numPages, process, extra, type);
|
|
}
|
|
finally {
|
|
Unlock(iflag);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Releases a previously reserved range of memory, but does not
|
|
// uncommit any pages.
|
|
//
|
|
internal UIntPtr Unreserve(UIntPtr startAddr, UIntPtr numPages, Process process)
|
|
{
|
|
DebugStub.Assert(MemoryManager.IsPageAligned(startAddr));
|
|
DebugStub.Assert(numPages > 0);
|
|
UIntPtr blockLimit = startAddr + MemoryManager.BytesFromPages(numPages);
|
|
DebugStub.Assert(startAddr >= dataStart);
|
|
DebugStub.Assert(blockLimit <= rangeLimit);
|
|
|
|
// Strictly a sanity check
|
|
uint tag = process != null ? process.ProcessTag : MemoryManager.KernelPage;
|
|
VerifyOwner(startAddr, blockLimit, tag);
|
|
|
|
return FreeInternal(startAddr, numPages);
|
|
}
|
|
|
|
//
|
|
// Allocates and commits a new range of memory.
|
|
//
|
|
internal UIntPtr Allocate(UIntPtr numPages, Process process,
|
|
uint extra, PageType type,
|
|
ProtectionDomain inDomain)
|
|
{
|
|
DebugStub.Assert(numPages > 0);
|
|
UIntPtr mapAddr = UIntPtr.Zero;
|
|
|
|
bool iflag = Lock();
|
|
|
|
// Within our lock, figure out where we're going to stick the newly
|
|
// mapped memory, and mark it used.
|
|
try {
|
|
mapAddr = ReserveInternal(numPages, process, extra, type);
|
|
|
|
if (mapAddr == UIntPtr.Zero) {
|
|
DebugStub.Assert(false, "Failed to find a mapping point for new memory");
|
|
return mapAddr;
|
|
}
|
|
}
|
|
finally {
|
|
Unlock(iflag);
|
|
}
|
|
|
|
UIntPtr limitAddr = mapAddr + MemoryManager.BytesFromPages(numPages);
|
|
|
|
// Now actually map pages to the chosen region.
|
|
if (!MemoryManager.CommitAndMapRange(mapAddr, limitAddr, inDomain)) {
|
|
// yikes; failure
|
|
return UIntPtr.Zero;
|
|
}
|
|
|
|
allocatedCount++;
|
|
allocatedBytes += (ulong)MemoryManager.BytesFromPages(numPages);
|
|
|
|
if (process != null) {
|
|
process.Allocated(MemoryManager.BytesFromPages(numPages));
|
|
}
|
|
|
|
return mapAddr;
|
|
}
|
|
|
|
//
|
|
// Releases and uncommits a range of memory
|
|
//
|
|
internal unsafe UIntPtr Free(UIntPtr startPage,
|
|
UIntPtr numPages,
|
|
Process process)
|
|
{
|
|
DebugStub.Assert(startPage >= dataStart);
|
|
DebugStub.Assert(MemoryManager.IsPageAligned(startPage));
|
|
UIntPtr blockLimit = startPage + MemoryManager.BytesFromPages(numPages);
|
|
DebugStub.Assert(blockLimit <= rangeLimit);
|
|
|
|
//
|
|
// NOTE This is strictly a sanity check. The pagetable
|
|
// is ultimately not trustworthy because it is writeable by
|
|
// user-mode code.
|
|
//
|
|
uint tag = process != null ? process.ProcessTag : MemoryManager.KernelPage;
|
|
VerifyOwner(startPage, blockLimit, tag);
|
|
|
|
// Do it
|
|
return FreeAndUncommit(startPage, numPages);
|
|
}
|
|
|
|
// Returns the number of *addressable* pages, which may be less than
|
|
// the number of actually *usable* pages, if we describe a larger
|
|
// range than we manage.
|
|
internal UIntPtr PageCount
|
|
{ get { return describedPages; } }
|
|
|
|
// This lets the caller look directly into the page table; beware!
|
|
internal unsafe uint* PageTable
|
|
{ get { return pageTable; } }
|
|
|
|
internal UIntPtr AllocateExtend(UIntPtr addr,
|
|
UIntPtr bytes,
|
|
Process process,
|
|
uint extra,
|
|
PageType type)
|
|
{
|
|
// TODO: Extend not yet implemented
|
|
DebugStub.Break();
|
|
return (UIntPtr)null;
|
|
}
|
|
|
|
//
|
|
// Releases and uncommits all pages that are marked as belonging to a
|
|
// particular process.
|
|
//
|
|
internal unsafe UIntPtr FreeAll(Process process)
|
|
{
|
|
DebugStub.Assert(process != null);
|
|
DebugStub.Assert(process.ProcessTag != MemoryManager.KernelPage);
|
|
|
|
uint tag = process.ProcessTag & MemoryManager.ProcessPageMask;
|
|
uint *pageLimit = PageTable + PageCount;
|
|
UIntPtr bytesFreed = 0;
|
|
|
|
//
|
|
// NOTE here we choose to use the pagetable data,
|
|
// which is writeable by user-mode code. Keep in mind that
|
|
// it may be corrupt. We choose NOT to lock while traversing
|
|
// for a particular process' pages because no further allocations
|
|
// should occur tagged for that process once it's been stopped.
|
|
// Obviously, a rogue process may make more pages that appear
|
|
// to be owned by the zombie process appear.
|
|
//
|
|
for (uint *begin = PageTable; begin < pageLimit;) {
|
|
uint *limit = begin;
|
|
uint val = (*limit++) & MemoryManager.ProcessPageMask;
|
|
|
|
if (val == tag) {
|
|
while ((((*limit) & MemoryManager.ProcessPageMask) == tag) && (limit < pageLimit)) {
|
|
limit++;
|
|
}
|
|
|
|
UIntPtr startPage = (UIntPtr)(begin - PageTable);
|
|
UIntPtr pageCount = (UIntPtr)(limit - begin);
|
|
|
|
bytesFreed += FreeAndUncommit(AddrFromPage(startPage), pageCount);
|
|
}
|
|
else {
|
|
while ((((*limit) & MemoryManager.ProcessPageMask) != tag) && (limit < pageLimit)) {
|
|
limit++;
|
|
}
|
|
}
|
|
|
|
begin = limit;
|
|
}
|
|
|
|
if (process != null) {
|
|
process.Freed(bytesFreed);
|
|
}
|
|
|
|
return bytesFreed;
|
|
}
|
|
|
|
//
|
|
// Sets a range of pages to have the provided tag
|
|
//
|
|
internal unsafe void SetPages(UIntPtr startAddr, UIntPtr limitAddr, uint tag)
|
|
{
|
|
DebugStub.Assert(startAddr >= descrBase);
|
|
DebugStub.Assert(limitAddr <= descrLimit);
|
|
|
|
UIntPtr startIdx = PageFromAddr(startAddr);
|
|
UIntPtr limitIdx = MemoryManager.PageFromAddr(limitAddr - descrBase);
|
|
DebugStub.Assert(limitIdx <= PageCount);
|
|
|
|
for (UIntPtr i = startIdx; i < limitIdx; i++) {
|
|
pageTable[(uint)i] = tag;
|
|
}
|
|
}
|
|
|
|
internal PageType Query(UIntPtr queryAddr,
|
|
Process process,
|
|
out UIntPtr regionAddr,
|
|
out UIntPtr regionSize)
|
|
{
|
|
// TODO: Query not yet implemented
|
|
DebugStub.Break();
|
|
regionAddr = 0;
|
|
regionSize = 0;
|
|
return 0;
|
|
}
|
|
|
|
internal unsafe void Dump(string where)
|
|
{
|
|
// TODO: Dump not yet implemented
|
|
}
|
|
|
|
public UIntPtr GetMaxMemory()
|
|
{
|
|
// Return the amount of space we *manage*
|
|
return rangeLimit - rangeBase;
|
|
}
|
|
|
|
public unsafe UIntPtr GetFreeMemory()
|
|
{
|
|
// Report as free only that memory above our current
|
|
// allocate-next mark (even though there may be
|
|
// free holes below that mark that get reclaimed
|
|
// later)
|
|
return rangeLimit - nextAlloc;
|
|
}
|
|
|
|
public unsafe UIntPtr GetUsedMemory()
|
|
{
|
|
return GetMaxMemory() - GetFreeMemory();
|
|
}
|
|
|
|
public void GetUsageStatistics(out ulong allocatedCount,
|
|
out ulong allocatedBytes,
|
|
out ulong freedCount,
|
|
out ulong freedBytes)
|
|
{
|
|
allocatedCount = this.allocatedCount;
|
|
allocatedBytes = this.allocatedBytes;
|
|
freedCount = this.freedCount;
|
|
freedBytes = this.freedBytes;
|
|
}
|
|
|
|
internal UIntPtr PageFromAddr(UIntPtr addr)
|
|
{
|
|
DebugStub.Assert(addr >= descrBase);
|
|
DebugStub.Assert(addr < descrLimit);
|
|
return MemoryManager.PageFromAddr(addr - descrBase);
|
|
}
|
|
|
|
internal UIntPtr AddrFromPage(UIntPtr pageNum)
|
|
{
|
|
DebugStub.Assert(pageNum < PageCount);
|
|
return descrBase + MemoryManager.BytesFromPages(pageNum);
|
|
}
|
|
|
|
internal UIntPtr BaseAddress
|
|
{
|
|
get {
|
|
// Return the first *described* address
|
|
return descrBase;
|
|
}
|
|
}
|
|
|
|
// Returns the first address at which we may allocate memory.
|
|
// For validating pointers into this range.
|
|
internal UIntPtr DataStartAddr
|
|
{
|
|
get {
|
|
return dataStart;
|
|
}
|
|
}
|
|
|
|
// Returns the limit of the range we use for allocations.
|
|
internal UIntPtr DataLimitAddr
|
|
{
|
|
get {
|
|
return rangeLimit;
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////
|
|
// PRIVATE METHODS
|
|
/////////////////////////////////////
|
|
|
|
//
|
|
// Finds a contiguous block of virtual memory.
|
|
//
|
|
// *** CALLER MUST HOLD OUR PROTECTIVE LOCK! ***
|
|
//
|
|
// We don't acquire the spinlock ourselves in case the caller
|
|
// wants to do more work within it.
|
|
//
|
|
// Currently, the approach is VERY simplistic; we just keep handing out
|
|
// pages starting with the base of the virtual memory space until we
|
|
// bump into the top, and then we become much more inefficient.
|
|
//
|
|
// The expectation is that higher-level components will do smarter
|
|
// space management than this!
|
|
//
|
|
private UIntPtr ReserveInternal(UIntPtr numPages, Process process, uint extra, PageType type)
|
|
{
|
|
UIntPtr mapAddr = UIntPtr.Zero;
|
|
|
|
if (nextAlloc + MemoryManager.BytesFromPages(numPages) <= rangeLimit) {
|
|
mapAddr = nextAlloc;
|
|
UIntPtr limitAddr = mapAddr + MemoryManager.BytesFromPages(numPages);
|
|
nextAlloc = limitAddr;
|
|
|
|
uint tag =
|
|
(process != null ? process.ProcessTag : MemoryManager.KernelPage)
|
|
| (extra & MemoryManager.ExtraMask)
|
|
| (uint)type;
|
|
|
|
SetPages(mapAddr, limitAddr, tag);
|
|
}
|
|
else {
|
|
// TODO slow-path allocation not yet implemented
|
|
}
|
|
|
|
return mapAddr;
|
|
}
|
|
|
|
private unsafe UIntPtr FreeAndUncommit(UIntPtr startPage,
|
|
UIntPtr numPages)
|
|
{
|
|
// Drop all the memory
|
|
MemoryManager.UnmapAndReleaseRange(
|
|
startPage, startPage + MemoryManager.BytesFromPages(numPages));
|
|
|
|
// Do the bookkeeping
|
|
return FreeInternal(startPage, numPages);
|
|
}
|
|
|
|
private unsafe UIntPtr FreeInternal(UIntPtr startPage,
|
|
UIntPtr numPages)
|
|
{
|
|
DebugStub.Assert(MemoryManager.IsPageAligned(startPage));
|
|
DebugStub.Assert(startPage >= dataStart);
|
|
UIntPtr blockLimit = startPage + MemoryManager.BytesFromPages(numPages);
|
|
DebugStub.Assert(blockLimit <= rangeLimit);
|
|
DebugStub.Assert(nextAlloc >= blockLimit);
|
|
|
|
bool iflag = Lock();
|
|
|
|
try {
|
|
// Mark the pages free within our lock so we can rely on
|
|
// blocks being uniformly marked free in here
|
|
SetPages(startPage, blockLimit, MemoryManager.PageFree);
|
|
|
|
// Reduce fragmentation: lower the nextAlloc pointer if
|
|
// we have freed the top end of memory.
|
|
if (nextAlloc == blockLimit) {
|
|
nextAlloc = startPage;
|
|
|
|
//
|
|
// NOTE this chooses to use the page table
|
|
// information, which is currently usermode-accessible
|
|
// in the case of a user-mode range. Keep in mind that
|
|
// the information may be corrupt!
|
|
//
|
|
|
|
// Further optimization: drop nextAlloc more
|
|
// if the memory below us is free, too.
|
|
uint* pageTable = PageTable;
|
|
UIntPtr stepPage = nextAlloc;
|
|
uint val;
|
|
|
|
do {
|
|
nextAlloc = stepPage;
|
|
stepPage -= MemoryManager.PageSize;
|
|
uint dsc = pageTable[(uint)PageFromAddr(stepPage)];
|
|
val = dsc & MemoryManager.SystemPageMask;
|
|
}
|
|
while((val == MemoryManager.PageFree) &&
|
|
(!VMManager.IsPageMapped(stepPage))); // sanity
|
|
}
|
|
|
|
freedCount++;
|
|
freedBytes += (ulong)MemoryManager.BytesFromPages(numPages);
|
|
}
|
|
finally {
|
|
Unlock(iflag);
|
|
}
|
|
|
|
return MemoryManager.BytesFromPages(numPages);
|
|
}
|
|
|
|
// Confirms that the provided tag is consistent with the pagetable over
|
|
// a certain range
|
|
private unsafe void VerifyOwner(UIntPtr startAddr, UIntPtr limitAddr, uint tag)
|
|
{
|
|
#if DEBUG
|
|
DebugStub.Assert(startAddr >= dataStart);
|
|
DebugStub.Assert(limitAddr <= rangeLimit);
|
|
|
|
UIntPtr startIdx = PageFromAddr(startAddr);
|
|
UIntPtr limitIdx = MemoryManager.PagesFromBytes(limitAddr - descrBase);
|
|
DebugStub.Assert(limitIdx < PageCount);
|
|
|
|
tag &= MemoryManager.ProcessPageMask;
|
|
|
|
for (UIntPtr i = startIdx; i < limitIdx; i++) {
|
|
DebugStub.Assert
|
|
((pageTable[(uint)i] & MemoryManager.ProcessPageMask) == tag,
|
|
"VirtualMemoryRange.VerifyOwner page={0} i={1} tag={2}",
|
|
__arglist(i, i, tag));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
[Inline]
|
|
[NoStackLinkCheck]
|
|
private static bool Lock()
|
|
{
|
|
bool enabled = Processor.DisableInterrupts();
|
|
#if SINGULARITY_MP
|
|
rangeLock.Acquire(Thread.CurrentThread);
|
|
#endif // SINGULARITY_MP
|
|
return enabled;
|
|
}
|
|
|
|
[Inline]
|
|
[NoStackLinkCheck]
|
|
private static void Unlock(bool iflag)
|
|
{
|
|
#if SINGULARITY_MP
|
|
rangeLock.Release(Thread.CurrentThread);
|
|
#endif // SINGULARITY_MP
|
|
Processor.RestoreInterrupts(iflag);
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// This wrapper is not pretty. We want to use it to wrap structs that were
|
|
// placed at early-boot time into static memory, as well as structs that
|
|
// are allocated later when we have the use of a GC heap.
|
|
//
|
|
// The natural way to write this would be to write an abstract base class
|
|
// and then a subclass that knows how to wrap a pointer, and another that
|
|
// wraps an instance of the struct. Here, we cheezily trade a test for
|
|
// the VTable indirection.
|
|
//
|
|
public class VirtualMemoryRange
|
|
{
|
|
// We only use one or the other of these two fields
|
|
private readonly unsafe VirtualMemoryRange_struct* pRange;
|
|
private VirtualMemoryRange_struct range;
|
|
private readonly bool indirect; // true -> we use pRange
|
|
private readonly ProtectionDomain parentDomain;
|
|
|
|
internal unsafe VirtualMemoryRange(VirtualMemoryRange_struct* pRange,
|
|
ProtectionDomain inDomain)
|
|
{
|
|
this.pRange = pRange;
|
|
this.indirect = true;
|
|
// never use this.range
|
|
DebugStub.Assert(inDomain != null);
|
|
this.parentDomain = inDomain;
|
|
}
|
|
|
|
internal unsafe VirtualMemoryRange(UIntPtr baseAddr,
|
|
UIntPtr limitAddr,
|
|
ProtectionDomain inDomain)
|
|
{
|
|
DebugStub.Assert(inDomain != null);
|
|
this.parentDomain = inDomain;
|
|
|
|
range = new VirtualMemoryRange_struct(baseAddr, limitAddr,
|
|
baseAddr, limitAddr,
|
|
inDomain);
|
|
this.indirect = false;
|
|
// never use this.pRange
|
|
}
|
|
|
|
internal ProtectionDomain ParentDomain {
|
|
get {
|
|
return this.parentDomain;
|
|
}
|
|
}
|
|
|
|
internal unsafe UIntPtr Reserve(UIntPtr numPages, Process process,
|
|
uint extra, PageType type)
|
|
{
|
|
CheckAddressSpace();
|
|
|
|
if (indirect) {
|
|
return pRange->Reserve(numPages, process, extra, type);
|
|
} else {
|
|
return range.Reserve(numPages, process, extra, type);
|
|
}
|
|
}
|
|
|
|
internal unsafe UIntPtr Unreserve(UIntPtr startPage, UIntPtr numPages,
|
|
Process process)
|
|
{
|
|
CheckAddressSpace();
|
|
|
|
if (indirect) {
|
|
return pRange->Unreserve(startPage, numPages, process);
|
|
} else {
|
|
return range.Unreserve(startPage, numPages, process);
|
|
}
|
|
}
|
|
|
|
internal unsafe UIntPtr Allocate(UIntPtr numPages, Process process,
|
|
uint extra, PageType type)
|
|
{
|
|
CheckAddressSpace();
|
|
|
|
if (indirect) {
|
|
return pRange->Allocate(numPages, process, extra, type,
|
|
parentDomain);
|
|
} else {
|
|
return range.Allocate(numPages, process, extra, type,
|
|
parentDomain);
|
|
}
|
|
}
|
|
|
|
internal unsafe UIntPtr Free(UIntPtr startPage, UIntPtr numPages,
|
|
Process process)
|
|
{
|
|
CheckAddressSpace();
|
|
|
|
if (indirect) {
|
|
return pRange->Free(startPage, numPages, process);
|
|
} else {
|
|
return range.Free(startPage, numPages, process);
|
|
}
|
|
}
|
|
|
|
internal unsafe UIntPtr PageCount
|
|
{
|
|
get {
|
|
CheckAddressSpace();
|
|
|
|
if (indirect) {
|
|
return pRange->PageCount;
|
|
} else {
|
|
return range.PageCount;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal unsafe uint* PageTable
|
|
{
|
|
get {
|
|
CheckAddressSpace();
|
|
|
|
if (indirect) {
|
|
return pRange->PageTable;
|
|
} else {
|
|
return range.PageTable;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal unsafe UIntPtr AllocateExtend(UIntPtr addr,
|
|
UIntPtr bytes,
|
|
Process process,
|
|
uint extra,
|
|
PageType type)
|
|
{
|
|
CheckAddressSpace();
|
|
|
|
if (indirect) {
|
|
return pRange->AllocateExtend(addr, bytes, process, extra, type);
|
|
} else {
|
|
return range.AllocateExtend(addr, bytes, process, extra, type);
|
|
}
|
|
}
|
|
|
|
internal unsafe UIntPtr FreeAll(Process process)
|
|
{
|
|
CheckAddressSpace();
|
|
|
|
if (indirect) {
|
|
return pRange->FreeAll(process);
|
|
} else {
|
|
return range.FreeAll(process);
|
|
}
|
|
}
|
|
|
|
internal unsafe PageType Query(UIntPtr queryAddr,
|
|
Process process,
|
|
out UIntPtr regionAddr,
|
|
out UIntPtr regionSize)
|
|
{
|
|
CheckAddressSpace();
|
|
|
|
if (indirect) {
|
|
return pRange->Query(queryAddr, process,
|
|
out regionAddr, out regionSize);
|
|
} else {
|
|
return range.Query(queryAddr, process,
|
|
out regionAddr, out regionSize);
|
|
}
|
|
}
|
|
|
|
internal unsafe void Dump(string where)
|
|
{
|
|
CheckAddressSpace();
|
|
|
|
if (indirect) {
|
|
pRange->Dump(where);
|
|
} else {
|
|
range.Dump(where);
|
|
}
|
|
}
|
|
|
|
internal unsafe UIntPtr GetMaxMemory()
|
|
{
|
|
CheckAddressSpace();
|
|
|
|
if (indirect) {
|
|
return pRange->GetMaxMemory();
|
|
} else {
|
|
return range.GetMaxMemory();
|
|
}
|
|
}
|
|
|
|
internal unsafe UIntPtr GetFreeMemory()
|
|
{
|
|
CheckAddressSpace();
|
|
|
|
if (indirect) {
|
|
return pRange->GetFreeMemory();
|
|
} else {
|
|
return range.GetFreeMemory();
|
|
}
|
|
}
|
|
|
|
internal unsafe UIntPtr GetUsedMemory()
|
|
{
|
|
CheckAddressSpace();
|
|
|
|
if (indirect) {
|
|
return pRange->GetUsedMemory();
|
|
} else {
|
|
return range.GetUsedMemory();
|
|
}
|
|
}
|
|
|
|
internal unsafe void GetUsageStatistics(out ulong allocatedCount,
|
|
out ulong allocatedBytes,
|
|
out ulong freedCount,
|
|
out ulong freedBytes)
|
|
{
|
|
CheckAddressSpace();
|
|
|
|
if (indirect) {
|
|
pRange->GetUsageStatistics(
|
|
out allocatedCount, out allocatedBytes,
|
|
out freedCount, out freedBytes);
|
|
} else {
|
|
range.GetUsageStatistics(
|
|
out allocatedCount, out allocatedBytes,
|
|
out freedCount, out freedBytes);
|
|
}
|
|
}
|
|
|
|
internal unsafe UIntPtr PageFromAddr(UIntPtr addr)
|
|
{
|
|
CheckAddressSpace();
|
|
|
|
if (indirect) {
|
|
return pRange->PageFromAddr(addr);
|
|
} else {
|
|
return range.PageFromAddr(addr);
|
|
}
|
|
}
|
|
|
|
internal unsafe UIntPtr AddrFromPage(UIntPtr pageNum)
|
|
{
|
|
CheckAddressSpace();
|
|
|
|
if (indirect) {
|
|
return pRange->AddrFromPage(pageNum);
|
|
} else {
|
|
return range.AddrFromPage(pageNum);
|
|
}
|
|
}
|
|
|
|
internal unsafe UIntPtr BaseAddress
|
|
{
|
|
get {
|
|
CheckAddressSpace();
|
|
|
|
if (indirect) {
|
|
return pRange->BaseAddress;
|
|
} else {
|
|
return range.BaseAddress;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal unsafe UIntPtr DataStartAddr
|
|
{
|
|
get {
|
|
CheckAddressSpace();
|
|
|
|
if (indirect) {
|
|
return pRange->DataStartAddr;
|
|
} else {
|
|
return range.DataStartAddr;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal unsafe UIntPtr DataLimitAddr
|
|
{
|
|
get {
|
|
CheckAddressSpace();
|
|
|
|
if (indirect) {
|
|
return pRange->DataLimitAddr;
|
|
} else {
|
|
return range.DataLimitAddr;
|
|
}
|
|
}
|
|
}
|
|
|
|
[Inline]
|
|
private unsafe void CheckAddressSpace()
|
|
{
|
|
#if DEBUG
|
|
UIntPtr rangeLimit;
|
|
|
|
if (indirect) {
|
|
rangeLimit = pRange->DataLimitAddr;
|
|
} else {
|
|
rangeLimit = range.DataLimitAddr;
|
|
}
|
|
|
|
if (rangeLimit > BootInfo.KERNEL_BOUNDARY) {
|
|
// We are a user-space range. We had better
|
|
// be using the right address space!
|
|
DebugStub.Assert(parentDomain.AddressSpace == Processor.GetCurrentAddressSpace());
|
|
} // else we are a kernel range and are always mapped.
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // PAGING
|