diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs
index 6aa211dd6..0986adf7a 100644
--- a/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs
@@ -115,19 +115,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
 
                 ulong clientEndAddrTruncated = BitUtils.AlignDown(clientEndAddr, KMemoryManager.PageSize);
                 ulong clientEndAddrRounded   = BitUtils.AlignUp  (clientEndAddr, KMemoryManager.PageSize);
-                ulong serverEndAddrTruncated = BitUtils.AlignDown(clientEndAddr, KMemoryManager.PageSize);
+                ulong serverEndAddrTruncated = BitUtils.AlignDown(serverEndAddr, KMemoryManager.PageSize);
 
-                if (clientEndAddrTruncated < clientAddrRounded)
+                if (clientEndAddrTruncated < clientEndAddrRounded &&
+                    (clientAddrTruncated == clientAddrRounded || clientAddrTruncated < clientEndAddrTruncated))
                 {
-                    KernelResult result = memoryManager.CopyDataToCurrentProcess(
+                    KernelResult result = memoryManager.CopyDataFromCurrentProcess(
                         clientEndAddrTruncated,
                         clientEndAddr - clientEndAddrTruncated,
-                        serverEndAddrTruncated,
                         stateMask,
                         stateMask,
                         MemoryPermission.ReadAndWrite,
                         attributeMask,
-                        MemoryAttribute.None);
+                        MemoryAttribute.None,
+                        serverEndAddrTruncated);
 
                     if (result != KernelResult.Success)
                     {
diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs
index 9c542ca0a..8d6669cf5 100644
--- a/Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs
@@ -1,21 +1,19 @@
 using Ryujinx.HLE.HOS.Kernel.Common;
 using Ryujinx.HLE.HOS.Kernel.Process;
 using Ryujinx.HLE.HOS.Services;
+using System.Threading;
 
 namespace Ryujinx.HLE.HOS.Kernel.Ipc
 {
     class KClientPort : KSynchronizationObject
     {
         private int _sessionsCount;
-        private int _currentCapacity;
         private readonly int _maxSessions;
 
         private readonly KPort _parent;
 
         public bool IsLight => _parent.IsLight;
 
-        private readonly object _countIncLock;
-
         // 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; }
@@ -24,8 +22,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
         {
             _maxSessions = maxSessions;
             _parent      = parent;
-
-            _countIncLock = new object();
         }
 
         public KernelResult Connect(out KClientSession clientSession)
@@ -40,26 +36,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
                 return KernelResult.ResLimitExceeded;
             }
 
-            lock (_countIncLock)
+            if (!IncrementSessionsCount())
             {
-                if (_sessionsCount < _maxSessions)
-                {
-                    _sessionsCount++;
-                }
-                else
-                {
-                    currentProcess.ResourceLimit?.Release(LimitableResource.Session, 1);
+                currentProcess.ResourceLimit?.Release(LimitableResource.Session, 1);
 
-                    return KernelResult.SessionCountExceeded;
-                }
-
-                if (_currentCapacity < _sessionsCount)
-                {
-                    _currentCapacity = _sessionsCount;
-                }
+                return KernelResult.SessionCountExceeded;
             }
 
-            KSession session = new KSession(KernelContext);
+            KSession session = new KSession(KernelContext, this);
 
             if (Service != null)
             {
@@ -93,18 +77,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
                 return KernelResult.ResLimitExceeded;
             }
 
-            lock (_countIncLock)
+            if (!IncrementSessionsCount())
             {
-                if (_sessionsCount < _maxSessions)
-                {
-                    _sessionsCount++;
-                }
-                else
-                {
-                    currentProcess.ResourceLimit?.Release(LimitableResource.Session, 1);
+                currentProcess.ResourceLimit?.Release(LimitableResource.Session, 1);
 
-                    return KernelResult.SessionCountExceeded;
-                }
+                return KernelResult.SessionCountExceeded;
             }
 
             KLightSession session = new KLightSession(KernelContext);
@@ -124,6 +101,43 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
             return result;
         }
 
+        private bool IncrementSessionsCount()
+        {
+            while (true)
+            {
+                int currentCount = _sessionsCount;
+
+                if (currentCount < _maxSessions)
+                {
+                    if (Interlocked.CompareExchange(ref _sessionsCount, currentCount + 1, currentCount) == currentCount)
+                    {
+                        return true;
+                    }
+                }
+                else
+                {
+                    return false;
+                }
+            }
+        }
+
+        public void Disconnect()
+        {
+            KernelContext.CriticalSection.Enter();
+
+            SignalIfMaximumReached(Interlocked.Decrement(ref _sessionsCount));
+
+            KernelContext.CriticalSection.Leave();
+        }
+
+        private void SignalIfMaximumReached(int value)
+        {
+            if (value == _maxSessions)
+            {
+                Signal();
+            }
+        }
+
         public new static KernelResult RemoveName(KernelContext context, string name)
         {
             KAutoObject foundObj = FindNamedObject(context, name);
diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs
index b99dd1cb9..262058d97 100644
--- a/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs
@@ -13,18 +13,22 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
 
         public ChannelState State { get; set; }
 
+        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) : base(context)
+        public KClientSession(KernelContext context, KSession parent, KClientPort parentPort) : base(context)
         {
-            _parent = parent;
+            _parent    = parent;
+            ParentPort = parentPort;
+
+            parentPort?.IncrementReferenceCount();
 
             State = ChannelState.Open;
 
             CreatorProcess = context.Scheduler.GetCurrentProcess();
-
             CreatorProcess.IncrementReferenceCount();
         }
 
@@ -51,6 +55,30 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
             return result;
         }
 
+        public KernelResult SendAsyncRequest(KWritableEvent asyncEvent, ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0)
+        {
+            KThread currentThread = KernelContext.Scheduler.GetCurrentThread();
+
+            KSessionRequest request = new KSessionRequest(currentThread, customCmdBuffAddr, customCmdBuffSize, asyncEvent);
+
+            KernelContext.CriticalSection.Enter();
+
+            KernelResult result = _parent.ServerSession.EnqueueRequest(request);
+
+            KernelContext.CriticalSection.Leave();
+
+            return result;
+        }
+
+        public void DisconnectFromPort()
+        {
+            if (ParentPort != null)
+            {
+                ParentPort.Disconnect();
+                ParentPort.DecrementReferenceCount();
+            }
+        }
+
         protected override void Destroy()
         {
             _parent.DisconnectClient();
diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs
index 70b54d054..486698328 100644
--- a/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs
@@ -633,7 +633,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
             {
                 CloseAllHandles(clientMsg, serverHeader, clientProcess);
 
-                CancelRequest(request, clientResult);
+                FinishRequest(request, clientResult);
             }
 
             if (clientHeader.ReceiveListType < 2 &&
@@ -770,6 +770,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
 
                 PointerBufferDesc descriptor = new PointerBufferDesc(pointerDesc);
 
+                ulong recvListBufferAddress = 0;
+
                 if (descriptor.BufferSize != 0)
                 {
                     clientResult = GetReceiveListAddress(
@@ -778,8 +780,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
                         clientHeader.ReceiveListType,
                         serverHeader.MessageSizeInWords,
                         receiveList,
-                        ref       recvListDstOffset,
-                        out ulong recvListBufferAddress);
+                        ref recvListDstOffset,
+                        out recvListBufferAddress);
 
                     if (clientResult != KernelResult.Success)
                     {
@@ -806,6 +808,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
                     }
                 }
 
+                ulong dstDescAddress = clientMsg.DramAddress + offset * 4;
+
+                ulong clientPointerDesc =
+                    (recvListBufferAddress << 32) |
+                    ((recvListBufferAddress >> 20) & 0xf000) |
+                    ((recvListBufferAddress >> 30) & 0xffc0);
+
+                clientPointerDesc |= pointerDesc & 0xffff000f;
+
+                KernelContext.Memory.Write(dstDescAddress + 0, clientPointerDesc);
+
                 offset += 2;
             }
 
@@ -860,16 +873,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
             }
 
             // Unmap buffers from server.
