diff --git a/ARMeilleure/State/ExecutionContext.cs b/ARMeilleure/State/ExecutionContext.cs
index 9f89bff458..a964f6baa8 100644
--- a/ARMeilleure/State/ExecutionContext.cs
+++ b/ARMeilleure/State/ExecutionContext.cs
@@ -66,7 +66,7 @@ namespace ARMeilleure.State
             }
         }
 
-        internal bool Running { get; private set; }
+        public bool Running { get; private set; }
 
         public event EventHandler<EventArgs>              Interrupt;
         public event EventHandler<InstExceptionEventArgs> Break;
diff --git a/Ryujinx.Audio.Renderer/Dsp/Command/AuxiliaryBufferCommand.cs b/Ryujinx.Audio.Renderer/Dsp/Command/AuxiliaryBufferCommand.cs
index f63722c128..46aff27e71 100644
--- a/Ryujinx.Audio.Renderer/Dsp/Command/AuxiliaryBufferCommand.cs
+++ b/Ryujinx.Audio.Renderer/Dsp/Command/AuxiliaryBufferCommand.cs
@@ -16,7 +16,7 @@
 //
 
 using Ryujinx.Audio.Renderer.Common;
-using Ryujinx.Cpu;
+using Ryujinx.Memory;
 using System;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
@@ -65,7 +65,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
             IsEffectEnabled = isEnabled;
         }
 
-        private uint Read(MemoryManager memoryManager, ulong bufferAddress, uint countMax, Span<int> outBuffer, uint count, uint readOffset, uint updateCount)
+        private uint Read(IVirtualMemoryManager memoryManager, ulong bufferAddress, uint countMax, Span<int> outBuffer, uint count, uint readOffset, uint updateCount)
         {
             if (countMax == 0 || bufferAddress == 0)
             {
@@ -104,7 +104,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
             return count;
         }
 
-        private uint Write(MemoryManager memoryManager, ulong outBufferAddress, uint countMax, ReadOnlySpan<int> buffer, uint count, uint writeOffset, uint updateCount)
+        private uint Write(IVirtualMemoryManager memoryManager, ulong outBufferAddress, uint countMax, ReadOnlySpan<int> buffer, uint count, uint writeOffset, uint updateCount)
         {
             if (countMax == 0 || outBufferAddress == 0)
             {
@@ -175,8 +175,8 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
             }
             else
             {
-                MemoryHelper.FillWithZeros(context.MemoryManager, (long)BufferInfo.SendBufferInfo, Unsafe.SizeOf<AuxiliaryBufferInfo>());
-                MemoryHelper.FillWithZeros(context.MemoryManager, (long)BufferInfo.ReturnBufferInfo, Unsafe.SizeOf<AuxiliaryBufferInfo>());
+                ZeroFill(context.MemoryManager, BufferInfo.SendBufferInfo, Unsafe.SizeOf<AuxiliaryBufferInfo>());
+                ZeroFill(context.MemoryManager, BufferInfo.ReturnBufferInfo, Unsafe.SizeOf<AuxiliaryBufferInfo>());
 
                 if (InputBufferIndex != OutputBufferIndex)
                 {
@@ -184,5 +184,22 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
                 }
             }
         }
+
+        private static void ZeroFill(IVirtualMemoryManager memoryManager, ulong address, int size)
+        {
+            ulong endAddress = address + (ulong)size;
+
+            while (address + 7UL < endAddress)
+            {
+                memoryManager.Write(address, 0UL);
+                address += 8;
+            }
+
+            while (address < endAddress)
+            {
+                memoryManager.Write(address, (byte)0);
+                address++;
+            }
+        }
     }
 }
diff --git a/Ryujinx.Audio.Renderer/Dsp/Command/CommandList.cs b/Ryujinx.Audio.Renderer/Dsp/Command/CommandList.cs
index d65f6cedab..4b80c93b2f 100644
--- a/Ryujinx.Audio.Renderer/Dsp/Command/CommandList.cs
+++ b/Ryujinx.Audio.Renderer/Dsp/Command/CommandList.cs
@@ -19,7 +19,7 @@ using Ryujinx.Audio.Renderer.Integration;
 using Ryujinx.Audio.Renderer.Server;
 using Ryujinx.Common;
 using Ryujinx.Common.Logging;
-using Ryujinx.Cpu;
+using Ryujinx.Memory;
 using System;
 using System.Collections.Generic;
 
@@ -37,7 +37,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
 
         public List<ICommand> Commands { get; }
 
-        public MemoryManager MemoryManager { get; }
+        public IVirtualMemoryManager MemoryManager { get; }
 
         public HardwareDevice OutputDevice { get; private set; }
 
@@ -50,7 +50,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
         {
         }
 
-        public CommandList(MemoryManager memoryManager, Memory<float> mixBuffer, uint sampleCount, uint sampleRate, uint mixBufferCount, uint voiceChannelCountMax)
+        public CommandList(IVirtualMemoryManager memoryManager, Memory<float> mixBuffer, uint sampleCount, uint sampleRate, uint mixBufferCount, uint voiceChannelCountMax)
         {
             SampleCount = sampleCount;
             SampleRate = sampleRate;
diff --git a/Ryujinx.Audio.Renderer/Dsp/DataSourceHelper.cs b/Ryujinx.Audio.Renderer/Dsp/DataSourceHelper.cs
index ccc97b924a..6e270df453 100644
--- a/Ryujinx.Audio.Renderer/Dsp/DataSourceHelper.cs
+++ b/Ryujinx.Audio.Renderer/Dsp/DataSourceHelper.cs
@@ -18,7 +18,7 @@
 using Ryujinx.Audio.Renderer.Common;
 using Ryujinx.Audio.Renderer.Dsp.State;
 using Ryujinx.Common.Logging;
-using Ryujinx.Cpu;
+using Ryujinx.Memory;
 using System;
 using System.Buffers;
 using System.Diagnostics;
@@ -63,7 +63,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
             }
         }
 
-        public static void ProcessWaveBuffers(MemoryManager memoryManager, Span<float> outputBuffer, WaveBufferInformation info, uint targetSampleRate, int sampleCount)
+        public static void ProcessWaveBuffers(IVirtualMemoryManager memoryManager, Span<float> outputBuffer, WaveBufferInformation info, uint targetSampleRate, int sampleCount)
         {
             const int tempBufferSize = 0x3F00;
 
diff --git a/Ryujinx.Audio.Renderer/Dsp/State/AuxiliaryBufferHeader.cs b/Ryujinx.Audio.Renderer/Dsp/State/AuxiliaryBufferHeader.cs
index eebbac53eb..3923a490d0 100644
--- a/Ryujinx.Audio.Renderer/Dsp/State/AuxiliaryBufferHeader.cs
+++ b/Ryujinx.Audio.Renderer/Dsp/State/AuxiliaryBufferHeader.cs
@@ -15,7 +15,7 @@
 // along with this program.  If not, see <https://www.gnu.org/licenses/>.
 //
 
-using Ryujinx.Cpu;
+using Ryujinx.Memory;
 using System.Runtime.InteropServices;
 
 namespace Ryujinx.Audio.Renderer.Dsp.State
@@ -33,22 +33,22 @@ namespace Ryujinx.Audio.Renderer.Dsp.State
             public uint WriteOffset;
             private uint _reserved;
 
-            public static uint GetReadOffset(MemoryManager manager, ulong bufferAddress)
+            public static uint GetReadOffset(IVirtualMemoryManager manager, ulong bufferAddress)
             {
                 return manager.Read<uint>(bufferAddress + ReadOffsetPosition);
             }
 
-            public static uint GetWriteOffset(MemoryManager manager, ulong bufferAddress)
+            public static uint GetWriteOffset(IVirtualMemoryManager manager, ulong bufferAddress)
             {
                 return manager.Read<uint>(bufferAddress + WriteOffsetPosition);
             }
 
-            public static void SetReadOffset(MemoryManager manager, ulong bufferAddress, uint value)
+            public static void SetReadOffset(IVirtualMemoryManager manager, ulong bufferAddress, uint value)
             {
                 manager.Write(bufferAddress + ReadOffsetPosition, value);
             }
 
-            public static void SetWriteOffset(MemoryManager manager, ulong bufferAddress, uint value)
+            public static void SetWriteOffset(IVirtualMemoryManager manager, ulong bufferAddress, uint value)
             {
                 manager.Write(bufferAddress + WriteOffsetPosition, value);
             }
diff --git a/Ryujinx.Audio.Renderer/Ryujinx.Audio.Renderer.csproj b/Ryujinx.Audio.Renderer/Ryujinx.Audio.Renderer.csproj
index eac49fb3a5..ccdeae3e69 100644
--- a/Ryujinx.Audio.Renderer/Ryujinx.Audio.Renderer.csproj
+++ b/Ryujinx.Audio.Renderer/Ryujinx.Audio.Renderer.csproj
@@ -8,6 +8,7 @@
   <ItemGroup>
     <ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
     <ProjectReference Include="..\Ryujinx.Cpu\Ryujinx.Cpu.csproj" />
+    <ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" />
   </ItemGroup>
 
 </Project>
diff --git a/Ryujinx.Audio.Renderer/Server/AudioRenderSystem.cs b/Ryujinx.Audio.Renderer/Server/AudioRenderSystem.cs
index 30f326a6d1..a59252847d 100644
--- a/Ryujinx.Audio.Renderer/Server/AudioRenderSystem.cs
+++ b/Ryujinx.Audio.Renderer/Server/AudioRenderSystem.cs
@@ -31,7 +31,7 @@ using Ryujinx.Audio.Renderer.Server.Voice;
 using Ryujinx.Audio.Renderer.Utils;
 using Ryujinx.Common;
 using Ryujinx.Common.Logging;
-using Ryujinx.Cpu;
+using Ryujinx.Memory;
 using System;
 using System.Buffers;
 using System.Diagnostics;
@@ -87,7 +87,7 @@ namespace Ryujinx.Audio.Renderer.Server
 
         private Memory<byte> _performanceBuffer;
 
-        public MemoryManager MemoryManager { get; private set; }
+        public IVirtualMemoryManager MemoryManager { get; private set; }
 
         private ulong _elapsedFrameCount;
         private ulong _renderingStartTick;
@@ -96,14 +96,14 @@ namespace Ryujinx.Audio.Renderer.Server
 
         public AudioRenderSystem(AudioRendererManager manager, IWritableEvent systemEvent)
         {
-            _manager           = manager;
-            _terminationEvent  = new ManualResetEvent(false);
+            _manager            = manager;
+            _terminationEvent   = new ManualResetEvent(false);
             _dspMemoryPoolState = MemoryPoolState.Create(MemoryPoolState.LocationType.Dsp);
-            _voiceContext      = new VoiceContext();
-            _mixContext        = new MixContext();
-            _sinkContext       = new SinkContext();
-            _splitterContext   = new SplitterContext();
-            _effectContext     = new EffectContext();
+            _voiceContext       = new VoiceContext();
+            _mixContext         = new MixContext();
+            _sinkContext        = new SinkContext();
+            _splitterContext    = new SplitterContext();
+            _effectContext      = new EffectContext();
 
             _commandProcessingTimeEstimator = null;
             _systemEvent = systemEvent;
@@ -113,7 +113,7 @@ namespace Ryujinx.Audio.Renderer.Server
             _sessionId                 = 0;
         }
 
-        public ResultCode Initialize(ref AudioRendererConfiguration parameter, uint processHandle, CpuAddress workBuffer, ulong workBufferSize, int sessionId, ulong appletResourceId, MemoryManager memoryManager)
+        public ResultCode Initialize(ref AudioRendererConfiguration parameter, uint processHandle, CpuAddress workBuffer, ulong workBufferSize, int sessionId, ulong appletResourceId, IVirtualMemoryManager memoryManager)
         {
             if (!BehaviourContext.CheckValidRevision(parameter.Revision))
             {
diff --git a/Ryujinx.Audio.Renderer/Server/AudioRendererManager.cs b/Ryujinx.Audio.Renderer/Server/AudioRendererManager.cs
index 023dd477cb..3df0ab31e2 100644
--- a/Ryujinx.Audio.Renderer/Server/AudioRendererManager.cs
+++ b/Ryujinx.Audio.Renderer/Server/AudioRendererManager.cs
@@ -19,7 +19,7 @@ using Ryujinx.Audio.Renderer.Dsp;
 using Ryujinx.Audio.Renderer.Integration;
 using Ryujinx.Audio.Renderer.Parameter;
 using Ryujinx.Common.Logging;
-using Ryujinx.Cpu;
+using Ryujinx.Memory;
 using System;
 using System.Diagnostics;
 using System.Threading;
@@ -288,7 +288,7 @@ namespace Ryujinx.Audio.Renderer.Server
         /// <param name="workBufferSize">The guest work buffer size.</param>
         /// <param name="processHandle">The process handle of the application.</param>
         /// <returns>A <see cref="ResultCode"/> reporting an error or a success.</returns>
-        public ResultCode OpenAudioRenderer(out AudioRenderSystem renderer, MemoryManager memoryManager, ref AudioRendererConfiguration parameter, ulong appletResourceUserId, ulong workBufferAddress, ulong workBufferSize, uint processHandle)
+        public ResultCode OpenAudioRenderer(out AudioRenderSystem renderer, IVirtualMemoryManager memoryManager, ref AudioRendererConfiguration parameter, ulong appletResourceUserId, ulong workBufferAddress, ulong workBufferSize, uint processHandle)
         {
             int sessionId = AcquireSessionId();
 
@@ -321,6 +321,14 @@ namespace Ryujinx.Audio.Renderer.Server
         {
             if (disposing)
             {
+                lock (_audioProcessorLock)
+                {
+                    if (_isRunning)
+                    {
+                        StopLocked();
+                    }
+                }
+
                 Processor.Dispose();
 
                 foreach (HardwareDevice device in OutputDevices)
diff --git a/Ryujinx.Cpu/MemoryHelper.cs b/Ryujinx.Cpu/MemoryHelper.cs
index 5ba5ef38e2..8ef4bc6679 100644
--- a/Ryujinx.Cpu/MemoryHelper.cs
+++ b/Ryujinx.Cpu/MemoryHelper.cs
@@ -1,4 +1,5 @@
-using System;
+using Ryujinx.Memory;
+using System;
 using System.IO;
 using System.Runtime.InteropServices;
 using System.Text;
@@ -7,7 +8,7 @@ namespace Ryujinx.Cpu
 {
     public static class MemoryHelper
     {
-        public static void FillWithZeros(MemoryManager memory, long position, int size)
+        public static void FillWithZeros(IVirtualMemoryManager memory, long position, int size)
         {
             int size8 = size & ~(8 - 1);
 
@@ -22,7 +23,7 @@ namespace Ryujinx.Cpu
             }
         }
 
-        public unsafe static T Read<T>(MemoryManager memory, long position) where T : struct
+        public unsafe static T Read<T>(IVirtualMemoryManager memory, long position) where T : struct
         {
             long size = Marshal.SizeOf<T>();
 
@@ -36,7 +37,7 @@ namespace Ryujinx.Cpu
             }
         }
 
-        public unsafe static void Write<T>(MemoryManager memory, long position, T value) where T : struct
+        public unsafe static void Write<T>(IVirtualMemoryManager memory, long position, T value) where T : struct
         {
             long size = Marshal.SizeOf<T>();
 
@@ -50,7 +51,7 @@ namespace Ryujinx.Cpu
             memory.Write((ulong)position, data);
         }
 
-        public static string ReadAsciiString(MemoryManager memory, long position, long maxSize = -1)
+        public static string ReadAsciiString(IVirtualMemoryManager memory, long position, long maxSize = -1)
         {
             using (MemoryStream ms = new MemoryStream())
             {
diff --git a/Ryujinx.Cpu/MemoryManager.cs b/Ryujinx.Cpu/MemoryManager.cs
index 3fa08fe752..36ae9b3272 100644
--- a/Ryujinx.Cpu/MemoryManager.cs
+++ b/Ryujinx.Cpu/MemoryManager.cs
@@ -13,7 +13,7 @@ namespace Ryujinx.Cpu
     /// <summary>
     /// Represents a CPU memory manager.
     /// </summary>
-    public sealed class MemoryManager : IMemoryManager, IDisposable, IVirtualMemoryManager
+    public sealed class MemoryManager : IMemoryManager, IVirtualMemoryManager, IDisposable
     {
         public const int PageBits = 12;
         public const int PageSize = 1 << PageBits;
@@ -468,6 +468,11 @@ namespace Ryujinx.Cpu
         /// <returns>True if the entire range is mapped, false otherwise</returns>
         public bool IsRangeMapped(ulong va, ulong size)
         {
+            if (size == 0UL)
+            {
+                return true;
+            }
+
             ulong endVa = (va + size + PageMask) & ~(ulong)PageMask;
 
             va &= ~(ulong)PageMask;
diff --git a/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs b/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs
index 517dacef57..91575e205a 100644
--- a/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs
+++ b/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs
@@ -1,4 +1,4 @@
-using Ryujinx.Cpu;
+using Ryujinx.Memory;
 using System;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
diff --git a/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs b/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs
index 3ebf2fd79a..de41fb9aba 100644
--- a/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs
+++ b/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs
@@ -1,5 +1,6 @@
 using Ryujinx.Cpu;
 using Ryujinx.Cpu.Tracking;
+using Ryujinx.Memory;
 using System;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
@@ -12,7 +13,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
     /// </summary>
     class PhysicalMemory
     {
-        public const int PageSize = Cpu.MemoryManager.PageSize;
+        public const int PageSize = 0x1000;
 
         private readonly Cpu.MemoryManager _cpuMemory;
 
diff --git a/Ryujinx.HLE/Exceptions/ServiceNotImplementedException.cs b/Ryujinx.HLE/Exceptions/ServiceNotImplementedException.cs
index 1be7609bb3..58c15fdde4 100644
--- a/Ryujinx.HLE/Exceptions/ServiceNotImplementedException.cs
+++ b/Ryujinx.HLE/Exceptions/ServiceNotImplementedException.cs
@@ -14,28 +14,31 @@ namespace Ryujinx.HLE.Exceptions
     [Serializable]
     internal class ServiceNotImplementedException : Exception
     {
+        public IIpcService Service { get; }
         public ServiceCtx Context { get; }
         public IpcMessage Request { get; }
 
-        public ServiceNotImplementedException(ServiceCtx context)
-            : this(context, "The service call is not implemented.")
+        public ServiceNotImplementedException(IIpcService service, ServiceCtx context)
+            : this(service, context, "The service call is not implemented.")
         { }
 
-        public ServiceNotImplementedException(ServiceCtx context, string message)
+        public ServiceNotImplementedException(IIpcService service, ServiceCtx context, string message)
             : base(message)
         {
+            Service = service;
             Context = context;
             Request = context.Request;
         }
 
-        public ServiceNotImplementedException(ServiceCtx context, string message, Exception inner)
+        public ServiceNotImplementedException(IIpcService service, ServiceCtx context, string message, Exception inner)
             : base(message, inner)
         {
+            Service = service;
             Context = context;
             Request = context.Request;
         }
 
-        protected ServiceNotImplementedException(SerializationInfo info, StreamingContext context) 
+        protected ServiceNotImplementedException(SerializationInfo info, StreamingContext context)
             : base(info, context)
         { }
 
@@ -59,17 +62,16 @@ namespace Ryujinx.HLE.Exceptions
 
             if (callingType != null && callingMethod != null)
             {
-                var ipcService  = Context.Session.Service;
-                var ipcCommands = ipcService.Commands;
+                var ipcCommands = Service.Commands;
 
                 // Find the handler for the method called
-                var ipcHandler   = ipcCommands.FirstOrDefault(x => x.Value as MethodBase == callingMethod);
+                var ipcHandler   = ipcCommands.FirstOrDefault(x => x.Value == callingMethod);
                 var ipcCommandId = ipcHandler.Key;
                 var ipcMethod    = ipcHandler.Value;
 
                 if (ipcMethod != null)
                 {
-                    sb.AppendLine($"Service Command: {ipcService.GetType().FullName}: {ipcCommandId} ({ipcMethod.Name})");
+                    sb.AppendLine($"Service Command: {Service.GetType().FullName}: {ipcCommandId} ({ipcMethod.Name})");
                     sb.AppendLine();
                 }
             }
diff --git a/Ryujinx.HLE/HOS/ArmProcessContext.cs b/Ryujinx.HLE/HOS/ArmProcessContext.cs
new file mode 100644
index 0000000000..fb7703b791
--- /dev/null
+++ b/Ryujinx.HLE/HOS/ArmProcessContext.cs
@@ -0,0 +1,24 @@
+using ARMeilleure.State;
+using Ryujinx.Cpu;
+using Ryujinx.HLE.HOS.Kernel.Process;
+using Ryujinx.Memory;
+
+namespace Ryujinx.HLE.HOS
+{
+    class ArmProcessContext : IProcessContext
+    {
+        private readonly MemoryManager _memoryManager;
+        private readonly CpuContext _cpuContext;
+
+        public IVirtualMemoryManager AddressSpace => _memoryManager;
+
+        public ArmProcessContext(MemoryManager memoryManager)
+        {
+            _memoryManager = memoryManager;
+            _cpuContext = new CpuContext(memoryManager);
+        }
+
+        public void Execute(ExecutionContext context, ulong codeAddress) => _cpuContext.Execute(context, codeAddress);
+        public void Dispose() => _memoryManager.Dispose();
+    }
+}
diff --git a/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs b/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs
new file mode 100644
index 0000000000..050d3690ef
--- /dev/null
+++ b/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs
@@ -0,0 +1,14 @@
+using Ryujinx.Cpu;
+using Ryujinx.HLE.HOS.Kernel.Process;
+using Ryujinx.Memory;
+
+namespace Ryujinx.HLE.HOS
+{
+    class ArmProcessContextFactory : IProcessContextFactory
+    {
+        public IProcessContext Create(MemoryBlock backingMemory, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler)
+        {
+            return new ArmProcessContext(new MemoryManager(backingMemory, addressSpaceSize, invalidAccessHandler));
+        }
+    }
+}
diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs
index b7e76a7217..d4b49f4513 100644
--- a/Ryujinx.HLE/HOS/Horizon.cs
+++ b/Ryujinx.HLE/HOS/Horizon.cs
@@ -120,11 +120,11 @@ namespace Ryujinx.HLE.HOS
             iirsPageList.AddRange(iirsPa, IirsSize / KMemoryManager.PageSize);
             timePageList.AddRange(timePa, TimeSize / KMemoryManager.PageSize);
 
-            HidSharedMem  = new KSharedMemory(KernelContext, hidPageList,  0, 0, MemoryPermission.Read);
-            FontSharedMem = new KSharedMemory(KernelContext, fontPageList, 0, 0, MemoryPermission.Read);
-            IirsSharedMem = new KSharedMemory(KernelContext, iirsPageList, 0, 0, MemoryPermission.Read);
+            HidSharedMem  = new KSharedMemory(KernelContext, hidPageList,  0, 0, KMemoryPermission.Read);
+            FontSharedMem = new KSharedMemory(KernelContext, fontPageList, 0, 0, KMemoryPermission.Read);
+            IirsSharedMem = new KSharedMemory(KernelContext, iirsPageList, 0, 0, KMemoryPermission.Read);
 
-            KSharedMemory timeSharedMemory = new KSharedMemory(KernelContext, timePageList, 0, 0, MemoryPermission.Read);
+            KSharedMemory timeSharedMemory = new KSharedMemory(KernelContext, timePageList, 0, 0, KMemoryPermission.Read);
 
             TimeServiceManager.Instance.Initialize(device, this, timeSharedMemory, timePa - DramMemoryMap.DramBase, TimeSize);
 
@@ -134,8 +134,6 @@ namespace Ryujinx.HLE.HOS
 
             Font = new SharedFontManager(device, fontPa - DramMemoryMap.DramBase);
 
-            IUserInterface.InitializePort(this);
-
             VsyncEvent = new KEvent(KernelContext);
 
             DisplayResolutionChangeEvent = new KEvent(KernelContext);
@@ -224,6 +222,16 @@ namespace Ryujinx.HLE.HOS
             AudioRendererManager.Initialize(writableEvents, devices);
         }
 
+        public void InitializeServices()
+        {
+            IUserInterface sm = new IUserInterface(KernelContext);
+
+            // Wait until SM server thread is done with initialization,
+            // only then doing connections to SM is safe.
+            sm.Server.InitDone.WaitOne();
+            sm.Server.InitDone.Dispose();
+        }
+
         public void LoadKip(string kipPath)
         {
             using IStorage kipFile = new LocalStorage(kipPath, FileAccess.Read);
diff --git a/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs b/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs
index 012c3167a3..135a0a1fe2 100644
--- a/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs
+++ b/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs
@@ -84,6 +84,11 @@ namespace Ryujinx.HLE.HOS.Ipc
 
             long pad0 = GetPadSize16(reader.BaseStream.Position + cmdPtr);
 
+            if (rawDataSize != 0)
+            {
+                rawDataSize -= (int)pad0;
+            }
+
             reader.BaseStream.Seek(pad0, SeekOrigin.Current);
 
             int recvListCount = recvListFlags - 2;
@@ -107,7 +112,7 @@ namespace Ryujinx.HLE.HOS.Ipc
             }
         }
 
-        public byte[] GetBytes(long cmdPtr)
+        public byte[] GetBytes(long cmdPtr, ulong recvListAddr)
         {
             using (MemoryStream ms = new MemoryStream())
             {
@@ -131,7 +136,11 @@ namespace Ryujinx.HLE.HOS.Ipc
 
                 int dataLength = RawData?.Length ?? 0;
 
-                int pad0 = (int)GetPadSize16(cmdPtr + 8 + handleData.Length);
+                dataLength = (dataLength + 3) & ~3;
+
+                int rawLength = dataLength;
+
+                int pad0 = (int)GetPadSize16(cmdPtr + 8 + handleData.Length + PtrBuff.Count * 8);
 
                 // Apparently, padding after Raw Data is 16 bytes, however when there is
                 // padding before Raw Data too, we need to subtract the size of this padding.
@@ -140,7 +149,7 @@ namespace Ryujinx.HLE.HOS.Ipc
 
                 dataLength = (dataLength + pad0 + pad1) / 4;
 
-                word1 = dataLength & 0x3ff;
+                word1 = (dataLength & 0x3ff) | (2 << 10);
 
                 if (HandleDesc != null)
                 {
@@ -151,14 +160,22 @@ namespace Ryujinx.HLE.HOS.Ipc
                 writer.Write(word1);
                 writer.Write(handleData);
 
+                for (int index = 0; index < PtrBuff.Count; index++)
+                {
+                    writer.Write(PtrBuff[index].GetWord0());
+                    writer.Write(PtrBuff[index].GetWord1());
+                }
+
                 ms.Seek(pad0, SeekOrigin.Current);
 
                 if (RawData != null)
                 {
                     writer.Write(RawData);
+                    ms.Seek(rawLength - RawData.Length, SeekOrigin.Current);
                 }
 
                 writer.Write(new byte[pad1]);
+                writer.Write(recvListAddr);
 
                 return ms.ToArray();
             }
diff --git a/Ryujinx.HLE/HOS/Ipc/IpcPtrBuffDesc.cs b/Ryujinx.HLE/HOS/Ipc/IpcPtrBuffDesc.cs
index cdc43f17f0..c17f248f80 100644
--- a/Ryujinx.HLE/HOS/Ipc/IpcPtrBuffDesc.cs
+++ b/Ryujinx.HLE/HOS/Ipc/IpcPtrBuffDesc.cs
@@ -8,6 +8,13 @@ namespace Ryujinx.HLE.HOS.Ipc
         public int   Index    { get; private set; }
         public long  Size     { get; private set; }
 
+        public IpcPtrBuffDesc(long position, int index, long size)
+        {
+            Position = position;
+            Index = index;
+            Size = size;
+        }
+
         public IpcPtrBuffDesc(BinaryReader reader)
         {
             long word0 = reader.ReadUInt32();
@@ -22,5 +29,25 @@ namespace Ryujinx.HLE.HOS.Ipc
 
             Size = (ushort)(word0 >> 16);
         }
+
+        public uint GetWord0()
+        {
+            uint word0;
+
+            word0  = (uint)((Position & 0x0f00000000) >> 20);
+            word0 |= (uint)((Position & 0x7000000000) >> 30);
+
+            word0 |= (uint)(Index & 0x03f) << 0;
+            word0 |= (uint)(Index & 0x1c0) << 3;
+
+            word0 |= (uint)Size << 16;
+
+            return word0;
+        }
+
+        public uint GetWord1()
+        {
+            return (uint)Position;
+        }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs b/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs
index 3fd92f02a0..7873255044 100644
--- a/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs
+++ b/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs
@@ -7,6 +7,12 @@ namespace Ryujinx.HLE.HOS.Ipc
         public long Position { get; private set; }
         public long Size     { get; private set; }
 
+        public IpcRecvListBuffDesc(long position, long size)
+        {
+            Position = position;
+            Size = size;
+        }
+
         public IpcRecvListBuffDesc(BinaryReader reader)
         {
             long value = reader.ReadInt64();
diff --git a/Ryujinx.HLE/HOS/Kernel/Common/OnScopeExit.cs b/Ryujinx.HLE/HOS/Kernel/Common/OnScopeExit.cs
new file mode 100644
index 0000000000..098d83d135
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/Common/OnScopeExit.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace Ryujinx.HLE.HOS.Kernel.Common
+{
+    struct OnScopeExit : IDisposable
+    {
+        private readonly Action _action;
+        public OnScopeExit(Action action) => _action = action;
+        public void Dispose() => _action();
+    }
+}
diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs
index 0986adf7a7..fbd3284591 100644
--- a/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs
@@ -99,7 +99,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
                         copySize,
                         stateMask,
                         stateMask,
-                        MemoryPermission.ReadAndWrite,
+                        KMemoryPermission.ReadAndWrite,
                         attributeMask,
                         MemoryAttribute.None,
                         desc.ServerAddress);
@@ -125,7 +125,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
                         clientEndAddr - clientEndAddrTruncated,
                         stateMask,
                         stateMask,
-                        MemoryPermission.ReadAndWrite,
+                        KMemoryPermission.ReadAndWrite,
                         attributeMask,
                         MemoryAttribute.None,
                         serverEndAddrTruncated);
diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs
index 8d6669cf55..c3b7d1dd38 100644
--- a/Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs
@@ -14,10 +14,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
 
         public bool IsLight => _parent.IsLight;
 
-        // TODO: Remove that, we need it for now to allow HLE
-        // SM implementation to work with the new IPC system.
-        public IpcService Service { get; set; }
-
         public KClientPort(KernelContext context, KPort parent, int maxSessions) : base(context)
         {
             _maxSessions = maxSessions;
@@ -45,11 +41,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
 
             KSession session = new KSession(KernelContext, this);
 
-            if (Service != null)
-            {
-                session.ClientSession.Service = Service;
-            }
-
             KernelResult result = _parent.EnqueueIncomingSession(session.ServerSession);
 
             if (result != KernelResult.Success)
diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs
index 2c2d9644d3..d535bf82ba 100644
--- a/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs
@@ -16,10 +16,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
 
         public KClientPort ParentPort { get; }
 
-        // TODO: Remove that, we need it for now to allow HLE
-        // services implementation to work with the new IPC system.
-        public IpcService Service { get; set; }
-
         public KClientSession(KernelContext context, KSession parent, KClientPort parentPort) : base(context)
         {
             _parent    = parent;
@@ -84,11 +80,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
         {
             _parent.DisconnectClient();
             _parent.DecrementReferenceCount();
-
-            if (Service is IDisposable disposableObj)
-            {
-                disposableObj.Dispose();
-            }
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs
index 486698328f..e9c6127fa6 100644
--- a/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs
@@ -430,7 +430,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
                         descriptor.BufferAddress,
                         MemoryState.IsPoolAllocated,
                         MemoryState.IsPoolAllocated,
-                        MemoryPermission.Read,
+                        KMemoryPermission.Read,
                         MemoryAttribute.Uncached,
                         MemoryAttribute.None);
 
@@ -473,9 +473,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
                 bool notReceiveDesc = isSendDesc || isExchangeDesc;
                 bool isReceiveDesc  = !notReceiveDesc;
 
-                MemoryPermission permission = index >= clientHeader.SendBuffersCount
-                    ? MemoryPermission.ReadAndWrite
-                    : MemoryPermission.Read;
+                KMemoryPermission permission = index >= clientHeader.SendBuffersCount
+                    ? KMemoryPermission.ReadAndWrite
+                    : KMemoryPermission.Read;
 
                 uint sizeHigh4 = (descWord2 >> 24) & 0xf;
 
@@ -559,9 +559,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
 
                 if (serverMsg.IsCustom || clientMsg.IsCustom)
                 {
-                    MemoryPermission permission = clientMsg.IsCustom
-                        ? MemoryPermission.None
-                        : MemoryPermission.Read;
+                    KMemoryPermission permission = clientMsg.IsCustom
+                        ? KMemoryPermission.None
+                        : KMemoryPermission.Read;
 
                     clientResult = clientProcess.MemoryManager.CopyDataToCurrentProcess(
                         copyDst,
@@ -795,7 +795,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
                         descriptor.BufferSize,
                         MemoryState.IsPoolAllocated,
                         MemoryState.IsPoolAllocated,
-                        MemoryPermission.Read,
+                        KMemoryPermission.Read,
                         MemoryAttribute.Uncached,
                         MemoryAttribute.None,
                         descriptor.BufferAddress);
@@ -849,9 +849,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
 
                 if (serverMsg.IsCustom || clientMsg.IsCustom)
                 {
-                    MemoryPermission permission = clientMsg.IsCustom
-                        ? MemoryPermission.None
-                        : MemoryPermission.Read;
+                    KMemoryPermission permission = clientMsg.IsCustom
+                        ? KMemoryPermission.None
+                        : KMemoryPermission.Read;
 
                     clientResult = clientProcess.MemoryManager.CopyDataFromCurrentProcess(
                         copyDst,
@@ -898,11 +898,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
             return new MessageHeader(word0, word1, word2);
         }
 
-        private KernelResult GetCopyObjectHandle(
-            KThread  srcThread,
-            KProcess dstProcess,
-            int      srcHandle,
-            out int  dstHandle)
+        private KernelResult GetCopyObjectHandle(KThread srcThread, KProcess dstProcess, int srcHandle, out int dstHandle)
         {
             dstHandle = 0;
 
@@ -933,11 +929,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
             }
         }
 
-        private KernelResult GetMoveObjectHandle(
-            KProcess srcProcess,
-            KProcess dstProcess,
-            int      srcHandle,
-            out int  dstHandle)
+        private KernelResult GetMoveObjectHandle(KProcess srcProcess, KProcess dstProcess, int srcHandle, out int dstHandle)
         {
             dstHandle = 0;
 
diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs
index 4b5886a9d8..d614857c9a 100644
--- a/Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs
@@ -4,7 +4,7 @@ using System;
 
 namespace Ryujinx.HLE.HOS.Kernel.Ipc
 {
-    class KSession : KAutoObject, IDisposable
+    class KSession : KAutoObject
     {
         public KServerSession ServerSession { get; }
         public KClientSession ClientSession { get; }
@@ -37,19 +37,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
             }
         }
 
-        public void Dispose()
-        {
-            Dispose(true);
-        }
-
-        protected virtual void Dispose(bool disposing)
-        {
-            if (disposing && ClientSession.Service is IDisposable disposableService)
-            {
-                disposableService.Dispose();
-            }
-        }
-
         protected override void Destroy()
         {
             if (_hasBeenInitialized)
diff --git a/Ryujinx.HLE/HOS/Kernel/KernelContext.cs b/Ryujinx.HLE/HOS/Kernel/KernelContext.cs
index a2d8bc47b3..d19c43eefe 100644
--- a/Ryujinx.HLE/HOS/Kernel/KernelContext.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KernelContext.cs
@@ -51,7 +51,7 @@ namespace Ryujinx.HLE.HOS.Kernel
             Device = device;
             Memory = memory;
 
-            Syscall = new Syscall(device, this);
+            Syscall = new Syscall(this);
 
             SyscallHandler = new SyscallHandler(this);
 
diff --git a/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs b/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs
new file mode 100644
index 0000000000..c7deadae95
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs
@@ -0,0 +1,38 @@
+using Ryujinx.HLE.HOS.Kernel.Threading;
+using System;
+using System.Threading.Tasks;
+
+namespace Ryujinx.HLE.HOS.Kernel
+{
+    static class KernelStatic
+    {
+        [ThreadStatic]
+        private static KernelContext Context;
+
+        public static void YieldUntilCompletion(Action action)
+        {
+            YieldUntilCompletion(Task.Factory.StartNew(action));
+        }
+
+        public static void YieldUntilCompletion(Task task)
+        {
+            KThread currentThread = Context.Scheduler.GetCurrentThread();
+
+            Context.CriticalSection.Enter();
+
+            currentThread.Reschedule(ThreadSchedState.Paused);
+
+            task.ContinueWith((antecedent) =>
+            {
+                currentThread.Reschedule(ThreadSchedState.Running);
+            });
+
+            Context.CriticalSection.Leave();
+        }
+
+        internal static void SetKernelContext(KernelContext context)
+        {
+            Context = context;
+        }
+    }
+}
diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlock.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlock.cs
index 04e14e1b31..b93b68d94a 100644
--- a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlock.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlock.cs
@@ -8,9 +8,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
         public ulong PagesCount  { get; private set; }
 
         public MemoryState      State            { get; private set; }
-        public MemoryPermission Permission       { get; private set; }
+        public KMemoryPermission Permission       { get; private set; }
         public MemoryAttribute  Attribute        { get; private set; }
-        public MemoryPermission SourcePermission { get; private set; }
+        public KMemoryPermission SourcePermission { get; private set; }
 
         public int IpcRefCount    { get; private set; }
         public int DeviceRefCount { get; private set; }
@@ -19,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             ulong            baseAddress,
             ulong            pagesCount,
             MemoryState      state,
-            MemoryPermission permission,
+            KMemoryPermission permission,
             MemoryAttribute  attribute,
             int              ipcRefCount    = 0,
             int              deviceRefCount = 0)
@@ -33,7 +33,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             DeviceRefCount = deviceRefCount;
         }
 
-        public void SetState(MemoryPermission permission, MemoryState state, MemoryAttribute attribute)
+        public void SetState(KMemoryPermission permission, MemoryState state, MemoryAttribute attribute)
         {
             Permission = permission;
             State      = state;
@@ -41,7 +41,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             Attribute |= attribute;
         }
 
-        public void SetIpcMappingPermission(MemoryPermission newPermission)
+        public void SetIpcMappingPermission(KMemoryPermission newPermission)
         {
             int oldIpcRefCount = IpcRefCount++;
 
@@ -54,8 +54,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             {
                 SourcePermission = Permission;
 
-                Permission &= ~MemoryPermission.ReadAndWrite;
-                Permission |=  MemoryPermission.ReadAndWrite & newPermission;
+                Permission &= ~KMemoryPermission.ReadAndWrite;
+                Permission |=  KMemoryPermission.ReadAndWrite & newPermission;
             }
 
             Attribute |= MemoryAttribute.IpcMapped;
@@ -74,7 +74,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             {
                 Permission = SourcePermission;
 
-                SourcePermission = MemoryPermission.None;
+                SourcePermission = KMemoryPermission.None;
 
                 Attribute &= ~MemoryAttribute.IpcMapped;
             }
diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryInfo.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryInfo.cs
index 21e9e49407..af070ac23d 100644
--- a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryInfo.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryInfo.cs
@@ -6,9 +6,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
         public ulong Size    { get; }
 
         public MemoryState      State            { get; }
-        public MemoryPermission Permission       { get; }
+        public KMemoryPermission Permission       { get; }
         public MemoryAttribute  Attribute        { get; }
-        public MemoryPermission SourcePermission { get; }
+        public KMemoryPermission SourcePermission { get; }
 
         public int IpcRefCount    { get; }
         public int DeviceRefCount { get; }
@@ -17,9 +17,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             ulong            address,
             ulong            size,
             MemoryState      state,
-            MemoryPermission permission,
+            KMemoryPermission permission,
             MemoryAttribute  attribute,
-            MemoryPermission sourcePermission,
+            KMemoryPermission sourcePermission,
             int              ipcRefCount,
             int              deviceRefCount)
         {
diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs
index 43e7ad6916..63ac8583e5 100644
--- a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs
@@ -1,9 +1,10 @@
 using Ryujinx.Common;
-using Ryujinx.Cpu;
 using Ryujinx.HLE.HOS.Kernel.Common;
 using Ryujinx.HLE.HOS.Kernel.Process;
+using Ryujinx.Memory;
 using System;
 using System.Collections.Generic;
+using System.Diagnostics;
 
 namespace Ryujinx.HLE.HOS.Kernel.Memory
 {
@@ -27,11 +28,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
         // needs to be split in 2, plus one block that will be the new one inserted.
         private const int MaxBlocksNeededForInsertion = 2;
 
-        private LinkedList<KMemoryBlock> _blocks;
+        private readonly LinkedList<KMemoryBlock> _blocks;
 
-        private MemoryManager _cpuMemory;
+        private readonly IVirtualMemoryManager _cpuMemory;
 
-        private KernelContext _context;
+        private readonly KernelContext _context;
 
         public ulong AddrSpaceStart { get; private set; }
         public ulong AddrSpaceEnd   { get; private set; }
@@ -73,7 +74,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
 
         private MersenneTwister _randomNumberGenerator;
 
-        public KMemoryManager(KernelContext context, MemoryManager cpuMemory)
+        public KMemoryManager(KernelContext context, IVirtualMemoryManager cpuMemory)
         {
             _context   = context;
             _cpuMemory = cpuMemory;
@@ -352,7 +353,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                 addrSpaceStart,
                 addrSpacePagesCount,
                 MemoryState.Unmapped,
-                MemoryPermission.None,
+                KMemoryPermission.None,
                 MemoryAttribute.None));
 
             return KernelResult.Success;
@@ -362,13 +363,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             ulong            address,
             KPageList        pageList,
             MemoryState      state,
-            MemoryPermission permission)
+            KMemoryPermission permission)
         {
             ulong pagesCount = pageList.GetPagesCount();
 
             ulong size = pagesCount * PageSize;
 
-            if (!ValidateRegionForState(address, size, state))
+            if (!CanContain(address, size, state))
             {
                 return KernelResult.InvalidMemState;
             }
@@ -437,8 +438,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                     size,
                     MemoryState.Mask,
                     stateExpected,
-                    MemoryPermission.None,
-                    MemoryPermission.None,
+                    KMemoryPermission.None,
+                    KMemoryPermission.None,
                     MemoryAttribute.Mask,
                     MemoryAttribute.None,
                     MemoryAttribute.IpcAndDeviceMapped,
@@ -467,13 +468,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             }
         }
 
-        public KernelResult MapNormalMemory(long address, long size, MemoryPermission permission)
+        public KernelResult MapNormalMemory(long address, long size, KMemoryPermission permission)
         {
             // TODO.
             return KernelResult.Success;
         }
 
-        public KernelResult MapIoMemory(long address, long size, MemoryPermission permission)
+        public KernelResult MapIoMemory(long address, long size, KMemoryPermission permission)
         {
             // TODO.
             return KernelResult.Success;
@@ -487,7 +488,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             ulong            regionStart,
             ulong            regionPagesCount,
             MemoryState      state,
-            MemoryPermission permission,
+            KMemoryPermission permission,
             out ulong        address)
         {
             address = 0;
@@ -496,7 +497,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
 
             ulong regionEndAddr = regionStart + regionSize;
 
-            if (!ValidateRegionForState(regionStart, regionSize, state))
+            if (!CanContain(regionStart, regionSize, state))
             {
                 return KernelResult.InvalidMemState;
             }
@@ -547,11 +548,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             ulong            address,
             ulong            pagesCount,
             MemoryState      state,
-            MemoryPermission permission)
+            KMemoryPermission permission)
         {
             ulong size = pagesCount * PageSize;
 
-            if (!ValidateRegionForState(address, size, state))
+            if (!CanContain(address, size, state))
             {
                 return KernelResult.InvalidMemState;
             }
@@ -596,13 +597,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                     size,
                     MemoryState.Mask,
                     MemoryState.Heap,
-                    MemoryPermission.Mask,
-                    MemoryPermission.ReadAndWrite,
+                    KMemoryPermission.Mask,
+                    KMemoryPermission.ReadAndWrite,
                     MemoryAttribute.Mask,
                     MemoryAttribute.None,
                     MemoryAttribute.IpcAndDeviceMapped,
                     out MemoryState      state,
-                    out MemoryPermission permission,
+                    out KMemoryPermission permission,
                     out _);
 
                 success &= IsUnmapped(dst, size);
@@ -618,14 +619,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
 
                     AddVaRangeToPageList(pageList, src, pagesCount);
 
-                    KernelResult result = MmuChangePermission(src, pagesCount, MemoryPermission.None);
+                    KernelResult result = MmuChangePermission(src, pagesCount, KMemoryPermission.None);
 
                     if (result != KernelResult.Success)
                     {
                         return result;
                     }
 
-                    result = MapPages(dst, pageList, MemoryPermission.None);
+                    result = MapPages(dst, pageList, KMemoryPermission.None);
 
                     if (result != KernelResult.Success)
                     {
@@ -634,7 +635,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                         return result;
                     }
 
-                    InsertBlock(src, pagesCount, state, MemoryPermission.None, MemoryAttribute.Borrowed);
+                    InsertBlock(src, pagesCount, state, KMemoryPermission.None, MemoryAttribute.Borrowed);
                     InsertBlock(dst, pagesCount, MemoryState.ModCodeStatic);
 
                     return KernelResult.Success;
@@ -657,8 +658,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                     size,
                     MemoryState.Mask,
                     MemoryState.Heap,
-                    MemoryPermission.None,
-                    MemoryPermission.None,
+                    KMemoryPermission.None,
+                    KMemoryPermission.None,
                     MemoryAttribute.Mask,
                     MemoryAttribute.Borrowed,
                     MemoryAttribute.IpcAndDeviceMapped,
@@ -671,8 +672,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                     PageSize,
                     MemoryState.UnmapProcessCodeMemoryAllowed,
                     MemoryState.UnmapProcessCodeMemoryAllowed,
-                    MemoryPermission.None,
-                    MemoryPermission.None,
+                    KMemoryPermission.None,
+                    KMemoryPermission.None,
                     MemoryAttribute.Mask,
                     MemoryAttribute.None,
                     MemoryAttribute.IpcAndDeviceMapped,
@@ -685,8 +686,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                     size,
                     MemoryState.Mask,
                     state,
-                    MemoryPermission.None,
-                    MemoryPermission.None,
+                    KMemoryPermission.None,
+                    KMemoryPermission.None,
                     MemoryAttribute.Mask,
                     MemoryAttribute.None);
 
@@ -707,7 +708,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                     }
 
                     InsertBlock(dst, pagesCount, MemoryState.Unmapped);
-                    InsertBlock(src, pagesCount, MemoryState.Heap, MemoryPermission.ReadAndWrite);
+                    InsertBlock(src, pagesCount, MemoryState.Heap, KMemoryPermission.ReadAndWrite);
 
                     return KernelResult.Success;
                 }
@@ -788,7 +789,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                         _currentHeapAddr,
                         pagesCount,
                         pageList,
-                        MemoryPermission.ReadAndWrite,
+                        KMemoryPermission.ReadAndWrite,
                         MemoryOperation.MapVa);
 
                     if (result != KernelResult.Success)
@@ -798,7 +799,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                         return result;
                     }
 
-                    InsertBlock(_currentHeapAddr, pagesCount, MemoryState.Heap, MemoryPermission.ReadAndWrite);
+                    InsertBlock(_currentHeapAddr, pagesCount, MemoryState.Heap, KMemoryPermission.ReadAndWrite);
                 }
                 else
                 {
@@ -816,8 +817,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                         sizeDelta,
                         MemoryState.Mask,
                         MemoryState.Heap,
-                        MemoryPermission.Mask,
-                        MemoryPermission.ReadAndWrite,
+                        KMemoryPermission.Mask,
+                        KMemoryPermission.ReadAndWrite,
                         MemoryAttribute.Mask,
                         MemoryAttribute.None,
                         MemoryAttribute.IpcAndDeviceMapped,
@@ -886,13 +887,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                     size,
                     MemoryState.AttributeChangeAllowed,
                     MemoryState.AttributeChangeAllowed,
-                    MemoryPermission.None,
-                    MemoryPermission.None,
+                    KMemoryPermission.None,
+                    KMemoryPermission.None,
                     MemoryAttribute.BorrowedAndIpcMapped,
                     MemoryAttribute.None,
                     MemoryAttribute.DeviceMappedAndUncached,
                     out MemoryState      state,
-                    out MemoryPermission permission,
+                    out KMemoryPermission permission,
                     out MemoryAttribute  attribute))
                 {
                     if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
@@ -932,9 +933,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                     AddrSpaceEnd,
                     ~AddrSpaceEnd + 1,
                     MemoryState.Reserved,
-                    MemoryPermission.None,
+                    KMemoryPermission.None,
                     MemoryAttribute.None,
-                    MemoryPermission.None,
+                    KMemoryPermission.None,
                     0,
                     0);
             }
@@ -951,8 +952,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                     size,
                     MemoryState.MapAllowed,
                     MemoryState.MapAllowed,
-                    MemoryPermission.Mask,
-                    MemoryPermission.ReadAndWrite,
+                    KMemoryPermission.Mask,
+                    KMemoryPermission.ReadAndWrite,
                     MemoryAttribute.Mask,
                     MemoryAttribute.None,
                     MemoryAttribute.IpcAndDeviceMapped,
@@ -975,18 +976,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
 
                     AddVaRangeToPageList(pageList, src, pagesCount);
 
-                    KernelResult result = MmuChangePermission(src, pagesCount, MemoryPermission.None);
+                    KernelResult result = MmuChangePermission(src, pagesCount, KMemoryPermission.None);
 
                     if (result != KernelResult.Success)
                     {
                         return result;
                     }
 
-                    result = MapPages(dst, pageList, MemoryPermission.ReadAndWrite);
+                    result = MapPages(dst, pageList, KMemoryPermission.ReadAndWrite);
 
                     if (result != KernelResult.Success)
                     {
-                        if (MmuChangePermission(src, pagesCount, MemoryPermission.ReadAndWrite) != KernelResult.Success)
+                        if (MmuChangePermission(src, pagesCount, KMemoryPermission.ReadAndWrite) != KernelResult.Success)
                         {
                             throw new InvalidOperationException("Unexpected failure reverting memory permission.");
                         }
@@ -994,8 +995,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                         return result;
                     }
 
-                    InsertBlock(src, pagesCount, srcState, MemoryPermission.None, MemoryAttribute.Borrowed);
-                    InsertBlock(dst, pagesCount, MemoryState.Stack, MemoryPermission.ReadAndWrite);
+                    InsertBlock(src, pagesCount, srcState, KMemoryPermission.None, MemoryAttribute.Borrowed);
+                    InsertBlock(dst, pagesCount, MemoryState.Stack, KMemoryPermission.ReadAndWrite);
 
                     return KernelResult.Success;
                 }
@@ -1017,8 +1018,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                     size,
                     MemoryState.Mask,
                     stateExpected,
-                    MemoryPermission.None,
-                    MemoryPermission.None,
+                    KMemoryPermission.None,
+                    KMemoryPermission.None,
                     MemoryAttribute.Mask,
                     MemoryAttribute.None,
                     MemoryAttribute.IpcAndDeviceMapped,
@@ -1058,8 +1059,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                     size,
                     MemoryState.MapAllowed,
                     MemoryState.MapAllowed,
-                    MemoryPermission.Mask,
-                    MemoryPermission.None,
+                    KMemoryPermission.Mask,
+                    KMemoryPermission.None,
                     MemoryAttribute.Mask,
                     MemoryAttribute.Borrowed,
                     MemoryAttribute.IpcAndDeviceMapped,
@@ -1072,13 +1073,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                     size,
                     MemoryState.Mask,
                     MemoryState.Stack,
-                    MemoryPermission.None,
-                    MemoryPermission.None,
+                    KMemoryPermission.None,
+                    KMemoryPermission.None,
                     MemoryAttribute.Mask,
                     MemoryAttribute.None,
                     MemoryAttribute.IpcAndDeviceMapped,
                     out _,
-                    out MemoryPermission dstPermission,
+                    out KMemoryPermission dstPermission,
                     out _);
 
                 if (success)
@@ -1108,7 +1109,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                         return result;
                     }
 
