mirror of
https://github.com/ryujinx-mirror/ryujinx.git
synced 2024-11-30 10:54:28 +01:00
2cb8541462
* Print stack trace on invalid memory accesses * Rebased, change code region base address for 39-bits address space, print stack trace on break and undefined instructions too
1191 lines
38 KiB
C#
1191 lines
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 = 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<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;
|
|
}
|
|
}
|
|
} |