-            clientResult = request.BufferDescriptorTable.UnmapServerBuffers(serverProcess.MemoryManager);
-
-            if (clientResult != KernelResult.Success)
-            {
-                CleanUpForError();
-
-                return serverResult;
-            }
-
-            WakeClientThread(request, clientResult);
+            FinishRequest(request, clientResult);
 
             return serverResult;
         }
@@ -1109,7 +1113,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
         {
             foreach (KSessionRequest request in IterateWithRemovalOfAllRequests())
             {
-                CancelRequest(request, KernelResult.PortRemoteClosed);
+                FinishRequest(request, KernelResult.PortRemoteClosed);
             }
         }
 
@@ -1180,7 +1184,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
             return hasRequest;
         }
 
-        private void CancelRequest(KSessionRequest request, KernelResult result)
+        private void FinishRequest(KSessionRequest request, KernelResult result)
         {
             KProcess clientProcess = request.ClientThread.Owner;
             KProcess serverProcess = request.ServerProcess;
@@ -1221,14 +1225,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
         {
             KProcess clientProcess = request.ClientThread.Owner;
 
-            ulong address = clientProcess.MemoryManager.GetDramAddressFromVa(request.CustomCmdBuffAddr);
+            if (result != KernelResult.Success)
+            {
+                ulong address = clientProcess.MemoryManager.GetDramAddressFromVa(request.CustomCmdBuffAddr);
 
-            KernelContext.Memory.Write<ulong>(address, 0);
-            KernelContext.Memory.Write(address + 8, (int)result);
+                KernelContext.Memory.Write<ulong>(address, 0);
+                KernelContext.Memory.Write(address + 8, (int)result);
+            }
 
-            clientProcess.MemoryManager.UnborrowIpcBuffer(
-                request.CustomCmdBuffAddr,
-                request.CustomCmdBuffSize);
+            clientProcess.MemoryManager.UnborrowIpcBuffer(request.CustomCmdBuffAddr, request.CustomCmdBuffSize);
 
             request.AsyncEvent.Signal();
         }
diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs
index 25e6eee57..4b5886a9d 100644
--- a/Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs
@@ -11,10 +11,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
 
         private bool _hasBeenInitialized;
 
-        public KSession(KernelContext context) : base(context)
+        public KSession(KernelContext context, KClientPort parentPort = null) : base(context)
         {
             ServerSession = new KServerSession(context, this);
-            ClientSession = new KClientSession(context, this);
+            ClientSession = new KClientSession(context, this, parentPort);
 
             _hasBeenInitialized = true;
         }
@@ -54,10 +54,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
         {
             if (_hasBeenInitialized)
             {
+                ClientSession.DisconnectFromPort();
+
                 KProcess creatorProcess = ClientSession.CreatorProcess;
 
                 creatorProcess.ResourceLimit?.Release(LimitableResource.Session, 1);
-
                 creatorProcess.DecrementReferenceCount();
             }
         }
diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KSessionRequest.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KSessionRequest.cs
index f3467f395..31ddfc9c4 100644
--- a/Ryujinx.HLE/HOS/Kernel/Ipc/KSessionRequest.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KSessionRequest.cs
@@ -17,13 +17,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
         public ulong CustomCmdBuffSize { get; }
 
         public KSessionRequest(
-            KThread clientThread,
-            ulong   customCmdBuffAddr,
-            ulong   customCmdBuffSize)
+            KThread        clientThread,
+            ulong          customCmdBuffAddr,
+            ulong          customCmdBuffSize,
+            KWritableEvent asyncEvent = null)
         {
             ClientThread      = clientThread;
             CustomCmdBuffAddr = customCmdBuffAddr;
             CustomCmdBuffSize = customCmdBuffSize;
+            AsyncEvent        = asyncEvent;
 
             BufferDescriptorTable = new KBufferDescriptorTable();
         }
diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlock.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlock.cs
index b7c2b309d..04e14e1b3 100644
--- a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlock.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlock.cs
@@ -41,7 +41,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             Attribute |= attribute;
         }
 
-        public void SetIpcMappingPermission(MemoryPermission permission)
+        public void SetIpcMappingPermission(MemoryPermission newPermission)
         {
             int oldIpcRefCount = IpcRefCount++;
 
@@ -52,10 +52,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
 
             if (oldIpcRefCount == 0)
             {
-                SourcePermission = permission;
+                SourcePermission = Permission;
 
                 Permission &= ~MemoryPermission.ReadAndWrite;
-                Permission |=  MemoryPermission.ReadAndWrite & permission;
+                Permission |=  MemoryPermission.ReadAndWrite & newPermission;
             }
 
             Attribute |= MemoryAttribute.IpcMapped;
diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs
index 1cbe4e7c1..b13e28412 100644
--- a/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryManager.cs
@@ -1688,6 +1688,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             }
 
             ulong addressRounded   = BitUtils.AlignUp  (address, PageSize);
+            ulong addressTruncated = BitUtils.AlignDown(address, PageSize);
             ulong endAddrRounded   = BitUtils.AlignUp  (endAddr, PageSize);
             ulong endAddrTruncated = BitUtils.AlignDown(endAddr, PageSize);
 
