Explorar el Código

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
gdkchan hace 7 años
padre
commit
c393cdf8e3
Se han modificado 64 ficheros con 3196 adiciones y 1759 borrados
  1. 0 2
      ChocolArm64/AOptimizations.cs
  2. 0 14
      ChocolArm64/Exceptions/VmmAccessViolationException.cs
  3. 4 4
      ChocolArm64/Exceptions/VmmOutOfMemoryException.cs
  4. 18 68
      ChocolArm64/Instruction/AInstEmitMemoryHelper.cs
  5. 277 311
      ChocolArm64/Memory/AMemory.cs
  6. 4 20
      ChocolArm64/Memory/AMemoryHelper.cs
  7. 0 21
      ChocolArm64/Memory/AMemoryMapInfo.cs
  8. 0 258
      ChocolArm64/Memory/AMemoryMgr.cs
  9. 0 15
      ChocolArm64/Memory/AMemoryPerm.cs
  10. 0 92
      ChocolArm64/Memory/AMemoryWin32.cs
  11. 1 0
      Ryujinx.Graphics/Gal/IGalShader.cs
  12. 1 1
      Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs
  13. 13 3
      Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs
  14. 11 10
      Ryujinx.Graphics/Gal/OpenGL/OGLShaderProgram.cs
  15. 0 177
      Ryujinx.HLE/Font/SharedFontManager.cs
  16. 24 11
      Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs
  17. 5 42
      Ryujinx.HLE/Gpu/Memory/NvGpuVmm.cs
  18. 6 8
      Ryujinx.HLE/Gpu/Memory/NvGpuVmmCache.cs
  19. 11 11
      Ryujinx.HLE/Gpu/Texture/TextureReader.cs
  20. 1 1
      Ryujinx.HLE/Gpu/Texture/TextureWriter.cs
  21. 74 130
      Ryujinx.HLE/Hid/Hid.cs
  22. 30 37
      Ryujinx.HLE/Loaders/Executable.cs
  23. 112 0
      Ryujinx.HLE/Memory/ArenaAllocator.cs
  24. 130 0
      Ryujinx.HLE/Memory/DeviceMemory.cs
  25. 122 0
      Ryujinx.HLE/OsHle/Font/SharedFontManager.cs
  26. 3 2
      Ryujinx.HLE/OsHle/Font/SharedFontType.cs
  27. 10 0
      Ryujinx.HLE/OsHle/Handles/AddressSpaceType.cs
  28. 0 44
      Ryujinx.HLE/OsHle/Handles/HSharedMem.cs
  29. 0 21
      Ryujinx.HLE/OsHle/Handles/HTransferMem.cs
  30. 43 0
      Ryujinx.HLE/OsHle/Handles/KMemoryBlock.cs
  31. 33 0
      Ryujinx.HLE/OsHle/Handles/KMemoryInfo.cs
  32. 1082 0
      Ryujinx.HLE/OsHle/Handles/KMemoryManager.cs
  33. 14 0
      Ryujinx.HLE/OsHle/Handles/KSharedMemory.cs
  34. 60 0
      Ryujinx.HLE/OsHle/Handles/KTlsPageManager.cs
  35. 14 0
      Ryujinx.HLE/OsHle/Handles/KTransferMemory.cs
  36. 22 0
      Ryujinx.HLE/OsHle/Handles/MemoryAttribute.cs
  37. 18 0
      Ryujinx.HLE/OsHle/Handles/MemoryPermission.cs
  38. 49 0
      Ryujinx.HLE/OsHle/Handles/MemoryState.cs
  39. 8 8
      Ryujinx.HLE/OsHle/Homebrew.cs
  40. 36 25
      Ryujinx.HLE/OsHle/Horizon.cs
  41. 16 13
      Ryujinx.HLE/OsHle/Kernel/KernelErr.cs
  42. 1 28
      Ryujinx.HLE/OsHle/Kernel/SvcHandler.cs
  43. 410 117
      Ryujinx.HLE/OsHle/Kernel/SvcMemory.cs
  44. 23 18
      Ryujinx.HLE/OsHle/Kernel/SvcSystem.cs
  45. 2 2
      Ryujinx.HLE/OsHle/Kernel/SvcThread.cs
  46. 8 8
      Ryujinx.HLE/OsHle/Kernel/SvcThreadSync.cs
  47. 0 12
      Ryujinx.HLE/OsHle/MemoryAllocator.cs
  48. 0 29
      Ryujinx.HLE/OsHle/MemoryRegions.cs
  49. 71 64
      Ryujinx.HLE/OsHle/Process.cs
  50. 2 2
      Ryujinx.HLE/OsHle/Services/Hid/IAppletResource.cs
  51. 198 0
      Ryujinx.HLE/OsHle/Services/Nv/NvGpuAS/NvGpuASCtx.cs
  52. 148 62
      Ryujinx.HLE/OsHle/Services/Nv/NvGpuAS/NvGpuASIoctl.cs
  53. 2 2
      Ryujinx.HLE/OsHle/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs
  54. 1 1
      Ryujinx.HLE/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs
  55. 1 1
      Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapFree.cs
  56. 1 1
      Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapHandle.cs
  57. 8 8
      Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapIoctl.cs
  58. 43 35
      Ryujinx.HLE/OsHle/Services/Pl/ISharedFontManager.cs
  59. 1 1
      Ryujinx.HLE/OsHle/Services/Vi/NvFlinger.cs
  60. 10 0
      Ryujinx.HLE/OsHle/Utilities/IntUtils.cs
  61. 0 1
      Ryujinx.HLE/PerformanceStatistics.cs
  62. 6 12
      Ryujinx.HLE/Switch.cs
  63. 8 4
      Ryujinx.Tests/Cpu/CpuTest.cs
  64. 0 2
      Ryujinx/Config.cs

+ 0 - 2
ChocolArm64/AOptimizations.cs

