Ryujinx/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs
gdkchan 056c2840b1
Fix MapPhysicalMemory when block position is less than map position (#359)
* Fix MapPhysicalMemory when block position is less than map position

* Only try to free pages that are actually mapped

* Only unmap after freeing the memory
2018-08-17 10:39:35 -03:00

1093 lines
No EOL
35 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 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);
}
}
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;
}
}
}