aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.HLE/OsHle/Handles
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2018-08-15 15:59:51 -0300
committerGitHub <noreply@github.com>2018-08-15 15:59:51 -0300
commitc393cdf8e3775bc95850e4d8c8e4c446b286d3b4 (patch)
tree25035a244741d2daf3f7d6be8b23153ff061ea15 /Ryujinx.HLE/OsHle/Handles
parent76d95dee05e3c51c18e1799f54cc407e0f633b4e (diff)
More flexible memory manager (#307)
* Keep track mapped buffers with fixed offsets * Started rewriting the memory manager * Initial support for MapPhysicalMemory and UnmapPhysicalMemory, other tweaks * MapPhysicalMemory/UnmapPhysicalMemory support, other tweaks * Rebased * Optimize the map/unmap physical memory svcs * Integrate shared font support * Fix address space reserve alignment * Some fixes related to gpu memory mapping * Some cleanup * Only try uploading const buffers that are really used * Check if memory region is contiguous * Rebased * Add missing count increment on IsRegionModified * Check for reads/writes outside of the address space, optimize translation with a tail call
Diffstat (limited to 'Ryujinx.HLE/OsHle/Handles')
-rw-r--r--Ryujinx.HLE/OsHle/Handles/AddressSpaceType.cs10
-rw-r--r--Ryujinx.HLE/OsHle/Handles/HSharedMem.cs44
-rw-r--r--Ryujinx.HLE/OsHle/Handles/HTransferMem.cs21
-rw-r--r--Ryujinx.HLE/OsHle/Handles/KMemoryBlock.cs43
-rw-r--r--Ryujinx.HLE/OsHle/Handles/KMemoryInfo.cs33
-rw-r--r--Ryujinx.HLE/OsHle/Handles/KMemoryManager.cs1082
-rw-r--r--Ryujinx.HLE/OsHle/Handles/KSharedMemory.cs14
-rw-r--r--Ryujinx.HLE/OsHle/Handles/KTlsPageManager.cs60
-rw-r--r--Ryujinx.HLE/OsHle/Handles/KTransferMemory.cs14
-rw-r--r--Ryujinx.HLE/OsHle/Handles/MemoryAttribute.cs22
-rw-r--r--Ryujinx.HLE/OsHle/Handles/MemoryPermission.cs18
-rw-r--r--Ryujinx.HLE/OsHle/Handles/MemoryState.cs49
12 files changed, 1345 insertions, 65 deletions
diff --git a/Ryujinx.HLE/OsHle/Handles/AddressSpaceType.cs b/Ryujinx.HLE/OsHle/Handles/AddressSpaceType.cs
new file mode 100644
index 00000000..946c51e1
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Handles/AddressSpaceType.cs
@@ -0,0 +1,10 @@
+namespace Ryujinx.HLE.OsHle.Handles
+{
+ enum AddressSpaceType
+ {
+ Addr32Bits = 0,
+ Addr36Bits = 1,
+ Addr36BitsNoMap = 2,
+ Addr39Bits = 3
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/OsHle/Handles/HSharedMem.cs b/Ryujinx.HLE/OsHle/Handles/HSharedMem.cs
deleted file mode 100644
index cd3d8223..00000000
--- a/Ryujinx.HLE/OsHle/Handles/HSharedMem.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using ChocolArm64.Memory;
-using System;
-using System.Collections.Generic;
-
-namespace Ryujinx.HLE.OsHle.Handles
-{
- class HSharedMem
- {
- private List<(AMemory, long, long)> Positions;
-
- public EventHandler<EventArgs> MemoryMapped;
- public EventHandler<EventArgs> MemoryUnmapped;
-
- public HSharedMem()
- {
- Positions = new List<(AMemory, long, long)>();
- }
-
- public void AddVirtualPosition(AMemory Memory, long Position, long Size)
- {
- lock (Positions)
- {
- Positions.Add((Memory, Position, Size));
-
- MemoryMapped?.Invoke(this, EventArgs.Empty);
- }
- }
-
- public void RemoveVirtualPosition(AMemory Memory, long Position, long Size)
- {
- lock (Positions)
- {
- Positions.Remove((Memory, Position, Size));
-
- MemoryUnmapped?.Invoke(this, EventArgs.Empty);
- }
- }
-
- public (AMemory, long, long)[] GetVirtualPositions()
- {
- return Positions.ToArray();
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/OsHle/Handles/HTransferMem.cs b/Ryujinx.HLE/OsHle/Handles/HTransferMem.cs
deleted file mode 100644
index 2969a2ba..00000000
--- a/Ryujinx.HLE/OsHle/Handles/HTransferMem.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using ChocolArm64.Memory;
-
-namespace Ryujinx.HLE.OsHle.Handles
-{
- class HTransferMem
- {
- public AMemory Memory { get; private set; }
- public AMemoryPerm Perm { get; private set; }
-
- public long Position { get; private set; }
- public long Size { get; private set; }
-
- public HTransferMem(AMemory Memory, AMemoryPerm Perm, long Position, long Size)
- {
- this.Memory = Memory;
- this.Perm = Perm;
- this.Position = Position;
- this.Size = Size;
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/OsHle/Handles/KMemoryBlock.cs b/Ryujinx.HLE/OsHle/Handles/KMemoryBlock.cs
new file mode 100644
index 00000000..e33b6cb9
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Handles/KMemoryBlock.cs
@@ -0,0 +1,43 @@
+namespace Ryujinx.HLE.OsHle.Handles
+{
+ class KMemoryBlock
+ {
+ public long BasePosition { get; set; }
+ public long PagesCount { get; set; }
+
+ public MemoryState State { get; set; }
+ public MemoryPermission Permission { get; set; }
+ public MemoryAttribute Attribute { get; set; }
+
+ public int IpcRefCount { get; set; }
+ public int DeviceRefCount { get; set; }
+
+ public KMemoryBlock(
+ long BasePosition,
+ long PagesCount,
+ MemoryState State,
+ MemoryPermission Permission,
+ MemoryAttribute Attribute)
+ {
+ this.BasePosition = BasePosition;
+ this.PagesCount = PagesCount;
+ this.State = State;
+ this.Attribute = Attribute;
+ this.Permission = Permission;
+ }
+
+ public KMemoryInfo GetInfo()
+ {
+ long Size = PagesCount * KMemoryManager.PageSize;
+
+ return new KMemoryInfo(
+ BasePosition,
+ Size,
+ State,
+ Permission,
+ Attribute,
+ IpcRefCount,
+ DeviceRefCount);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/OsHle/Handles/KMemoryInfo.cs b/Ryujinx.HLE/OsHle/Handles/KMemoryInfo.cs
new file mode 100644
index 00000000..3f79f000
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Handles/KMemoryInfo.cs
@@ -0,0 +1,33 @@
+namespace Ryujinx.HLE.OsHle.Handles
+{
+ class KMemoryInfo
+ {
+ public long Position { get; private set; }
+ public long Size { get; private set; }
+
+ public MemoryState State { get; private set; }
+ public MemoryPermission Permission { get; private set; }
+ public MemoryAttribute Attribute { get; private set; }
+
+ public int IpcRefCount { get; private set; }
+ public int DeviceRefCount { get; private set; }
+
+ public KMemoryInfo(
+ long Position,
+ long Size,
+ MemoryState State,
+ MemoryPermission Permission,
+ MemoryAttribute Attribute,
+ int IpcRefCount,
+ int DeviceRefCount)
+ {
+ this.Position = Position;
+ this.Size = Size;
+ this.State = State;
+ this.Attribute = Attribute;
+ this.Permission = Permission;
+ this.IpcRefCount = IpcRefCount;
+ this.DeviceRefCount = DeviceRefCount;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/OsHle/Handles/KMemoryManager.cs b/Ryujinx.HLE/OsHle/Handles/KMemoryManager.cs
new file mode 100644
index 00000000..76c5a53d
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Handles/KMemoryManager.cs
@@ -0,0 +1,1082 @@
+using ChocolArm64.Memory;
+using Ryujinx.HLE.Memory;
+using Ryujinx.HLE.OsHle.Kernel;
+using System;
+using System.Collections.Generic;
+
+using static Ryujinx.HLE.OsHle.ErrorCode;
+
+namespace Ryujinx.HLE.OsHle.Handles
+{
+ 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.Ns.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);
+
+ CpuMemory.Unmap(FreeAddr, DiffSize);
+
+ FreePages(FreeAddr, PagesCount);
+ }
+ }
+ }
+
+ 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);
+
+ CpuMemory.Map(Info.Position, 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);
+
+ CpuMemory.Unmap(Position, Size);
+
+ FreePages(Position, PagesCount);
+
+ 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;
+
+ 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;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/OsHle/Handles/KSharedMemory.cs b/Ryujinx.HLE/OsHle/Handles/KSharedMemory.cs
new file mode 100644
index 00000000..5c410474
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Handles/KSharedMemory.cs
@@ -0,0 +1,14 @@
+namespace Ryujinx.HLE.OsHle.Handles
+{
+ class KSharedMemory
+ {
+ public long PA { get; private set; }
+ public long Size { get; private set; }
+
+ public KSharedMemory(long PA, long Size)
+ {
+ this.PA = PA;
+ this.Size = Size;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/OsHle/Handles/KTlsPageManager.cs b/Ryujinx.HLE/OsHle/Handles/KTlsPageManager.cs
new file mode 100644
index 00000000..f116f548
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Handles/KTlsPageManager.cs
@@ -0,0 +1,60 @@
+using System;
+
+namespace Ryujinx.HLE.OsHle.Handles
+{
+ class KTlsPageManager
+ {
+ private const int TlsEntrySize = 0x200;
+
+ private long PagePosition;
+
+ private int UsedSlots;
+
+ private bool[] Slots;
+
+ public bool IsEmpty => UsedSlots == 0;
+ public bool IsFull => UsedSlots == Slots.Length;
+
+ public KTlsPageManager(long PagePosition)
+ {
+ this.PagePosition = PagePosition;
+
+ Slots = new bool[KMemoryManager.PageSize / TlsEntrySize];
+ }
+
+ public bool TryGetFreeTlsAddr(out long Position)
+ {
+ Position = PagePosition;
+
+ for (int Index = 0; Index < Slots.Length; Index++)
+ {
+ if (!Slots[Index])
+ {
+ Slots[Index] = true;
+
+ UsedSlots++;
+
+ return true;
+ }
+
+ Position += TlsEntrySize;
+ }
+
+ Position = 0;
+
+ return false;
+ }
+
+ public void FreeTlsSlot(int Slot)
+ {
+ if ((uint)Slot > Slots.Length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(Slot));
+ }
+
+ Slots[Slot] = false;
+
+ UsedSlots--;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/OsHle/Handles/KTransferMemory.cs b/Ryujinx.HLE/OsHle/Handles/KTransferMemory.cs
new file mode 100644
index 00000000..5f9c0409
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Handles/KTransferMemory.cs
@@ -0,0 +1,14 @@
+namespace Ryujinx.HLE.OsHle.Handles
+{
+ class KTransferMemory
+ {
+ public long Position { get; private set; }
+ public long Size { get; private set; }
+
+ public KTransferMemory(long Position, long Size)
+ {
+ this.Position = Position;
+ this.Size = Size;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/OsHle/Handles/MemoryAttribute.cs b/Ryujinx.HLE/OsHle/Handles/MemoryAttribute.cs
new file mode 100644
index 00000000..e234d7f0
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Handles/MemoryAttribute.cs
@@ -0,0 +1,22 @@
+using System;
+
+namespace Ryujinx.HLE.OsHle.Handles
+{
+ [Flags]
+ enum MemoryAttribute : byte
+ {
+ None = 0,
+ Mask = 0xff,
+
+ Borrowed = 1 << 0,
+ IpcMapped = 1 << 1,
+ DeviceMapped = 1 << 2,
+ Uncached = 1 << 3,
+
+ IpcAndDeviceMapped = IpcMapped | DeviceMapped,
+
+ BorrowedAndIpcMapped = Borrowed | IpcMapped,
+
+ DeviceMappedAndUncached = DeviceMapped | Uncached
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/OsHle/Handles/MemoryPermission.cs b/Ryujinx.HLE/OsHle/Handles/MemoryPermission.cs
new file mode 100644
index 00000000..416f171e
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Handles/MemoryPermission.cs
@@ -0,0 +1,18 @@
+using System;
+
+namespace Ryujinx.HLE.OsHle.Handles
+{
+ [Flags]
+ enum MemoryPermission : byte
+ {
+ None = 0,
+ Mask = 0xff,
+
+ Read = 1 << 0,
+ Write = 1 << 1,
+ Execute = 1 << 2,
+
+ ReadAndWrite = Read | Write,
+ ReadAndExecute = Read | Execute
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/OsHle/Handles/MemoryState.cs b/Ryujinx.HLE/OsHle/Handles/MemoryState.cs
new file mode 100644
index 00000000..5437d6a3
--- /dev/null
+++ b/Ryujinx.HLE/OsHle/Handles/MemoryState.cs
@@ -0,0 +1,49 @@
+using System;
+
+namespace Ryujinx.HLE.OsHle
+{
+ [Flags]
+ enum MemoryState : uint
+ {
+ Unmapped = 0x00000000,
+ Io = 0x00002001,
+ Normal = 0x00042002,
+ CodeStatic = 0x00DC7E03,
+ CodeMutable = 0x03FEBD04,
+ Heap = 0x037EBD05,
+ SharedMemory = 0x00402006,
+ ModCodeStatic = 0x00DD7E08,
+ ModCodeMutable = 0x03FFBD09,
+ IpcBuffer0 = 0x005C3C0A,
+ MappedMemory = 0x005C3C0B,
+ ThreadLocal = 0x0040200C,
+ TransferMemoryIsolated = 0x015C3C0D,
+ TransferMemory = 0x005C380E,
+ ProcessMemory = 0x0040380F,
+ Reserved = 0x00000010,
+ IpcBuffer1 = 0x005C3811,
+ IpcBuffer3 = 0x004C2812,
+ KernelStack = 0x00002013,
+ CodeReadOnly = 0x00402214,
+ CodeWritable = 0x00402015,
+ Mask = 0xffffffff,
+
+ PermissionChangeAllowed = 1 << 8,
+ ForceReadWritableByDebugSyscalls = 1 << 9,
+ IpcSendAllowedType0 = 1 << 10,
+ IpcSendAllowedType3 = 1 << 11,
+ IpcSendAllowedType1 = 1 << 12,
+ ProcessPermissionChangeAllowed = 1 << 14,
+ MapAllowed = 1 << 15,
+ UnmapProcessCodeMemoryAllowed = 1 << 16,
+ TransferMemoryAllowed = 1 << 17,
+ QueryPhysicalAddressAllowed = 1 << 18,
+ MapDeviceAllowed = 1 << 19,
+ MapDeviceAlignedAllowed = 1 << 20,
+ IpcBufferAllowed = 1 << 21,
+ IsPoolAllocated = 1 << 22,
+ MapProcessAllowed = 1 << 23,
+ AttributeChangeAllowed = 1 << 24,
+ CodeMemoryAllowed = 1 << 25
+ }
+} \ No newline at end of file