@@ -2,8 +2,6 @@ using System.Runtime.Intrinsics.X86;
 
 public static class AOptimizations
 {
-    public static bool DisableMemoryChecks = false;
-
     public static bool GenerateCallStack = true;
 
     private static bool UseAllSseIfAvailable = true;

+ 0 - 14
ChocolArm64/Exceptions/VmmAccessViolationException.cs

@@ -1,14 +0,0 @@
-using ChocolArm64.Memory;
-using System;
-
-namespace ChocolArm64.Exceptions
-{
-    public class VmmAccessViolationException : Exception
-    {
-        private const string ExMsg = "Address 0x{0:x16} does not have \"{1}\" permission!";
-
-        public VmmAccessViolationException() { }
-
-        public VmmAccessViolationException(long Position, AMemoryPerm Perm) : base(string.Format(ExMsg, Position, Perm)) { }
-    }
-}

+ 4 - 4
ChocolArm64/Exceptions/VmmOutOfMemoryException.cs

@@ -2,12 +2,12 @@ using System;
 
 namespace ChocolArm64.Exceptions
 {
-    public class VmmOutOfMemoryException : Exception
+    public class VmmAccessException : Exception
     {
-        private const string ExMsg = "Failed to allocate {0} bytes of memory!";
+        private const string ExMsg = "Memory region at 0x{0} with size 0x{1} is not contiguous!";
 
-        public VmmOutOfMemoryException() { }
+        public VmmAccessException() { }
 
-        public VmmOutOfMemoryException(long Size) : base(string.Format(ExMsg, Size)) { }
+        public VmmAccessException(long Position, long Size) : base(string.Format(ExMsg, Position, Size)) { }
     }
 }

+ 18 - 68
ChocolArm64/Instruction/AInstEmitMemoryHelper.cs

@@ -45,46 +45,21 @@ namespace ChocolArm64.Instruction
             {
                 switch (Size)
                 {
-                    case 0: Name = AOptimizations.DisableMemoryChecks
-                        ? nameof(AMemory.ReadVector8Unchecked)
-                        : nameof(AMemory.ReadVector8); break;
-
-                    case 1: Name = AOptimizations.DisableMemoryChecks
-                        ? nameof(AMemory.ReadVector16Unchecked)
-                        : nameof(AMemory.ReadVector16); break;
-
-                    case 2: Name = AOptimizations.DisableMemoryChecks
-                        ? nameof(AMemory.ReadVector32Unchecked)
-                        : nameof(AMemory.ReadVector32); break;
-
-                    case 3: Name = AOptimizations.DisableMemoryChecks
-                        ? nameof(AMemory.ReadVector64Unchecked)
-                        : nameof(AMemory.ReadVector64); break;
-
-                    case 4: Name = AOptimizations.DisableMemoryChecks
-                        ? nameof(AMemory.ReadVector128Unchecked)
-                        : nameof(AMemory.ReadVector128); break;
+                    case 0: Name = nameof(AMemory.ReadVector8);   break;
+                    case 1: Name = nameof(AMemory.ReadVector16);  break;
+                    case 2: Name = nameof(AMemory.ReadVector32);  break;
+                    case 3: Name = nameof(AMemory.ReadVector64);  break;
+                    case 4: Name = nameof(AMemory.ReadVector128); break;
                 }
             }
             else
             {
                 switch (Size)
                 {
-                    case 0: Name = AOptimizations.DisableMemoryChecks
-                        ? nameof(AMemory.ReadByteUnchecked)
-                        : nameof(AMemory.ReadByte); break;
-
-                    case 1: Name = AOptimizations.DisableMemoryChecks
-                        ? nameof(AMemory.ReadUInt16Unchecked)
-                        : nameof(AMemory.ReadUInt16); break;
-
-                    case 2: Name = AOptimizations.DisableMemoryChecks
-                        ? nameof(AMemory.ReadUInt32Unchecked)
-                        : nameof(AMemory.ReadUInt32); break;
-
-                    case 3: Name = AOptimizations.DisableMemoryChecks
-                        ? nameof(AMemory.ReadUInt64Unchecked)
-                        : nameof(AMemory.ReadUInt64); break;
+                    case 0: Name = nameof(AMemory.ReadByte);   break;
+                    case 1: Name = nameof(AMemory.ReadUInt16); break;
+                    case 2: Name = nameof(AMemory.ReadUInt32); break;
+                    case 3: Name = nameof(AMemory.ReadUInt64); break;
                 }
             }
 
@@ -132,46 +107,21 @@ namespace ChocolArm64.Instruction
             {
                 switch (Size)
                 {
-                    case 0: Name = AOptimizations.DisableMemoryChecks
-                        ? nameof(AMemory.WriteVector8Unchecked)
-                        : nameof(AMemory.WriteVector8); break;
-
-                    case 1: Name = AOptimizations.DisableMemoryChecks
-                        ? nameof(AMemory.WriteVector16Unchecked)
-                        : nameof(AMemory.WriteVector16); break;
-
-                    case 2: Name = AOptimizations.DisableMemoryChecks
-                        ? nameof(AMemory.WriteVector32Unchecked)
-                        : nameof(AMemory.WriteVector32); break;
-
-                    case 3: Name = AOptimizations.DisableMemoryChecks
-                        ? nameof(AMemory.WriteVector64Unchecked)
-                        : nameof(AMemory.WriteVector64); break;
-
-                    case 4: Name = AOptimizations.DisableMemoryChecks
-                        ? nameof(AMemory.WriteVector128Unchecked)
-                        : nameof(AMemory.WriteVector128); break;
+                    case 0: Name = nameof(AMemory.WriteVector8);   break;
+                    case 1: Name = nameof(AMemory.WriteVector16);  break;
+                    case 2: Name = nameof(AMemory.WriteVector32);  break;
+                    case 3: Name = nameof(AMemory.WriteVector64);  break;
+                    case 4: Name = nameof(AMemory.WriteVector128); break;
                 }
             }
             else
             {
                 switch (Size)
                 {
-                    case 0: Name = AOptimizations.DisableMemoryChecks
-                        ? nameof(AMemory.WriteByteUnchecked)
-                        : nameof(AMemory.WriteByte); break;
-
-                    case 1: Name = AOptimizations.DisableMemoryChecks
-                        ? nameof(AMemory.WriteUInt16Unchecked)
-                        : nameof(AMemory.WriteUInt16); break;
-
-                    case 2: Name = AOptimizations.DisableMemoryChecks
-                        ? nameof(AMemory.WriteUInt32Unchecked)
-                        : nameof(AMemory.WriteUInt32); break;
-
-                    case 3: Name = AOptimizations.DisableMemoryChecks
-                        ? nameof(AMemory.WriteUInt64Unchecked)
-                        : nameof(AMemory.WriteUInt64); break;
+                    case 0: Name = nameof(AMemory.WriteByte);   break;
+                    case 1: Name = nameof(AMemory.WriteUInt16); break;
+                    case 2: Name = nameof(AMemory.WriteUInt32); break;
+                    case 3: Name = nameof(AMemory.WriteUInt64); break;
                 }
             }
 

+ 277 - 311
ChocolArm64/Memory/AMemory.cs

@@ -1,6 +1,7 @@
 using ChocolArm64.Exceptions;
 using ChocolArm64.State;
 using System;
+using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
@@ -12,9 +13,22 @@ namespace ChocolArm64.Memory
 {
     public unsafe class AMemory : IAMemory, IDisposable
     {
-        private const long ErgMask = (4 << AThreadState.ErgSizeLog2) - 1;
+        private const int PTLvl0Bits = 13;
+        private const int PTLvl1Bits = 14;
+        private const int PTPageBits = 12;
+
+        private const int PTLvl0Size = 1 << PTLvl0Bits;
+        private const int PTLvl1Size = 1 << PTLvl1Bits;
+        public  const int PageSize   = 1 << PTPageBits;
+
+        private const int PTLvl0Mask = PTLvl0Size - 1;
+        private const int PTLvl1Mask = PTLvl1Size - 1;
+        public  const int PageMask   = PageSize   - 1;
 
-        public AMemoryMgr Manager { get; private set; }
+        private const int PTLvl0Bit = PTPageBits + PTLvl1Bits;
+        private const int PTLvl1Bit = PTPageBits;
+
+        private const long ErgMask = (4 << AThreadState.ErgSizeLog2) - 1;
 
         private class ArmMonitor
         {
@@ -29,32 +43,30 @@ namespace ChocolArm64.Memory
 
         private Dictionary<int, ArmMonitor> Monitors;
 
+        private ConcurrentDictionary<long, IntPtr> ObservedPages;
+
         public IntPtr Ram { get; private set; }
 
         private byte* RamPtr;
 
-        private int HostPageSize;
+        private byte*** PageTable;
 
-        public AMemory()
+        public AMemory(IntPtr Ram)
         {
-            Manager = new AMemoryMgr();
-
             Monitors = new Dictionary<int, ArmMonitor>();
 
-            IntPtr Size = (IntPtr)AMemoryMgr.RamSize + AMemoryMgr.PageSize;
+            ObservedPages = new ConcurrentDictionary<long, IntPtr>();
 
-            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
-            {
-                Ram = AMemoryWin32.Allocate(Size);
+            this.Ram = Ram;
 
-                HostPageSize = AMemoryWin32.GetPageSize(Ram, Size);
-            }
-            else
+            RamPtr = (byte*)Ram;
+
+            PageTable = (byte***)Marshal.AllocHGlobal(PTLvl0Size * IntPtr.Size);
+
+            for (int L0 = 0; L0 < PTLvl0Size; L0++)
             {
-                Ram = Marshal.AllocHGlobal(Size);
+                PageTable[L0] = null;
             }
-
-            RamPtr = (byte*)Ram;
         }
 
         public void RemoveMonitor(AThreadState State)
@@ -155,62 +167,6 @@ namespace ChocolArm64.Memory
             }
         }
 
-        public int GetHostPageSize()
-        {
-            return HostPageSize;
-        }
-
-        public (bool[], long) IsRegionModified(long Position, long Size)
-        {
-            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
-            {
-                return (null, 0);
-            }
-
-            long EndPos = Position + Size;
-
-            if ((ulong)EndPos < (ulong)Position)
-            {
-                return (null, 0);
-            }
-
-            if ((ulong)EndPos > AMemoryMgr.RamSize)
-            {
-                return (null, 0);
-            }
-
-            IntPtr MemAddress = new IntPtr(RamPtr + Position);
-            IntPtr MemSize    = new IntPtr(Size);
-
-            int HostPageMask = HostPageSize - 1;
-
-            Position &= ~HostPageMask;
-
-            Size = EndPos - Position;
-
-            IntPtr[] Addresses  = new IntPtr[(Size + HostPageMask) / HostPageSize];
-
-            AMemoryWin32.IsRegionModified(MemAddress, MemSize, Addresses, out int Count);
-
-            bool[] Modified = new bool[Addresses.Length];
-
-            for (int Index = 0; Index < Count; Index++)
-            {
-                long VA = Addresses[Index].ToInt64() - Ram.ToInt64();
-
-                Modified[(VA - Position) / HostPageSize] = true;
-            }
-
-            return (Modified, Count);
-        }
-
-        public IntPtr GetHostAddress(long Position, long Size)
-        {
-            EnsureRangeIsValid(Position, Size, AMemoryPerm.Read);
-
-            return (IntPtr)(RamPtr + (ulong)Position);
-        }
-
         public sbyte ReadSByte(long Position)
         {
             return (sbyte)ReadByte(Position);
@@ -233,33 +189,22 @@ namespace ChocolArm64.Memory
 
         public byte ReadByte(long Position)
         {
-            EnsureAccessIsValid(Position, AMemoryPerm.Read);
-
-            return ReadByteUnchecked(Position);
+            return *((byte*)Translate(Position));
         }
 
         public ushort ReadUInt16(long Position)
         {
-            EnsureAccessIsValid(Position + 0, AMemoryPerm.Read);
-            EnsureAccessIsValid(Position + 1, AMemoryPerm.Read);
-
-            return ReadUInt16Unchecked(Position);
+            return *((ushort*)Translate(Position));
         }
 
         public uint ReadUInt32(long Position)
         {
-            EnsureAccessIsValid(Position + 0, AMemoryPerm.Read);
-            EnsureAccessIsValid(Position + 3, AMemoryPerm.Read);
-
-            return ReadUInt32Unchecked(Position);
+            return *((uint*)Translate(Position));
         }
 
         public ulong ReadUInt64(long Position)
         {
-            EnsureAccessIsValid(Position + 0, AMemoryPerm.Read);
-            EnsureAccessIsValid(Position + 7, AMemoryPerm.Read);
-
-            return ReadUInt64Unchecked(Position);
+            return *((ulong*)Translate(Position));
         }
 
         public Vector128<float> ReadVector8(long Position)
@@ -274,6 +219,7 @@ namespace ChocolArm64.Memory
             }
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public Vector128<float> ReadVector16(long Position)
         {
             if (Sse2.IsSupported)
@@ -286,122 +232,12 @@ namespace ChocolArm64.Memory
             }
         }
 
-        public Vector128<float> ReadVector32(long Position)
-        {
-            EnsureAccessIsValid(Position + 0, AMemoryPerm.Read);
-            EnsureAccessIsValid(Position + 3, AMemoryPerm.Read);
-
-            if (Sse.IsSupported)
-            {
-                return Sse.LoadScalarVector128((float*)(RamPtr + (uint)Position));
-            }
-            else
-            {
-                throw new PlatformNotSupportedException();
-            }
-        }
-
-        public Vector128<float> ReadVector64(long Position)
-        {
-            EnsureAccessIsValid(Position + 0, AMemoryPerm.Read);
-            EnsureAccessIsValid(Position + 7, AMemoryPerm.Read);
-
-            if (Sse2.IsSupported)
-            {
-                return Sse.StaticCast<double, float>(Sse2.LoadScalarVector128((double*)(RamPtr + (uint)Position)));
-            }
-            else
-            {
-                throw new PlatformNotSupportedException();
-            }
-        }
-
-        public Vector128<float> ReadVector128(long Position)
-        {
-            EnsureAccessIsValid(Position + 0,  AMemoryPerm.Read);
-            EnsureAccessIsValid(Position + 15, AMemoryPerm.Read);
-
-            if (Sse.IsSupported)
-            {
-                return Sse.LoadVector128((float*)(RamPtr + (uint)Position));
-            }
-            else
-            {
-                throw new PlatformNotSupportedException();
-            }
-        }
-
-        public sbyte ReadSByteUnchecked(long Position)
-        {
-            return (sbyte)ReadByteUnchecked(Position);
-        }
-
-        public short ReadInt16Unchecked(long Position)
-        {
-            return (short)ReadUInt16Unchecked(Position);
-        }
-
-        public int ReadInt32Unchecked(long Position)
-        {
-            return (int)ReadUInt32Unchecked(Position);
-        }
-
-        public long ReadInt64Unchecked(long Position)
-        {
-            return (long)ReadUInt64Unchecked(Position);
-        }
-
-        public byte ReadByteUnchecked(long Position)
-        {
-            return *((byte*)(RamPtr + (uint)Position));
-        }
-
-        public ushort ReadUInt16Unchecked(long Position)
-        {
-            return *((ushort*)(RamPtr + (uint)Position));
-        }
-
-        public uint ReadUInt32Unchecked(long Position)
-        {
-            return *((uint*)(RamPtr + (uint)Position));
-        }
-
-        public ulong ReadUInt64Unchecked(long Position)
-        {
-            return *((ulong*)(RamPtr + (uint)Position));
-        }
-
-        public Vector128<float> ReadVector8Unchecked(long Position)
-        {
-            if (Sse2.IsSupported)
-            {
-                return Sse.StaticCast<byte, float>(Sse2.SetVector128(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ReadByte(Position)));
-            }
-            else
-            {
-                throw new PlatformNotSupportedException();
-            }
-        }
-
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public Vector128<float> ReadVector16Unchecked(long Position)
-        {
-            if (Sse2.IsSupported)
-            {
-                return Sse.StaticCast<ushort, float>(Sse2.Insert(Sse2.SetZeroVector128<ushort>(), ReadUInt16Unchecked(Position), 0));
-            }
-            else
-            {
-                throw new PlatformNotSupportedException();
-            }
-        }
-
         [MethodImpl(MethodImplOptions.NoInlining)]
-        public Vector128<float> ReadVector32Unchecked(long Position)
+        public Vector128<float> ReadVector32(long Position)
         {
             if (Sse.IsSupported)
             {
-                return Sse.LoadScalarVector128((float*)(RamPtr + (uint)Position));
+                return Sse.LoadScalarVector128((float*)Translate(Position));
             }
             else
             {
@@ -410,11 +246,11 @@ namespace ChocolArm64.Memory
         }
 
         [MethodImpl(MethodImplOptions.NoInlining)]
-        public Vector128<float> ReadVector64Unchecked(long Position)
+        public Vector128<float> ReadVector64(long Position)
         {
             if (Sse2.IsSupported)
             {
-                return Sse.StaticCast<double, float>(Sse2.LoadScalarVector128((double*)(RamPtr + (uint)Position)));
+                return Sse.StaticCast<double, float>(Sse2.LoadScalarVector128((double*)Translate(Position)));
             }
             else
             {
@@ -423,11 +259,11 @@ namespace ChocolArm64.Memory
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public Vector128<float> ReadVector128Unchecked(long Position)
+        public Vector128<float> ReadVector128(long Position)
         {
             if (Sse.IsSupported)
             {
-                return Sse.LoadVector128((float*)(RamPtr + (uint)Position));
+                return Sse.LoadVector128((float*)Translate(Position));
             }
             else
             {
@@ -442,11 +278,11 @@ namespace ChocolArm64.Memory
                 throw new ArgumentOutOfRangeException(nameof(Size));
             }
 
-            EnsureRangeIsValid(Position, Size, AMemoryPerm.Read);
+            EnsureRangeIsValid(Position, Size);
 
             byte[] Data = new byte[Size];
 
-            Marshal.Copy((IntPtr)(RamPtr + (uint)Position), Data, 0, (int)Size);
+            Marshal.Copy((IntPtr)Translate(Position), Data, 0, (int)Size);
 
             return Data;
         }
@@ -473,35 +309,25 @@ namespace ChocolArm64.Memory
 
         public void WriteByte(long Position, byte Value)
         {
-            EnsureAccessIsValid(Position, AMemoryPerm.Write);
-
-            WriteByteUnchecked(Position, Value);
+            *((byte*)TranslateWrite(Position)) = Value;
         }
 
         public void WriteUInt16(long Position, ushort Value)
         {
-            EnsureAccessIsValid(Position + 0, AMemoryPerm.Write);
-            EnsureAccessIsValid(Position + 1, AMemoryPerm.Write);
-
-            WriteUInt16Unchecked(Position, Value);
+            *((ushort*)TranslateWrite(Position)) = Value;
         }
 
         public void WriteUInt32(long Position, uint Value)
         {
-            EnsureAccessIsValid(Position + 0, AMemoryPerm.Write);
-            EnsureAccessIsValid(Position + 3, AMemoryPerm.Write);
-
-            WriteUInt32Unchecked(Position, Value);
+            *((uint*)TranslateWrite(Position)) = Value;
         }
 
         public void WriteUInt64(long Position, ulong Value)
         {
-            EnsureAccessIsValid(Position + 0, AMemoryPerm.Write);
-            EnsureAccessIsValid(Position + 7, AMemoryPerm.Write);
-
-            WriteUInt64Unchecked(Position, Value);
+            *((ulong*)TranslateWrite(Position)) = Value;
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public void WriteVector8(long Position, Vector128<float> Value)
         {
             if (Sse41.IsSupported)
@@ -518,6 +344,7 @@ namespace ChocolArm64.Memory
             }
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public void WriteVector16(long Position, Vector128<float> Value)
         {
             if (Sse2.IsSupported)
@@ -530,14 +357,12 @@ namespace ChocolArm64.Memory
             }
         }
 
+        [MethodImpl(MethodImplOptions.NoInlining)]
         public void WriteVector32(long Position, Vector128<float> Value)
         {
-            EnsureAccessIsValid(Position + 0, AMemoryPerm.Write);
-            EnsureAccessIsValid(Position + 3, AMemoryPerm.Write);
-
             if (Sse.IsSupported)
             {
-                Sse.StoreScalar((float*)(RamPtr + (uint)Position), Value);
+                Sse.StoreScalar((float*)TranslateWrite(Position), Value);
             }
             else
             {
@@ -545,14 +370,12 @@ namespace ChocolArm64.Memory
             }
         }
 
+        [MethodImpl(MethodImplOptions.NoInlining)]
         public void WriteVector64(long Position, Vector128<float> Value)
         {
-            EnsureAccessIsValid(Position + 0, AMemoryPerm.Write);
-            EnsureAccessIsValid(Position + 7, AMemoryPerm.Write);
-
             if (Sse2.IsSupported)
             {
-                Sse2.StoreScalar((double*)(RamPtr + (uint)Position), Sse.StaticCast<float, double>(Value));
+                Sse2.StoreScalar((double*)TranslateWrite(Position), Sse.StaticCast<float, double>(Value));
             }
             else
             {
@@ -560,14 +383,12 @@ namespace ChocolArm64.Memory
             }
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public void WriteVector128(long Position, Vector128<float> Value)
         {
-            EnsureAccessIsValid(Position + 0,  AMemoryPerm.Write);
-            EnsureAccessIsValid(Position + 15, AMemoryPerm.Write);
-
             if (Sse.IsSupported)
             {
-                Sse.Store((float*)(RamPtr + (uint)Position), Value);
+                Sse.Store((float*)TranslateWrite(Position), Value);
             }
             else
             {
@@ -575,147 +396,287 @@ namespace ChocolArm64.Memory
             }
         }
 
-        public void WriteSByteUnchecked(long Position, sbyte Value)
+        public void WriteBytes(long Position, byte[] Data)
         {
-            WriteByteUnchecked(Position, (byte)Value);
-        }
+            EnsureRangeIsValid(Position, (uint)Data.Length);
 
-        public void WriteInt16Unchecked(long Position, short Value)
-        {
-            WriteUInt16Unchecked(Position, (ushort)Value);
+            Marshal.Copy(Data, 0, (IntPtr)TranslateWrite(Position), Data.Length);
         }
 
-        public void WriteInt32Unchecked(long Position, int Value)
+        public void Map(long VA, long PA, long Size)
         {
-            WriteUInt32Unchecked(Position, (uint)Value);
+            SetPTEntries(VA, RamPtr + PA, Size);
         }
 
-        public void WriteInt64Unchecked(long Position, long Value)
+        public void Unmap(long Position, long Size)
         {
-            WriteUInt64Unchecked(Position, (ulong)Value);
-        }
+            SetPTEntries(Position, null, Size);
 
-        public void WriteByteUnchecked(long Position, byte Value)
-        {
-            *((byte*)(RamPtr + (uint)Position)) = Value;
+            StopObservingRegion(Position, Size);
         }
 
-        public void WriteUInt16Unchecked(long Position, ushort Value)
+        public bool IsMapped(long Position)
         {
-            *((ushort*)(RamPtr + (uint)Position)) = Value;
-        }
+            if (!(IsValidPosition(Position)))
+            {
+                return false;
+            }
 
-        public void WriteUInt32Unchecked(long Position, uint Value)
-        {
-            *((uint*)(RamPtr + (uint)Position)) = Value;
+            long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask;
+            long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask;
+
+            if (PageTable[L0] == null)
+            {
+                return false;
+            }
+
+            return PageTable[L0][L1] != null || ObservedPages.ContainsKey(Position >> PTPageBits);
         }
 
-        public void WriteUInt64Unchecked(long Position, ulong Value)
+        public long GetPhysicalAddress(long VirtualAddress)
         {
-            *((ulong*)(RamPtr + (uint)Position)) = Value;
+            byte* Ptr = Translate(VirtualAddress);
+
+            return (long)(Ptr - RamPtr);
         }
 
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public void WriteVector8Unchecked(long Position, Vector128<float> Value)
+        internal byte* Translate(long Position)
         {
-            if (Sse41.IsSupported)
+            long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask;
+            long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask;
+
+            long Old = Position;
+
+            byte** Lvl1 = PageTable[L0];
+
+            if ((Position >> (PTLvl0Bit + PTLvl0Bits)) != 0)
             {
-                WriteByteUnchecked(Position, Sse41.Extract(Sse.StaticCast<float, byte>(Value), 0));
+                goto Unmapped;
             }
-            else if (Sse2.IsSupported)
+
+            if (Lvl1 == null)
             {
-                WriteByteUnchecked(Position, (byte)Sse2.Extract(Sse.StaticCast<float, ushort>(Value), 0));
+                goto Unmapped;
             }
-            else
+
+            Position &= PageMask;
+
+            byte* Ptr = Lvl1[L1];
+
+            if (Ptr == null)
             {
-                throw new PlatformNotSupportedException();
+                goto Unmapped;
             }
+
+            return Ptr + Position;
+
+Unmapped:
+            return HandleNullPte(Old);
         }
 
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public void WriteVector16Unchecked(long Position, Vector128<float> Value)
+        private byte* HandleNullPte(long Position)
         {
-            if (Sse2.IsSupported)
+            long Key = Position >> PTPageBits;
+
+            if (ObservedPages.TryGetValue(Key, out IntPtr Ptr))
             {
-                WriteUInt16Unchecked(Position, Sse2.Extract(Sse.StaticCast<float, ushort>(Value), 0));
+                return (byte*)Ptr + (Position & PageMask);
             }
-            else
+
+            throw new VmmPageFaultException(Position);
+        }
+
+        internal byte* TranslateWrite(long Position)
+        {
+            long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask;
+            long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask;
+
+            long Old = Position;
+
+            byte** Lvl1 = PageTable[L0];
+
+            if ((Position >> (PTLvl0Bit + PTLvl0Bits)) != 0)
             {
-                throw new PlatformNotSupportedException();
+                goto Unmapped;
+            }
+
+            if (Lvl1 == null)
+            {
+                goto Unmapped;
+            }
+
+            Position &= PageMask;
+
+            byte* Ptr = Lvl1[L1];
+
+            if (Ptr == null)
+            {
+                goto Unmapped;
             }
+
+            return Ptr + Position;
+
+Unmapped:
+            return HandleNullPteWrite(Old);
         }
 
-        [MethodImpl(MethodImplOptions.NoInlining)]
-        public void WriteVector32Unchecked(long Position, Vector128<float> Value)
+        private byte* HandleNullPteWrite(long Position)
         {
-            if (Sse.IsSupported)
+            long Key = Position >> PTPageBits;
+
+            if (ObservedPages.TryGetValue(Key, out IntPtr Ptr))
             {
-                Sse.StoreScalar((float*)(RamPtr + (uint)Position), Value);
+                SetPTEntry(Position, (byte*)Ptr);
+
+                return (byte*)Ptr + (Position & PageMask);
             }
-            else
+
+            throw new VmmPageFaultException(Position);
+        }
+
+        private void SetPTEntries(long VA, byte* Ptr, long Size)
+        {
+            long EndPosition = (VA + Size + PageMask) & ~PageMask;
+
+            while ((ulong)VA < (ulong)EndPosition)
             {
-                throw new PlatformNotSupportedException();
+                SetPTEntry(VA, Ptr);
+
+                VA += PageSize;
+
+                if (Ptr != null)
+                {
+                    Ptr += PageSize;
+                }
             }
         }
 
-        [MethodImpl(MethodImplOptions.NoInlining)]
-        public void WriteVector64Unchecked(long Position, Vector128<float> Value)
+        private void SetPTEntry(long Position, byte* Ptr)
         {
-            if (Sse2.IsSupported)
+            if (!IsValidPosition(Position))
             {
-                Sse2.StoreScalar((double*)(RamPtr + (uint)Position), Sse.StaticCast<float, double>(Value));
+                throw new ArgumentOutOfRangeException(nameof(Position));
             }
-            else
+
+            long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask;
+            long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask;
+
+            if (PageTable[L0] == null)
             {
-                throw new PlatformNotSupportedException();
+                byte** Lvl1 = (byte**)Marshal.AllocHGlobal(PTLvl1Size * IntPtr.Size);
+
+                for (int ZL1 = 0; ZL1 < PTLvl1Size; ZL1++)
+                {
+                    Lvl1[ZL1] = null;
+                }
+
+                Thread.MemoryBarrier();
+
+                PageTable[L0] = Lvl1;
             }
+
+            PageTable[L0][L1] = Ptr;
         }
 
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public void WriteVector128Unchecked(long Position, Vector128<float> Value)
+        public (bool[], int) IsRegionModified(long Position, long Size)
         {
-            if (Sse.IsSupported)
+            long EndPosition = (Position + Size + PageMask) & ~PageMask;
+
+            Position &= ~PageMask;
+
+            Size = EndPosition - Position;
+
+            bool[] Modified = new bool[Size >> PTPageBits];
+
+            int Count = 0;
+
+            lock (ObservedPages)
             {
-                Sse.Store((float*)(RamPtr + (uint)Position), Value);
+                for (int Page = 0; Page < Modified.Length; Page++)
+                {
+                    byte* Ptr = Translate(Position);
+
+                    if (ObservedPages.TryAdd(Position >> PTPageBits, (IntPtr)Ptr))
+                    {
+                        Modified[Page] = true;
+
+                        Count++;
+                    }
+                    else
+                    {
+                        long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask;
+                        long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask;
+
+                        byte** Lvl1 = PageTable[L0];
+
+                        if (Lvl1 != null)
+                        {
+                            if (Modified[Page] = Lvl1[L1] != null)
+                            {
+                                Count++;
+                            }
+                        }
+                    }
+
+                    SetPTEntry(Position, null);
+
+                    Position += PageSize;
+                }
             }
-            else
+
+            return (Modified, Count);
+        }
+
+        public void StopObservingRegion(long Position, long Size)
+        {
+            long EndPosition = (Position + Size + PageMask) & ~PageMask;
+
+            while (Position < EndPosition)
             {
-                throw new PlatformNotSupportedException();
+                lock (ObservedPages)
+                {
+                    if (ObservedPages.TryRemove(Position >> PTPageBits, out IntPtr Ptr))
+                    {
+                        SetPTEntry(Position, (byte*)Ptr);
+                    }
+                }
+
+                Position += PageSize;
             }
         }
 
-        public void WriteBytes(long Position, byte[] Data)
+        public IntPtr GetHostAddress(long Position, long Size)
         {
-            EnsureRangeIsValid(Position, (uint)Data.Length, AMemoryPerm.Write);
+            EnsureRangeIsValid(Position, Size);
 
-            Marshal.Copy(Data, 0, (IntPtr)(RamPtr + (uint)Position), Data.Length);
+            return (IntPtr)Translate(Position);
         }
 
-        private void EnsureRangeIsValid(long Position, long Size, AMemoryPerm Perm)
+        internal void EnsureRangeIsValid(long Position, long Size)
         {
             long EndPos = Position + Size;
 
-            Position &= ~AMemoryMgr.PageMask;
+            Position &= ~PageMask;
+
+            long ExpectedPA = GetPhysicalAddress(Position);
 
             while ((ulong)Position < (ulong)EndPos)
             {
-                EnsureAccessIsValid(Position, Perm);
+                long PA = GetPhysicalAddress(Position);
+
+                if (PA != ExpectedPA)
+                {
+                    throw new VmmAccessException(Position, Size);
+                }
 
-                Position += AMemoryMgr.PageSize;
+                Position   += PageSize;
+                ExpectedPA += PageSize;
             }
         }
 
-        private void EnsureAccessIsValid(long Position, AMemoryPerm Perm)
+        public bool IsValidPosition(long Position)
         {
-            if (!Manager.IsMapped(Position))
-            {
-                throw new VmmPageFaultException(Position);
-            }
-
-            if (!Manager.HasPermission(Position, Perm))
-            {
-                throw new VmmAccessViolationException(Position, Perm);
-            }
+            return Position >> (PTLvl0Bits + PTLvl1Bits + PTPageBits) == 0;
         }
 
         public void Dispose()
@@ -725,19 +686,24 @@ namespace ChocolArm64.Memory
 
         protected virtual void Dispose(bool disposing)
         {
-            if (Ram != IntPtr.Zero)
+            if (PageTable == null)
             {
-                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
-                {
-                    AMemoryWin32.Free(Ram);
-                }
-                else
+                return;
+            }
+
+            for (int L0 = 0; L0 < PTLvl0Size; L0++)
+            {
+                if (PageTable[L0] != null)
                 {
-                    Marshal.FreeHGlobal(Ram);
+                    Marshal.FreeHGlobal((IntPtr)PageTable[L0]);
                 }
 
-                Ram = IntPtr.Zero;
+                PageTable[L0] = null;
             }
+
+            Marshal.FreeHGlobal((IntPtr)PageTable);
+
+            PageTable = null;
         }
     }
 }

+ 4 - 20
ChocolArm64/Memory/AMemoryHelper.cs

@@ -26,12 +26,9 @@ namespace ChocolArm64.Memory
         {
             long Size = Marshal.SizeOf<T>();
 
-            if ((ulong)(Position + Size) > AMemoryMgr.AddrSize)
-            {
-                throw new ArgumentOutOfRangeException(nameof(Position));
-            }
+            Memory.EnsureRangeIsValid(Position, Size);
 
-            IntPtr Ptr = new IntPtr((byte*)Memory.Ram + Position);
+            IntPtr Ptr = (IntPtr)Memory.Translate(Position);
 
             return Marshal.PtrToStructure<T>(Ptr);
         }
@@ -40,12 +37,9 @@ namespace ChocolArm64.Memory
         {
             long Size = Marshal.SizeOf<T>();
 
-            if ((ulong)(Position + Size) > AMemoryMgr.AddrSize)
-            {
-                throw new ArgumentOutOfRangeException(nameof(Position));
-            }
+            Memory.EnsureRangeIsValid(Position, Size);
 
-            IntPtr Ptr = new IntPtr((byte*)Memory.Ram + Position);
+            IntPtr Ptr = (IntPtr)Memory.TranslateWrite(Position);
 
             Marshal.StructureToPtr<T>(Value, Ptr, false);
         }
@@ -69,15 +63,5 @@ namespace ChocolArm64.Memory
                 return Encoding.ASCII.GetString(MS.ToArray());
             }
         }
-
-        public static long PageRoundUp(long Value)
-        {
-            return (Value + AMemoryMgr.PageMask) & ~AMemoryMgr.PageMask;
-        }
-
-        public static long PageRoundDown(long Value)
-        {
-            return Value & ~AMemoryMgr.PageMask;
-        }
     }
 }

+ 0 - 21
ChocolArm64/Memory/AMemoryMapInfo.cs

@@ -1,21 +0,0 @@
-namespace ChocolArm64.Memory
-{
-    public class AMemoryMapInfo
-    {
-        public long Position { get; private set; }
-        public long Size     { get; private set; }
-        public int  Type     { get; private set; }
-        public int  Attr     { get; private set; }
-
-        public AMemoryPerm Perm { get; private set; }
-
-        public AMemoryMapInfo(long Position, long Size, int Type, int Attr, AMemoryPerm Perm)
-        {
-            this.Position = Position;
-            this.Size     = Size;
-            this.Type     = Type;
-            this.Attr     = Attr;
-            this.Perm     = Perm;
-        }
-    }
-}

+ 0 - 258
ChocolArm64/Memory/AMemoryMgr.cs

@@ -1,258 +0,0 @@
-using System;
-
-namespace ChocolArm64.Memory
-{
-    public class AMemoryMgr
-    {
-        public const long RamSize  = 4L * 1024 * 1024 * 1024;
-        public const long AddrSize = RamSize;
-
-        private const int PTLvl0Bits = 10;
-        private const int PTLvl1Bits = 10;
-        private const int PTPageBits = 12;
-
-        private const int PTLvl0Size = 1 << PTLvl0Bits;
-        private const int PTLvl1Size = 1 << PTLvl1Bits;
-        public  const int PageSize   = 1 << PTPageBits;
-
-        private const int PTLvl0Mask = PTLvl0Size - 1;
-        private const int PTLvl1Mask = PTLvl1Size - 1;
-        public  const int PageMask   = PageSize   - 1;
-
-        private const int PTLvl0Bit = PTPageBits + PTLvl1Bits;
-        private const int PTLvl1Bit = PTPageBits;
-
-        private enum PTMap
-        {
-            Unmapped,
-            Mapped
-        }
-
-        private struct PTEntry
-        {
-            public PTMap       Map;
-            public AMemoryPerm Perm;
-
-            public int Type;
-            public int Attr;
-
-            public PTEntry(PTMap Map, AMemoryPerm Perm, int Type, int Attr)
-            {
-                this.Map  = Map;
-                this.Perm = Perm;
-                this.Type = Type;
-                this.Attr = Attr;
-            }
-        }
-
-        private PTEntry[][] PageTable;
-
-        public AMemoryMgr()
-        {
-            PageTable = new PTEntry[PTLvl0Size][];
-        }
-
-        public void Map(long Position, long Size, int Type, AMemoryPerm Perm)
-        {
-            SetPTEntry(Position, Size, new PTEntry(PTMap.Mapped, Perm, Type, 0));
-        }
-
-        public void Unmap(long Position, long Size)
-        {
-            SetPTEntry(Position, Size, new PTEntry(PTMap.Unmapped, 0, 0, 0));
-        }
-
-        public void Unmap(long Position, long Size, int Type)
-        {
-            SetPTEntry(Position, Size, Type, new PTEntry(PTMap.Unmapped, 0, 0, 0));
-        }
-
-        public void Reprotect(long Position, long Size, AMemoryPerm Perm)
-        {
-            Position = AMemoryHelper.PageRoundDown(Position);
-
-            Size = AMemoryHelper.PageRoundUp(Size);
-
-            long PagesCount = Size / PageSize;
-
-            while (PagesCount-- > 0)
-            {
-                PTEntry Entry = GetPTEntry(Position);
-
-                Entry.Perm = Perm;
-
-                SetPTEntry(Position, Entry);
-
-                Position += PageSize;
-            }
-        }
-
-        public AMemoryMapInfo GetMapInfo(long Position)
-        {
-            if (!IsValidPosition(Position))
-            {
-                return null;
-            }
-
-            Position = AMemoryHelper.PageRoundDown(Position);
-
-            PTEntry BaseEntry = GetPTEntry(Position);
-
-            bool IsSameSegment(long Pos)
-            {
-                if (!IsValidPosition(Pos))
-                {
-                    return false;
-                }
-
-                PTEntry Entry = GetPTEntry(Pos);
-
-                return Entry.Map  == BaseEntry.Map  &&
-                       Entry.Perm == BaseEntry.Perm &&
-                       Entry.Type == BaseEntry.Type &&
-                       Entry.Attr == BaseEntry.Attr;
-            }
-
-            long Start = Position;
-            long End   = Position + PageSize;
-
-            while (Start > 0 && IsSameSegment(Start - PageSize))
-            {
-                Start -= PageSize;
-            }
-
-            while (End < AddrSize && IsSameSegment(End))
-            {
-                End += PageSize;
-            }
-
-            long Size = End - Start;
-
-            return new AMemoryMapInfo(
-                Start,
-                Size,
-                BaseEntry.Type,
-                BaseEntry.Attr,
-                BaseEntry.Perm);
-        }
-
-        public void ClearAttrBit(long Position, long Size, int Bit)
-        {
-            while (Size > 0)
-            {
-                PTEntry Entry = GetPTEntry(Position);
-
-                Entry.Attr &= ~(1 << Bit);
-
-                SetPTEntry(Position, Entry);
-
-                Position += PageSize;
-                Size     -= PageSize;
-            }
-        }
-
-        public void SetAttrBit(long Position, long Size, int Bit)
-        {
-            while (Size > 0)
-            {
-                PTEntry Entry = GetPTEntry(Position);
-
-                Entry.Attr |= (1 << Bit);
-
-                SetPTEntry(Position, Entry);
-
-                Position += PageSize;
-                Size     -= PageSize;
-            }
-        }
-
-        public bool HasPermission(long Position, AMemoryPerm Perm)
-        {
-            return GetPTEntry(Position).Perm.HasFlag(Perm);
-        }
-
-        public bool IsValidPosition(long Position)
-        {
-            if (Position >> PTLvl0Bits + PTLvl1Bits + PTPageBits != 0)
-            {
-                return false;
-            }
-
-            return true;
-        }
-
-        public bool IsMapped(long Position)
-        {
-            if (Position >> PTLvl0Bits + PTLvl1Bits + PTPageBits != 0)
-            {
-                return false;
-            }
-
-            long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask;
-            long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask;
-
-            if (PageTable[L0] == null)
-            {
-                return false;
-            }
-
-            return PageTable[L0][L1].Map != PTMap.Unmapped;
-        }
-
-        private PTEntry GetPTEntry(long Position)
-        {
-            long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask;
-            long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask;
-
-            if (PageTable[L0] == null)
-            {
-                return default(PTEntry);
-            }
-
-            return PageTable[L0][L1];
-        }
-
-        private void SetPTEntry(long Position, long Size, PTEntry Entry)
-        {
-            while (Size > 0)
-            {
-                SetPTEntry(Position, Entry);
-
-                Position += PageSize;
-                Size     -= PageSize;
-            }
-        }
-
-        private void SetPTEntry(long Position, long Size, int Type, PTEntry Entry)
-        {
-            while (Size > 0)
-            {
-                if (GetPTEntry(Position).Type == Type)
-                {
-                    SetPTEntry(Position, Entry);
-                }
-
-                Position += PageSize;
-                Size     -= PageSize;
-            }
-        }
-
-        private void SetPTEntry(long Position, PTEntry Entry)
-        {
-            if (!IsValidPosition(Position))
-            {
-                throw new ArgumentOutOfRangeException(nameof(Position));
-            }
-
-            long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask;
-            long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask;
-
-            if (PageTable[L0] == null)
-            {
-                PageTable[L0] = new PTEntry[PTLvl1Size];
-            }
-
-            PageTable[L0][L1] = Entry;
-        }
-    }
-}

