Ryujinx/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs
Thomas Guillemard d5c0de8362 Implement IRoInterface (#445)
* Implement IRoInterface

This is required by Super Mario Party.
This commit also adds MapProcessCodeMemory and UnmapProcessCodeMemory functions in KMemoryManager. Those two calls might not reflect what the SVC of the same names do.

* Fix some code style issues

* Use MakeError to clarify error code

* Add NRR and NRO constants

* Fix some codestyle issues

* Fix InvalidMemoryState error code
2018-10-10 01:01:49 +02:00

1191 lines
No EOL
38 KiB
C#

using ChocolArm64.Memory;
using Ryujinx.HLE.Memory;
using System;
using System.Collections.Generic;
using static Ryujinx.HLE.HOS.ErrorCode;
namespace Ryujinx.HLE.HOS.Kernel
{
class KMemoryManager
{
public const int PageSize = 0x1000;
private LinkedList<KMemoryBlock> Blocks;
private AMemory CpuMemory;
private ArenaAllocator Allocator;
public long AddrSpaceStart { get; private set; }
public long AddrSpaceEnd { get; private set; }
public long CodeRegionStart { get; private set; }
public long CodeRegionEnd { get; private set; }
public long MapRegionStart { get; private set; }
public long MapRegionEnd { get; private set; }
public long HeapRegionStart { get; private set; }
public long HeapRegionEnd { get; private set; }
public long NewMapRegionStart { get; private set; }
public long NewMapRegionEnd { get; private set; }
public long TlsIoRegionStart { get; private set; }
public long TlsIoRegionEnd { get; private set; }
public long PersonalMmHeapUsage { get; private set; }
private long CurrentHeapAddr;
public KMemoryManager(Process Process)
{
CpuMemory = Process.Memory;
Allocator = Process.Device.Memory.Allocator;
long CodeRegionSize;
long MapRegionSize;
long HeapRegionSize;
long NewMapRegionSize;
long TlsIoRegionSize;
int AddrSpaceWidth;
AddressSpaceType AddrType = AddressSpaceType.Addr39Bits;
if (Process.MetaData != null)
{
AddrType = (AddressSpaceType)Process.MetaData.AddressSpaceWidth;
}
switch (AddrType)
{
case AddressSpaceType.Addr32Bits:
CodeRegionStart = 0x200000;
CodeRegionSize = 0x3fe00000;
MapRegionSize = 0x40000000;
HeapRegionSize = 0x40000000;
NewMapRegionSize = 0;
TlsIoRegionSize = 0;
AddrSpaceWidth = 32;
break;
case AddressSpaceType.Addr36Bits:
CodeRegionStart = 0x8000000;
CodeRegionSize = 0x78000000;
MapRegionSize = 0x180000000;
HeapRegionSize = 0x180000000;
NewMapRegionSize = 0;
TlsIoRegionSize = 0;
AddrSpaceWidth = 36;
break;
case AddressSpaceType.Addr36BitsNoMap:
CodeRegionStart = 0x200000;
CodeRegionSize = 0x3fe00000;
MapRegionSize = 0;
HeapRegionSize = 0x80000000;
NewMapRegionSize = 0;
TlsIoRegionSize = 0;
AddrSpaceWidth = 36;
break;
case AddressSpaceType.Addr39Bits:
CodeRegionStart = 0;
CodeRegionSize = 0x80000000;
MapRegionSize = 0x1000000000;
HeapRegionSize = 0x180000000;
NewMapRegionSize = 0x80000000;
TlsIoRegionSize = 0x1000000000;
AddrSpaceWidth = 39;
break;
default: throw new InvalidOperationException();
}
AddrSpaceStart = 0;
AddrSpaceEnd = 1L << AddrSpaceWidth;
CodeRegionEnd = CodeRegionStart + CodeRegionSize;
MapRegionStart = CodeRegionEnd;
MapRegionEnd = CodeRegionEnd + MapRegionSize;
HeapRegionStart = MapRegionEnd;
HeapRegionEnd = MapRegionEnd + HeapRegionSize;
NewMapRegionStart = HeapRegionEnd;
NewMapRegionEnd = HeapRegionEnd + NewMapRegionSize;
TlsIoRegionStart = NewMapRegionEnd;
TlsIoRegionEnd = NewMapRegionEnd + TlsIoRegionSize;
CurrentHeapAddr = HeapRegionStart;
if (NewMapRegionSize == 0)
{
NewMapRegionStart = AddrSpaceStart;
NewMapRegionEnd = AddrSpaceEnd;
}
Blocks = new LinkedList<KMemoryBlock>();
long AddrSpacePagesCount = (AddrSpaceEnd - AddrSpaceStart) / PageSize;
InsertBlock(AddrSpaceStart, AddrSpacePagesCount, MemoryState.Unmapped);
}
public void HleMapProcessCode(long Position, long Size)
{
long PagesCount = Size / PageSize;
if (!Allocator.TryAllocate(Size, out long PA))
{
throw new InvalidOperationException();
}
lock (Blocks)
{
InsertBlock(Position, PagesCount, MemoryState.CodeStatic, MemoryPermission.ReadAndExecute);
CpuMemory.Map(Position, PA, Size);
}
}
public long MapProcessCodeMemory(long Dst, long Src, long Size)
{
lock (Blocks)
{
long PagesCount = Size / PageSize;
bool Success = IsUnmapped(Dst, Size);
Success &= CheckRange(
Src,
Size,
MemoryState.Mask,
MemoryState.Heap,
MemoryPermission.Mask,
MemoryPermission.ReadAndWrite,
MemoryAttribute.Mask,
MemoryAttribute.None,
MemoryAttribute.IpcAndDeviceMapped,
out _,
out _,
out _);
if (Success)
{
long PA = CpuMemory.GetPhysicalAddress(Src);
InsertBlock(Dst, PagesCount, MemoryState.CodeStatic, MemoryPermission.ReadAndExecute);
InsertBlock(Src, PagesCount, MemoryState.Heap, MemoryPermission.None);
CpuMemory.Map(Dst, PA, Size);
return 0;
}
}
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
public long UnmapProcessCodeMemory(long Dst, long Src, long Size)
{
lock (Blocks)
{
long PagesCount = Size / PageSize;
bool Success = CheckRange(
Dst,
Size,
MemoryState.Mask,
MemoryState.CodeStatic,
MemoryPermission.None,
MemoryPermission.None,
MemoryAttribute.Mask,
MemoryAttribute.None,
MemoryAttribute.IpcAndDeviceMapped,
out _,
out _,
out _);
Success &= CheckRange(
Src,
Size,
MemoryState.Mask,
MemoryState.Heap,
MemoryPermission.Mask,
MemoryPermission.None,
MemoryAttribute.Mask,
MemoryAttribute.None,
MemoryAttribute.IpcAndDeviceMapped,
out _,
out _,
out _);
if (Success)
{
InsertBlock(Dst, PagesCount, MemoryState.Unmapped);
InsertBlock(Src, PagesCount, MemoryState.Heap, MemoryPermission.ReadAndWrite);
CpuMemory.Unmap(Dst, Size);
return 0;
}
}
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
public void HleMapCustom(long Position, long Size, MemoryState State, MemoryPermission Permission)
{
long PagesCount = Size / PageSize;
if (!Allocator.TryAllocate(Size, out long PA))
{
throw new InvalidOperationException();
}
lock (Blocks)
{
InsertBlock(Position, PagesCount, State, Permission);
CpuMemory.Map(Position, PA, Size);
}
}
public long HleMapTlsPage()
{
bool HasTlsIoRegion = TlsIoRegionStart != TlsIoRegionEnd;
long Position = HasTlsIoRegion ? TlsIoRegionStart : CodeRegionStart;
lock (Blocks)
{
while (Position < (HasTlsIoRegion ? TlsIoRegionEnd : CodeRegionEnd))
{
if (FindBlock(Position).State == MemoryState.Unmapped)
{
InsertBlock(Position, 1, MemoryState.ThreadLocal, MemoryPermission.ReadAndWrite);
if (!Allocator.TryAllocate(PageSize, out long PA))
{
throw new InvalidOperationException();
}
CpuMemory.Map(Position, PA, PageSize);
return Position;
}
Position += PageSize;
}
throw new InvalidOperationException();
}
}
public long TrySetHeapSize(long Size, out long Position)
{
Position = 0;
if ((ulong)Size > (ulong)(HeapRegionEnd - HeapRegionStart))
{
return MakeError(ErrorModule.Kernel, KernelErr.OutOfMemory);
}
bool Success = false;
long CurrentHeapSize = GetHeapSize();
if ((ulong)CurrentHeapSize <= (ulong)Size)
{
//Expand.
long DiffSize = Size - CurrentHeapSize;
lock (Blocks)
{
if (Success = IsUnmapped(CurrentHeapAddr, DiffSize))
{
if (!Allocator.TryAllocate(DiffSize, out long PA))
{
return MakeError(ErrorModule.Kernel, KernelErr.OutOfMemory);
}
long PagesCount = DiffSize / PageSize;
InsertBlock(CurrentHeapAddr, PagesCount, MemoryState.Heap, MemoryPermission.ReadAndWrite);
CpuMemory.Map(CurrentHeapAddr, PA, DiffSize);
}
}
}
else
{
//Shrink.
long FreeAddr = HeapRegionStart + Size;
long DiffSize = CurrentHeapSize - Size;
lock (Blocks)
{
Success = CheckRange(
FreeAddr,
DiffSize,
MemoryState.Mask,
MemoryState.Heap,
MemoryPermission.Mask,
MemoryPermission.ReadAndWrite,
MemoryAttribute.Mask,
MemoryAttribute.None,
MemoryAttribute.IpcAndDeviceMapped,
out _,
out _,
out _);
if (Success)
{
long PagesCount = DiffSize / PageSize;
InsertBlock(FreeAddr, PagesCount, MemoryState.Unmapped);
FreePages(FreeAddr, PagesCount);
CpuMemory.Unmap(FreeAddr, DiffSize);
}
}
}
CurrentHeapAddr = HeapRegionStart + Size;
if (Success)
{
Position = HeapRegionStart;
return 0;
}
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
public long GetHeapSize()
{
return CurrentHeapAddr - HeapRegionStart;
}
public long SetMemoryAttribute(
long Position,
long Size,
MemoryAttribute AttributeMask,
MemoryAttribute AttributeValue)
{
lock (Blocks)
{
if (CheckRange(
Position,
Size,
MemoryState.AttributeChangeAllowed,
MemoryState.AttributeChangeAllowed,
MemoryPermission.None,
MemoryPermission.None,
MemoryAttribute.BorrowedAndIpcMapped,
MemoryAttribute.None,
MemoryAttribute.DeviceMappedAndUncached,
out MemoryState State,
out MemoryPermission Permission,
out MemoryAttribute Attribute))
{
long PagesCount = Size / PageSize;
Attribute &= ~AttributeMask;
Attribute |= AttributeMask & AttributeValue;
InsertBlock(Position, PagesCount, State, Permission, Attribute);
return 0;
}
}
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
public KMemoryInfo QueryMemory(long Position)
{
if ((ulong)Position >= (ulong)AddrSpaceStart &&
(ulong)Position < (ulong)AddrSpaceEnd)
{
lock (Blocks)
{
return FindBlock(Position).GetInfo();
}
}
else
{
return new KMemoryInfo(
AddrSpaceEnd,
-AddrSpaceEnd,
MemoryState.Reserved,
MemoryPermission.None,
MemoryAttribute.None,
0,
0);
}
}
public long Map(long Src, long Dst, long Size)
{
bool Success;
lock (Blocks)
{
Success = CheckRange(
Src,
Size,
MemoryState.MapAllowed,
MemoryState.MapAllowed,
MemoryPermission.Mask,
MemoryPermission.ReadAndWrite,
MemoryAttribute.Mask,
MemoryAttribute.None,
MemoryAttribute.IpcAndDeviceMapped,
out MemoryState SrcState,
out _,
out _);
Success &= IsUnmapped(Dst, Size);
if (Success)
{
long PagesCount = Size / PageSize;
InsertBlock(Src, PagesCount, SrcState, MemoryPermission.None, MemoryAttribute.Borrowed);
InsertBlock(Dst, PagesCount, MemoryState.MappedMemory, MemoryPermission.ReadAndWrite);
long PA = CpuMemory.GetPhysicalAddress(Src);
CpuMemory.Map(Dst, PA, Size);
}
}
return Success ? 0 : MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
public long Unmap(long Src, long Dst, long Size)
{
bool Success;
lock (Blocks)
{
Success = CheckRange(
Src,
Size,
MemoryState.MapAllowed,
MemoryState.MapAllowed,
MemoryPermission.Mask,
MemoryPermission.None,
MemoryAttribute.Mask,
MemoryAttribute.Borrowed,
MemoryAttribute.IpcAndDeviceMapped,
out MemoryState SrcState,
out _,
out _);
Success &= CheckRange(
Dst,
Size,
MemoryState.Mask,
MemoryState.MappedMemory,
MemoryPermission.None,
MemoryPermission.None,
MemoryAttribute.Mask,
MemoryAttribute.None,
MemoryAttribute.IpcAndDeviceMapped,
out _,
out _,
out _);
if (Success)
{
long PagesCount = Size / PageSize;
InsertBlock(Src, PagesCount, SrcState, MemoryPermission.ReadAndWrite);
InsertBlock(Dst, PagesCount, MemoryState.Unmapped);
CpuMemory.Unmap(Dst, Size);
}
}
return Success ? 0 : MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
public long MapSharedMemory(KSharedMemory SharedMemory, MemoryPermission Permission, long Position)
{
lock (Blocks)
{
if (IsUnmapped(Position, SharedMemory.Size))
{
long PagesCount = SharedMemory.Size / PageSize;
InsertBlock(Position, PagesCount, MemoryState.SharedMemory, Permission);
CpuMemory.Map(Position, SharedMemory.PA, SharedMemory.Size);
return 0;
}
}
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
public long UnmapSharedMemory(long Position, long Size)
{
lock (Blocks)
{
if (CheckRange(
Position,
Size,
MemoryState.Mask,
MemoryState.SharedMemory,
MemoryPermission.None,
MemoryPermission.None,
MemoryAttribute.Mask,
MemoryAttribute.None,
MemoryAttribute.IpcAndDeviceMapped,
out MemoryState State,
out _,
out _))
{
long PagesCount = Size / PageSize;
InsertBlock(Position, PagesCount, MemoryState.Unmapped);
CpuMemory.Unmap(Position, Size);
return 0;
}
}
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
public long ReserveTransferMemory(long Position, long Size, MemoryPermission Permission)
{
lock (Blocks)
{
if (CheckRange(
Position,
Size,
MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated,
MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated,
MemoryPermission.Mask,
MemoryPermission.ReadAndWrite,
MemoryAttribute.Mask,
MemoryAttribute.None,
MemoryAttribute.IpcAndDeviceMapped,
out MemoryState State,
out _,
out MemoryAttribute Attribute))
{
long PagesCount = Size / PageSize;
Attribute |= MemoryAttribute.Borrowed;
InsertBlock(Position, PagesCount, State, Permission, Attribute);
return 0;
}
}
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
public long ResetTransferMemory(long Position, long Size)
{
lock (Blocks)
{
if (CheckRange(
Position,
Size,
MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated,
MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated,
MemoryPermission.None,
MemoryPermission.None,
MemoryAttribute.Mask,
MemoryAttribute.Borrowed,
MemoryAttribute.IpcAndDeviceMapped,
out MemoryState State,
out _,
out _))
{
long PagesCount = Size / PageSize;
InsertBlock(Position, PagesCount, State, MemoryPermission.ReadAndWrite);
return 0;
}
}
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
public long SetProcessMemoryPermission(long Position, long Size, MemoryPermission Permission)
{
lock (Blocks)
{
if (CheckRange(
Position,
Size,
MemoryState.ProcessPermissionChangeAllowed,
MemoryState.ProcessPermissionChangeAllowed,
MemoryPermission.None,
MemoryPermission.None,
MemoryAttribute.Mask,
MemoryAttribute.None,
MemoryAttribute.IpcAndDeviceMapped,
out MemoryState State,
out _,
out _))
{
if (State == MemoryState.CodeStatic)
{
State = MemoryState.CodeMutable;
}
else if (State == MemoryState.ModCodeStatic)
{
State = MemoryState.ModCodeMutable;
}
else
{
throw new InvalidOperationException();
}
long PagesCount = Size / PageSize;
InsertBlock(Position, PagesCount, State, Permission);
return 0;
}
}
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
public long MapPhysicalMemory(long Position, long Size)
{
long End = Position + Size;
lock (Blocks)
{
long MappedSize = 0;
KMemoryInfo Info;
LinkedListNode<KMemoryBlock> BaseNode = FindBlockNode(Position);
LinkedListNode<KMemoryBlock> Node = BaseNode;
do
{
Info = Node.Value.GetInfo();
if (Info.State != MemoryState.Unmapped)
{
MappedSize += GetSizeInRange(Info, Position, End);
}
Node = Node.Next;
}
while ((ulong)(Info.Position + Info.Size) < (ulong)End && Node != null);
if (MappedSize == Size)
{
return 0;
}
long RemainingSize = Size - MappedSize;
if (!Allocator.TryAllocate(RemainingSize, out long PA))
{
return MakeError(ErrorModule.Kernel, KernelErr.OutOfMemory);
}
Node = BaseNode;
do
{
Info = Node.Value.GetInfo();
if (Info.State == MemoryState.Unmapped)
{
long CurrSize = GetSizeInRange(Info, Position, End);
long MapPosition = Info.Position;
if ((ulong)MapPosition < (ulong)Position)
{
MapPosition = Position;
}
CpuMemory.Map(MapPosition, PA, CurrSize);
PA += CurrSize;
}
Node = Node.Next;
}
while ((ulong)(Info.Position + Info.Size) < (ulong)End && Node != null);
PersonalMmHeapUsage += RemainingSize;
long PagesCount = Size / PageSize;
InsertBlock(
Position,
PagesCount,
MemoryState.Unmapped,
MemoryPermission.None,
MemoryAttribute.None,
MemoryState.Heap,
MemoryPermission.ReadAndWrite,
MemoryAttribute.None);
}
return 0;
}
public long UnmapPhysicalMemory(long Position, long Size)
{
long End = Position + Size;
lock (Blocks)
{
long HeapMappedSize = 0;
long CurrPosition = Position;
KMemoryInfo Info;
LinkedListNode<KMemoryBlock> Node = FindBlockNode(CurrPosition);
do
{
Info = Node.Value.GetInfo();
if (Info.State == MemoryState.Heap)
{
if (Info.Attribute != MemoryAttribute.None)
{
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
HeapMappedSize += GetSizeInRange(Info, Position, End);
}
else if (Info.State != MemoryState.Unmapped)
{
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
Node = Node.Next;
}
while ((ulong)(Info.Position + Info.Size) < (ulong)End && Node != null);
if (HeapMappedSize == 0)
{
return 0;
}
PersonalMmHeapUsage -= HeapMappedSize;
long PagesCount = Size / PageSize;
InsertBlock(Position, PagesCount, MemoryState.Unmapped);
FreePages(Position, PagesCount);
CpuMemory.Unmap(Position, Size);
return 0;
}
}
private long GetSizeInRange(KMemoryInfo Info, long Start, long End)
{
long CurrEnd = Info.Size + Info.Position;
long CurrSize = Info.Size;
if ((ulong)Info.Position < (ulong)Start)
{
CurrSize -= Start - Info.Position;
}
if ((ulong)CurrEnd > (ulong)End)
{
CurrSize -= CurrEnd - End;
}
return CurrSize;
}
private void FreePages(long Position, long PagesCount)
{
for (long Page = 0; Page < PagesCount; Page++)
{
long VA = Position + Page * PageSize;
if (!CpuMemory.IsMapped(VA))
{
continue;
}
long PA = CpuMemory.GetPhysicalAddress(VA);
Allocator.Free(PA, PageSize);
}
}
public bool HleIsUnmapped(long Position, long Size)
{
bool Result = false;
lock (Blocks)
{
Result = IsUnmapped(Position, Size);
}
return Result;
}
private bool IsUnmapped(long Position, long Size)
{
return CheckRange(
Position,
Size,
MemoryState.Mask,
MemoryState.Unmapped,
MemoryPermission.Mask,
MemoryPermission.None,
MemoryAttribute.Mask,
MemoryAttribute.None,
MemoryAttribute.IpcAndDeviceMapped,
out _,
out _,
out _);
}
private bool CheckRange(
long Position,
long Size,
MemoryState StateMask,
MemoryState StateExpected,
MemoryPermission PermissionMask,
MemoryPermission PermissionExpected,
MemoryAttribute AttributeMask,
MemoryAttribute AttributeExpected,
MemoryAttribute AttributeIgnoreMask,
out MemoryState OutState,
out MemoryPermission OutPermission,
out MemoryAttribute OutAttribute)
{
KMemoryInfo BlkInfo = FindBlock(Position).GetInfo();
ulong Start = (ulong)Position;
ulong End = (ulong)Size + Start;
if (End <= (ulong)(BlkInfo.Position + BlkInfo.Size))
{
if ((BlkInfo.Attribute & AttributeMask) == AttributeExpected &&
(BlkInfo.State & StateMask) == StateExpected &&
(BlkInfo.Permission & PermissionMask) == PermissionExpected)
{
OutState = BlkInfo.State;
OutPermission = BlkInfo.Permission;
OutAttribute = BlkInfo.Attribute & ~AttributeIgnoreMask;
return true;
}
}
OutState = MemoryState.Unmapped;
OutPermission = MemoryPermission.None;
OutAttribute = MemoryAttribute.None;
return false;
}
private void InsertBlock(
long BasePosition,
long PagesCount,
MemoryState OldState,
MemoryPermission OldPermission,
MemoryAttribute OldAttribute,
MemoryState NewState,
MemoryPermission NewPermission,
MemoryAttribute NewAttribute)
{
//Insert new block on the list only on areas where the state
//of the block matches the state specified on the Old* state
//arguments, otherwise leave it as is.
OldAttribute |= MemoryAttribute.IpcAndDeviceMapped;
ulong Start = (ulong)BasePosition;
ulong End = (ulong)PagesCount * PageSize + Start;
LinkedListNode<KMemoryBlock> Node = Blocks.First;
while (Node != null)
{
LinkedListNode<KMemoryBlock> NewNode = Node;
LinkedListNode<KMemoryBlock> NextNode = Node.Next;
KMemoryBlock CurrBlock = Node.Value;
ulong CurrStart = (ulong)CurrBlock.BasePosition;
ulong CurrEnd = (ulong)CurrBlock.PagesCount * PageSize + CurrStart;
if (Start < CurrEnd && CurrStart < End)
{
MemoryAttribute CurrBlockAttr = CurrBlock.Attribute | MemoryAttribute.IpcAndDeviceMapped;
if (CurrBlock.State != OldState ||
CurrBlock.Permission != OldPermission ||
CurrBlockAttr != OldAttribute)
{
Node = NextNode;
continue;
}
if (CurrStart >= Start && CurrEnd <= End)
{
CurrBlock.State = NewState;
CurrBlock.Permission = NewPermission;
CurrBlock.Attribute &= ~MemoryAttribute.IpcAndDeviceMapped;
CurrBlock.Attribute |= NewAttribute;
}
else if (CurrStart >= Start)
{
CurrBlock.BasePosition = (long)End;
CurrBlock.PagesCount = (long)((CurrEnd - End) / PageSize);
long NewPagesCount = (long)((End - CurrStart) / PageSize);
NewNode = Blocks.AddBefore(Node, new KMemoryBlock(
(long)CurrStart,
NewPagesCount,
NewState,
NewPermission,
NewAttribute));
}
else if (CurrEnd <= End)
{
CurrBlock.PagesCount = (long)((Start - CurrStart) / PageSize);
long NewPagesCount = (long)((CurrEnd - Start) / PageSize);
NewNode = Blocks.AddAfter(Node, new KMemoryBlock(
BasePosition,
NewPagesCount,
NewState,
NewPermission,
NewAttribute));
}
else
{
CurrBlock.PagesCount = (long)((Start - CurrStart) / PageSize);
long NextPagesCount = (long)((CurrEnd - End) / PageSize);
NewNode = Blocks.AddAfter(Node, new KMemoryBlock(
BasePosition,
PagesCount,
NewState,
NewPermission,
NewAttribute));
Blocks.AddAfter(NewNode, new KMemoryBlock(
(long)End,
NextPagesCount,
CurrBlock.State,
CurrBlock.Permission,
CurrBlock.Attribute));
NextNode = null;
}
MergeEqualStateNeighbours(NewNode);
}
Node = NextNode;
}
}
private void InsertBlock(
long BasePosition,
long PagesCount,
MemoryState State,
MemoryPermission Permission = MemoryPermission.None,
MemoryAttribute Attribute = MemoryAttribute.None)
{
//Inserts new block at the list, replacing and spliting
//existing blocks as needed.
KMemoryBlock Block = new KMemoryBlock(BasePosition, PagesCount, State, Permission, Attribute);
ulong Start = (ulong)BasePosition;
ulong End = (ulong)PagesCount * PageSize + Start;
LinkedListNode<KMemoryBlock> NewNode = null;
LinkedListNode<KMemoryBlock> Node = Blocks.First;
while (Node != null)
{
KMemoryBlock CurrBlock = Node.Value;
LinkedListNode<KMemoryBlock> NextNode = Node.Next;
ulong CurrStart = (ulong)CurrBlock.BasePosition;
ulong CurrEnd = (ulong)CurrBlock.PagesCount * PageSize + CurrStart;
if (Start < CurrEnd && CurrStart < End)
{
if (Start >= CurrStart && End <= CurrEnd)
{
Block.Attribute |= CurrBlock.Attribute & MemoryAttribute.IpcAndDeviceMapped;
}
if (Start > CurrStart && End < CurrEnd)
{
CurrBlock.PagesCount = (long)((Start - CurrStart) / PageSize);
long NextPagesCount = (long)((CurrEnd - End) / PageSize);
NewNode = Blocks.AddAfter(Node, Block);
Blocks.AddAfter(NewNode, new KMemoryBlock(
(long)End,
NextPagesCount,
CurrBlock.State,
CurrBlock.Permission,
CurrBlock.Attribute));
break;
}
else if (Start <= CurrStart && End < CurrEnd)
{
CurrBlock.BasePosition = (long)End;
CurrBlock.PagesCount = (long)((CurrEnd - End) / PageSize);
if (NewNode == null)
{
NewNode = Blocks.AddBefore(Node, Block);
}
}
else if (Start > CurrStart && End >= CurrEnd)
{
CurrBlock.PagesCount = (long)((Start - CurrStart) / PageSize);
if (NewNode == null)
{
NewNode = Blocks.AddAfter(Node, Block);
}
}
else
{
if (NewNode == null)
{
NewNode = Blocks.AddBefore(Node, Block);
}
Blocks.Remove(Node);
}
}
Node = NextNode;
}
if (NewNode == null)
{
NewNode = Blocks.AddFirst(Block);
}
MergeEqualStateNeighbours(NewNode);
}
private void MergeEqualStateNeighbours(LinkedListNode<KMemoryBlock> Node)
{
KMemoryBlock Block = Node.Value;
ulong Start = (ulong)Block.BasePosition;
ulong End = (ulong)Block.PagesCount * PageSize + Start;
if (Node.Previous != null)
{
KMemoryBlock Previous = Node.Previous.Value;
if (BlockStateEquals(Block, Previous))
{
Blocks.Remove(Node.Previous);
Block.BasePosition = Previous.BasePosition;
Start = (ulong)Block.BasePosition;
}
}
if (Node.Next != null)
{
KMemoryBlock Next = Node.Next.Value;
if (BlockStateEquals(Block, Next))
{
Blocks.Remove(Node.Next);
End = (ulong)(Next.BasePosition + Next.PagesCount * PageSize);
}
}
Block.PagesCount = (long)((End - Start) / PageSize);
}
private static bool BlockStateEquals(KMemoryBlock LHS, KMemoryBlock RHS)
{
return LHS.State == RHS.State &&
LHS.Permission == RHS.Permission &&
LHS.Attribute == RHS.Attribute &&
LHS.DeviceRefCount == RHS.DeviceRefCount &&
LHS.IpcRefCount == RHS.IpcRefCount;
}
private KMemoryBlock FindBlock(long Position)
{
return FindBlockNode(Position)?.Value;
}
private LinkedListNode<KMemoryBlock> FindBlockNode(long Position)
{
ulong Addr = (ulong)Position;
lock (Blocks)
{
LinkedListNode<KMemoryBlock> Node = Blocks.First;
while (Node != null)
{
KMemoryBlock Block = Node.Value;
ulong Start = (ulong)Block.BasePosition;
ulong End = (ulong)Block.PagesCount * PageSize + Start;
if (Start <= Addr && End - 1 >= Addr)
{
return Node;
}
Node = Node.Next;
}
}
return null;
}
}
}