-                    result = MmuChangePermission(src, pagesCount, MemoryPermission.ReadAndWrite);
+                    result = MmuChangePermission(src, pagesCount, KMemoryPermission.ReadAndWrite);
 
                     if (result != KernelResult.Success)
                     {
@@ -1117,7 +1118,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                         return result;
                     }
 
-                    InsertBlock(src, pagesCount, srcState, MemoryPermission.ReadAndWrite);
+                    InsertBlock(src, pagesCount, srcState, KMemoryPermission.ReadAndWrite);
                     InsertBlock(dst, pagesCount, MemoryState.Unmapped);
 
                     return KernelResult.Success;
@@ -1129,7 +1130,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             }
         }
 
-        public KernelResult SetProcessMemoryPermission(ulong address, ulong size, MemoryPermission permission)
+        public KernelResult SetProcessMemoryPermission(ulong address, ulong size, KMemoryPermission permission)
         {
             lock (_blocks)
             {
@@ -1138,20 +1139,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                     size,
                     MemoryState.ProcessPermissionChangeAllowed,
                     MemoryState.ProcessPermissionChangeAllowed,
-                    MemoryPermission.None,
-                    MemoryPermission.None,
+                    KMemoryPermission.None,
+                    KMemoryPermission.None,
                     MemoryAttribute.Mask,
                     MemoryAttribute.None,
                     MemoryAttribute.IpcAndDeviceMapped,
                     out MemoryState      oldState,
-                    out MemoryPermission oldPermission,
+                    out KMemoryPermission oldPermission,
                     out _))
                 {
                     MemoryState newState = oldState;
 
                     // If writing into the code region is allowed, then we need
                     // to change it to mutable.
-                    if ((permission & MemoryPermission.Write) != 0)
+                    if ((permission & KMemoryPermission.Write) != 0)
                     {
                         if (oldState == MemoryState.CodeStatic)
                         {
@@ -1176,7 +1177,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
 
                         ulong pagesCount = size / PageSize;
 
-                        MemoryOperation operation = (permission & MemoryPermission.Execute) != 0
+                        MemoryOperation operation = (permission & KMemoryPermission.Execute) != 0
                             ? MemoryOperation.ChangePermsAndAttributes
                             : MemoryOperation.ChangePermRw;
 
@@ -1270,10 +1271,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                     address,
                     pagesCount,
                     MemoryState.Unmapped,
-                    MemoryPermission.None,
+                    KMemoryPermission.None,
                     MemoryAttribute.None,
                     MemoryState.Heap,
-                    MemoryPermission.ReadAndWrite,
+                    KMemoryPermission.ReadAndWrite,
                     MemoryAttribute.None);
             }
 
@@ -1410,7 +1411,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                             pagesCount,
                             srcPa,
                             true,
-                            MemoryPermission.ReadAndWrite,
+                            KMemoryPermission.ReadAndWrite,
                             MemoryOperation.MapPa);
 
                         dstVa      += pagesCount * PageSize;
@@ -1428,7 +1429,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             ulong            src,
             MemoryState      stateMask,
             MemoryState      stateExpected,
-            MemoryPermission permission,
+            KMemoryPermission permission,
             MemoryAttribute  attributeMask,
             MemoryAttribute  attributeExpected)
         {
@@ -1450,7 +1451,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             ulong            size,
             MemoryState      stateMask,
             MemoryState      stateExpected,
-            MemoryPermission permission,
+            KMemoryPermission permission,
             MemoryAttribute  attributeMask,
             MemoryAttribute  attributeExpected,
             ulong            src)
@@ -1474,7 +1475,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             ulong            serverAddress,
             MemoryState      stateMask,
             MemoryState      stateExpected,
-            MemoryPermission permission,
+            KMemoryPermission permission,
             MemoryAttribute  attributeMask,
             MemoryAttribute  attributeExpected,
             bool             toServer)
@@ -1529,7 +1530,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             ulong            size,
             ulong            src,
             KMemoryManager   sourceMemMgr,
-            MemoryPermission permission,
+            KMemoryPermission permission,
             MemoryState      state,
             bool             copyData,
             out ulong        dst)
@@ -1568,7 +1569,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
         private KernelResult GetPagesForMappingIntoAnotherProcess(
             ulong            address,
             ulong            size,
-            MemoryPermission permission,
+            KMemoryPermission permission,
             MemoryState      state,
             bool             copyData,
             bool             aslrDisabled,
@@ -1600,9 +1601,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                 default: return KernelResult.InvalidCombination;
             }
 
-            MemoryPermission permissionMask = permission == MemoryPermission.ReadAndWrite
-                ? MemoryPermission.None
-                : MemoryPermission.Read;
+            KMemoryPermission permissionMask = permission == KMemoryPermission.ReadAndWrite
+                ? KMemoryPermission.None
+                : KMemoryPermission.Read;
 
             MemoryAttribute attributeMask = MemoryAttribute.Borrowed | MemoryAttribute.Uncached;
 
@@ -1634,7 +1635,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
 
                 foreach (KMemoryInfo info in IterateOverRange(addressRounded, endAddrVisited))
                 {
-                    if ((info.Permission & MemoryPermission.ReadAndWrite) != permissionMask && info.IpcRefCount == 0)
+                    if ((info.Permission & KMemoryPermission.ReadAndWrite) != permissionMask && info.IpcRefCount == 0)
                     {
                         ulong blockAddress = GetAddrInRange(info, addressRounded);
                         ulong blockSize    = GetSizeInRange(info, addressRounded, endAddrVisited);
@@ -1661,7 +1662,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
 
                 if (addressRounded < endAddrTruncated)
                 {
-                    foreach (KMemoryInfo info in IterateOverRange(addressTruncated, endAddrRounded))
+                    foreach (KMemoryInfo info in IterateOverRange(addressRounded, endAddrTruncated))
                     {
                         // Check if the block state matches what we expect.
                         if ((info.State      & stateMask)     != stateMask  ||
@@ -1678,7 +1679,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
 
                         ulong blockPagesCount = blockSize / PageSize;
 
-                        if ((info.Permission & MemoryPermission.ReadAndWrite) != permissionMask && info.IpcRefCount == 0)
+                        if ((info.Permission & KMemoryPermission.ReadAndWrite) != permissionMask && info.IpcRefCount == 0)
                         {
                             result = DoMmuOperation(
                                 blockAddress,
@@ -1784,7 +1785,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                 {
                     ulong unusedSizeBefore = address - addressTruncated;
 
-                    _context.Memory.ZeroFill(dstFirstPagePa, unusedSizeBefore);
+                    _context.Memory.ZeroFill(GetDramAddressFromPa(dstFirstPagePa), unusedSizeBefore);
 
                     ulong copySize = addressRounded <= endAddr ? addressRounded - address : size;
 
@@ -1803,7 +1804,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
 
                 if (unusedSizeAfter != 0)
                 {
-                    _context.Memory.ZeroFill(firstPageFillAddress, unusedSizeAfter);
+                    _context.Memory.ZeroFill(GetDramAddressFromPa(firstPageFillAddress), unusedSizeAfter);
                 }
 
                 if (pages.AddRange(dstFirstPagePa, 1) != KernelResult.Success)
@@ -1865,7 +1866,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                     unusedSizeAfter = PageSize;
                 }
 
-                _context.Memory.ZeroFill(lastPageFillAddr, unusedSizeAfter);
+                _context.Memory.ZeroFill(GetDramAddressFromPa(lastPageFillAddr), unusedSizeAfter);
 
                 if (pages.AddRange(dstLastPagePa, 1) != KernelResult.Success)
                 {
@@ -1897,7 +1898,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
         private KernelResult MapPagesFromAnotherProcess(
             ulong            size,
             ulong            address,
-            MemoryPermission permission,
+            KMemoryPermission permission,
             MemoryState      state,
             KPageList        pageList,
             out ulong        dst)
@@ -1975,8 +1976,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                     size,
                     MemoryState.Mask,
                     state,
-                    MemoryPermission.Read,
-                    MemoryPermission.Read,
+                    KMemoryPermission.Read,
+                    KMemoryPermission.Read,
                     MemoryAttribute.Mask,
                     MemoryAttribute.None,
                     MemoryAttribute.IpcAndDeviceMapped,
@@ -1996,14 +1997,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
 
                     ulong pagesCount = (endAddrRounded - addressTruncated) / PageSize;
 
-                    KernelResult result = DoMmuOperation(
-                        addressTruncated,
-                        pagesCount,
-                        0,
-                        false,
-                        MemoryPermission.None,
-                        MemoryOperation.Unmap);
-
                     // Free pages we had to create on-demand, if any of the buffer was not page aligned.
                     // Real kernel has page ref counting, so this is done as part of the unmap operation.
                     if (addressTruncated != addressRounded)
@@ -2016,6 +2009,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                         FreeSinglePage(_memRegion, ConvertVaToPa(endAddrTruncated));
                     }
 
+                    KernelResult result = DoMmuOperation(
+                        addressTruncated,
+                        pagesCount,
+                        0,
+                        false,
+                        KMemoryPermission.None,
+                        MemoryOperation.Unmap);
+
                     if (result == KernelResult.Success)
                     {
                         InsertBlock(addressTruncated, pagesCount, MemoryState.Unmapped);
@@ -2037,7 +2038,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             ulong addressRounded   = BitUtils.AlignUp  (address, PageSize);
             ulong endAddrTruncated = BitUtils.AlignDown(endAddr, PageSize);
 
-            ulong pagesCount = (endAddrTruncated - addressRounded) / PageSize;
+            ulong pagesCount = addressRounded < endAddrTruncated ? (endAddrTruncated - addressRounded) / PageSize : 0;
+
+            if (pagesCount == 0)
+            {
+                return KernelResult.Success;
+            }
 
             MemoryState stateMask;
 
@@ -2111,23 +2117,23 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                 size,
                 MemoryState.IpcBufferAllowed,
                 MemoryState.IpcBufferAllowed,
-                MemoryPermission.Mask,
-                MemoryPermission.ReadAndWrite,
+                KMemoryPermission.Mask,
+                KMemoryPermission.ReadAndWrite,
                 MemoryAttribute.Mask,
                 MemoryAttribute.None,
-                MemoryPermission.None,
+                KMemoryPermission.None,
                 MemoryAttribute.Borrowed);
         }
 
-        public KernelResult BorrowTransferMemory(KPageList pageList, ulong address, ulong size, MemoryPermission permission)
+        public KernelResult BorrowTransferMemory(KPageList pageList, ulong address, ulong size, KMemoryPermission permission)
         {
             return SetAttributesAndChangePermission(
                 address,
                 size,
                 MemoryState.TransferMemoryAllowed,
                 MemoryState.TransferMemoryAllowed,
-                MemoryPermission.Mask,
-                MemoryPermission.ReadAndWrite,
+                KMemoryPermission.Mask,
+                KMemoryPermission.ReadAndWrite,
                 MemoryAttribute.Mask,
                 MemoryAttribute.None,
                 permission,
@@ -2140,11 +2146,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             ulong            size,
             MemoryState      stateMask,
             MemoryState      stateExpected,
-            MemoryPermission permissionMask,
-            MemoryPermission permissionExpected,
+            KMemoryPermission permissionMask,
+            KMemoryPermission permissionExpected,
             MemoryAttribute  attributeMask,
             MemoryAttribute  attributeExpected,
-            MemoryPermission newPermission,
+            KMemoryPermission newPermission,
             MemoryAttribute  attributeSetMask,
             KPageList        pageList = null)
         {
@@ -2166,7 +2172,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                     attributeExpected,
                     MemoryAttribute.IpcAndDeviceMapped,
                     out MemoryState      oldState,
-                    out MemoryPermission oldPermission,
+                    out KMemoryPermission oldPermission,
                     out MemoryAttribute  oldAttribute))
                 {
                     ulong pagesCount = size / PageSize;
@@ -2181,7 +2187,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                         return KernelResult.OutOfResource;
                     }
 
-                    if (newPermission == MemoryPermission.None)
+                    if (newPermission == KMemoryPermission.None)
                     {
                         newPermission = oldPermission;
                     }
@@ -2222,11 +2228,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                 size,
                 MemoryState.IpcBufferAllowed,
                 MemoryState.IpcBufferAllowed,
-                MemoryPermission.None,
-                MemoryPermission.None,
+                KMemoryPermission.None,
+                KMemoryPermission.None,
                 MemoryAttribute.Mask,
                 MemoryAttribute.Borrowed,
-                MemoryPermission.ReadAndWrite,
+                KMemoryPermission.ReadAndWrite,
                 MemoryAttribute.Borrowed);
         }
 
@@ -2237,11 +2243,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                 size,
                 MemoryState.TransferMemoryAllowed,
                 MemoryState.TransferMemoryAllowed,
-                MemoryPermission.None,
-                MemoryPermission.None,
+                KMemoryPermission.None,
+                KMemoryPermission.None,
                 MemoryAttribute.Mask,
                 MemoryAttribute.Borrowed,
-                MemoryPermission.ReadAndWrite,
+                KMemoryPermission.ReadAndWrite,
                 MemoryAttribute.Borrowed,
                 pageList);
         }
@@ -2251,11 +2257,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             ulong            size,
             MemoryState      stateMask,
             MemoryState      stateExpected,
-            MemoryPermission permissionMask,
-            MemoryPermission permissionExpected,
+            KMemoryPermission permissionMask,
+            KMemoryPermission permissionExpected,
             MemoryAttribute  attributeMask,
             MemoryAttribute  attributeExpected,
-            MemoryPermission newPermission,
+            KMemoryPermission newPermission,
             MemoryAttribute  attributeClearMask,
             KPageList        pageList = null)
         {
@@ -2277,7 +2283,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                     attributeExpected,
                     MemoryAttribute.IpcAndDeviceMapped,
                     out MemoryState      oldState,
-                    out MemoryPermission oldPermission,
+                    out KMemoryPermission oldPermission,
                     out MemoryAttribute  oldAttribute))
                 {
                     ulong pagesCount = size / PageSize;
@@ -2299,7 +2305,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                         return KernelResult.OutOfResource;
                     }
 
-                    if (newPermission == MemoryPermission.None)
+                    if (newPermission == KMemoryPermission.None)
                     {
                         newPermission = oldPermission;
                     }
@@ -2385,8 +2391,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                 size,
                 MemoryState.Mask,
                 MemoryState.Unmapped,
-                MemoryPermission.Mask,
-                MemoryPermission.None,
+                KMemoryPermission.Mask,
+                KMemoryPermission.None,
                 MemoryAttribute.Mask,
                 MemoryAttribute.None,
                 MemoryAttribute.IpcAndDeviceMapped,
@@ -2400,13 +2406,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             ulong                size,
             MemoryState          stateMask,
             MemoryState          stateExpected,
-            MemoryPermission     permissionMask,
-            MemoryPermission     permissionExpected,
+            KMemoryPermission     permissionMask,
+            KMemoryPermission     permissionExpected,
             MemoryAttribute      attributeMask,
             MemoryAttribute      attributeExpected,
             MemoryAttribute      attributeIgnoreMask,
             out MemoryState      outState,
-            out MemoryPermission outPermission,
+            out KMemoryPermission outPermission,
             out MemoryAttribute  outAttribute)
         {
             ulong endAddr = address + size;
@@ -2416,7 +2422,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             KMemoryInfo info = node.Value.GetInfo();
 
             MemoryState      firstState      = info.State;
-            MemoryPermission firstPermission = info.Permission;
+            KMemoryPermission firstPermission = info.Permission;
             MemoryAttribute  firstAttribute  = info.Attribute;
 
             do
@@ -2432,7 +2438,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                     (firstPermission & permissionMask)      != permissionExpected)
                 {
                     outState      = MemoryState.Unmapped;
-                    outPermission = MemoryPermission.None;
+                    outPermission = KMemoryPermission.None;
                     outAttribute  = MemoryAttribute.None;
 
                     return false;
@@ -2452,8 +2458,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             ulong            size,
             MemoryState      stateMask,
             MemoryState      stateExpected,
-            MemoryPermission permissionMask,
-            MemoryPermission permissionExpected,
+            KMemoryPermission permissionMask,
+            KMemoryPermission permissionExpected,
             MemoryAttribute  attributeMask,
             MemoryAttribute  attributeExpected)
         {
@@ -2490,10 +2496,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             ulong            baseAddress,
             ulong            pagesCount,
             MemoryState      oldState,
-            MemoryPermission oldPermission,
+            KMemoryPermission oldPermission,
             MemoryAttribute  oldAttribute,
             MemoryState      newState,
-            MemoryPermission newPermission,
+            KMemoryPermission newPermission,
             MemoryAttribute  newAttribute)
         {
             // Insert new block on the list only on areas where the state
@@ -2553,13 +2559,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             }
 
             _blockAllocator.Count += _blocks.Count - oldCount;
+
+            ValidateInternalState();
         }
 
         private void InsertBlock(
             ulong            baseAddress,
             ulong            pagesCount,
             MemoryState      state,
-            MemoryPermission permission = MemoryPermission.None,
+            KMemoryPermission permission = KMemoryPermission.None,
             MemoryAttribute  attribute  = MemoryAttribute.None)
         {
             // Inserts new block at the list, replacing and splitting
@@ -2605,25 +2613,27 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             }
 
             _blockAllocator.Count += _blocks.Count - oldCount;
+
+            ValidateInternalState();
         }
 
-        private static void SetIpcMappingPermissions(KMemoryBlock block, MemoryPermission permission)
+        private static void SetIpcMappingPermissions(KMemoryBlock block, KMemoryPermission permission)
         {
             block.SetIpcMappingPermission(permission);
         }
 
-        private static void RestoreIpcMappingPermissions(KMemoryBlock block, MemoryPermission permission)
+        private static void RestoreIpcMappingPermissions(KMemoryBlock block, KMemoryPermission permission)
         {
             block.RestoreIpcMappingPermission();
         }
 
-        private delegate void BlockMutator(KMemoryBlock block, MemoryPermission newPerm);
+        private delegate void BlockMutator(KMemoryBlock block, KMemoryPermission newPerm);
 
         private void InsertBlock(
             ulong            baseAddress,
             ulong            pagesCount,
             BlockMutator     blockMutate,
-            MemoryPermission permission = MemoryPermission.None)
+            KMemoryPermission permission = KMemoryPermission.None)
         {
             // Inserts new block at the list, replacing and splitting
             // existing blocks as needed, then calling the callback
@@ -2671,6 +2681,31 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             }
 
             _blockAllocator.Count += _blocks.Count - oldCount;
+
+            ValidateInternalState();
+        }
+
+        [Conditional("DEBUG")]
+        private void ValidateInternalState()
+        {
+            ulong expectedAddress = 0;
+
+            LinkedListNode<KMemoryBlock> node = _blocks.First;
+
+            while (node != null)
+            {
+                LinkedListNode<KMemoryBlock> newNode = node;
+
+                KMemoryBlock currBlock = node.Value;
+
+                Debug.Assert(currBlock.BaseAddress == expectedAddress);
+
+                expectedAddress = currBlock.BaseAddress + currBlock.PagesCount * PageSize;
+
+                node = newNode.Next;
+            }
+
+            Debug.Assert(expectedAddress == AddrSpaceEnd);
         }
 
         private LinkedListNode<KMemoryBlock> MergeEqualStateNeighbors(LinkedListNode<KMemoryBlock> node)
