From 9f6b24edfddf871320290463437b3f3cb7e29006 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Fri, 17 Jul 2020 01:19:07 -0300 Subject: [PATCH] Improve kernel IPC related syscalls (#1379) * Implement session count decrement when the handle is closed * Remove unused field * Implement SendSyncRequestWithUserBuffer, SendAsyncRequestWithUserBuffer and ReplyAndReceiveWithUserBuffer syscalls * Nits * Fix swapped copy dst/src * Add missing pointer buffer descriptor write on reply * Fix IPC unaligned buffer copy and restoring client attributes on reply * Oops * Fix SetIpcMappingPermission * Fix unaligned copy bugs * Free memory used for temporary IPC buffers --- .../HOS/Kernel/Ipc/KBufferDescriptorTable.cs | 11 +- Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs | 76 ++-- Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs | 34 +- Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs | 47 +-- Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs | 7 +- Ryujinx.HLE/HOS/Kernel/Ipc/KSessionRequest.cs | 8 +- Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlock.cs | 6 +- .../HOS/Kernel/Memory/KMemoryManager.cs | 329 +++++++++++++----- Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs | 2 +- .../HOS/Kernel/SupervisorCall/Syscall.cs | 274 +++++++++++++-- .../HOS/Kernel/SupervisorCall/Syscall32.cs | 6 +- .../HOS/Kernel/SupervisorCall/Syscall64.cs | 34 +- .../HOS/Kernel/SupervisorCall/SyscallTable.cs | 114 +++--- Ryujinx.Memory/MemoryBlock.cs | 4 +- 14 files changed, 705 insertions(+), 247 deletions(-) diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs index 6aa211dd..0986adf7 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 9c542ca0..8d6669cf 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 b99dd1cb..262058d9 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 70b54d05..48669832 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(address, 0); - KernelContext.Memory.Write(address + 8, (int)result); + KernelContext.Memory.Write(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 25e6eee5..4b5886a9 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 f3467f39..31ddfc9c 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 b7c2b309..04e14e1b 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 1cbe4e7c..b13e2841 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 c67e5c5c..0fa22837 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 a0a15fcf..fba22fc1 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(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(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(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(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(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(handles[handleIndex]); + KServerSession replyTarget = currentProcess.HandleTable.GetObject(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(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 d7cbcbf5..224af6d8 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 5dfcdcba..47f78a25 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 043a54af..dcfd9347 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 svcFuncs64 = new Dictionary { - { 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 value in svcFuncs64) diff --git a/Ryujinx.Memory/MemoryBlock.cs b/Ryujinx.Memory/MemoryBlock.cs index 37439a8a..065e0713 100644 --- a/Ryujinx.Memory/MemoryBlock.cs +++ b/Ryujinx.Memory/MemoryBlock.cs @@ -127,12 +127,12 @@ namespace Ryujinx.Memory /// /// Copies data from one memory location to another. /// - /// Source offset to read the data from /// Destination offset to write the data into + /// Source offset to read the data from /// Size of the copy in bytes /// Throw when the memory block has already been disposed /// Throw when , or is out of range - public void Copy(ulong srcOffset, ulong dstOffset, ulong size) + public void Copy(ulong dstOffset, ulong srcOffset, ulong size) { const int MaxChunkSize = 1 << 30;