+ 0 - 15
ChocolArm64/Memory/AMemoryPerm.cs

@@ -1,15 +0,0 @@
-using System;
-
-namespace ChocolArm64.Memory
-{
-    [Flags]
-    public enum AMemoryPerm
-    {
-        None    = 0,
-        Read    = 1 << 0,
-        Write   = 1 << 1,
-        Execute = 1 << 2,
-        RW      = Read | Write,
-        RX      = Read | Execute
-    }
-}

+ 0 - 92
ChocolArm64/Memory/AMemoryWin32.cs

@@ -1,92 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-
-namespace ChocolArm64.Memory
-{
-    static class AMemoryWin32
-    {
-        private const int MEM_COMMIT      = 0x00001000;
-        private const int MEM_RESERVE     = 0x00002000;
-        private const int MEM_WRITE_WATCH = 0x00200000;
-
-        private const int PAGE_READWRITE = 0x04;
-
-        private const int MEM_RELEASE = 0x8000;
-
-        private const int WRITE_WATCH_FLAG_RESET = 1;
-
-        [DllImport("kernel32.dll")]
-        private static extern IntPtr VirtualAlloc(IntPtr lpAddress, IntPtr dwSize, int flAllocationType, int flProtect);
-
-        [DllImport("kernel32.dll")]
-        private static extern bool VirtualFree(IntPtr lpAddress, IntPtr dwSize, int dwFreeType);
-
-        [DllImport("kernel32.dll")]
-        private unsafe static extern int GetWriteWatch(
-            int      dwFlags,
-            IntPtr   lpBaseAddress,
-            IntPtr   dwRegionSize,
-            IntPtr[] lpAddresses,
-            long*    lpdwCount,
-            long*    lpdwGranularity);
-
-        public static IntPtr Allocate(IntPtr Size)
-        {
-            const int Flags = MEM_COMMIT | MEM_RESERVE | MEM_WRITE_WATCH;
-
-            IntPtr Address = VirtualAlloc(IntPtr.Zero, Size, Flags, PAGE_READWRITE);
-
-            if (Address == IntPtr.Zero)
-            {
-                throw new InvalidOperationException();
-            }
-
-            return Address;
-        }
-
-        public static void Free(IntPtr Address)
-        {
-            VirtualFree(Address, IntPtr.Zero, MEM_RELEASE);
-        }
-
-        public unsafe static int GetPageSize(IntPtr Address, IntPtr Size)
-        {
-            IntPtr[] Addresses = new IntPtr[1];
-
-            long Count = Addresses.Length;
-
-            long Granularity;
-
-            GetWriteWatch(
-                0,
-                Address,
-                Size,
-                Addresses,
-                &Count,
-                &Granularity);
-
-            return (int)Granularity;
-        }
-
-        public unsafe static void IsRegionModified(
-            IntPtr   Address,
-            IntPtr   Size,
-            IntPtr[] Addresses,
-            out int  AddrCount)
-        {
-            long Count = Addresses.Length;
-
-            long Granularity;
-
-            GetWriteWatch(
-                WRITE_WATCH_FLAG_RESET,
-                Address,
-                Size,
-                Addresses,
-                &Count,
-                &Granularity);
-
-            AddrCount = (int)Count;
-        }
-    }
-}

+ 1 - 0
Ryujinx.Graphics/Gal/IGalShader.cs

@@ -9,6 +9,7 @@ namespace Ryujinx.Graphics.Gal
 
         void Create(IGalMemory Memory, long VpAPos, long Key, GalShaderType Type);
 
+        IEnumerable<ShaderDeclInfo> GetConstBufferUsage(long Key);
         IEnumerable<ShaderDeclInfo> GetTextureUsage(long Key);
 
         void EnsureTextureBinding(string UniformName, int Value);

+ 1 - 1
Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs

