diff options
| author | gdkchan <gab.dark.100@gmail.com> | 2020-07-17 01:19:07 -0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-07-17 14:19:07 +1000 |
| commit | 9f6b24edfddf871320290463437b3f3cb7e29006 (patch) | |
| tree | 4b8b45db5fe931ac37f843778c58a2d676fe3fba /Ryujinx.HLE/HOS/Kernel/Ipc | |
| parent | 46f8cef6a9e4a305803f1356446e25ce54909130 (diff) | |
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
Diffstat (limited to 'Ryujinx.HLE/HOS/Kernel/Ipc')
| -rw-r--r-- | Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs | 11 | ||||
| -rw-r--r-- | Ryujinx.HLE/HOS/Kernel/Ipc/KClientPort.cs | 76 | ||||
| -rw-r--r-- | Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs | 34 | ||||
| -rw-r--r-- | Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs | 47 | ||||
| -rw-r--r-- | Ryujinx.HLE/HOS/Kernel/Ipc/KSession.cs | 7 | ||||
| -rw-r--r-- | Ryujinx.HLE/HOS/Kernel/Ipc/KSessionRequest.cs | 8 |
6 files changed, 117 insertions, 66 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); - - return KernelResult.SessionCountExceeded; - } + currentProcess.ResourceLimit?.Release(LimitableResource.Session, 1); - 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<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 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(); } |