@@ -2876,13 +2911,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             return null;
         }
 
-        private bool ValidateRegionForState(ulong address, ulong size, MemoryState state)
+        public bool CanContain(ulong address, ulong size, MemoryState state)
         {
             ulong endAddr = address + size;
 
-            ulong regionBaseAddr = GetBaseAddrForState(state);
-
-            ulong regionEndAddr = regionBaseAddr + GetSizeForState(state);
+            ulong regionBaseAddr = GetBaseAddress(state);
+            ulong regionEndAddr = regionBaseAddr + GetSize(state);
 
             bool InsideRegion()
             {
@@ -2891,17 +2925,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                        endAddr - 1    <= regionEndAddr - 1;
             }
 
-            bool OutsideHeapRegion()
-            {
-                return endAddr <= HeapRegionStart ||
-                       address >= HeapRegionEnd;
-            }
-
-            bool OutsideMapRegion()
-            {
-                return endAddr <= AliasRegionStart ||
-                       address >= AliasRegionEnd;
-            }
+            bool OutsideHeapRegion() => endAddr <= HeapRegionStart ||  address >= HeapRegionEnd;
+            bool OutsideAliasRegion() => endAddr <= AliasRegionStart || address >= AliasRegionEnd;
 
             switch (state)
             {
@@ -2919,10 +2944,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                 case MemoryState.ProcessMemory:
                 case MemoryState.CodeReadOnly:
                 case MemoryState.CodeWritable:
-                    return InsideRegion() && OutsideHeapRegion() && OutsideMapRegion();
+                    return InsideRegion() && OutsideHeapRegion() && OutsideAliasRegion();
 
                 case MemoryState.Heap:
-                    return InsideRegion() && OutsideMapRegion();
+                    return InsideRegion() && OutsideAliasRegion();
 
                 case MemoryState.IpcBuffer0:
                 case MemoryState.IpcBuffer1:
@@ -2936,7 +2961,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             throw new ArgumentException($"Invalid state value \"{state}\".");
         }
 
-        private ulong GetBaseAddrForState(MemoryState state)
+        private ulong GetBaseAddress(MemoryState state)
         {
             switch (state)
             {
@@ -2975,7 +3000,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             throw new ArgumentException($"Invalid state value \"{state}\".");
         }
 
-        private ulong GetSizeForState(MemoryState state)
+        private ulong GetSize(MemoryState state)
         {
             switch (state)
             {
@@ -3050,7 +3075,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             }
         }
 
-        private KernelResult MapPages(ulong address, KPageList pageList, MemoryPermission permission)
+        private KernelResult MapPages(ulong address, KPageList pageList, KMemoryPermission permission)
         {
             ulong currAddr = address;
 
@@ -3090,11 +3115,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                 pagesCount,
                 0,
                 false,
-                MemoryPermission.None,
+                KMemoryPermission.None,
                 MemoryOperation.Unmap);
         }
 
-        private KernelResult MmuChangePermission(ulong address, ulong pagesCount, MemoryPermission permission)
+        private KernelResult MmuChangePermission(ulong address, ulong pagesCount, KMemoryPermission permission)
         {
             return DoMmuOperation(
                 address,
@@ -3110,7 +3135,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             ulong            pagesCount,
             ulong            srcPa,
             bool             map,
-            MemoryPermission permission,
+            KMemoryPermission permission,
             MemoryOperation  operation)
         {
             if (map != (operation == MemoryOperation.MapPa))
@@ -3171,7 +3196,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             ulong            address,
             ulong            pagesCount,
             KPageList        pageList,
-            MemoryPermission permission,
+            KMemoryPermission permission,
             MemoryOperation  operation)
         {
             if (operation != MemoryOperation.MapVa)
diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs
index 65134f0d5f..ca0e3421ab 100644
--- a/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs
@@ -10,15 +10,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
 
         private readonly long _ownerPid;
 
-        private readonly MemoryPermission _ownerPermission;
-        private readonly MemoryPermission _userPermission;
+        private readonly KMemoryPermission _ownerPermission;
+        private readonly KMemoryPermission _userPermission;
 
         public KSharedMemory(
             KernelContext    context,
             KPageList        pageList,
             long             ownerPid,
-            MemoryPermission ownerPermission,
-            MemoryPermission userPermission) : base(context)
+            KMemoryPermission ownerPermission,
+            KMemoryPermission userPermission) : base(context)
         {
             _pageList        = pageList;
             _ownerPid        = ownerPid;
@@ -31,7 +31,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             ulong            address,
             ulong            size,
             KProcess         process,
-            MemoryPermission permission)
+            KMemoryPermission permission)
         {
             ulong pagesCountRounded = BitUtils.DivRoundUp(size, KMemoryManager.PageSize);
 
@@ -40,7 +40,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                 return KernelResult.InvalidSize;
             }
 
-            MemoryPermission expectedPermission = process.Pid == _ownerPid
+            KMemoryPermission expectedPermission = process.Pid == _ownerPid
                 ? _ownerPermission
                 : _userPermission;
 
diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs
index d3e6208e34..96349452a5 100644
--- a/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs
@@ -8,12 +8,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
     {
         private KProcess _creator;
 
+        // TODO: Remove when we no longer need to read it from the owner directly.
+        public KProcess Creator => _creator;
+
         private readonly KPageList _pageList;
 
         public ulong Address { get; private set; }
         public ulong Size => _pageList.GetPagesCount() * KMemoryManager.PageSize;
 
-        public MemoryPermission Permission { get; private set; }
+        public KMemoryPermission Permission { get; private set; }
 
         private bool _hasBeenInitialized;
         private bool _isMapped;
@@ -23,7 +26,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             _pageList = new KPageList();
         }
 
-        public KernelResult Initialize(ulong address, ulong size, MemoryPermission permission)
+        public KernelResult Initialize(ulong address, ulong size, KMemoryPermission permission)
         {
             KProcess creator = KernelContext.Scheduler.GetCurrentProcess();
 
diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/MemoryPermission.cs b/Ryujinx.HLE/HOS/Kernel/Memory/MemoryPermission.cs
index 0ad90abd7c..8bfd8d944d 100644
--- a/Ryujinx.HLE/HOS/Kernel/Memory/MemoryPermission.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Memory/MemoryPermission.cs
@@ -3,7 +3,7 @@ using System;
 namespace Ryujinx.HLE.HOS.Kernel.Memory
 {
     [Flags]
-    enum MemoryPermission : byte
+    enum KMemoryPermission : byte
     {
         None = 0,
         Mask = 0xff,
diff --git a/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs b/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs
index 376badfb6b..5ad33154d0 100644
--- a/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs
@@ -1,7 +1,7 @@
-using Ryujinx.Cpu;
 using Ryujinx.HLE.HOS.Diagnostics.Demangler;
 using Ryujinx.HLE.HOS.Kernel.Memory;
 using Ryujinx.HLE.Loaders.Elf;
+using Ryujinx.Memory;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
@@ -224,7 +224,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
                     break;
                 }
 
-                if (info.State == MemoryState.CodeStatic && info.Permission == MemoryPermission.ReadAndExecute)
+                if (info.State == MemoryState.CodeStatic && info.Permission == KMemoryPermission.ReadAndExecute)
                 {
                     LoadMod0Symbols(_owner.CpuMemory, info.Address);
                 }
@@ -235,7 +235,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
             }
         }
 
-        private void LoadMod0Symbols(MemoryManager memory, ulong textOffset)
+        private void LoadMod0Symbols(IVirtualMemoryManager memory, ulong textOffset)
         {
             ulong mod0Offset = textOffset + memory.Read<uint>(textOffset + 4);
 
@@ -319,7 +319,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
             }
         }
 
-        private ElfSymbol GetSymbol64(MemoryManager memory, ulong address, ulong strTblAddr)
+        private ElfSymbol GetSymbol64(IVirtualMemoryManager memory, ulong address, ulong strTblAddr)
         {
             ElfSymbol64 sym = memory.Read<ElfSymbol64>(address);
 
@@ -335,7 +335,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
             return new ElfSymbol(name, sym.Info, sym.Other, sym.SectionIndex, sym.ValueAddress, sym.Size);
         }
 
-        private ElfSymbol GetSymbol32(MemoryManager memory, ulong address, ulong strTblAddr)
+        private ElfSymbol GetSymbol32(IVirtualMemoryManager memory, ulong address, ulong strTblAddr)
         {
             ElfSymbol32 sym = memory.Read<ElfSymbol32>(address);
 
diff --git a/Ryujinx.HLE/HOS/Kernel/Process/IProcessContext.cs b/Ryujinx.HLE/HOS/Kernel/Process/IProcessContext.cs
new file mode 100644
index 0000000000..758ac6b10b
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/Process/IProcessContext.cs
@@ -0,0 +1,13 @@
+using ARMeilleure.State;
+using Ryujinx.Memory;
+using System;
+
+namespace Ryujinx.HLE.HOS.Kernel.Process
+{
+    interface IProcessContext : IDisposable
+    {
+        IVirtualMemoryManager AddressSpace { get; }
+
+        void Execute(ExecutionContext context, ulong codeAddress);
+    }
+}
diff --git a/Ryujinx.HLE/HOS/Kernel/Process/IProcessContextFactory.cs b/Ryujinx.HLE/HOS/Kernel/Process/IProcessContextFactory.cs
new file mode 100644
index 0000000000..c438b570e6
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/Process/IProcessContextFactory.cs
@@ -0,0 +1,10 @@
+using Ryujinx.Cpu;
+using Ryujinx.Memory;
+
+namespace Ryujinx.HLE.HOS.Kernel.Process
+{
+    interface IProcessContextFactory
+    {
+        IProcessContext Create(MemoryBlock backingMemory, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler);
+    }
+}
diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs
index d02e25a3ca..8e914f1943 100644
--- a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs
@@ -6,6 +6,7 @@ using Ryujinx.HLE.Exceptions;
 using Ryujinx.HLE.HOS.Kernel.Common;
 using Ryujinx.HLE.HOS.Kernel.Memory;
 using Ryujinx.HLE.HOS.Kernel.Threading;
+using Ryujinx.Memory;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -15,13 +16,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
 {
     class KProcess : KSynchronizationObject
     {
-        public const int KernelVersionMajor    = 10;
-        public const int KernelVersionMinor    = 4;
+        public const int KernelVersionMajor = 10;
+        public const int KernelVersionMinor = 4;
         public const int KernelVersionRevision = 0;
 
         public const int KernelVersionPacked =
-            (KernelVersionMajor    << 19) |
-            (KernelVersionMinor    << 15) |
+            (KernelVersionMajor << 19) |
+            (KernelVersionMinor << 15) |
             (KernelVersionRevision << 0);
 
         public KMemoryManager MemoryManager { get; private set; }
@@ -47,27 +48,27 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
         public long[] RandomEntropy { get; private set; }
 
         private bool _signaled;
-        private bool _useSystemMemBlocks;
 
         public string Name { get; private set; }
 
         private int _threadCount;
 
-        public int MmuFlags { get; private set; }
+        public ProcessCreationFlags Flags { get; private set; }
 
         private MemoryRegion _memRegion;
 
         public KProcessCapabilities Capabilities { get; private set; }
 
         public ulong TitleId { get; private set; }
-        public long  Pid     { get; private set; }
+        public long Pid { get; private set; }
 
-        private long  _creationTimestamp;
+        private long _creationTimestamp;
         private ulong _entrypoint;
+        private ThreadStart _customThreadStart;
         private ulong _imageSize;
         private ulong _mainThreadStackSize;
         private ulong _memoryUsageCapacity;
-        private int   _version;
+        private int _version;
 
         public KHandleTable HandleTable { get; private set; }
 
@@ -77,14 +78,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
 
         public bool IsPaused { get; private set; }
 
-        public MemoryManager CpuMemory { get; private set; }
-        public CpuContext CpuContext { get; private set; }
+        private IProcessContextFactory _contextFactory;
+        public IProcessContext Context { get; private set; }
+        public IVirtualMemoryManager CpuMemory => Context.AddressSpace;
 
         public HleProcessDebugger Debugger { get; private set; }
 
         public KProcess(KernelContext context) : base(context)
         {
-            _processLock   = new object();
+            _processLock = new object();
             _threadingLock = new object();
 
             AddressArbiter = new KAddressArbiter(context);
@@ -96,6 +98,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
 
             RandomEntropy = new long[KScheduler.CpuCoresCount];
 
+            // TODO: Remove once we no longer need to initialize it externally.
+            HandleTable = new KHandleTable(context);
+
             _threads = new LinkedList<KThread>();
 
             Debugger = new HleProcessDebugger(this);
@@ -103,25 +108,27 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
 
         public KernelResult InitializeKip(
             ProcessCreationInfo creationInfo,
-            int[]               caps,
-            KPageList           pageList,
-            KResourceLimit      resourceLimit,
-            MemoryRegion        memRegion)
+            ReadOnlySpan<int> capabilities,
+            KPageList pageList,
+            KResourceLimit resourceLimit,
+            MemoryRegion memRegion,
+            IProcessContextFactory contextFactory)
         {
             ResourceLimit = resourceLimit;
-            _memRegion     = memRegion;
+            _memRegion = memRegion;
+            _contextFactory = contextFactory ?? new ProcessContextFactory();
 
-            AddressSpaceType addrSpaceType = (AddressSpaceType)((creationInfo.MmuFlags >> 1) & 7);
+            AddressSpaceType addrSpaceType = (AddressSpaceType)((int)(creationInfo.Flags & ProcessCreationFlags.AddressSpaceMask) >> (int)ProcessCreationFlags.AddressSpaceShift);
 
-            InitializeMemoryManager(addrSpaceType, memRegion);
+            InitializeMemoryManager(creationInfo.Flags);
 
-            bool aslrEnabled = ((creationInfo.MmuFlags >> 5) & 1) != 0;
+            bool aslrEnabled = creationInfo.Flags.HasFlag(ProcessCreationFlags.EnableAslr);
 
             ulong codeAddress = creationInfo.CodeAddress;
 
             ulong codeSize = (ulong)creationInfo.CodePagesCount * KMemoryManager.PageSize;
 
-            KMemoryBlockAllocator memoryBlockAllocator = (MmuFlags & 0x40) != 0
+            KMemoryBlockAllocator memoryBlockAllocator = creationInfo.Flags.HasFlag(ProcessCreationFlags.IsApplication)
                 ? KernelContext.LargeMemoryBlockAllocator
                 : KernelContext.SmallMemoryBlockAllocator;
 
@@ -139,7 +146,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
                 return result;
             }
 
-            if (!ValidateCodeAddressAndSize(codeAddress, codeSize))
+            if (!MemoryManager.CanContain(codeAddress, codeSize, MemoryState.CodeStatic))
             {
                 return KernelResult.InvalidMemRange;
             }
@@ -148,14 +155,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
                 codeAddress,
                 pageList,
                 MemoryState.CodeStatic,
-                MemoryPermission.None);
+                KMemoryPermission.None);
 
             if (result != KernelResult.Success)
             {
                 return result;
             }
 
-            result = Capabilities.InitializeForKernel(caps, MemoryManager);
+            result = Capabilities.InitializeForKernel(capabilities, MemoryManager);
 
             if (result != KernelResult.Success)
             {
@@ -176,14 +183,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
 
         public KernelResult Initialize(
             ProcessCreationInfo creationInfo,
-            int[]               caps,
-            KResourceLimit      resourceLimit,
-            MemoryRegion        memRegion)
+            ReadOnlySpan<int> capabilities,
+            KResourceLimit resourceLimit,
+            MemoryRegion memRegion,
+            IProcessContextFactory contextFactory,
+            ThreadStart customThreadStart = null)
         {
             ResourceLimit = resourceLimit;
-            _memRegion    = memRegion;
+            _memRegion = memRegion;
+            _contextFactory = contextFactory ?? new ProcessContextFactory();
 
-            ulong personalMmHeapSize = GetPersonalMmHeapSize((ulong)creationInfo.PersonalMmHeapPagesCount, memRegion);
+            ulong personalMmHeapSize = GetPersonalMmHeapSize((ulong)creationInfo.SystemResourcePagesCount, memRegion);
 
             ulong codePagesCount = (ulong)creationInfo.CodePagesCount;
 
@@ -205,7 +215,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
                 }
             }
 
-            PersonalMmHeapPagesCount = (ulong)creationInfo.PersonalMmHeapPagesCount;
+            PersonalMmHeapPagesCount = (ulong)creationInfo.SystemResourcePagesCount;
 
             KMemoryBlockAllocator memoryBlockAllocator;
 
@@ -215,16 +225,16 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
             }
             else
             {
-                memoryBlockAllocator = (MmuFlags & 0x40) != 0
+                memoryBlockAllocator = creationInfo.Flags.HasFlag(ProcessCreationFlags.IsApplication)
                     ? KernelContext.LargeMemoryBlockAllocator
                     : KernelContext.SmallMemoryBlockAllocator;
             }
 
-            AddressSpaceType addrSpaceType = (AddressSpaceType)((creationInfo.MmuFlags >> 1) & 7);
+            AddressSpaceType addrSpaceType = (AddressSpaceType)((int)(creationInfo.Flags & ProcessCreationFlags.AddressSpaceMask) >> (int)ProcessCreationFlags.AddressSpaceShift);
 
-            InitializeMemoryManager(addrSpaceType, memRegion);
+            InitializeMemoryManager(creationInfo.Flags);
 
-            bool aslrEnabled = ((creationInfo.MmuFlags >> 5) & 1) != 0;
+            bool aslrEnabled = creationInfo.Flags.HasFlag(ProcessCreationFlags.EnableAslr);
 
             ulong codeAddress = creationInfo.CodeAddress;
 
@@ -246,7 +256,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
                 return result;
             }
 
-            if (!ValidateCodeAddressAndSize(codeAddress, codeSize))
+            if (!MemoryManager.CanContain(codeAddress, codeSize, MemoryState.CodeStatic))
             {
                 CleanUpForError();
 
@@ -257,7 +267,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
                 codeAddress,
                 codePagesCount,
                 MemoryState.CodeStatic,
-                MemoryPermission.None);
+                KMemoryPermission.None);
 
             if (result != KernelResult.Success)
             {
@@ -266,7 +276,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
                 return result;
             }
 
-            result = Capabilities.InitializeForUser(caps, MemoryManager);
+            result = Capabilities.InitializeForUser(capabilities, MemoryManager);
 
             if (result != KernelResult.Success)
             {
@@ -289,57 +299,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
                 CleanUpForError();
             }
 
+            _customThreadStart = customThreadStart;
+
             return result;
         }
 
-        private bool ValidateCodeAddressAndSize(ulong address, ulong size)
-        {
-            ulong codeRegionStart;
-            ulong codeRegionSize;
-
-            switch (MemoryManager.AddrSpaceWidth)
-            {
-                case 32:
-                    codeRegionStart = 0x200000;
-                    codeRegionSize  = 0x3fe00000;
-                    break;
-
-                case 36:
-                    codeRegionStart = 0x8000000;
-                    codeRegionSize  = 0x78000000;
-                    break;
-
-                case 39:
-                    codeRegionStart = 0x8000000;
-                    codeRegionSize  = 0x7ff8000000;
-                    break;
-
-                default: throw new InvalidOperationException("Invalid address space width on memory manager.");
-            }
-
-            ulong endAddr = address + size;
-
-            ulong codeRegionEnd = codeRegionStart + codeRegionSize;
-
-            if (endAddr     <= address ||
-                endAddr - 1 >  codeRegionEnd - 1)
-            {
-                return false;
-            }
-
-            if (MemoryManager.InsideHeapRegion (address, size) ||
-                MemoryManager.InsideAliasRegion(address, size))
-            {
-                return false;
-            }
-
-            return true;
-        }
-
         private KernelResult ParseProcessInfo(ProcessCreationInfo creationInfo)
         {
             // Ensure that the current kernel version is equal or above to the minimum required.
-            uint requiredKernelVersionMajor =  (uint)Capabilities.KernelReleaseVersion >> 19;
+            uint requiredKernelVersionMajor = (uint)Capabilities.KernelReleaseVersion >> 19;
             uint requiredKernelVersionMinor = ((uint)Capabilities.KernelReleaseVersion >> 15) & 0xf;
 
             if (KernelContext.EnableVersionChecks)
@@ -377,31 +345,29 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
 
             _creationTimestamp = PerformanceCounter.ElapsedMilliseconds;
 
-            MmuFlags    = creationInfo.MmuFlags;
-            _version   = creationInfo.Version;
-            TitleId     = creationInfo.TitleId;
+            Flags = creationInfo.Flags;
+            _version = creationInfo.Version;
+            TitleId = creationInfo.TitleId;
             _entrypoint = creationInfo.CodeAddress;
-            _imageSize  = (ulong)creationInfo.CodePagesCount * KMemoryManager.PageSize;
+            _imageSize = (ulong)creationInfo.CodePagesCount * KMemoryManager.PageSize;
 
-            _useSystemMemBlocks = ((MmuFlags >> 6) & 1) != 0;
-
-            switch ((AddressSpaceType)((MmuFlags >> 1) & 7))
+            switch (Flags & ProcessCreationFlags.AddressSpaceMask)
             {
-                case AddressSpaceType.Addr32Bits:
-                case AddressSpaceType.Addr36Bits:
-                case AddressSpaceType.Addr39Bits:
+                case ProcessCreationFlags.AddressSpace32Bit:
+                case ProcessCreationFlags.AddressSpace64BitDeprecated:
+                case ProcessCreationFlags.AddressSpace64Bit:
                     _memoryUsageCapacity = MemoryManager.HeapRegionEnd -
                                            MemoryManager.HeapRegionStart;
                     break;
 
-                case AddressSpaceType.Addr32BitsNoMap:
+                case ProcessCreationFlags.AddressSpace32BitWithoutAlias:
                     _memoryUsageCapacity = MemoryManager.HeapRegionEnd -
                                            MemoryManager.HeapRegionStart +
                                            MemoryManager.AliasRegionEnd -
                                            MemoryManager.AliasRegionStart;
                     break;
 
-                default: throw new InvalidOperationException($"Invalid MMU flags value 0x{MmuFlags:x2}.");
+                default: throw new InvalidOperationException($"Invalid MMU flags value 0x{Flags:x2}.");
             }
 
             GenerateRandomEntropy();
@@ -469,7 +435,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
             }
 
             ulong regionStart = MemoryManager.TlsIoRegionStart;
-            ulong regionSize  = MemoryManager.TlsIoRegionEnd - regionStart;
+            ulong regionSize = MemoryManager.TlsIoRegionEnd - regionStart;
 
             ulong regionPagesCount = regionSize / KMemoryManager.PageSize;
 
@@ -481,7 +447,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
                 regionStart,
                 regionPagesCount,
                 MemoryState.ThreadLocal,
-                MemoryPermission.ReadAndWrite,
+                KMemoryPermission.ReadAndWrite,
                 out ulong tlsPageVa);
 
             if (result != KernelResult.Success)
@@ -506,7 +472,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
 
             KernelResult result = KernelResult.Success;
 