@@ -279,7 +279,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             {
                 if (Stage != null)
                 {
-                    foreach (ShaderDeclInfo DeclInfo in Stage.UniformUsage)
+                    foreach (ShaderDeclInfo DeclInfo in Stage.ConstBufferUsage)
                     {
                         long Key = New.ConstBufferKeys[(int)Stage.Type][DeclInfo.Cbuf];
 

+ 13 - 3
Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs

@@ -72,8 +72,18 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             return new OGLShaderStage(
                 Type,
                 Program.Code,
-                Program.Textures,
-                Program.Uniforms);
+                Program.Uniforms,
+                Program.Textures);
+        }
+
+        public IEnumerable<ShaderDeclInfo> GetConstBufferUsage(long Key)
+        {
+            if (Stages.TryGetValue(Key, out OGLShaderStage Stage))
+            {
+                return Stage.ConstBufferUsage;
+            }
+
+            return Enumerable.Empty<ShaderDeclInfo>();
         }
 
         public IEnumerable<ShaderDeclInfo> GetTextureUsage(long Key)
@@ -224,7 +234,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             {
                 if (Stage != null)
                 {
-                    foreach (ShaderDeclInfo DeclInfo in Stage.UniformUsage)
+                    foreach (ShaderDeclInfo DeclInfo in Stage.ConstBufferUsage)
                     {
                         int BlockIndex = GL.GetUniformBlockIndex(ProgramHandle, DeclInfo.Name);
 

+ 11 - 10
Ryujinx.Graphics/Gal/OpenGL/OGLShaderProgram.cs

@@ -1,6 +1,7 @@
 using OpenTK.Graphics.OpenGL;
 using System;
 using System.Collections.Generic;
+using System.Linq;
 
 namespace Ryujinx.Graphics.Gal.OpenGL
 {
@@ -23,19 +24,19 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 
         public string Code { get; private set; }
 
-        public IEnumerable<ShaderDeclInfo> TextureUsage { get; private set; }
-        public IEnumerable<ShaderDeclInfo> UniformUsage { get; private set; }
+        public IEnumerable<ShaderDeclInfo> ConstBufferUsage { get; private set; }
+        public IEnumerable<ShaderDeclInfo> TextureUsage     { get; private set; }
 
         public OGLShaderStage(
-            GalShaderType Type,
-            string Code,
-            IEnumerable<ShaderDeclInfo> TextureUsage,
-            IEnumerable<ShaderDeclInfo> UniformUsage)
+            GalShaderType               Type,
+            string                      Code,
+            IEnumerable<ShaderDeclInfo> ConstBufferUsage,
+            IEnumerable<ShaderDeclInfo> TextureUsage)
         {
-            this.Type = Type;
-            this.Code = Code;
-            this.TextureUsage = TextureUsage;
-            this.UniformUsage = UniformUsage;
+            this.Type             = Type;
+            this.Code             = Code;
+            this.ConstBufferUsage = ConstBufferUsage;
+            this.TextureUsage     = TextureUsage;
         }
 
         public void Compile()

+ 0 - 177
Ryujinx.HLE/Font/SharedFontManager.cs

@@ -1,177 +0,0 @@
-using ChocolArm64.Exceptions;
-using ChocolArm64.Memory;
-using Ryujinx.HLE.Logging;
-using Ryujinx.HLE.OsHle;
-using Ryujinx.HLE.OsHle.Handles;
-using Ryujinx.HLE.Resource;
-using System;
-using System.Collections.Generic;
-using System.IO;
-
-
-namespace Ryujinx.HLE.Font
-{
-    public class SharedFontManager
-    {
-        private const uint SharedMemorySize = 0x1100000;
-        private Logger Log;
-
-        private string FontsPath;
-
-        private object ShMemLock;
-
-        private (AMemory, long, long)[] ShMemPositions;
-
-        private Dictionary<SharedFontType, byte[]> FontData;
-
-        private uint[] LoadedFonts;
-
-        public SharedFontManager(Logger Log, string SystemPath)
-        {
-            this.Log          = Log;
-            this.FontsPath    = Path.Combine(SystemPath, "fonts");
-
-            ShMemLock         = new object();
-
-            ShMemPositions    = new(AMemory, long, long)[0];
-
-            FontData          = new Dictionary<SharedFontType, byte[]>()
-            {
-                { SharedFontType.JapanUsEurope,       GetData("FontStandard")                  },
-                { SharedFontType.SimplifiedChinese,   GetData("FontChineseSimplified")         },
-                { SharedFontType.SimplifiedChineseEx, GetData("FontExtendedChineseSimplified") },
-                { SharedFontType.TraditionalChinese,  GetData("FontChineseTraditional")        },
-                { SharedFontType.Korean,              GetData("FontKorean")                    },
-                { SharedFontType.NintendoEx,          GetData("FontNintendoExtended")          }
-            };
-
-            int FontMemoryUsage = 0;
-            foreach (byte[] data in FontData.Values)
-            {
-                FontMemoryUsage += data.Length;
-                FontMemoryUsage += 0x8;
-            }
-
-            if (FontMemoryUsage > SharedMemorySize)
-            {
-                throw new InvalidSystemResourceException($"The sum of all fonts size exceed the shared memory size. Please make sure that the fonts don't exceed {SharedMemorySize} bytes in total. (actual size: {FontMemoryUsage} bytes)");
-            }
-
-            LoadedFonts       = new uint[FontData.Count];
-        }
-
-        public byte[] GetData(string FontName)
-        {
-            string FontFilePath = Path.Combine(FontsPath, $"{FontName}.ttf");
-            if (File.Exists(FontFilePath))
-            {
-                return File.ReadAllBytes(FontFilePath);
-            }
-            else
-            {
-                throw new InvalidSystemResourceException($"Font \"{FontName}.ttf\" not found. Please provide it in \"{FontsPath}\".");
-            }
-        }
-
-        public void MapFont(SharedFontType FontType, AMemory Memory, long Position)
-        {
-            uint SharedMemoryAddressOffset = GetSharedMemoryAddressOffset(FontType);
-            // TODO: find what are the 8 bytes before the font
-            Memory.WriteUInt64(Position + SharedMemoryAddressOffset - 8, 0);
-            Memory.WriteBytes(Position + SharedMemoryAddressOffset, FontData[FontType]);
-        }
-
-        public void PropagateNewMapFont(SharedFontType Type)
-        {
-            lock (ShMemLock)
-            {
-                foreach ((AMemory Memory, long Position, long Size) in ShMemPositions)
-                {
-                    AMemoryMapInfo MemoryInfo = Memory.Manager.GetMapInfo(Position);
-
-                    if (MemoryInfo == null)
-                    {
-                        throw new VmmPageFaultException(Position);
-                    }
-
-                    // The memory is read only, we need to changes that to add the new font
-                    AMemoryPerm originalPerms = MemoryInfo.Perm;
-                    Memory.Manager.Reprotect(Position, Size, AMemoryPerm.RW);
-                    MapFont(Type, Memory, Position);
-                    Memory.Manager.Reprotect(Position, Size, originalPerms);
-                }
-            }
-        }
-
-        internal void ShMemMap(object sender, EventArgs e)
-        {
-            HSharedMem SharedMem = (HSharedMem)sender;
-
-            lock (ShMemLock)
-            {
-                ShMemPositions = SharedMem.GetVirtualPositions();
-
-                (AMemory Memory, long Position, long Size) = ShMemPositions[ShMemPositions.Length - 1];
-
-                for (int Type = 0; Type < LoadedFonts.Length; Type++)
-                {
-                    if (LoadedFonts[(int)Type] == 1)
-                    {
-                        MapFont((SharedFontType)Type, Memory, Position);
-                    }
-                }
-            }
-        }
-
-        internal void ShMemUnmap(object sender, EventArgs e)
-        {
-            HSharedMem SharedMem = (HSharedMem)sender;
-
-            lock (ShMemLock)
-            {
-                ShMemPositions = SharedMem.GetVirtualPositions();
-            }
-        }
-
-        public void Load(SharedFontType FontType)
-        {
-            if (LoadedFonts[(int)FontType] == 0)
-            {
-                PropagateNewMapFont(FontType);
-            }
-
-            LoadedFonts[(int)FontType] = 1;
-        }
-
-        public uint GetLoadState(SharedFontType FontType)
-        {
-            if (LoadedFonts[(int)FontType] != 1)
-            {
-                // Some games don't request a load, so we need to load it here.
-                Load(FontType);
-                return 0;
-            }
-            return LoadedFonts[(int)FontType];
-        }
-
-        public uint GetFontSize(SharedFontType FontType)
-        {
-            return (uint)FontData[FontType].Length;
-        }
-
-        public uint GetSharedMemoryAddressOffset(SharedFontType FontType)
-        {
-            uint Pos = 0x8;
-
-            for (SharedFontType Type = SharedFontType.JapanUsEurope; Type < FontType; Type++)
-            {
-                Pos += GetFontSize(Type);
-                Pos += 0x8;
-            }
-
-            return Pos;
-        }
-
-        public int Count => FontData.Count;
-    }
-}

+ 24 - 11
Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs

@@ -109,7 +109,7 @@ namespace Ryujinx.HLE.Gpu.Engines
             Gpu.Renderer.Shader.BindProgram();
 
             UploadTextures(Vmm, State, Keys);
-            UploadConstBuffers(Vmm, State);
+            UploadConstBuffers(Vmm, State, Keys);
             UploadVertexArrays(Vmm, State);
 
             DispatchRender(Vmm, State);
@@ -426,6 +426,12 @@ namespace Ryujinx.HLE.Gpu.Engines
 
             Key = Vmm.GetPhysicalAddress(Key);
 
+            if (Key == -1)
+            {
+                //FIXME: Should'nt ignore invalid addresses.
+                return;
+            }
+
             if (IsFrameBufferPosition(Key))
             {
                 //This texture is a frame buffer texture,
@@ -465,24 +471,29 @@ namespace Ryujinx.HLE.Gpu.Engines
             Gpu.Renderer.Texture.SetSampler(Sampler);
         }
 
-        private void UploadConstBuffers(NvGpuVmm Vmm, GalPipelineState State)
+        private void UploadConstBuffers(NvGpuVmm Vmm, GalPipelineState State, long[] Keys)
         {
-            for (int Stage = 0; Stage < State.ConstBufferKeys.Length; Stage++)
+            for (int Stage = 0; Stage < Keys.Length; Stage++)
             {
-                for (int Index = 0; Index < State.ConstBufferKeys[Stage].Length; Index++)
+                foreach (ShaderDeclInfo DeclInfo in Gpu.Renderer.Shader.GetConstBufferUsage(Keys[Stage]))
                 {
-                    ConstBuffer Cb = ConstBuffers[Stage][Index];
+                    ConstBuffer Cb = ConstBuffers[Stage][DeclInfo.Cbuf];
 
-                    long Key = Cb.Position;
+                    if (!Cb.Enabled)
+                    {
+                        continue;
+                    }
 
-                    if (Cb.Enabled && QueryKeyUpload(Vmm, Key, Cb.Size, NvGpuBufferType.ConstBuffer))
+                    long Key = Vmm.GetPhysicalAddress(Cb.Position);
+
+                    if (QueryKeyUpload(Vmm, Key, Cb.Size, NvGpuBufferType.ConstBuffer))
                     {
-                        IntPtr Source = Vmm.GetHostAddress(Key, Cb.Size);
+                        IntPtr Source = Vmm.GetHostAddress(Cb.Position, Cb.Size);
 
                         Gpu.Renderer.Buffer.SetData(Key, Cb.Size, Source);
                     }
 
-                    State.ConstBufferKeys[Stage][Index] = Key;
+                    State.ConstBufferKeys[Stage][DeclInfo.Cbuf] = Key;
                 }
             }
         }
@@ -668,11 +679,13 @@ namespace Ryujinx.HLE.Gpu.Engines
 
             long Position = MakeInt64From2xInt32(NvGpuEngine3dReg.ConstBufferAddress);
 
+            long CbKey = Vmm.GetPhysicalAddress(Position);
+
             int Size = ReadRegister(NvGpuEngine3dReg.ConstBufferSize);
 
-            if (!Gpu.Renderer.Buffer.IsCached(Position, Size))
+            if (!Gpu.Renderer.Buffer.IsCached(CbKey, Size))
             {
-                Gpu.Renderer.Buffer.Create(Position, Size);
+                Gpu.Renderer.Buffer.Create(CbKey, Size);
             }
 
             ConstBuffer Cb = ConstBuffers[Stage][Index];

+ 5 - 42
Ryujinx.HLE/Gpu/Memory/NvGpuVmm.cs

@@ -1,7 +1,6 @@
 using ChocolArm64.Memory;
 using Ryujinx.Graphics.Gal;
 using System;
-using System.Collections.Concurrent;
 
 namespace Ryujinx.HLE.Gpu.Memory
 {
@@ -26,18 +25,6 @@ namespace Ryujinx.HLE.Gpu.Memory
 
         public AMemory Memory { get; private set; }
 
-        private struct MappedMemory
-        {
-            public long Size;
-
-            public MappedMemory(long Size)
-            {
-                this.Size = Size;
-            }
-        }
-
-        private ConcurrentDictionary<long, MappedMemory> Maps;
-
         private NvGpuVmmCache Cache;
 
         private const long PteUnmapped = -1;
@@ -49,8 +36,6 @@ namespace Ryujinx.HLE.Gpu.Memory
         {
             this.Memory = Memory;
 
-            Maps = new ConcurrentDictionary<long, MappedMemory>();
-
             Cache = new NvGpuVmmCache();
 
             PageTable = new long[PTLvl0Size][];
@@ -60,14 +45,6 @@ namespace Ryujinx.HLE.Gpu.Memory
         {
             lock (PageTable)
             {
-                for (long Offset = 0; Offset < Size; Offset += PageSize)
-                {
-                    if (GetPte(VA + Offset) != PteReserved)
-                    {
-                        return Map(PA, Size);
-                    }
-                }
-
                 for (long Offset = 0; Offset < Size; Offset += PageSize)
                 {
                     SetPte(VA + Offset, PA + Offset);
@@ -85,10 +62,6 @@ namespace Ryujinx.HLE.Gpu.Memory
 
                 if (VA != -1)
                 {
-                    MappedMemory Map = new MappedMemory(Size);
-
-                    Maps.AddOrUpdate(VA, Map, (Key, Old) => Map);
-
                     for (long Offset = 0; Offset < Size; Offset += PageSize)
                     {
                         SetPte(VA + Offset, PA + Offset);
@@ -99,19 +72,7 @@ namespace Ryujinx.HLE.Gpu.Memory
             }
         }
 
-        public bool Unmap(long VA)
-        {
-            if (Maps.TryRemove(VA, out MappedMemory Map))
-            {
-                Free(VA, Map.Size);
-
-                return true;
-            }
-
-            return false;
-        }
-
-        public long Reserve(long VA, long Size, long Align)
+        public long ReserveFixed(long VA, long Size)
         {
             lock (PageTable)
             {
@@ -119,7 +80,7 @@ namespace Ryujinx.HLE.Gpu.Memory
                 {
                     if (IsPageInUse(VA + Offset))
                     {
-                        return Reserve(Size, Align);
+                        return -1;
                     }
                 }
 
@@ -163,7 +124,9 @@ namespace Ryujinx.HLE.Gpu.Memory
 
         private long GetFreePosition(long Size, long Align = 1)
         {
-            long Position = 0;
+            //Note: Address 0 is not considered valid by the driver,
+            //when 0 is returned it's considered a mapping error.
+            long Position = PageSize;
             long FreeSize = 0;
 
             if (Align < 1)

+ 6 - 8
Ryujinx.HLE/Gpu/Memory/NvGpuVmmCache.cs

@@ -1,4 +1,5 @@
 using ChocolArm64.Memory;
+using Ryujinx.HLE.Memory;
 using System;
 using System.Collections.Generic;
 
@@ -129,14 +130,11 @@ namespace Ryujinx.HLE.Gpu.Memory
         {
             (bool[] Modified, long ModifiedCount) = Memory.IsRegionModified(PA, Size);
 
-            if (Modified == null)
-            {
-                return true;
-            }
+            PA = Memory.GetPhysicalAddress(PA);
 
             ClearCachedPagesIfNeeded();
 
-            long PageSize = Memory.GetHostPageSize();
+            long PageSize = AMemory.PageSize;
 
             EnsureResidencyInitialized(PageSize);
 
@@ -159,9 +157,9 @@ namespace Ryujinx.HLE.Gpu.Memory
 
             while (PA < PAEnd)
             {
-                long Key = PA & ~Mask;
+                long Key = PA & ~AMemory.PageMask;
 
-                long PAPgEnd = Math.Min((PA + PageSize) & ~Mask, PAEnd);
+                long PAPgEnd = Math.Min((PA + AMemory.PageSize) & ~AMemory.PageMask, PAEnd);
 
                 bool IsCached = Cache.TryGetValue(Key, out CachedPage Cp);
 
@@ -228,7 +226,7 @@ namespace Ryujinx.HLE.Gpu.Memory
         {
             if (Residency == null)
             {
-                Residency = new HashSet<long>[AMemoryMgr.RamSize / PageSize];
+                Residency = new HashSet<long>[DeviceMemory.RamSize / PageSize];
 
                 for (int i = 0; i < Residency.Length; i++)
                 {

+ 11 - 11
Ryujinx.HLE/Gpu/Texture/TextureReader.cs

@@ -72,7 +72,7 @@ namespace Ryujinx.HLE.Gpu.Texture
                 {
                     long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
 
-                    byte Pixel = CpuMem.ReadByteUnchecked(Position + Offset);
+                    byte Pixel = CpuMem.ReadByte(Position + Offset);
 
                     *(BuffPtr + OutOffs) = Pixel;
 
@@ -105,7 +105,7 @@ namespace Ryujinx.HLE.Gpu.Texture
                 {
                     long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
 
-                    uint Pixel = (uint)CpuMem.ReadInt16Unchecked(Position + Offset);
+                    uint Pixel = (uint)CpuMem.ReadInt16(Position + Offset);
 
                     Pixel = (Pixel & 0x001f) << 11 |
                             (Pixel & 0x03e0) << 1  |
@@ -143,7 +143,7 @@ namespace Ryujinx.HLE.Gpu.Texture
                 {
                     long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
 
-                    uint Pixel = (uint)CpuMem.ReadInt16Unchecked(Position + Offset);
+                    uint Pixel = (uint)CpuMem.ReadInt16(Position + Offset);
 
                     Pixel = (Pixel & 0x001f) << 11 |
                             (Pixel & 0x07e0)       |
@@ -180,7 +180,7 @@ namespace Ryujinx.HLE.Gpu.Texture
                 {
                     long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
 
-                    short Pixel = CpuMem.ReadInt16Unchecked(Position + Offset);
+                    short Pixel = CpuMem.ReadInt16(Position + Offset);
 
                     *(short*)(BuffPtr + OutOffs) = Pixel;
 
@@ -213,7 +213,7 @@ namespace Ryujinx.HLE.Gpu.Texture
                 {
                     long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
 
-                    int Pixel = CpuMem.ReadInt32Unchecked(Position + Offset);
+                    int Pixel = CpuMem.ReadInt32(Position + Offset);
 
                     *(int*)(BuffPtr + OutOffs) = Pixel;
 
@@ -246,7 +246,7 @@ namespace Ryujinx.HLE.Gpu.Texture
                 {
                     long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
 
-                    long Pixel = CpuMem.ReadInt64Unchecked(Position + Offset);
+                    long Pixel = CpuMem.ReadInt64(Position + Offset);
 
                     *(long*)(BuffPtr + OutOffs) = Pixel;
 
@@ -279,8 +279,8 @@ namespace Ryujinx.HLE.Gpu.Texture
                 {
                     long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
 
-                    long PxLow  = CpuMem.ReadInt64Unchecked(Position + Offset + 0);
-                    long PxHigh = CpuMem.ReadInt64Unchecked(Position + Offset + 8);
+                    long PxLow  = CpuMem.ReadInt64(Position + Offset + 0);
+                    long PxHigh = CpuMem.ReadInt64(Position + Offset + 8);
 
                     *(long*)(BuffPtr + OutOffs + 0) = PxLow;
                     *(long*)(BuffPtr + OutOffs + 8) = PxHigh;
@@ -314,7 +314,7 @@ namespace Ryujinx.HLE.Gpu.Texture
                 {
                     long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
 
-                    long Tile = CpuMem.ReadInt64Unchecked(Position + Offset);
+                    long Tile = CpuMem.ReadInt64(Position + Offset);
 
                     *(long*)(BuffPtr + OutOffs) = Tile;
 
@@ -347,8 +347,8 @@ namespace Ryujinx.HLE.Gpu.Texture
                 {
                     long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
 
-                    long Tile0 = CpuMem.ReadInt64Unchecked(Position + Offset + 0);
-                    long Tile1 = CpuMem.ReadInt64Unchecked(Position + Offset + 8);
+                    long Tile0 = CpuMem.ReadInt64(Position + Offset + 0);
+                    long Tile1 = CpuMem.ReadInt64(Position + Offset + 8);
 
                     *(long*)(BuffPtr + OutOffs + 0) = Tile0;
                     *(long*)(BuffPtr + OutOffs + 8) = Tile1;

+ 1 - 1
Ryujinx.HLE/Gpu/Texture/TextureWriter.cs

@@ -25,7 +25,7 @@ namespace Ryujinx.HLE.Gpu.Texture
 
                     int Pixel = *(int*)(BuffPtr + InOffs);
 
-                    CpuMem.WriteInt32Unchecked(Position + Offset, Pixel);
+                    CpuMem.WriteInt32(Position + Offset, Pixel);
 
                     InOffs += 4;
                 }

+ 74 - 130
Ryujinx.HLE/Hid/Hid.cs

@@ -1,7 +1,4 @@
-using ChocolArm64.Memory;
-using Ryujinx.HLE.Logging;
-using Ryujinx.HLE.OsHle;
-using Ryujinx.HLE.OsHle.Handles;
+using Ryujinx.HLE.OsHle;
 using System;
 
 namespace Ryujinx.HLE.Input
@@ -63,57 +60,18 @@ namespace Ryujinx.HLE.Input
 
         private const int HidEntryCount = 17;
 
-        private Logger Log;
+        private Switch Device;
 
-        private object ShMemLock;
+        private long HidPosition;
 
-        private (AMemory, long, long)[] ShMemPositions;
-
-        public Hid(Logger Log)
+        public Hid(Switch Device, long HidPosition)
         {
-            this.Log = Log;
-
-            ShMemLock = new object();
-
-            ShMemPositions = new (AMemory, long, long)[0];
-        }
+            this.Device      = Device;
+            this.HidPosition = HidPosition;
 
-        internal void ShMemMap(object sender, EventArgs e)
-        {
-            HSharedMem SharedMem = (HSharedMem)sender;
-
-            lock (ShMemLock)
-            {
-                ShMemPositions = SharedMem.GetVirtualPositions();
-
-                (AMemory Memory, long Position, long Size) = ShMemPositions[ShMemPositions.Length - 1];
-
-                for (long Offset = 0; Offset < Horizon.HidSize; Offset += 8)
-                {
-                    Memory.WriteInt64Unchecked(Position + Offset, 0);
-                }
-
-                Log.PrintInfo(LogClass.Hid, $"HID shared memory successfully mapped to 0x{Position:x16}!");
-
-                Init(Memory, Position);
-            }
-        }
+            Device.Memory.FillWithZeros(HidPosition, Horizon.HidSize);
 
-        internal void ShMemUnmap(object sender, EventArgs e)
-        {
-            HSharedMem SharedMem = (HSharedMem)sender;
-
-            lock (ShMemLock)
-            {
-                ShMemPositions = SharedMem.GetVirtualPositions();
-            }
-        }
-
-        private void Init(AMemory Memory, long Position)
-        {
             InitializeJoyconPair(
-                Memory,
-                Position,
                 JoyConColor.Body_Neon_Red,
                 JoyConColor.Buttons_Neon_Red,
                 JoyConColor.Body_Neon_Blue,
@@ -121,14 +79,12 @@ namespace Ryujinx.HLE.Input
         }
 
         private void InitializeJoyconPair(
-            AMemory     Memory,
-            long        Position,
             JoyConColor LeftColorBody,
             JoyConColor LeftColorButtons,
             JoyConColor RightColorBody,
             JoyConColor RightColorButtons)
         {
-            long BaseControllerOffset = Position + HidControllersOffset + 8 * HidControllerSize;
+            long BaseControllerOffset = HidPosition + HidControllersOffset + 8 * HidControllerSize;
 
             HidControllerType Type = HidControllerType.ControllerType_Handheld;
 
@@ -142,20 +98,20 @@ namespace Ryujinx.HLE.Input
 
             HidControllerColorDesc SplitColorDesc = 0;
 
-            Memory.WriteInt32Unchecked(BaseControllerOffset + 0x0,  (int)Type);
+            Device.Memory.WriteInt32(BaseControllerOffset + 0x0,  (int)Type);
 
-            Memory.WriteInt32Unchecked(BaseControllerOffset + 0x4,  IsHalf ? 1 : 0);
+            Device.Memory.WriteInt32(BaseControllerOffset + 0x4,  IsHalf ? 1 : 0);
 
-            Memory.WriteInt32Unchecked(BaseControllerOffset + 0x8,  (int)SingleColorDesc);
-            Memory.WriteInt32Unchecked(BaseControllerOffset + 0xc,  (int)SingleColorBody);
-            Memory.WriteInt32Unchecked(BaseControllerOffset + 0x10, (int)SingleColorButtons);
-            Memory.WriteInt32Unchecked(BaseControllerOffset + 0x14, (int)SplitColorDesc);
+            Device.Memory.WriteInt32(BaseControllerOffset + 0x8,  (int)SingleColorDesc);
+            Device.Memory.WriteInt32(BaseControllerOffset + 0xc,  (int)SingleColorBody);
+            Device.Memory.WriteInt32(BaseControllerOffset + 0x10, (int)SingleColorButtons);
+            Device.Memory.WriteInt32(BaseControllerOffset + 0x14, (int)SplitColorDesc);
 
-            Memory.WriteInt32Unchecked(BaseControllerOffset + 0x18, (int)LeftColorBody);
-            Memory.WriteInt32Unchecked(BaseControllerOffset + 0x1c, (int)LeftColorButtons);
+            Device.Memory.WriteInt32(BaseControllerOffset + 0x18, (int)LeftColorBody);
+            Device.Memory.WriteInt32(BaseControllerOffset + 0x1c, (int)LeftColorButtons);
 
-            Memory.WriteInt32Unchecked(BaseControllerOffset + 0x20, (int)RightColorBody);
-            Memory.WriteInt32Unchecked(BaseControllerOffset + 0x24, (int)RightColorButtons);
+            Device.Memory.WriteInt32(BaseControllerOffset + 0x20, (int)RightColorBody);
+            Device.Memory.WriteInt32(BaseControllerOffset + 0x24, (int)RightColorButtons);
         }
 
         public void SetJoyconButton(
@@ -165,107 +121,95 @@ namespace Ryujinx.HLE.Input
             HidJoystickPosition  LeftStick,
             HidJoystickPosition  RightStick)
         {
-            lock (ShMemLock)
-            {
-                foreach ((AMemory Memory, long Position, long Size) in ShMemPositions)
-                {
-                    long ControllerOffset = Position + HidControllersOffset;
+            long ControllerOffset = HidPosition + HidControllersOffset;
 
-                    ControllerOffset += (int)ControllerId * HidControllerSize;
+            ControllerOffset += (int)ControllerId * HidControllerSize;
 
-                    ControllerOffset += HidControllerHeaderSize;
+            ControllerOffset += HidControllerHeaderSize;
 
-                    ControllerOffset += (int)ControllerLayout * HidControllerLayoutsSize;
+            ControllerOffset += (int)ControllerLayout * HidControllerLayoutsSize;
 
-                    long LastEntry = Memory.ReadInt64Unchecked(ControllerOffset + 0x10);
+            long LastEntry = Device.Memory.ReadInt64(ControllerOffset + 0x10);
 
-                    long CurrEntry = (LastEntry + 1) % HidEntryCount;
+            long CurrEntry = (LastEntry + 1) % HidEntryCount;
 
-                    long Timestamp = GetTimestamp();
+            long Timestamp = GetTimestamp();
 
-                    Memory.WriteInt64Unchecked(ControllerOffset + 0x0,  Timestamp);
-                    Memory.WriteInt64Unchecked(ControllerOffset + 0x8,  HidEntryCount);
-                    Memory.WriteInt64Unchecked(ControllerOffset + 0x10, CurrEntry);
-                    Memory.WriteInt64Unchecked(ControllerOffset + 0x18, HidEntryCount - 1);
+            Device.Memory.WriteInt64(ControllerOffset + 0x0,  Timestamp);
+            Device.Memory.WriteInt64(ControllerOffset + 0x8,  HidEntryCount);
+            Device.Memory.WriteInt64(ControllerOffset + 0x10, CurrEntry);
+            Device.Memory.WriteInt64(ControllerOffset + 0x18, HidEntryCount - 1);
 
-                    ControllerOffset += HidControllersLayoutHeaderSize;
+            ControllerOffset += HidControllersLayoutHeaderSize;
 
-                    long LastEntryOffset = ControllerOffset + LastEntry * HidControllersInputEntrySize;
+            long LastEntryOffset = ControllerOffset + LastEntry * HidControllersInputEntrySize;
 
-                    ControllerOffset += CurrEntry * HidControllersInputEntrySize;
+            ControllerOffset += CurrEntry * HidControllersInputEntrySize;
 
-                    long SampleCounter = Memory.ReadInt64Unchecked(LastEntryOffset) + 1;
+            long SampleCounter = Device.Memory.ReadInt64(LastEntryOffset) + 1;
 
-                    Memory.WriteInt64Unchecked(ControllerOffset + 0x0,  SampleCounter);
-                    Memory.WriteInt64Unchecked(ControllerOffset + 0x8,  SampleCounter);
+            Device.Memory.WriteInt64(ControllerOffset + 0x0, SampleCounter);
+            Device.Memory.WriteInt64(ControllerOffset + 0x8, SampleCounter);
 
-                    Memory.WriteInt64Unchecked(ControllerOffset + 0x10, (uint)Buttons);
+            Device.Memory.WriteInt64(ControllerOffset + 0x10, (uint)Buttons);
 
-                    Memory.WriteInt32Unchecked(ControllerOffset + 0x18, LeftStick.DX);
-                    Memory.WriteInt32Unchecked(ControllerOffset + 0x1c, LeftStick.DY);
+            Device.Memory.WriteInt32(ControllerOffset + 0x18, LeftStick.DX);
+            Device.Memory.WriteInt32(ControllerOffset + 0x1c, LeftStick.DY);
 
-                    Memory.WriteInt32Unchecked(ControllerOffset + 0x20, RightStick.DX);
-                    Memory.WriteInt32Unchecked(ControllerOffset + 0x24, RightStick.DY);
+            Device.Memory.WriteInt32(ControllerOffset + 0x20, RightStick.DX);
+            Device.Memory.WriteInt32(ControllerOffset + 0x24, RightStick.DY);
 
-                    Memory.WriteInt64Unchecked(ControllerOffset + 0x28,
-                        (uint)HidControllerConnState.Controller_State_Connected |
-                        (uint)HidControllerConnState.Controller_State_Wired);
-                }
-            }
+            Device.Memory.WriteInt64(ControllerOffset + 0x28,
+                (uint)HidControllerConnState.Controller_State_Connected |
+                (uint)HidControllerConnState.Controller_State_Wired);
         }
 
         public void SetTouchPoints(params HidTouchPoint[] Points)
         {
-            lock (ShMemLock)
-            {
-                foreach ((AMemory Memory, long Position, long Size) in ShMemPositions)
-                {
-                    long TouchScreenOffset = Position + HidTouchScreenOffset;
-
-                    long LastEntry = Memory.ReadInt64Unchecked(TouchScreenOffset + 0x10);
+            long TouchScreenOffset = HidPosition + HidTouchScreenOffset;
 
-                    long CurrEntry = (LastEntry + 1) % HidEntryCount;
+            long LastEntry = Device.Memory.ReadInt64(TouchScreenOffset + 0x10);
 
-                    long Timestamp = GetTimestamp();
+            long CurrEntry = (LastEntry + 1) % HidEntryCount;
 
-                    Memory.WriteInt64Unchecked(TouchScreenOffset + 0x0,  Timestamp);
-                    Memory.WriteInt64Unchecked(TouchScreenOffset + 0x8,  HidEntryCount);
-                    Memory.WriteInt64Unchecked(TouchScreenOffset + 0x10, CurrEntry);
-                    Memory.WriteInt64Unchecked(TouchScreenOffset + 0x18, HidEntryCount - 1);
-                    Memory.WriteInt64Unchecked(TouchScreenOffset + 0x20, Timestamp);
+            long Timestamp = GetTimestamp();
 
-                    long TouchEntryOffset = TouchScreenOffset + HidTouchHeaderSize;
+            Device.Memory.WriteInt64(TouchScreenOffset + 0x0,  Timestamp);
+            Device.Memory.WriteInt64(TouchScreenOffset + 0x8,  HidEntryCount);
+            Device.Memory.WriteInt64(TouchScreenOffset + 0x10, CurrEntry);
+            Device.Memory.WriteInt64(TouchScreenOffset + 0x18, HidEntryCount - 1);
+            Device.Memory.WriteInt64(TouchScreenOffset + 0x20, Timestamp);
 
-                    long LastEntryOffset = TouchEntryOffset + LastEntry * HidTouchEntrySize;
+            long TouchEntryOffset = TouchScreenOffset + HidTouchHeaderSize;
 
-                    long SampleCounter = Memory.ReadInt64Unchecked(LastEntryOffset) + 1;
+            long LastEntryOffset = TouchEntryOffset + LastEntry * HidTouchEntrySize;
 
-                    TouchEntryOffset += CurrEntry * HidTouchEntrySize;
+            long SampleCounter = Device.Memory.ReadInt64(LastEntryOffset) + 1;
 
-                    Memory.WriteInt64Unchecked(TouchEntryOffset + 0x0, SampleCounter);
-                    Memory.WriteInt64Unchecked(TouchEntryOffset + 0x8, Points.Length);
+            TouchEntryOffset += CurrEntry * HidTouchEntrySize;
 
-                    TouchEntryOffset += HidTouchEntryHeaderSize;
+            Device.Memory.WriteInt64(TouchEntryOffset + 0x0, SampleCounter);
+            Device.Memory.WriteInt64(TouchEntryOffset + 0x8, Points.Length);
 
-                    const int Padding = 0;
+            TouchEntryOffset += HidTouchEntryHeaderSize;
 
-                    int Index = 0;
+            const int Padding = 0;
 
-                    foreach (HidTouchPoint Point in Points)
-                    {
-                        Memory.WriteInt64Unchecked(TouchEntryOffset + 0x0,  Timestamp);
-                        Memory.WriteInt32Unchecked(TouchEntryOffset + 0x8,  Padding);
-                        Memory.WriteInt32Unchecked(TouchEntryOffset + 0xc,  Index++);
-                        Memory.WriteInt32Unchecked(TouchEntryOffset + 0x10, Point.X);
-                        Memory.WriteInt32Unchecked(TouchEntryOffset + 0x14, Point.Y);
-                        Memory.WriteInt32Unchecked(TouchEntryOffset + 0x18, Point.DiameterX);
-                        Memory.WriteInt32Unchecked(TouchEntryOffset + 0x1c, Point.DiameterY);
-                        Memory.WriteInt32Unchecked(TouchEntryOffset + 0x20, Point.Angle);
-                        Memory.WriteInt32Unchecked(TouchEntryOffset + 0x24, Padding);
+            int Index = 0;
 
-                        TouchEntryOffset += HidTouchEntryTouchSize;
-                    }
-                }
+            foreach (HidTouchPoint Point in Points)
+            {
+                Device.Memory.WriteInt64(TouchEntryOffset + 0x0,  Timestamp);
+                Device.Memory.WriteInt32(TouchEntryOffset + 0x8,  Padding);
+                Device.Memory.WriteInt32(TouchEntryOffset + 0xc,  Index++);
+                Device.Memory.WriteInt32(TouchEntryOffset + 0x10, Point.X);
+                Device.Memory.WriteInt32(TouchEntryOffset + 0x14, Point.Y);
+                Device.Memory.WriteInt32(TouchEntryOffset + 0x18, Point.DiameterX);
+                Device.Memory.WriteInt32(TouchEntryOffset + 0x1c, Point.DiameterY);
+                Device.Memory.WriteInt32(TouchEntryOffset + 0x20, Point.Angle);
+                Device.Memory.WriteInt32(TouchEntryOffset + 0x24, Padding);
+
+                TouchEntryOffset += HidTouchEntryTouchSize;
             }
         }
 

+ 30 - 37
Ryujinx.HLE/Loaders/Executable.cs

@@ -1,6 +1,8 @@
 using ChocolArm64.Memory;
 using Ryujinx.HLE.Loaders.Executables;
 using Ryujinx.HLE.OsHle;
+using Ryujinx.HLE.OsHle.Handles;
+using Ryujinx.HLE.OsHle.Utilities;
 using System.Collections.Generic;
 using System.IO;
 
@@ -18,12 +20,14 @@ namespace Ryujinx.HLE.Loaders
 
         public string FilePath { get; private set; }
 
-        private AMemory Memory;
-
         public long ImageBase { get; private set; }
         public long ImageEnd  { get; private set; }
 
-        public Executable(IExecutable Exe, AMemory Memory, long ImageBase)
+        private AMemory Memory;
+
+        private KMemoryManager MemoryManager;
+
+        public Executable(IExecutable Exe, KMemoryManager MemoryManager, AMemory Memory, long ImageBase)
         {
             Dynamic = new List<ElfDyn>();
 
@@ -36,23 +40,34 @@ namespace Ryujinx.HLE.Loaders
                 Name = Path.GetFileNameWithoutExtension(FilePath.Replace(Homebrew.TemporaryNroSuffix, ""));
             }
 
-            this.Memory    = Memory;
-            this.ImageBase = ImageBase;
-            this.ImageEnd  = ImageBase;
+            this.Memory        = Memory;
+            this.MemoryManager = MemoryManager;
+            this.ImageBase     = ImageBase;
+            this.ImageEnd      = ImageBase;
 
-            WriteData(ImageBase + Exe.TextOffset, Exe.Text, MemoryType.CodeStatic,  AMemoryPerm.RX);
-            WriteData(ImageBase + Exe.ROOffset,   Exe.RO,   MemoryType.CodeMutable, AMemoryPerm.Read);
-            WriteData(ImageBase + Exe.DataOffset, Exe.Data, MemoryType.CodeMutable, AMemoryPerm.RW);
+            long TextPosition = ImageBase + (uint)Exe.TextOffset;
+            long ROPosition   = ImageBase + (uint)Exe.ROOffset;
+            long DataPosition = ImageBase + (uint)Exe.DataOffset;
 
-            if (Exe.Mod0Offset == 0)
-            {
-                int BssOffset = Exe.DataOffset + Exe.Data.Length;
-                int BssSize   = Exe.BssSize;
+            long TextSize = (uint)IntUtils.AlignUp(Exe.Text.Length, KMemoryManager.PageSize);
+            long ROSize   = (uint)IntUtils.AlignUp(Exe.RO.Length,   KMemoryManager.PageSize);
+            long DataSize = (uint)IntUtils.AlignUp(Exe.Data.Length, KMemoryManager.PageSize);
+
+            long DataAndBssSize = (uint)IntUtils.AlignUp(Exe.BssSize, KMemoryManager.PageSize) + DataSize;
+
+            ImageEnd = DataPosition + DataAndBssSize;
 
-                MapBss(ImageBase + BssOffset, BssSize);
+            MemoryManager.HleMapProcessCode(TextPosition, TextSize + ROSize + DataAndBssSize);
 
-                ImageEnd = ImageBase + BssOffset + BssSize;
+            MemoryManager.SetProcessMemoryPermission(ROPosition,   ROSize,         MemoryPermission.Read);
+            MemoryManager.SetProcessMemoryPermission(DataPosition, DataAndBssSize, MemoryPermission.ReadAndWrite);
 
+            Memory.WriteBytes(TextPosition, Exe.Text);
+            Memory.WriteBytes(ROPosition,   Exe.RO);
+            Memory.WriteBytes(DataPosition, Exe.Data);
+
+            if (Exe.Mod0Offset == 0)
+            {
                 return;
             }
 
@@ -66,10 +81,6 @@ namespace Ryujinx.HLE.Loaders
             long EhHdrEndOffset   = Memory.ReadInt32(Mod0Offset + 0x14) + Mod0Offset;
             long ModObjOffset     = Memory.ReadInt32(Mod0Offset + 0x18) + Mod0Offset;
 
-            MapBss(BssStartOffset, BssEndOffset - BssStartOffset);
-
-            ImageEnd = BssEndOffset;
-
             while (true)
             {
                 long TagVal = Memory.ReadInt64(DynamicOffset + 0);
@@ -102,24 +113,6 @@ namespace Ryujinx.HLE.Loaders
             }
         }
 
-        private void WriteData(
-            long        Position,
-            byte[]      Data,
-            MemoryType  Type,
-            AMemoryPerm Perm)
-        {
-            Memory.Manager.Map(Position, Data.Length, (int)Type, AMemoryPerm.Write);
-
-            Memory.WriteBytes(Position, Data);
-
-            Memory.Manager.Reprotect(Position, Data.Length, Perm);
-        }
-
-        private void MapBss(long Position, long Size)
-        {
-            Memory.Manager.Map(Position, Size, (int)MemoryType.Normal, AMemoryPerm.RW);
-        }
-
         private ElfRel GetRelocation(long Position)
         {
             long Offset = Memory.ReadInt64(Position + 0);

+ 112 - 0
Ryujinx.HLE/Memory/ArenaAllocator.cs

@@ -0,0 +1,112 @@
+using System.Collections.Generic;
+
+namespace Ryujinx.HLE.Memory
+{
+    class ArenaAllocator
+    {
+        private class Region
+        {
+            public long Position { get; set; }
+            public long Size     { get; set; }
+
+            public Region(long Position, long Size)
+            {
+                this.Position = Position;
+                this.Size     = Size;
+            }
+        }
+
+        private LinkedList<Region> FreeRegions;
+
+        public long TotalAvailableSize { get; private set; }
+        public long TotalUsedSize      { get; private set; }
+
+        public ArenaAllocator(long ArenaSize)
+        {
+            TotalAvailableSize = ArenaSize;
+
+            FreeRegions = new LinkedList<Region>();
+
+            FreeRegions.AddFirst(new Region(0, ArenaSize));
+        }
+
+        public bool TryAllocate(long Size, out long Position)
+        {
+            LinkedListNode<Region> Node = FreeRegions.First;
+
+            while (Node != null)
+            {
+                Region Rg = Node.Value;
+
+                if ((ulong)Rg.Size >= (ulong)Size)
+                {
+                    Position = Rg.Position;
+
+                    Rg.Position += Size;
+                    Rg.Size     -= Size;
+
+                    TotalUsedSize += Size;
+
+                    return true;
+                }
+
+                Node = Node.Next;
+            }
+
+            Position = 0;
+
+            return false;
+        }
+
+        public void Free(long Position, long Size)
+        {
+            long End = Position + Size;
+
+            Region NewRg = new Region(Position, Size);
+
+            LinkedListNode<Region> Node   = FreeRegions.First;
+            LinkedListNode<Region> PrevSz = Node;
+
+            while (Node != null)
+            {
+                LinkedListNode<Region> NextNode = Node.Next;
+
+                Region Rg = Node.Value;
+
+                long RgEnd = Rg.Position + Rg.Size;
+
+                if (Rg.Position == End)
+                {
+                    NewRg.Size += Rg.Size;
+
+                    FreeRegions.Remove(Node);
+                }
+                else if (RgEnd == Position)
+                {
+                    NewRg.Position  = Rg.Position;
+                    NewRg.Size     += Rg.Size;
+
+                    FreeRegions.Remove(Node);
+                }
+                else if ((ulong)Rg.Size < (ulong)NewRg.Size &&
+                         (ulong)Rg.Size > (ulong)PrevSz.Value.Size)
+                {
+                    PrevSz = Node;
+                }
+
+                Node = NextNode;
+            }
+
+            if ((ulong)PrevSz.Value.Size < (ulong)Size)
+            {
+                FreeRegions.AddAfter(PrevSz, NewRg);
+            }
+            else
+            {
+                FreeRegions.AddFirst(NewRg);
+            }
+
+            TotalUsedSize -= Size;
+        }
+    }
+}

+ 130 - 0
Ryujinx.HLE/Memory/DeviceMemory.cs

@@ -0,0 +1,130 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.Memory
+{
+    class DeviceMemory : IDisposable
+    {
+        public const long RamSize = 4L * 1024 * 1024 * 1024;
+
+        public ArenaAllocator Allocator { get; private set; }
+
+        public IntPtr RamPointer { get; private set; }
+
+        private unsafe byte* RamPtr;
+
+        public unsafe DeviceMemory()
+        {
+            Allocator = new ArenaAllocator(RamSize);
+
+            RamPointer = Marshal.AllocHGlobal(new IntPtr(RamSize));
+
+            RamPtr = (byte*)RamPointer;
+        }
+
+        public sbyte ReadSByte(long Position)
+        {
+            return (sbyte)ReadByte(Position);
+        }
+
+        public short ReadInt16(long Position)
+        {
+            return (short)ReadUInt16(Position);
+        }
+
+        public int ReadInt32(long Position)
+        {
+            return (int)ReadUInt32(Position);
+        }
+
+        public long ReadInt64(long Position)
+        {
+            return (long)ReadUInt64(Position);
+        }
+
+        public unsafe byte ReadByte(long Position)
+        {
+            return *((byte*)(RamPtr + Position));
+        }
+
+        public unsafe ushort ReadUInt16(long Position)
+        {
+            return *((ushort*)(RamPtr + Position));
+        }
+
+        public unsafe uint ReadUInt32(long Position)
+        {
+            return *((uint*)(RamPtr + Position));
+        }
+
+        public unsafe ulong ReadUInt64(long Position)
+        {
+            return *((ulong*)(RamPtr + Position));
+        }
+
+        public void WriteSByte(long Position, sbyte Value)
+        {
+            WriteByte(Position, (byte)Value);
+        }
+
+        public void WriteInt16(long Position, short Value)
+        {
+            WriteUInt16(Position, (ushort)Value);
+        }
+
+        public void WriteInt32(long Position, int Value)
+        {
+            WriteUInt32(Position, (uint)Value);
+        }
+
+        public void WriteInt64(long Position, long Value)
+        {
+            WriteUInt64(Position, (ulong)Value);
+        }
+
+        public unsafe void WriteByte(long Position, byte Value)
+        {
+            *((byte*)(RamPtr + Position)) = Value;
+        }
+
+        public unsafe void WriteUInt16(long Position, ushort Value)
+        {
+            *((ushort*)(RamPtr + Position)) = Value;
+        }
+
+        public unsafe void WriteUInt32(long Position, uint Value)
+        {
+            *((uint*)(RamPtr + Position)) = Value;
+        }
+
+        public unsafe void WriteUInt64(long Position, ulong Value)
+        {
+            *((ulong*)(RamPtr + Position)) = Value;
+        }
+
+        public void FillWithZeros(long Position, int Size)
+        {
+            int Size8 = Size & ~(8 - 1);
+
+            for (int Offs = 0; Offs < Size8; Offs += 8)
+            {
+                WriteInt64(Position + Offs, 0);
+            }
+
+            for (int Offs = Size8; Offs < (Size - Size8); Offs++)
+            {
+                WriteByte(Position + Offs, 0);
+            }
+        }
+
+        public void Dispose()
+        {
+            Dispose(true);
+        }
+
+        protected virtual void Dispose(bool Disposing)
+        {
+            Marshal.FreeHGlobal(RamPointer);
+        }
+    }
+}

+ 122 - 0
Ryujinx.HLE/OsHle/Font/SharedFontManager.cs

@@ -0,0 +1,122 @@
+using Ryujinx.HLE.Memory;
+using Ryujinx.HLE.OsHle.Utilities;
+using Ryujinx.HLE.Resource;
+using System.Collections.Generic;
+using System.IO;
+
+namespace Ryujinx.HLE.OsHle.Font
+{
+    class SharedFontManager
+    {
+        private DeviceMemory Memory;
+
+        private long PhysicalAddress;
+
+        private string FontsPath;
+
+        private struct FontInfo
+        {
+            public int Offset;
+            public int Size;
+
+            public FontInfo(int Offset, int Size)
+            {
+                this.Offset = Offset;
+                this.Size   = Size;
+            }
+        }
+
+        private Dictionary<SharedFontType, FontInfo> FontData;
+
+        public SharedFontManager(Switch Device, long PhysicalAddress)
+        {
+            this.PhysicalAddress = PhysicalAddress;
+
+            Memory = Device.Memory;
+
+            FontsPath = Path.Combine(Device.VFs.GetSystemPath(), "fonts");
+        }
+
+        public void EnsureInitialized()
+        {
+            if (FontData == null)
+            {
+                Memory.FillWithZeros(PhysicalAddress, Horizon.FontSize);
+
+                uint FontOffset = 0;
+
+                FontInfo CreateFont(string Name)
+                {
+                    string FontFilePath = Path.Combine(FontsPath, Name + ".ttf");
+
+                    if (File.Exists(FontFilePath))
+                    {
+                        byte[] Data = File.ReadAllBytes(FontFilePath);
+
+                        FontInfo Info = new FontInfo((int)FontOffset, Data.Length);
+
+                        WriteMagicAndSize(PhysicalAddress + FontOffset, Data.Length);
+
+                        FontOffset += 8;
+
+                        uint Start = FontOffset;
+
+                        for (; FontOffset - Start < Data.Length; FontOffset++)
+                        {
+                            Memory.WriteByte(PhysicalAddress + FontOffset, Data[FontOffset - Start]);
+                        }
+
+                        return Info;
+                    }
+                    else
+                    {
+                        throw new InvalidSystemResourceException($"Font \"{Name}.ttf\" not found. Please provide it in \"{FontsPath}\".");
+                    }
+                }
+
+                FontData = new Dictionary<SharedFontType, FontInfo>()
+                {
+                    { SharedFontType.JapanUsEurope,       CreateFont("FontStandard")                  },
+                    { SharedFontType.SimplifiedChinese,   CreateFont("FontChineseSimplified")         },
+                    { SharedFontType.SimplifiedChineseEx, CreateFont("FontExtendedChineseSimplified") },
+                    { SharedFontType.TraditionalChinese,  CreateFont("FontChineseTraditional")        },
+                    { SharedFontType.Korean,              CreateFont("FontKorean")                    },
+                    { SharedFontType.NintendoEx,          CreateFont("FontNintendoExtended")          }
+                };
+
+                if (FontOffset > Horizon.FontSize)
+                {
+                    throw new InvalidSystemResourceException(
+                        $"The sum of all fonts size exceed the shared memory size. " +
+                        $"Please make sure that the fonts don't exceed {Horizon.FontSize} bytes in total. " +
+                        $"(actual size: {FontOffset} bytes).");
+                }
+            }
+        }
+
+        private void WriteMagicAndSize(long Position, int Size)
+        {
+            const int DecMagic = 0x18029a7f;
+            const int Key      = 0x49621806;
+
+            int EncryptedSize = EndianSwap.Swap32(Size ^ Key);
+
+            Memory.WriteInt32(Position + 0, DecMagic);
+            Memory.WriteInt32(Position + 4, EncryptedSize);
+        }
+
+        public int GetFontSize(SharedFontType FontType)
+        {
+            EnsureInitialized();
+
+            return FontData[FontType].Size;
+        }
+
+        public int GetSharedMemoryAddressOffset(SharedFontType FontType)
+        {
+            EnsureInitialized();
+
+            return FontData[FontType].Offset + 8;
+        }
+    }
+}

+ 3 - 2
Ryujinx.HLE/Font/SharedFontType.cs → Ryujinx.HLE/OsHle/Font/SharedFontType.cs

@@ -1,4 +1,4 @@
-namespace Ryujinx.HLE.Font
+namespace Ryujinx.HLE.OsHle.Font
 {
     public enum SharedFontType
     {
@@ -7,6 +7,7 @@ namespace Ryujinx.HLE.Font
         SimplifiedChineseEx = 2,
         TraditionalChinese  = 3,
         Korean              = 4,
-        NintendoEx          = 5
+        NintendoEx          = 5,
+        Count
     }
 }

+ 10 - 0
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
+    }
+}

+ 0 - 44
Ryujinx.HLE/OsHle/Handles/HSharedMem.cs

@@ -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();
-        }
-    }
-}

+ 0 - 21
Ryujinx.HLE/OsHle/Handles/HTransferMem.cs

@@ -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;
-        }
-    }
-}

+ 43 - 0
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);
+        }
+    }
+}

+ 33 - 0
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;
+        }
+    }
+}

+ 1082 - 0
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;
+        }
+    }
+}

+ 14 - 0
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;
+        }
+    }
+}

+ 60 - 0
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--;
+        }
+    }
+}

+ 14 - 0
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;
+        }
+    }
+}

+ 22 - 0
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
+    }
+}