@@ -1700,9 +1701,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
 
             void CleanUpForError()
             {
+                if (visitedSize == 0)
+                {
+                    return;
+                }
+
                 ulong endAddrVisited = address + visitedSize;
 
-                foreach (KMemoryInfo info in IterateOverRange(address, endAddrVisited))
+                foreach (KMemoryInfo info in IterateOverRange(addressRounded, endAddrVisited))
                 {
                     if ((info.Permission & MemoryPermission.ReadAndWrite) != permissionMask && info.IpcRefCount == 0)
                     {
@@ -1729,42 +1735,45 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             {
                 KernelResult result;
 
-                foreach (KMemoryInfo info in IterateOverRange(address, endAddrRounded))
+                if (addressRounded < endAddrTruncated)
                 {
-                    // Check if the block state matches what we expect.
-                    if ((info.State      & stateMask)     != stateMask  ||
-                        (info.Permission & permission)    != permission ||
-                        (info.Attribute  & attributeMask) != MemoryAttribute.None)
+                    foreach (KMemoryInfo info in IterateOverRange(addressTruncated, endAddrRounded))
                     {
-                        CleanUpForError();
-
-                        return KernelResult.InvalidMemState;
-                    }
-
-                    ulong blockAddress = GetAddrInRange(info, addressRounded);
-                    ulong blockSize    = GetSizeInRange(info, addressRounded, endAddrTruncated);
-
-                    ulong blockPagesCount = blockSize / PageSize;
-
-                    if ((info.Permission & MemoryPermission.ReadAndWrite) != permissionMask && info.IpcRefCount == 0)
-                    {
-                        result = DoMmuOperation(
-                            blockAddress,
-                            blockPagesCount,
-                            0,
-                            false,
-                            permissionMask,
-                            MemoryOperation.ChangePermRw);
-
-                        if (result != KernelResult.Success)
+                        // Check if the block state matches what we expect.
+                        if ((info.State      & stateMask)     != stateMask  ||
+                            (info.Permission & permission)    != permission ||
+                            (info.Attribute  & attributeMask) != MemoryAttribute.None)
                         {
                             CleanUpForError();
 
-                            return result;
+                            return KernelResult.InvalidMemState;
                         }
-                    }
 
-                    visitedSize += blockSize;
+                        ulong blockAddress = GetAddrInRange(info, addressRounded);
+                        ulong blockSize    = GetSizeInRange(info, addressRounded, endAddrTruncated);
+
+                        ulong blockPagesCount = blockSize / PageSize;
+
+                        if ((info.Permission & MemoryPermission.ReadAndWrite) != permissionMask && info.IpcRefCount == 0)
+                        {
+                            result = DoMmuOperation(
+                                blockAddress,
+                                blockPagesCount,
+                                0,
+                                false,
+                                permissionMask,
+                                MemoryOperation.ChangePermRw);
+
+                            if (result != KernelResult.Success)
+                            {
+                                CleanUpForError();
+
+                                return result;
+                            }
+                        }
+
+                        visitedSize += blockSize;
+                    }
                 }
 
                 result = GetPagesForIpcTransfer(address, size, copyData, aslrDisabled, region, out pageList);
@@ -1778,7 +1787,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
 
                 if (visitedSize != 0)
                 {
-                    InsertBlock(address, visitedSize / PageSize, SetIpcMappingPermissions, permissionMask);
+                    InsertBlock(addressRounded, visitedSize / PageSize, SetIpcMappingPermissions, permissionMask);
                 }
             }
 
@@ -1793,25 +1802,31 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             MemoryRegion  region,
             out KPageList pageList)
         {
+            // When the start address is unaligned, we can't safely map the
+            // first page as it would expose other undesirable information on the
+            // target process. So, instead we allocate new pages, copy the data
+            // inside the range, and then clear the remaining space.
+            // The same also holds for the last page, if the end address
+            // (address + size) is also not aligned.
+
             pageList = null;
 
+            KPageList pages = new KPageList();
+
             ulong addressTruncated = BitUtils.AlignDown(address, PageSize);
             ulong addressRounded   = BitUtils.AlignUp  (address, PageSize);
 
             ulong endAddr = address + size;
 
-            ulong dstFirstPagePa = AllocateSinglePage(region, aslrDisabled);
-
-            if (dstFirstPagePa == 0)
-            {
-                return KernelResult.OutOfMemory;
-            }
-
-            ulong dstLastPagePa = 0;
+            ulong dstFirstPagePa = 0;
+            ulong dstLastPagePa  = 0;
 
             void CleanUpForError()
             {
-                FreeSinglePage(region, dstFirstPagePa);
+                if (dstFirstPagePa != 0)
+                {
+                    FreeSinglePage(region, dstFirstPagePa);
+                }
 
                 if (dstLastPagePa != 0)
                 {
@@ -1819,56 +1834,60 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                 }
             }
 
-            ulong firstPageFillAddress = dstFirstPagePa;
-
-            if (!ConvertVaToPa(addressTruncated, out ulong srcFirstPagePa))
+            // Is the first page address aligned?
+            // If not, allocate a new page and copy the unaligned chunck.
+            if (addressTruncated < addressRounded)
             {
-                CleanUpForError();
+                dstFirstPagePa = AllocateSinglePage(region, aslrDisabled);
 
-                return KernelResult.InvalidMemState;
-            }
+                if (dstFirstPagePa == 0)
+                {
+                    return KernelResult.OutOfMemory;
+                }
 
-            ulong unusedSizeAfter;
+                ulong firstPageFillAddress = dstFirstPagePa;
 
-            // When the start address is unaligned, we can't safely map the
-            // first page as it would expose other undesirable information on the
-            // target process. So, instead we allocate new pages, copy the data
-            // inside the range, and then clear the remaining space.
-            // The same also holds for the last page, if the end address
-            // (address + size) is also not aligned.
-            if (copyData)
-            {
-                ulong unusedSizeBefore = address - addressTruncated;
+                if (!TryConvertVaToPa(addressTruncated, out ulong srcFirstPagePa))
+                {
+                    CleanUpForError();
 
-                _context.Memory.ZeroFill(dstFirstPagePa, unusedSizeBefore);
+                    return KernelResult.InvalidMemState;
+                }
 
-                ulong copySize = addressRounded <= endAddr ? addressRounded - address : size;
+                ulong unusedSizeAfter;
 
-                _context.Memory.Copy(
-                    GetDramAddressFromPa(dstFirstPagePa + unusedSizeBefore),
-                    GetDramAddressFromPa(srcFirstPagePa + unusedSizeBefore), copySize);
+                if (copyData)
+                {
+                    ulong unusedSizeBefore = address - addressTruncated;
 
-                firstPageFillAddress += unusedSizeBefore + copySize;
+                    _context.Memory.ZeroFill(dstFirstPagePa, unusedSizeBefore);
 
-                unusedSizeAfter = addressRounded > endAddr ? addressRounded - endAddr : 0;
-            }
-            else
-            {
-                unusedSizeAfter = PageSize;
-            }
+                    ulong copySize = addressRounded <= endAddr ? addressRounded - address : size;
 
-            if (unusedSizeAfter != 0)
-            {
-                _context.Memory.ZeroFill(firstPageFillAddress, unusedSizeAfter);
-            }
+                    _context.Memory.Copy(
+                        GetDramAddressFromPa(dstFirstPagePa + unusedSizeBefore),
+                        GetDramAddressFromPa(srcFirstPagePa + unusedSizeBefore), copySize);
 
-            KPageList pages = new KPageList();
+                    firstPageFillAddress += unusedSizeBefore + copySize;
 
-            if (pages.AddRange(dstFirstPagePa, 1) != KernelResult.Success)
-            {
-                CleanUpForError();
+                    unusedSizeAfter = addressRounded > endAddr ? addressRounded - endAddr : 0;
+                }
+                else
+                {
+                    unusedSizeAfter = PageSize;
+                }
 
-                return KernelResult.OutOfResource;
+                if (unusedSizeAfter != 0)
+                {
+                    _context.Memory.ZeroFill(firstPageFillAddress, unusedSizeAfter);
+                }
+
+                if (pages.AddRange(dstFirstPagePa, 1) != KernelResult.Success)
+                {
+                    CleanUpForError();
+
+                    return KernelResult.OutOfResource;
+                }
             }
 
             ulong endAddrTruncated = BitUtils.AlignDown(endAddr, PageSize);
@@ -1881,9 +1900,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                 AddVaRangeToPageList(pages, addressRounded, alignedPagesCount);
             }
 
-            if (endAddrTruncated != endAddrRounded)
+            // Is the last page end address aligned?
+            // If not, allocate a new page and copy the unaligned chunck.
+            if (endAddrTruncated < endAddrRounded && (addressTruncated == addressRounded || addressTruncated < endAddrTruncated))
             {
-                // End is also not aligned...
                 dstLastPagePa = AllocateSinglePage(region, aslrDisabled);
 
                 if (dstLastPagePa == 0)
@@ -1895,13 +1915,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
 
                 ulong lastPageFillAddr = dstLastPagePa;
 
-                if (!ConvertVaToPa(endAddrTruncated, out ulong srcLastPagePa))
+                if (!TryConvertVaToPa(endAddrTruncated, out ulong srcLastPagePa))
                 {
                     CleanUpForError();
 
                     return KernelResult.InvalidMemState;
                 }
 
+                ulong unusedSizeAfter;
+
                 if (copyData)
                 {
                     ulong copySize = endAddr - endAddrTruncated;
@@ -1921,7 +1943,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
 
                 _context.Memory.ZeroFill(lastPageFillAddr, unusedSizeAfter);
 
-                if (pages.AddRange(dstFirstPagePa, 1) != KernelResult.Success)
+                if (pages.AddRange(dstLastPagePa, 1) != KernelResult.Success)
                 {
                     CleanUpForError();
 
@@ -1954,9 +1976,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             MemoryPermission permission,
             MemoryState      state,
             KPageList        pageList,
-            out ulong        mappedVa)
+            out ulong        dst)
         {
-            mappedVa = 0;
+            dst = 0;
 
             lock (_blocks)
             {
@@ -2002,7 +2024,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
 
                 InsertBlock(va, neededPagesCount, state, permission);
 
-                mappedVa = va;
+                dst = va + (address - addressTruncated);
             }
 
             return KernelResult.Success;
@@ -2044,6 +2066,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                     }
 
                     ulong addressTruncated = BitUtils.AlignDown(address, PageSize);
+                    ulong addressRounded   = BitUtils.AlignUp  (address, PageSize);
+                    ulong endAddrTruncated = BitUtils.AlignDown(endAddr, PageSize);
                     ulong endAddrRounded   = BitUtils.AlignUp  (endAddr, PageSize);
 
                     ulong pagesCount = (endAddrRounded - addressTruncated) / PageSize;
@@ -2056,6 +2080,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                         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)
+                    {
+                        FreeSinglePage(_memRegion, ConvertVaToPa(addressTruncated));
+                    }
+
+                    if (endAddrTruncated < endAddrRounded && (addressTruncated == addressRounded || addressTruncated < endAddrTruncated))
+                    {
+                        FreeSinglePage(_memRegion, ConvertVaToPa(endAddrTruncated));
+                    }
+
                     if (result == KernelResult.Success)
                     {
                         InsertBlock(addressTruncated, pagesCount, MemoryState.Unmapped);
@@ -2107,7 +2143,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
 
             lock (_blocks)
             {
-                foreach (KMemoryInfo info in IterateOverRange(address, endAddrTruncated))
+                foreach (KMemoryInfo info in IterateOverRange(addressRounded, endAddrTruncated))
                 {
                     // Check if the block state matches what we expect.
                     if ((info.State      & stateMask)     != stateMask ||
@@ -2139,11 +2175,113 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
                 }
             }
 
-            InsertBlock(address, pagesCount, RestoreIpcMappingPermissions);
+            InsertBlock(addressRounded, pagesCount, RestoreIpcMappingPermissions);
 
             return KernelResult.Success;
         }
 
+        public KernelResult BorrowIpcBuffer(ulong address, ulong size)
+        {
+            return SetAttributesAndChangePermission(
+                address,
+                size,
+                MemoryState.IpcBufferAllowed,
+                MemoryState.IpcBufferAllowed,
+                MemoryPermission.Mask,
+                MemoryPermission.ReadAndWrite,
+                MemoryAttribute.Mask,
+                MemoryAttribute.None,
+                MemoryPermission.None,
+                MemoryAttribute.Borrowed);
+        }
+
+        private KernelResult SetAttributesAndChangePermission(
+            ulong            address,
+            ulong            size,
+            MemoryState      stateMask,
+            MemoryState      stateExpected,
+            MemoryPermission permissionMask,
+            MemoryPermission permissionExpected,
+            MemoryAttribute  attributeMask,
+            MemoryAttribute  attributeExpected,
+            MemoryPermission newPermission,
+            MemoryAttribute  attributeSetMask,
+            KPageList        pageList = null)
+        {
+            if (address + size <= address || !InsideAddrSpace(address, size))
+            {
+                return KernelResult.InvalidMemState;
+            }
+
+            lock (_blocks)
+            {
+                if (CheckRange(
+                    address,
+                    size,
+                    stateMask     | MemoryState.IsPoolAllocated,
+                    stateExpected | MemoryState.IsPoolAllocated,
+                    permissionMask,
+                    permissionExpected,
+                    attributeMask,
+                    attributeExpected,
+                    MemoryAttribute.IpcAndDeviceMapped,
+                    out MemoryState      oldState,
+                    out MemoryPermission oldPermission,
+                    out MemoryAttribute  oldAttribute))
+                {
+                    ulong pagesCount = size / PageSize;
+
+                    if (pageList != null)
+                    {
+                        KPageList currPageList = new KPageList();
+
+                        AddVaRangeToPageList(currPageList, address, pagesCount);
+
+                        if (!currPageList.IsEqual(pageList))
+                        {
+                            return KernelResult.InvalidMemRange;
+                        }
+                    }
+
+                    if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+                    {
+                        return KernelResult.OutOfResource;
+                    }
+
+                    if (newPermission == MemoryPermission.None)
+                    {
+                        newPermission = oldPermission;
+                    }
+
+                    if (newPermission != oldPermission)
+                    {
+                        KernelResult result = DoMmuOperation(
+                            address,
+                            pagesCount,
+                            0,
+                            false,
+                            newPermission,
+                            MemoryOperation.ChangePermRw);
+
+                        if (result != KernelResult.Success)
+                        {
+                            return result;
+                        }
+                    }
+
+                    MemoryAttribute newAttribute = oldAttribute | attributeSetMask;
+
+                    InsertBlock(address, pagesCount, oldState, newPermission, newAttribute);
+
+                    return KernelResult.Success;
+                }
+                else
+                {
+                    return KernelResult.InvalidMemState;
+                }
+            }
+        }
+
         public KernelResult UnborrowIpcBuffer(ulong address, ulong size)
         {
             return ClearAttributesAndChangePermission(
@@ -2172,6 +2310,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             MemoryAttribute  attributeClearMask,
             KPageList        pageList = null)
         {
+            if (address + size <= address || !InsideAddrSpace(address, size))
+            {
+                return KernelResult.InvalidMemState;
+            }
+
             lock (_blocks)
             {
                 if (CheckRange(
@@ -2247,7 +2390,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
 
             while (address < start + pagesCount * PageSize)
             {
-                if (!ConvertVaToPa(address, out ulong pa))
+                if (!TryConvertVaToPa(address, out ulong pa))
                 {
                     throw new InvalidOperationException("Unexpected failure translating virtual address.");
                 }
@@ -3114,7 +3257,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
             return _cpuMemory.GetPhysicalAddress(va);
         }
 
-        public bool ConvertVaToPa(ulong va, out ulong pa)
+        public ulong ConvertVaToPa(ulong va)
+        {
+            if (!TryConvertVaToPa(va, out ulong pa))
+            {
+                throw new ArgumentException($"Invalid virtual address 0x{va:X} specified.");
+            }
+
+            return pa;
+        }
+
+        public bool TryConvertVaToPa(ulong va, out ulong pa)
         {
             pa = DramMemoryMap.DramBase + _cpuMemory.GetPhysicalAddress(va);
 
diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs
index c67e5c5c2..0fa228370 100644
--- a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs
@@ -544,7 +544,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
 
         private KernelResult FreeTlsPage(KTlsPageInfo pageInfo)
         {
-            if (!MemoryManager.ConvertVaToPa(pageInfo.PageAddr, out ulong tlsPagePa))
+            if (!MemoryManager.TryConvertVaToPa(pageInfo.PageAddr, out ulong tlsPagePa))
             {
                 throw new InvalidOperationException("Unexpected failure translating virtual address to physical.");
             }
diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs
index a0a15fcf4..fba22fc11 100644
--- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs
@@ -1,5 +1,4 @@
-using ARMeilleure.Memory;
-using Ryujinx.Common;
+using Ryujinx.Common;
 using Ryujinx.Common.Logging;
 using Ryujinx.Cpu;
 using Ryujinx.HLE.Exceptions;
@@ -96,16 +95,25 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
             return result;
         }
 
-        public KernelResult SendSyncRequest(int handle)
-        {
-            return SendSyncRequestWithUserBuffer((ulong)_context.Scheduler.GetCurrentThread().Context.Tpidr, 0x100, handle);
-        }
-
-        public KernelResult SendSyncRequestWithUserBuffer(ulong messagePtr, ulong size, int handle)
+        public KernelResult SendSyncRequestHLE(int handle)
         {
             KProcess process = _context.Scheduler.GetCurrentProcess();
 
-            byte[] messageData = new byte[size];
+            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);
 
@@ -113,7 +121,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
 
             if (clientSession == null || clientSession.Service == null)
             {
-                return SendSyncRequest_(handle);
+                return SendSyncRequestWithUserBuffer(messagePtr, messageSize, handle);
             }
 
             if (clientSession != null)
@@ -168,7 +176,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
             ipcMessage.Thread.Reschedule(ThreadSchedState.Running);
         }
 
-        private KernelResult SendSyncRequest_(int handle)
+        private KernelResult SendSyncRequest(int handle)
         {
             KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
 
@@ -182,6 +190,123 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
             return session.SendSyncRequest();
         }
 
+        public KernelResult SendSyncRequestWithUserBuffer(ulong messagePtr, ulong messageSize, int handle)
+        {
+            if (!PageAligned(messagePtr))
+            {
+                return KernelResult.InvalidAddress;
+            }
+
+            if (!PageAligned(messageSize) || messageSize == 0)
+            {
+                return KernelResult.InvalidSize;
+            }
+
+            if (messagePtr + messageSize <= messagePtr)
+            {
+                return KernelResult.InvalidMemState;
+            }
+
+            KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+
+            KernelResult result = currentProcess.MemoryManager.BorrowIpcBuffer(messagePtr, messageSize);
+
+            if (result != KernelResult.Success)
+            {
+                return result;
+            }
+
+            KClientSession session = currentProcess.HandleTable.GetObject<KClientSession>(handle);
+
+            if (session == null)
+            {
+                result = KernelResult.InvalidHandle;
+            }
+            else
+            {
+                result = session.SendSyncRequest(messagePtr, messageSize);
+            }
+
+            KernelResult result2 = currentProcess.MemoryManager.UnborrowIpcBuffer(messagePtr, messageSize);
+
+            if (result == KernelResult.Success)
+            {
+                result = result2;
+            }
+
+            return result;
+        }
+
+        public KernelResult SendAsyncRequestWithUserBuffer(ulong messagePtr, ulong messageSize, int handle, out int doneEventHandle)
+        {
+            doneEventHandle = 0;
+
+            if (!PageAligned(messagePtr))
+            {
+                return KernelResult.InvalidAddress;
+            }
+
+            if (!PageAligned(messageSize) || messageSize == 0)
+            {
+                return KernelResult.InvalidSize;
+            }
+
+            if (messagePtr + messageSize <= messagePtr)
+            {
+                return KernelResult.InvalidMemState;
+            }
+
+            KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+
+            KernelResult result = currentProcess.MemoryManager.BorrowIpcBuffer(messagePtr, messageSize);
+
+            if (result != KernelResult.Success)
+            {
+                return result;
+            }
+
+            KResourceLimit resourceLimit = currentProcess.ResourceLimit;
+
+            if (resourceLimit != null && !resourceLimit.Reserve(LimitableResource.Event, 1))
+            {
+                currentProcess.MemoryManager.UnborrowIpcBuffer(messagePtr, messageSize);
+
+                return KernelResult.ResLimitExceeded;
+            }
+
+            KClientSession session = currentProcess.HandleTable.GetObject<KClientSession>(handle);
+
+            if (session == null)
+            {
+                result = KernelResult.InvalidHandle;
+            }
+            else
+            {
+                KEvent doneEvent = new KEvent(_context);
+
+                result = currentProcess.HandleTable.GenerateHandle(doneEvent.ReadableEvent, out doneEventHandle);
+
+                if (result == KernelResult.Success)
+                {
+                    result = session.SendAsyncRequest(doneEvent.WritableEvent, messagePtr, messageSize);
+
+                    if (result != KernelResult.Success)
+                    {
+                        currentProcess.HandleTable.CloseHandle(doneEventHandle);
+                    }
+                }
+            }
+
+            if (result != KernelResult.Success)
+            {
+                resourceLimit?.Release(LimitableResource.Event, 1);
+
+                currentProcess.MemoryManager.UnborrowIpcBuffer(messagePtr, messageSize);
+            }
+
+            return result;
+        }
+
         public KernelResult CreateSession(
             bool isLight,
             ulong namePtr,
@@ -348,7 +473,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
                 syncObjs[index] = obj;
             }
 
-            KernelResult result;
+            KernelResult result = KernelResult.Success;
 
             if (replyTargetHandle != 0)
             {
@@ -356,32 +481,131 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
 
                 if (replyTarget == null)
                 {
+                    result = KernelResult.InvalidHandle;
+                }
+                else
+                {
+                    result = replyTarget.Reply();
+                }
+            }
+
+            if (result == KernelResult.Success)
+            {
+                while ((result = _context.Synchronization.WaitFor(syncObjs, timeout, out handleIndex)) == KernelResult.Success)
+                {
+                    KServerSession session = currentProcess.HandleTable.GetObject<KServerSession>(handles[handleIndex]);
+
+                    if (session == null)
+                    {
+                        break;
+                    }
+
+                    if ((result = session.Receive()) != KernelResult.NotFound)
+                    {
+                        break;
+                    }
+                }
+            }
+
+            return result;
+        }
+
+        public KernelResult ReplyAndReceiveWithUserBuffer(
+            ulong handlesPtr,
+            ulong messagePtr,
+            ulong messageSize,
+            int handlesCount,
+            int replyTargetHandle,
+            long timeout,
+            out int handleIndex)
+        {
+            handleIndex = 0;
+
+            if ((uint)handlesCount > 0x40)
+            {
+                return KernelResult.MaximumExceeded;
+            }
+
+            KProcess currentProcess = _context.Scheduler.GetCurrentProcess();
+
+            ulong copySize = (ulong)((long)handlesCount * 4);
+
+            if (!currentProcess.MemoryManager.InsideAddrSpace(handlesPtr, copySize))
+            {
+                return KernelResult.UserCopyFailed;
+            }
+
+            if (handlesPtr + copySize < handlesPtr)
+            {
+                return KernelResult.UserCopyFailed;
+            }
+
+            KernelResult result = currentProcess.MemoryManager.BorrowIpcBuffer(messagePtr, messageSize);
+
+            if (result != KernelResult.Success)
+            {
+                return result;
+            }
+
+            int[] handles = new int[handlesCount];
+
+            if (!KernelTransfer.UserToKernelInt32Array(_context, handlesPtr, handles))
+            {
+                currentProcess.MemoryManager.UnborrowIpcBuffer(messagePtr, messageSize);
+
+                return KernelResult.UserCopyFailed;
+            }
+
+            KSynchronizationObject[] syncObjs = new KSynchronizationObject[handlesCount];
+
+            for (int index = 0; index < handlesCount; index++)
+            {
+                KSynchronizationObject obj = currentProcess.HandleTable.GetObject<KSynchronizationObject>(handles[index]);
+
+                if (obj == null)
+                {
+                    currentProcess.MemoryManager.UnborrowIpcBuffer(messagePtr, messageSize);
+
                     return KernelResult.InvalidHandle;
                 }
 
-                result = replyTarget.Reply();
-
-                if (result != KernelResult.Success)
-                {
-                    return result;
-                }
+                syncObjs[index] = obj;
             }
 
-            while ((result = _context.Synchronization.WaitFor(syncObjs, timeout, out handleIndex)) == KernelResult.Success)
+            if (replyTargetHandle != 0)
             {
-                KServerSession session = currentProcess.HandleTable.GetObject<KServerSession>(handles[handleIndex]);
+                KServerSession replyTarget = currentProcess.HandleTable.GetObject<KServerSession>(replyTargetHandle);
 
-                if (session == null)
+                if (replyTarget == null)
                 {
-                    break;
+                    result = KernelResult.InvalidHandle;
                 }
-
-                if ((result = session.Receive()) != KernelResult.NotFound)
+                else
                 {
-                    break;
+                    result = replyTarget.Reply(messagePtr, messageSize);
                 }
             }
 
+            if (result == KernelResult.Success)
+            {
+                while ((result = _context.Synchronization.WaitFor(syncObjs, timeout, out handleIndex)) == KernelResult.Success)
+                {
+                    KServerSession session = currentProcess.HandleTable.GetObject<KServerSession>(handles[handleIndex]);
+
+                    if (session == null)
+                    {
+                        break;
+                    }
+
+                    if ((result = session.Receive(messagePtr, messageSize)) != KernelResult.NotFound)
+                    {
+                        break;
+                    }
+                }
+            }
+
+            currentProcess.MemoryManager.UnborrowIpcBuffer(messagePtr, messageSize);
+
             return result;
         }
 
diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall32.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall32.cs
index d7cbcbf51..224af6d84 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.SendSyncRequest(handle);
+            return _syscall.SendSyncRequestHLE(handle);
         }
 
-        public KernelResult SendSyncRequestWithUserBuffer32([R(0)] uint messagePtr, [R(1)] uint size, [R(2)] int handle)
+        public KernelResult SendSyncRequestWithUserBuffer32([R(0)] uint messagePtr, [R(1)] uint messageSize, [R(2)] int handle)
         {
-            return _syscall.SendSyncRequestWithUserBuffer(messagePtr, size, handle);
+            return _syscall.SendSyncRequestWithUserBufferHLE(messagePtr, messageSize, handle);
         }
 
         public KernelResult CreateSession32(
diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall64.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall64.cs
index 5dfcdcba9..47f78a25a 100644
--- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall64.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall64.cs
@@ -22,12 +22,21 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
 
         public KernelResult SendSyncRequest64([R(0)] int handle)
         {
-            return _syscall.SendSyncRequest(handle);
+            return _syscall.SendSyncRequestHLE(handle);
         }
 
-        public KernelResult SendSyncRequestWithUserBuffer64([R(0)] ulong messagePtr, [R(1)] ulong size, [R(2)] int handle)
+        public KernelResult SendSyncRequestWithUserBuffer64([R(0)] ulong messagePtr, [R(1)] ulong messageSize, [R(2)] int handle)
         {
-            return _syscall.SendSyncRequestWithUserBuffer(messagePtr, size, handle);
+            return _syscall.SendSyncRequestWithUserBufferHLE(messagePtr, messageSize, handle);
+        }
+
+        public KernelResult SendAsyncRequestWithUserBuffer64(
+            [R(1)] ulong messagePtr,
+            [R(2)] ulong messageSize,
+            [R(3)] int handle,
+            [R(1)] out int doneEventHandle)
+        {
+            return _syscall.SendAsyncRequestWithUserBuffer(messagePtr, messageSize, handle, out doneEventHandle);
         }
 
         public KernelResult CreateSession64(
@@ -54,6 +63,25 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
             return _syscall.ReplyAndReceive(handlesPtr, handlesCount, replyTargetHandle, timeout, out handleIndex);
         }
 
+        public KernelResult ReplyAndReceiveWithUserBuffer64(
+            [R(1)] ulong messagePtr,
+            [R(2)] ulong messageSize,
+            [R(3)] ulong handlesPtr,
+            [R(4)] int handlesCount,
+            [R(5)] int replyTargetHandle,
+            [R(6)] long timeout,
+            [R(1)] out int handleIndex)
+        {
+            return _syscall.ReplyAndReceiveWithUserBuffer(
+                handlesPtr,
+                messagePtr,
+                messageSize,
+                handlesCount,
+                replyTargetHandle,
+                timeout,
+                out handleIndex);
+        }
+
         public KernelResult CreatePort64(
             [R(2)] int maxSessions,
             [R(3)] bool isLight,
diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SyscallTable.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SyscallTable.cs
index 043a54afb..dcfd9347d 100644
--- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SyscallTable.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SyscallTable.cs
@@ -24,62 +24,64 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
 
             Dictionary<int, string> svcFuncs64 = new Dictionary<int, string>
             {
-                { 0x01, nameof(Syscall64.SetHeapSize64)                   },
-                { 0x03, nameof(Syscall64.SetMemoryAttribute64)            },
-                { 0x04, nameof(Syscall64.MapMemory64)                     },
-                { 0x05, nameof(Syscall64.UnmapMemory64)                   },
-                { 0x06, nameof(Syscall64.QueryMemory64)                   },
-                { 0x07, nameof(Syscall64.ExitProcess64)                   },
-                { 0x08, nameof(Syscall64.CreateThread64)                  },
-                { 0x09, nameof(Syscall64.StartThread64)                   },
-                { 0x0a, nameof(Syscall64.ExitThread64)                    },
-                { 0x0b, nameof(Syscall64.SleepThread64)                   },
-                { 0x0c, nameof(Syscall64.GetThreadPriority64)             },
-                { 0x0d, nameof(Syscall64.SetThreadPriority64)             },
-                { 0x0e, nameof(Syscall64.GetThreadCoreMask64)             },
-                { 0x0f, nameof(Syscall64.SetThreadCoreMask64)             },
-                { 0x10, nameof(Syscall64.GetCurrentProcessorNumber64)     },
-                { 0x11, nameof(Syscall64.SignalEvent64)                   },
-                { 0x12, nameof(Syscall64.ClearEvent64)                    },
-                { 0x13, nameof(Syscall64.MapSharedMemory64)               },
-                { 0x14, nameof(Syscall64.UnmapSharedMemory64)             },
-                { 0x15, nameof(Syscall64.CreateTransferMemory64)          },
-                { 0x16, nameof(Syscall64.CloseHandle64)                   },
-                { 0x17, nameof(Syscall64.ResetSignal64)                   },
-                { 0x18, nameof(Syscall64.WaitSynchronization64)           },
-                { 0x19, nameof(Syscall64.CancelSynchronization64)         },
-                { 0x1a, nameof(Syscall64.ArbitrateLock64)                 },
-                { 0x1b, nameof(Syscall64.ArbitrateUnlock64)               },
-                { 0x1c, nameof(Syscall64.WaitProcessWideKeyAtomic64)      },
-                { 0x1d, nameof(Syscall64.SignalProcessWideKey64)          },
-                { 0x1e, nameof(Syscall64.GetSystemTick64)                 },
-                { 0x1f, nameof(Syscall64.ConnectToNamedPort64)            },
-                { 0x21, nameof(Syscall64.SendSyncRequest64)               },
-                { 0x22, nameof(Syscall64.SendSyncRequestWithUserBuffer64) },
-                { 0x24, nameof(Syscall64.GetProcessId64)                  },
-                { 0x25, nameof(Syscall64.GetThreadId64)                   },
-                { 0x26, nameof(Syscall64.Break64)                         },
-                { 0x27, nameof(Syscall64.OutputDebugString64)             },
-                { 0x29, nameof(Syscall64.GetInfo64)                       },
-                { 0x2c, nameof(Syscall64.MapPhysicalMemory64)             },
-                { 0x2d, nameof(Syscall64.UnmapPhysicalMemory64)           },
-                { 0x32, nameof(Syscall64.SetThreadActivity64)             },
-                { 0x33, nameof(Syscall64.GetThreadContext364)             },
-                { 0x34, nameof(Syscall64.WaitForAddress64)                },
-                { 0x35, nameof(Syscall64.SignalToAddress64)               },
-                { 0x40, nameof(Syscall64.CreateSession64)                 },
-                { 0x41, nameof(Syscall64.AcceptSession64)                 },
-                { 0x43, nameof(Syscall64.ReplyAndReceive64)               },
-                { 0x45, nameof(Syscall64.CreateEvent64)                   },
-                { 0x65, nameof(Syscall64.GetProcessList64)                },
-                { 0x6f, nameof(Syscall64.GetSystemInfo64)                 },
-                { 0x70, nameof(Syscall64.CreatePort64)                    },
-                { 0x71, nameof(Syscall64.ManageNamedPort64)               },
-                { 0x72, nameof(Syscall64.ConnectToPort64)                 },
-                { 0x73, nameof(Syscall64.SetProcessMemoryPermission64)    },
-                { 0x77, nameof(Syscall64.MapProcessCodeMemory64)          },
-                { 0x78, nameof(Syscall64.UnmapProcessCodeMemory64)        },
-                { 0x7B, nameof(Syscall64.TerminateProcess64)              }
+                { 0x01, nameof(Syscall64.SetHeapSize64)                    },
+                { 0x03, nameof(Syscall64.SetMemoryAttribute64)             },
+                { 0x04, nameof(Syscall64.MapMemory64)                      },
+                { 0x05, nameof(Syscall64.UnmapMemory64)                    },
+                { 0x06, nameof(Syscall64.QueryMemory64)                    },
+                { 0x07, nameof(Syscall64.ExitProcess64)                    },
+                { 0x08, nameof(Syscall64.CreateThread64)                   },
+                { 0x09, nameof(Syscall64.StartThread64)                    },
+                { 0x0a, nameof(Syscall64.ExitThread64)                     },
+                { 0x0b, nameof(Syscall64.SleepThread64)                    },
+                { 0x0c, nameof(Syscall64.GetThreadPriority64)              },
+                { 0x0d, nameof(Syscall64.SetThreadPriority64)              },
+                { 0x0e, nameof(Syscall64.GetThreadCoreMask64)              },
+                { 0x0f, nameof(Syscall64.SetThreadCoreMask64)              },
+                { 0x10, nameof(Syscall64.GetCurrentProcessorNumber64)      },
+                { 0x11, nameof(Syscall64.SignalEvent64)                    },
+                { 0x12, nameof(Syscall64.ClearEvent64)                     },
+                { 0x13, nameof(Syscall64.MapSharedMemory64)                },
+                { 0x14, nameof(Syscall64.UnmapSharedMemory64)              },
+                { 0x15, nameof(Syscall64.CreateTransferMemory64)           },
+                { 0x16, nameof(Syscall64.CloseHandle64)                    },
+                { 0x17, nameof(Syscall64.ResetSignal64)                    },
+                { 0x18, nameof(Syscall64.WaitSynchronization64)            },
+                { 0x19, nameof(Syscall64.CancelSynchronization64)          },
+                { 0x1a, nameof(Syscall64.ArbitrateLock64)                  },
+                { 0x1b, nameof(Syscall64.ArbitrateUnlock64)                },
+                { 0x1c, nameof(Syscall64.WaitProcessWideKeyAtomic64)       },
+                { 0x1d, nameof(Syscall64.SignalProcessWideKey64)           },
+                { 0x1e, nameof(Syscall64.GetSystemTick64)                  },
+                { 0x1f, nameof(Syscall64.ConnectToNamedPort64)             },
+                { 0x21, nameof(Syscall64.SendSyncRequest64)                },
+                { 0x22, nameof(Syscall64.SendSyncRequestWithUserBuffer64)  },
+                { 0x23, nameof(Syscall64.SendAsyncRequestWithUserBuffer64) },
+                { 0x24, nameof(Syscall64.GetProcessId64)                   },
+                { 0x25, nameof(Syscall64.GetThreadId64)                    },
+                { 0x26, nameof(Syscall64.Break64)                          },
+                { 0x27, nameof(Syscall64.OutputDebugString64)              },
+                { 0x29, nameof(Syscall64.GetInfo64)                        },
+                { 0x2c, nameof(Syscall64.MapPhysicalMemory64)              },
+                { 0x2d, nameof(Syscall64.UnmapPhysicalMemory64)            },
+                { 0x32, nameof(Syscall64.SetThreadActivity64)              },
+                { 0x33, nameof(Syscall64.GetThreadContext364)              },
+                { 0x34, nameof(Syscall64.WaitForAddress64)                 },
+                { 0x35, nameof(Syscall64.SignalToAddress64)                },
+                { 0x40, nameof(Syscall64.CreateSession64)                  },
+                { 0x41, nameof(Syscall64.AcceptSession64)                  },
+                { 0x43, nameof(Syscall64.ReplyAndReceive64)                },
+                { 0x44, nameof(Syscall64.ReplyAndReceiveWithUserBuffer64)  },
+                { 0x45, nameof(Syscall64.CreateEvent64)                    },
+                { 0x65, nameof(Syscall64.GetProcessList64)                 },
+                { 0x6f, nameof(Syscall64.GetSystemInfo64)                  },
+                { 0x70, nameof(Syscall64.CreatePort64)                     },
+                { 0x71, nameof(Syscall64.ManageNamedPort64)                },
+                { 0x72, nameof(Syscall64.ConnectToPort64)                  },
+                { 0x73, nameof(Syscall64.SetProcessMemoryPermission64)     },
+                { 0x77, nameof(Syscall64.MapProcessCodeMemory64)           },
+                { 0x78, nameof(Syscall64.UnmapProcessCodeMemory64)         },
+                { 0x7B, nameof(Syscall64.TerminateProcess64)               }
             };
 
             foreach (KeyValuePair<int, string> value in svcFuncs64)
diff --git a/Ryujinx.Memory/MemoryBlock.cs b/Ryujinx.Memory/MemoryBlock.cs
index 37439a8a2..065e07134 100644
--- a/Ryujinx.Memory/MemoryBlock.cs
+++ b/Ryujinx.Memory/MemoryBlock.cs
@@ -127,12 +127,12 @@ namespace Ryujinx.Memory
         /// <summary>
         /// Copies data from one memory location to another.
         /// </summary>
-        /// <param name="srcOffset">Source offset to read the data from</param>
         /// <param name="dstOffset">Destination offset to write the data into</param>
+        /// <param name="srcOffset">Source offset to read the data from</param>
         /// <param name="size">Size of the copy in bytes</param>
         /// <exception cref="ObjectDisposedException">Throw when the memory block has already been disposed</exception>
         /// <exception cref="ArgumentOutOfRangeException">Throw when <paramref name="srcOffset"/>, <paramref name="dstOffset"/> or <paramref name="size"/> is out of range</exception>
-        public void Copy(ulong srcOffset, ulong dstOffset, ulong size)
+        public void Copy(ulong dstOffset, ulong srcOffset, ulong size)
         {
             const int MaxChunkSize = 1 << 30;