-            KTlsPageInfo pageInfo = null;
+            KTlsPageInfo pageInfo;
 
             if (_fullTlsPages.TryGetValue(tlsPageAddr, out pageInfo))
             {
@@ -594,8 +560,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
                 // Check if the needed size for the code and the stack will fit on the
                 // memory usage capacity of this Process. Also check for possible overflow
                 // on the above addition.
-                if (neededSize > _memoryUsageCapacity ||
-                    neededSize < stackSizeRounded)
+                if (neededSize > _memoryUsageCapacity || neededSize < stackSizeRounded)
                 {
                     threadResourceLimit?.Release(LimitableResource.Thread, 1);
 
@@ -646,7 +611,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
                     ulong stackPagesCount = stackSizeRounded / KMemoryManager.PageSize;
 
                     ulong regionStart = MemoryManager.StackRegionStart;
-                    ulong regionSize  = MemoryManager.StackRegionEnd - regionStart;
+                    ulong regionSize = MemoryManager.StackRegionEnd - regionStart;
 
                     ulong regionPagesCount = regionSize / KMemoryManager.PageSize;
 
@@ -658,7 +623,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
                         regionStart,
                         regionPagesCount,
                         MemoryState.Stack,
-                        MemoryPermission.ReadAndWrite,
+                        KMemoryPermission.ReadAndWrite,
                         out ulong stackBottom);
 
                     if (result != KernelResult.Success)
@@ -703,7 +668,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
                     stackTop,
                     mainThreadPriority,
                     DefaultCpuCore,
-                    this);
+                    this,
+                    ThreadType.User,
+                    _customThreadStart);
 
                 if (result != KernelResult.Success)
                 {
@@ -730,20 +697,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
 
                 SetState(newState);
 
-                // TODO: We can't call KThread.Start from a non-guest thread.
-                // We will need to make some changes to allow the creation of
-                // dummy threads that will be used to initialize the current
-                // thread on KCoreContext so that GetCurrentThread doesn't fail.
-                /* Result = MainThread.Start();
+                result = mainThread.Start();
 
-                if (Result != KernelResult.Success)
+                if (result != KernelResult.Success)
                 {
-                    SetState(OldState);
+                    SetState(oldState);
 
                     CleanUpForError();
-                } */
-
-                mainThread.Reschedule(ThreadSchedState.Running);
+                }
 
                 if (result == KernelResult.Success)
                 {
@@ -760,7 +721,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
         {
             if (State != newState)
             {
-                State     = newState;
+                State = newState;
                 _signaled = true;
 
                 Signal();
@@ -769,23 +730,23 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
 
         public KernelResult InitializeThread(
             KThread thread,
-            ulong   entrypoint,
-            ulong   argsPtr,
-            ulong   stackTop,
-            int     priority,
-            int     cpuCore)
+            ulong entrypoint,
+            ulong argsPtr,
+            ulong stackTop,
+            int priority,
+            int cpuCore)
         {
             lock (_processLock)
             {
-                return thread.Initialize(entrypoint, argsPtr, stackTop, priority, cpuCore, this);
+                return thread.Initialize(entrypoint, argsPtr, stackTop, priority, cpuCore, this, ThreadType.User, null);
             }
         }
 
         public void SubscribeThreadEventHandlers(ARMeilleure.State.ExecutionContext context)
         {
-            context.Interrupt      += InterruptHandler;
+            context.Interrupt += InterruptHandler;
             context.SupervisorCall += KernelContext.SyscallHandler.SvcCall;
-            context.Undefined      += UndefinedInstructionHandler;
+            context.Undefined += UndefinedInstructionHandler;
         }
 
         private void InterruptHandler(object sender, EventArgs e)
@@ -910,8 +871,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
             {
                 if (State >= ProcessState.Started)
                 {
-                    if (State == ProcessState.Started  ||
-                        State == ProcessState.Crashed  ||
+                    if (State == ProcessState.Started ||
+                        State == ProcessState.Crashed ||
                         State == ProcessState.Attached ||
                         State == ProcessState.DebugSuspended)
                     {
@@ -1072,23 +1033,25 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
             return result;
         }
 
-        private void InitializeMemoryManager(AddressSpaceType addrSpaceType, MemoryRegion memRegion)
+        private void InitializeMemoryManager(ProcessCreationFlags flags)
         {
-            int addrSpaceBits = addrSpaceType switch
+            int addrSpaceBits = (flags & ProcessCreationFlags.AddressSpaceMask) switch
             {
-                AddressSpaceType.Addr32Bits => 32,
-                AddressSpaceType.Addr36Bits => 36,
-                AddressSpaceType.Addr32BitsNoMap => 32,
-                AddressSpaceType.Addr39Bits => 39,
-                _ => throw new ArgumentException(nameof(addrSpaceType))
+                ProcessCreationFlags.AddressSpace32Bit => 32,
+                ProcessCreationFlags.AddressSpace64BitDeprecated => 36,
+                ProcessCreationFlags.AddressSpace32BitWithoutAlias => 32,
+                ProcessCreationFlags.AddressSpace64Bit => 39,
+                _ => 39
             };
 
-            CpuMemory = new MemoryManager(KernelContext.Memory, 1UL << addrSpaceBits, InvalidAccessHandler);
-            CpuContext = new CpuContext(CpuMemory);
+            Context = _contextFactory.Create(KernelContext.Memory, 1UL << addrSpaceBits, InvalidAccessHandler);
 
             // TODO: This should eventually be removed.
             // The GPU shouldn't depend on the CPU memory manager at all.
-            KernelContext.Device.Gpu.SetVmm(CpuMemory);
+            if (flags.HasFlag(ProcessCreationFlags.IsApplication))
+            {
+                KernelContext.Device.Gpu.SetVmm((MemoryManager)CpuMemory);
+            }
 
             MemoryManager = new KMemoryManager(KernelContext, CpuMemory);
         }
@@ -1109,9 +1072,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
             throw new UndefinedInstructionException(e.Address, e.OpCode);
         }
 
-        protected override void Destroy()
-        {
-            CpuMemory.Dispose();
-        }
+        protected override void Destroy() => Context.Dispose();
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs b/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs
index 2396aea83e..e1cdb30f73 100644
--- a/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs
@@ -2,6 +2,7 @@ using Ryujinx.Common;
 using Ryujinx.HLE.HOS.Kernel.Common;
 using Ryujinx.HLE.HOS.Kernel.Memory;
 using Ryujinx.HLE.HOS.Kernel.Threading;
+using System;
 
 namespace Ryujinx.HLE.HOS.Kernel.Process
 {
@@ -24,29 +25,29 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
             IrqAccessMask = new byte[0x80];
         }
 
-        public KernelResult InitializeForKernel(int[] caps, KMemoryManager memoryManager)
+        public KernelResult InitializeForKernel(ReadOnlySpan<int> capabilities, KMemoryManager memoryManager)
         {
             AllowedCpuCoresMask    = 0xf;
             AllowedThreadPriosMask = -1;
             DebuggingFlags        &= ~3;
             KernelReleaseVersion   = KProcess.KernelVersionPacked;
 
-            return Parse(caps, memoryManager);
+            return Parse(capabilities, memoryManager);
         }
 
-        public KernelResult InitializeForUser(int[] caps, KMemoryManager memoryManager)
+        public KernelResult InitializeForUser(ReadOnlySpan<int> capabilities, KMemoryManager memoryManager)
         {
-            return Parse(caps, memoryManager);
+            return Parse(capabilities, memoryManager);
         }
 
-        private KernelResult Parse(int[] caps, KMemoryManager memoryManager)
+        private KernelResult Parse(ReadOnlySpan<int> capabilities, KMemoryManager memoryManager)
         {
             int mask0 = 0;
             int mask1 = 0;
 
-            for (int index = 0; index < caps.Length; index++)
+            for (int index = 0; index < capabilities.Length; index++)
             {
-                int cap = caps[index];
+                int cap = capabilities[index];
 
                 if (((cap + 1) & ~cap) != 0x40)
                 {
@@ -59,14 +60,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
                 }
                 else
                 {
-                    if ((uint)index + 1 >= caps.Length)
+                    if ((uint)index + 1 >= capabilities.Length)
                     {
                         return KernelResult.InvalidCombination;
                     }
 
                     int prevCap = cap;
 
-                    cap = caps[++index];
+                    cap = capabilities[++index];
 
                     if (((cap + 1) & ~cap) != 0x40)
                     {
@@ -91,9 +92,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
                         return KernelResult.InvalidAddress;
                     }
 
-                    MemoryPermission perm = (prevCap >> 31) != 0
-                        ? MemoryPermission.Read
-                        : MemoryPermission.ReadAndWrite;
+                    KMemoryPermission perm = (prevCap >> 31) != 0
+                        ? KMemoryPermission.Read
+                        : KMemoryPermission.ReadAndWrite;
 
                     KernelResult result;
 
@@ -216,7 +217,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
                 {
                     long address = ((long)(uint)cap << 4) & 0xffffff000;
 
-                    memoryManager.MapIoMemory(address, KMemoryManager.PageSize, MemoryPermission.ReadAndWrite);
+                    memoryManager.MapIoMemory(address, KMemoryManager.PageSize, KMemoryPermission.ReadAndWrite);
 
                     break;
                 }
diff --git a/Ryujinx.HLE/HOS/Kernel/Process/ProcessContext.cs b/Ryujinx.HLE/HOS/Kernel/Process/ProcessContext.cs
new file mode 100644
index 0000000000..54997cb7c0
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/Process/ProcessContext.cs
@@ -0,0 +1,25 @@
+using ARMeilleure.State;
+using Ryujinx.Memory;
+using System;
+
+namespace Ryujinx.HLE.HOS.Kernel.Process
+{
+    class ProcessContext : IProcessContext
+    {
+        public IVirtualMemoryManager AddressSpace { get; }
+
+        public ProcessContext(IVirtualMemoryManager asManager)
+        {
+            AddressSpace = asManager;
+        }
+
+        public void Execute(ExecutionContext context, ulong codeAddress)
+        {
+            throw new NotSupportedException();
+        }
+
+        public void Dispose()
+        {
+        }
+    }
+}
diff --git a/Ryujinx.HLE/HOS/Kernel/Process/ProcessContextFactory.cs b/Ryujinx.HLE/HOS/Kernel/Process/ProcessContextFactory.cs
new file mode 100644
index 0000000000..03db62fa97
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/Process/ProcessContextFactory.cs
@@ -0,0 +1,13 @@
+using Ryujinx.Cpu;
+using Ryujinx.Memory;
+
+namespace Ryujinx.HLE.HOS.Kernel.Process
+{
+    class ProcessContextFactory : IProcessContextFactory
+    {
+        public IProcessContext Create(MemoryBlock backingMemory, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler)
+        {
+            return new ProcessContext(new AddressSpaceManager(backingMemory, addressSpaceSize));
+        }
+    }
+}
diff --git a/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationFlags.cs b/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationFlags.cs
new file mode 100644
index 0000000000..a34481e55e
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationFlags.cs
@@ -0,0 +1,38 @@
+namespace Ryujinx.HLE.HOS.Kernel.Process
+{
+    enum ProcessCreationFlags
+    {
+        Is64Bit = 1 << 0,
+
+        AddressSpaceShift = 1,
+        AddressSpace32Bit = 0 << AddressSpaceShift,
+        AddressSpace64BitDeprecated = 1 << AddressSpaceShift,
+        AddressSpace32BitWithoutAlias = 2 << AddressSpaceShift,
+        AddressSpace64Bit = 3 << AddressSpaceShift,
+        AddressSpaceMask = 7 << AddressSpaceShift,
+
+        EnableDebug = 1 << 4,
+        EnableAslr = 1 << 5,
+        IsApplication = 1 << 6,
+        DeprecatedUseSecureMemory = 1 << 7,
+
+        PoolPartitionShift = 7,
+        PoolPartitionApplication = 0 << PoolPartitionShift,
+        PoolPartitionApplet = 1 << PoolPartitionShift,
+        PoolPartitionSystem = 2 << PoolPartitionShift,
+        PoolPartitionSystemNonSecure = 3 << PoolPartitionShift,
+        PoolPartitionMask = 0xf << PoolPartitionShift,
+
+        OptimizeMemoryAllocation = 1 << 11,
+
+        All =
+            Is64Bit |
+            AddressSpaceMask |
+            EnableDebug |
+            EnableAslr |
+            IsApplication |
+            DeprecatedUseSecureMemory |
+            PoolPartitionMask |
+            OptimizeMemoryAllocation
+    }
+}
diff --git a/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationInfo.cs b/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationInfo.cs
index a582086538..26c23b3bb0 100644
--- a/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationInfo.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationInfo.cs
@@ -2,36 +2,36 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
 {
     struct ProcessCreationInfo
     {
-        public string Name { get; private set; }
+        public string Name { get; }
 
-        public int   Version { get; private set; }
-        public ulong TitleId  { get; private set; }
+        public int Version { get; }
+        public ulong TitleId { get; }
 
-        public ulong CodeAddress    { get; private set; }
-        public int   CodePagesCount { get; private set; }
+        public ulong CodeAddress { get; }
+        public int CodePagesCount { get; }
 
-        public int MmuFlags                 { get; private set; }
-        public int ResourceLimitHandle      { get; private set; }
-        public int PersonalMmHeapPagesCount { get; private set; }
+        public ProcessCreationFlags Flags { get; }
+        public int ResourceLimitHandle { get; }
+        public int SystemResourcePagesCount { get; }
 
         public ProcessCreationInfo(
             string name,
-            int    category,
-            ulong  titleId,
-            ulong  codeAddress,
-            int    codePagesCount,
-            int    mmuFlags,
-            int    resourceLimitHandle,
-            int    personalMmHeapPagesCount)
+            int version,
+            ulong titleId,
+            ulong codeAddress,
+            int codePagesCount,
+            ProcessCreationFlags flags,
+            int resourceLimitHandle,
+            int systemResourcePagesCount)
         {
-            Name                     = name;
-            Version                  = category;
-            TitleId                  = titleId;
-            CodeAddress              = codeAddress;
-            CodePagesCount           = codePagesCount;
-            MmuFlags                 = mmuFlags;
-            ResourceLimitHandle      = resourceLimitHandle;
-            PersonalMmHeapPagesCount = personalMmHeapPagesCount;
+            Name = name;
+            Version = version;
+            TitleId = titleId;
+            CodeAddress = codeAddress;
+            CodePagesCount = codePagesCount;
+            Flags = flags;
+            ResourceLimitHandle = resourceLimitHandle;
+            SystemResourcePagesCount = systemResourcePagesCount;
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs
index c1e7026be8..5e32ca5805 100644
--- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs
@@ -7,21 +7,167 @@ using Ryujinx.HLE.HOS.Kernel.Ipc;
 using Ryujinx.HLE.HOS.Kernel.Memory;
 using Ryujinx.HLE.HOS.Kernel.Process;
 using Ryujinx.HLE.HOS.Kernel.Threading;
+using Ryujinx.Memory;
 using System;
+using System.Threading;
 
 namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
 {
     class Syscall
     {
-        private readonly Switch _device;
         private readonly KernelContext _context;
 
-        public Syscall(Switch device, KernelContext context)
+        public Syscall(KernelContext context)
         {
-            _device = device;
             _context = context;
         }
 
+        // Process
+
+        public KernelResult GetProcessId(int handle, out long pid)
+        {
+            KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+
+            KProcess process = currentProcess.HandleTable.GetKProcess(handle);
+
+            if (process == null)
+            {
+                KThread thread = currentProcess.HandleTable.GetKThread(handle);
+
+                if (thread != null)
+                {
+                    process = thread.Owner;
+                }
+
+                // TODO: KDebugEvent.
+            }
+
+            pid = process?.Pid ?? 0;
+
+            return process != null
+                ? KernelResult.Success
+                : KernelResult.InvalidHandle;
+        }
+
+        public KernelResult CreateProcess(
+            ProcessCreationInfo info,
+            ReadOnlySpan<int> capabilities,
+            out int handle,
+            IProcessContextFactory contextFactory,
+            ThreadStart customThreadStart = null)
+        {
+            handle = 0;
+
+            if ((info.Flags & ~ProcessCreationFlags.All) != 0)
+            {
+                return KernelResult.InvalidEnumValue;
+            }
+
+            // TODO: Address space check.
+
+            if ((info.Flags & ProcessCreationFlags.PoolPartitionMask) > ProcessCreationFlags.PoolPartitionSystemNonSecure)
+            {
+                return KernelResult.InvalidEnumValue;
+            }
+
+            if ((info.CodeAddress & 0x1fffff) != 0)
+            {
+                return KernelResult.InvalidAddress;
+            }
+
+            if (info.CodePagesCount < 0 || info.SystemResourcePagesCount < 0)
+            {
+                return KernelResult.InvalidSize;
+            }
+
+            if (info.Flags.HasFlag(ProcessCreationFlags.OptimizeMemoryAllocation) &&
+                !info.Flags.HasFlag(ProcessCreationFlags.IsApplication))
+            {
+                return KernelResult.InvalidThread;
+            }
+
+            KHandleTable handleTable = _context.Scheduler.GetCurrentProcess().HandleTable;
+
+            KProcess process = new KProcess(_context);
+
+            using var _ = new OnScopeExit(process.DecrementReferenceCount);
+
+            KResourceLimit resourceLimit;
+
+            if (info.ResourceLimitHandle != 0)
+            {
+                resourceLimit = handleTable.GetObject<KResourceLimit>(info.ResourceLimitHandle);
+
+                if (resourceLimit == null)
+                {
+                    return KernelResult.InvalidHandle;
+                }
+            }
+            else
+            {
+                resourceLimit = _context.ResourceLimit;
+            }
+
+            MemoryRegion memRegion = (info.Flags & ProcessCreationFlags.PoolPartitionMask) switch
+            {
+                ProcessCreationFlags.PoolPartitionApplication => MemoryRegion.Application,
+                ProcessCreationFlags.PoolPartitionApplet => MemoryRegion.Applet,
+                ProcessCreationFlags.PoolPartitionSystem => MemoryRegion.Service,
+                ProcessCreationFlags.PoolPartitionSystemNonSecure => MemoryRegion.NvServices,
+                _ => MemoryRegion.NvServices
+            };
+
+            KernelResult result = process.Initialize(
+                info,
+                capabilities,
+                resourceLimit,
+                memRegion,
+                contextFactory,
+                customThreadStart);
+
+            if (result != KernelResult.Success)
+            {
+                return result;
+            }
+
+            _context.Processes.TryAdd(process.Pid, process);
+
+            return handleTable.GenerateHandle(process, out handle);
+        }
+
+        public KernelResult StartProcess(int handle, int priority, int cpuCore, ulong mainThreadStackSize)
+        {
+            KProcess process = _context.Scheduler.GetCurrentProcess().HandleTable.GetObject<KProcess>(handle);
+
+            if (process == null)
+            {
+                return KernelResult.InvalidHandle;
+            }
+
+            if ((uint)cpuCore >= KScheduler.CpuCoresCount || !process.IsCpuCoreAllowed(cpuCore))
+            {
+                return KernelResult.InvalidCpuCore;
+            }
+
+            if ((uint)priority >= KScheduler.PrioritiesCount || !process.IsPriorityAllowed(priority))
+            {
+                return KernelResult.InvalidPriority;
+            }
+
+            process.DefaultCpuCore = cpuCore;
+
+            KernelResult result = process.Start(priority, mainThreadStackSize);
+
+            if (result != KernelResult.Success)
+            {
+                return result;
+            }
+
+            process.IncrementReferenceCount();
+
+            return KernelResult.Success;
+        }
+
         // IPC
 
         public KernelResult ConnectToNamedPort(ulong namePtr, out int handle)
@@ -33,6 +179,13 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
                 return KernelResult.UserCopyFailed;
             }
 
+            return ConnectToNamedPort(name, out handle);
+        }
+
+        public KernelResult ConnectToNamedPort(string name, out int handle)
+        {
+            handle = 0;
+
             if (name.Length > 11)
             {
                 return KernelResult.MaximumExceeded;
@@ -70,61 +223,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
             return result;
         }
 
-        public KernelResult SendSyncRequestHLE(int handle)
-        {
-            KProcess process = _context.Scheduler.GetCurrentProcess();
-
-            KClientSession clientSession = process.HandleTable.GetObject<KClientSession>(handle);
-
-            if (clientSession == null || clientSession.Service == null)
-            {
-                return SendSyncRequest(handle);
-            }
-
-            return SendSyncRequestWithUserBufferHLE((ulong)_context.Scheduler.GetCurrentThread().Context.Tpidr, 0x100, handle);
-        }
-
-        public KernelResult SendSyncRequestWithUserBufferHLE(ulong messagePtr, ulong messageSize, int handle)
-        {
-            KProcess process = _context.Scheduler.GetCurrentProcess();
-
-            byte[] messageData = new byte[messageSize];
-
-            process.CpuMemory.Read(messagePtr, messageData);
-
-            KClientSession clientSession = process.HandleTable.GetObject<KClientSession>(handle);
-
-            if (clientSession == null || clientSession.Service == null)
-            {
-                return SendSyncRequestWithUserBuffer(messagePtr, messageSize, handle);
-            }
-
-            if (clientSession != null)
-            {
-                _context.CriticalSection.Enter();
-
-                KThread currentThread = _context.Scheduler.GetCurrentThread();
-
-                currentThread.SignaledObj = null;
-                currentThread.ObjSyncResult = KernelResult.Success;
-
-                currentThread.Reschedule(ThreadSchedState.Paused);
-
-                clientSession.Service.Server.PushMessage(_device, currentThread, clientSession, messagePtr, messageSize);
-
-                _context.CriticalSection.Leave();
-
-                return currentThread.ObjSyncResult;
-            }
-            else
-            {
-                Logger.Warning?.Print(LogClass.KernelSvc, $"Invalid session handle 0x{handle:x8}!");
-
-                return KernelResult.InvalidHandle;
-            }
-        }
-
-        private KernelResult SendSyncRequest(int handle)
+        public KernelResult SendSyncRequest(int handle)
         {
             KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
 
@@ -407,9 +506,18 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
                 return KernelResult.UserCopyFailed;
             }
 
-            KSynchronizationObject[] syncObjs = new KSynchronizationObject[handlesCount];
+            return ReplyAndReceive(handles, replyTargetHandle, timeout, out handleIndex);
+        }
 
-            for (int index = 0; index < handlesCount; index++)
+        public KernelResult ReplyAndReceive(ReadOnlySpan<int> handles, int replyTargetHandle, long timeout, out int handleIndex)
+        {
+            handleIndex = 0;
+
+            KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+
+            KSynchronizationObject[] syncObjs = new KSynchronizationObject[handles.Length];
+
+            for (int index = 0; index < handles.Length; index++)
             {
                 KSynchronizationObject obj = currentProcess.HandleTable.GetObject<KSynchronizationObject>(handles[index]);
 
@@ -601,7 +709,19 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
                 return KernelResult.UserCopyFailed;
             }
 
-            if (maxSessions < 0 || name.Length > 11)
+            if (name.Length > 11)
+            {
+                return KernelResult.MaximumExceeded;
+            }
+
+            return ManageNamedPort(name, maxSessions, out handle);
+        }
+
+        public KernelResult ManageNamedPort(string name, int maxSessions, out int handle)
+        {
+            handle = 0;
+
+            if (maxSessions < 0)
             {
                 return KernelResult.MaximumExceeded;
             }
@@ -826,7 +946,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
             return KernelResult.Success;
         }
 
-        public KernelResult MapSharedMemory(int handle, ulong address, ulong size, MemoryPermission permission)
+        public KernelResult MapSharedMemory(int handle, ulong address, ulong size, KMemoryPermission permission)
         {
             if (!PageAligned(address))
             {
@@ -843,7 +963,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
                 return KernelResult.InvalidMemState;
             }
 
-            if ((permission | MemoryPermission.Write) != MemoryPermission.ReadAndWrite)
+            if ((permission | KMemoryPermission.Write) != KMemoryPermission.ReadAndWrite)
             {
                 return KernelResult.InvalidPermission;
             }
@@ -912,7 +1032,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
                 currentProcess);
         }
 
-        public KernelResult CreateTransferMemory(ulong address, ulong size, MemoryPermission permission, out int handle)
+        public KernelResult CreateTransferMemory(ulong address, ulong size, KMemoryPermission permission, out int handle)
         {
             handle = 0;
 
@@ -931,7 +1051,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
                 return KernelResult.InvalidMemState;
             }
 
-            if (permission > MemoryPermission.ReadAndWrite || permission == MemoryPermission.Write)
+            if (permission > KMemoryPermission.ReadAndWrite || permission == KMemoryPermission.Write)
             {
                 return KernelResult.InvalidPermission;
             }
@@ -1119,7 +1239,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
             return targetProcess.MemoryManager.UnmapProcessCodeMemory(dst, src, size);
         }
 
-        public KernelResult SetProcessMemoryPermission(int handle, ulong src, ulong size, MemoryPermission permission)
+        public KernelResult SetProcessMemoryPermission(int handle, ulong src, ulong size, KMemoryPermission permission)
         {
             if (!PageAligned(src))
             {
@@ -1131,10 +1251,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
                 return KernelResult.InvalidSize;
             }
 
-            if (permission != MemoryPermission.None &&
-                permission != MemoryPermission.Read &&
-                permission != MemoryPermission.ReadAndWrite &&
-                permission != MemoryPermission.ReadAndExecute)
+            if (permission != KMemoryPermission.None &&
+                permission != KMemoryPermission.Read &&
+                permission != KMemoryPermission.ReadAndWrite &&
+                permission != KMemoryPermission.ReadAndExecute)
             {
                 return KernelResult.InvalidPermission;
             }
@@ -1282,31 +1402,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
             return _context.Scheduler.GetCurrentThread().Context.CntpctEl0;
         }
 
-        public KernelResult GetProcessId(int handle, out long pid)
-        {
-            KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
-
-            KProcess process = currentProcess.HandleTable.GetKProcess(handle);
-
-            if (process == null)
-            {
-                KThread thread = currentProcess.HandleTable.GetKThread(handle);
-
-                if (thread != null)
-                {
-                    process = thread.Owner;
-                }
-
-                // TODO: KDebugEvent.
-            }
-
-            pid = process?.Pid ?? 0;
-
-            return process != null
-                ? KernelResult.Success
-                : KernelResult.InvalidHandle;
-        }
-
         public void Break(ulong reason)
         {
             KThread currentThread = _context.Scheduler.GetCurrentThread();
@@ -1997,7 +2092,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
                 return KernelResult.InvalidThread;
             }
 
-            MemoryManager memory = currentProcess.CpuMemory;
+            IVirtualMemoryManager memory = currentProcess.CpuMemory;
 
             memory.Write(address + 0x0, thread.Context.GetX(0));
             memory.Write(address + 0x8, thread.Context.GetX(1));
diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall32.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall32.cs
index 224af6d843..b57175a369 100644
--- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall32.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall32.cs
@@ -22,12 +22,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
 
         public KernelResult SendSyncRequest32([R(0)] int handle)
         {
-            return _syscall.SendSyncRequestHLE(handle);
+            return _syscall.SendSyncRequest(handle);
         }
 
         public KernelResult SendSyncRequestWithUserBuffer32([R(0)] uint messagePtr, [R(1)] uint messageSize, [R(2)] int handle)
         {
-            return _syscall.SendSyncRequestWithUserBufferHLE(messagePtr, messageSize, handle);
+            return _syscall.SendSyncRequestWithUserBuffer(messagePtr, messageSize, handle);
         }
 
         public KernelResult CreateSession32(
@@ -116,7 +116,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
             return result;
         }
 
-        public KernelResult MapSharedMemory32([R(0)] int handle, [R(1)] uint address, [R(2)] uint size, [R(3)] MemoryPermission permission)
+        public KernelResult MapSharedMemory32([R(0)] int handle, [R(1)] uint address, [R(2)] uint size, [R(3)] KMemoryPermission permission)
         {
             return _syscall.MapSharedMemory(handle, address, size, permission);
         }
@@ -129,7 +129,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
         public KernelResult CreateTransferMemory32(
             [R(1)] uint address,
             [R(2)] uint size,
-            [R(3)] MemoryPermission permission,
+            [R(3)] KMemoryPermission permission,
             [R(1)] out int handle)
         {
             return _syscall.CreateTransferMemory(address, size, permission, out handle);
@@ -169,7 +169,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
             [R(2)] uint srcLow,
             [R(3)] uint srcHigh,
             [R(4)] uint sizeHigh,
-            [R(5)] MemoryPermission permission)
+            [R(5)] KMemoryPermission permission)
         {
             ulong src = srcLow | ((ulong)srcHigh << 32);
             ulong size = sizeLow | ((ulong)sizeHigh << 32);
diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall64.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall64.cs
index 47f78a25a4..07ca4aae7b 100644
--- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall64.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall64.cs
@@ -22,12 +22,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
 
         public KernelResult SendSyncRequest64([R(0)] int handle)
         {
-            return _syscall.SendSyncRequestHLE(handle);
+            return _syscall.SendSyncRequest(handle);
         }
 
         public KernelResult SendSyncRequestWithUserBuffer64([R(0)] ulong messagePtr, [R(1)] ulong messageSize, [R(2)] int handle)
         {
-            return _syscall.SendSyncRequestWithUserBufferHLE(messagePtr, messageSize, handle);
+            return _syscall.SendSyncRequestWithUserBuffer(messagePtr, messageSize, handle);
         }
 
         public KernelResult SendAsyncRequestWithUserBuffer64(
@@ -133,7 +133,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
             return _syscall.QueryMemory(infoPtr, position, out pageInfo);
         }
 
-        public KernelResult MapSharedMemory64([R(0)] int handle, [R(1)] ulong address, [R(2)] ulong size, [R(3)] MemoryPermission permission)
+        public KernelResult MapSharedMemory64([R(0)] int handle, [R(1)] ulong address, [R(2)] ulong size, [R(3)] KMemoryPermission permission)
         {
             return _syscall.MapSharedMemory(handle, address, size, permission);
         }
@@ -146,7 +146,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
         public KernelResult CreateTransferMemory64(
             [R(1)] ulong address,
             [R(2)] ulong size,
-            [R(3)] MemoryPermission permission,
+            [R(3)] KMemoryPermission permission,
             [R(1)] out int handle)
         {
             return _syscall.CreateTransferMemory(address, size, permission, out handle);
@@ -172,7 +172,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
             return _syscall.UnmapProcessCodeMemory(handle, dst, src, size);
         }
 
-        public KernelResult SetProcessMemoryPermission64([R(0)] int handle, [R(1)] ulong src, [R(2)] ulong size, [R(3)] MemoryPermission permission)
+        public KernelResult SetProcessMemoryPermission64([R(0)] int handle, [R(1)] ulong src, [R(2)] ulong size, [R(3)] KMemoryPermission permission)
         {
             return _syscall.SetProcessMemoryPermission(handle, src, size, permission);
         }
diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs
index 8499551394..c6da361d0a 100644
--- a/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs
@@ -229,6 +229,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
 
             KProcess dummyProcess = new KProcess(_context);
 
+            dummyProcess.HandleTable.Initialize(1024);
+
             KThread dummyThread = new KThread(_context);
 
             dummyThread.Initialize(0, 0, 0, 44, 0, dummyProcess, ThreadType.Dummy);
diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs
index 27ff388328..f523cb9cf0 100644
--- a/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs
@@ -31,6 +31,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
         public ulong CondVarAddress { get; set; }
 
         private ulong _entrypoint;
+        private ThreadStart _customThreadStart;
 
         public ulong MutexAddress { get; set; }
 
@@ -48,12 +49,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
 
         public LinkedListNode<KThread>[] SiblingsPerCore { get; private set; }
 
-        public LinkedList<KThread>     Withholder     { get; set; }
+        public LinkedList<KThread> Withholder { get; set; }
         public LinkedListNode<KThread> WithholderNode { get; set; }
 
         public LinkedListNode<KThread> ProcessListNode { get; set; }
 
-        private LinkedList<KThread>     _mutexWaiters;
+        private LinkedList<KThread> _mutexWaiters;
         private LinkedListNode<KThread> _mutexWaiterNode;
 
         public KThread MutexOwner { get; private set; }
@@ -65,24 +66,28 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
         public KernelResult ObjSyncResult { get; set; }
 
         public int DynamicPriority { get; set; }
-        public int CurrentCore     { get; set; }
-        public int BasePriority    { get; set; }
-        public int PreferredCore   { get; set; }
+        public int CurrentCore { get; set; }
+        public int BasePriority { get; set; }
+        public int PreferredCore { get; set; }
 
         private long _affinityMaskOverride;
-        private int  _preferredCoreOverride;
+        private int _preferredCoreOverride;
 #pragma warning disable CS0649
-        private int  _affinityOverrideCount;
+        private int _affinityOverrideCount;
 #pragma warning restore CS0649
 
         public ThreadSchedState SchedFlags { get; private set; }
 
         private int _shallBeTerminated;
 
-        public bool ShallBeTerminated { get => _shallBeTerminated != 0; set => _shallBeTerminated = value ? 1 : 0; }
+        public bool ShallBeTerminated
+        {
+            get => _shallBeTerminated != 0;
+            set => _shallBeTerminated = value ? 1 : 0;
+        }
 
         public bool SyncCancelled { get; set; }
-        public bool WaitingSync   { get; set; }
+        public bool WaitingSync { get; set; }
 
         private bool _hasExited;
         private bool _hasBeenInitialized;
@@ -98,7 +103,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
 
         public KThread(KernelContext context) : base(context)
         {
-            _scheduler      = KernelContext.Scheduler;
+            _scheduler = KernelContext.Scheduler;
             _schedulingData = KernelContext.Scheduler.SchedulingData;
 
             WaitSyncObjects = new KSynchronizationObject[MaxWaitSyncObjects];
@@ -110,14 +115,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
         }
 
         public KernelResult Initialize(
-            ulong      entrypoint,
-            ulong      argsPtr,
-            ulong      stackTop,
-            int        priority,
-            int        defaultCpuCore,
-            KProcess   owner,
-            ThreadType type = ThreadType.User,
-            ThreadStart customHostThreadStart = null)
+            ulong entrypoint,
+            ulong argsPtr,
+            ulong stackTop,
+            int priority,
+            int defaultCpuCore,
+            KProcess owner,
+            ThreadType type,
+            ThreadStart customThreadStart = null)
         {
             if ((uint)type > 3)
             {
@@ -135,11 +140,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
             CurrentCore = PreferredCore;
 
             DynamicPriority = priority;
-            BasePriority    = priority;
+            BasePriority = priority;
 
             ObjSyncResult = KernelResult.ThreadNotStarted;
 
             _entrypoint = entrypoint;
+            _customThreadStart = customThreadStart;
 
             if (type == ThreadType.User)
             {
@@ -162,18 +168,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
                 owner.IncrementReferenceCount();
                 owner.IncrementThreadCount();
 
-                is64Bits = (owner.MmuFlags & 1) != 0;
+                is64Bits = owner.Flags.HasFlag(ProcessCreationFlags.Is64Bit);
             }
             else
             {
                 is64Bits = true;
             }
 
-            HostThread = new Thread(customHostThreadStart ?? (() => ThreadStart(entrypoint)));
+            HostThread = new Thread(ThreadStart);
 
             Context = CpuContext.CreateExecutionContext();
 
-            bool isAarch32 = (Owner.MmuFlags & 1) == 0;
+            bool isAarch32 = !Owner.Flags.HasFlag(ProcessCreationFlags.Is64Bit);
 
             Context.IsAarch32 = isAarch32;
 
@@ -189,7 +195,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
             }
 
             Context.CntfrqEl0 = 19200000;
-            Context.Tpidr     = (long)_tlsAddress;
+            Context.Tpidr = (long)_tlsAddress;
 
             owner.SubscribeThreadEventHandlers(Context);
 
@@ -249,7 +255,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
             {
                 KThread currentThread = KernelContext.Scheduler.GetCurrentThread();
 
-                while (SchedFlags               != ThreadSchedState.TerminationPending &&
+                while (SchedFlags != ThreadSchedState.TerminationPending &&
                        currentThread.SchedFlags != ThreadSchedState.TerminationPending &&
                        !currentThread.ShallBeTerminated)
                 {
@@ -351,7 +357,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
                         Context.RequestInterrupt();
                     }
 
-                    SignaledObj   = null;
+                    SignaledObj = null;
                     ObjSyncResult = KernelResult.ThreadTerminating;
 
                     ReleaseAndResume();
@@ -524,7 +530,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
                     // If the candidate was scheduled after the current thread, then it's not worth it,
                     // unless the priority is higher than the current one.
                     if (nextThreadOnCurrentQueue.LastScheduledTime >= thread.LastScheduledTime ||
-                        nextThreadOnCurrentQueue.DynamicPriority    <  thread.DynamicPriority)
+                        nextThreadOnCurrentQueue.DynamicPriority < thread.DynamicPriority)
                     {
                         yield return thread;
                     }
@@ -701,7 +707,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
             }
             else
             {
-                SignaledObj   = null;
+                SignaledObj = null;
                 ObjSyncResult = KernelResult.Cancelled;
 
                 SetNewSchedFlags(ThreadSchedState.Running);
@@ -734,14 +740,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
             if (useOverride)
             {
                 _preferredCoreOverride = newCore;
-                _affinityMaskOverride  = newAffinityMask;
+                _affinityMaskOverride = newAffinityMask;
             }
             else
             {
                 long oldAffinityMask = AffinityMask;
 
                 PreferredCore = newCore;
-                AffinityMask  = newAffinityMask;
+                AffinityMask = newAffinityMask;
 
                 if (oldAffinityMask != newAffinityMask)
                 {
@@ -783,7 +789,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
 
         private void CombineForcePauseFlags()
         {
-            ThreadSchedState oldFlags  = SchedFlags;
+            ThreadSchedState oldFlags = SchedFlags;
             ThreadSchedState lowNibble = SchedFlags & ThreadSchedState.LowMask;
 
             SchedFlags = lowNibble | _forcePauseFlags;
@@ -1143,19 +1149,23 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
             }
         }
 
-        private void ThreadStart(ulong entrypoint)
+        private void ThreadStart()
         {
-            Owner.CpuContext.Execute(Context, entrypoint);
+            KernelStatic.SetKernelContext(KernelContext);
 
-            ThreadExit();
+            if (_customThreadStart != null)
+            {
+                _customThreadStart();
+            }
+            else
+            {
+                Owner.Context.Execute(Context, _entrypoint);
+            }
 
-            Context.Dispose();
-        }
-
-        private void ThreadExit()
-        {
             KernelContext.Scheduler.ExitThread(this);
             KernelContext.Scheduler.RemoveThread(this);
+
+            Context.Dispose();
         }
 
         public bool IsCurrentHostThread()
@@ -1203,9 +1213,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
             // Wake up all threads that may be waiting for a mutex being held by this thread.
             foreach (KThread thread in _mutexWaiters)
             {
-                thread.MutexOwner             = null;
+                thread.MutexOwner = null;
                 thread._preferredCoreOverride = 0;
-                thread.ObjSyncResult          = KernelResult.InvalidState;
+                thread.ObjSyncResult = KernelResult.InvalidState;
 
                 thread.ReleaseAndResume();
             }
diff --git a/Ryujinx.HLE/HOS/ProgramLoader.cs b/Ryujinx.HLE/HOS/ProgramLoader.cs
index 58e3704174..637c3efc24 100644
--- a/Ryujinx.HLE/HOS/ProgramLoader.cs
+++ b/Ryujinx.HLE/HOS/ProgramLoader.cs
@@ -36,23 +36,23 @@ namespace Ryujinx.HLE.HOS
 
             ulong codeAddress = codeBaseAddress + (ulong)kip.TextOffset;
 
-            int mmuFlags = 0;
+            ProcessCreationFlags flags = 0;
 
             if (AslrEnabled)
             {
                 // TODO: Randomization.
 
-                mmuFlags |= 0x20;
+                flags |= ProcessCreationFlags.EnableAslr;
             }
 
             if (kip.Is64BitAddressSpace)
             {
-                mmuFlags |= (int)AddressSpaceType.Addr39Bits << 1;
+                flags |= ProcessCreationFlags.AddressSpace64Bit;
             }
 
             if (kip.Is64Bit)
             {
-                mmuFlags |= 1;
+                flags |= ProcessCreationFlags.Is64Bit;
             }
 
             ProcessCreationInfo creationInfo = new ProcessCreationInfo(
@@ -61,7 +61,7 @@ namespace Ryujinx.HLE.HOS
                 kip.ProgramId,
                 codeAddress,
                 codePagesCount,
-                mmuFlags,
+                flags,
                 0,
                 0);
 
@@ -82,12 +82,15 @@ namespace Ryujinx.HLE.HOS
 
             KProcess process = new KProcess(context);
 
+            var processContextFactory = new ArmProcessContextFactory();
+
             result = process.InitializeKip(
                 creationInfo,
                 kip.Capabilities,
                 pageList,
                 context.ResourceLimit,
-                memoryRegion);
+                memoryRegion,
+                processContextFactory);
 
             if (result != KernelResult.Success)
             {
@@ -183,7 +186,7 @@ namespace Ryujinx.HLE.HOS
                 metaData.Aci0.TitleId,
                 codeStart,
                 codePagesCount,
-                metaData.MmuFlags,
+                (ProcessCreationFlags)metaData.ProcessFlags | ProcessCreationFlags.IsApplication,
                 0,
                 personalMmHeapPagesCount);
 
@@ -217,11 +220,14 @@ namespace Ryujinx.HLE.HOS
                 return false;
             }
 
+            var processContextFactory = new ArmProcessContextFactory();
+
             result = process.Initialize(
                 creationInfo,
                 metaData.Aci0.KernelAccessControl.Capabilities,
                 resourceLimit,
-                memoryRegion);
+                memoryRegion,
+                processContextFactory);
 
             if (result != KernelResult.Success)
             {
@@ -280,7 +286,7 @@ namespace Ryujinx.HLE.HOS
 
             MemoryHelper.FillWithZeros(process.CpuMemory, (long)bssStart, image.BssSize);
 
-            KernelResult SetProcessMemoryPermission(ulong address, ulong size, MemoryPermission permission)
+            KernelResult SetProcessMemoryPermission(ulong address, ulong size, KMemoryPermission permission)
             {
                 if (size == 0)
                 {
@@ -292,21 +298,21 @@ namespace Ryujinx.HLE.HOS
                 return process.MemoryManager.SetProcessMemoryPermission(address, size, permission);
             }
 
-            KernelResult result = SetProcessMemoryPermission(textStart, (ulong)image.Text.Length, MemoryPermission.ReadAndExecute);
+            KernelResult result = SetProcessMemoryPermission(textStart, (ulong)image.Text.Length, KMemoryPermission.ReadAndExecute);
 
             if (result != KernelResult.Success)
             {
                 return result;
             }
 
-            result = SetProcessMemoryPermission(roStart, (ulong)image.Ro.Length, MemoryPermission.Read);
+            result = SetProcessMemoryPermission(roStart, (ulong)image.Ro.Length, KMemoryPermission.Read);
 
             if (result != KernelResult.Success)
             {
                 return result;
             }
 
-            return SetProcessMemoryPermission(dataStart, end - dataStart, MemoryPermission.ReadAndWrite);
+            return SetProcessMemoryPermission(dataStart, end - dataStart, KMemoryPermission.ReadAndWrite);
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/ServiceCtx.cs b/Ryujinx.HLE/HOS/ServiceCtx.cs
index 533c1d677b..36d4163eb1 100644
--- a/Ryujinx.HLE/HOS/ServiceCtx.cs
+++ b/Ryujinx.HLE/HOS/ServiceCtx.cs
@@ -1,43 +1,39 @@
-using Ryujinx.Cpu;
 using Ryujinx.HLE.HOS.Ipc;
-using Ryujinx.HLE.HOS.Kernel.Ipc;
 using Ryujinx.HLE.HOS.Kernel.Process;
 using Ryujinx.HLE.HOS.Kernel.Threading;
+using Ryujinx.Memory;
 using System.IO;
 
 namespace Ryujinx.HLE.HOS
 {
     class ServiceCtx
     {
-        public Switch         Device       { get; }
-        public KProcess       Process      { get; }
-        public MemoryManager  Memory       { get; }
-        public KThread        Thread       { get; }
-        public KClientSession Session      { get; }
-        public IpcMessage     Request      { get; }
-        public IpcMessage     Response     { get; }
-        public BinaryReader   RequestData  { get; }
-        public BinaryWriter   ResponseData { get; }
+        public Switch Device { get; }
+        public KProcess Process { get; }
+        public IVirtualMemoryManager Memory { get; }
+        public KThread Thread { get; }
+        public IpcMessage Request { get; }
+        public IpcMessage Response { get; }
+        public BinaryReader RequestData { get; }
+        public BinaryWriter ResponseData { get; }
 
         public ServiceCtx(
-            Switch         device,
-            KProcess       process,
-            MemoryManager  memory,
-            KThread        thread,
-            KClientSession session,
-            IpcMessage     request,
-            IpcMessage     response,
-            BinaryReader   requestData,
-            BinaryWriter   responseData)
+            Switch device,
+            KProcess process,
+            IVirtualMemoryManager memory,
+            KThread thread,
+            IpcMessage request,
+            IpcMessage response,
+            BinaryReader requestData,
+            BinaryWriter responseData)
         {
-            Device       = device;
-            Process      = process;
-            Memory       = memory;
-            Thread       = thread;
-            Session      = session;
-            Request      = request;
-            Response     = response;
-            RequestData  = requestData;
+            Device = device;
+            Process = process;
+            Memory = memory;
+            Thread = thread;
+            Request = request;
+            Response = response;
+            RequestData = requestData;
             ResponseData = responseData;
         }
     }
diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemAppletProxy.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemAppletProxy.cs
index f131aff853..555396370a 100644
--- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemAppletProxy.cs
+++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemAppletProxy.cs
@@ -4,7 +4,12 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService
 {
     class ISystemAppletProxy : IpcService
     {
-        public ISystemAppletProxy() { }
+        private readonly long _pid;
+
+        public ISystemAppletProxy(long pid)
+        {
+            _pid = pid;
+        }
 
         [Command(0)]
         // GetCommonStateGetter() -> object<nn::am::service::ICommonStateGetter>
@@ -19,7 +24,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService
         // GetSelfController() -> object<nn::am::service::ISelfController>
         public ResultCode GetSelfController(ServiceCtx context)
         {
-            MakeObject(context, new ISelfController(context.Device.System));
+            MakeObject(context, new ISelfController(context.Device.System, _pid));
 
             return ResultCode.Success;
         }
@@ -28,7 +33,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService
         // GetWindowController() -> object<nn::am::service::IWindowController>
         public ResultCode GetWindowController(ServiceCtx context)
         {
-            MakeObject(context, new IWindowController());
+            MakeObject(context, new IWindowController(_pid));
 
             return ResultCode.Success;
         }
diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs
index 16ffe9ee6a..7d9d06e55f 100644
--- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs
+++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs
@@ -18,6 +18,10 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib
         private KEvent _normalOutDataEvent;
         private KEvent _interactiveOutDataEvent;
 
+        private int _stateChangedEventHandle;
+        private int _normalOutDataEventHandle;
+        private int _interactiveOutDataEventHandle;
+
         public ILibraryAppletAccessor(AppletId appletId, Horizon system)
         {
             _stateChangedEvent       = new KEvent(system.KernelContext);
@@ -32,7 +36,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib
             _applet.AppletStateChanged        += OnAppletStateChanged;
             _normalSession.DataAvailable      += OnNormalOutData;
             _interactiveSession.DataAvailable += OnInteractiveOutData;
-            
+
             Logger.Info?.Print(LogClass.ServiceAm, $"Applet '{appletId}' created.");
         }
 
@@ -55,12 +59,15 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib
         // GetAppletStateChangedEvent() -> handle<copy>
         public ResultCode GetAppletStateChangedEvent(ServiceCtx context)
         {
-            if (context.Process.HandleTable.GenerateHandle(_stateChangedEvent.ReadableEvent, out int handle) != KernelResult.Success)
+            if (_stateChangedEventHandle == 0)
             {
-                throw new InvalidOperationException("Out of handles!");
+                if (context.Process.HandleTable.GenerateHandle(_stateChangedEvent.ReadableEvent, out _stateChangedEventHandle) != KernelResult.Success)
+                {
+                    throw new InvalidOperationException("Out of handles!");
+                }
             }
 
-            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle);
+            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_stateChangedEventHandle);
 
             return ResultCode.Success;
         }
@@ -69,8 +76,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib
         // Start()
         public ResultCode Start(ServiceCtx context)
         {
-            return (ResultCode)_applet.Start(_normalSession.GetConsumer(),
-                                             _interactiveSession.GetConsumer());
+            return (ResultCode)_applet.Start(_normalSession.GetConsumer(), _interactiveSession.GetConsumer());
         }
 
         [Command(30)]
@@ -138,12 +144,16 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib
         // GetPopOutDataEvent() -> handle<copy>
         public ResultCode GetPopOutDataEvent(ServiceCtx context)
         {
-            if (context.Process.HandleTable.GenerateHandle(_normalOutDataEvent.ReadableEvent, out int handle) != KernelResult.Success)
+            if (_normalOutDataEventHandle == 0)
             {
-                throw new InvalidOperationException("Out of handles!");
+                if (context.Process.HandleTable.GenerateHandle(_normalOutDataEvent.ReadableEvent, out _normalOutDataEventHandle) != KernelResult.Success)
+                {
+                    throw new InvalidOperationException("Out of handles!");
+                }
             }
 
-            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle);
+
+            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_normalOutDataEventHandle);
 
             return ResultCode.Success;
         }
@@ -152,12 +162,15 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib
         // GetPopInteractiveOutDataEvent() -> handle<copy>
         public ResultCode GetPopInteractiveOutDataEvent(ServiceCtx context)
         {
-            if (context.Process.HandleTable.GenerateHandle(_interactiveOutDataEvent.ReadableEvent, out int handle) != KernelResult.Success)
+            if (_interactiveOutDataEventHandle == 0)
             {
-                throw new InvalidOperationException("Out of handles!");
+                if (context.Process.HandleTable.GenerateHandle(_interactiveOutDataEvent.ReadableEvent, out _interactiveOutDataEventHandle) != KernelResult.Success)
+                {
+                    throw new InvalidOperationException("Out of handles!");
+                }
             }
 
-            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle);
+            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_interactiveOutDataEventHandle);
 
             return ResultCode.Success;
         }
diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs
index 62319d476b..e3b73c2941 100644
--- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs
+++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs
@@ -12,7 +12,9 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
         private Apm.SystemManagerServer _apmSystemManagerServer;
         private Lbl.LblControllerServer _lblControllerServer;
 
-        private bool _vrModeEnabled = false;
+        private bool _vrModeEnabled;
+        private int _messageEventHandle;
+        private int _displayResolutionChangedEventHandle;
 
         public ICommonStateGetter(ServiceCtx context)
         {
@@ -25,14 +27,17 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
         // GetEventHandle() -> handle<copy>
         public ResultCode GetEventHandle(ServiceCtx context)
         {
-            KEvent Event = context.Device.System.AppletState.MessageEvent;
+            KEvent messageEvent = context.Device.System.AppletState.MessageEvent;
 
-            if (context.Process.HandleTable.GenerateHandle(Event.ReadableEvent, out int handle) != KernelResult.Success)
+            if (_messageEventHandle == 0)
             {
-                throw new InvalidOperationException("Out of handles!");
+                if (context.Process.HandleTable.GenerateHandle(messageEvent.ReadableEvent, out _messageEventHandle) != KernelResult.Success)
+                {
+                    throw new InvalidOperationException("Out of handles!");
+                }
             }
 
-            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle);
+            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_messageEventHandle);
 
             return ResultCode.Success;
         }
@@ -147,7 +152,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
                 _lblControllerServer.DisableVrMode();
             }
 
-            // TODO: It signals an internal event of ICommonStateGetter. We have to determine where this event is used. 
+            // TODO: It signals an internal event of ICommonStateGetter. We have to determine where this event is used.
         }
 
         [Command(60)] // 3.0.0+
@@ -164,12 +169,15 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
         // GetDefaultDisplayResolutionChangeEvent() -> handle<copy>
         public ResultCode GetDefaultDisplayResolutionChangeEvent(ServiceCtx context)
         {
-            if (context.Process.HandleTable.GenerateHandle(context.Device.System.DisplayResolutionChangeEvent.ReadableEvent, out int handle) != KernelResult.Success)
+            if (_displayResolutionChangedEventHandle == 0)
             {
-                throw new InvalidOperationException("Out of handles!");
+                if (context.Process.HandleTable.GenerateHandle(context.Device.System.DisplayResolutionChangeEvent.ReadableEvent, out _displayResolutionChangedEventHandle) != KernelResult.Success)
+                {
+                    throw new InvalidOperationException("Out of handles!");
+                }
             }
 
-            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle);
+            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_displayResolutionChangedEventHandle);
 
             Logger.Stub?.PrintStub(LogClass.ServiceAm);
 