+ 18 - 0
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
+    }
+}

+ 49 - 0
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
+    }
+}

+ 8 - 8
Ryujinx.HLE/OsHle/Homebrew.cs

@@ -10,23 +10,23 @@ namespace Ryujinx.HLE.OsHle
         //http://switchbrew.org/index.php?title=Homebrew_ABI
         public static void WriteHbAbiData(AMemory Memory, long Position, int MainThreadHandle, string SwitchPath)
         {
-            Memory.Manager.Map(Position, AMemoryMgr.PageSize, (int)MemoryType.Normal, AMemoryPerm.RW);
-
-            //MainThreadHandle
+            //MainThreadHandle.
             WriteConfigEntry(Memory, ref Position, 1, 0, MainThreadHandle);
 
-            //NextLoadPath
+            //NextLoadPath.
             WriteConfigEntry(Memory, ref Position, 2, 0, Position + 0x200, Position + 0x400);
 
-            // Argv
+            //Argv.
             long ArgvPosition = Position + 0xC00;
-            WriteConfigEntry(Memory, ref Position, 5, 0, 0, ArgvPosition);
+
             Memory.WriteBytes(ArgvPosition, Encoding.ASCII.GetBytes(SwitchPath + "\0"));
 
-            //AppletType
+            WriteConfigEntry(Memory, ref Position, 5, 0, 0, ArgvPosition);
+
+            //AppletType.
             WriteConfigEntry(Memory, ref Position, 7);
 
-            //EndOfList
+            //EndOfList.
             WriteConfigEntry(Memory, ref Position, 0);
         }
 

+ 36 - 25
Ryujinx.HLE/OsHle/Horizon.cs

@@ -1,6 +1,7 @@
 using Ryujinx.HLE.Loaders.Executables;
 using Ryujinx.HLE.Loaders.Npdm;
 using Ryujinx.HLE.Logging;
+using Ryujinx.HLE.OsHle.Font;
 using Ryujinx.HLE.OsHle.Handles;
 using Ryujinx.HLE.OsHle.SystemState;
 using System;
@@ -12,7 +13,7 @@ namespace Ryujinx.HLE.OsHle
     public class Horizon : IDisposable
     {
         internal const int HidSize  = 0x40000;
-        internal const int FontSize = 0x50;
+        internal const int FontSize = 0x1100000;
 
         private Switch Ns;
 
@@ -22,10 +23,10 @@ namespace Ryujinx.HLE.OsHle
 
         public SystemStateMgr SystemState { get; private set; }
 
-        internal MemoryAllocator Allocator { get; private set; }
+        internal KSharedMemory HidSharedMem  { get; private set; }
+        internal KSharedMemory FontSharedMem { get; private set; }
 
-        internal HSharedMem HidSharedMem  { get; private set; }
-        internal HSharedMem FontSharedMem { get; private set; }
+        internal SharedFontManager Font { get; private set; }
 
         internal KEvent VsyncEvent { get; private set; }
 
@@ -39,10 +40,16 @@ namespace Ryujinx.HLE.OsHle
 
             SystemState = new SystemStateMgr();
 
-            Allocator = new MemoryAllocator();
+            if (!Ns.Memory.Allocator.TryAllocate(HidSize,  out long HidPA) ||
+                !Ns.Memory.Allocator.TryAllocate(FontSize, out long FontPA))
+            {
+                throw new InvalidOperationException();
+            }
+
+            HidSharedMem  = new KSharedMemory(HidPA, HidSize);
+            FontSharedMem = new KSharedMemory(FontPA, FontSize);
 
-            HidSharedMem  = new HSharedMem();
-            FontSharedMem = new HSharedMem();
+            Font = new SharedFontManager(Ns, FontSharedMem.PA);
 
             VsyncEvent = new KEvent();
         }
@@ -54,7 +61,25 @@ namespace Ryujinx.HLE.OsHle
                 Ns.VFs.LoadRomFs(RomFsFile);
             }
 
-            Process MainProcess = MakeProcess();
+            string NpdmFileName = Path.Combine(ExeFsDir, "main.npdm");
+
+            Npdm MetaData = null;
+
+            if (File.Exists(NpdmFileName))
+            {
+                Ns.Log.PrintInfo(LogClass.Loader, $"Loading main.npdm...");
+
+                using (FileStream Input = new FileStream(NpdmFileName, FileMode.Open))
+                {
+                    MetaData = new Npdm(Input);
+                }
+            }
+            else
+            {
+                Ns.Log.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!");
+            }
+
+            Process MainProcess = MakeProcess(MetaData);
 
             void LoadNso(string FileName)
             {
@@ -78,21 +103,7 @@ namespace Ryujinx.HLE.OsHle
                 }
             }
 
-            void LoadNpdm(string FileName)
-            {
-                string File = Directory.GetFiles(ExeFsDir, FileName)[0];
-
-                Ns.Log.PrintInfo(LogClass.Loader, "Loading Title Metadata...");
-
-                using (FileStream Input = new FileStream(File, FileMode.Open))
-                {
-                    MainProcess.Metadata = new Npdm(Input);
-                }
-            }
-
-            LoadNpdm("*.npdm");
-
-            if (!MainProcess.Metadata.Is64Bits)
+            if (!MainProcess.MetaData.Is64Bits)
             {
                 throw new NotImplementedException("32-bit titles are unsupported!");
             }
@@ -145,7 +156,7 @@ namespace Ryujinx.HLE.OsHle
 
         public void SignalVsync() => VsyncEvent.WaitEvent.Set();
 
-        private Process MakeProcess()
+        private Process MakeProcess(Npdm MetaData = null)
         {
             Process Process;
 
@@ -158,7 +169,7 @@ namespace Ryujinx.HLE.OsHle
                     ProcessId++;
                 }
 
-                Process = new Process(Ns, Scheduler, ProcessId);
+                Process = new Process(Ns, Scheduler, ProcessId, MetaData);
 
                 Processes.TryAdd(ProcessId, Process);
             }

+ 16 - 13
Ryujinx.HLE/OsHle/Kernel/KernelErr.cs

@@ -2,18 +2,21 @@ namespace Ryujinx.HLE.OsHle.Kernel
 {
     static class KernelErr
     {
-        public const int InvalidAlignment = 102;
-        public const int InvalidAddress   = 106;
-        public const int InvalidMemRange  = 110;
-        public const int InvalidPriority  = 112;
-        public const int InvalidCoreId    = 113;
-        public const int InvalidHandle    = 114;
-        public const int InvalidCoreMask  = 116;
-        public const int Timeout          = 117;
-        public const int Canceled         = 118;
-        public const int CountOutOfRange  = 119;
-        public const int InvalidEnumValue = 120;
-        public const int InvalidThread    = 122;
-        public const int InvalidState     = 125;
+        public const int InvalidSize       = 101;
+        public const int InvalidAddress    = 102;
+        public const int OutOfMemory       = 104;
+        public const int NoAccessPerm      = 106;
+        public const int InvalidPermission = 108;
+        public const int InvalidMemRange   = 110;
+        public const int InvalidPriority   = 112;
+        public const int InvalidCoreId     = 113;
+        public const int InvalidHandle     = 114;
+        public const int InvalidMaskValue  = 116;
+        public const int Timeout           = 117;
+        public const int Canceled          = 118;
+        public const int CountOutOfRange   = 119;
+        public const int InvalidEnumValue  = 120;
+        public const int InvalidThread     = 122;
+        public const int InvalidState      = 125;
     }
 }

+ 1 - 28
Ryujinx.HLE/OsHle/Kernel/SvcHandler.cs

@@ -10,7 +10,7 @@ using System.Threading;
 
 namespace Ryujinx.HLE.OsHle.Kernel
 {
-    partial class SvcHandler : IDisposable
+    partial class SvcHandler
     {
         private delegate void SvcFunc(AThreadState ThreadState);
 
@@ -22,10 +22,6 @@ namespace Ryujinx.HLE.OsHle.Kernel
 
         private ConcurrentDictionary<KThread, AutoResetEvent> SyncWaits;
 
-        private HashSet<(HSharedMem, long, long)> MappedSharedMems;
-
-        private ulong CurrentHeapSize;
-
         private const uint SelfThreadHandle  = 0xffff8000;
         private const uint SelfProcessHandle = 0xffff8001;
 
@@ -82,8 +78,6 @@ namespace Ryujinx.HLE.OsHle.Kernel
             this.Memory  = Process.Memory;
 
             SyncWaits = new ConcurrentDictionary<KThread, AutoResetEvent>();
-
-            MappedSharedMems = new HashSet<(HSharedMem, long, long)>();
         }
 
         static SvcHandler()
@@ -126,26 +120,5 @@ namespace Ryujinx.HLE.OsHle.Kernel
                 return Process.HandleTable.GetData<KThread>(Handle);
             }
         }
-
-        public void Dispose()
-        {
-            Dispose(true);
-        }
-
-        protected virtual void Dispose(bool Disposing)
-        {
-            if (Disposing)
-            {
-                lock (MappedSharedMems)
-                {
-                    foreach ((HSharedMem SharedMem, long Position, long Size) in MappedSharedMems)
-                    {
-                        SharedMem.RemoveVirtualPosition(Memory, Position, Size);
-                    }
-
-                    MappedSharedMems.Clear();
-                }
-            }
-        }
     }
 }

+ 410 - 117
Ryujinx.HLE/OsHle/Kernel/SvcMemory.cs

@@ -1,4 +1,3 @@
-using ChocolArm64.Memory;
 using ChocolArm64.State;
 using Ryujinx.HLE.Logging;
 using Ryujinx.HLE.OsHle.Handles;
