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 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 = 0x8000000; 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(); 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 BaseNode = FindBlockNode(Position); LinkedListNode 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 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 Node = Blocks.First; while (Node != null) { LinkedListNode NewNode = Node; LinkedListNode 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 NewNode = null; LinkedListNode Node = Blocks.First; while (Node != null) { KMemoryBlock CurrBlock = Node.Value; LinkedListNode 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 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 FindBlockNode(long Position) { ulong Addr = (ulong)Position; lock (Blocks) { LinkedListNode 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; } } }