@@ -189,7 +197,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
 
             _apmSystemManagerServer.SetCpuBoostMode((Apm.CpuBoostMode)cpuBoostMode);
 
-            // TODO: It signals an internal event of ICommonStateGetter. We have to determine where this event is used. 
+            // TODO: It signals an internal event of ICommonStateGetter. We have to determine where this event is used.
 
             return ResultCode.Success;
         }
diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IHomeMenuFunctions.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IHomeMenuFunctions.cs
index 5fafa15478..580574a89f 100644
--- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IHomeMenuFunctions.cs
+++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IHomeMenuFunctions.cs
@@ -9,6 +9,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
     class IHomeMenuFunctions : IpcService
     {
         private KEvent _channelEvent;
+        private int    _channelEventHandle;
 
         public IHomeMenuFunctions(Horizon system)
         {
@@ -29,12 +30,15 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
         // GetPopFromGeneralChannelEvent() -> handle<copy>
         public ResultCode GetPopFromGeneralChannelEvent(ServiceCtx context)
         {
-            if (context.Process.HandleTable.GenerateHandle(_channelEvent.ReadableEvent, out int handle) != KernelResult.Success)
+            if (_channelEventHandle == 0)
             {
-                throw new InvalidOperationException("Out of handles!");
+                if (context.Process.HandleTable.GenerateHandle(_channelEvent.ReadableEvent, out _channelEventHandle) != KernelResult.Success)
+                {
+                    throw new InvalidOperationException("Out of handles!");
+                }
             }
 
-            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle);
+            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_channelEventHandle);
 
             Logger.Stub?.PrintStub(LogClass.ServiceAm);
 
diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ILibraryAppletCreator.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ILibraryAppletCreator.cs
index decf547087..5b91e235ed 100644
--- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ILibraryAppletCreator.cs
+++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ILibraryAppletCreator.cs
@@ -49,7 +49,9 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
             }
 
             var data = new byte[transferMem.Size];
-            context.Memory.Read(transferMem.Address, data);
+            transferMem.Creator.CpuMemory.Read(transferMem.Address, data);
+
+            context.Device.System.KernelContext.Syscall.CloseHandle(handle);
 
             MakeObject(context, new IStorage(data));
 
diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs
index 8b1275ac2e..b2cc716028 100644
--- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs
+++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs
@@ -8,10 +8,13 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
 {
     class ISelfController : IpcService
     {
+        private readonly long _pid;
+
         private KEvent _libraryAppletLaunchableEvent;
+        private int    _libraryAppletLaunchableEventHandle;
 
         private KEvent _accumulatedSuspendedTickChangedEvent;
-        private int    _accumulatedSuspendedTickChangedEventHandle = 0;
+        private int    _accumulatedSuspendedTickChangedEventHandle;
 
         private object _fatalSectionLock = new object();
         private int    _fatalSectionCount;
@@ -32,9 +35,10 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
         private uint _screenShotImageOrientation = 0;
         private uint _idleTimeDetectionExtension = 0;
 
-        public ISelfController(Horizon system)
+        public ISelfController(Horizon system, long pid)
         {
             _libraryAppletLaunchableEvent = new KEvent(system.KernelContext);
+            _pid = pid;
         }
 
         [Command(0)]
@@ -103,12 +107,15 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
         {
             _libraryAppletLaunchableEvent.ReadableEvent.Signal();
 
-            if (context.Process.HandleTable.GenerateHandle(_libraryAppletLaunchableEvent.ReadableEvent, out int handle) != KernelResult.Success)
+            if (_libraryAppletLaunchableEventHandle == 0)
             {
-                throw new InvalidOperationException("Out of handles!");
+                if (context.Process.HandleTable.GenerateHandle(_libraryAppletLaunchableEvent.ReadableEvent, out _libraryAppletLaunchableEventHandle) != KernelResult.Success)
+                {
+                    throw new InvalidOperationException("Out of handles!");
+                }
             }
 
-            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle);
+            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_libraryAppletLaunchableEventHandle);
 
             Logger.Stub?.PrintStub(LogClass.ServiceAm);
 
@@ -206,6 +213,31 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
             return ResultCode.Success;
         }
 
+        [Command(40)]
+        // CreateManagedDisplayLayer() -> u64
+        public ResultCode CreateManagedDisplayLayer(ServiceCtx context)
+        {
+            context.Device.System.SurfaceFlinger.CreateLayer(_pid, out long layerId);
+
+            context.ResponseData.Write(layerId);
+
+            return ResultCode.Success;
+        }
+
+        [Command(44)] // 10.0.0+
+        // CreateManagedDisplaySeparableLayer() -> (u64, u64)
+        public ResultCode CreateManagedDisplaySeparableLayer(ServiceCtx context)
+        {
+            // NOTE: first create the recoding layer and then the display one because right now Surface Flinger only use the last id.
+            context.Device.System.SurfaceFlinger.CreateLayer(_pid, out long recordingLayerId);
+            context.Device.System.SurfaceFlinger.CreateLayer(_pid, out long displayLayerId);
+
+            context.ResponseData.Write(displayLayerId);
+            context.ResponseData.Write(recordingLayerId);
+
+            return ResultCode.Success;
+        }
+
         [Command(50)]
         // SetHandlesRequestToDisplay(b8)
         public ResultCode SetHandlesRequestToDisplay(ServiceCtx context)
diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IWindowController.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IWindowController.cs
index 5c5c9b885e..8c127b988f 100644
--- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IWindowController.cs
+++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IWindowController.cs
@@ -4,7 +4,12 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
 {
     class IWindowController : IpcService
     {
-        public IWindowController() { }
+        private readonly long _pid;
+
+        public IWindowController(long pid)
+        {
+            _pid = pid;
+        }
 
         [Command(1)]
         // GetAppletResourceUserId() -> nn::applet::AppletResourceUserId
@@ -12,7 +17,9 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
         {
             Logger.Stub?.PrintStub(LogClass.ServiceAm);
 
-            context.ResponseData.Write(0L);
+            long appletResourceUserId = context.Device.System.AppletState.AppletResourceUserIds.Add(_pid);
+
+            context.ResponseData.Write(appletResourceUserId);
 
             return ResultCode.Success;
         }
diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs
index d29a8da443..d518dde448 100644
--- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs
+++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs
@@ -11,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE
         // OpenSystemAppletProxy(u64, pid, handle<copy>) -> object<nn::am::service::ISystemAppletProxy>
         public ResultCode OpenSystemAppletProxy(ServiceCtx context)
         {
-            MakeObject(context, new ISystemAppletProxy());
+            MakeObject(context, new ISystemAppletProxy(context.Request.HandleDesc.PId));
 
             return ResultCode.Success;
         }
diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs b/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs
index b9dfee30c0..6a5ad4c18f 100644
--- a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs
+++ b/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs
@@ -28,6 +28,10 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
         private KEvent _friendInvitationStorageChannelEvent;
         private KEvent _notificationStorageChannelEvent;
 
+        private int _gpuErrorDetectedSystemEventHandle;
+        private int _friendInvitationStorageChannelEventHandle;
+        private int _notificationStorageChannelEventHandle;
+
         public IApplicationFunctions(Horizon system)
         {
             _gpuErrorDetectedSystemEvent         = new KEvent(system.KernelContext);
@@ -122,7 +126,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
                 return ResultCode.Success;
             }
 
-            // If desired language is not supported by application, use first supported language from TitleLanguage. 
+            // If desired language is not supported by application, use first supported language from TitleLanguage.
             // TODO: In the future, a GUI could enable user-specified search priority
             if (((1 << (int)context.Device.System.State.DesiredTitleLanguage) & supportedLanguages) == 0)
             {
@@ -293,12 +297,10 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
                 resultCode = InitializeApplicationCopyrightFrameBufferImpl(transferMemoryAddress, transferMemorySize, width, height);
             }
 
-            /*
-            if (transferMemoryHandle)
+            if (transferMemoryHandle != 0)
             {
-                svcCloseHandle(transferMemoryHandle);
+                context.Device.System.KernelContext.Syscall.CloseHandle(transferMemoryHandle);
             }
-            */
 
             return resultCode;
         }
@@ -455,15 +457,18 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
         // GetGpuErrorDetectedSystemEvent() -> handle<copy>
         public ResultCode GetGpuErrorDetectedSystemEvent(ServiceCtx context)
         {
-            if (context.Process.HandleTable.GenerateHandle(_gpuErrorDetectedSystemEvent.ReadableEvent, out int gpuErrorDetectedSystemEventHandle) != KernelResult.Success)
+            if (_gpuErrorDetectedSystemEventHandle == 0)
             {
-                throw new InvalidOperationException("Out of handles!");
+                if (context.Process.HandleTable.GenerateHandle(_gpuErrorDetectedSystemEvent.ReadableEvent, out _gpuErrorDetectedSystemEventHandle) != KernelResult.Success)
+                {
+                    throw new InvalidOperationException("Out of handles!");
+                }
             }
 
-            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(gpuErrorDetectedSystemEventHandle);
+            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_gpuErrorDetectedSystemEventHandle);
 
-            // NOTE: This is used by "sdk" NSO during applet-application initialization. 
-            //       A seperate thread is setup where event-waiting is handled. 
+            // NOTE: This is used by "sdk" NSO during applet-application initialization.
+            //       A seperate thread is setup where event-waiting is handled.
             //       When the Event is signaled, official sw will assert.
 
             return ResultCode.Success;
@@ -473,12 +478,15 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
         // GetFriendInvitationStorageChannelEvent() -> handle<copy>
         public ResultCode GetFriendInvitationStorageChannelEvent(ServiceCtx context)
         {
-            if (context.Process.HandleTable.GenerateHandle(_friendInvitationStorageChannelEvent.ReadableEvent, out int friendInvitationStorageChannelEventHandle) != KernelResult.Success)
+            if (_friendInvitationStorageChannelEventHandle == 0)
             {
-                throw new InvalidOperationException("Out of handles!");
+                if (context.Process.HandleTable.GenerateHandle(_friendInvitationStorageChannelEvent.ReadableEvent, out _friendInvitationStorageChannelEventHandle) != KernelResult.Success)
+                {
+                    throw new InvalidOperationException("Out of handles!");
+                }
             }
 
-            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(friendInvitationStorageChannelEventHandle);
+            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_friendInvitationStorageChannelEventHandle);
 
             return ResultCode.Success;
         }
@@ -501,12 +509,15 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
         // GetNotificationStorageChannelEvent() -> handle<copy>
         public ResultCode GetNotificationStorageChannelEvent(ServiceCtx context)
         {
-            if (context.Process.HandleTable.GenerateHandle(_notificationStorageChannelEvent.ReadableEvent, out int notificationStorageChannelEventHandle) != KernelResult.Success)
+            if (_notificationStorageChannelEventHandle == 0)
             {
-                throw new InvalidOperationException("Out of handles!");
+                if (context.Process.HandleTable.GenerateHandle(_notificationStorageChannelEvent.ReadableEvent, out _notificationStorageChannelEventHandle) != KernelResult.Success)
+                {
+                    throw new InvalidOperationException("Out of handles!");
+                }
             }
 
-            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(notificationStorageChannelEventHandle);
+            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_notificationStorageChannelEventHandle);
 
             return ResultCode.Success;
         }
diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/IApplicationProxy.cs b/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/IApplicationProxy.cs
index 700886fd7a..28a52573ca 100644
--- a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/IApplicationProxy.cs
+++ b/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/IApplicationProxy.cs
@@ -5,7 +5,12 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService
 {
     class IApplicationProxy : IpcService
     {
-        public IApplicationProxy() { }
+        private readonly long _pid;
+
+        public IApplicationProxy(long pid)
+        {
+            _pid = pid;
+        }
 
         [Command(0)]
         // GetCommonStateGetter() -> object<nn::am::service::ICommonStateGetter>
@@ -20,7 +25,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService
         // GetSelfController() -> object<nn::am::service::ISelfController>
         public ResultCode GetSelfController(ServiceCtx context)
         {
-            MakeObject(context, new ISelfController(context.Device.System));
+            MakeObject(context, new ISelfController(context.Device.System, _pid));
 
             return ResultCode.Success;
         }
@@ -29,7 +34,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService
         // GetWindowController() -> object<nn::am::service::IWindowController>
         public ResultCode GetWindowController(ServiceCtx context)
         {
-            MakeObject(context, new IWindowController());
+            MakeObject(context, new IWindowController(_pid));
 
             return ResultCode.Success;
         }
diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletOE/IApplicationProxyService.cs b/Ryujinx.HLE/HOS/Services/Am/AppletOE/IApplicationProxyService.cs
index 0aee24fe93..8487d31996 100644
--- a/Ryujinx.HLE/HOS/Services/Am/AppletOE/IApplicationProxyService.cs
+++ b/Ryujinx.HLE/HOS/Services/Am/AppletOE/IApplicationProxyService.cs
@@ -11,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Services.Am
         // OpenApplicationProxy(u64, pid, handle<copy>) -> object<nn::am::service::IApplicationProxy>
         public ResultCode OpenApplicationProxy(ServiceCtx context)
         {
-            MakeObject(context, new IApplicationProxy());
+            MakeObject(context, new IApplicationProxy(context.Request.HandleDesc.PId));
 
             return ResultCode.Success;
         }
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager/IAudioOut.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager/IAudioOut.cs
index af9cccb2cb..86e5566aac 100644
--- a/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager/IAudioOut.cs
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager/IAudioOut.cs
@@ -10,15 +10,18 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioOutManager
 {
     class IAudioOut : IpcService, IDisposable
     {
-        private IAalOutput _audioOut;
-        private KEvent     _releaseEvent;
-        private int        _track;
+        private readonly IAalOutput _audioOut;
+        private readonly KEvent     _releaseEvent;
+        private          int        _releaseEventHandle;
+        private readonly int        _track;
+        private readonly int        _clientHandle;
 
-        public IAudioOut(IAalOutput audioOut, KEvent releaseEvent, int track)
+        public IAudioOut(IAalOutput audioOut, KEvent releaseEvent, int track, int clientHandle)
         {
             _audioOut     = audioOut;
             _releaseEvent = releaseEvent;
             _track        = track;
+            _clientHandle = clientHandle;
         }
 
         [Command(0)]
@@ -59,12 +62,15 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioOutManager
         // RegisterBufferEvent() -> handle<copy>
         public ResultCode RegisterBufferEvent(ServiceCtx context)
         {
-            if (context.Process.HandleTable.GenerateHandle(_releaseEvent.ReadableEvent, out int handle) != KernelResult.Success)
+            if (_releaseEventHandle == 0)
             {
-                throw new InvalidOperationException("Out of handles!");
+                if (context.Process.HandleTable.GenerateHandle(_releaseEvent.ReadableEvent, out _releaseEventHandle) != KernelResult.Success)
+                {
+                    throw new InvalidOperationException("Out of handles!");
+                }
             }
 
-            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle);
+            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_releaseEventHandle);
 
             return ResultCode.Success;
         }
@@ -108,7 +114,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioOutManager
             // NOTE: Assume PCM16 all the time, change if new format are found.
             short[] buffer = new short[data.SampleBufferSize / sizeof(short)];
 
-            context.Memory.Read((ulong)data.SampleBufferPtr, MemoryMarshal.Cast<short, byte>(buffer));
+            context.Process.HandleTable.GetKProcess(_clientHandle).CpuMemory.Read((ulong)data.SampleBufferPtr, MemoryMarshal.Cast<short, byte>(buffer));
 
             _audioOut.AppendBuffer(_track, tag, buffer);
 
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager.cs
index e12a991998..5aff34757f 100644
--- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager.cs
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager.cs
@@ -33,7 +33,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio
 
         public ResultCode OpenAudioRenderer(ServiceCtx context, out IAudioRenderer obj, ref AudioRendererConfiguration parameter, ulong workBufferSize, ulong appletResourceUserId, KTransferMemory workBufferTransferMemory, uint processHandle)
         {
-            ResultCode result = (ResultCode)_impl.OpenAudioRenderer(out AudioRenderSystem renderer, context.Memory, ref parameter, appletResourceUserId, workBufferTransferMemory.Address, workBufferTransferMemory.Size, processHandle);
+            var memoryManager = context.Process.HandleTable.GetKProcess((int)processHandle).CpuMemory;
+
+            ResultCode result = (ResultCode)_impl.OpenAudioRenderer(out AudioRenderSystem renderer, memoryManager, ref parameter, appletResourceUserId, workBufferTransferMemory.Address, workBufferTransferMemory.Size, processHandle);
 
             if (result == ResultCode.Success)
             {
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManagerServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManagerServer.cs
index effd8f151f..a789dfed0e 100644
--- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManagerServer.cs
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManagerServer.cs
@@ -14,9 +14,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio
 
         private IAudioRendererManager _impl;
 
-        public AudioRendererManagerServer(ServiceCtx context) : this(new AudioRendererManager(context.Device.System.AudioRendererManager, context.Device.System.AudioDeviceSessionRegistry)) { }
+        public AudioRendererManagerServer(ServiceCtx context) : this(context, new AudioRendererManager(context.Device.System.AudioRendererManager, context.Device.System.AudioDeviceSessionRegistry)) { }
 
-        public AudioRendererManagerServer(IAudioRendererManager impl) : base(new ServerBase("AudioRendererServer"))
+        public AudioRendererManagerServer(ServiceCtx context, IAudioRendererManager impl) : base(new ServerBase(context.Device.System.KernelContext, "AudioRendererServer"))
         {
             _impl = impl;
         }
@@ -40,6 +40,8 @@ namespace Ryujinx.HLE.HOS.Services.Audio
                 MakeObject(context, new AudioRendererServer(renderer));
             }
 
+            context.Device.System.KernelContext.Syscall.CloseHandle((int)processHandle);
+
             return result;
         }
 
diff --git a/Ryujinx.HLE/HOS/Services/Audio/IAudioInManager.cs b/Ryujinx.HLE/HOS/Services/Audio/IAudioInManager.cs
index 3081cd499e..b3f7f5e0ca 100644
--- a/Ryujinx.HLE/HOS/Services/Audio/IAudioInManager.cs
+++ b/Ryujinx.HLE/HOS/Services/Audio/IAudioInManager.cs
@@ -1,4 +1,5 @@
 using Ryujinx.Cpu;
+using Ryujinx.Memory;
 using System;
 using System.Text;
 
@@ -57,7 +58,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio
             return ResultCode.Success;
         }
 
-        private uint ListAudioInsImpl(MemoryManager memory, long bufferPosition, long bufferSize, bool filtered = false)
+        private uint ListAudioInsImpl(IVirtualMemoryManager memory, long bufferPosition, long bufferSize, bool filtered = false)
         {
             uint count = 0;
 
diff --git a/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs b/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs
index 646acef4d8..91ec287c36 100644
--- a/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs
+++ b/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs
@@ -14,7 +14,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio
         private const int    DefaultSampleRate    = 48000;
         private const int    DefaultChannelsCount = 2;
 
-        public IAudioOutManager(ServiceCtx context) : base(new ServerBase("AudioOutServer")) { }
+        public IAudioOutManager(ServiceCtx context) : base(new ServerBase(context.Device.System.KernelContext, "AudioOutServer")) { }
 
         [Command(0)]
         // ListAudioOuts() -> (u32 count, buffer<bytes, 6>)
@@ -134,7 +134,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio
 
             int track = audioOut.OpenTrack(sampleRate, channels, callback);
 
-            MakeObject(context, new IAudioOut(audioOut, releaseEvent, track));
+            MakeObject(context, new IAudioOut(audioOut, releaseEvent, track, context.Request.HandleDesc.ToCopy[0]));
 
             context.ResponseData.Write(sampleRate);
             context.ResponseData.Write(channels);
diff --git a/Ryujinx.HLE/HOS/Services/Audio/IHardwareOpusDecoderManager.cs b/Ryujinx.HLE/HOS/Services/Audio/IHardwareOpusDecoderManager.cs
index ed40cdadb9..c8cc281e2c 100644
--- a/Ryujinx.HLE/HOS/Services/Audio/IHardwareOpusDecoderManager.cs
+++ b/Ryujinx.HLE/HOS/Services/Audio/IHardwareOpusDecoderManager.cs
@@ -16,6 +16,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio
 
             MakeObject(context, new IHardwareOpusDecoder(sampleRate, channelsCount));
 
+            // Close transfer memory immediately as we don't use it.
+            context.Device.System.KernelContext.Syscall.CloseHandle(context.Request.HandleDesc.ToCopy[0]);
+
             return ResultCode.Success;
         }
 
diff --git a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs
index d56a6a43f7..54b1a58acb 100644
--- a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs
+++ b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs
@@ -12,6 +12,7 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator
     class IDeliveryCacheProgressService : IpcService
     {
         private KEvent _event;
+        private int    _eventHandle;
 
         public IDeliveryCacheProgressService(ServiceCtx context)
         {
@@ -22,12 +23,15 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator
         // GetEvent() -> handle<copy>
         public ResultCode GetEvent(ServiceCtx context)
         {
-            if (context.Process.HandleTable.GenerateHandle(_event.ReadableEvent, out int handle) != KernelResult.Success)
+            if (_eventHandle == 0)
             {
-                throw new InvalidOperationException("Out of handles!");
+                if (context.Process.HandleTable.GenerateHandle(_event.ReadableEvent, out _eventHandle) != KernelResult.Success)
+                {
+                    throw new InvalidOperationException("Out of handles!");
+                }
             }
 
-            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle);
+            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_eventHandle);
 
             Logger.Stub?.PrintStub(LogClass.ServiceBcat);
 
diff --git a/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs b/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs
index 44f406a070..e29a040f13 100644
--- a/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs
+++ b/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs
@@ -33,7 +33,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs
         }
 
         [Command(8)]
-        // OpenFileSystemWithId(nn::fssrv::sf::FileSystemType filesystem_type, nn::ApplicationId tid, buffer<bytes<0x301>, 0x19, 0x301> path) 
+        // OpenFileSystemWithId(nn::fssrv::sf::FileSystemType filesystem_type, nn::ApplicationId tid, buffer<bytes<0x301>, 0x19, 0x301> path)
         // -> object<nn::fssrv::sf::IFileSystem> contentFs
         public ResultCode OpenFileSystemWithId(ServiceCtx context)
         {
@@ -138,12 +138,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs
             // Workaround that by setting the application ID and owner ID if they're not already set
             if (attribute.ProgramId == ProgramId.InvalidId)
             {
-                attribute.ProgramId = new ProgramId(context.Process.TitleId);
-            }
-
-            if (creationInfo.OwnerId == 0)
-            {
-                creationInfo.OwnerId = 0;
+                attribute.ProgramId = new ProgramId(context.Device.Application.TitleId);
             }
 
             Logger.Info?.Print(LogClass.ServiceFs, $"Creating save with title ID {attribute.ProgramId.Value:x16}");
@@ -215,12 +210,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs
             // Workaround that by setting the application ID and owner ID if they're not already set
             if (attribute.ProgramId == ProgramId.InvalidId)
             {
-                attribute.ProgramId = new ProgramId(context.Process.TitleId);
-            }
-
-            if (creationInfo.OwnerId == 0)
-            {
-                creationInfo.OwnerId = 0;
+                attribute.ProgramId = new ProgramId(context.Device.Application.TitleId);
             }
 
             Result result = _baseFileSystemProxy.CreateSaveDataFileSystemWithHashSalt(ref attribute, ref creationInfo, ref metaCreateInfo, ref hashSalt);
@@ -239,7 +229,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs
             // Workaround that by setting the application ID if it's not already set
             if (attribute.ProgramId == ProgramId.InvalidId)
             {
-                attribute.ProgramId = new ProgramId(context.Process.TitleId);
+                attribute.ProgramId = new ProgramId(context.Device.Application.TitleId);
             }
 
             Result result = _baseFileSystemProxy.OpenSaveDataFileSystem(out LibHac.Fs.Fsa.IFileSystem fileSystem, spaceId, ref attribute);
@@ -280,7 +270,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs
             // Workaround that by setting the application ID if it's not already set
             if (attribute.ProgramId == ProgramId.InvalidId)
             {
-                attribute.ProgramId = new ProgramId(context.Process.TitleId);
+                attribute.ProgramId = new ProgramId(context.Device.Application.TitleId);
             }
 
             Result result = _baseFileSystemProxy.OpenReadOnlySaveDataFileSystem(out LibHac.Fs.Fsa.IFileSystem fileSystem, spaceId, ref attribute);
@@ -328,7 +318,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs
             filter.SetSaveDataType(SaveDataType.Cache);
             filter.SetProgramId(new ProgramId(context.Process.TitleId));
 
-            // FS would query the User and SdCache space IDs to find where the existing cache is (if any). 
+            // FS would query the User and SdCache space IDs to find where the existing cache is (if any).
             // We always have the SD card inserted, so we can always use SdCache for now.
             Result result = _baseFileSystemProxy.OpenSaveDataInfoReaderBySaveDataSpaceId(
                 out ReferenceCountedDisposable<LibHac.FsSrv.ISaveDataInfoReader> infoReader, SaveDataSpaceId.SdCache);
diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidServer/IAppletResource.cs b/Ryujinx.HLE/HOS/Services/Hid/HidServer/IAppletResource.cs
index 2c3a6500a3..dfce2d73e0 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/HidServer/IAppletResource.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/HidServer/IAppletResource.cs
@@ -8,6 +8,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.HidServer
     class IAppletResource : IpcService
     {
         private KSharedMemory _hidSharedMem;
+        private int           _hidSharedMemHandle;
 
         public IAppletResource(KSharedMemory hidSharedMem)
         {
@@ -18,12 +19,15 @@ namespace Ryujinx.HLE.HOS.Services.Hid.HidServer
         // GetSharedMemoryHandle() -> handle<copy>
         public ResultCode GetSharedMemoryHandle(ServiceCtx context)
         {
-            if (context.Process.HandleTable.GenerateHandle(_hidSharedMem, out int handle) != KernelResult.Success)
+            if (_hidSharedMemHandle == 0)
             {
-                throw new InvalidOperationException("Out of handles!");
+                if (context.Process.HandleTable.GenerateHandle(_hidSharedMem, out _hidSharedMemHandle) != KernelResult.Success)
+                {
+                    throw new InvalidOperationException("Out of handles!");
+                }
             }
 
-            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle);
+            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_hidSharedMemHandle);
 
             return ResultCode.Success;
         }
diff --git a/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs b/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs
index d3bf931956..dfcf2b250a 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs
@@ -35,7 +35,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
         private HidAccelerometerParameters _accelerometerParams;
         private HidVibrationValue          _vibrationValue;
 
-        public IHidServer(ServiceCtx context) : base(new ServerBase("HidServer"))
+        public IHidServer(ServiceCtx context) : base(new ServerBase(context.Device.System.KernelContext, "HidServer"))
         {
             _xpadIdEvent                 = new KEvent(context.Device.System.KernelContext);
             _palmaOperationCompleteEvent = new KEvent(context.Device.System.KernelContext);
@@ -559,9 +559,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid
             ControllerType type = (ControllerType)context.RequestData.ReadInt32();
             long appletResourceUserId = context.RequestData.ReadInt64();
 
-            Logger.Stub?.PrintStub(LogClass.ServiceHid, new { 
-                    appletResourceUserId, 
-                    type 
+            Logger.Stub?.PrintStub(LogClass.ServiceHid, new {
+                    appletResourceUserId,
+                    type
                 });
 
             context.Device.Hid.Npads.SupportedStyleSets = type;
@@ -577,9 +577,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid
 
             context.ResponseData.Write((int)context.Device.Hid.Npads.SupportedStyleSets);
 
-            Logger.Stub?.PrintStub(LogClass.ServiceHid, new { 
+            Logger.Stub?.PrintStub(LogClass.ServiceHid, new {
                     appletResourceUserId,
-                    context.Device.Hid.Npads.SupportedStyleSets 
+                    context.Device.Hid.Npads.SupportedStyleSets
                 });
 
             return ResultCode.Success;
@@ -704,9 +704,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid
             long appletResourceUserId = context.RequestData.ReadInt64();
             context.Device.Hid.Npads.JoyHold = (NpadJoyHoldType)context.RequestData.ReadInt64();
 
-            Logger.Stub?.PrintStub(LogClass.ServiceHid, new { 
-                    appletResourceUserId, 
-                    context.Device.Hid.Npads.JoyHold 
+            Logger.Stub?.PrintStub(LogClass.ServiceHid, new {
+                    appletResourceUserId,
+                    context.Device.Hid.Npads.JoyHold
                 });
 
             return ResultCode.Success;
@@ -720,9 +720,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid
 
             context.ResponseData.Write((long)context.Device.Hid.Npads.JoyHold);
 
-            Logger.Stub?.PrintStub(LogClass.ServiceHid, new { 
-                    appletResourceUserId, 
-                    context.Device.Hid.Npads.JoyHold 
+            Logger.Stub?.PrintStub(LogClass.ServiceHid, new {
+                    appletResourceUserId,
+                    context.Device.Hid.Npads.JoyHold
                 });
 
             return ResultCode.Success;
diff --git a/Ryujinx.HLE/HOS/Services/IpcService.cs b/Ryujinx.HLE/HOS/Services/IpcService.cs
index 9d40d80fbe..f98581072e 100644
--- a/Ryujinx.HLE/HOS/Services/IpcService.cs
+++ b/Ryujinx.HLE/HOS/Services/IpcService.cs
@@ -1,8 +1,6 @@
 using Ryujinx.Common.Logging;
 using Ryujinx.HLE.Exceptions;
 using Ryujinx.HLE.HOS.Ipc;
-using Ryujinx.HLE.HOS.Kernel.Common;
-using Ryujinx.HLE.HOS.Kernel.Ipc;
 using System;
 using System.Collections.Generic;
 using System.IO;
@@ -17,6 +15,7 @@ namespace Ryujinx.HLE.HOS.Services
 
         public ServerBase Server { get; private set; }
 
+        private IpcService _parent;
         private IdDictionary _domainObjects;
         private int _selfId;
         private bool _isDomain;
@@ -32,6 +31,7 @@ namespace Ryujinx.HLE.HOS.Services
 
             Server = server;
 
+            _parent = this;
             _domainObjects = new IdDictionary();
             _selfId = -1;
         }
@@ -62,8 +62,8 @@ namespace Ryujinx.HLE.HOS.Services
                 int domainWord0 = context.RequestData.ReadInt32();
                 int domainObjId = context.RequestData.ReadInt32();
 
-                int domainCmd       = (domainWord0 >> 0)  & 0xff;
-                int inputObjCount   = (domainWord0 >> 8)  & 0xff;
+                int domainCmd = (domainWord0 >> 0) & 0xff;
+                int inputObjCount = (domainWord0 >> 8) & 0xff;
                 int dataPayloadSize = (domainWord0 >> 16) & 0xffff;
 
                 context.RequestData.BaseStream.Seek(0x10 + dataPayloadSize, SeekOrigin.Begin);
@@ -96,8 +96,8 @@ namespace Ryujinx.HLE.HOS.Services
                 }
             }
 
-            long sfciMagic =      context.RequestData.ReadInt64();
-            int  commandId = (int)context.RequestData.ReadInt64();
+            long sfciMagic = context.RequestData.ReadInt64();
+            int commandId = (int)context.RequestData.ReadInt64();
 
             bool serviceExists = service.Commands.TryGetValue(commandId, out MethodInfo processRequest);
 
@@ -145,56 +145,37 @@ namespace Ryujinx.HLE.HOS.Services
             {
                 string dbgMessage = $"{service.GetType().FullName}: {commandId}";
 
-                throw new ServiceNotImplementedException(context, dbgMessage);
+                throw new ServiceNotImplementedException(service, context, dbgMessage);
             }
         }
 
-        protected static void MakeObject(ServiceCtx context, IpcService obj)
+        protected void MakeObject(ServiceCtx context, IpcService obj)
         {
-            IpcService service = context.Session.Service;
+            obj.TrySetServer(_parent.Server);
 
-            obj.TrySetServer(service.Server);
-
-            if (service._isDomain)
+            if (_parent._isDomain)
             {
-                context.Response.ObjectIds.Add(service.Add(obj));
+                obj._parent = _parent;
+
+                context.Response.ObjectIds.Add(_parent.Add(obj));
             }
             else
             {
-                KSession session = new KSession(context.Device.System.KernelContext);
+                context.Device.System.KernelContext.Syscall.CreateSession(false, 0, out int serverSessionHandle, out int clientSessionHandle);
 
-                session.ClientSession.Service = obj;
+                obj.Server.AddSessionObj(serverSessionHandle, obj);
 
-                if (context.Process.HandleTable.GenerateHandle(session.ClientSession, out int handle) != KernelResult.Success)
-                {
-                    throw new InvalidOperationException("Out of handles!");
-                }
-
-                session.ServerSession.DecrementReferenceCount();
-                session.ClientSession.DecrementReferenceCount();
-
-                context.Response.HandleDesc = IpcHandleDesc.MakeMove(handle);
+                context.Response.HandleDesc = IpcHandleDesc.MakeMove(clientSessionHandle);
             }
         }
 
-        protected static T GetObject<T>(ServiceCtx context, int index) where T : IpcService
+        protected T GetObject<T>(ServiceCtx context, int index) where T : IpcService
         {
-            IpcService service = context.Session.Service;
-
-            if (!service._isDomain)
-            {
-                int handle = context.Request.HandleDesc.ToMove[index];
-
-                KClientSession session = context.Process.HandleTable.GetObject<KClientSession>(handle);
-
-                return session?.Service is T ? (T)session.Service : null;
-            }
-
             int objId = context.Request.ObjectIds[index];
 
-            IIpcService obj = service.GetObject(objId);
+            IIpcService obj = _parent.GetObject(objId);
 
-            return obj is T ? (T)obj : null;
+            return obj is T t ? t : null;
         }
 
         public bool TrySetServer(ServerBase newServer)
@@ -230,5 +211,10 @@ namespace Ryujinx.HLE.HOS.Services
         {
             return _domainObjects.GetData<IIpcService>(id);
         }
+
+        public void SetParent(IpcService parent)
+        {
+            _parent = parent._parent;
+        }
     }
 }
diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/IUser.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/IUser.cs
index bc55d1a184..2cd35b9ed2 100644
--- a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/IUser.cs
+++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/IUser.cs
@@ -103,98 +103,98 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
         // StartDetection(bytes<8, 4>)
         public ResultCode StartDetection(ServiceCtx context)
         {
-            throw new ServiceNotImplementedException(context);
+            throw new ServiceNotImplementedException(this, context);
         }
 
         [Command(4)]
         // StopDetection(bytes<8, 4>)
         public ResultCode StopDetection(ServiceCtx context)
         {
-            throw new ServiceNotImplementedException(context);
+            throw new ServiceNotImplementedException(this, context);
         }
 
         [Command(5)]
         // Mount(bytes<8, 4>, u32, u32)
         public ResultCode Mount(ServiceCtx context)
         {
-            throw new ServiceNotImplementedException(context);
+            throw new ServiceNotImplementedException(this, context);
         }
 
         [Command(6)]
         // Unmount(bytes<8, 4>)
         public ResultCode Unmount(ServiceCtx context)
         {
-            throw new ServiceNotImplementedException(context);
+            throw new ServiceNotImplementedException(this, context);
         }
 
         [Command(7)]
         // OpenApplicationArea(bytes<8, 4>, u32)
         public ResultCode OpenApplicationArea(ServiceCtx context)
         {
-            throw new ServiceNotImplementedException(context);
+            throw new ServiceNotImplementedException(this, context);
         }
 
         [Command(8)]
         // GetApplicationArea(bytes<8, 4>) -> (u32, buffer<unknown, 6>)
         public ResultCode GetApplicationArea(ServiceCtx context)
         {
-            throw new ServiceNotImplementedException(context);
+            throw new ServiceNotImplementedException(this, context);
         }
 
         [Command(9)]
         // SetApplicationArea(bytes<8, 4>, buffer<unknown, 5>)
         public ResultCode SetApplicationArea(ServiceCtx context)
         {
-            throw new ServiceNotImplementedException(context);
+            throw new ServiceNotImplementedException(this, context);
         }
 
         [Command(10)]
         // Flush(bytes<8, 4>)
         public ResultCode Flush(ServiceCtx context)
         {
-            throw new ServiceNotImplementedException(context);
+            throw new ServiceNotImplementedException(this, context);
         }
 
         [Command(11)]
         // Restore(bytes<8, 4>)
         public ResultCode Restore(ServiceCtx context)
         {
-            throw new ServiceNotImplementedException(context);
+            throw new ServiceNotImplementedException(this, context);
         }
 
         [Command(12)]
         // CreateApplicationArea(bytes<8, 4>, u32, buffer<unknown, 5>)
         public ResultCode CreateApplicationArea(ServiceCtx context)
         {
-            throw new ServiceNotImplementedException(context);
+            throw new ServiceNotImplementedException(this, context);
         }
 
         [Command(13)]
         // GetTagInfo(bytes<8, 4>) -> buffer<unknown<0x58>, 0x1a>
         public ResultCode GetTagInfo(ServiceCtx context)
         {
-            throw new ServiceNotImplementedException(context);
+            throw new ServiceNotImplementedException(this, context);
         }
 
         [Command(14)]
         // GetRegisterInfo(bytes<8, 4>) -> buffer<unknown<0x100>, 0x1a>
         public ResultCode GetRegisterInfo(ServiceCtx context)
         {
-            throw new ServiceNotImplementedException(context);
+            throw new ServiceNotImplementedException(this, context);
         }
 
         [Command(15)]
         // GetCommonInfo(bytes<8, 4>) -> buffer<unknown<0x40>, 0x1a>
         public ResultCode GetCommonInfo(ServiceCtx context)
         {
-            throw new ServiceNotImplementedException(context);
+            throw new ServiceNotImplementedException(this, context);
         }
 
         [Command(16)]
         // GetModelInfo(bytes<8, 4>) -> buffer<unknown<0x40>, 0x1a>
         public ResultCode GetModelInfo(ServiceCtx context)
         {
-            throw new ServiceNotImplementedException(context);
+            throw new ServiceNotImplementedException(this, context);
         }
 
         [Command(17)]
@@ -308,7 +308,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
         // GetApplicationAreaSize(bytes<8, 4>) -> u32
         public ResultCode GetApplicationAreaSize(ServiceCtx context)
         {
-            throw new ServiceNotImplementedException(context);
+            throw new ServiceNotImplementedException(this, context);
         }
 
         [Command(23)] // 3.0.0+
@@ -334,7 +334,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
         // RecreateApplicationArea(bytes<8, 4>, u32, buffer<unknown, 5>)
         public ResultCode RecreateApplicationArea(ServiceCtx context)
         {
-            throw new ServiceNotImplementedException(context);
+            throw new ServiceNotImplementedException(this, context);
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IRequest.cs b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IRequest.cs
index 2f6be8bf6a..51f9487f57 100644
--- a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IRequest.cs
+++ b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IRequest.cs
@@ -11,6 +11,9 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
         private KEvent _event0;
         private KEvent _event1;
 
+        private int _event0Handle;
+        private int _event1Handle;
+
         private uint _version;
 
         public IRequest(Horizon system, uint version)
@@ -50,17 +53,23 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
         // GetSystemEventReadableHandles() -> (handle<copy>, handle<copy>)
         public ResultCode GetSystemEventReadableHandles(ServiceCtx context)
         {
-            if (context.Process.HandleTable.GenerateHandle(_event0.ReadableEvent, out int handle0) != KernelResult.Success)
+            if (_event0Handle == 0)
             {
-                throw new InvalidOperationException("Out of handles!");
+                if (context.Process.HandleTable.GenerateHandle(_event0.ReadableEvent, out _event0Handle) != KernelResult.Success)
+                {
+                    throw new InvalidOperationException("Out of handles!");
+                }
             }
 
-            if (context.Process.HandleTable.GenerateHandle(_event1.ReadableEvent, out int handle1) != KernelResult.Success)
+            if (_event1Handle == 0)
             {
-                throw new InvalidOperationException("Out of handles!");
+                if (context.Process.HandleTable.GenerateHandle(_event1.ReadableEvent, out _event1Handle) != KernelResult.Success)
+                {
+                    throw new InvalidOperationException("Out of handles!");
+                }
             }
 
-            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle0, handle1);
+            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_event0Handle, _event1Handle);
 
             return ResultCode.Success;
         }
@@ -107,7 +116,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
                 return ResultCode.Unknown180;
             }
 
-            // Returns appletId, libraryAppletMode, outSize and a buffer. 
+            // Returns appletId, libraryAppletMode, outSize and a buffer.
             // Returned applet ids- (0x19, 0xf, 0xe)
             // libraryAppletMode seems to be 0 for all applets supported.
 
diff --git a/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessor.cs b/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessor.cs
index 8cfafb1add..e6fca499ca 100644
--- a/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessor.cs
+++ b/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessor.cs
@@ -11,6 +11,8 @@ namespace Ryujinx.HLE.HOS.Services.Nim.ShopServiceAccessServerInterface.ShopServ
     {
         private readonly KEvent _event;
 
+        private int _eventHandle;
+
         public IShopServiceAccessor(Horizon system)
         {
             _event = new KEvent(system.KernelContext);
@@ -22,12 +24,15 @@ namespace Ryujinx.HLE.HOS.Services.Nim.ShopServiceAccessServerInterface.ShopServ
         {
             MakeObject(context, new IShopServiceAsync());
 
-            if (context.Process.HandleTable.GenerateHandle(_event.ReadableEvent, out int handle) != KernelResult.Success)
+            if (_eventHandle == 0)
             {
-                throw new InvalidOperationException("Out of handles!");
+                if (context.Process.HandleTable.GenerateHandle(_event.ReadableEvent, out _eventHandle) != KernelResult.Success)
+                {
+                    throw new InvalidOperationException("Out of handles!");
+                }
             }
 
-            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle);
+            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_eventHandle);
 
             Logger.Stub?.PrintStub(LogClass.ServiceNim);
 
diff --git a/Ryujinx.HLE/HOS/Services/Ns/IAddOnContentManager.cs b/Ryujinx.HLE/HOS/Services/Ns/IAddOnContentManager.cs
index e9f27cb568..debcd57e30 100644
--- a/Ryujinx.HLE/HOS/Services/Ns/IAddOnContentManager.cs
+++ b/Ryujinx.HLE/HOS/Services/Ns/IAddOnContentManager.cs
@@ -9,7 +9,9 @@ namespace Ryujinx.HLE.HOS.Services.Ns
     [Service("aoc:u")]
     class IAddOnContentManager : IpcService
     {
-        KEvent _addOnContentListChangedEvent;
+        private readonly KEvent _addOnContentListChangedEvent;
+
+        private int _addOnContentListChangedEventHandle;
 
         public IAddOnContentManager(ServiceCtx context)
         {
@@ -22,7 +24,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns
         {
             long pid = context.Process.Pid;
 
-            // Official code checks ApplicationControlProperty.RuntimeAddOnContentInstall 
+            // Official code checks ApplicationControlProperty.RuntimeAddOnContentInstall
             // if true calls ns:am ListAvailableAddOnContent again to get updated count
 
             byte runtimeAddOnContentInstall = context.Device.Application.ControlData.Value.RuntimeAddOnContentInstall;
@@ -135,12 +137,15 @@ namespace Ryujinx.HLE.HOS.Services.Ns
         {
             // Official code seems to make an internal call to ns:am Cmd 84 GetDynamicCommitEvent()
 
-            if (context.Process.HandleTable.GenerateHandle(_addOnContentListChangedEvent.ReadableEvent, out int handle) != KernelResult.Success)
+            if (_addOnContentListChangedEventHandle == 0)
             {
-                throw new InvalidOperationException("Out of handles!");
+                if (context.Process.HandleTable.GenerateHandle(_addOnContentListChangedEvent.ReadableEvent, out _addOnContentListChangedEventHandle) != KernelResult.Success)
+                {
+                    throw new InvalidOperationException("Out of handles!");
+                }
             }
 
-            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle);
+            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_addOnContentListChangedEventHandle);
 
             Logger.Stub?.PrintStub(LogClass.ServiceNs);
 
@@ -148,7 +153,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns
         }
 
 
-        [Command(9)] // [10.0.0+] 
+        [Command(9)] // [10.0.0+]
         // GetAddOnContentLostErrorCode() -> u64
         public ResultCode GetAddOnContentLostErrorCode(ServiceCtx context)
         {
diff --git a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs b/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs
index 0812683b7a..9f1ace36b8 100644
--- a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs
+++ b/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs
@@ -4,7 +4,6 @@ using Ryujinx.Cpu;
 using Ryujinx.HLE.Exceptions;
 using Ryujinx.HLE.HOS.Ipc;
 using Ryujinx.HLE.HOS.Kernel.Memory;
-using Ryujinx.HLE.HOS.Kernel.Process;
 using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices;
 using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu;
 using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel;
@@ -12,6 +11,7 @@ using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl;
 using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu;
 using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
 using Ryujinx.HLE.HOS.Services.Nv.Types;
+using Ryujinx.Memory;
 using System;
 using System.Collections.Generic;
 using System.Reflection;
@@ -27,47 +27,45 @@ namespace Ryujinx.HLE.HOS.Services.Nv
         private static Dictionary<string, Type> _deviceFileRegistry =
                    new Dictionary<string, Type>()
         {
-                       { "/dev/nvmap",           typeof(NvMapDeviceFile)         },
-                       { "/dev/nvhost-ctrl",     typeof(NvHostCtrlDeviceFile)    },
-                       { "/dev/nvhost-ctrl-gpu", typeof(NvHostCtrlGpuDeviceFile) },
-                       { "/dev/nvhost-as-gpu",   typeof(NvHostAsGpuDeviceFile)   },
-                       { "/dev/nvhost-gpu",      typeof(NvHostGpuDeviceFile)     },
-                     //{ "/dev/nvhost-msenc",    typeof(NvHostChannelDeviceFile) },
-                       { "/dev/nvhost-nvdec",    typeof(NvHostChannelDeviceFile) },
-                     //{ "/dev/nvhost-nvjpg",    typeof(NvHostChannelDeviceFile) },
-                       { "/dev/nvhost-vic",      typeof(NvHostChannelDeviceFile) },
-                     //{ "/dev/nvhost-display",  typeof(NvHostChannelDeviceFile) },
+            { "/dev/nvmap",           typeof(NvMapDeviceFile)         },
+            { "/dev/nvhost-ctrl",     typeof(NvHostCtrlDeviceFile)    },
+            { "/dev/nvhost-ctrl-gpu", typeof(NvHostCtrlGpuDeviceFile) },
+            { "/dev/nvhost-as-gpu",   typeof(NvHostAsGpuDeviceFile)   },
+            { "/dev/nvhost-gpu",      typeof(NvHostGpuDeviceFile)     },
+            //{ "/dev/nvhost-msenc",    typeof(NvHostChannelDeviceFile) },
+            { "/dev/nvhost-nvdec",    typeof(NvHostChannelDeviceFile) },
+            //{ "/dev/nvhost-nvjpg",    typeof(NvHostChannelDeviceFile) },
+            { "/dev/nvhost-vic",      typeof(NvHostChannelDeviceFile) },
+            //{ "/dev/nvhost-display",  typeof(NvHostChannelDeviceFile) },
         };
 
         private static IdDictionary _deviceFileIdRegistry = new IdDictionary();
 
-        private KProcess _owner;
+        private IVirtualMemoryManager _clientMemory;
+        private long _owner;
 
         private bool _transferMemInitialized = false;
 
-        public INvDrvServices(ServiceCtx context) : base(new ServerBase("NvservicesServer"))
+        public INvDrvServices(ServiceCtx context) : base(new ServerBase(context.Device.System.KernelContext, "NvservicesServer"))
         {
-            _owner = null;
+            _owner = 0;
         }
 
         private int Open(ServiceCtx context, string path)
         {
-            if (context.Process == _owner)
+            if (_deviceFileRegistry.TryGetValue(path, out Type deviceFileClass))
             {
-                if (_deviceFileRegistry.TryGetValue(path, out Type deviceFileClass))
-                {
-                    ConstructorInfo constructor = deviceFileClass.GetConstructor(new Type[] { typeof(ServiceCtx) });
+                ConstructorInfo constructor = deviceFileClass.GetConstructor(new Type[] { typeof(ServiceCtx), typeof(IVirtualMemoryManager), typeof(long) });
 
-                    NvDeviceFile deviceFile = (NvDeviceFile)constructor.Invoke(new object[] { context });
+                NvDeviceFile deviceFile = (NvDeviceFile)constructor.Invoke(new object[] { context, _clientMemory, _owner });
 
-                    deviceFile.Path = path;
+                deviceFile.Path = path;
 
-                    return _deviceFileIdRegistry.Add(deviceFile);
-                }
-                else
-                {
-                    Logger.Warning?.Print(LogClass.ServiceNv, $"Cannot find file device \"{path}\"!");
-                }
+                return _deviceFileIdRegistry.Add(deviceFile);
+            }
+            else
+            {
+                Logger.Warning?.Print(LogClass.ServiceNv, $"Cannot find file device \"{path}\"!");
             }
 
             return -1;
@@ -150,7 +148,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
                 return NvResult.NotImplemented;
             }
 
-            if (deviceFile.Owner.Pid != _owner.Pid)
+            if (deviceFile.Owner != _owner)
             {
                 return NvResult.AccessDenied;
             }
@@ -160,7 +158,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
 
         private NvResult EnsureInitialized()
         {
-            if (_owner == null)
+            if (_owner == 0)
             {
                 Logger.Warning?.Print(LogClass.ServiceNv, "INvDrvServices is not initialized!");
 
@@ -229,8 +227,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv
             if (errorCode == NvResult.Success)
             {
                 long pathPtr = context.Request.SendBuff[0].Position;
+                long pathSize = context.Request.SendBuff[0].Size;
 
-                string path = MemoryHelper.ReadAsciiString(context.Memory, pathPtr);
+                string path = MemoryHelper.ReadAsciiString(context.Memory, pathPtr, pathSize);
 
                 fd = Open(context, path);
 
@@ -322,7 +321,11 @@ namespace Ryujinx.HLE.HOS.Services.Nv
             // TODO: When transfer memory will be implemented, this could be removed.
             _transferMemInitialized = true;
 
-            _owner = context.Process;
+            int clientHandle = context.Request.HandleDesc.ToCopy[0];
+
+            _clientMemory = context.Process.HandleTable.GetKProcess(clientHandle).CpuMemory;
+
+            context.Device.System.KernelContext.Syscall.GetProcessId(clientHandle, out _owner);
 
             context.ResponseData.Write((uint)NvResult.Success);
 
@@ -425,7 +428,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
         // ForceSetClientPid(u64) -> u32 error_code
         public ResultCode ForceSetClientPid(ServiceCtx context)
         {
-            throw new ServiceNotImplementedException(context);
+            throw new ServiceNotImplementedException(this, context);
         }
 
         [Command(8)]
@@ -452,7 +455,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
         // InitializeDevtools(u32, handle<copy>) -> u32 error_code;
         public ResultCode InitializeDevtools(ServiceCtx context)
         {
-            throw new ServiceNotImplementedException(context);
+            throw new ServiceNotImplementedException(this, context);
         }
 
         [Command(11)] // 3.0.0+
diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvDeviceFile.cs
index af4734ea95..34ff4ccb43 100644
--- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvDeviceFile.cs
+++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvDeviceFile.cs
@@ -1,6 +1,5 @@
 using Ryujinx.Common.Logging;
 using Ryujinx.HLE.HOS.Kernel.Memory;
-using Ryujinx.HLE.HOS.Kernel.Process;
 using System;
 using System.Diagnostics;
 using System.Reflection;
@@ -12,14 +11,14 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices
     abstract class NvDeviceFile
     {
         public readonly ServiceCtx Context;
-        public readonly KProcess   Owner;
+        public readonly long       Owner;
 
         public string Path;
 
-        public NvDeviceFile(ServiceCtx context)
+        public NvDeviceFile(ServiceCtx context, long owner)
         {
             Context = context;
-            Owner   = context.Process;
+            Owner   = owner;
         }
 
         public virtual NvInternalResult QueryEvent(out int eventHandle, uint eventId)
diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs
index 0000f49592..6c49fd5ca1 100644
--- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs
+++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs
@@ -3,6 +3,7 @@ using Ryujinx.Graphics.Gpu.Memory;
 using Ryujinx.HLE.HOS.Kernel.Process;
 using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types;
 using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
+using Ryujinx.Memory;
 using System;
 using System.Collections.Concurrent;
 
@@ -12,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
     {
         private static ConcurrentDictionary<KProcess, AddressSpaceContext> _addressSpaceContextRegistry = new ConcurrentDictionary<KProcess, AddressSpaceContext>();
 
-        public NvHostAsGpuDeviceFile(ServiceCtx context) : base(context) { }
+        public NvHostAsGpuDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, long owner) : base(context, owner) { }
 
         public override NvInternalResult Ioctl(NvIoctl command, Span<byte> arguments)
         {
diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs
index 863b01d1d0..d675ffc701 100644
--- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs
+++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs
@@ -5,6 +5,7 @@ using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel.Types;
 using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl;
 using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
 using Ryujinx.HLE.HOS.Services.Nv.Types;
+using Ryujinx.Memory;
 using System;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
@@ -19,9 +20,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
         private uint _submitTimeout;
         private uint _timeslice;
 
-        private Switch _device;
+        private readonly Switch _device;
 
-        private Cpu.MemoryManager _memory;
+        private readonly IVirtualMemoryManager _memory;
 
         public enum ResourcePolicy
         {
@@ -37,10 +38,10 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
 
         private NvFence _channelSyncpoint;
 
-        public NvHostChannelDeviceFile(ServiceCtx context) : base(context)
+        public NvHostChannelDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, long owner) : base(context, owner)
         {
             _device        = context.Device;
-            _memory        = context.Memory;
+            _memory        = memory;
             _timeout       = 3000;
             _submitTimeout = 0;
             _timeslice     = 0;
diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostGpuDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostGpuDeviceFile.cs
index 523891404f..f27c065ed6 100644
--- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostGpuDeviceFile.cs
+++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostGpuDeviceFile.cs
@@ -1,6 +1,7 @@
 using Ryujinx.HLE.HOS.Kernel.Common;
 using Ryujinx.HLE.HOS.Kernel.Threading;
 using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel.Types;
+using Ryujinx.Memory;
 using System;
 
 namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
@@ -11,7 +12,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
         private KEvent _smExceptionBptPauseReportEvent;
         private KEvent _errorNotifierEvent;
 
-        public NvHostGpuDeviceFile(ServiceCtx context) : base(context)
+        public NvHostGpuDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, long owner) : base(context, memory, owner)
         {
             _smExceptionBptIntReportEvent   = new KEvent(context.Device.System.KernelContext);
             _smExceptionBptPauseReportEvent = new KEvent(context.Device.System.KernelContext);
@@ -55,7 +56,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
 
             if (targetEvent != null)
             {
-                if (Owner.HandleTable.GenerateHandle(targetEvent.ReadableEvent, out eventHandle) != KernelResult.Success)
+                if (Context.Process.HandleTable.GenerateHandle(targetEvent.ReadableEvent, out eventHandle) != KernelResult.Success)
                 {
                     throw new InvalidOperationException("Out of handles!");
                 }
diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/NvHostCtrlDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/NvHostCtrlDeviceFile.cs
index a538459605..f0e5634e67 100644
--- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/NvHostCtrlDeviceFile.cs
+++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/NvHostCtrlDeviceFile.cs
@@ -5,7 +5,7 @@ using Ryujinx.HLE.HOS.Kernel.Threading;
 using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl.Types;
 using Ryujinx.HLE.HOS.Services.Nv.Types;
 using Ryujinx.HLE.HOS.Services.Settings;
-
+using Ryujinx.Memory;
 using System;
 using System.Text;
 using System.Threading;
@@ -20,7 +20,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
         private Switch        _device;
         private NvHostEvent[] _events;
 
-        public NvHostCtrlDeviceFile(ServiceCtx context) : base(context)
+        public NvHostCtrlDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, long owner) : base(context, owner)
         {
             if (NxSettings.Settings.TryGetValue("nv!rmos_set_production_mode", out object productionModeSetting))
             {
@@ -126,7 +126,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
 
             if (targetEvent != null)
             {
-                if (Owner.HandleTable.GenerateHandle(targetEvent.ReadableEvent, out eventHandle) != KernelResult.Success)
+                if (Context.Process.HandleTable.GenerateHandle(targetEvent.ReadableEvent, out eventHandle) != KernelResult.Success)
                 {
                     throw new InvalidOperationException("Out of handles!");
                 }
diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/NvHostCtrlGpuDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/NvHostCtrlGpuDeviceFile.cs
index e0acf0dfd9..840d95b93a 100644
--- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/NvHostCtrlGpuDeviceFile.cs
+++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/NvHostCtrlGpuDeviceFile.cs
@@ -2,6 +2,7 @@
 using Ryujinx.HLE.HOS.Kernel.Common;
 using Ryujinx.HLE.HOS.Kernel.Threading;
 using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu.Types;
+using Ryujinx.Memory;
 using System;
 using System.Diagnostics;
 
@@ -15,7 +16,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu
         private KEvent _errorEvent;
         private KEvent _unknownEvent;
 
-        public NvHostCtrlGpuDeviceFile(ServiceCtx context) : base(context)
+        public NvHostCtrlGpuDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, long owner) : base(context, owner)
         {
             _errorEvent   = new KEvent(context.Device.System.KernelContext);
             _unknownEvent = new KEvent(context.Device.System.KernelContext);
@@ -98,7 +99,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu
 
             if (targetEvent != null)
             {
-                if (Owner.HandleTable.GenerateHandle(targetEvent.ReadableEvent, out eventHandle) != KernelResult.Success)
+                if (Context.Process.HandleTable.GenerateHandle(targetEvent.ReadableEvent, out eventHandle) != KernelResult.Success)
                 {
                     throw new InvalidOperationException("Out of handles!");
                 }
diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs
index 3cc47c422f..a549c81ca3 100644
--- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs
+++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs
@@ -1,7 +1,7 @@
 using Ryujinx.Common;
 using Ryujinx.Common.Logging;
 using Ryujinx.Graphics.Gpu.Memory;
-using Ryujinx.HLE.HOS.Kernel.Process;
+using Ryujinx.Memory;
 using System;
 using System.Collections.Concurrent;
 
@@ -11,9 +11,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
     {
         private const int FlagNotFreedYet = 1;
 
-        private static ConcurrentDictionary<KProcess, IdDictionary> _maps = new ConcurrentDictionary<KProcess, IdDictionary>();
+        private static ConcurrentDictionary<long, IdDictionary> _maps = new ConcurrentDictionary<long, IdDictionary>();
 
-        public NvMapDeviceFile(ServiceCtx context) : base(context)
+        public NvMapDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, long owner) : base(context, owner)
         {
             IdDictionary dict = _maps.GetOrAdd(Owner, (key) => new IdDictionary());
 
@@ -244,9 +244,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
             return dict.Add(map);
         }
 
-        private static bool DeleteMapWithHandle(KProcess process, int handle)
+        private static bool DeleteMapWithHandle(long pid, int handle)
         {
-            if (_maps.TryGetValue(process, out IdDictionary dict))
+            if (_maps.TryGetValue(pid, out IdDictionary dict))
             {
                 return dict.Delete(handle) != null;
             }
@@ -254,14 +254,14 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
             return false;
         }
 
-        public static void IncrementMapRefCount(KProcess process, int handle, bool allowHandleZero = false)
+        public static void IncrementMapRefCount(long pid, int handle, bool allowHandleZero = false)
         {
-            GetMapFromHandle(process, handle, allowHandleZero)?.IncrementRefCount();
+            GetMapFromHandle(pid, handle, allowHandleZero)?.IncrementRefCount();
         }
 
-        public static bool DecrementMapRefCount(KProcess process, int handle)
+        public static bool DecrementMapRefCount(long pid, int handle)
         {
-            NvMapHandle map = GetMapFromHandle(process, handle, false);
+            NvMapHandle map = GetMapFromHandle(pid, handle, false);
 
             if (map == null)
             {
@@ -270,7 +270,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
 
             if (map.DecrementRefCount() <= 0)
             {
-                DeleteMapWithHandle(process, handle);
+                DeleteMapWithHandle(pid, handle);
 
                 Logger.Info?.Print(LogClass.ServiceNv, $"Deleted map {handle}!");
 
@@ -282,9 +282,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
             }
         }
 
-        public static NvMapHandle GetMapFromHandle(KProcess process, int handle, bool allowHandleZero = false)
+        public static NvMapHandle GetMapFromHandle(long pid, int handle, bool allowHandleZero = false)
         {
-            if ((allowHandleZero || handle != 0) && _maps.TryGetValue(process, out IdDictionary dict))
+            if ((allowHandleZero || handle != 0) && _maps.TryGetValue(pid, out IdDictionary dict))
             {
                 return dict.GetData<NvMapHandle>(handle);
             }
diff --git a/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs b/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs
index 46374acc45..c78634faf7 100644
--- a/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs
+++ b/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs
@@ -53,7 +53,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro
                 return ResultCode.InvalidAddress;
             }
 
-            StructReader reader = new StructReader(context.Memory, nrrAddress);
+            StructReader reader = new StructReader(_owner.CpuMemory, nrrAddress);
             NrrHeader    header = reader.Read<NrrHeader>();
 
             if (header.Magic != NrrMagic)
@@ -71,7 +71,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro
             {
                 byte[] temp = new byte[0x20];
 
-                context.Memory.Read((ulong)(nrrAddress + header.HashOffset + (i * 0x20)), temp);
+                _owner.CpuMemory.Read((ulong)(nrrAddress + header.HashOffset + (i * 0x20)), temp);
 
                 hashes.Add(temp);
             }
@@ -131,8 +131,8 @@ namespace Ryujinx.HLE.HOS.Services.Ro
                 return ResultCode.InvalidAddress;
             }
 
-            uint magic       = context.Memory.Read<uint>(nroAddress + 0x10);
-            uint nroFileSize = context.Memory.Read<uint>(nroAddress + 0x18);
+            uint magic       = _owner.CpuMemory.Read<uint>(nroAddress + 0x10);
+            uint nroFileSize = _owner.CpuMemory.Read<uint>(nroAddress + 0x18);
 
             if (magic != NroMagic || nroSize != nroFileSize)
             {
@@ -141,7 +141,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro
 
             byte[] nroData = new byte[nroSize];
 
-            context.Memory.Read(nroAddress, nroData);
+            _owner.CpuMemory.Read(nroAddress, nroData);
 
             byte[] nroHash = null;
 
@@ -176,7 +176,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro
             // Check if everything is contiguous.
             if (nro.RoOffset   != nro.TextOffset + nro.Text.Length ||
                 nro.DataOffset != nro.RoOffset   + nro.Ro.Length   ||
-                nroFileSize           != nro.DataOffset + nro.Data.Length)
+                nroFileSize    != nro.DataOffset + nro.Data.Length)
             {
                 return ResultCode.InvalidNro;
             }
@@ -337,21 +337,21 @@ namespace Ryujinx.HLE.HOS.Services.Ro
 
             KernelResult result;
 
-            result = process.MemoryManager.SetProcessMemoryPermission(textStart, roStart - textStart, MemoryPermission.ReadAndExecute);
+            result = process.MemoryManager.SetProcessMemoryPermission(textStart, roStart - textStart, KMemoryPermission.ReadAndExecute);
 
             if (result != KernelResult.Success)
             {
                 return result;
             }
 
-            result = process.MemoryManager.SetProcessMemoryPermission(roStart, dataStart - roStart, MemoryPermission.Read);
+            result = process.MemoryManager.SetProcessMemoryPermission(roStart, dataStart - roStart, KMemoryPermission.Read);
 
             if (result != KernelResult.Success)
             {
                 return result;
             }
 
-            return process.MemoryManager.SetProcessMemoryPermission(dataStart, bssEnd - dataStart, MemoryPermission.ReadAndWrite);
+            return process.MemoryManager.SetProcessMemoryPermission(dataStart, bssEnd - dataStart, KMemoryPermission.ReadAndWrite);
         }
 
         private ResultCode RemoveNrrInfo(long nrrAddress)
@@ -420,9 +420,9 @@ namespace Ryujinx.HLE.HOS.Services.Ro
             return (ResultCode)result;
         }
 
-        private ResultCode IsInitialized(KProcess process)
+        private ResultCode IsInitialized(long pid)
         {
-            if (_owner != null && _owner.Pid == process.Pid)
+            if (_owner != null && _owner.Pid == pid)
             {
                 return ResultCode.Success;
             }
@@ -434,7 +434,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro
         // LoadNro(u64, u64, u64, u64, u64, pid) -> u64
         public ResultCode LoadNro(ServiceCtx context)
         {
-            ResultCode result = IsInitialized(context.Process);
+            ResultCode result = IsInitialized(_owner.Pid);
 
             // Zero
             context.RequestData.ReadUInt64();
@@ -454,11 +454,11 @@ namespace Ryujinx.HLE.HOS.Services.Ro
 
                 if (result == ResultCode.Success)
                 {
-                    result = MapNro(context.Process, info, out nroMappedAddress);
+                    result = MapNro(_owner, info, out nroMappedAddress);
 
                     if (result == ResultCode.Success)
                     {
-                        result = (ResultCode)SetNroMemoryPermissions(context.Process, info.Executable, nroMappedAddress);
+                        result = (ResultCode)SetNroMemoryPermissions(_owner, info.Executable, nroMappedAddress);
 
                         if (result == ResultCode.Success)
                         {
@@ -479,7 +479,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro
         // UnloadNro(u64, u64, pid)
         public ResultCode UnloadNro(ServiceCtx context)
         {
-            ResultCode result = IsInitialized(context.Process);
+            ResultCode result = IsInitialized(_owner.Pid);
 
             // Zero
             context.RequestData.ReadUInt64();
@@ -503,7 +503,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro
         // LoadNrr(u64, u64, u64, pid)
         public ResultCode LoadNrr(ServiceCtx context)
         {
-            ResultCode result = IsInitialized(context.Process);
+            ResultCode result = IsInitialized(_owner.Pid);
 
             // pid placeholder, zero
             context.RequestData.ReadUInt64();
@@ -536,7 +536,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro
         // UnloadNrr(u64, u64, pid)
         public ResultCode UnloadNrr(ServiceCtx context)
         {
-            ResultCode result = IsInitialized(context.Process);
+            ResultCode result = IsInitialized(_owner.Pid);
 
             // pid placeholder, zero
             context.RequestData.ReadUInt64();
@@ -565,7 +565,8 @@ namespace Ryujinx.HLE.HOS.Services.Ro
                 return ResultCode.InvalidSession;
             }
 
-            _owner = context.Process;
+            _owner = context.Process.HandleTable.GetKProcess(context.Request.HandleDesc.ToCopy[0]);
+            context.Device.System.KernelContext.Syscall.CloseHandle(context.Request.HandleDesc.ToCopy[0]);
 
             return ResultCode.Success;
         }
diff --git a/Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs b/Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs
index 001c38e2de..5be54f009b 100644
--- a/Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs
+++ b/Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs
@@ -9,6 +9,8 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl
     [Service("pl:s")] // 9.0.0+
     class ISharedFontManager : IpcService
     {
+        private int _fontSharedMemHandle;
+
         public ISharedFontManager(ServiceCtx context) { }
 
         [Command(0)]
@@ -63,12 +65,15 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl
         {
             context.Device.System.Font.EnsureInitialized(context.Device.System.ContentManager);
 
-            if (context.Process.HandleTable.GenerateHandle(context.Device.System.FontSharedMem, out int handle) != KernelResult.Success)
+            if (_fontSharedMemHandle == 0)
             {
-                throw new InvalidOperationException("Out of handles!");
+                if (context.Process.HandleTable.GenerateHandle(context.Device.System.FontSharedMem, out _fontSharedMemHandle) != KernelResult.Success)
+                {
+                    throw new InvalidOperationException("Out of handles!");
+                }
             }
 
-            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle);
+            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_fontSharedMemHandle);
 
             return ResultCode.Success;
         }
diff --git a/Ryujinx.HLE/HOS/Services/ServerBase.cs b/Ryujinx.HLE/HOS/Services/ServerBase.cs
index 211e0e6be7..f70d930faf 100644
--- a/Ryujinx.HLE/HOS/Services/ServerBase.cs
+++ b/Ryujinx.HLE/HOS/Services/ServerBase.cs
@@ -1,62 +1,193 @@
-using Ryujinx.Common;
 using Ryujinx.HLE.HOS.Ipc;
+using Ryujinx.HLE.HOS.Kernel;
 using Ryujinx.HLE.HOS.Kernel.Common;
 using Ryujinx.HLE.HOS.Kernel.Ipc;
 using Ryujinx.HLE.HOS.Kernel.Process;
 using Ryujinx.HLE.HOS.Kernel.Threading;
 using System;
+using System.Buffers.Binary;
+using System.Collections.Generic;
 using System.IO;
+using System.Threading;
 
 namespace Ryujinx.HLE.HOS.Services
 {
     class ServerBase
     {
-        private struct IpcRequest
-        {
-            public Switch Device { get; }
-            public KProcess Process => Thread?.Owner;
-            public KThread Thread { get; }
-            public KClientSession Session { get; }
-            public ulong MessagePtr { get; }
-            public ulong MessageSize { get; }
+        // Must be the maximum value used by services (highest one know is the one used by nvservices = 0x8000).
+        // Having a size that is too low will cause failures as data copy will fail if the receiving buffer is
+        // not large enough.
+        private const int PointerBufferSize = 0x8000;
 
-            public IpcRequest(Switch device, KThread thread, KClientSession session, ulong messagePtr, ulong messageSize)
+        private readonly static int[] DefaultCapabilities = new int[]
+        {
+            0x030363F7,
+            0x1FFFFFCF,
+            0x207FFFEF,
+            0x47E0060F,
+            0x0048BFFF,
+            0x01007FFF
+        };
+
+        private readonly KernelContext _context;
+        private readonly KProcess _selfProcess;
+
+        private readonly List<int> _sessionHandles = new List<int>();
+        private readonly List<int> _portHandles = new List<int>();
+        private readonly Dictionary<int, IpcService> _sessions = new Dictionary<int, IpcService>();
+        private readonly Dictionary<int, IpcService> _ports = new Dictionary<int, IpcService>();
+
+        public ManualResetEvent InitDone { get; }
+        public IpcService SmObject { get; set; }
+        public string Name { get; }
+
+        public ServerBase(KernelContext context, string name)
+        {
+            InitDone = new ManualResetEvent(false);
+            Name = name;
+            _context = context;
+
+            const ProcessCreationFlags flags =
+                ProcessCreationFlags.EnableAslr |
+                ProcessCreationFlags.AddressSpace64Bit |
+                ProcessCreationFlags.Is64Bit |
+                ProcessCreationFlags.PoolPartitionSystem;
+
+            ProcessCreationInfo creationInfo = new ProcessCreationInfo("Service", 1, 0, 0x8000000, 1, flags, 0, 0);
+
+            context.Syscall.CreateProcess(creationInfo, DefaultCapabilities, out int handle, null, ServerLoop);
+
+            _selfProcess = context.Scheduler.GetCurrentProcess().HandleTable.GetKProcess(handle);
+
+            context.Syscall.StartProcess(handle, 44, 3, 0x1000);
+        }
+
+        private void AddPort(int serverPortHandle, IpcService obj)
+        {
+            _portHandles.Add(serverPortHandle);
+            _ports.Add(serverPortHandle, obj);
+        }
+
+        public void AddSessionObj(KServerSession serverSession, IpcService obj)
+        {
+            _selfProcess.HandleTable.GenerateHandle(serverSession, out int serverSessionHandle);
+            AddSessionObj(serverSessionHandle, obj);
+        }
+
+        public void AddSessionObj(int serverSessionHandle, IpcService obj)
+        {
+            _sessionHandles.Add(serverSessionHandle);
+            _sessions.Add(serverSessionHandle, obj);
+        }
+
+        private void ServerLoop()
+        {
+            if (SmObject != null)
             {
-                Device = device;
-                Thread = thread;
-                Session = session;
-                MessagePtr = messagePtr;
-                MessageSize = messageSize;
+                _context.Syscall.ManageNamedPort("sm:", 50, out int serverPortHandle);
+
+                AddPort(serverPortHandle, SmObject);
+
+                InitDone.Set();
+            }
+            else
+            {
+                InitDone.Dispose();
             }
 
-            public void SignalDone(KernelResult result)
+            KThread thread = _context.Scheduler.GetCurrentThread();
+            ulong messagePtr = thread.TlsAddress;
+            _context.Syscall.SetHeapSize(0x200000, out ulong heapAddr);
+
+            _selfProcess.CpuMemory.Write(messagePtr + 0x0, 0);
+            _selfProcess.CpuMemory.Write(messagePtr + 0x4, 2 << 10);
+            _selfProcess.CpuMemory.Write(messagePtr + 0x8, heapAddr | ((ulong)PointerBufferSize << 48));
+
+            int replyTargetHandle = 0;
+
+            while (true)
             {
-                Thread.ObjSyncResult = result;
-                Thread.Reschedule(ThreadSchedState.Running);
+                int[] handles = _portHandles.ToArray();
+
+                for (int i = 0; i < handles.Length; i++)
+                {
+                    if (_context.Syscall.AcceptSession(handles[i], out int serverSessionHandle) == KernelResult.Success)
+                    {
+                        AddSessionObj(serverSessionHandle, _ports[handles[i]]);
+                    }
+                }
+
+                handles = _sessionHandles.ToArray();
+
+                var rc = _context.Syscall.ReplyAndReceive(handles, replyTargetHandle, 1000000L, out int signaledIndex);
+
+                thread.HandlePostSyscall();
+
+                if (!thread.Context.Running)
+                {
+                    break;
+                }
+
+                replyTargetHandle = 0;
+
+                if (rc == KernelResult.Success && signaledIndex != -1)
+                {
+                    int signaledHandle = handles[signaledIndex];
+
+                    if (Process(signaledHandle, heapAddr))
+                    {
+                        replyTargetHandle = signaledHandle;
+                    }
+                }
+                else
+                {
+                    _selfProcess.CpuMemory.Write(messagePtr + 0x0, 0);
+                    _selfProcess.CpuMemory.Write(messagePtr + 0x4, 2 << 10);
+                    _selfProcess.CpuMemory.Write(messagePtr + 0x8, heapAddr | ((ulong)PointerBufferSize << 48));
+                }
             }
         }
 
-        private readonly AsyncWorkQueue<IpcRequest> _ipcProcessor;
-
-        public ServerBase(string name)
+        private bool Process(int serverSessionHandle, ulong recvListAddr)
         {
-            _ipcProcessor = new AsyncWorkQueue<IpcRequest>(Process, name);
-        }
+            KProcess process = _context.Scheduler.GetCurrentProcess();
+            KThread thread = _context.Scheduler.GetCurrentThread();
+            ulong messagePtr = thread.TlsAddress;
+            ulong messageSize = 0x100;
 
-        public void PushMessage(Switch device, KThread thread, KClientSession session, ulong messagePtr, ulong messageSize)
-        {
-            _ipcProcessor.Add(new IpcRequest(device, thread, session, messagePtr, messageSize));
-        }
+            byte[] reqData = new byte[messageSize];
 
-        private void Process(IpcRequest message)
-        {
-            byte[] reqData = new byte[message.MessageSize];
+            process.CpuMemory.Read(messagePtr, reqData);
 
-            message.Process.CpuMemory.Read(message.MessagePtr, reqData);
-
-            IpcMessage request = new IpcMessage(reqData, (long)message.MessagePtr);
+            IpcMessage request = new IpcMessage(reqData, (long)messagePtr);
             IpcMessage response = new IpcMessage();
 
+            ulong tempAddr = recvListAddr;
+            int sizesOffset = request.RawData.Length - ((request.RecvListBuff.Count * 2 + 3) & ~3);
+
+            bool noReceive = true;
+
+            for (int i = 0; i < request.ReceiveBuff.Count; i++)
+            {
+                noReceive &= (request.ReceiveBuff[i].Position == 0);
+            }
+
+            if (noReceive)
+            {
+                for (int i = 0; i < request.RecvListBuff.Count; i++)
+                {
+                    int size = BinaryPrimitives.ReadInt16LittleEndian(request.RawData.AsSpan().Slice(sizesOffset + i * 2, 2));
+
+                    response.PtrBuff.Add(new IpcPtrBuffDesc((long)tempAddr, i, size));
+
+                    request.RecvListBuff[i] = new IpcRecvListBuffDesc((long)tempAddr, size);
+
+                    tempAddr += (ulong)size;
+                }
+            }
+
+            bool shouldReply = true;
+
             using (MemoryStream raw = new MemoryStream(request.RawData))
             {
                 BinaryReader reqReader = new BinaryReader(raw);
@@ -71,17 +202,16 @@ namespace Ryujinx.HLE.HOS.Services
                         BinaryWriter resWriter = new BinaryWriter(resMs);
 
                         ServiceCtx context = new ServiceCtx(
-                            message.Device,
-                            message.Process,
-                            message.Process.CpuMemory,
-                            message.Thread,
-                            message.Session,
+                            _context.Device,
+                            process,
+                            process.CpuMemory,
+                            thread,
                             request,
                             response,
                             reqReader,
                             resWriter);
 
-                        message.Session.Service.CallMethod(context);
+                        _sessions[serverSessionHandle].CallMethod(context);
 
                         response.RawData = resMs.ToArray();
                     }
@@ -95,11 +225,11 @@ namespace Ryujinx.HLE.HOS.Services
                     switch (cmdId)
                     {
                         case 0:
-                            request = FillResponse(response, 0, message.Session.Service.ConvertToDomain());
+                            request = FillResponse(response, 0, _sessions[serverSessionHandle].ConvertToDomain());
                             break;
 
                         case 3:
-                            request = FillResponse(response, 0, 0x1000);
+                            request = FillResponse(response, 0, PointerBufferSize);
                             break;
 
                         // TODO: Whats the difference between IpcDuplicateSession/Ex?
@@ -107,12 +237,11 @@ namespace Ryujinx.HLE.HOS.Services
                         case 4:
                             int unknown = reqReader.ReadInt32();
 
-                            if (message.Process.HandleTable.GenerateHandle(message.Session, out int handle) != KernelResult.Success)
-                            {
-                                throw new InvalidOperationException("Out of handles!");
-                            }
+                            _context.Syscall.CreateSession(false, 0, out int dupServerSessionHandle, out int dupClientSessionHandle);
 
-                            response.HandleDesc = IpcHandleDesc.MakeMove(handle);
+                            AddSessionObj(dupServerSessionHandle, _sessions[serverSessionHandle]);
+
+                            response.HandleDesc = IpcHandleDesc.MakeMove(dupClientSessionHandle);
 
                             request = FillResponse(response, 0);
 
@@ -123,18 +252,24 @@ namespace Ryujinx.HLE.HOS.Services
                 }
                 else if (request.Type == IpcMessageType.CloseSession)
                 {
-                    message.SignalDone(KernelResult.PortRemoteClosed);
-                    return;
+                    _context.Syscall.CloseHandle(serverSessionHandle);
+                    _sessionHandles.Remove(serverSessionHandle);
+                    IpcService service = _sessions[serverSessionHandle];
+                    if (service is IDisposable disposableObj)
+                    {
+                        disposableObj.Dispose();
+                    }
+                    _sessions.Remove(serverSessionHandle);
+                    shouldReply = false;
                 }
                 else
                 {
                     throw new NotImplementedException(request.Type.ToString());
                 }
 
-                message.Process.CpuMemory.Write(message.MessagePtr, response.GetBytes((long)message.MessagePtr));
+                process.CpuMemory.Write(messagePtr, response.GetBytes((long)messagePtr, recvListAddr | ((ulong)PointerBufferSize << 48)));
+                return shouldReply;
             }
-
-            message.SignalDone(KernelResult.Success);
         }
 
         private static IpcMessage FillResponse(IpcMessage response, long result, params int[] values)
diff --git a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs b/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs
index d8f31faf52..69bb37080a 100644
--- a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs
+++ b/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs
@@ -1,5 +1,6 @@
 using Ryujinx.Common.Logging;
 using Ryujinx.HLE.HOS.Ipc;
+using Ryujinx.HLE.HOS.Kernel;
 using Ryujinx.HLE.HOS.Kernel.Common;
 using Ryujinx.HLE.HOS.Kernel.Ipc;
 using System;
@@ -11,18 +12,17 @@ using System.Reflection;
 
 namespace Ryujinx.HLE.HOS.Services.Sm
 {
-    [Service("sm:")]
     class IUserInterface : IpcService
     {
         private Dictionary<string, Type> _services;
 
-        private ConcurrentDictionary<string, KPort> _registeredServices;
+        private readonly ConcurrentDictionary<string, KPort> _registeredServices;
 
         private readonly ServerBase _commonServer;
 
         private bool _isInitialized;
 
-        public IUserInterface(ServiceCtx context = null) : base(new ServerBase("SmServer"))
+        public IUserInterface(KernelContext context)
         {
             _registeredServices = new ConcurrentDictionary<string, KPort>();
 
@@ -31,18 +31,9 @@ namespace Ryujinx.HLE.HOS.Services.Sm
                 .Select(service => (((ServiceAttribute)service).Name, type)))
                 .ToDictionary(service => service.Name, service => service.type);
 
-            _commonServer = new ServerBase("CommonServer");
-        }
+            TrySetServer(new ServerBase(context, "SmServer") { SmObject = this });
 
-        public static void InitializePort(Horizon system)
-        {
-            KPort port = new KPort(system.KernelContext, 256, false, 0);
-
-            port.ClientPort.SetName("sm:");
-
-            IUserInterface smService = new IUserInterface();
-
-            port.ClientPort.Service = smService;
+            _commonServer = new ServerBase(context, "CommonServer");
         }
 
         [Command(0)]
@@ -92,16 +83,13 @@ namespace Ryujinx.HLE.HOS.Services.Sm
                         : (IpcService)Activator.CreateInstance(type, context);
 
                     service.TrySetServer(_commonServer);
-
-                    session.ClientSession.Service = service;
+                    service.Server.AddSessionObj(session.ServerSession, service);
                 }
                 else
                 {
                     if (ServiceConfiguration.IgnoreMissingServices)
                     {
                         Logger.Warning?.Print(LogClass.Service, $"Missing service {name} ignored");
-
-                        session.ClientSession.Service = new DummyService(name);
                     }
                     else
                     {
@@ -142,7 +130,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm
 
             int maxSessions = context.RequestData.ReadInt32();
 
-            if (name == string.Empty)
+            if (string.IsNullOrEmpty(name))
             {
                 return ResultCode.InvalidName;
             }
@@ -185,7 +173,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm
 
             int maxSessions = context.RequestData.ReadInt32();
 
-            if (name == string.Empty)
+            if (string.IsNullOrEmpty(name))
             {
                 return ResultCode.InvalidName;
             }
diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs
index 4ed621288f..f7dd7e1726 100644
--- a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs
+++ b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs
@@ -102,7 +102,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
 
         private List<BsdSocket> _sockets = new List<BsdSocket>();
 
-        public IClient(ServiceCtx context, bool isPrivileged)  : base(new ServerBase("BsdServer"))
+        public IClient(ServiceCtx context, bool isPrivileged) : base(new ServerBase(context.Device.System.KernelContext, "BsdServer"))
         {
             _isPrivileged = isPrivileged;
         }
@@ -247,6 +247,9 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
 
             Logger.Stub?.PrintStub(LogClass.ServiceBsd);
 
+            // Close transfer memory immediately as we don't use it.
+            context.Device.System.KernelContext.Syscall.CloseHandle(context.Request.HandleDesc.ToCopy[0]);
+
             return ResultCode.Success;
         }
 
diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs b/Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs
index 02a508b017..90f22dfb43 100644
--- a/Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs
+++ b/Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs
@@ -118,14 +118,14 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd
         // ImportSettings(u32, buffer<unknown, 5>) -> buffer<unknown, 6>
         public ResultCode ImportSettings(ServiceCtx context)
         {
-            throw new ServiceNotImplementedException(context);
+            throw new ServiceNotImplementedException(this, context);
         }
 
         [Command(15)]
         // Unknown(bytes<1>)
         public ResultCode Unknown(ServiceCtx context)
         {
-            throw new ServiceNotImplementedException(context);
+            throw new ServiceNotImplementedException(this, context);
         }
 
         [Command(20)]
@@ -164,49 +164,49 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd
         // GetNasServiceSetting(buffer<unknown<0x10>, 0x15>) -> buffer<unknown<0x108>, 0x16>
         public ResultCode GetNasServiceSetting(ServiceCtx context)
         {
-            throw new ServiceNotImplementedException(context);
+            throw new ServiceNotImplementedException(this, context);
         }
 
         [Command(31)]
         // GetNasServiceSettingEx(buffer<unknown<0x10>, 0x15>) -> (u32, buffer<unknown<0x108>, 0x16>)
         public ResultCode GetNasServiceSettingEx(ServiceCtx context)
         {
-            throw new ServiceNotImplementedException(context);
+            throw new ServiceNotImplementedException(this, context);
         }
 
         [Command(40)]
         // GetNasRequestFqdn() -> buffer<unknown<0x100>, 0x16>
         public ResultCode GetNasRequestFqdn(ServiceCtx context)
         {
-            throw new ServiceNotImplementedException(context);
+            throw new ServiceNotImplementedException(this, context);
         }
 
         [Command(41)]
         // GetNasRequestFqdnEx() -> (u32, buffer<unknown<0x100>, 0x16>)
         public ResultCode GetNasRequestFqdnEx(ServiceCtx context)
         {
-            throw new ServiceNotImplementedException(context);
+            throw new ServiceNotImplementedException(this, context);
         }
 
         [Command(42)]
         // GetNasApiFqdn() -> buffer<unknown<0x100>, 0x16>
         public ResultCode GetNasApiFqdn(ServiceCtx context)
         {
-            throw new ServiceNotImplementedException(context);
+            throw new ServiceNotImplementedException(this, context);
         }
 
         [Command(43)]
         // GetNasApiFqdnEx() -> (u32, buffer<unknown<0x100>, 0x16>)
         public ResultCode GetNasApiFqdnEx(ServiceCtx context)
         {
-            throw new ServiceNotImplementedException(context);
+            throw new ServiceNotImplementedException(this, context);
         }
 
         [Command(50)]
         // GetCurrentSetting() -> buffer<unknown<0x12bf0>, 0x16>
         public ResultCode GetCurrentSetting(ServiceCtx context)
         {
-            throw new ServiceNotImplementedException(context);
+            throw new ServiceNotImplementedException(this, context);
         }
 
         [Command(60)]
@@ -262,7 +262,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd
         // IsChangeEnvironmentIdentifierDisabled() -> bytes<1>
         public ResultCode IsChangeEnvironmentIdentifierDisabled(ServiceCtx context)
         {
-            throw new ServiceNotImplementedException(context);
+            throw new ServiceNotImplementedException(this, context);
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueue.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueue.cs
index 3dd21fde9b..2a9a606446 100644
--- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueue.cs
+++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueue.cs
@@ -1,15 +1,15 @@
-using Ryujinx.HLE.HOS.Kernel.Process;
-
-namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
+namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
 {
-    class BufferQueue
+    static class BufferQueue
     {
-        public static void CreateBufferQueue(Switch device, KProcess process, out BufferQueueProducer producer, out BufferQueueConsumer consumer)
+        public static BufferQueueCore CreateBufferQueue(Switch device, long pid, out BufferQueueProducer producer, out BufferQueueConsumer consumer)
         {
-            BufferQueueCore core = new BufferQueueCore(device, process);
+            BufferQueueCore core = new BufferQueueCore(device, pid);
 
             producer = new BufferQueueProducer(core);
             consumer = new BufferQueueConsumer(core);
+
+            return core;
         }
     }
 }
diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs
index 7f6f6c310d..389a980bb6 100644
--- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs
+++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs
@@ -1,5 +1,5 @@
 using Ryujinx.Common.Logging;
-using Ryujinx.HLE.HOS.Kernel.Process;
+using Ryujinx.HLE.HOS.Kernel;
 using Ryujinx.HLE.HOS.Kernel.Threading;
 using Ryujinx.HLE.HOS.Services.SurfaceFlinger.Types;
 using System;
@@ -40,11 +40,13 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
         private KEvent _waitBufferFreeEvent;
         private KEvent _frameAvailableEvent;
 
-        public KProcess Owner { get; }
+        public long Owner { get; }
+
+        public bool Active { get; private set; }
 
         public const int BufferHistoryArraySize = 8;
 
-        public BufferQueueCore(Switch device, KProcess process)
+        public BufferQueueCore(Switch device, long pid)
         {
             Slots                    = new BufferSlotArray();
             IsAbandoned              = false;
@@ -70,7 +72,9 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
             _waitBufferFreeEvent  = new KEvent(device.System.KernelContext);
             _frameAvailableEvent = new KEvent(device.System.KernelContext);
 
-            Owner = process;
+            Owner = pid;
+
+            Active = true;
 
             BufferHistory        = new BufferInfo[BufferHistoryArraySize];
             EnableExternalEvent  = true;
@@ -162,6 +166,16 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
             }
         }
 
+        public void PrepareForExit()
+        {
+            lock (Lock)
+            {
+                Active = false;
+
+                Monitor.PulseAll(Lock);
+            }
+        }
+
         // TODO: Find an accurate way to handle a regular condvar here as this will wake up unwanted threads in some edge cases.
         public void SignalDequeueEvent()
         {
@@ -170,7 +184,11 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
 
         public void WaitDequeueEvent()
         {
-            Monitor.Wait(Lock);
+            Monitor.Exit(Lock);
+
+            KernelStatic.YieldUntilCompletion(WaitForLock);
+
+            Monitor.Enter(Lock);
         }
 
         public void SignalIsAllocatingEvent()
@@ -180,7 +198,22 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
 
         public void WaitIsAllocatingEvent()
         {
-            Monitor.Wait(Lock);
+            Monitor.Exit(Lock);
+
+            KernelStatic.YieldUntilCompletion(WaitForLock);
+
+            Monitor.Enter(Lock);
+        }
+
+        private void WaitForLock()
+        {
+            lock (Lock)
+            {
+                if (Active)
+                {
+                    Monitor.Wait(Lock);
+                }
+            }
         }
 
         public void FreeBufferLocked(int slot)
diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueProducer.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueProducer.cs
index 6ef49538ec..03df04adb6 100644
--- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueProducer.cs
+++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueProducer.cs
@@ -816,6 +816,11 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
                     }
 
                     Core.WaitDequeueEvent();
+
+                    if (!Core.Active)
+                    {
+                        break;
+                    }
                 }
             }
 
diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs
index 8ee943df80..b3c8138193 100644
--- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs
+++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs
@@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
 {
     abstract class IHOSBinderDriver : IpcService
     {
-        public IHOSBinderDriver() {}
+        public IHOSBinderDriver() { }
 
         [Command(0)]
         // TransactParcel(s32, u32, u32, buffer<unknown, 5, 0>) -> buffer<unknown, 6, 0>
diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs
index 4713e50b66..4927b40ec4 100644
--- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs
+++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs
@@ -1,10 +1,7 @@
 using Ryujinx.Common.Logging;
 using Ryujinx.Graphics.GAL;
 using Ryujinx.Graphics.Gpu;
-using Ryujinx.HLE.HOS.Kernel.Process;
-using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl;
 using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
-using Ryujinx.HLE.HOS.Services.Nv.Types;
 using System;
 using System.Collections.Generic;
 using System.Diagnostics;
@@ -40,7 +37,8 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
             public int                    ProducerBinderId;
             public IGraphicBufferProducer Producer;
             public BufferItemConsumer     Consumer;
-            public KProcess               Owner;
+            public BufferQueueCore        Core;
+            public long                   Owner;
         }
 
         private class TextureCallbackInformation
@@ -84,7 +82,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
             }
         }
 
-        public IGraphicBufferProducer OpenLayer(KProcess process, long layerId)
+        public IGraphicBufferProducer OpenLayer(long pid, long layerId)
         {
             bool needCreate;
 
@@ -95,13 +93,13 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
 
             if (needCreate)
             {
-                CreateLayerFromId(process, layerId);
+                CreateLayerFromId(pid, layerId);
             }
 
             return GetProducerByLayerId(layerId);
         }
 
-        public IGraphicBufferProducer CreateLayer(KProcess process, out long layerId)
+        public IGraphicBufferProducer CreateLayer(long pid, out long layerId)
         {
             layerId = 1;
 
@@ -116,25 +114,26 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
                 }
             }
 
-            CreateLayerFromId(process, layerId);
+            CreateLayerFromId(pid, layerId);
 
             return GetProducerByLayerId(layerId);
         }
 
-        private void CreateLayerFromId(KProcess process, long layerId)
+        private void CreateLayerFromId(long pid, long layerId)
         {
             lock (Lock)
             {
                 Logger.Info?.Print(LogClass.SurfaceFlinger, $"Creating layer {layerId}");
 
-                BufferQueue.CreateBufferQueue(_device, process, out BufferQueueProducer producer, out BufferQueueConsumer consumer);
+                BufferQueueCore core = BufferQueue.CreateBufferQueue(_device, pid, out BufferQueueProducer producer, out BufferQueueConsumer consumer);
 
                 _layers.Add(layerId, new Layer
                 {
                     ProducerBinderId = HOSBinderDriverServer.RegisterBinderObject(producer),
                     Producer         = producer,
                     Consumer         = new BufferItemConsumer(_device, consumer, 0, -1, false, this),
-                    Owner            = process
+                    Core             = core,
+                    Owner            = pid
                 });
 
                 LastId = layerId;
@@ -345,6 +344,11 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
         public void Dispose()
         {
             _isRunning = false;
+
+            foreach (Layer layer in _layers.Values)
+            {
+                layer.Core.PrepareForExit();
+            }
         }
 
         public void OnFrameAvailable(ref BufferItem item)
diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/GraphicBuffer.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/GraphicBuffer.cs
index 8d63d9ccce..d86ff21e85 100644
--- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/GraphicBuffer.cs
+++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/GraphicBuffer.cs
@@ -42,23 +42,23 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
             Buffer = parcel.ReadUnmanagedType<NvGraphicBuffer>();
         }
 
-        public void IncrementNvMapHandleRefCount(KProcess process)
+        public void IncrementNvMapHandleRefCount(long pid)
         {
-            NvMapDeviceFile.IncrementMapRefCount(process, Buffer.NvMapId);
+            NvMapDeviceFile.IncrementMapRefCount(pid, Buffer.NvMapId);
 
             for (int i = 0; i < Buffer.Surfaces.Length; i++)
             {
-                NvMapDeviceFile.IncrementMapRefCount(process, Buffer.Surfaces[i].NvMapHandle);
+                NvMapDeviceFile.IncrementMapRefCount(pid, Buffer.Surfaces[i].NvMapHandle);
             }
         }
 
-        public void DecrementNvMapHandleRefCount(KProcess process)
+        public void DecrementNvMapHandleRefCount(long pid)
         {
-            NvMapDeviceFile.DecrementMapRefCount(process, Buffer.NvMapId);
+            NvMapDeviceFile.DecrementMapRefCount(pid, Buffer.NvMapId);
 
             for (int i = 0; i < Buffer.Surfaces.Length; i++)
             {
-                NvMapDeviceFile.DecrementMapRefCount(process, Buffer.Surfaces[i].NvMapHandle);
+                NvMapDeviceFile.DecrementMapRefCount(pid, Buffer.Surfaces[i].NvMapHandle);
             }
         }
 
diff --git a/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForGlue.cs b/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForGlue.cs
index 743b9e13b1..ecee7206fa 100644
--- a/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForGlue.cs
+++ b/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForGlue.cs
@@ -15,10 +15,12 @@ namespace Ryujinx.HLE.HOS.Services.Time
         private IStaticServiceForPsc _inner;
         private TimePermissions      _permissions;
 
-        public IStaticServiceForGlue(ServiceCtx context, TimePermissions permissions) : base(new ServerBase("TimeServer"))
+        public IStaticServiceForGlue(ServiceCtx context, TimePermissions permissions) : base(new ServerBase(context.Device.System.KernelContext, "TimeServer"))
         {
             _permissions = permissions;
             _inner       = new IStaticServiceForPsc(context, permissions);
+            _inner.TrySetServer(Server);
+            _inner.SetParent(this);
         }
 
         [Command(0)]
diff --git a/Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs b/Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs
index 64b213816c..aeb0e8a8c5 100644
--- a/Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs
+++ b/Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs
@@ -149,7 +149,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
         public ResultCode Unknown50(ServiceCtx context)
         {
             // TODO: figure out the usage of this event
-            throw new ServiceNotImplementedException(context);
+            throw new ServiceNotImplementedException(this, context);
         }
 
         [Command(51)]
@@ -157,7 +157,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
         public ResultCode Unknown51(ServiceCtx context)
         {
             // TODO: figure out the usage of this event
-            throw new ServiceNotImplementedException(context);
+            throw new ServiceNotImplementedException(this, context);
         }
 
         [Command(52)]
@@ -165,7 +165,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
         public ResultCode Unknown52(ServiceCtx context)
         {
             // TODO: figure out the usage of this event
-            throw new ServiceNotImplementedException(context);
+            throw new ServiceNotImplementedException(this, context);
         }
 
         [Command(60)]
@@ -201,7 +201,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
         public ResultCode GetAlarmRegistrationEvent(ServiceCtx context)
         {
             // TODO
-            throw new ServiceNotImplementedException(context);
+            throw new ServiceNotImplementedException(this, context);
         }
 
         [Command(201)]
@@ -209,7 +209,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
         public ResultCode UpdateSteadyAlarms(ServiceCtx context)
         {
             // TODO
-            throw new ServiceNotImplementedException(context);
+            throw new ServiceNotImplementedException(this, context);
         }
 
         [Command(202)]
@@ -217,7 +217,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
         public ResultCode TryGetNextSteadyClockAlarmSnapshot(ServiceCtx context)
         {
             // TODO
-            throw new ServiceNotImplementedException(context);
+            throw new ServiceNotImplementedException(this, context);
         }
     }
 }
diff --git a/Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs b/Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs
index 22ffe65615..ee99da7753 100644
--- a/Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs
+++ b/Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs
@@ -5,8 +5,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi
     [Service("vi:u")]
     class IApplicationRootService : IpcService
     {
-        // vi:u/m/s aren't on 3 separate threads but we can't put them together with the current ServerBase
-        public IApplicationRootService(ServiceCtx context) : base(new ServerBase("ViServerU")) { }
+        public IApplicationRootService(ServiceCtx context) : base(new ServerBase(context.Device.System.KernelContext, "ViServerU")) { }
 
         [Command(0)]
         // GetDisplayService(u32) -> object<nn::visrv::sf::IApplicationDisplayService>
diff --git a/Ryujinx.HLE/HOS/Services/Vi/IManagerRootService.cs b/Ryujinx.HLE/HOS/Services/Vi/IManagerRootService.cs
index a90690ea50..6157a16f80 100644
--- a/Ryujinx.HLE/HOS/Services/Vi/IManagerRootService.cs
+++ b/Ryujinx.HLE/HOS/Services/Vi/IManagerRootService.cs
@@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi
     class IManagerRootService : IpcService
     {
         // vi:u/m/s aren't on 3 separate threads but we can't put them together with the current ServerBase
-        public IManagerRootService(ServiceCtx context) : base(new ServerBase("ViServerM")) { }
+        public IManagerRootService(ServiceCtx context) : base(new ServerBase(context.Device.System.KernelContext, "ViServerM")) { }
 
         [Command(2)]
         // GetDisplayService(u32) -> object<nn::visrv::sf::IApplicationDisplayService>
diff --git a/Ryujinx.HLE/HOS/Services/Vi/ISystemRootService.cs b/Ryujinx.HLE/HOS/Services/Vi/ISystemRootService.cs
index 65b2161306..9847a2da22 100644
--- a/Ryujinx.HLE/HOS/Services/Vi/ISystemRootService.cs
+++ b/Ryujinx.HLE/HOS/Services/Vi/ISystemRootService.cs
@@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi
     class ISystemRootService : IpcService
     {
         // vi:u/m/s aren't on 3 separate threads but we can't put them together with the current ServerBase
-        public ISystemRootService(ServiceCtx context) : base(new ServerBase("ViServerS")) { }
+        public ISystemRootService(ServiceCtx context) : base(new ServerBase(context.Device.System.KernelContext, "ViServerS")) { }
 
         [Command(1)]
         // GetDisplayService(u32) -> object<nn::visrv::sf::IApplicationDisplayService>
diff --git a/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IManagerDisplayService.cs b/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IManagerDisplayService.cs
index 012a81ff8f..6b874722d2 100644
--- a/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IManagerDisplayService.cs
+++ b/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IManagerDisplayService.cs
@@ -1,11 +1,10 @@
 using Ryujinx.Common.Logging;
-using Ryujinx.HLE.HOS.Services.SurfaceFlinger;
 
 namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService
 {
     class IManagerDisplayService : IpcService
     {
-        private static IApplicationDisplayService _applicationDisplayService;
+        private IApplicationDisplayService _applicationDisplayService;
 
         public IManagerDisplayService(IApplicationDisplayService applicationDisplayService)
         {
@@ -16,10 +15,13 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService
         // CreateManagedLayer(u32, u64, nn::applet::AppletResourceUserId) -> u64
         public ResultCode CreateManagedLayer(ServiceCtx context)
         {
-            long layerFlags = context.RequestData.ReadInt64();
-            long displayId  = context.RequestData.ReadInt64();
+            long layerFlags           = context.RequestData.ReadInt64();
+            long displayId            = context.RequestData.ReadInt64();
+            long appletResourceUserId = context.RequestData.ReadInt64();
 
-            context.Device.System.SurfaceFlinger.CreateLayer(context.Process, out long layerId);
+            long pid = context.Device.System.AppletState.AppletResourceUserIds.GetData<long>((int)appletResourceUserId);
+
+            context.Device.System.SurfaceFlinger.CreateLayer(pid, out long layerId);
 
             context.ResponseData.Write(layerId);
 
diff --git a/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/ISystemDisplayService.cs b/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/ISystemDisplayService.cs
index 194bda2dab..36f220c627 100644
--- a/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/ISystemDisplayService.cs
+++ b/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/ISystemDisplayService.cs
@@ -4,7 +4,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService
 {
     class ISystemDisplayService : IpcService
     {
-        private static IApplicationDisplayService _applicationDisplayService;
+        private IApplicationDisplayService _applicationDisplayService;
 
         public ISystemDisplayService(IApplicationDisplayService applicationDisplayService)
         {
diff --git a/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs b/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs
index bd00fc02bb..a4b9f358ac 100644
--- a/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs
+++ b/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs
@@ -11,7 +11,9 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
 {
     class IApplicationDisplayService : IpcService
     {
-        private IdDictionary _displays;
+        private readonly IdDictionary _displays;
+
+        private int _vsyncEventHandle;
 
         public IApplicationDisplayService()
         {
@@ -121,7 +123,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
             long userId    = context.RequestData.ReadInt64();
             long parcelPtr = context.Request.ReceiveBuff[0].Position;
 
-            IBinder producer = context.Device.System.SurfaceFlinger.OpenLayer(context.Process, layerId);
+            IBinder producer = context.Device.System.SurfaceFlinger.OpenLayer(context.Request.HandleDesc.PId, layerId);
 
             Parcel parcel = new Parcel(0x28, 0x4);
 
@@ -159,7 +161,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
             // TODO: support multi display.
             Display disp = _displays.GetData<Display>((int)displayId);
 
-            IBinder producer = context.Device.System.SurfaceFlinger.CreateLayer(context.Process, out long layerId);
+            IBinder producer = context.Device.System.SurfaceFlinger.CreateLayer(0, out long layerId);
 
             Parcel parcel = new Parcel(0x28, 0x4);
 
@@ -268,8 +270,8 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
                 const ulong defaultAlignment = 0x1000;
                 const ulong defaultSize      = 0x20000;
 
-                // NOTE: The official service setup a A8B8G8R8 texture with a linear layout and then query its size. 
-                //       As we don't need this texture on the emulator, we can just simplify this logic and directly 
+                // NOTE: The official service setup a A8B8G8R8 texture with a linear layout and then query its size.
+                //       As we don't need this texture on the emulator, we can just simplify this logic and directly
                 //       do a linear layout size calculation. (stride * height * bytePerPixel)
                 int   pitch              = BitUtils.AlignUp(BitUtils.DivRoundUp(width * 32, 8), 64);
                 int   memorySize         = pitch * BitUtils.AlignUp(height, 64);
@@ -289,12 +291,15 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
         {
             string name = GetDisplayName(context);
 
-            if (context.Process.HandleTable.GenerateHandle(context.Device.System.VsyncEvent.ReadableEvent, out int handle) != KernelResult.Success)
+            if (_vsyncEventHandle == 0)
             {
-                throw new InvalidOperationException("Out of handles!");
+                if (context.Process.HandleTable.GenerateHandle(context.Device.System.VsyncEvent.ReadableEvent, out _vsyncEventHandle) != KernelResult.Success)
+                {
+                    throw new InvalidOperationException("Out of handles!");
+                }
             }
 
-            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle);
+            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_vsyncEventHandle);
 
             return ResultCode.Success;
         }
diff --git a/Ryujinx.HLE/HOS/SystemState/AppletStateMgr.cs b/Ryujinx.HLE/HOS/SystemState/AppletStateMgr.cs
index 14348a12ff..b9229b3303 100644
--- a/Ryujinx.HLE/HOS/SystemState/AppletStateMgr.cs
+++ b/Ryujinx.HLE/HOS/SystemState/AppletStateMgr.cs
@@ -10,13 +10,17 @@ namespace Ryujinx.HLE.HOS.SystemState
 
         public FocusState FocusState { get; private set; }
 
-        public KEvent MessageEvent { get; private set; }
+        public KEvent MessageEvent { get; }
+
+        public IdDictionary AppletResourceUserIds { get; }
 
         public AppletStateMgr(Horizon system)
         {
             _messages = new ConcurrentQueue<MessageInfo>();
 
             MessageEvent = new KEvent(system.KernelContext);
+
+            AppletResourceUserIds = new IdDictionary();
         }
 
         public void SetFocus(bool isFocused)
diff --git a/Ryujinx.HLE/Loaders/Npdm/Npdm.cs b/Ryujinx.HLE/Loaders/Npdm/Npdm.cs
index 7f1e2935e3..29a2b0fc09 100644
--- a/Ryujinx.HLE/Loaders/Npdm/Npdm.cs
+++ b/Ryujinx.HLE/Loaders/Npdm/Npdm.cs
@@ -11,7 +11,7 @@ namespace Ryujinx.HLE.Loaders.Npdm
     {
         private const int MetaMagic = 'M' << 0 | 'E' << 8 | 'T' << 16 | 'A' << 24;
 
-        public byte   MmuFlags            { get; private set; }
+        public byte   ProcessFlags        { get; private set; }
         public bool   Is64Bit             { get; private set; }
         public byte   MainThreadPriority  { get; private set; }
         public byte   DefaultCpuId        { get; private set; }
@@ -35,9 +35,9 @@ namespace Ryujinx.HLE.Loaders.Npdm
 
             reader.ReadInt64();
 
-            MmuFlags = reader.ReadByte();
+            ProcessFlags = reader.ReadByte();
 
-            Is64Bit = (MmuFlags & 1) != 0;
+            Is64Bit = (ProcessFlags & 1) != 0;
 
             reader.ReadByte();
 
diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs
index 36ffc56326..2c58898c4c 100644
--- a/Ryujinx.HLE/Switch.cs
+++ b/Ryujinx.HLE/Switch.cs
@@ -93,6 +93,7 @@ namespace Ryujinx.HLE
             FileSystem = fileSystem;
 
             System = new Horizon(this, contentManager);
+            System.InitializeServices();
 
             Statistics = new PerformanceStatistics();
 
diff --git a/Ryujinx.HLE/Utilities/StructReader.cs b/Ryujinx.HLE/Utilities/StructReader.cs
index 0c45a8ee52..3bde1c77b5 100644
--- a/Ryujinx.HLE/Utilities/StructReader.cs
+++ b/Ryujinx.HLE/Utilities/StructReader.cs
@@ -1,15 +1,16 @@
 using Ryujinx.Cpu;
+using Ryujinx.Memory;
 using System.Runtime.InteropServices;
 
 namespace Ryujinx.HLE.Utilities
 {
     class StructReader
     {
-        private MemoryManager _memory;
+        private IVirtualMemoryManager _memory;
 
         public long Position { get; private set; }
 
-        public StructReader(MemoryManager memory, long position)
+        public StructReader(IVirtualMemoryManager memory, long position)
         {
             _memory  = memory;
             Position = position;
diff --git a/Ryujinx.HLE/Utilities/StructWriter.cs b/Ryujinx.HLE/Utilities/StructWriter.cs
index 46a07a60cb..6b1e0838dd 100644
--- a/Ryujinx.HLE/Utilities/StructWriter.cs
+++ b/Ryujinx.HLE/Utilities/StructWriter.cs
@@ -1,15 +1,16 @@
 using Ryujinx.Cpu;
+using Ryujinx.Memory;
 using System.Runtime.InteropServices;
 
 namespace Ryujinx.HLE.Utilities
 {
     class StructWriter
     {
-        private MemoryManager _memory;
+        private IVirtualMemoryManager _memory;
 
         public long Position { get; private set; }
 
-        public StructWriter(MemoryManager memory, long position)
+        public StructWriter(IVirtualMemoryManager memory, long position)
         {
             _memory  = memory;
             Position = position;
diff --git a/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs b/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs
index 037bedc8cc..c673bc8038 100644
--- a/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs
+++ b/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs
@@ -1,4 +1,4 @@
-using Ryujinx.Memory.Tracking;
+using System;
 
 namespace Ryujinx.Memory.Tests
 {
@@ -10,19 +10,73 @@ namespace Ryujinx.Memory.Tests
         {
         }
 
+        public void Map(ulong va, ulong pa, ulong size)
+        {
+            throw new NotImplementedException();
+        }
+
+        public void Unmap(ulong va, ulong size)
+        {
+            throw new NotImplementedException();
+        }
+
+        public T Read<T>(ulong va) where T : unmanaged
+        {
+            throw new NotImplementedException();
+        }
+
+        public void Read(ulong va, Span<byte> data)
+        {
+            throw new NotImplementedException();
+        }
+
+        public void Write<T>(ulong va, T value) where T : unmanaged
+        {
+            throw new NotImplementedException();
+        }
+
+        public void Write(ulong va, ReadOnlySpan<byte> data)
+        {
+            throw new NotImplementedException();
+        }
+
+        public ReadOnlySpan<byte> GetSpan(ulong va, int size, bool tracked = false)
+        {
+            throw new NotImplementedException();
+        }
+
+        public WritableRegion GetWritableRegion(ulong va, int size)
+        {
+            throw new NotImplementedException();
+        }
+
+        public ref T GetRef<T>(ulong va) where T : unmanaged
+        {
+            throw new NotImplementedException();
+        }
+
         public (ulong address, ulong size)[] GetPhysicalRegions(ulong va, ulong size)
         {
             return NoMappings ? new (ulong address, ulong size)[0] : new (ulong address, ulong size)[] { (va, size) };
         }
 
+        public bool IsMapped(ulong va)
+        {
+            return true;
+        }
+
         public bool IsRangeMapped(ulong va, ulong size)
         {
             return true;
         }
 
+        public ulong GetPhysicalAddress(ulong va)
+        {
+            throw new NotImplementedException();
+        }
+
         public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection)
         {
-            
         }
     }
 }
diff --git a/Ryujinx.Memory/AddressSpaceManager.cs b/Ryujinx.Memory/AddressSpaceManager.cs
new file mode 100644
index 0000000000..c6d6cab562
--- /dev/null
+++ b/Ryujinx.Memory/AddressSpaceManager.cs
@@ -0,0 +1,549 @@
+using Ryujinx.Common;
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Memory
+{
+    /// <summary>
+    /// Represents a address space manager.
+    /// Supports virtual memory region mapping, address translation and read/write access to mapped regions.
+    /// </summary>
+    public sealed class AddressSpaceManager : IVirtualMemoryManager
+    {
+        public const int PageBits = 12;
+        public const int PageSize = 1 << PageBits;
+        public const int PageMask = PageSize - 1;
+
+        private const int PtLevelBits = 9; // 9 * 4 + 12 = 48 (max address space size)
+        private const int PtLevelSize = 1 << PtLevelBits;
+        private const int PtLevelMask = PtLevelSize - 1;
+
+        private const ulong Unmapped = ulong.MaxValue;
+
+        /// <summary>
+        /// Address space width in bits.
+        /// </summary>
+        public int AddressSpaceBits { get; }
+
+        private readonly ulong _addressSpaceSize;
+
+        private readonly MemoryBlock _backingMemory;
+
+        private readonly ulong[][][][] _pageTable;
+
+        /// <summary>
+        /// Creates a new instance of the memory manager.
+        /// </summary>
+        /// <param name="backingMemory">Physical backing memory where virtual memory will be mapped to</param>
+        /// <param name="addressSpaceSize">Size of the address space</param>
+        public AddressSpaceManager(MemoryBlock backingMemory, ulong addressSpaceSize)
+        {
+            ulong asSize = PageSize;
+            int asBits = PageBits;
+
+            while (asSize < addressSpaceSize)
+            {
+                asSize <<= 1;
+                asBits++;
+            }
+
+            AddressSpaceBits = asBits;
+            _addressSpaceSize = asSize;
+            _backingMemory = backingMemory;
+            _pageTable = new ulong[PtLevelSize][][][];
+        }
+
+        /// <summary>
+        /// Maps a virtual memory range into a physical memory range.
+        /// </summary>
+        /// <remarks>
+        /// Addresses and size must be page aligned.
+        /// </remarks>
+        /// <param name="va">Virtual memory address</param>
+        /// <param name="pa">Physical memory address</param>
+        /// <param name="size">Size to be mapped</param>
+        public void Map(ulong va, ulong pa, ulong size)
+        {
+            while (size != 0)
+            {
+                PtMap(va, pa);
+
+                va += PageSize;
+                pa += PageSize;
+                size -= PageSize;
+            }
+        }
+
+        /// <summary>
+        /// Unmaps a previously mapped range of virtual memory.
+        /// </summary>
+        /// <param name="va">Virtual address of the range to be unmapped</param>
+        /// <param name="size">Size of the range to be unmapped</param>
+        public void Unmap(ulong va, ulong size)
+        {
+            while (size != 0)
+            {
+                PtUnmap(va);
+
+                va += PageSize;
+                size -= PageSize;
+            }
+        }
+
+        /// <summary>
+        /// Reads data from mapped memory.
+        /// </summary>
+        /// <typeparam name="T">Type of the data being read</typeparam>
+        /// <param name="va">Virtual address of the data in memory</param>
+        /// <returns>The data</returns>
+        /// <exception cref="InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception>
+        public T Read<T>(ulong va) where T : unmanaged
+        {
+            return MemoryMarshal.Cast<byte, T>(GetSpan(va, Unsafe.SizeOf<T>()))[0];
+        }
+
+        /// <summary>
+        /// Reads data from mapped memory.
+        /// </summary>
+        /// <param name="va">Virtual address of the data in memory</param>
+        /// <param name="data">Span to store the data being read into</param>
+        /// <exception cref="InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception>
+        public void Read(ulong va, Span<byte> data)
+        {
+            ReadImpl(va, data);
+        }
+
+        /// <summary>
+        /// Writes data to mapped memory.
+        /// </summary>
+        /// <typeparam name="T">Type of the data being written</typeparam>
+        /// <param name="va">Virtual address to write the data into</param>
+        /// <param name="value">Data to be written</param>
+        /// <exception cref="InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception>
+        public void Write<T>(ulong va, T value) where T : unmanaged
+        {
+            Write(va, MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateSpan(ref value, 1)));
+        }
+
+        /// <summary>
+        /// Writes data to mapped memory.
+        /// </summary>
+        /// <param name="va">Virtual address to write the data into</param>
+        /// <param name="data">Data to be written</param>
+        /// <exception cref="InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception>
+        public void Write(ulong va, ReadOnlySpan<byte> data)
+        {
+            if (data.Length == 0)
+            {
+                return;
+            }
+
+            if (IsContiguousAndMapped(va, data.Length))
+            {
+                data.CopyTo(_backingMemory.GetSpan(GetPhysicalAddressInternal(va), data.Length));
+            }
+            else
+            {
+                int offset = 0, size;
+
+                if ((va & PageMask) != 0)
+                {
+                    ulong pa = GetPhysicalAddressInternal(va);
+
+                    size = Math.Min(data.Length, PageSize - (int)(va & PageMask));
+
+                    data.Slice(0, size).CopyTo(_backingMemory.GetSpan(pa, size));
+
+                    offset += size;
+                }
+
+                for (; offset < data.Length; offset += size)
+                {
+                    ulong pa = GetPhysicalAddressInternal(va + (ulong)offset);
+
+                    size = Math.Min(data.Length - offset, PageSize);
+
+                    data.Slice(offset, size).CopyTo(_backingMemory.GetSpan(pa, size));
+                }
+            }
+        }
+
+        /// <summary>
+        /// Gets a read-only span of data from mapped memory.
+        /// </summary>
+        /// <remarks>
+        /// This may perform a allocation if the data is not contiguous in memory.
+        /// For this reason, the span is read-only, you can't modify the data.
+        /// </remarks>
+        /// <param name="va">Virtual address of the data</param>
+        /// <param name="size">Size of the data</param>
+        /// <param name="tracked">True if read tracking is triggered on the span</param>
+        /// <returns>A read-only span of the data</returns>
+        /// <exception cref="InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception>
+        public ReadOnlySpan<byte> GetSpan(ulong va, int size, bool tracked = false)
+        {
+            if (size == 0)
+            {
+                return ReadOnlySpan<byte>.Empty;
+            }
+
+            if (IsContiguousAndMapped(va, size))
+            {
+                return _backingMemory.GetSpan(GetPhysicalAddressInternal(va), size);
+            }
+            else
+            {
+                Span<byte> data = new byte[size];
+
+                ReadImpl(va, data);
+
+                return data;
+            }
+        }
+
+        /// <summary>
+        /// Gets a region of memory that can be written to.
+        /// </summary>
+        /// <remarks>
+        /// If the requested region is not contiguous in physical memory,
+        /// this will perform an allocation, and flush the data (writing it
+        /// back to the backing memory) on disposal.
+        /// </remarks>
+        /// <param name="va">Virtual address of the data</param>
+        /// <param name="size">Size of the data</param>
+        /// <returns>A writable region of memory containing the data</returns>
+        /// <exception cref="InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception>
+        public WritableRegion GetWritableRegion(ulong va, int size)
+        {
+            if (size == 0)
+            {
+                return new WritableRegion(null, va, Memory<byte>.Empty);
+            }
+
+            if (IsContiguousAndMapped(va, size))
+            {
+                return new WritableRegion(null, va, _backingMemory.GetMemory(GetPhysicalAddressInternal(va), size));
+            }
+            else
+            {
+                Memory<byte> memory = new byte[size];
+
+                GetSpan(va, size).CopyTo(memory.Span);
+
+                return new WritableRegion(this, va, memory);
+            }
+        }
+
+        /// <summary>
+        /// Gets a reference for the given type at the specified virtual memory address.
+        /// </summary>
+        /// <remarks>
+        /// The data must be located at a contiguous memory region.
+        /// </remarks>
+        /// <typeparam name="T">Type of the data to get the reference</typeparam>
+        /// <param name="va">Virtual address of the data</param>
+        /// <returns>A reference to the data in memory</returns>
+        /// <exception cref="MemoryNotContiguousException">Throw if the specified memory region is not contiguous in physical memory</exception>
+        public ref T GetRef<T>(ulong va) where T : unmanaged
+        {
+            if (!IsContiguous(va, Unsafe.SizeOf<T>()))
+            {
+                ThrowMemoryNotContiguous();
+            }
+
+            return ref _backingMemory.GetRef<T>(GetPhysicalAddressInternal(va));
+        }
+
+        private void ThrowMemoryNotContiguous() => throw new MemoryNotContiguousException();
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private bool IsContiguousAndMapped(ulong va, int size) => IsContiguous(va, size) && IsMapped(va);
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private bool IsContiguous(ulong va, int size)
+        {
+            if (!ValidateAddress(va))
+            {
+                return false;
+            }
+
+            ulong endVa = (va + (ulong)size + PageMask) & ~(ulong)PageMask;
+
+            va &= ~(ulong)PageMask;
+
+            int pages = (int)((endVa - va) / PageSize);
+
+            for (int page = 0; page < pages - 1; page++)
+            {
+                if (!ValidateAddress(va + PageSize))
+                {
+                    return false;
+                }
+
+                if (GetPhysicalAddressInternal(va) + PageSize != GetPhysicalAddressInternal(va + PageSize))
+                {
+                    return false;
+                }
+
+                va += PageSize;
+            }
+
+            return true;
+        }
+
+        /// <summary>
+        /// Gets the physical regions that make up the given virtual address region.
+        /// If any part of the virtual region is unmapped, null is returned.
+        /// </summary>
+        /// <param name="va">Virtual address of the range</param>
+        /// <param name="size">Size of the range</param>
+        /// <returns>Array of physical regions</returns>
+        public (ulong address, ulong size)[] GetPhysicalRegions(ulong va, ulong size)
+        {
+            throw new NotImplementedException();
+        }
+
+        private void ReadImpl(ulong va, Span<byte> data)
+        {
+            if (data.Length == 0)
+            {
+                return;
+            }
+
+            int offset = 0, size;
+
+            if ((va & PageMask) != 0)
+            {
+                ulong pa = GetPhysicalAddressInternal(va);
+
+                size = Math.Min(data.Length, PageSize - (int)(va & PageMask));
+
+                _backingMemory.GetSpan(pa, size).CopyTo(data.Slice(0, size));
+
+                offset += size;
+            }
+
+            for (; offset < data.Length; offset += size)
+            {
+                ulong pa = GetPhysicalAddressInternal(va + (ulong)offset);
+
+                size = Math.Min(data.Length - offset, PageSize);
+
+                _backingMemory.GetSpan(pa, size).CopyTo(data.Slice(offset, size));
+            }
+        }
+
+        /// <summary>
+        /// Checks if the page at a given virtual address is mapped.
+        /// </summary>
+        /// <param name="va">Virtual address to check</param>
+        /// <returns>True if the address is mapped, false otherwise</returns>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public bool IsMapped(ulong va)
+        {
+            if (!ValidateAddress(va))
+            {
+                return false;
+            }
+
+            return PtRead(va) != Unmapped;
+        }
+
+        /// <summary>
+        /// Checks if a memory range is mapped.
+        /// </summary>
+        /// <param name="va">Virtual address of the range</param>
+        /// <param name="size">Size of the range in bytes</param>
+        /// <returns>True if the entire range is mapped, false otherwise</returns>
+        public bool IsRangeMapped(ulong va, ulong size)
+        {
+            if (size == 0UL)
+            {
+                return true;
+            }
+
+            ulong endVa = (va + size + PageMask) & ~(ulong)PageMask;
+
+            va &= ~(ulong)PageMask;
+
+            while (va < endVa)
+            {
+                if (!IsMapped(va))
+                {
+                    return false;
+                }
+
+                va += PageSize;
+            }
+
+            return true;
+        }
+
+        private bool ValidateAddress(ulong va)
+        {
+            return va < _addressSpaceSize;
+        }
+
+        /// <summary>
+        /// Performs address translation of the address inside a mapped memory range.
+        /// </summary>
+        /// <remarks>
+        /// If the address is invalid or unmapped, -1 will be returned.
+        /// </remarks>
+        /// <param name="va">Virtual address to be translated</param>
+        /// <returns>The physical address</returns>
+        public ulong GetPhysicalAddress(ulong va)
+        {
+            // We return -1L if the virtual address is invalid or unmapped.
+            if (!ValidateAddress(va) || !IsMapped(va))
+            {
+                return ulong.MaxValue;
+            }
+
+            return GetPhysicalAddressInternal(va);
+        }
+
+        private ulong GetPhysicalAddressInternal(ulong va)
+        {
+            return PtRead(va) + (va & PageMask);
+        }
+
+        /// <summary>
+        /// Reprotect a region of virtual memory for tracking. Sets software protection bits.
+        /// </summary>
+        /// <param name="va">Virtual address base</param>
+        /// <param name="size">Size of the region to protect</param>
+        /// <param name="protection">Memory protection to set</param>
+        public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection)
+        {
+            throw new NotImplementedException();
+        }
+
+        private ulong PtRead(ulong va)
+        {
+            int l3 = (int)(va >> PageBits) & PtLevelMask;
+            int l2 = (int)(va >> (PageBits + PtLevelBits)) & PtLevelMask;
+            int l1 = (int)(va >> (PageBits + PtLevelBits * 2)) & PtLevelMask;
+            int l0 = (int)(va >> (PageBits + PtLevelBits * 3)) & PtLevelMask;
+
+            if (_pageTable[l0] == null)
+            {
+                return Unmapped;
+            }
+
+            if (_pageTable[l0][l1] == null)
+            {
+                return Unmapped;
+            }
+
+            if (_pageTable[l0][l1][l2] == null)
+            {
+                return Unmapped;
+            }
+
+            return _pageTable[l0][l1][l2][l3];
+        }
+
+        private void PtMap(ulong va, ulong value)
+        {
+            int l3 = (int)(va >> PageBits) & PtLevelMask;
+            int l2 = (int)(va >> (PageBits + PtLevelBits)) & PtLevelMask;
+            int l1 = (int)(va >> (PageBits + PtLevelBits * 2)) & PtLevelMask;
+            int l0 = (int)(va >> (PageBits + PtLevelBits * 3)) & PtLevelMask;
+
+            if (_pageTable[l0] == null)
+            {
+                _pageTable[l0] = new ulong[PtLevelSize][][];
+            }
+
+            if (_pageTable[l0][l1] == null)
+            {
+                _pageTable[l0][l1] = new ulong[PtLevelSize][];
+            }
+
+            if (_pageTable[l0][l1][l2] == null)
+            {
+                _pageTable[l0][l1][l2] = new ulong[PtLevelSize];
+
+                for (int i = 0; i < _pageTable[l0][l1][l2].Length; i++)
+                {
+                    _pageTable[l0][l1][l2][i] = Unmapped;
+                }
+            }
+
+            _pageTable[l0][l1][l2][l3] = value;
+        }
+
+        private void PtUnmap(ulong va)
+        {
+            int l3 = (int)(va >> PageBits) & PtLevelMask;
+            int l2 = (int)(va >> (PageBits + PtLevelBits)) & PtLevelMask;
+            int l1 = (int)(va >> (PageBits + PtLevelBits * 2)) & PtLevelMask;
+            int l0 = (int)(va >> (PageBits + PtLevelBits * 3)) & PtLevelMask;
+
+            if (_pageTable[l0] == null)
+            {
+                return;
+            }
+
+            if (_pageTable[l0][l1] == null)
+            {
+                return;
+            }
+
+            if (_pageTable[l0][l1][l2] == null)
+            {
+                return;
+            }
+
+            _pageTable[l0][l1][l2][l3] = Unmapped;
+
+            bool empty = true;
+
+            for (int i = 0; i < _pageTable[l0][l1][l2].Length; i++)
+            {
+                empty &= (_pageTable[l0][l1][l2][i] == Unmapped);
+            }
+
+            if (empty)
+            {
+                _pageTable[l0][l1][l2] = null;
+
+                RemoveIfAllNull(l0, l1);
+            }
+        }
+
+        private void RemoveIfAllNull(int l0, int l1)
+        {
+            bool empty = true;
+
+            for (int i = 0; i < _pageTable[l0][l1].Length; i++)
+            {
+                empty &= (_pageTable[l0][l1][i] == null);
+            }
+
+            if (empty)
+            {
+                _pageTable[l0][l1] = null;
+
+                RemoveIfAllNull(l0);
+            }
+        }
+
+        private void RemoveIfAllNull(int l0)
+        {
+            bool empty = true;
+
+            for (int i = 0; i < _pageTable[l0].Length; i++)
+            {
+                empty &= (_pageTable[l0][i] == null);
+            }
+
+            if (empty)
+            {
+                _pageTable[l0] = null;
+            }
+        }
+    }
+}
diff --git a/Ryujinx.Memory/IVirtualMemoryManager.cs b/Ryujinx.Memory/IVirtualMemoryManager.cs
new file mode 100644
index 0000000000..8c2296e22c
--- /dev/null
+++ b/Ryujinx.Memory/IVirtualMemoryManager.cs
@@ -0,0 +1,28 @@
+using System;
+
+namespace Ryujinx.Memory
+{
+    public interface IVirtualMemoryManager
+    {
+        void Map(ulong va, ulong pa, ulong size);
+        void Unmap(ulong va, ulong size);
+
+        T Read<T>(ulong va) where T : unmanaged;
+        void Read(ulong va, Span<byte> data);
+
+        void Write<T>(ulong va, T value) where T : unmanaged;
+        void Write(ulong va, ReadOnlySpan<byte> data);
+
+        ReadOnlySpan<byte> GetSpan(ulong va, int size, bool tracked = false);
+        WritableRegion GetWritableRegion(ulong va, int size);
+        ref T GetRef<T>(ulong va) where T : unmanaged;
+
+        (ulong address, ulong size)[] GetPhysicalRegions(ulong va, ulong size);
+
+        bool IsMapped(ulong va);
+        bool IsRangeMapped(ulong va, ulong size);
+        ulong GetPhysicalAddress(ulong va);
+
+        void TrackingReprotect(ulong va, ulong size, MemoryPermission protection);
+    }
+}
diff --git a/Ryujinx.Cpu/MemoryNotContiguousException.cs b/Ryujinx.Memory/MemoryNotContiguousException.cs
similarity index 82%
rename from Ryujinx.Cpu/MemoryNotContiguousException.cs
rename to Ryujinx.Memory/MemoryNotContiguousException.cs
index d7c827cc90..3106955b81 100644
--- a/Ryujinx.Cpu/MemoryNotContiguousException.cs
+++ b/Ryujinx.Memory/MemoryNotContiguousException.cs
@@ -1,8 +1,8 @@
 using System;
 
-namespace Ryujinx.Cpu
+namespace Ryujinx.Memory
 {
-    class MemoryNotContiguousException : Exception
+    public class MemoryNotContiguousException : Exception
     {
         public MemoryNotContiguousException() : base("The specified memory region is not contiguous.")
         {
diff --git a/Ryujinx.Memory/Tracking/IVirtualMemoryManager.cs b/Ryujinx.Memory/Tracking/IVirtualMemoryManager.cs
deleted file mode 100644
index e6d8e8c98f..0000000000
--- a/Ryujinx.Memory/Tracking/IVirtualMemoryManager.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-namespace Ryujinx.Memory.Tracking
-{
-    public interface IVirtualMemoryManager
-    {
-        (ulong address, ulong size)[] GetPhysicalRegions(ulong va, ulong size);
-
-        bool IsRangeMapped(ulong va, ulong size);
-        void TrackingReprotect(ulong va, ulong size, MemoryPermission protection);
-    }
-}
diff --git a/Ryujinx.Cpu/WritableRegion.cs b/Ryujinx.Memory/WritableRegion.cs
similarity index 74%
rename from Ryujinx.Cpu/WritableRegion.cs
rename to Ryujinx.Memory/WritableRegion.cs
index 5ea0a2d83d..e8ea310fb0 100644
--- a/Ryujinx.Cpu/WritableRegion.cs
+++ b/Ryujinx.Memory/WritableRegion.cs
@@ -1,17 +1,17 @@
 using System;
 
-namespace Ryujinx.Cpu
+namespace Ryujinx.Memory
 {
     public sealed class WritableRegion : IDisposable
     {
-        private readonly MemoryManager _mm;
+        private readonly IVirtualMemoryManager _mm;
         private readonly ulong _va;
 
         private bool NeedsWriteback => _mm != null;
 
         public Memory<byte> Memory { get; }
 
-        internal WritableRegion(MemoryManager mm, ulong va, Memory<byte> memory)
+        public WritableRegion(IVirtualMemoryManager mm, ulong va, Memory<byte> memory)
         {
             _mm = mm;
             _va = va;
diff --git a/Ryujinx/Ui/GLRenderer.cs b/Ryujinx/Ui/GLRenderer.cs
index 10525a4be6..c8887544ce 100644
--- a/Ryujinx/Ui/GLRenderer.cs
+++ b/Ryujinx/Ui/GLRenderer.cs
@@ -33,9 +33,9 @@ namespace Ryujinx.Ui
 
         public static event EventHandler<StatusUpdatedEventArgs> StatusUpdatedEvent;
 
-        public bool IsActive   { get; set; }
-        public bool IsStopped  { get; set; }
-        public bool IsFocused  { get; set; }
+        private bool _isActive;
+        private bool _isStopped;
+        private bool _isFocused;
 
         private double _mouseX;
         private double _mouseY;
@@ -48,9 +48,9 @@ namespace Ryujinx.Ui
 
         private long _ticks = 0;
 
-        private System.Diagnostics.Stopwatch _chrono;
+        private readonly System.Diagnostics.Stopwatch _chrono;
 
-        private Switch _device;
+        private readonly Switch _device;
 
         private Renderer _renderer;
 
@@ -60,11 +60,13 @@ namespace Ryujinx.Ui
 
         private GraphicsDebugLevel _glLogLevel;
 
+        private readonly ManualResetEvent _exitEvent;
+
         public GlRenderer(Switch device, GraphicsDebugLevel glLogLevel)
             : base (GetGraphicsMode(),
             3, 3,
-            glLogLevel == GraphicsDebugLevel.None 
-            ? GraphicsContextFlags.ForwardCompatible 
+            glLogLevel == GraphicsDebugLevel.None
+            ? GraphicsContextFlags.ForwardCompatible
             : GraphicsContextFlags.ForwardCompatible | GraphicsContextFlags.Debug)
         {
             WaitEvent = new ManualResetEvent(false);
@@ -92,6 +94,8 @@ namespace Ryujinx.Ui
             _dsuClient = new Client();
 
             _glLogLevel = glLogLevel;
+
+            _exitEvent = new ManualResetEvent(false);
         }
 
         private static GraphicsMode GetGraphicsMode()
@@ -107,12 +111,12 @@ namespace Ryujinx.Ui
 
         private void Parent_FocusOutEvent(object o, Gtk.FocusOutEventArgs args)
         {
-            IsFocused = false;
+            _isFocused = false;
         }
 
         private void Parent_FocusInEvent(object o, Gtk.FocusInEventArgs args)
         {
-            IsFocused = true;
+            _isFocused = true;
         }
 
         private void GLRenderer_Destroyed(object sender, EventArgs e)
@@ -123,7 +127,7 @@ namespace Ryujinx.Ui
 
         protected void Renderer_Shown(object sender, EventArgs e)
         {
-            IsFocused = this.ParentWindow.State.HasFlag(Gdk.WindowState.Focused);
+            _isFocused = this.ParentWindow.State.HasFlag(Gdk.WindowState.Focused);
         }
 
         public void HandleScreenState(KeyboardState keyboard)
@@ -204,7 +208,7 @@ namespace Ryujinx.Ui
 
             _chrono.Restart();
 
-            IsActive = true;
+            _isActive = true;
 
             Gtk.Window parent = this.Toplevel as Gtk.Window;
 
@@ -316,13 +320,17 @@ namespace Ryujinx.Ui
         public void Exit()
         {
             _dsuClient?.Dispose();
-            if (IsStopped)
+
+            if (_isStopped)
             {
                 return;
             }
 
-            IsStopped = true;
-            IsActive  = false;
+            _isStopped = true;
+            _isActive  = false;
+
+            _exitEvent.WaitOne();
+            _exitEvent.Dispose();
         }
 
         public void Initialize()
@@ -353,9 +361,9 @@ namespace Ryujinx.Ui
             _device.Gpu.InitializeShaderCache();
             Translator.IsReadyForTranslation.Set();
 
-            while (IsActive)
+            while (_isActive)
             {
-                if (IsStopped)
+                if (_isStopped)
                 {
                     return;
                 }
@@ -401,28 +409,30 @@ namespace Ryujinx.Ui
 
         public void MainLoop()
         {
-            while (IsActive)
+            while (_isActive)
             {
                 UpdateFrame();
 
                 // Polling becomes expensive if it's not slept
                 Thread.Sleep(1);
             }
+
+            _exitEvent.Set();
         }
 
         private bool UpdateFrame()
         {
-            if (!IsActive)
+            if (!_isActive)
             {
                 return true;
             }
 
-            if (IsStopped)
+            if (_isStopped)
             {
                 return false;
             }
 
-            if (IsFocused)
+            if (_isFocused)
             {
                 Gtk.Application.Invoke(delegate
                 {
@@ -444,7 +454,7 @@ namespace Ryujinx.Ui
             List<SixAxisInput> motionInputs  = new List<SixAxisInput>(NpadDevices.MaxControllers);
 
             MotionDevice motionDevice = new MotionDevice(_dsuClient);
-            
+
             foreach (InputConfig inputConfig in ConfigurationState.Instance.Hid.InputConfig.Value)
             {
                 ControllerKeys   currentButton = 0;
@@ -464,7 +474,7 @@ namespace Ryujinx.Ui
 
                 if (inputConfig is KeyboardConfig keyboardConfig)
                 {
-                    if (IsFocused)
+                    if (_isFocused)
                     {
                         // Keyboard Input
                         KeyboardController keyboardController = new KeyboardController(keyboardConfig);
@@ -571,11 +581,11 @@ namespace Ryujinx.Ui
                     motionInputs.Add(sixAxisInput);
                 }
             }
-            
+
             _device.Hid.Npads.Update(gamepadInputs);
             _device.Hid.Npads.UpdateSixAxis(motionInputs);
 
-            if(IsFocused)
+            if(_isFocused)
             {
                 // Hotkeys
                 HotkeyButtons currentHotkeyButtons = KeyboardController.GetHotkeyButtons(OpenTK.Input.Keyboard.GetState());
@@ -594,7 +604,7 @@ namespace Ryujinx.Ui
 
             // Get screen touch position from left mouse click
             // OpenTK always captures mouse events, even if out of focus, so check if window is focused.
-            if (IsFocused && _mousePressed)
+            if (_isFocused && _mousePressed)
             {
                 int screenWidth  = AllocatedWidth;
                 int screenHeight = AllocatedHeight;