@@ -11,43 +10,85 @@ namespace Ryujinx.HLE.OsHle.Kernel
     {
         private void SvcSetHeapSize(AThreadState ThreadState)
         {
-            uint Size = (uint)ThreadState.X1;
+            long Size = (long)ThreadState.X1;
 
-            long Position = MemoryRegions.HeapRegionAddress;
+            if ((Size & 0x1fffff) != 0 || Size != (uint)Size)
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Heap size 0x{Size:x16} is not aligned!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
+
+                return;
+            }
+
+            long Result = Process.MemoryManager.TrySetHeapSize(Size, out long Position);
 
-            if (Size > CurrentHeapSize)
+            ThreadState.X0 = (ulong)Result;
+
+            if (Result == 0)
             {
-                Memory.Manager.Map(Position, Size, (int)MemoryType.Heap, AMemoryPerm.RW);
+                ThreadState.X1 = (ulong)Position;
             }
             else
             {
-                Memory.Manager.Unmap(Position + Size, (long)CurrentHeapSize - Size);
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
             }
-
-            CurrentHeapSize = Size;
-
-            ThreadState.X0 = 0;
-            ThreadState.X1 = (ulong)Position;
         }
 
         private void SvcSetMemoryAttribute(AThreadState ThreadState)
         {
             long Position = (long)ThreadState.X0;
             long Size     = (long)ThreadState.X1;
-            int  State0   =  (int)ThreadState.X2;
-            int  State1   =  (int)ThreadState.X3;
 
-            if ((State0 == 0 && State1 == 0) ||
-                (State0 == 8 && State1 == 0))
+            if (!PageAligned(Position))
             {
-                Memory.Manager.ClearAttrBit(Position, Size, 3);
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
+
+                return;
             }
-            else if (State0 == 8 && State1 == 8)
+
+            if (!PageAligned(Size) || Size == 0)
             {
-                Memory.Manager.SetAttrBit(Position, Size, 3);
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
+
+                return;
             }
 
-            ThreadState.X0 = 0;
+            MemoryAttribute AttributeMask  = (MemoryAttribute)ThreadState.X2;
+            MemoryAttribute AttributeValue = (MemoryAttribute)ThreadState.X3;
+
+            MemoryAttribute Attributes = AttributeMask | AttributeValue;
+
+            if (Attributes != AttributeMask ||
+               (Attributes | MemoryAttribute.Uncached) != MemoryAttribute.Uncached)
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, "Invalid memory attributes!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue);
+
+                return;
+            }
+
+            long Result = Process.MemoryManager.SetMemoryAttribute(
+                Position,
+                Size,
+                AttributeMask,
+                AttributeValue);
+
+            if (Result != 0)
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
+            }
+            else
+            {
+                Memory.StopObservingRegion(Position, Size);
+            }
+
+            ThreadState.X0 = (ulong)Result;
         }
 
         private void SvcMapMemory(AThreadState ThreadState)
@@ -56,33 +97,59 @@ namespace Ryujinx.HLE.OsHle.Kernel
             long Src  = (long)ThreadState.X1;
             long Size = (long)ThreadState.X2;
 
-            if (!IsValidPosition(Src))
+            if (!PageAligned(Src | Dst))
             {
-                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid src address {Src:x16}!");
+                Ns.Log.PrintWarning(LogClass.KernelSvc, "Addresses are not page aligned!");
 
-                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
 
                 return;
             }
 
-            if (!IsValidMapPosition(Dst))
+            if (!PageAligned(Size) || Size == 0)
             {
-                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid dst address {Dst:x16}!");
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!");
 
-                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
 
                 return;
             }
 
-            AMemoryMapInfo SrcInfo = Memory.Manager.GetMapInfo(Src);
+            if ((ulong)(Src + Size) <= (ulong)Src || (ulong)(Dst + Size) <= (ulong)Dst)
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, "Addresses outside of range!");
 
-            Memory.Manager.Map(Dst, Size, (int)MemoryType.MappedMemory, SrcInfo.Perm);
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
 
-            Memory.Manager.Reprotect(Src, Size, AMemoryPerm.None);
+                return;
+            }
+
+            if (!InsideAddrSpace(Src, Size))
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Src address 0x{Src:x16} out of range!");
 
-            Memory.Manager.SetAttrBit(Src, Size, 0);
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
 
-            ThreadState.X0 = 0;
+                return;
+            }
+
+            if (!InsideNewMapRegion(Dst, Size))
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Dst address 0x{Dst:x16} out of range!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
+
+                return;
+            }
+
+            long Result = Process.MemoryManager.Map(Src, Dst, Size);
+
+            if (Result != 0)
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
+            }
+
+            ThreadState.X0 = (ulong)Result;
         }
 
         private void SvcUnmapMemory(AThreadState ThreadState)
@@ -91,33 +158,59 @@ namespace Ryujinx.HLE.OsHle.Kernel
             long Src  = (long)ThreadState.X1;
             long Size = (long)ThreadState.X2;
 
-            if (!IsValidPosition(Src))
+            if (!PageAligned(Src | Dst))
             {
-                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid src address {Src:x16}!");
+                Ns.Log.PrintWarning(LogClass.KernelSvc, "Addresses are not page aligned!");
 
-                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
 
                 return;
             }
 
-            if (!IsValidMapPosition(Dst))
+            if (!PageAligned(Size) || Size == 0)
             {
-                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid dst address {Dst:x16}!");
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!");
 
-                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
+
+                return;
+            }
+
+            if ((ulong)(Src + Size) <= (ulong)Src || (ulong)(Dst + Size) <= (ulong)Dst)
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, "Addresses outside of range!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
 
                 return;
             }
 
-            AMemoryMapInfo DstInfo = Memory.Manager.GetMapInfo(Dst);
+            if (!InsideAddrSpace(Src, Size))
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Src address 0x{Src:x16} out of range!");
 
-            Memory.Manager.Unmap(Dst, Size, (int)MemoryType.MappedMemory);
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
 
-            Memory.Manager.Reprotect(Src, Size, DstInfo.Perm);
+                return;
+            }
 
-            Memory.Manager.ClearAttrBit(Src, Size, 0);
+            if (!InsideNewMapRegion(Dst, Size))
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Dst address 0x{Dst:x16} out of range!");
 
-            ThreadState.X0 = 0;
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
+
+                return;
+            }
+
+            long Result = Process.MemoryManager.Unmap(Src, Dst, Size);
+
+            if (Result != 0)
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
+            }
+
+            ThreadState.X0 = (ulong)Result;
         }
 
         private void SvcQueryMemory(AThreadState ThreadState)
@@ -125,26 +218,16 @@ namespace Ryujinx.HLE.OsHle.Kernel
             long InfoPtr  = (long)ThreadState.X0;
             long Position = (long)ThreadState.X2;
 
-            AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Position);
-
-            if (MapInfo == null)
-            {
-                long AddrSpaceEnd = MemoryRegions.AddrSpaceStart + MemoryRegions.AddrSpaceSize;
-
-                long ReservedSize = (long)(ulong.MaxValue - (ulong)AddrSpaceEnd) + 1;
+            KMemoryInfo BlkInfo = Process.MemoryManager.QueryMemory(Position);
 
-                MapInfo = new AMemoryMapInfo(AddrSpaceEnd, ReservedSize, (int)MemoryType.Reserved, 0, AMemoryPerm.None);
-            }
-
-            Memory.WriteInt64(InfoPtr + 0x00, MapInfo.Position);
-            Memory.WriteInt64(InfoPtr + 0x08, MapInfo.Size);
-            Memory.WriteInt32(InfoPtr + 0x10, MapInfo.Type);
-            Memory.WriteInt32(InfoPtr + 0x14, MapInfo.Attr);
-            Memory.WriteInt32(InfoPtr + 0x18, (int)MapInfo.Perm);
-            Memory.WriteInt32(InfoPtr + 0x1c, 0);
-            Memory.WriteInt32(InfoPtr + 0x20, 0);
+            Memory.WriteInt64(InfoPtr + 0x00, BlkInfo.Position);
+            Memory.WriteInt64(InfoPtr + 0x08, BlkInfo.Size);
+            Memory.WriteInt32(InfoPtr + 0x10, (int)BlkInfo.State & 0xff);
+            Memory.WriteInt32(InfoPtr + 0x14, (int)BlkInfo.Attribute);
+            Memory.WriteInt32(InfoPtr + 0x18, (int)BlkInfo.Permission);
+            Memory.WriteInt32(InfoPtr + 0x1c, BlkInfo.IpcRefCount);
+            Memory.WriteInt32(InfoPtr + 0x20, BlkInfo.DeviceRefCount);
             Memory.WriteInt32(InfoPtr + 0x24, 0);
-            //TODO: X1.
 
             ThreadState.X0 = 0;
             ThreadState.X1 = 0;
@@ -152,134 +235,344 @@ namespace Ryujinx.HLE.OsHle.Kernel
 
         private void SvcMapSharedMemory(AThreadState ThreadState)
         {
-            int  Handle =  (int)ThreadState.X0;
-            long Src    = (long)ThreadState.X1;
-            long Size   = (long)ThreadState.X2;
-            int  Perm   =  (int)ThreadState.X3;
+            int  Handle   =  (int)ThreadState.X0;
+            long Position = (long)ThreadState.X1;
+            long Size     = (long)ThreadState.X2;
 
-            if (!IsValidPosition(Src))
+            if (!PageAligned(Position))
             {
-                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address {Src:x16}!");
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
 
-                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
+
+                return;
+            }
+
+            if (!PageAligned(Size) || Size == 0)
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
+
+                return;
+            }
+
+            if ((ulong)(Position + Size) <= (ulong)Position)
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
 
                 return;
             }
 
-            HSharedMem SharedMem = Process.HandleTable.GetData<HSharedMem>(Handle);
+            MemoryPermission Permission = (MemoryPermission)ThreadState.X3;
 
-            if (SharedMem != null)
+            if ((Permission | MemoryPermission.Write) != MemoryPermission.ReadAndWrite)
             {
-                Memory.Manager.Map(Src, Size, (int)MemoryType.SharedMemory, AMemoryPerm.Write);
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid permission {Permission}!");
 
-                AMemoryHelper.FillWithZeros(Memory, Src, (int)Size);
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidPermission);
 
-                SharedMem.AddVirtualPosition(Memory, Src, Size);
+                return;
+            }
 
-                Memory.Manager.Reprotect(Src, Size, (AMemoryPerm)Perm);
+            KSharedMemory SharedMemory = Process.HandleTable.GetData<KSharedMemory>(Handle);
+
+            if (SharedMemory == null)
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid shared memory handle 0x{Handle:x8}!");
 
-                lock (MappedSharedMems)
-                {
-                    MappedSharedMems.Add((SharedMem, Src, Size));
-                }
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
+
+                return;
+            }
 
-                ThreadState.X0 = 0;
+            if (!InsideAddrSpace(Position, Size) || InsideMapRegion(Position, Size) || InsideHeapRegion(Position, Size))
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} out of range!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
+
+                return;
+            }
+
+            if (SharedMemory.Size != Size)
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} does not match shared memory size 0x{SharedMemory.Size:16}!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
+
+                return;
+            }
+
+            long Result = Process.MemoryManager.MapSharedMemory(SharedMemory, Permission, Position);
+
+            if (Result != 0)
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
             }
 
-            //TODO: Error codes.
+            ThreadState.X0 = (ulong)Result;
         }
 
         private void SvcUnmapSharedMemory(AThreadState ThreadState)
         {
-            int  Handle =  (int)ThreadState.X0;
-            long Src    = (long)ThreadState.X1;
-            long Size   = (long)ThreadState.X2;
+            int  Handle   =  (int)ThreadState.X0;
+            long Position = (long)ThreadState.X1;
+            long Size     = (long)ThreadState.X2;
 
-            if (!IsValidPosition(Src))
+            if (!PageAligned(Position))
             {
-                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address {Src:x16}!");
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
 
-                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
 
                 return;
             }
 
-            HSharedMem SharedMem = Process.HandleTable.GetData<HSharedMem>(Handle);
+            if (!PageAligned(Size) || Size == 0)
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
+
+                return;
+            }
 
-            if (SharedMem != null)
+            if ((ulong)(Position + Size) <= (ulong)Position)
             {
-                Memory.Manager.Unmap(Src, Size, (int)MemoryType.SharedMemory);
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
 
-                SharedMem.RemoveVirtualPosition(Memory, Src, Size);
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
 
-                lock (MappedSharedMems)
-                {
-                    MappedSharedMems.Remove((SharedMem, Src, Size));
-                }
+                return;
+            }
 
-                ThreadState.X0 = 0;
+            KSharedMemory SharedMemory = Process.HandleTable.GetData<KSharedMemory>(Handle);
+
+            if (SharedMemory == null)
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid shared memory handle 0x{Handle:x8}!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
+
+                return;
             }
 
-            //TODO: Error codes.
+            if (!InsideAddrSpace(Position, Size) || InsideMapRegion(Position, Size) || InsideHeapRegion(Position, Size))
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} out of range!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
+
+                return;
+            }
+
+            long Result = Process.MemoryManager.UnmapSharedMemory(Position, Size);
+
+            if (Result != 0)
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
+            }
+
+            ThreadState.X0 = (ulong)Result;
         }
 
         private void SvcCreateTransferMemory(AThreadState ThreadState)
         {
-            long Src  = (long)ThreadState.X1;
-            long Size = (long)ThreadState.X2;
-            int  Perm =  (int)ThreadState.X3;
+            long Position = (long)ThreadState.X1;
+            long Size     = (long)ThreadState.X2;
 
-            if (!IsValidPosition(Src))
+            if (!PageAligned(Position))
             {
-                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address {Src:x16}!");
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
 
-                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
+
+                return;
+            }
+
+            if (!PageAligned(Size) || Size == 0)
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
 
                 return;
             }
 
-            AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Src);
+            if ((ulong)(Position + Size) <= (ulong)Position)
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
+
+                return;
+            }
+
+            MemoryPermission Permission = (MemoryPermission)ThreadState.X3;
+
+            if (Permission > MemoryPermission.ReadAndWrite || Permission == MemoryPermission.Write)
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid permission {Permission}!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidPermission);
+
+                return;
+            }
 
-            Memory.Manager.Reprotect(Src, Size, (AMemoryPerm)Perm);
+            Process.MemoryManager.ReserveTransferMemory(Position, Size, Permission);
 
-            HTransferMem TMem = new HTransferMem(Memory, MapInfo.Perm, Src, Size);
+            KTransferMemory TransferMemory = new KTransferMemory(Position, Size);
 
-            ulong Handle = (ulong)Process.HandleTable.OpenHandle(TMem);
+            int Handle = Process.HandleTable.OpenHandle(TransferMemory);
 
             ThreadState.X0 = 0;
-            ThreadState.X1 = Handle;
+            ThreadState.X1 = (ulong)Handle;
         }
 
         private void SvcMapPhysicalMemory(AThreadState ThreadState)
         {
             long Position = (long)ThreadState.X0;
-            uint Size     = (uint)ThreadState.X1;
+            long Size     = (long)ThreadState.X1;
+
+            if (!PageAligned(Position))
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
 
-            Memory.Manager.Map(Position, Size, (int)MemoryType.Heap, AMemoryPerm.RW);
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
 
-            ThreadState.X0 = 0;
+                return;
+            }
+
+            if (!PageAligned(Size) || Size == 0)
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
+
+                return;
+            }
+
+            if ((ulong)(Position + Size) <= (ulong)Position)
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
+
+                return;
+            }
+
+            if (!InsideAddrSpace(Position, Size))
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address {Position:x16}!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
+
+                return;
+            }
+
+            long Result = Process.MemoryManager.MapPhysicalMemory(Position, Size);
+
+            if (Result != 0)
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
+            }
+
+            ThreadState.X0 = (ulong)Result;
         }
 
         private void SvcUnmapPhysicalMemory(AThreadState ThreadState)
         {
             long Position = (long)ThreadState.X0;
-            uint Size     = (uint)ThreadState.X1;
+            long Size     = (long)ThreadState.X1;
+
+            if (!PageAligned(Position))
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
 
-            Memory.Manager.Unmap(Position, Size);
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
 
-            ThreadState.X0 = 0;
+                return;
+            }
+
+            if (!PageAligned(Size) || Size == 0)
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
+
+                return;
+            }
+
+            if ((ulong)(Position + Size) <= (ulong)Position)
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
+
+                return;
+            }
+
+            if (!InsideAddrSpace(Position, Size))
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address {Position:x16}!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
+
+                return;
+            }
+
+            long Result = Process.MemoryManager.UnmapPhysicalMemory(Position, Size);
+
+            if (Result != 0)
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
+            }
+
+            ThreadState.X0 = (ulong)Result;
+        }
+
+        private static bool PageAligned(long Position)
+        {
+            return (Position & (KMemoryManager.PageSize - 1)) == 0;
+        }
+
+        private bool InsideAddrSpace(long Position, long Size)
+        {
+            ulong Start = (ulong)Position;
+            ulong End   = (ulong)Size + Start;
+
+            return Start >= (ulong)Process.MemoryManager.AddrSpaceStart &&
+                   End   <  (ulong)Process.MemoryManager.AddrSpaceEnd;
         }
 
-        private static bool IsValidPosition(long Position)
+        private bool InsideMapRegion(long Position, long Size)
         {
-            return Position >= MemoryRegions.AddrSpaceStart &&
-                   Position <  MemoryRegions.AddrSpaceStart + MemoryRegions.AddrSpaceSize;
+            ulong Start = (ulong)Position;
+            ulong End   = (ulong)Size + Start;
+
+            return Start >= (ulong)Process.MemoryManager.MapRegionStart &&
+                   End   <  (ulong)Process.MemoryManager.MapRegionEnd;
         }
 
-        private static bool IsValidMapPosition(long Position)
+        private bool InsideHeapRegion(long Position, long Size)
         {
-            return Position >= MemoryRegions.MapRegionAddress &&
-                   Position <  MemoryRegions.MapRegionAddress + MemoryRegions.MapRegionSize;
+            ulong Start = (ulong)Position;
+            ulong End   = (ulong)Size + Start;
+
+            return Start >= (ulong)Process.MemoryManager.HeapRegionStart &&
+                   End   <  (ulong)Process.MemoryManager.HeapRegionEnd;
+        }
+
+        private bool InsideNewMapRegion(long Position, long Size)
+        {
+            ulong Start = (ulong)Position;
+            ulong End   = (ulong)Size + Start;
+
+            return Start >= (ulong)Process.MemoryManager.NewMapRegionStart &&
+                   End   <  (ulong)Process.MemoryManager.NewMapRegionEnd;
         }
     }
 }

+ 23 - 18
Ryujinx.HLE/OsHle/Kernel/SvcSystem.cs

@@ -18,8 +18,6 @@ namespace Ryujinx.HLE.OsHle.Kernel
 
         private const bool EnableProcessDebugging = false;
 
-        private const bool IsVirtualMemoryEnabled = true; //This is always true(?)
-
         private void SvcExitProcess(AThreadState ThreadState)
         {
             Ns.Os.ExitProcess(ThreadState.ProcessId);
@@ -53,12 +51,11 @@ namespace Ryujinx.HLE.OsHle.Kernel
             {
                 Session.Dispose();
             }
-            else if (Obj is HTransferMem TMem)
+            else if (Obj is KTransferMemory TransferMemory)
             {
-                TMem.Memory.Manager.Reprotect(
-                    TMem.Position,
-                    TMem.Size,
-                    TMem.Perm);
+                Process.MemoryManager.ResetTransferMemory(
+                    TransferMemory.Position,
+                    TransferMemory.Size);
             }
 
             ThreadState.X0 = 0;
@@ -306,27 +303,29 @@ namespace Ryujinx.HLE.OsHle.Kernel
                     break;
 
                 case 2:
-                    ThreadState.X1 = MemoryRegions.MapRegionAddress;
+                    ThreadState.X1 = (ulong)Process.MemoryManager.MapRegionStart;
                     break;
 
                 case 3:
-                    ThreadState.X1 = MemoryRegions.MapRegionSize;
+                    ThreadState.X1 = (ulong)Process.MemoryManager.MapRegionEnd -
+                                     (ulong)Process.MemoryManager.MapRegionStart;
                     break;
 
                 case 4:
-                    ThreadState.X1 = MemoryRegions.HeapRegionAddress;
+                    ThreadState.X1 = (ulong)Process.MemoryManager.HeapRegionStart;
                     break;
 
                 case 5:
-                    ThreadState.X1 = MemoryRegions.HeapRegionSize;
+                    ThreadState.X1 = (ulong)Process.MemoryManager.HeapRegionEnd -
+                                     (ulong)Process.MemoryManager.HeapRegionStart;
                     break;
 
                 case 6:
-                    ThreadState.X1 = MemoryRegions.TotalMemoryAvailable;
+                    ThreadState.X1 = (ulong)Process.Ns.Memory.Allocator.TotalAvailableSize;
                     break;
 
                 case 7:
-                    ThreadState.X1 = MemoryRegions.TotalMemoryUsed + CurrentHeapSize;
+                    ThreadState.X1 = (ulong)Process.Ns.Memory.Allocator.TotalUsedSize;
                     break;
 
                 case 8:
@@ -338,23 +337,29 @@ namespace Ryujinx.HLE.OsHle.Kernel
                     break;
 
                 case 12:
-                    ThreadState.X1 = MemoryRegions.AddrSpaceStart;
+                    ThreadState.X1 = (ulong)Process.MemoryManager.AddrSpaceStart;
                     break;
 
                 case 13:
-                    ThreadState.X1 = MemoryRegions.AddrSpaceSize;
+                    ThreadState.X1 = (ulong)Process.MemoryManager.AddrSpaceEnd -
+                                     (ulong)Process.MemoryManager.AddrSpaceStart;
                     break;
 
                 case 14:
-                    ThreadState.X1 = MemoryRegions.MapRegionAddress;
+                    ThreadState.X1 = (ulong)Process.MemoryManager.NewMapRegionStart;
                     break;
 
                 case 15:
-                    ThreadState.X1 = MemoryRegions.MapRegionSize;
+                    ThreadState.X1 = (ulong)Process.MemoryManager.NewMapRegionEnd -
+                                     (ulong)Process.MemoryManager.NewMapRegionStart;
                     break;
 
                 case 16:
-                    ThreadState.X1 = IsVirtualMemoryEnabled ? 1 : 0;
+                    ThreadState.X1 = (ulong)(Process.MetaData?.SystemResourceSize ?? 0);
+                    break;
+
+                case 17:
+                    ThreadState.X1 = (ulong)Process.MemoryManager.PersonalMmHeapUsage;
                     break;
 
                 default:

+ 2 - 2
Ryujinx.HLE/OsHle/Kernel/SvcThread.cs

@@ -204,7 +204,7 @@ namespace Ryujinx.HLE.OsHle.Kernel
                 {
                     Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid core mask 0x{CoreMask:x8}!");
 
-                    ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidCoreMask);
+                    ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue);
 
                     return;
                 }
@@ -226,7 +226,7 @@ namespace Ryujinx.HLE.OsHle.Kernel
             {
                 Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid core mask 0x{CoreMask:x8}!");
 
-                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidCoreMask);
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue);
 
                 return;
             }

+ 8 - 8
Ryujinx.HLE/OsHle/Kernel/SvcThreadSync.cs

@@ -26,7 +26,7 @@ namespace Ryujinx.HLE.OsHle.Kernel
             {
                 Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
 
-                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
 
                 return;
             }
@@ -35,7 +35,7 @@ namespace Ryujinx.HLE.OsHle.Kernel
             {
                 Ns.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!");
 
-                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAlignment);
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
 
                 return;
             }
@@ -79,7 +79,7 @@ namespace Ryujinx.HLE.OsHle.Kernel
             {
                 Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
 
-                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
 
                 return;
             }
@@ -88,7 +88,7 @@ namespace Ryujinx.HLE.OsHle.Kernel
             {
                 Ns.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!");
 
-                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAlignment);
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
 
                 return;
             }
@@ -115,7 +115,7 @@ namespace Ryujinx.HLE.OsHle.Kernel
             {
                 Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
 
-                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
 
                 return;
             }
@@ -124,7 +124,7 @@ namespace Ryujinx.HLE.OsHle.Kernel
             {
                 Ns.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!");
 
-                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAlignment);
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
 
                 return;
             }
@@ -214,7 +214,7 @@ namespace Ryujinx.HLE.OsHle.Kernel
             {
                 Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address 0x{Address:x16}!");
 
-                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
 
                 return;
             }
@@ -223,7 +223,7 @@ namespace Ryujinx.HLE.OsHle.Kernel
             {
                 Ns.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned address 0x{Address:x16}!");
 
-                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAlignment);
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
 
                 return;
             }

+ 0 - 12
Ryujinx.HLE/OsHle/MemoryAllocator.cs

@@ -1,12 +0,0 @@
-using System;
-
-namespace Ryujinx.HLE.OsHle
-{
-    class MemoryAllocator
-    {
-        public bool TryAllocate(long Size, out long Address)
-        {
-            throw new NotImplementedException();
-        }
-    }
-}

+ 0 - 29
Ryujinx.HLE/OsHle/MemoryRegions.cs

@@ -1,29 +0,0 @@
-using ChocolArm64.Memory;
-
-namespace Ryujinx.HLE.OsHle
-{
-    static class MemoryRegions
-    {
-        public const long AddrSpaceStart = 0x08000000;
-
-        public const long MapRegionAddress = 0x10000000;
-        public const long MapRegionSize    = 0x20000000;
-
-        public const long HeapRegionAddress = MapRegionAddress + MapRegionSize;
-        public const long HeapRegionSize    = TlsPagesAddress - HeapRegionAddress;
-
-        public const long MainStackSize = 0x100000;
-
-        public const long MainStackAddress = AMemoryMgr.AddrSize - MainStackSize;
-
-        public const long TlsPagesSize = 0x20000;
-
-        public const long TlsPagesAddress = MainStackAddress - TlsPagesSize;
-
-        public const long TotalMemoryUsed = HeapRegionAddress + TlsPagesSize + MainStackSize;
-
-        public const long TotalMemoryAvailable = AMemoryMgr.RamSize - AddrSpaceStart;
-
-        public const long AddrSpaceSize = AMemoryMgr.AddrSize - AddrSpaceStart;
-    }
-}

+ 71 - 64
Ryujinx.HLE/OsHle/Process.cs

@@ -12,6 +12,7 @@ using Ryujinx.HLE.OsHle.Handles;
 using Ryujinx.HLE.OsHle.Kernel;
 using Ryujinx.HLE.OsHle.Services.Nv;
 using Ryujinx.HLE.OsHle.SystemState;
+using Ryujinx.HLE.OsHle.Utilities;
 using System;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
@@ -22,13 +23,9 @@ namespace Ryujinx.HLE.OsHle
 {
     class Process : IDisposable
     {
-        private const int TlsSize = 0x200;
-
-        private const int TotalTlsSlots = (int)MemoryRegions.TlsPagesSize / TlsSize;
-
         private const int TickFreq = 19_200_000;
 
-        private Switch Ns;
+        public Switch Ns { get; private set; }
 
         public bool NeedsHbAbi { get; private set; }
 
@@ -40,22 +37,24 @@ namespace Ryujinx.HLE.OsHle
 
         public AMemory Memory { get; private set; }
 
+        public KMemoryManager MemoryManager { get; private set; }
+
+        private List<KTlsPageManager> TlsPages;
+
         public KProcessScheduler Scheduler { get; private set; }
 
         public List<KThread> ThreadArbiterList { get; private set; }
 
         public object ThreadSyncLock { get; private set; }
 
+        public Npdm MetaData { get; private set; }
+
         public KProcessHandleTable HandleTable { get; private set; }
 
         public AppletStateMgr AppletState { get; private set; }
 
-        public Npdm Metadata { get; set; }
-
         private SvcHandler SvcHandler;
 
-        private ConcurrentDictionary<int, AThread> TlsSlots;
-
         private ConcurrentDictionary<long, KThread> Threads;
 
         private KThread MainThread;
@@ -70,13 +69,18 @@ namespace Ryujinx.HLE.OsHle
 
         private bool Disposed;
 
-        public Process(Switch Ns, KProcessScheduler Scheduler, int ProcessId)
+        public Process(Switch Ns, KProcessScheduler Scheduler, int ProcessId, Npdm MetaData)
         {
             this.Ns        = Ns;
             this.Scheduler = Scheduler;
+            this.MetaData  = MetaData;
             this.ProcessId = ProcessId;
 
-            Memory = new AMemory();
+            Memory = new AMemory(Ns.Memory.RamPointer);
+
+            MemoryManager = new KMemoryManager(this);
+
+            TlsPages = new List<KTlsPageManager>();
 
             ThreadArbiterList = new List<KThread>();
 
@@ -88,18 +92,11 @@ namespace Ryujinx.HLE.OsHle
 
             SvcHandler = new SvcHandler(Ns, this);
 
-            TlsSlots = new ConcurrentDictionary<int, AThread>();
-
             Threads = new ConcurrentDictionary<long, KThread>();
 
             Executables = new List<Executable>();
 
-            ImageBase = MemoryRegions.AddrSpaceStart;
-
-            MapRWMemRegion(
-                MemoryRegions.TlsPagesAddress,
-                MemoryRegions.TlsPagesSize,
-                MemoryType.ThreadLocal);
+            ImageBase = MemoryManager.CodeRegionStart;
         }
 
         public void LoadProgram(IExecutable Program)
@@ -111,17 +108,17 @@ namespace Ryujinx.HLE.OsHle
 
             Ns.Log.PrintInfo(LogClass.Loader, $"Image base at 0x{ImageBase:x16}.");
 
-            Executable Executable = new Executable(Program, Memory, ImageBase);
+            Executable Executable = new Executable(Program, MemoryManager, Memory, ImageBase);
 
             Executables.Add(Executable);
 
-            ImageBase = AMemoryHelper.PageRoundUp(Executable.ImageEnd);
+            ImageBase = IntUtils.AlignUp(Executable.ImageEnd, KMemoryManager.PageSize);
         }
 
         public void SetEmptyArgs()
         {
             //TODO: This should be part of Run.
-            ImageBase += AMemoryMgr.PageSize;
+            ImageBase += KMemoryManager.PageSize;
         }
 
         public bool Run(bool NeedsHbAbi = false)
@@ -140,14 +137,19 @@ namespace Ryujinx.HLE.OsHle
 
             MakeSymbolTable();
 
-            MapRWMemRegion(
-                MemoryRegions.MainStackAddress,
-                MemoryRegions.MainStackSize,
-                MemoryType.Normal);
+            long MainStackTop = MemoryManager.CodeRegionEnd - KMemoryManager.PageSize;
+
+            long MainStackSize = 1 * 1024 * 1024;
 
-            long StackTop = MemoryRegions.MainStackAddress + MemoryRegions.MainStackSize;
+            long MainStackBottom = MainStackTop - MainStackSize;
 
-            int Handle = MakeThread(Executables[0].ImageBase, StackTop, 0, 44, 0);
+            MemoryManager.HleMapCustom(
+                MainStackBottom,
+                MainStackSize,
+                MemoryState.MappedMemory,
+                MemoryPermission.ReadAndWrite);
+
+            int Handle = MakeThread(Executables[0].ImageBase, MainStackTop, 0, 44, 0);
 
             if (Handle == -1)
             {
@@ -158,7 +160,15 @@ namespace Ryujinx.HLE.OsHle
 
             if (NeedsHbAbi)
             {
-                HbAbiDataPosition = AMemoryHelper.PageRoundUp(Executables[0].ImageEnd);
+                HbAbiDataPosition = IntUtils.AlignUp(Executables[0].ImageEnd, KMemoryManager.PageSize);
+
+                const long HbAbiDataSize = KMemoryManager.PageSize;
+
+                MemoryManager.HleMapCustom(
+                    HbAbiDataPosition,
+                    HbAbiDataSize,
+                    MemoryState.MappedMemory,
+                    MemoryPermission.ReadAndWrite);
 
                 string SwitchPath = Ns.VFs.SystemPathToSwitchPath(Executables[0].FilePath);
 
@@ -173,11 +183,6 @@ namespace Ryujinx.HLE.OsHle
             return true;
         }
 
-        private void MapRWMemRegion(long Position, long Size, MemoryType Type)
-        {
-            Memory.Manager.Map(Position, Size, (int)Type, AMemoryPerm.RW);
-        }
-
         public void StopAllThreadsAsync()
         {
             if (Disposed)
@@ -190,9 +195,9 @@ namespace Ryujinx.HLE.OsHle
                 MainThread.Thread.StopExecution();
             }
 
-            foreach (AThread Thread in TlsSlots.Values)
+            foreach (KThread Thread in Threads.Values)
             {
-                Thread.StopExecution();
+                Thread.Thread.StopExecution();
             }
         }
 
@@ -216,9 +221,9 @@ namespace Ryujinx.HLE.OsHle
 
             int Handle = HandleTable.OpenHandle(Thread);
 
-            int ThreadId = GetFreeTlsSlot(CpuThread);
+            long Tpidr = GetFreeTls();
 
-            long Tpidr = MemoryRegions.TlsPagesAddress + ThreadId * TlsSize;
+            int ThreadId = (int)((Tpidr - MemoryManager.TlsIoRegionStart) / 0x200) + 1;
 
             CpuThread.ThreadState.ProcessId = ProcessId;
             CpuThread.ThreadState.ThreadId  = ThreadId;
@@ -240,6 +245,32 @@ namespace Ryujinx.HLE.OsHle
             return Handle;
         }
 
+        private long GetFreeTls()
+        {
+            long Position;
+
+            lock (TlsPages)
+            {
+                for (int Index = 0; Index < TlsPages.Count; Index++)
+                {
+                    if (TlsPages[Index].TryGetFreeTlsAddr(out Position))
+                    {
+                        return Position;
+                    }
+                }
+
+                long PagePosition = MemoryManager.HleMapTlsPage();
+
+                KTlsPageManager TlsPage = new KTlsPageManager(PagePosition);
+
+                TlsPages.Add(TlsPage);
+
+                TlsPage.TryGetFreeTlsAddr(out Position);
+            }
+
+            return Position;
+        }
+
         private void BreakHandler(object sender, AInstExceptionEventArgs e)
         {
             throw new GuestBrokeExecutionException();
@@ -346,25 +377,10 @@ namespace Ryujinx.HLE.OsHle
             return Name;
         }
 
-        private int GetFreeTlsSlot(AThread Thread)
-        {
-            for (int Index = 1; Index < TotalTlsSlots; Index++)
-            {
-                if (TlsSlots.TryAdd(Index, Thread))
-                {
-                    return Index;
-                }
-            }
-
-            throw new InvalidOperationException();
-        }
-
         private void ThreadFinished(object sender, EventArgs e)
         {
             if (sender is AThread Thread)
             {
-                TlsSlots.TryRemove(GetTlsSlot(Thread.ThreadState.Tpidr), out _);
-
                 Threads.TryRemove(Thread.ThreadState.Tpidr, out KThread KernelThread);
 
                 Scheduler.RemoveThread(KernelThread);
@@ -372,7 +388,7 @@ namespace Ryujinx.HLE.OsHle
                 KernelThread.WaitEvent.Set();
             }
 
-            if (TlsSlots.Count == 0)
+            if (Threads.Count == 0)
             {
                 if (ShouldDispose)
                 {
@@ -383,11 +399,6 @@ namespace Ryujinx.HLE.OsHle
             }
         }
 
-        private int GetTlsSlot(long Position)
-        {
-            return (int)((Position - MemoryRegions.TlsPagesAddress) / TlsSize);
-        }
-
         public KThread GetThread(long Tpidr)
         {
             if (!Threads.TryGetValue(Tpidr, out KThread Thread))
@@ -411,7 +422,7 @@ namespace Ryujinx.HLE.OsHle
                 //safe as the thread may try to access those resources. Instead, we set
                 //the flag to have the Process disposed when all threads finishes.
                 //Note: This may not happen if the guest code gets stuck on a infinite loop.
-                if (TlsSlots.Count > 0)
+                if (Threads.Count > 0)
                 {
                     ShouldDispose = true;
 
@@ -439,10 +450,6 @@ namespace Ryujinx.HLE.OsHle
 
                 AppletState.Dispose();
 
-                SvcHandler.Dispose();
-
-                Memory.Dispose();
-
                 Ns.Log.PrintInfo(LogClass.Loader, $"Process {ProcessId} exiting...");
             }
         }

+ 2 - 2
Ryujinx.HLE/OsHle/Services/Hid/IAppletResource.cs

@@ -10,9 +10,9 @@ namespace Ryujinx.HLE.OsHle.Services.Hid
 
         public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
-        private HSharedMem HidSharedMem;
+        private KSharedMemory HidSharedMem;
 
-        public IAppletResource(HSharedMem HidSharedMem)
+        public IAppletResource(KSharedMemory HidSharedMem)
         {
             m_Commands = new Dictionary<int, ServiceProcessRequest>()
             {

+ 198 - 0
Ryujinx.HLE/OsHle/Services/Nv/NvGpuAS/NvGpuASCtx.cs

@@ -0,0 +1,198 @@
+using ChocolArm64.Memory;
+using Ryujinx.HLE.Gpu.Memory;
+using System.Collections.Generic;
+
+namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS
+{
+    class NvGpuASCtx
+    {
+        public NvGpuVmm Vmm { get; private set; }
+
+        private class Range
+        {
+            public ulong Start  { get; private set; }
+            public ulong End    { get; private set; }
+
+            public Range(long Position, long Size)
+            {
+                Start = (ulong)Position;
+                End   = (ulong)Size + Start;
+            }
+        }
+
+        private class MappedMemory : Range
+        {
+            public long PhysicalAddress { get; private set; }
+            public bool VaAllocated  { get; private set; }
+
+            public MappedMemory(
+                long Position,
+                long Size,
+                long PhysicalAddress,
+                bool VaAllocated) : base(Position, Size)
+            {
+                this.PhysicalAddress = PhysicalAddress;
+                this.VaAllocated     = VaAllocated;
+            }
+        }
+
+        private SortedList<long, Range> Maps;
+        private SortedList<long, Range> Reservations;
+
+        public NvGpuASCtx(ServiceCtx Context)
+        {
+            Vmm = new NvGpuVmm(Context.Memory);
+
+            Maps         = new SortedList<long, Range>();
+            Reservations = new SortedList<long, Range>();
+        }
+
+        public bool ValidateFixedBuffer(long Position, long Size)
+        {
+            long MapEnd = Position + Size;
+
+            //Check if size is valid (0 is also not allowed).
+            if ((ulong)MapEnd <= (ulong)Position)
+            {
+                return false;
+            }
+
+            //Check if address is page aligned.
+            if ((Position & NvGpuVmm.PageMask) != 0)
+            {
+                return false;
+            }
+
+            //Check if region is reserved.
+            if (BinarySearch(Reservations, Position) == null)
+            {
+                return false;
+            }
+
+            //Check for overlap with already mapped buffers.
+            Range Map = BinarySearchLt(Maps, MapEnd);
+
+            if (Map != null && Map.End > (ulong)Position)
+            {
+                return false;
+            }
+
+            return true;
+        }
+
+        public void AddMap(
+            long Position,
+            long Size,
+            long PhysicalAddress,
+            bool VaAllocated)
+        {
+            Maps.Add(Position, new MappedMemory(Position, Size, PhysicalAddress, VaAllocated));
+        }
+
+        public bool RemoveMap(long Position, out long Size)
+        {
+            Size = 0;
+
+            if (Maps.Remove(Position, out Range Value))
+            {
+                MappedMemory Map = (MappedMemory)Value;
+
+                if (Map.VaAllocated)
+                {
+                    Size = (long)(Map.End - Map.Start);
+                }
+
+                return true;
+            }
+
+            return false;
+        }
+
+        public bool TryGetMapPhysicalAddress(long Position, out long PhysicalAddress)
+        {
+            Range Map = BinarySearch(Maps, Position);
+
+            if (Map != null)
+            {
+                PhysicalAddress = ((MappedMemory)Map).PhysicalAddress;
+
+                return true;
+            }
+
+            PhysicalAddress = 0;
+
+            return false;
+        }
+
+        public void AddReservation(long Position, long Size)
+        {
+            Reservations.Add(Position, new Range(Position, Size));
+        }
+
+        public bool RemoveReservation(long Position)
+        {
+            return Reservations.Remove(Position);
+        }
+
+        private Range BinarySearch(SortedList<long, Range> Lst, long Position)
+        {
+            int Left  = 0;
+            int Right = Lst.Count - 1;
+
+            while (Left <= Right)
+            {
+                int Size = Right - Left;
+
+                int Middle = Left + (Size >> 1);
+
+                Range Rg = Lst.Values[Middle];
+
+                if ((ulong)Position >= Rg.Start && (ulong)Position < Rg.End)
+                {
+                    return Rg;
+                }
+
+                if ((ulong)Position < Rg.Start)
+                {
+                    Right = Middle - 1;
+                }
+                else
+                {
+                    Left = Middle + 1;
+                }
+            }
+
+            return null;
+        }
+
+        private Range BinarySearchLt(SortedList<long, Range> Lst, long Position)
+        {
+            Range LtRg = null;
+
+            int Left  = 0;
+            int Right = Lst.Count - 1;
+
+            while (Left <= Right)
+            {
+                int Size = Right - Left;
+
+                int Middle = Left + (Size >> 1);
+
+                Range Rg = Lst.Values[Middle];
+
+                if ((ulong)Position < Rg.Start)
+                {
+                    Right = Middle - 1;
+                }
+                else
+                {
+                    Left = Middle + 1;
+
+                    LtRg = Rg;
+                }
+            }
+
+            return LtRg;
+        }
+    }
+}

+ 148 - 62
Ryujinx.HLE/OsHle/Services/Nv/NvGpuAS/NvGpuASIoctl.cs

@@ -11,11 +11,13 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS
     {
         private const int FlagFixedOffset = 1;
 
-        private static ConcurrentDictionary<Process, NvGpuVmm> Vmms;
+        private const int FlagRemapSubRange = 0x100;
+
+        private static ConcurrentDictionary<Process, NvGpuASCtx> ASCtxs;
 
         static NvGpuASIoctl()
         {
-            Vmms = new ConcurrentDictionary<Process, NvGpuVmm>();
+            ASCtxs = new ConcurrentDictionary<Process, NvGpuASCtx>();
         }
 
         public static int ProcessIoctl(ServiceCtx Context, int Cmd)
@@ -29,7 +31,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS
                 case 0x4106: return MapBufferEx (Context);
                 case 0x4108: return GetVaRegions(Context);
                 case 0x4109: return InitializeEx(Context);
-                case 0x4114: return Remap       (Context);
+                case 0x4114: return Remap       (Context, Cmd);
             }
 
             throw new NotImplementedException(Cmd.ToString("x8"));
@@ -52,29 +54,38 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS
 
             NvGpuASAllocSpace Args = AMemoryHelper.Read<NvGpuASAllocSpace>(Context.Memory, InputPosition);
 
-            NvGpuVmm Vmm = GetVmm(Context);
+            NvGpuASCtx ASCtx = GetASCtx(Context);
 
             ulong Size = (ulong)Args.Pages *
                          (ulong)Args.PageSize;
 
-            if ((Args.Flags & FlagFixedOffset) != 0)
-            {
-                Args.Offset = Vmm.Reserve(Args.Offset, (long)Size, 1);
-            }
-            else
-            {
-                Args.Offset = Vmm.Reserve((long)Size, 1);
-            }
-
             int Result = NvResult.Success;
 
-            if (Args.Offset < 0)
+            lock (ASCtx)
             {
-                Args.Offset = 0;
+                //Note: When the fixed offset flag is not set,
+                //the Offset field holds the alignment size instead.
+                if ((Args.Flags & FlagFixedOffset) != 0)
+                {
+                    Args.Offset = ASCtx.Vmm.ReserveFixed(Args.Offset, (long)Size);
+                }
+                else
+                {
+                    Args.Offset = ASCtx.Vmm.Reserve((long)Size, Args.Offset);
+                }
 
-                Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"No memory to allocate size {Size:x16}!");
+                if (Args.Offset < 0)
+                {
+                    Args.Offset = 0;
 
-                Result = NvResult.OutOfMemory;
+                    Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Failed to allocate size {Size:x16}!");
+
+                    Result = NvResult.OutOfMemory;
+                }
+                else
+                {
+                    ASCtx.AddReservation(Args.Offset, (long)Size);
+                }
             }
 
             AMemoryHelper.Write(Context.Memory, OutputPosition, Args);
@@ -89,14 +100,29 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS
 
             NvGpuASAllocSpace Args = AMemoryHelper.Read<NvGpuASAllocSpace>(Context.Memory, InputPosition);
 
-            NvGpuVmm Vmm = GetVmm(Context);
+            NvGpuASCtx ASCtx = GetASCtx(Context);
 
-            ulong Size = (ulong)Args.Pages *
-                         (ulong)Args.PageSize;
+            int Result = NvResult.Success;
 
-            Vmm.Free(Args.Offset, (long)Size);
+            lock (ASCtx)
+            {
+                ulong Size = (ulong)Args.Pages *
+                             (ulong)Args.PageSize;
 
-            return NvResult.Success;
+                if (ASCtx.RemoveReservation(Args.Offset))
+                {
+                    ASCtx.Vmm.Free(Args.Offset, (long)Size);
+                }
+                else
+                {
+                    Context.Ns.Log.PrintWarning(LogClass.ServiceNv,
+                        $"Failed to free offset 0x{Args.Offset:x16} size 0x{Size:x16}!");
+
+                    Result = NvResult.InvalidInput;
+                }
+            }
+
+            return Result;
         }
 
         private static int UnmapBuffer(ServiceCtx Context)
@@ -106,11 +132,21 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS
 
             NvGpuASUnmapBuffer Args = AMemoryHelper.Read<NvGpuASUnmapBuffer>(Context.Memory, InputPosition);
 
-            NvGpuVmm Vmm = GetVmm(Context);
+            NvGpuASCtx ASCtx = GetASCtx(Context);
 
-            if (!Vmm.Unmap(Args.Offset))
+            lock (ASCtx)
             {
-                Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid buffer offset {Args.Offset:x16}!");
+                if (ASCtx.RemoveMap(Args.Offset, out long Size))
+                {
+                    if (Size != 0)
+                    {
+                        ASCtx.Vmm.Free(Args.Offset, Size);
+                    }
+                }
+                else
+                {
+                    Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid buffer offset {Args.Offset:x16}!");
+                }
             }
 
             return NvResult.Success;
@@ -118,12 +154,14 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS
 
         private static int MapBufferEx(ServiceCtx Context)
         {
+            const string MapErrorMsg = "Failed to map fixed buffer with offset 0x{0:x16} and size 0x{1:x16}!";
+
             long InputPosition  = Context.Request.GetBufferType0x21().Position;
             long OutputPosition = Context.Request.GetBufferType0x22().Position;
 
             NvGpuASMapBufferEx Args = AMemoryHelper.Read<NvGpuASMapBufferEx>(Context.Memory, InputPosition);
 
-            NvGpuVmm Vmm = GetVmm(Context);
+            NvGpuASCtx ASCtx = GetASCtx(Context);
 
             NvMapHandle Map = NvMapIoctl.GetNvMapWithFb(Context, Args.NvMapHandle);
 
@@ -134,7 +172,39 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS
                 return NvResult.InvalidInput;
             }
 
-            long PA = Map.Address + Args.BufferOffset;
+            long PA;
+
+            if ((Args.Flags & FlagRemapSubRange) != 0)
+            {
+                lock (ASCtx)
+                {
+                    if (ASCtx.TryGetMapPhysicalAddress(Args.Offset, out PA))
+                    {
+                        long VA = Args.Offset + Args.BufferOffset;
+
+                        PA += Args.BufferOffset;
+
+                        if (ASCtx.Vmm.Map(PA, VA, Args.MappingSize) < 0)
+                        {
+                            string Msg = string.Format(MapErrorMsg, VA, Args.MappingSize);
+
+                            Context.Ns.Log.PrintWarning(LogClass.ServiceNv, Msg);
+
+                            return NvResult.InvalidInput;
+                        }
+
+                        return NvResult.Success;
+                    }
+                    else
+                    {
+                        Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Address 0x{Args.Offset:x16} not mapped!");
+
+                        return NvResult.InvalidInput;
+                    }
+                }
+            }
+
+            PA = Map.Address + Args.BufferOffset;
 
             long Size = Args.MappingSize;
 
@@ -145,40 +215,44 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS
 
             int Result = NvResult.Success;
 
-            //Note: When the fixed offset flag is not set,
-            //the Offset field holds the alignment size instead.
-            if ((Args.Flags & FlagFixedOffset) != 0)
+            lock (ASCtx)
             {
-                long MapEnd = Args.Offset + Args.MappingSize;
+                //Note: When the fixed offset flag is not set,
+                //the Offset field holds the alignment size instead.
+                bool VaAllocated = (Args.Flags & FlagFixedOffset) == 0;
 
-                if ((ulong)MapEnd <= (ulong)Args.Offset)
+                if (!VaAllocated)
                 {
-                    Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Offset 0x{Args.Offset:x16} and size 0x{Args.MappingSize:x16} results in a overflow!");
-
-                    return NvResult.InvalidInput;
+                    if (ASCtx.ValidateFixedBuffer(Args.Offset, Size))
+                    {
+                        Args.Offset = ASCtx.Vmm.Map(PA, Args.Offset, Size);
+                    }
+                    else
+                    {
+                        string Msg = string.Format(MapErrorMsg, Args.Offset, Size);
+
+                        Context.Ns.Log.PrintWarning(LogClass.ServiceNv, Msg);
+
+                        Result = NvResult.InvalidInput;
+                    }
                 }
-
-                if ((Args.Offset & NvGpuVmm.PageMask) != 0)
+                else
                 {
-                    Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Offset 0x{Args.Offset:x16} is not page aligned!");
-
-                    return NvResult.InvalidInput;
+                    Args.Offset = ASCtx.Vmm.Map(PA, Size);
                 }
 
-                Args.Offset = Vmm.Map(PA, Args.Offset, Size);
-            }
-            else
-            {
-                Args.Offset = Vmm.Map(PA, Size);
-
                 if (Args.Offset < 0)
                 {
                     Args.Offset = 0;
 
-                    Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"No memory to map size {Args.MappingSize:x16}!");
+                    Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Failed to map size 0x{Size:x16}!");
 
                     Result = NvResult.InvalidInput;
                 }
+                else
+                {
+                    ASCtx.AddMap(Args.Offset, Size, PA, VaAllocated);
+                }
             }
 
             AMemoryHelper.Write(Context.Memory, OutputPosition, Args);
@@ -206,38 +280,50 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS
             return NvResult.Success;
         }
 
-        private static int Remap(ServiceCtx Context)
+        private static int Remap(ServiceCtx Context, int Cmd)
         {
+            int Count = ((Cmd >> 16) & 0xff) / 0x14;
+
             long InputPosition  = Context.Request.GetBufferType0x21().Position;
 
-            NvGpuASRemap Args = AMemoryHelper.Read<NvGpuASRemap>(Context.Memory, InputPosition);
+            for (int Index = 0; Index < Count; Index++, InputPosition += 0x14)
+            {
+                NvGpuASRemap Args = AMemoryHelper.Read<NvGpuASRemap>(Context.Memory, InputPosition);
 
-            NvGpuVmm Vmm = GetVmm(Context);
+                NvGpuVmm Vmm = GetASCtx(Context).Vmm;
 
-            NvMapHandle Map = NvMapIoctl.GetNvMapWithFb(Context, Args.NvMapHandle);
+                NvMapHandle Map = NvMapIoctl.GetNvMapWithFb(Context, Args.NvMapHandle);
 
-            if (Map == null)
-            {
-                Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap handle 0x{Args.NvMapHandle:x8}!");
+                if (Map == null)
+                {
+                    Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap handle 0x{Args.NvMapHandle:x8}!");
 
-                return NvResult.InvalidInput;
-            }
+                    return NvResult.InvalidInput;
+                }
 
-            //FIXME: This is most likely wrong...
-            Vmm.Map(Map.Address, (long)(uint)Args.Offset << 16,
-                                 (long)(uint)Args.Pages  << 16);
+                long Result = Vmm.Map(Map.Address, (long)(uint)Args.Offset << 16,
+                                                   (long)(uint)Args.Pages  << 16);
+
+                if (Result < 0)
+                {
+                    Context.Ns.Log.PrintWarning(LogClass.ServiceNv,
+                        $"Page 0x{Args.Offset:x16} size 0x{Args.Pages:x16} not allocated!");
+
+                    return NvResult.InvalidInput;
+                }
+            }
 
             return NvResult.Success;
         }
 
-        public static NvGpuVmm GetVmm(ServiceCtx Context)
+        public static NvGpuASCtx GetASCtx(ServiceCtx Context)
         {
-            return Vmms.GetOrAdd(Context.Process, (Key) => new NvGpuVmm(Context.Memory));
+            return ASCtxs.GetOrAdd(Context.Process, (Key) => new NvGpuASCtx(Context));
         }
 
         public static void UnloadProcess(Process Process)
         {
-            Vmms.TryRemove(Process, out _);
+            ASCtxs.TryRemove(Process, out _);
         }
     }
 }

+ 2 - 2
Ryujinx.HLE/OsHle/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs

@@ -88,7 +88,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvHostChannel
 
             NvHostChannelSubmitGpfifo Args = AMemoryHelper.Read<NvHostChannelSubmitGpfifo>(Context.Memory, InputPosition);
 
-            NvGpuVmm Vmm = NvGpuASIoctl.GetVmm(Context);
+            NvGpuVmm Vmm = NvGpuASIoctl.GetASCtx(Context).Vmm;;
 
             for (int Index = 0; Index < Args.NumEntries; Index++)
             {
@@ -162,7 +162,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvHostChannel
 
             NvHostChannelSubmitGpfifo Args = AMemoryHelper.Read<NvHostChannelSubmitGpfifo>(Context.Memory, InputPosition);
 
-            NvGpuVmm Vmm = NvGpuASIoctl.GetVmm(Context);
+            NvGpuVmm Vmm = NvGpuASIoctl.GetASCtx(Context).Vmm;;
 
             for (int Index = 0; Index < Args.NumEntries; Index++)
             {

+ 1 - 1
Ryujinx.HLE/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs

@@ -120,7 +120,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvHostCtrl
 
                     Context.Ns.Log.PrintDebug(Logging.LogClass.ServiceNv, $"Got setting {Domain}!{Name}");
                 }
-                
+
                 return NvResult.Success;
             }
 

+ 1 - 1
Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapFree.cs

@@ -4,7 +4,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvMap
     {
         public int  Handle;
         public int  Padding;
-        public long RefCount;
+        public long Address;
         public int  Size;
         public int  Flags;
     }

+ 1 - 1
Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapHandle.cs

@@ -31,7 +31,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvMap
 
         public long DecrementRefCount()
         {
-            return Interlocked.Decrement(ref Dupes) + 1;
+            return Interlocked.Decrement(ref Dupes);
         }
     }
 }

+ 8 - 8
Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapIoctl.cs

@@ -129,7 +129,8 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvMap
                 {
                     //When the address is zero, we need to allocate
                     //our own backing memory for the NvMap.
-                    if (!Context.Ns.Os.Allocator.TryAllocate((uint)Size, out Address))
+                    //TODO: Is this allocation inside the transfer memory?
+                    if (!Context.Ns.Memory.Allocator.TryAllocate((uint)Size, out Address))
                     {
                         Result = NvResult.OutOfMemory;
                     }
@@ -163,23 +164,22 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvMap
                 return NvResult.InvalidInput;
             }
 
-            long OldRefCount = Map.DecrementRefCount();
-
-            if (OldRefCount <= 1)
+            if (Map.DecrementRefCount() <= 0)
             {
                 DeleteNvMap(Context, Args.Handle);
 
                 Context.Ns.Log.PrintInfo(LogClass.ServiceNv, $"Deleted map {Args.Handle}!");
 
-                Args.Flags = 0;
+                Args.Address = Map.Address;
+                Args.Flags   = 0;
             }
             else
             {
-                Args.Flags = FlagNotFreedYet;
+                Args.Address = 0;
+                Args.Flags   = FlagNotFreedYet;
             }
 
-            Args.RefCount = OldRefCount;
-            Args.Size     = Map.Size;
+            Args.Size = Map.Size;
 
             AMemoryHelper.Write(Context.Memory, OutputPosition, Args);
 

+ 43 - 35
Ryujinx.HLE/OsHle/Services/Pl/ISharedFontManager.cs

@@ -1,4 +1,4 @@
-using Ryujinx.HLE.Font;
+using Ryujinx.HLE.OsHle.Font;
 using Ryujinx.HLE.OsHle.Ipc;
 using System.Collections.Generic;
 
@@ -27,8 +27,8 @@ namespace Ryujinx.HLE.OsHle.Services.Pl
         {
             SharedFontType FontType = (SharedFontType)Context.RequestData.ReadInt32();
 
-            Context.Ns.Font.Load(FontType);
-
+            //We don't need to do anything here because we do lazy initialization
+            //on SharedFontManager (the font is loaded when necessary).
             return 0;
         }
 
@@ -36,7 +36,9 @@ namespace Ryujinx.HLE.OsHle.Services.Pl
         {
             SharedFontType FontType = (SharedFontType)Context.RequestData.ReadInt32();
 
-            Context.ResponseData.Write(Context.Ns.Font.GetLoadState(FontType));
+            //1 (true) indicates that the font is already loaded.
+            //All fonts are already loaded.
+            Context.ResponseData.Write(1);
 
             return 0;
         }
@@ -45,7 +47,7 @@ namespace Ryujinx.HLE.OsHle.Services.Pl
         {
             SharedFontType FontType = (SharedFontType)Context.RequestData.ReadInt32();
 
-            Context.ResponseData.Write(Context.Ns.Font.GetFontSize(FontType));
+            Context.ResponseData.Write(Context.Ns.Os.Font.GetFontSize(FontType));
 
             return 0;
         }
@@ -54,13 +56,15 @@ namespace Ryujinx.HLE.OsHle.Services.Pl
         {
             SharedFontType FontType = (SharedFontType)Context.RequestData.ReadInt32();
 
-            Context.ResponseData.Write(Context.Ns.Font.GetSharedMemoryAddressOffset(FontType));
+            Context.ResponseData.Write(Context.Ns.Os.Font.GetSharedMemoryAddressOffset(FontType));
 
             return 0;
         }
 
         public long GetSharedMemoryNativeHandle(ServiceCtx Context)
         {
+            Context.Ns.Os.Font.EnsureInitialized();
+
             int Handle = Context.Process.HandleTable.OpenHandle(Context.Ns.Os.FontSharedMem);
 
             Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
@@ -68,50 +72,54 @@ namespace Ryujinx.HLE.OsHle.Services.Pl
             return 0;
         }
 
-        private uint AddFontToOrderOfPriorityList(ServiceCtx Context, SharedFontType FontType, uint BufferPos, out uint LoadState)
+        public long GetSharedFontInOrderOfPriority(ServiceCtx Context)
         {
-            long TypesPosition          = Context.Request.ReceiveBuff[0].Position;
-            long TypesSize              = Context.Request.ReceiveBuff[0].Size;
+            long LanguageCode = Context.RequestData.ReadInt64();
+            int  LoadedCount  = 0;
 
-            long OffsetsPosition        = Context.Request.ReceiveBuff[1].Position;
-            long OffsetsSize            = Context.Request.ReceiveBuff[1].Size;
-
-            long FontSizeBufferPosition = Context.Request.ReceiveBuff[2].Position;
-            long FontSizeBufferSize     = Context.Request.ReceiveBuff[2].Size;
+            for (SharedFontType Type = 0; Type < SharedFontType.Count; Type++)
+            {
+                int Offset = (int)Type * 4;
 
-            LoadState                   = Context.Ns.Font.GetLoadState(FontType);
+                if (!AddFontToOrderOfPriorityList(Context, (SharedFontType)Type, Offset))
+                {
+                    break;
+                }
 
-            if (BufferPos >= TypesSize || BufferPos >= OffsetsSize || BufferPos >= FontSizeBufferSize)
-            {
-                return 0;
+                LoadedCount++;
             }
 
-            Context.Memory.WriteUInt32(TypesPosition + BufferPos, (uint)FontType);
-            Context.Memory.WriteUInt32(OffsetsPosition + BufferPos, Context.Ns.Font.GetSharedMemoryAddressOffset(FontType));
-            Context.Memory.WriteUInt32(FontSizeBufferPosition + BufferPos, Context.Ns.Font.GetFontSize(FontType));
-
-            BufferPos += 4;
+            Context.ResponseData.Write(LoadedCount);
+            Context.ResponseData.Write((int)SharedFontType.Count);
 
-            return BufferPos;
+            return 0;
         }
 
-        public long GetSharedFontInOrderOfPriority(ServiceCtx Context)
+        private bool AddFontToOrderOfPriorityList(ServiceCtx Context, SharedFontType FontType, int Offset)
         {
-            ulong LanguageCode = Context.RequestData.ReadUInt64();
-            uint  LoadedCount  = 0;
-            uint  BufferPos    = 0;
-            uint  Loaded       = 0;
+            long TypesPosition = Context.Request.ReceiveBuff[0].Position;
+            long TypesSize     = Context.Request.ReceiveBuff[0].Size;
+
+            long OffsetsPosition = Context.Request.ReceiveBuff[1].Position;
+            long OffsetsSize     = Context.Request.ReceiveBuff[1].Size;
+
+            long FontSizeBufferPosition = Context.Request.ReceiveBuff[2].Position;
+            long FontSizeBufferSize     = Context.Request.ReceiveBuff[2].Size;
 
-            for (int Type = 0; Type < Context.Ns.Font.Count; Type++)
+            if ((uint)Offset + 4 > (uint)TypesSize   ||
+                (uint)Offset + 4 > (uint)OffsetsSize ||
+                (uint)Offset + 4 > (uint)FontSizeBufferSize)
             {
-                BufferPos   = AddFontToOrderOfPriorityList(Context, (SharedFontType)Type, BufferPos, out Loaded);
-                LoadedCount += Loaded;
+                return false;
             }
 
-            Context.ResponseData.Write(LoadedCount);
-            Context.ResponseData.Write(Context.Ns.Font.Count);
+            Context.Memory.WriteInt32(TypesPosition + Offset, (int)FontType);
 
-            return 0;
+            Context.Memory.WriteInt32(OffsetsPosition + Offset, Context.Ns.Os.Font.GetSharedMemoryAddressOffset(FontType));
+
+            Context.Memory.WriteInt32(FontSizeBufferPosition + Offset, Context.Ns.Os.Font.GetFontSize(FontType));
+
+            return true;
         }
     }
 }

+ 1 - 1
Ryujinx.HLE/OsHle/Services/Vi/NvFlinger.cs

@@ -159,7 +159,7 @@ namespace Ryujinx.HLE.OsHle.Services.Android
 
             int Slot = GetFreeSlotBlocking(Width, Height);
 
-            return MakeReplyParcel(Context, Slot, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+            return MakeReplyParcel(Context, Slot, 1, 0x24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
         }
 
         private long GbpQueueBuffer(ServiceCtx Context, BinaryReader ParcelReader)

+ 10 - 0
Ryujinx.HLE/OsHle/Utilities/IntUtils.cs

@@ -11,5 +11,15 @@ namespace Ryujinx.HLE.OsHle.Utilities
         {
             return (Value + (Size - 1)) & ~((long)Size - 1);
         }
+
+        public static int AlignDown(int Value, int Size)
+        {
+            return Value & ~(Size - 1);
+        }
+
+        public static long AlignDown(long Value, int Size)
+        {
+            return Value & ~((long)Size - 1);
+        }
     }
 }

+ 0 - 1
Ryujinx.HLE/PerformanceStatistics.cs

@@ -1,7 +1,6 @@
 using System.Diagnostics;
 using System.Timers;
 
-
 namespace Ryujinx.HLE
 {
     public class PerformanceStatistics

+ 6 - 12
Ryujinx.HLE/Switch.cs

@@ -1,9 +1,9 @@
 using Ryujinx.Audio;
 using Ryujinx.Graphics.Gal;
-using Ryujinx.HLE.Font;
 using Ryujinx.HLE.Gpu;
 using Ryujinx.HLE.Input;
 using Ryujinx.HLE.Logging;
+using Ryujinx.HLE.Memory;
 using Ryujinx.HLE.OsHle;
 using System;
 
@@ -15,6 +15,8 @@ namespace Ryujinx.HLE
 
         public Logger Log { get; private set; }
 
+        internal DeviceMemory Memory { get; private set; }
+
         internal NvGpu Gpu { get; private set; }
 
         internal VirtualFileSystem VFs { get; private set; }
@@ -25,8 +27,6 @@ namespace Ryujinx.HLE
 
         public Hid Hid { get; private set; }
 
-        public SharedFontManager Font { get; private set; }
-
         public event EventHandler Finish;
 
         public Switch(IGalRenderer Renderer, IAalOutput AudioOut)
@@ -45,6 +45,8 @@ namespace Ryujinx.HLE
 
             Log = new Logger();
 
+            Memory = new DeviceMemory();
+
             Gpu = new NvGpu(Renderer);
 
             VFs = new VirtualFileSystem();
@@ -53,15 +55,7 @@ namespace Ryujinx.HLE
 
             Statistics = new PerformanceStatistics();
 
-            Hid = new Hid(Log);
-
-            Font = new SharedFontManager(Log, VFs.GetSystemPath());
-
-            Os.HidSharedMem.MemoryMapped    += Hid.ShMemMap;
-            Os.HidSharedMem.MemoryUnmapped  += Hid.ShMemUnmap;
-
-            Os.FontSharedMem.MemoryMapped   += Font.ShMemMap;
-            Os.FontSharedMem.MemoryUnmapped += Font.ShMemUnmap;
+            Hid = new Hid(this, Os.HidSharedMem.PA);
         }
 
         public void LoadCart(string ExeFsDir, string RomFsFile = null)

+ 8 - 4
Ryujinx.Tests/Cpu/CpuTest.cs

@@ -5,6 +5,7 @@ using ChocolArm64.State;
 using NUnit.Framework;
 
 using System;
+using System.Runtime.InteropServices;
 using System.Runtime.Intrinsics;
 using System.Runtime.Intrinsics.X86;
 using System.Threading;
@@ -19,6 +20,8 @@ namespace Ryujinx.Tests.Cpu
 
         private long EntryPoint;
 
+        private IntPtr RamPointer;
+
         private AMemory Memory;
         private AThread Thread;
 
@@ -31,15 +34,16 @@ namespace Ryujinx.Tests.Cpu
             EntryPoint = Position;
 
             ATranslator Translator = new ATranslator();
-            Memory = new AMemory();
-            Memory.Manager.Map(Position, Size, 2, AMemoryPerm.Read | AMemoryPerm.Write | AMemoryPerm.Execute);
+            RamPointer = Marshal.AllocHGlobal(new IntPtr(Size));
+            Memory = new AMemory(RamPointer);
+            Memory.Map(Position, 0, Size);
             Thread = new AThread(Translator, Memory, EntryPoint);
         }
 
         [TearDown]
         public void Teardown()
         {
-            Memory.Dispose();
+            Marshal.FreeHGlobal(RamPointer);
             Memory = null;
             Thread = null;
         }
@@ -52,7 +56,7 @@ namespace Ryujinx.Tests.Cpu
 
         protected void Opcode(uint Opcode)
         {
-            Thread.Memory.WriteUInt32Unchecked(Position, Opcode);
+            Thread.Memory.WriteUInt32(Position, Opcode);
             Position += 4;
         }
 

+ 0 - 2
Ryujinx/Config.cs

@@ -23,8 +23,6 @@ namespace Ryujinx
 
             IniParser Parser = new IniParser(IniPath);
 
-            AOptimizations.DisableMemoryChecks = !Convert.ToBoolean(Parser.Value("Enable_Memory_Checks"));
-
             GraphicsConfig.ShadersDumpPath = Parser.Value("Graphics_Shaders_Dump_Path");
 
             Device.Log.SetEnable(LogLevel.Debug,   Convert.ToBoolean(Parser.Value("Logging_Enable_Debug")));